/* * 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.calib.squares; import boofcv.alg.shapes.polygon.BinaryPolygonDetector; import georegression.struct.shapes.Polygon2D_F64; import org.junit.Test; import java.util.ArrayList; import java.util.List; import static junit.framework.TestCase.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; /** * @author Peter Abeles */ public class TestSquaresIntoCrossClusters { /** * Create a simple perfect cluster. Do a crude test based on number of edge histogram */ @Test public void process_simple() { SquaresIntoCrossClusters alg = new SquaresIntoCrossClusters(0.05, -1); List<Polygon2D_F64> squares = new ArrayList<>(); squares.add( createSquare(7,8)); squares.add( createSquare(9,8)); squares.add( createSquare(8,9)); squares.add( createSquare(7,10)); squares.add( createSquare(9,10)); List<BinaryPolygonDetector.Info> squareInfo = createInfo(squares); List<List<SquareNode>> clusters = alg.process(squares,squareInfo); assertEquals(1,clusters.size()); List<SquareNode> cluster = clusters.get(0); int connections[] = new int[5]; for( SquareNode n : cluster ) { connections[n.getNumberOfConnections()]++; } assertEquals(0,connections[0]); assertEquals(4,connections[1]); assertEquals(0,connections[2]); assertEquals(0,connections[3]); assertEquals(1,connections[4]); } private List<BinaryPolygonDetector.Info> createInfo(List<Polygon2D_F64> squares) { List<BinaryPolygonDetector.Info> squareInfo = new ArrayList<>(); for (int i = 0; i < squares.size(); i++) { squareInfo.add( new BinaryPolygonDetector.Info() ); } return squareInfo; } /** * Tests shapes with corners that touch the image border */ @Test public void shapesOnBorder() { SquaresIntoCrossClusters alg = new SquaresIntoCrossClusters(0.05, -1); List<Polygon2D_F64> squares = new ArrayList<>(); squares.add( createSquare(7,8)); squares.add( createSquare(9,8)); squares.add( createSquare(8,9)); squares.add( createSquare(7,10)); squares.add( createSquare(9,10)); List<BinaryPolygonDetector.Info> squareInfo = createInfo(squares); markTouch(squareInfo.get(0),true,false,true,true); markTouch(squareInfo.get(1),false,true,true,false); markTouch(squareInfo.get(3),true,true,false,true); List<List<SquareNode>> clusters = alg.process(squares,squareInfo); assertEquals(1,clusters.size()); assertEquals(5,clusters.get(0).size()); } private void markTouch( BinaryPolygonDetector.Info info , boolean ...marks ) { for( boolean b : marks ) { info.borderCorners.add(b); } } /** * Tests the corner distance threshold. two nodes should be barely within tolerance of each other with the 3rd * barely not in tolerance */ @Test public void process_connect_threshold() { SquaresIntoCrossClusters alg = new SquaresIntoCrossClusters(0.2,-1); List<Polygon2D_F64> squares = new ArrayList<>(); squares.add( createSquare(5,6)); squares.add( createSquare(6.20001,7)); squares.add( createSquare(6.1999999,5)); List<BinaryPolygonDetector.Info> squareInfo = createInfo(squares); List<List<SquareNode>> clusters = alg.process(squares,squareInfo); assertEquals(2,clusters.size()); } private Polygon2D_F64 createSquare( double x , double y ) { Polygon2D_F64 out = new Polygon2D_F64(4); out.get(0).set(x,y); out.get(1).set(x+1,y); out.get(2).set(x+1,y-1); out.get(3).set(x,y-1); return out; } @Test public void getCornerIndex() { SquareNode node = new SquareNode(); node.corners = new Polygon2D_F64(4); node.corners.get(0).set(5,6); node.corners.get(1).set(6,7); node.corners.get(2).set(7,8); node.corners.get(3).set(8,9); SquaresIntoCrossClusters alg = new SquaresIntoCrossClusters(5,-1); assertEquals(0,alg.getCornerIndex(node,5,6)); assertEquals(1,alg.getCornerIndex(node,6,7)); assertEquals(2,alg.getCornerIndex(node,7,8)); assertEquals(3,alg.getCornerIndex(node,8,9)); } @Test public void candidateIsMuchCloser() { SquareNode node0 = new SquareNode(); SquareNode node1 = new SquareNode(); node0.largestSide = 2; node1.largestSide = 1; SquaresIntoCrossClusters alg = new SquaresIntoCrossClusters(5,-1); // test obvious cases assertTrue(alg.candidateIsMuchCloser(node0,node1,0)); assertFalse(alg.candidateIsMuchCloser(node0,node1,20)); double frac = alg.tooFarFraction; node1.corners = createSquare(12,10); // the closest neighboring node should be 1 away assertTrue(alg.candidateIsMuchCloser(node0,node1,Math.pow(2*frac-1e-6,2))); assertFalse(alg.candidateIsMuchCloser(node0,node1,Math.pow(2*frac+1e-6,2))); } @Test public void considerConnect() { SquareNode node0 = new SquareNode(); SquareNode node1 = new SquareNode(); // first do it with no connections SquaresIntoCrossClusters alg = new SquaresIntoCrossClusters(5,-1); alg.considerConnect(node0,0,node1,0,4); assertTrue(node0.edges[0]==node1.edges[0]); assertTrue(node0.edges[0].a==node0); assertTrue(node0.edges[0].b==node1); // try to connect when its worse alg.considerConnect(node0,0,node1,1,4.5); assertTrue(node0.edges[0].a==node0); assertTrue(node1.edges[1]==null); alg.considerConnect(node1,1,node0,0,4.5); assertTrue(node0.edges[0].a==node0); assertTrue(node1.edges[1]==null); // have one be better alg.considerConnect(node0,0,node1,1,3.5); assertTrue(node0.edges[0]==node1.edges[1]); assertTrue(node0.edges[0].a==node0); assertTrue(node0.edges[0].b==node1); assertTrue(node1.edges[0]==null); } }