/*
* 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.geometry.UtilLine2D_F64;
import georegression.struct.line.LineGeneral2D_F64;
import georegression.struct.line.LineParametric2D_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 java.util.Random;
import static org.junit.Assert.*;
/**
* @author Peter Abeles
*/
public class TestRefinePolyLineCorner {
Random rand = new Random(234);
/**
* Fit to a square
*/
@Test
public void fit_quad() {
int x0 = 10,y0 = 15;
int x1 = 60,y1 = 99;
List<Point2D_I32> points = new ArrayList<>();
addPoints(x0,y0,x1,y0,points);
addPoints(x1,y0,x1,y1,points);
addPoints(x1,y1,x0,y1,points);
addPoints(x0,y1,x0,y0,points);
RefinePolyLineCorner alg = new RefinePolyLineCorner(true);
GrowQueue_I32 corners = new GrowQueue_I32();
corners.add(0);
corners.add(50);
corners.add(50+84);
corners.add(50+84+50);
assertTrue(alg.fit(points, corners));
assertEquals(0, corners.get(0));
assertEquals(50, corners.get(1));
assertEquals(134, corners.get(2));
assertEquals(184, corners.get(3));
}
/**
* Fit to a square, but only a disconnected polyline on 3 sides
*/
@Test
public void fit_quad_segment() {
int x0 = 10,y0 = 15;
int x1 = 60,y1 = 99;
List<Point2D_I32> points = new ArrayList<>();
addPoints(x0,y0,x1,y0,points);
addPoints(x1,y0,x1,y1,points);
addPoints(x1,y1,x0,y1,points);
RefinePolyLineCorner alg = new RefinePolyLineCorner(false);
for( int i = 0; i < 10; i++ ) {
GrowQueue_I32 corners = new GrowQueue_I32();
corners.add(0);
corners.add(50 + rand.nextInt(6)-3);
corners.add(50 + 84 + rand.nextInt(6)-3);
corners.add(points.size()-1);
assertTrue(alg.fit(points, corners));
assertEquals(0, corners.get(0));
assertEquals(50, corners.get(1));
assertEquals(134, corners.get(2));
assertEquals(points.size()-1, corners.get(3));
}
}
/**
* Fit six sided shape
*/
@Test
public void fit_six_sides() {
List<Point2D_I32> points = new ArrayList<>();
addPoints(0,0,20,0,points);
addPoints(20,0,20,20,points);
addPoints(20,20,40,20,points);
addPoints(40,20,40,40, points);
addPoints(40,40, 0,40, points);
addPoints(0 ,40, 0,0, points);
GrowQueue_I32 corners = new GrowQueue_I32();
corners.add(0);
corners.add(20);
corners.add(40);
corners.add(60);
corners.add(80);
corners.add(120);
RefinePolyLineCorner alg = new RefinePolyLineCorner(true);
for (int i = 0; i < 10; i++) {
// noise up the inputs
for (int j = 0; j < corners.size(); j++) {
corners.data[j] = CircularIndex.addOffset(corners.data[j], rand.nextInt(10) - 5, points.size());
}
assertTrue(alg.fit(points, corners));
assertEquals(0, corners.get(0));
assertEquals(20, corners.get(1));
assertEquals(40,corners.get(2));
assertEquals(60,corners.get(3));
assertEquals(80,corners.get(4));
assertEquals(120,corners.get(5));
}
}
/**
* easy straight forward case
*/
@Test
public void optimize_easy() {
List<Point2D_I32> contour = new ArrayList<>();
addPoints(0, 0, 20, 0, contour);
addPoints(20, 0, 20, 20, contour);
RefinePolyLineCorner alg = new RefinePolyLineCorner(true);
alg.searchRadius = 5;
int found = alg.optimize(contour,2,16,36);
assertEquals(20,found);
}
/**
* Test case where a local minimum will cause it to get stuck if a local search is performed
*/
@Test
public void optimize_kinky() {
List<Point2D_I32> contour = new ArrayList<>();
addPoints(0, 0, 20, 0, contour);
addPoints(20, 0, 20, 20, contour);
// add a kink which could throw it off locally
contour.get(17).set(17, 1);
contour.get(18).set(18, 2);
RefinePolyLineCorner alg = new RefinePolyLineCorner(true);
alg.searchRadius = 5;
int found = alg.optimize(contour,2,16,36);
assertEquals(20,found);
}
@Test
public void distanceSum() {
RefinePolyLineCorner alg = new RefinePolyLineCorner(true);
List<Point2D_I32> contour = new ArrayList<>();
for (int i = 0; i < 20; i++) {
contour.add( new Point2D_I32(i,2));
}
LineGeneral2D_F64 line = UtilLine2D_F64.convert(new LineParametric2D_F64(0,0,1,0),(LineGeneral2D_F64)null);
// normal case
assertEquals(2 * 16, alg.distanceSum(line, 2, 17, contour), 1e-8);
// boundary case
assertEquals(2 * 15, alg.distanceSum(line, 10, 4, contour), 1e-8);
}
private void addPoints( int x0 , int y0 , int x1 , int y1 , List<Point2D_I32> points ) {
if( x0 == x1 ) {
int length = Math.abs(y1-y0);
int dir = y1>y0 ? 1 : -1;
for (int y = 0; y < length; y++) {
points.add(new Point2D_I32(x0,y0+dir*y));
}
} else {
int length = Math.abs(x1-x0);
int dir = x1>x0 ? 1 : -1;
for (int x = 0; x < length; x++) {
points.add(new Point2D_I32(x0+dir*x,y0));
}
}
}
/**
* Test to see if it gracefully handles the case where there are too few points
*/
@Test
public void tooFewPoints() {
RefinePolyLineCorner alg = new RefinePolyLineCorner(true);
GrowQueue_I32 corners = new GrowQueue_I32();
List<Point2D_I32> contour = new ArrayList<>();
for (int i = 0; i < 3; i++) {
assertFalse(alg.fit(contour, corners));
corners.add(i);
contour.add(new Point2D_I32(i, 2));
}
assertTrue(alg.fit(contour, corners));
}
}