/* * 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.shapes.polyline; import boofcv.misc.CircularIndex; import georegression.metric.Distance2D_F64; import georegression.struct.line.LineGeneral2D_F64; import georegression.struct.point.Point2D_F64; import georegression.struct.point.Point2D_I32; import org.ddogleg.struct.GrowQueue_I32; import org.junit.Test; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.*; /** * @author Peter Abeles */ public class TestFitLinesToContour { /** * Easy case were the corners are all in perfect location. Try all permutations of first anchor and second anchor. */ @Test public void fitAnchored_perfect_input() { FitLinesToContour alg = new FitLinesToContour(); alg.setContour(createSquare( 10,12 , 30,40)); GrowQueue_I32 corners = createSquareCorners(10,12 , 30,40); GrowQueue_I32 found = new GrowQueue_I32(); for (int anchor0 = 0; anchor0 < 4; anchor0++) { for (int j = 0; j < 4; j++) { if( j == 1 ) continue; // this case it can't optimize int anchor1 = (anchor0+j)%4; alg.fitAnchored(anchor0,anchor1,corners,found); assertEquals(4,found.size()); for (int i = 0; i < found.size(); i++) { assertEquals(corners.get(i),found.get(i)); } } } } /** * Have only one corner off by a few pixels with extremely clean input data */ @Test public void fitAnchored_easy_optimization() { FitLinesToContour alg = new FitLinesToContour(); alg.setContour(createSquare( 10,12 , 30,40)); GrowQueue_I32 expected = createSquareCorners(10,12 , 30,40); GrowQueue_I32 input = createSquareCorners(10,12 , 30,40); GrowQueue_I32 found = new GrowQueue_I32(); input.set(2, input.get(2) + 3); alg.fitAnchored(1, 3, input, found); for (int i = 0; i < found.size(); i++) { assertEquals(expected.get(i), found.get(i)); } } @Test public void sanityCheckCornerOrder() { FitLinesToContour alg = new FitLinesToContour(); alg.contour = createSquare( 10,12 , 30,40); GrowQueue_I32 corners = new GrowQueue_I32(); corners.add(6); corners.add(12); corners.add(20); corners.add(41); corners.add(1); // test positive cases first for (int i = 0; i < 5; i++) { alg.anchor0 = i; assertTrue(alg.sanityCheckCornerOrder(3, corners)); } // should fail corners.add(8); corners.add(3); alg.anchor0 = 3; assertFalse(alg.sanityCheckCornerOrder(4, corners)); } @Test public void linesIntoCorners() { FitLinesToContour alg = new FitLinesToContour(); alg.contour = createSquare( 10,12 , 30,40); GrowQueue_I32 corners = createSquareCorners(10,12 , 30,40); // first generate the lines it will fit alg.lines.resize(3); alg.anchor0 = 1; alg.fitLinesUsingCorners(3, corners); // now extract the corners GrowQueue_I32 found = new GrowQueue_I32(corners.size); found.resize(corners.size()); alg.anchor0 = 1; alg.linesIntoCorners(3,found); // only corners 2 and 3 should be updated with no change for (int i = 2; i < found.size(); i++) { assertEquals(corners.get(i), found.get(i)); } } @Test public void fitLinesUsingCorners() { FitLinesToContour alg = new FitLinesToContour(); alg.contour = createSquare( 10,12 , 30,40); GrowQueue_I32 corners = createSquareCorners(10,12 , 30,40); alg.lines.resize(3); alg.anchor0 = 1; alg.fitLinesUsingCorners(3, corners); LineGeneral2D_F64 expected = new LineGeneral2D_F64(); for (int i = 0; i < 3; i++) { alg.fitLine(corners.get((i+1)%4),corners.get((i+2)%4),expected); LineGeneral2D_F64 found = alg.lines.get(i); assertEquals(expected.A,found.A,1e-8); assertEquals(expected.B,found.B,1e-8); assertEquals(expected.C,found.C,1e-8); } } @Test public void fitLine() { FitLinesToContour alg = new FitLinesToContour(); // create the rectangle so that two sizes are less than max samples and the other two more int w = alg.maxSamples; alg.contour = createSquare( 10,12 , 10+w-1,12+w+4); GrowQueue_I32 corners = createSquareCorners(10,12 , 10+w-1,12+w+4); LineGeneral2D_F64 line = new LineGeneral2D_F64(); for (int i = 0,j=corners.size()-1; i < corners.size(); j=i,i++) { alg.fitLine(corners.get(j),corners.get(i),line); // see if the line lies perfectly along the side int contour0 = corners.get(j); int contour1 = corners.get(i); int length = CircularIndex.distanceP(contour0,contour1,alg.contour.size()); for (int k = 0; k < length; k++) { int contourIndex = CircularIndex.addOffset(contour0,k,alg.contour.size()); Point2D_I32 p = alg.contour.get(contourIndex); double found = Distance2D_F64.distance(line,new Point2D_F64(p.x,p.y)); assertEquals(0,found,1e-8); } } } @Test public void closestPoint() { FitLinesToContour alg = new FitLinesToContour(); alg.contour = createSquare( 10,12 , 30,40); Point2D_F64 p = new Point2D_F64(15.5,11); Point2D_I32 corner = alg.contour.get( alg.closestPoint(p)); assertEquals(15,corner.x); assertEquals(12,corner.y); } private List<Point2D_I32> createSquare( int x0 , int y0 , int x1 , int y1 ) { List<Point2D_I32> output = new ArrayList<>(); for (int x = x0; x < x1; x++) { output.add( new Point2D_I32(x,y0)); } for (int y = y0; y < y1; y++) { output.add( new Point2D_I32(x1,y)); } for (int x = x1; x > x0; x--) { output.add( new Point2D_I32(x,y1)); } for (int y = y1; y > y0; y--) { output.add( new Point2D_I32(x0,y)); } return output; } private GrowQueue_I32 createSquareCorners( int x0 , int y0 , int x1 , int y1 ) { GrowQueue_I32 corners = new GrowQueue_I32(); int c0 = 0; int c1 = c0 + x1-x0; int c2 = c1 + y1-y0; int c3 = c2 + x1-x0; corners.add(c0); corners.add(c1); corners.add(c2); corners.add(c3); return corners; } }