nom.tam.image.StreamingTileImageData Maven / Gradle / Ivy
package nom.tam.image;
/*-
* #%L
* nom.tam.fits
* %%
* Copyright (C) 1996 - 2024 nom-tam-fits
* %%
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
* #L%
*/
import java.io.IOException;
import java.util.Arrays;
import nom.tam.fits.FitsException;
import nom.tam.fits.FitsUtil;
import nom.tam.fits.Header;
import nom.tam.fits.ImageData;
import nom.tam.util.ArrayDataOutput;
import nom.tam.util.ArrayFuncs;
/**
*
* Simple streaming image tile implementation. Useful for web applications that provide a cutout service. The idea is
* that the ImageData object will be extracted from an overlapping HDU (without first reading so as not to fill up the
* memory), and one of these objects are created for the output.
*
*
*
* Fits source = new Fits(myFile);
* ImageHDU imageHDU = source.getHDU(1);
*
* // We must manually adjust the header for the cutout image as necessary
* Header tileHeader = ...
*
* // Define the cutout region
* int[] tileStarts = new int[]{10, 10};
* int[] tileLengths = new int[]{45, 60};
* int[] tileSteps = new int[]{1, 1};
*
* // Create the cutout
* StreamingTileImageData streamingTileImageData = new StreamingTileImageData(tileHeader, imageHDU.getTiler(),
* tileStarts, tileLengths, tileSteps);
*
* // Write the cutout to the output
* Fits output = new Fits();
* output.addHDU(FitsFactory.hduFactory(tileHeader, streamingTileImageData));
*
* // The cutout happens at write time!
* output.write(outputStream);
*
*
* @since 1.18
*/
public class StreamingTileImageData extends ImageData {
private final int[] corners;
private final int[] lengths;
private final int[] steps;
private final ImageTiler imageTiler;
/**
* Constructor for a tile image data object.
*
* @param header The header representing the desired cutout. It is the responsibility of the caller to
* adjust the header appropriately.
* @param tiler The tiler to slice pixels out with.
* @param corners The corners to start tiling.
* @param lengths The count of values to extract.
* @param steps The number of jumps to make to the next read. Optional, defaults to 1 for each axis.
*
* @throws FitsException If the provided Header is unreadable
*/
public StreamingTileImageData(final Header header, final ImageTiler tiler, final int[] corners, final int[] lengths,
int[] steps) throws FitsException {
super(header);
if (ArrayFuncs.isEmpty(corners) || ArrayFuncs.isEmpty(lengths)) {
throw new IllegalArgumentException(
"Cannot tile out with empty corners or lengths. Use ImageData if no " + "tiling is desired.");
}
if (ArrayFuncs.isEmpty(steps)) {
this.steps = new int[corners.length];
Arrays.fill(this.steps, 1);
} else if (Arrays.stream(steps).anyMatch(i -> i < 1)) {
throw new IllegalArgumentException("Negative or zero step values not supported.");
} else {
this.steps = steps;
}
imageTiler = tiler;
this.corners = corners;
this.lengths = lengths;
}
/**
* Returns the striding step sizes along the various image dimensions.
*
* @return an array containing the steps sizes along the dimensions
*/
public int[] getSteps() {
// Steps is always initialized so no need to check for null here...
return Arrays.copyOf(steps, steps.length);
}
@SuppressWarnings("deprecation")
@Override
public void write(ArrayDataOutput o) throws FitsException {
try {
final ImageTiler tiler = imageTiler;
if (tiler == null || getTrueSize() == 0) {
// Defer writing of unknowns to the parent.
super.write(o);
} else {
tiler.getTile(o, corners, lengths, steps);
FitsUtil.pad(o, getTrueSize());
}
} catch (IOException ioException) {
throw new FitsException(ioException.getMessage(), ioException);
}
}
}