/* * 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.fiducial.square; import boofcv.abst.filter.binary.InputToBinary; import boofcv.alg.filter.binary.GThresholdImageOps; import boofcv.alg.misc.ImageMiscOps; import boofcv.alg.misc.PixelMath; import boofcv.alg.shapes.polygon.BinaryPolygonDetector; import boofcv.core.image.ConvertImage; import boofcv.factory.filter.binary.FactoryThresholdBinary; import boofcv.factory.shape.ConfigPolygonDetector; import boofcv.factory.shape.FactoryShapeDetector; import boofcv.struct.image.GrayF32; import boofcv.struct.image.GrayU8; import boofcv.struct.image.ImageGray; import org.junit.Test; import java.util.Arrays; import java.util.List; import java.util.Random; import static org.junit.Assert.*; /** * @author Peter Abeles */ public class TestDetectFiducialSquareImage { private Random rand = new Random(234); private BinaryPolygonDetector squareDetector = FactoryShapeDetector. polygon(new ConfigPolygonDetector(false, 4,4), GrayU8.class); private InputToBinary<GrayU8> inputToBinary = FactoryThresholdBinary.globalFixed(50, true, GrayU8.class); @Test public void processSquare() { // randomly create a pattern GrayU8 pattern = new GrayU8(16*4,16*4); ImageMiscOps.fillUniform(pattern, rand, 0, 2); PixelMath.multiply(pattern,255,pattern); // add a border around it GrayU8 border = new GrayU8(16*8,16*8); border.subimage(16*2,16*2,16*6,16*6,null).setTo(pattern); GrayF32 input = new GrayF32(border.width,border.height); ConvertImage.convert(border,input); // BufferedImage foo = ConvertBufferedImage.convertTo(input,null); // ShowImages.showWindow(foo,"target"); // // BoofMiscOps.pause(10000); // process it in different orientations DetectFiducialSquareImage<GrayU8> alg = new DetectFiducialSquareImage<>(inputToBinary,squareDetector,0.25,0.65,0.1,GrayU8.class); alg.addPattern(threshold(pattern, 125), 1.0); BaseDetectFiducialSquare.Result result = new BaseDetectFiducialSquare.Result(); assertTrue(alg.processSquare(input, result,0,0)); assertEquals(0,result.which); assertEquals(0,result.rotation); GrayF32 input2 = new GrayF32(input.width,input.height); ImageMiscOps.rotateCCW(input,input2); assertTrue(alg.processSquare(input2, result,0,0)); assertEquals(0,result.which); assertEquals(1,result.rotation); ImageMiscOps.rotateCCW(input2,input); assertTrue(alg.processSquare(input, result,0,0)); assertEquals(0,result.which); assertEquals(2,result.rotation); ImageMiscOps.rotateCCW(input,input2); assertTrue(alg.processSquare(input2, result,0,0)); assertEquals(0,result.which); assertEquals(3,result.rotation); // give it a random input that shouldn't match ImageMiscOps.fillUniform(pattern, rand, 0, 2); PixelMath.multiply(pattern, 255, pattern); border.subimage(16*2,16*2,16*6,16*6,null).setTo(pattern); ConvertImage.convert(border,input); assertFalse(alg.processSquare(input, result,0,0)); } @Test public void addPattern() { GrayU8 image = new GrayU8(16*8,16*4); // fill just one square for (int y = 0; y < 1; y++) { for (int x = 0; x < 2; x++) { image.set(x,y,255); } } DetectFiducialSquareImage<GrayU8> alg = new DetectFiducialSquareImage<>(inputToBinary,squareDetector,0.25,0.65,0.1,GrayU8.class); alg.addPattern(threshold(image, 100), 1.0); List<DetectFiducialSquareImage.FiducialDef> defs = alg.getTargets(); assertEquals(1,defs.size()); DetectFiducialSquareImage.FiducialDef def = defs.get(0); // manually construct the descriptor in each corner short desc[] = new short[16*16]; Arrays.fill(desc,(short)0x0000); desc[0] = (short)0x0001; compare(desc, def.desc[0]); desc[0] = (short)0x0000; desc[252] = (short)0x0001; compare(desc, def.desc[1]); desc[252] = (short)0x0000; desc[255] = (short)0x8000; compare(desc,def.desc[2]); desc[255] = (short)0x0000; desc[3] = (short)0x8000; compare(desc,def.desc[3]); } private void compare( short a[] , short b[] ) { assertEquals(a.length, b.length); for (int i = 0; i < a.length; i++) { assertEquals("index = "+i,a[i],b[i]); } } @Test public void binaryToDef() { GrayU8 image = new GrayU8(8,4); ImageMiscOps.fillUniform(image,rand,0,2); short[] out = new short[2]; DetectFiducialSquareImage.binaryToDef(image, out); for (int i = 0; i < 32; i++) { int expected = image.data[i]; int found = (out[i/16] >> (i%16)) & 1; assertEquals(expected,found); } } @Test public void hamming() { short[] a = new short[3]; short[] b = new short[3]; for (int i = 0; i < 3; i++) { a[i] = (short)rand.nextInt(); b[i] = (short)rand.nextInt(); } int expected = 0; for (int i = 0; i < 16*3; i++) { int valA = (a[i/16] >> (i%16)) & 1; int valB = (b[i/16] >> (i%16)) & 1; expected += valA != valB ? 1 : 0; } DetectFiducialSquareImage alg = new DetectFiducialSquareImage(inputToBinary,squareDetector,0.25,0.65,0.1,GrayF32.class); int found = alg.hamming(a, b); assertEquals(expected, found); } /** * See if it can process a border that isn't 0.25 */ @Test public void checkDifferentBorder() { double borderWidths[] = new double[]{0.1,0.2,0.3}; for( double border : borderWidths ) { // randomly create a pattern GrayU8 pattern = new GrayU8(16*4,16*4); ImageMiscOps.fillUniform(pattern, rand, 0, 2); PixelMath.multiply(pattern,255,pattern); // add a border around it int w = (int)Math.round(16*4/(1.0-2.0*border)); int r = (w-pattern.width)/2; GrayU8 bordered = new GrayU8(w,w); bordered.subimage(r, r, r + 16 * 4, r + 16 * 4, null).setTo(pattern); GrayF32 input = new GrayF32(bordered.width,bordered.height); ConvertImage.convert(bordered,input); DetectFiducialSquareImage alg = new DetectFiducialSquareImage(inputToBinary,squareDetector,border,0.65,0.1,GrayF32.class); alg.addPattern(threshold(pattern, 125), 1.0); BaseDetectFiducialSquare.Result result = new BaseDetectFiducialSquare.Result(); assertTrue(alg.processSquare(input, result,0,0)); } } private GrayU8 threshold(ImageGray image , double threshold ) { GrayU8 out = new GrayU8(image.width,image.height); GThresholdImageOps.threshold(image,out,threshold,false); return out; } }