/* * 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.InterpolatePixelS; import boofcv.alg.misc.GImageMiscOps; import boofcv.core.image.border.FactoryImageBorder; import boofcv.core.image.border.ImageBorder; import boofcv.struct.image.ImageGray; 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 boofcv.alg.interpolate.InterpolatePixelS}. * * @author Peter Abeles */ public abstract class GeneralChecksInterpolationPixelS< T extends ImageGray> { protected Random rand = new Random(0xff34); protected int width = 320; protected int height = 240; protected boolean exceptionOutside = true; protected abstract T createImage( int width , int height ); protected abstract InterpolatePixelS<T> wrap(T 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); GImageMiscOps.fillUniform(img, rand, 0, 100); BoofTesting.checkSubImage(this, "get", false, img); } public void get(T img) { InterpolatePixelS<T> interp = wrap(img, 0, 100); assertEquals(compute(img, 10, 10), interp.get(10, 10), 1e-5f); assertEquals(compute(img, 10.1f, 10), interp.get(10.1f, 10), 1e-5f); assertEquals(compute(img, 10, 10.6f), interp.get(10, 10.6f), 1e-5f); assertEquals(compute(img, 10.8f, 10.6f), interp.get(10.8f, 10.6f), 1e-5f); } /** * See if accessing the image edge causes it to blow up. */ @Test public void get_edges() { T img = createImage(width, height); GImageMiscOps.fillUniform(img, rand, 0, 100); BoofTesting.checkSubImage(this, "get_edges", false, img); } public void get_edges(T img) { InterpolatePixelS<T> interp = wrap(img, 0, 100); int borderX0 = interp.getFastBorderX(); int borderX1 = interp.getFastBorderX(); int borderY0 = interp.getFastBorderY(); int borderY1 = interp.getFastBorderY(); compare(interp,img, width-borderX1-1, height/2); compare(interp,img, borderX0, height/2); compare(interp,img, width/2, height-borderY1-1); compare(interp,img, width/2, borderY0); compare(interp,img, borderX0, borderY0); compare(interp,img, width - borderX1-1, height - borderY1-1); } protected void compare( InterpolatePixelS<T> interp , T img , float x , float y ) { assertEquals(compute(img, x, y), interp.get(x, y), 1e-5f); } /** * Compute the interpolation manually using independently written code. For * example, easy to write but inefficient. */ protected abstract float compute(T img, float x, float y); /** * Sees if get throws an exception if it is out of bounds */ @Test public void get_outside_noborder() { T img = createImage(width, height); InterpolatePixelS<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(InterpolatePixelS<T> interp, float x , float y) { try { interp.get(x, y); 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); GImageMiscOps.fillUniform(img, rand, 0, 100); BoofTesting.checkSubImage(this, "get_fast", false, img); } public void get_fast(T img) { InterpolatePixelS<T> interp = wrap(img, 0, 100); assertEquals(interp.get(10, 10), interp.get_fast(10, 10), 1e-6); assertEquals(interp.get(10.1f, 10), interp.get_fast(10.1f, 10), 1e-6); assertEquals(interp.get(10, 10.6f), interp.get_fast(10, 10.6f), 1e-6); assertEquals(interp.get(10.8f, 10.6f), interp.get_fast(10.8f, 10.6f), 1e-6); } /** * If a border is specified it should handle everything just fine */ @Test public void get_outside_border() { T img = createImage(width, height); GImageMiscOps.fillUniform(img, rand, 0, 100); BoofTesting.checkSubImage(this, "get_outside_border", false, img); } public void get_outside_border(T img) { InterpolatePixelS<T> interp = wrap(img, 0, 100); ImageBorder<T> border = (ImageBorder)FactoryImageBorder.singleValue(img.getClass(), 5); interp.setBorder(border); interp.setImage(img); // outside the image it should work just fine assertEquals(5,interp.get(-10, 23),1e-6); assertEquals(5,interp.get(0,2330),1e-6); } @Test public void getImage() { T img = createImage(width, height); InterpolatePixelS<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); GImageMiscOps.fillUniform(img, rand, 0, 100); InterpolatePixelS<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)) { float a = interp.get(x,y); float b = interp.get_fast(x, y); assertEquals(a,b,1e-4); } } } } /** * Pixels out of the image are clearly not in the fast bounds */ @Test public void isInFastBounds_outOfBounds() { T img = createImage(width, height); InterpolatePixelS<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); InterpolatePixelS<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); GImageMiscOps.fillUniform(img, rand, 0, 100); InterpolatePixelS<T> interp = wrap(img, 0, 100); interp.setBorder(FactoryImageBorder.singleValue(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++ ) { float v = interp.get(x+frac,y+frac); assertTrue( v >= 0 && v <= 100 ); } } } } /** * Should produce identical results when given a sub-image. */ @Test public void checkSubImage() { T imgA = createImage(30, 40); GImageMiscOps.fillUniform(imgA, rand, 0, 100); InterpolatePixelS<T> interpA = wrap(imgA, 0, 100); T imgB = BoofTesting.createSubImageOf(imgA); InterpolatePixelS<T> interpB = wrap(imgB, 0, 100); interpA.setBorder(FactoryImageBorder.singleValue(imgA, 0)); interpB.setBorder(FactoryImageBorder.singleValue(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; assertTrue("( " + x + " , " + y + " )", interpA.get(xx, yy) == interpB.get(xx,yy)); } } } }