This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit 533950959c450594b60feb88ad9df5bcea337d5c
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Fri Aug 21 12:32:34 2020 +0200
Do not overwrite puxels in destination image that are not covered by a pixel in source
image.
---
.../java/org/apache/sis/image/ResampledImage.java | 47 ++++++++++++++++----
.../org/apache/sis/image/ImageCombinerTest.java | 51 +++++++++++++++++-----
2 files changed, 78 insertions(+), 20 deletions(-)
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java b/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
index c3b823a..592b921 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
@@ -638,7 +638,8 @@ public class ResampledImage extends ComputedImage {
* can take a shorter path were data are just copied. The lossless criterion allows
us to omit the checks
* for minimal and maximal values. Shortcut may apply to both integer values and
floating point values.
*/
- final boolean shortcut = (interpolation == Interpolation.NEAREST) &&
+ final boolean useFillValues = (getDestination() == null);
+ final boolean shortcut = useFillValues && (interpolation == Interpolation.NEAREST)
&&
ImageUtilities.isLosslessConversion(sampleModel, tile.getSampleModel());
/*
* Prepare a buffer where to store a line of interpolated values. We use this buffer
for transferring
@@ -700,10 +701,17 @@ public class ResampledImage extends ComputedImage {
}
toSourceSupport.transform(coordinates, 0, coordinates, 0, scanline);
/*
- * Special case for nearest-neighbor interpolation without the need to check
for min/max values.
- * In this case values will be copied as `int` or `double` type without further
processing.
+ * Pixel coordinate along X axis where to start writing the `values` or `intValues`
array.
+ * This is usually the first column of the tile, and the number of pixels to
write is the
+ * tile width (i.e. we write a full tile row). However those values may be modified
below
+ * if we avoid writing pixels that are outside the source image.
*/
+ int posX = tileMinX;
if (shortcut) {
+ /*
+ * Special case for nearest-neighbor interpolation without the need to check
for min/max values.
+ * In this case values will be copied as `int` or `double` type without further
processing.
+ */
int ci = 0; // Index in `coordinates` array.
int vi = 0; // Index in `values` or `intValues` array.
for (int tx=tileMinX; tx<tileMaxX; tx++, ci+=tgtDim, vi+=numBands) {
@@ -743,7 +751,7 @@ public class ResampledImage extends ComputedImage {
*/
int ci = 0; // Index in `coordinates` array.
int vi = 0; // Index in `values` or `intValues` array.
- for (int tx=tileMinX; tx<tileMaxX; tx++, ci+=tgtDim, vi+=numBands) {
+ for (int tx=tileMinX; tx<tileMaxX; tx++, ci+=tgtDim) {
double x = coordinates[ci];
if (x <= xlim) {
// Separate integer and fractional parts with 0 ≤ xf < 1 except
on borders.
@@ -779,6 +787,7 @@ public class ResampledImage extends ComputedImage {
Math.min(maxValues[b],
Math.round(values[b])));
}
}
+ vi += numBands;
continue; // Values have been set, move to next
pixel.
}
}
@@ -788,18 +797,38 @@ public class ResampledImage extends ComputedImage {
* If we reach this point then any of the "if" conditions above failed
* (i.e. the point to interpolate is outside the source image bounds)
* and no values have been set in the `values` or `intValues` array.
+ * If we are writing in an existing image, do not write anything
+ * (i.e. keep the existing value). Otherwise write the fill values.
*/
- System.arraycopy(fillValues, 0, valuesArray, vi, numBands);
+ if (useFillValues) {
+ System.arraycopy(fillValues, 0, valuesArray, vi, numBands);
+ vi += numBands;
+ } else {
+ if (vi != 0) {
+ final int numX = vi / numBands;
+ if (isInteger) {
+ tile.setPixels(posX, ty, numX, 1, intValues);
+ } else {
+ tile.setPixels(posX, ty, numX, 1, values);
+ }
+ posX += numX;
+ vi = 0;
+ }
+ posX++;
+ }
}
}
/*
* At this point we finished to compute the value of a scanline.
* Copy to its final destination then move to next line.
*/
- if (isInteger) {
- tile.setPixels(tileMinX, ty, scanline, 1, intValues);
- } else {
- tile.setPixels(tileMinX, ty, scanline, 1, values);
+ final int numX = scanline - (posX - tileMinX);
+ if (numX != 0) {
+ if (isInteger) {
+ tile.setPixels(posX, ty, numX, 1, intValues);
+ } else {
+ tile.setPixels(posX, ty, numX, 1, values);
+ }
}
}
return tile;
diff --git a/core/sis-feature/src/test/java/org/apache/sis/image/ImageCombinerTest.java b/core/sis-feature/src/test/java/org/apache/sis/image/ImageCombinerTest.java
index 61dfefa..2e64157 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/image/ImageCombinerTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/ImageCombinerTest.java
@@ -92,7 +92,7 @@ public final strictfp class ImageCombinerTest extends ImageTestCase {
{400, 401, 402, 403, 500, 501, 502, 503, 600, 601, 602, 603},
{410, 411, 412, 413, 510, 511, 512, 513, 610, 611, 612, 613},
{420, 421, 422, 423, 520, 521, 522, 523, 620, 621, 622, 623},
- {430, 431, 432, 433, 530, 531, 532, 533, 630, 631, 632, 633},
+ {430, 431, 432, 433, 530, 531, 532, 533, 630, 631, 632, 633}
});
/*
* Verify source image, before combine operation.
@@ -122,14 +122,14 @@ public final strictfp class ImageCombinerTest extends ImageTestCase
{
*/
combiner.accept(toAdd);
assertValuesEqual(image = combiner.result(), 0, new double[][] {
- { 100, 101, +110, +111, +112, +210, +211, +212, +310, +311, +312, +303},
- { 110, 111, +400, +401, +402, +500, +501, +502, +600, +601, +602, +313},
- { 120, 121, +410, +411, +412, +510, +511, +512, +610, +611, +612, +323},
- { 130, 131, +700, +701, +702, +800, +801, +802, +900, +901, +902, +333},
- { 400, 401, +710, +711, +712, +810, +811, +812, +910, +911, +912, +603},
+ { 100, 101, +110, +111, +112, +210, +211, +212, +310, +311, +312, 303},
+ { 110, 111, +400, +401, +402, +500, +501, +502, +600, +601, +602, 313},
+ { 120, 121, +410, +411, +412, +510, +511, +512, +610, +611, +612, 323},
+ { 130, 131, +700, +701, +702, +800, +801, +802, +900, +901, +902, 333},
+ { 400, 401, +710, +711, +712, +810, +811, +812, +910, +911, +912, 603},
{ 410, 411, 412, 413, 510, 511, 512, 513, 610, 611, 612, 613},
{ 420, 421, 422, 423, 520, 521, 522, 523, 620, 621, 622, 623},
- { 430, 431, 432, 433, 530, 531, 532, 533, 630, 631, 632, 633},
+ { 430, 431, 432, 433, 530, 531, 532, 533, 630, 631, 632, 633}
});
}
@@ -138,24 +138,53 @@ public final strictfp class ImageCombinerTest extends ImageTestCase
{
* The transform used in this test is a simple translation. The expected result is
* similar to the {@link #testAccept()} one, with the new pixel values (identified
* by a + sign in source code) shifted by 2 rows and 2 columns.
+ *
+ * <p>In this test, the X coordinate of the first pixel to write is at the beginning
of a tile.
+ * This alignment creates a situation where each row in {@link #toAdd} is either copied
in full
+ * or not copied at all. This characteristics help to isolate the problem if a test fails.</p>
*/
@Test
- public void testResample() {
+ public void testResampleAligned() {
final ImageCombiner combiner = initialize();
final Rectangle bounds = toAdd.getBounds();
bounds.translate(2, 2);
final MathTransform toSource = MathTransforms.translation(-2, -2);
combiner.resample(toAdd, bounds, toSource);
assertValuesEqual(image = combiner.result(), 0, new double[][] {
- { 100, 101, 102, 103, 0, 0, 0, 0, 0, 0, 0, 0},
+ { 100, 101, 102, 103, 200, 201, 202, 203, 300, 301, 302, 303},
{ 110, 111, 112, 113, +100, +101, +102, +200, +201, +202, +300, +301},
{ 120, 121, 122, 123, +110, +111, +112, +210, +211, +212, +310, +311},
{ 130, 131, 132, 133, +400, +401, +402, +500, +501, +502, +600, +601},
{ 400, 401, 402, 403, +410, +411, +412, +510, +511, +512, +610, +611},
{ 410, 411, 412, 413, +700, +701, +702, +800, +801, +802, +900, +901},
{ 420, 421, 422, 423, +710, +711, +712, +810, +811, +812, +910, +911},
- { 430, 431, 432, 433, 0, 0, 0, 0, 0, 0, 0, 0},
+ { 430, 431, 432, 433, 530, 531, 532, 533, 630, 631, 632, 633}
+ });
+ }
+
+ /**
+ * Same as {@link #testResampleAligned()}, but with a "more difficult" translation.
+ * In this test, {@link #toAdd} rows are only partially copied.
+ *
+ * <p><b>Tip:</b> if this test fails, it is easier to first make sure
that
+ * {@link #testResampleAligned()} pass before to debug this test.</p>
+ */
+ @Test
+ public void testResample() {
+ final ImageCombiner combiner = initialize();
+ final Rectangle bounds = toAdd.getBounds();
+ bounds.translate(-1, 2);
+ final MathTransform toSource = MathTransforms.translation(1, -2);
+ combiner.resample(toAdd, bounds, toSource);
+ assertValuesEqual(image = combiner.result(), 0, new double[][] {
+ { 100, 101, 102, 103, 200, 201, 202, 203, 300, 301, 302, 303},
+ { 110, +100, +101, +102, +200, +201, +202, +300, +301, +302, 312, 313},
+ { 120, +110, +111, +112, +210, +211, +212, +310, +311, +312, 322, 323},
+ { 130, +400, +401, +402, +500, +501, +502, +600, +601, +602, 332, 333},
+ { 400, +410, +411, +412, +510, +511, +512, +610, +611, +612, 602, 603},
+ { 410, +700, +701, +702, +800, +801, +802, +900, +901, +902, 612, 613},
+ { 420, +710, +711, +712, +810, +811, +812, +910, +911, +912, 622, 623},
+ { 430, 431, 432, 433, 530, 531, 532, 533, 630, 631, 632, 633}
});
- // TODO: value 0 above should not overwrite previous values.
}
}
|