use of org.apache.sis.image.PixelIterator in project sis by apache.
the class FeatureAssert method assertPixelsEqual.
/**
* Verifies that sample values in the given image are equal to the expected values.
* Sample values are compared using floating point {@code double} precision.
* NaN values must be strictly equals (same bit pattern).
*
* @param expected the expected sample values.
* @param boundsExpected bounds of the expected region, or {@code null} for the whole image.
* @param actual the image to verify.
* @param boundsActual bounds of the actual region, or {@code null} for the whole image.
*/
public static void assertPixelsEqual(final RenderedImage expected, final Rectangle boundsExpected, final RenderedImage actual, final Rectangle boundsActual) {
if (boundsExpected != null && boundsActual != null) {
assertEquals("width", boundsExpected.width, boundsActual.width);
assertEquals("height", boundsExpected.height, boundsActual.height);
}
final PixelIterator ie = new PixelIterator.Builder().setIteratorOrder(SequenceType.LINEAR).setRegionOfInterest(boundsExpected).create(expected);
final PixelIterator ia = new PixelIterator.Builder().setIteratorOrder(SequenceType.LINEAR).setRegionOfInterest(boundsActual).create(actual);
double[] ev = null;
double[] av = null;
while (ie.next()) {
assertTrue(ia.next());
ev = ie.getPixel(ev);
av = ia.getPixel(av);
assertEquals(ev.length, av.length);
for (int band = 0; band < ev.length; band++) {
final double e = ev[band];
final double a = av[band];
if (Double.doubleToRawLongBits(a) != Double.doubleToRawLongBits(e)) {
final Point p = ia.getPosition();
fail(mismatchedSampleValue(p.x, p.y, p.x - actual.getMinX(), p.y - actual.getMinY(), band, e, a));
}
}
}
assertFalse(ia.next());
}
use of org.apache.sis.image.PixelIterator in project sis by apache.
the class RasterWriter method write.
/**
* Encodes the given raster to the specified output.
*
* @param raster the raster to encode.
* @param output where to write the bytes.
* @throws RasterFormatException if the raster to write is not supported.
* @throws IOException in an error occurred while writing to the given output.
*/
public void write(final Raster raster, final ChannelDataOutput output) throws IOException {
if (gridToCRS == null) {
gridToCRS = new AffineTransform();
}
final SampleModel sm = raster.getSampleModel();
final int numBands = sm.getNumBands();
final int width = raster.getWidth();
final int height = raster.getHeight();
/*
* The `direct` flag tells whether we can write the backing array directly or whether we need
* to use the pixel iterator because of sample model complexity. We can write arrays directly
* if each array contains a single band and the scanline stride is equal to the raster width.
*/
// Default value for sample models of unknown type.
boolean direct = false;
int dataType = sm.getDataType();
int pixelType = Band.bufferToPixelType(dataType);
if (sm instanceof SinglePixelPackedSampleModel) {
if (numBands == 1) {
direct = ((SinglePixelPackedSampleModel) sm).getScanlineStride() == width;
} else {
final int sampleSize = Arrays.stream(sm.getSampleSize()).max().orElse(0);
if (sampleSize >= 1 && sampleSize <= Short.SIZE) {
dataType = (sampleSize <= Byte.SIZE ? DataBuffer.TYPE_BYTE : DataBuffer.TYPE_USHORT);
pixelType = Band.bufferToPixelType(dataType);
}
}
} else if (sm instanceof MultiPixelPackedSampleModel) {
if (dataType == DataBuffer.TYPE_BYTE) {
final MultiPixelPackedSampleModel mp = (MultiPixelPackedSampleModel) sm;
final int sampleSize = mp.getPixelBitStride();
direct = (mp.getScanlineStride() * Byte.SIZE == width * sampleSize);
pixelType = Band.sizeToPixelType(sampleSize);
}
} else if (sm instanceof ComponentSampleModel) {
direct = (((ComponentSampleModel) sm).getScanlineStride() == width);
}
/*
* Write the header followed by all bands.
*/
output.buffer.order(byteOrder);
output.writeByte(ByteOrder.LITTLE_ENDIAN.equals(byteOrder) ? 1 : 0);
// WKB version number.
output.writeShort(0);
output.writeShort(ensureUnsignedShort("numBands", numBands));
output.writeDouble(gridToCRS.getScaleX());
output.writeDouble(gridToCRS.getScaleY());
output.writeDouble(gridToCRS.getTranslateX());
output.writeDouble(gridToCRS.getTranslateY());
output.writeDouble(gridToCRS.getShearX());
output.writeDouble(gridToCRS.getShearY());
output.writeInt(srid);
output.writeShort(ensureUnsignedShort("width", width));
output.writeShort(ensureUnsignedShort("height", height));
for (int b = 0; b < numBands; b++) {
final Number fill = (noDataValues != null && b < noDataValues.length) ? noDataValues[b] : null;
final Band band = new Band(pixelType, fill);
output.writeByte(band.header);
switch(dataType) {
// Fall through
case DataBuffer.TYPE_USHORT:
case DataBuffer.TYPE_SHORT:
output.writeShort(fill != null ? fill.intValue() : 0);
break;
case DataBuffer.TYPE_BYTE:
output.writeByte(fill != null ? fill.intValue() : 0);
break;
case DataBuffer.TYPE_INT:
output.writeInt(fill != null ? fill.intValue() : 0);
break;
case DataBuffer.TYPE_FLOAT:
output.writeFloat(fill != null ? fill.floatValue() : Float.NaN);
break;
case DataBuffer.TYPE_DOUBLE:
output.writeDouble(fill != null ? fill.doubleValue() : Double.NaN);
break;
default:
throw new RasterFormatException(Errors.format(Errors.Keys.UnsupportedType_1, dataType));
}
if (direct) {
final DataBuffer buffer = raster.getDataBuffer();
final int offset = buffer.getOffsets()[b];
final int length = width * height;
switch(dataType) {
case DataBuffer.TYPE_BYTE:
output.write(((DataBufferByte) buffer).getData(b), offset, length);
break;
case DataBuffer.TYPE_USHORT:
output.writeShorts(((DataBufferUShort) buffer).getData(b), offset, length);
break;
case DataBuffer.TYPE_SHORT:
output.writeShorts(((DataBufferShort) buffer).getData(b), offset, length);
break;
case DataBuffer.TYPE_INT:
output.writeInts(((DataBufferInt) buffer).getData(b), offset, length);
break;
case DataBuffer.TYPE_FLOAT:
output.writeFloats(((DataBufferFloat) buffer).getData(b), offset, length);
break;
case DataBuffer.TYPE_DOUBLE:
output.writeDoubles(((DataBufferDouble) buffer).getData(b), offset, length);
break;
}
} else {
final PixelIterator it = new PixelIterator.Builder().create(raster);
while (it.next()) {
switch(dataType) {
// Fall through
case DataBuffer.TYPE_USHORT:
case DataBuffer.TYPE_SHORT:
output.writeShort(it.getSample(b));
break;
case DataBuffer.TYPE_BYTE:
output.writeByte(it.getSample(b));
break;
case DataBuffer.TYPE_INT:
output.writeInt(it.getSample(b));
break;
case DataBuffer.TYPE_FLOAT:
output.writeFloat(it.getSample(b));
break;
case DataBuffer.TYPE_DOUBLE:
output.writeDouble(it.getSample(b));
break;
}
}
}
}
}
use of org.apache.sis.image.PixelIterator in project sis by apache.
the class CoverageReadConsistency method readAndCompareRandomRegions.
/**
* Implementation of methods testing reading in random sub-regions with random sub-samplings.
*
* @param label a label for the test being run.
* @throws DataStoreException if an error occurred while using the resource.
*/
private void readAndCompareRandomRegions(final String label) throws DataStoreException {
randomConfigureResource();
final GridGeometry gg = resource.getGridGeometry();
final int dimension = gg.getDimension();
final long[] low = new long[dimension];
final long[] high = new long[dimension];
final int[] subsampling = new int[dimension];
final int[] subOffsets = new int[dimension];
final int numBands = resource.getSampleDimensions().size();
/*
* We will collect statistics on execution time only if the
* test is executed in a more verbose mode than the default.
*/
final Statistics durations = (VERBOSE || !failOnMismatch) ? new Statistics(label) : null;
int failuresCount = 0;
for (int it = 0; it < numIterations; it++) {
final GridGeometry domain = randomDomain(gg, low, high, subsampling);
final int[] selectedBands = randomRange(numBands);
/*
* Read a coverage containing the requested sub-domain. Note that the reader is free to read
* more data than requested. The extent actually read is `actualReadExtent`. It shall contain
* fully the requested `domain`.
*/
final long startTime = System.nanoTime();
final GridCoverage subset = resource.read(domain, selectedBands);
final GridExtent actualReadExtent = subset.getGridGeometry().getExtent();
if (failOnMismatch) {
assertEquals("Unexpected number of dimensions.", dimension, actualReadExtent.getDimension());
for (int d = 0; d < dimension; d++) {
if (subsampling[d] == 1) {
assertTrue("Actual extent is too small.", actualReadExtent.getSize(d) > high[d] - low[d]);
assertTrue("Actual extent is too small.", actualReadExtent.getLow(d) <= low[d]);
assertTrue("Actual extent is too small.", actualReadExtent.getHigh(d) >= high[d]);
}
}
}
/*
* If subsampling was enabled, the factors selected by the reader may be different than
* the subsampling factors that we specified. The following block updates those values.
*/
if (allowSubsampling && full != null) {
final GridDerivation change = full.getGridGeometry().derive().subgrid(subset.getGridGeometry());
System.arraycopy(change.getSubsampling(), 0, subsampling, 0, dimension);
System.arraycopy(change.getSubsamplingOffsets(), 0, subOffsets, 0, dimension);
}
/*
* Iterate over all dimensions greater than 2. In the common case where we are reading a
* two-dimensional image, the following loop will be executed only once. If reading a 3D
* or 4D image, the loop is executed for all possible two-dimensional slices in the cube.
*/
final int sd = actualReadExtent.getDimension();
final long[] sliceMin = new long[sd];
final long[] sliceMax = new long[sd];
for (int i = 0; i < sd; i++) {
sliceMin[i] = actualReadExtent.getLow(i);
sliceMax[i] = actualReadExtent.getHigh(i);
}
nextSlice: for (; ; ) {
System.arraycopy(sliceMin, BIDIMENSIONAL, sliceMax, BIDIMENSIONAL, dimension - BIDIMENSIONAL);
final PixelIterator itr = iterator(full, sliceMin, sliceMax, subsampling, subOffsets, allowSubsampling);
final PixelIterator itc = iterator(subset, sliceMin, sliceMax, subsampling, subOffsets, false);
if (itr != null) {
assertEquals(itr.getDomain().getSize(), itc.getDomain().getSize());
final double[] expected = new double[selectedBands.length];
double[] reference = null, actual = null;
while (itr.next()) {
assertTrue(itc.next());
reference = itr.getPixel(reference);
actual = itc.getPixel(actual);
for (int i = 0; i < selectedBands.length; i++) {
expected[i] = reference[selectedBands[i]];
}
if (!Arrays.equals(expected, actual)) {
failuresCount++;
if (!failOnMismatch)
break;
final Point pr = itr.getPosition();
final Point pc = itc.getPosition();
final StringBuilder message = new StringBuilder(100).append("Mismatch at position (").append(pr.x).append(", ").append(pr.y).append(") in full image and (").append(pc.x).append(", ").append(pc.y).append(") in tested sub-image");
findMatchPosition(itr, pr, selectedBands, actual, message);
assertArrayEquals(message.toString(), expected, actual, STRICT);
/*
* POSSIBLE CAUSES FOR TEST FAILURE (known issues):
*
* - If the `GridGeometry` has no `gridToCRS` transform, then `GridDerivation` manages
* to save the scales (subsampling factors) anyway but the translations (subsampling
* offsets) are lost. It causes an image shift if the offsets were not zero. Because
* `gridToCRS` should never be null with spatial data, we do not complexify the code
* for what may be a non-issue.
*/
}
}
assertFalse(itc.next());
} else {
// Unable to create a reference image. Just check that no exception is thrown.
double[] actual = null;
while (itc.next()) {
actual = itc.getPixel(actual);
}
}
/*
* Move to the next two-dimensional slice and read again.
* We stop the loop after we have read all 2D slices.
*/
for (int d = dimension; --d >= BIDIMENSIONAL; ) {
if (sliceMin[d]++ <= actualReadExtent.getHigh(d))
continue nextSlice;
sliceMin[d] = actualReadExtent.getLow(d);
}
break;
}
if (durations != null) {
durations.accept((System.nanoTime() - startTime) / (double) StandardDateFormat.NANOS_PER_MILLISECOND);
}
}
/*
* Show statistics only if the test are executed with the `VERBOSE` flag set,
* or if this `CoverageReadConsistency` is used for benchmark.
*/
if (durations != null) {
if (statistics == null) {
statistics = new ArrayList<>();
}
statistics.add(durations);
final int totalCount = durations.count();
out.println("Number of failures: " + failuresCount + " / " + totalCount + " (" + (failuresCount / (totalCount / 100f)) + "%)");
}
}
use of org.apache.sis.image.PixelIterator in project sis by apache.
the class FeatureAssert method assertValuesEqual.
/**
* Verifies that sample values in the given image are equal to the expected floating point values.
* NaN values are compared using {@link Double#doubleToRawLongBits(double)} (i.e. different NaNs
* are <em>not</em> collapsed in a canonical NaN value).
*
* @param image the image to verify.
* @param band the band to verify.
* @param expected the expected sample values.
*/
public static void assertValuesEqual(final RenderedImage image, final int band, final double[][] expected) {
assertEquals("Height", expected.length, image.getHeight());
final PixelIterator it = new PixelIterator.Builder().setIteratorOrder(SequenceType.LINEAR).create(image);
for (int j = 0; j < expected.length; j++) {
final double[] row = expected[j];
assertEquals("Width", row.length, image.getWidth());
for (int i = 0; i < row.length; i++) {
assertTrue(it.next());
final double a = it.getSampleDouble(band);
final double e = row[i];
if (Double.doubleToRawLongBits(a) != Double.doubleToRawLongBits(e)) {
final Point p = it.getPosition();
fail(mismatchedSampleValue(p.x, p.y, i, j, band, e, a));
}
}
}
assertFalse(it.next());
}
Aggregations