/* * 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.misc.CircularIndex; import georegression.struct.shapes.Polygon2D_F64; import org.junit.Test; import java.util.ArrayList; import java.util.List; import static junit.framework.Assert.assertFalse; import static junit.framework.TestCase.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * @author Peter Abeles */ public class TestSquareCrossClustersIntoGrids { @Test public void processCluster_positive() { SquareCrossClustersIntoGrids alg = new SquareCrossClustersIntoGrids(); for (int rows = 2; rows <= 5; rows++) { for (int cols = 2; cols <= 5; cols++) { // System.out.println(rows+" "+cols); int[] levels = createLevels(rows, cols); List<SquareNode> nodes = createCluster(false,levels); alg.grids.reset(); alg.processCluster(nodes); assertEquals(1,alg.grids.size()); SquareGrid grid = alg.grids.get(0); assertTrue((rows==grid.rows&&cols==grid.columns)||(cols==grid.rows&&rows==grid.columns)); for (int i = 0; i < grid.rows; i++) { boolean expected = grid.get(i,0) != null; if( i > 0 ) { assertEquals(!expected,grid.get(i-1,0) != null); } for (int j = 0; j < grid.columns; j++) { assertEquals(expected , (grid.get(i,j) != null)); expected = !expected; } } } } } private int[] createLevels(int rows, int cols) { int levels[] = new int[rows]; for (int i = 0; i < rows; i++) { levels[i] = cols/2 + (i%2==0 ? cols%2 : 0); } return levels; } @Test public void firstRow1and2() { // X X X X X // X X X X checkFirstRow1and2(false,5,4); // X X X X // X X X X X checkFirstRow1and2(true,4,5); // X // x checkFirstRow1and2(false,1,1); // X // x checkFirstRow1and2(true,1,1); } private void checkFirstRow1and2( boolean skip , int top , int bottom ) { List<SquareNode> cluster = createCluster(skip,top,bottom); SquareCrossClustersIntoGrids alg = new SquareCrossClustersIntoGrids(); // see if any of the nodes in the first row can be the seed for (int i = 0; i < top; i++) { List<SquareNode> list; SquareNode seed = cluster.get(i); if( seed.getNumberOfConnections() == 1 ) { list = alg.firstRow1(seed); } else { list = alg.firstRow2(seed); } assertEquals(top,list.size()); // Check to see if the edge index ordering is as expected for (int j = 0; j < top-1; j++) { SquareNode a = list.get(j); SquareNode b = list.get(j+1); checkConnection(a,b); } // check to see if the nodes are marked correctly and then reset them for (int j = 0; j < top; j++) { assertTrue(list.get(j).graph != SquareNode.RESET_GRAPH); list.get(j).graph = SquareNode.RESET_GRAPH; } for (int j = 0; j < bottom; j++) { assertTrue(cluster.get(top+j).graph == SquareNode.RESET_GRAPH); } } } /** * Seeds if 'a' and 'b' are connected to each other through a common node. The out going * indexes from the common node should be 'i' to 'a' and 'i+1' to 'b' */ private void checkConnection( SquareNode a , SquareNode b ) { for (int i = 0; i < 4; i++) { SquareEdge edgeA = a.edges[i]; if( edgeA == null ) continue; SquareNode common = edgeA.destination(a); int commonToA = edgeA.destinationSide(a); int commonToB = CircularIndex.addOffset(commonToA,1,4); SquareEdge edgeB = common.edges[commonToB]; if( edgeB != null && edgeB.destination(common) == b ) { return; } } fail("Failed"); } @Test public void addNextRow() { // X X X X X // X X X X checkAddNextRow(false,5,4); // X X X X // X X X X X checkAddNextRow(true,4,5); // X // x checkAddNextRow(false,1,1); // X // x checkAddNextRow(true,1,1); } private void checkAddNextRow( boolean skip , int top , int bottom ) { List<SquareNode> cluster = createCluster(skip,top,bottom); SquareCrossClustersIntoGrids alg = new SquareCrossClustersIntoGrids(); List<List<SquareNode>> ordered = new ArrayList<>(); // mark the first row as traversed, which it will be for (int i = 0; i < top; i++) { cluster.get(i).graph = 0; } // see if any of the nodes in the first row can be the seed for (int i = 0; i < top; i++) { assertTrue(alg.addNextRow(cluster.get(i),ordered)); List<SquareNode> found = ordered.remove( ordered.size()-1); assertEquals(bottom,found.size()); for (int j = 0; j < bottom; j++) { assertTrue(j+"",cluster.get(top+j)==found.get(j)); // try adding a row below the bottom, which should fail, from any of the bottom nodes assertFalse(alg.addNextRow(found.get(j),ordered)); assertEquals(0,ordered.size()); } // reset the markings so that in the next loop it can add them for (int j = 0; j < bottom; j++) { found.get(j).graph = SquareNode.RESET_GRAPH; } } } @Test public void lowerEdgeIndex() { for( int numCorners = 3; numCorners <= 5; numCorners++ ) { for (int first = 0; first < numCorners; first++) { int second = (first + 1) % numCorners; SquareNode node = new SquareNode(); node.corners = new Polygon2D_F64(numCorners); node.updateArrayLength(); connect(node, first, new SquareNode(), 0); node.edges[first].b.graph = SquareNode.RESET_GRAPH; connect(node, second, new SquareNode(), 0); node.edges[second].b.graph = SquareNode.RESET_GRAPH; assertEquals(first, SquareCrossClustersIntoGrids.lowerEdgeIndex(node)); } } } @Test public void isOpenEdge() { SquareNode node = new SquareNode(); for (int i = 0; i < 4; i++) { assertFalse(SquareCrossClustersIntoGrids.isOpenEdge(node,i)); } for (int i = 0; i < 4; i++) { connect(node,i,new SquareNode(),i); } for (int i = 0; i < 4; i++) { assertFalse(SquareCrossClustersIntoGrids.isOpenEdge(node,i)); } for (int i = 0; i < 4; i++) { node.edges[i].b.graph = SquareNode.RESET_GRAPH; assertTrue(SquareCrossClustersIntoGrids.isOpenEdge(node,i)); node.edges[i].b.graph = 0; } } @Test public void addToRow() { List<SquareNode> cluster; // X // X cluster = createCluster(false,1,1); checkAdd(cluster,0,2,-1,true,new int[]{}); checkAdd(cluster,0,2,-1,false,new int[]{1}); // X X // X cluster = createCluster(false,2,1); checkAdd(cluster,0,2,-1,true,new int[]{1}); checkAdd(cluster,0,2,-1,false,new int[]{2}); // X X // X X cluster = createCluster(false,2,2); checkAdd(cluster,0,2,-1,true,new int[]{1}); checkAdd(cluster,0,2,-1,false,new int[]{2,3}); // X // X cluster = createCluster(true,1,1); checkAdd(cluster,0,3,1,true,new int[]{}); checkAdd(cluster,0,3,1,false,new int[]{1}); // X // X X cluster = createCluster(true,1,2); checkAdd(cluster,0,2,-1,true,new int[]{}); checkAdd(cluster,0,2,-1,false,new int[]{2}); checkAdd(cluster,0,3,1,true,new int[]{}); checkAdd(cluster,0,3,1,false,new int[]{1}); // X X // X X cluster = createCluster(true,2,2); checkAdd(cluster,0,2,-1,true,new int[]{1}); checkAdd(cluster,0,2,-1,false,new int[]{3}); checkAdd(cluster,0,3,1,true,new int[]{}); checkAdd(cluster,0,3,1,false,new int[]{2}); } private void checkAdd( List<SquareNode> cluster, int seed , int seedCorner , int sign , boolean skip , int expected[] ) { List<SquareNode> row = new ArrayList<>(); SquareCrossClustersIntoGrids alg = new SquareCrossClustersIntoGrids(); alg.addToRow(cluster.get(seed),seedCorner,sign,skip,row); assertEquals(expected.length,row.size()); for (int i = 0; i < row.size(); i++) { SquareNode e = cluster.get(expected[i]); SquareNode found = row.get(i); assertTrue(SquareNode.RESET_GRAPH != found.graph); assertTrue(e==found); } } @Test public void numberOfOpenEdges() { SquareNode a = new SquareNode(); a.corners = new Polygon2D_F64(4); assertEquals(0, SquareCrossClustersIntoGrids.numberOfOpenEdges(a)); connect(a,1,new SquareNode(),0); assertEquals(0, SquareCrossClustersIntoGrids.numberOfOpenEdges(a)); a.edges[1].b.graph = SquareNode.RESET_GRAPH; assertEquals(1, SquareCrossClustersIntoGrids.numberOfOpenEdges(a)); } /** * Creates a new two row graph. Skip indicates if the first row skips the first column or not. The * other two parameters specify how many nodes in each row */ private List<SquareNode> createCluster(boolean skip, int ...levels ) { int total = 0; for (int i = 0; i < levels.length; i++) { total += levels[i]; } List<SquareNode> out = new ArrayList<>(); for (int i = 0; i < total; i++) { out.add( new SquareNode()); out.get(i).graph = SquareNode.RESET_GRAPH; out.get(i).corners = new Polygon2D_F64(4); } int previous = 0; for (int i = 0; i < levels.length-1; i++) { int current = previous + levels[i]; int next = current + levels[i + 1]; for (int a = 0; a < levels[i]; a++) { SquareNode n = out.get(previous + a); int right = skip ? current + a + 1 : current + a; int left = right - 1; if (right < next) connect(n, 2, out.get(right), 0); if (left >= current) { connect(n, 3, out.get(left), 1); } } previous = current; skip = !skip; } return out; } public static void connect( SquareNode a , int cornerA , SquareNode b , int cornerB ) { SquareEdge edge = new SquareEdge(); edge.a = a; edge.sideA = cornerA; edge.b = b; edge.sideB = cornerB; a.edges[cornerA] = edge; b.edges[cornerB] = edge; } @Test public void findSeedNode() { List<SquareNode> cluster = new ArrayList<>(); cluster.add( new SquareNode()); cluster.add( new SquareNode()); cluster.add( new SquareNode()); for( SquareNode n : cluster ) { n.corners = new Polygon2D_F64(4); } cluster.get(1).edges[2] = new SquareEdge(); cluster.get(2).edges[0] = new SquareEdge(); cluster.get(2).edges[1] = new SquareEdge(); assertTrue(cluster.get(1)== SquareCrossClustersIntoGrids.findSeedNode(cluster)); cluster.get(1).edges[3] = new SquareEdge(); assertTrue(cluster.get(1)== SquareCrossClustersIntoGrids.findSeedNode(cluster)); cluster.get(1).edges[0] = new SquareEdge(); assertTrue(cluster.get(2)== SquareCrossClustersIntoGrids.findSeedNode(cluster)); } }