/* * Copyright (c) 2011-2016, Peter Abeles. All Rights Reserved. * * This file is part of BoofCV (http://boofcv.org). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package boofcv.alg.interpolate.impl; import boofcv.alg.interpolate.InterpolatePixelMB; import boofcv.alg.interpolate.InterpolatePixelS; import boofcv.alg.misc.GImageMiscOps; import boofcv.core.image.GeneralizedImageOps; import boofcv.core.image.border.FactoryImageBorder; import boofcv.core.image.border.ImageBorder; import boofcv.struct.image.ImageDataType; import boofcv.struct.image.ImageGray; import boofcv.struct.image.ImageInterleaved; import boofcv.struct.image.ImageMultiBand; import boofcv.testing.BoofTesting; import org.junit.Test; import java.util.Random; import static org.junit.Assert.*; /** * Several standardized tests that ensure correct implementations of {@link InterpolatePixelS}. * * @author Peter Abeles */ public abstract class GeneralChecksInterpolationPixelMB< T extends ImageMultiBand> { protected Random rand = new Random(0xff34); protected int width = 320; protected int height = 240; int numBands = 2; float tmp0[] = new float[numBands]; float tmp1[] = new float[numBands]; protected boolean exceptionOutside = true; protected abstract T createImage( int width , int height , int numBands ); protected abstract InterpolatePixelMB<T> wrap(T image, int minValue, int maxValue); /** * Creates the equivalent single band interpolation algorithm. If none exist then return null */ protected abstract<SB extends ImageGray> InterpolatePixelS<SB> wrapSingle(SB image, int minValue, int maxValue); /** * Checks value returned by get() against values computed using * an alternative approach. */ @Test public void get() { T img = createImage(width, height,numBands); GImageMiscOps.fillUniform(img, rand, 0, 100); BoofTesting.checkSubImage(this, "get", false, img); } public void get(T img) { InterpolatePixelMB<T> interp = wrap(img, 0, 100); compareGet(10, 10, img, interp); compareGet(10.1f, 10, img, interp); compareGet(10, 10.6f, img, interp); compareGet(10.8f, 10.6f, img, interp); } private void compareGet(float x, float y, T img, InterpolatePixelMB<T> interp) { compute(img, x, y, tmp0); interp.get(x, y, tmp1); for (int i = 0; i < numBands; i++) { assertEquals(tmp0[i],tmp1[i],1e-5f); } } private void compareFast(float x, float y, T img, InterpolatePixelMB<T> interp) { compute(img, x, y, tmp0); interp.get_fast(x, y, tmp1); for (int i = 0; i < numBands; i++) { assertEquals(tmp0[i],tmp1[i],1e-5f); } } /** * See if accessing the image edge causes it to blow up. */ @Test public void get_edges() { T img = createImage(width, height,numBands); GImageMiscOps.fillUniform(img, rand, 0, 100); BoofTesting.checkSubImage(this, "get_edges", false, img); } public void get_edges(T img) { InterpolatePixelMB<T> interp = wrap(img, 0, 100); int borderX0 = interp.getFastBorderX(); int borderX1 = interp.getFastBorderX(); int borderY0 = interp.getFastBorderY(); int borderY1 = interp.getFastBorderY(); compareGet(width - borderX1 - 1, height / 2, img, interp); compareGet(borderX0, height / 2, img, interp); compareGet(width / 2, height - borderY1 - 1, img, interp); compareGet(width / 2, borderY0, img, interp); compareGet(borderX0, borderY0, img, interp); compareGet(width - borderX1 - 1, height - borderY1 - 1, img, interp); } /** * Compute the interpolation manually using independently written code. For * example, easy to write but inefficient. */ protected abstract void compute(T img, float x, float y , float pixel[] ); /** * Sees if get throws an exception if it is out of bounds */ @Test public void get_outside_noborder() { T img = createImage(width, height, numBands); InterpolatePixelMB<T> interp = wrap(img, 0, 100); checkOutside(interp,-0.1f,0); checkOutside(interp,0,-0.1f); checkOutside(interp,width-0.99f,0); checkOutside(interp,0,height-0.99f); } private void checkOutside(InterpolatePixelMB<T> interp, float x , float y) { try { interp.get(x, y, tmp0); if( exceptionOutside ) fail("Didn't throw an exception when accessing an outside pixel"); } catch( RuntimeException e ) {} } /** * Compare get_fast against the value returned by get() */ @Test public void get_fast() { T img = createImage(width, height, numBands); GImageMiscOps.fillUniform(img, rand, 0, 100); BoofTesting.checkSubImage(this, "get_fast", false, img); } public void get_fast(T img) { InterpolatePixelMB<T> interp = wrap(img, 0, 100); compareFast(10, 10, img, interp); compareFast(10.1f, 10, img, interp); compareFast(10, 10.6f, img, interp); compareFast(10.8f, 10.6f, img, interp); } /** * If a border is specified it should handle everything just fine */ @Test public void get_outside_border() { T img = createImage(width, height, numBands); GImageMiscOps.fillUniform(img, rand, 0, 100); BoofTesting.checkSubImage(this, "get_outside_border", false, img); } public void get_outside_border(T img) { InterpolatePixelMB<T> interp = wrap(img, 0, 100); ImageBorder<T> border = (ImageBorder)FactoryImageBorder.interleavedValue((Class) img.getClass(), 5); interp.setBorder(border); interp.setImage(img); // outside the image it should work just fine for (int i = 0; i < numBands; i++) { tmp1[i] = 5; } interp.get(-10, 23, tmp0); for (int i = 0; i < numBands; i++) { assertEquals(tmp0[i],tmp1[i],1e-4); } interp.get(5, 2330, tmp1); for (int i = 0; i < numBands; i++) { assertEquals(tmp0[i],tmp1[i],1e-4); } } @Test public void getImage() { T img = createImage(width, height, numBands); InterpolatePixelMB<T> interp = wrap(img, 0, 100); assertTrue(img == interp.getImage()); } /** * Scans through the whole image and for each pixel which is "safe" it compares the safe * value to the unsafe value. */ @Test public void isInFastBounds() { T img = createImage(width, height, numBands); GImageMiscOps.fillUniform(img, rand, 0, 100); InterpolatePixelMB<T> interp = wrap(img, 0, 100); for( int y = 0; y < height; y++ ) { for( int x = 0; x < width; x++ ) { if( interp.isInFastBounds(x, y)) { interp.get(x, y, tmp0); interp.get_fast(x, y, tmp1); for (int i = 0; i < numBands; i++) { assertEquals(tmp0[i],tmp1[i],1e-4); } } } } } /** * Pixels out of the image are clearly not in the fast bounds */ @Test public void isInFastBounds_outOfBounds() { T img = createImage(width, height, numBands); InterpolatePixelMB<T> interp = wrap(img, 0, 100); assertFalse(interp.isInFastBounds(-0.1f,0)); assertFalse(interp.isInFastBounds(0, -0.1f)); assertFalse(interp.isInFastBounds(width-0.99f,0)); assertFalse(interp.isInFastBounds(0,height-0.99f)); } @Test public void getFastBorder() { T img = createImage(width, height, numBands); InterpolatePixelMB<T> interp = wrap(img, 0, 100); // create a region with positive cases int x0 = interp.getFastBorderX(); int x1 = width - interp.getFastBorderX(); int y0 = interp.getFastBorderX(); int y1 = height - interp.getFastBorderX(); for( int y = 0; y < height; y++ ) { for( int x = 0; x < width; x++ ) { if( x >= x0 && x < x1 && y >= y0 && y < y1 ) { assertTrue(interp.isInFastBounds(x,y)); } else { // stuff outside of the border does not need to be outside the fast bounds // this is a crude test to avoid checking the bounds every time through a loop // assertFalse(interp.isInFastBounds(x,y)); } } } } /** * Interpolates the whole image and sees if the values returned are within the specified bounds */ @Test public void checkPixelValueBoundsHonored() { T img = createImage(20, 30, numBands); GImageMiscOps.fillUniform(img, rand, 0, 100); InterpolatePixelMB<T> interp = wrap(img, 0, 100); interp.setBorder((ImageBorder)FactoryImageBorder.interleavedValue((ImageInterleaved)img, 0)); for( int off = 0; off < 5; off++ ) { float frac = off/5.0f; for( int y = 0; y < img.height; y++ ) { for( int x = 0; x < img.width; x++ ) { interp.get(x + frac, y + frac, tmp0); for (int i = 0; i < numBands; i++) { assertTrue( tmp0[i] >= 0 && tmp0[i] <= 100 ); } } } } } /** * Should produce identical results when given a sub-image. */ @Test public void checkSubImage() { T imgA = createImage(30, 40, numBands); GImageMiscOps.fillUniform(imgA, rand, 0, 100); InterpolatePixelMB<T> interpA = wrap(imgA, 0, 100); T imgB = BoofTesting.createSubImageOf(imgA); InterpolatePixelMB<T> interpB = wrap(imgB, 0, 100); interpA.setBorder((ImageBorder)FactoryImageBorder.interleavedValue((ImageInterleaved)imgA, 0)); interpB.setBorder((ImageBorder) FactoryImageBorder.interleavedValue((ImageInterleaved) imgB, 0)); for (int y = 0; y < 40; y++) { for (int x = 0; x < 30; x++) { float dx = rand.nextFloat()*2-1f; float dy = rand.nextFloat()*2-1f; float xx = x + dx; float yy = y + dy; // ,make sure it is inside the image bound if( yy < 0 ) yy = 0; else if( yy > 39 ) yy = 39; if( xx < 0 ) xx = 0; else if( xx > 29 ) xx = 29; interpA.get(xx, yy, tmp0); interpB.get(xx, yy, tmp1); for (int i = 0; i < numBands; i++) { assertTrue("( " + x + " , " + y + " )", tmp0[i] == tmp1[i]); } } } } /** * Compares interpolation to two single band images and sees if they produce nearly identical results */ @Test public void compareToSingleBand() { T origMB = createImage(30, 40, 2); GImageMiscOps.fillUniform(origMB, rand, 0, 100); ImageDataType dataType = origMB.getImageType().getDataType(); ImageGray band0 = GeneralizedImageOps.createSingleBand(dataType,origMB.width,origMB.height); ImageGray band1 = GeneralizedImageOps.createSingleBand(dataType,origMB.width,origMB.height); for (int y = 0; y < origMB.height; y++) { for (int x = 0; x < origMB.width; x++) { double val0 = GeneralizedImageOps.get(origMB,x,y,0); double val1 = GeneralizedImageOps.get(origMB,x,y,1); GeneralizedImageOps.set(band0,x,y,val0); GeneralizedImageOps.set(band1,x,y,val1); } } InterpolatePixelS interpBand0 = wrapSingle(band0,0,255); InterpolatePixelS interpBand1 = wrapSingle(band1,0,255); InterpolatePixelMB<T> interpMB = wrap(origMB,0,255); interpBand0.setBorder(FactoryImageBorder.genericValue(0,band0.getImageType())); interpBand1.setBorder(FactoryImageBorder.genericValue(0,band1.getImageType())); interpMB.setBorder(FactoryImageBorder.genericValue(0,interpMB.getImageType())); interpBand0.setImage(band0); interpBand1.setImage(band1); interpMB.setImage(origMB); float values[] = new float[2]; for (int y = 0; y < origMB.height-1; y++) { for (int x = 0; x < origMB.width-1; x++) { float val0 = interpBand0.get(x+0.2f,y+0.3f); float val1 = interpBand1.get(x+0.2f,y+0.3f); interpMB.get(x+0.2f,y+0.3f,values); assertEquals(val0,values[0],1e-4f); assertEquals(val1,values[1],1e-4f); } } } }