/* * 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.transform.ii; import boofcv.alg.filter.convolve.ConvolveImageNoBorder; import boofcv.alg.filter.convolve.ConvolveWithBorder; import boofcv.alg.filter.kernel.KernelMath; import boofcv.alg.misc.ImageMiscOps; import boofcv.core.image.border.FactoryImageBorderAlgs; import boofcv.core.image.border.ImageBorder_F32; import boofcv.struct.convolve.Kernel2D_F32; import boofcv.struct.image.GrayF32; import boofcv.testing.BoofTesting; import org.junit.Test; import java.util.Random; /** * @author Peter Abeles */ public class TestDerivativeIntegralImage { Random rand = new Random(234); int width = 30; int height = 40; @Test public void kernelDerivX() { GrayF32 orig = new GrayF32(width,height); GrayF32 integral = new GrayF32(width,height); ImageMiscOps.fillUniform(orig,rand,0,20); GrayF32 expected = new GrayF32(width,height); GrayF32 found = new GrayF32(width,height); IntegralImageOps.transform(orig,integral); ImageBorder_F32 border = (ImageBorder_F32)FactoryImageBorderAlgs.value(orig, 0); for( int r = 1; r < 5; r++ ) { IntegralKernel kernelI = DerivativeIntegralImage.kernelDerivX(r,null); Kernel2D_F32 kernel = createDerivX(r); ConvolveWithBorder.convolve(kernel,orig,expected,border); IntegralImageOps.convolve(integral,kernelI,found); BoofTesting.assertEquals(expected,found,1e-2); } } @Test public void kernelDerivY() { GrayF32 orig = new GrayF32(width,height); GrayF32 integral = new GrayF32(width,height); ImageMiscOps.fillUniform(orig,rand,0,20); GrayF32 expected = new GrayF32(width,height); GrayF32 found = new GrayF32(width,height); IntegralImageOps.transform(orig,integral); ImageBorder_F32 border = (ImageBorder_F32)FactoryImageBorderAlgs.value(orig, 0); for( int r = 1; r < 5; r++ ) { IntegralKernel kernelI = DerivativeIntegralImage.kernelDerivY(r,null); Kernel2D_F32 kernel = createDerivX(r); kernel = KernelMath.transpose(kernel); ConvolveWithBorder.convolve(kernel,orig,expected,border); IntegralImageOps.convolve(integral,kernelI,found); BoofTesting.assertEquals(expected,found,1e-2); } } @Test public void kernelHaarX() { GrayF32 orig = new GrayF32(width,height); GrayF32 integral = new GrayF32(width,height); ImageMiscOps.fillUniform(orig,rand,0,20); GrayF32 expected = new GrayF32(width,height); GrayF32 found = new GrayF32(width,height); IntegralImageOps.transform(orig,integral); ImageBorder_F32 border = (ImageBorder_F32)FactoryImageBorderAlgs.value(orig, 0); for( int r = 1; r < 5; r++ ) { IntegralKernel kernelI = DerivativeIntegralImage.kernelHaarX(r,null); Kernel2D_F32 kernel = createHaarX(r); ConvolveWithBorder.convolve(kernel,orig,expected,border); IntegralImageOps.convolve(integral,kernelI,found); BoofTesting.assertEquals(expected,found,1e-2); } } @Test public void kernelHaarY() { GrayF32 orig = new GrayF32(width,height); GrayF32 integral = new GrayF32(width,height); ImageMiscOps.fillUniform(orig,rand,0,20); GrayF32 expected = new GrayF32(width,height); GrayF32 found = new GrayF32(width,height); IntegralImageOps.transform(orig,integral); ImageBorder_F32 border = (ImageBorder_F32)FactoryImageBorderAlgs.value(orig, 0); for( int i = 1; i < 5; i++ ) { int size = i*2; IntegralKernel kernelI = DerivativeIntegralImage.kernelHaarY(size,null); Kernel2D_F32 kernel = createHaarX(size); kernel = KernelMath.transpose(kernel); ConvolveWithBorder.convolve(kernel,orig,expected,border); IntegralImageOps.convolve(integral,kernelI,found); BoofTesting.assertEquals(expected,found,1e-2); } } @Test public void kernelDerivXX() { GrayF32 orig = new GrayF32(width,height); GrayF32 integral = new GrayF32(width,height); ImageMiscOps.fillUniform(orig,rand,0,20); GrayF32 expected = new GrayF32(width,height); GrayF32 found = new GrayF32(width,height); IntegralImageOps.transform(orig,integral); ImageBorder_F32 border = (ImageBorder_F32)FactoryImageBorderAlgs.value(orig, 0); for( int i = 1; i <= 5; i += 2 ) { int size = i*3; IntegralKernel kernelI = DerivativeIntegralImage.kernelDerivXX(size,null); Kernel2D_F32 kernel = createDerivXX(size); ConvolveWithBorder.convolve(kernel,orig,expected,border); IntegralImageOps.convolve(integral,kernelI,found); BoofTesting.assertEquals(expected,found,1e-2); } } @Test public void derivXX() { GrayF32 orig = new GrayF32(width,height); GrayF32 integral = new GrayF32(width,height); ImageMiscOps.fillUniform(orig,rand,0,20); GrayF32 expected = new GrayF32(width,height); GrayF32 found = new GrayF32(width,height); IntegralImageOps.transform(orig,integral); for( int i = 1; i <= 5; i += 2 ) { int size = i*3; Kernel2D_F32 kernel = createDerivXX(size); ConvolveImageNoBorder.convolve(kernel,orig,expected); DerivativeIntegralImage.derivXX(integral,found,size); int r = size/2; GrayF32 a = expected.subimage(r+1,r+1,expected.width-r,expected.height-r, null); GrayF32 b = found.subimage(r+1,r+1,found.width-r,found.height-r, null); BoofTesting.assertEquals(a,b,1e-2); } } @Test public void kernelDerivYY() { GrayF32 orig = new GrayF32(width,height); GrayF32 integral = new GrayF32(width,height); ImageMiscOps.fillUniform(orig,rand,0,20); GrayF32 expected = new GrayF32(width,height); GrayF32 found = new GrayF32(width,height); IntegralImageOps.transform(orig,integral); ImageBorder_F32 border = (ImageBorder_F32)FactoryImageBorderAlgs.value(orig, 0); for( int i = 1; i <= 5; i += 2 ) { int size = i*3; IntegralKernel kernelI = DerivativeIntegralImage.kernelDerivYY(size,null); Kernel2D_F32 kernel = createDerivXX(size); kernel = KernelMath.transpose(kernel); ConvolveWithBorder.convolve(kernel,orig,expected,border); IntegralImageOps.convolve(integral,kernelI,found); BoofTesting.assertEquals(expected,found,1e-2); } } @Test public void derivYY() { GrayF32 orig = new GrayF32(width,height); GrayF32 integral = new GrayF32(width,height); ImageMiscOps.fillUniform(orig,rand,0,20); GrayF32 expected = new GrayF32(width,height); GrayF32 found = new GrayF32(width,height); IntegralImageOps.transform(orig,integral); for( int i = 1; i <= 5; i += 2 ) { int size = i*3; Kernel2D_F32 kernel = createDerivXX(size); kernel = KernelMath.transpose(kernel); ConvolveImageNoBorder.convolve(kernel,orig,expected); DerivativeIntegralImage.derivYY(integral,found,size); int r = size/2; GrayF32 a = expected.subimage(r+1,r+1,expected.width-r,expected.height-r, null); GrayF32 b = found.subimage(r+1,r+1,found.width-r,found.height-r, null); BoofTesting.assertEquals(a,b,1e-2); } } @Test public void kernelDerivXY() { GrayF32 orig = new GrayF32(width,height); GrayF32 integral = new GrayF32(width,height); ImageMiscOps.fillUniform(orig,rand,0,20); GrayF32 expected = new GrayF32(width,height); GrayF32 found = new GrayF32(width,height); IntegralImageOps.transform(orig,integral); ImageBorder_F32 border = (ImageBorder_F32)FactoryImageBorderAlgs.value(orig, 0); for( int i = 1; i <= 5; i += 2 ) { int size = i*3; IntegralKernel kernelI = DerivativeIntegralImage.kernelDerivXY(size,null); Kernel2D_F32 kernel = createDerivXY(size); ConvolveWithBorder.convolve(kernel,orig,expected,border); IntegralImageOps.convolve(integral,kernelI,found); BoofTesting.assertEquals(expected,found,1e-2); } } @Test public void derivXY() { GrayF32 orig = new GrayF32(width,height); GrayF32 integral = new GrayF32(width,height); ImageMiscOps.fillUniform(orig,rand,0,20); GrayF32 expected = new GrayF32(width,height); GrayF32 found = new GrayF32(width,height); IntegralImageOps.transform(orig,integral); for( int i = 1; i <= 5; i += 2 ) { int size = i*3; Kernel2D_F32 kernel = createDerivXY(size); ConvolveImageNoBorder.convolve(kernel,orig,expected); DerivativeIntegralImage.derivXY(integral,found,size); int r = size/2; GrayF32 a = expected.subimage(r+1,r+1,expected.width-r,expected.height-r, null); GrayF32 b = found.subimage(r+1,r+1,found.width-r,found.height-r, null); BoofTesting.assertEquals(a,b,1e-2); } } private Kernel2D_F32 createDerivX( int r ) { int size = r*2+1; Kernel2D_F32 ret = new Kernel2D_F32(size); for( int y = 0; y < size; y++ ) { for( int x = 0; x < r; x++ ) { ret.set(x,y,-1); ret.set(x+r+1,y,1); } } return ret; } private Kernel2D_F32 createHaarX( int r ) { int size = r*2; // TODO kernels only support odd sizes right now... change if that changes (remove +1) Kernel2D_F32 ret = new Kernel2D_F32(size+1); for( int y = 1; y <= size; y++ ) { for( int x = 1; x <= r; x++ ) { ret.set(x,y,-1); ret.set(x+r,y,1); } } return ret; } private Kernel2D_F32 createDerivXX( int size ) { int blockW = size/3; int blockH = size-blockW-1; int borderY = (size-blockH)/2; Kernel2D_F32 ret = new Kernel2D_F32(size); for( int y = borderY; y < size-borderY; y++ ) { for( int x = 0; x < blockW; x++ ) { ret.set(x,y,1); ret.set(x+blockW*2,y,1); } for( int x = blockW; x < 2*blockW; x++ ) { ret.set(x,y,-2); } } return ret; } private Kernel2D_F32 createDerivXY( int size ) { int block = size/3; int border = (size-2*block-1)/2; int w = block*3; Kernel2D_F32 ret = new Kernel2D_F32(w); for( int y = border; y < border+block; y++ ) { for( int x = border; x < block+border; x++ ) { ret.set(x,y,1); ret.set(x+block+1,y,-1); } } for( int y = border+block+1; y < size-border; y++ ) { for( int x = border; x < block+border; x++ ) { ret.set(x,y,-1); ret.set(x+block+1,y,1); } } return ret; } }