/*
* 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 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.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* @author Peter Abeles
*/
public class TestMinimizeEnergyPrune {
/**
* Perfect case. See if it does nothing
*/
@Test
public void prine_no_change() {
List<Point2D_I32> contours = createSquare(10,12,20,30);
GrowQueue_I32 corners = createSquareCorners(10, 12, 20, 30);
MinimizeEnergyPrune alg = new MinimizeEnergyPrune(1);
GrowQueue_I32 output = new GrowQueue_I32();
alg.prune(contours, corners, output);
assertEquals(corners.size(),output.size());
for (int i = 0; i < corners.size(); i++) {
assertEquals(corners.get(i),output.get(i));
}
}
/**
* Adds an obviously redundant corner and see if it gets removed
*/
@Test
public void prune_obvious() {
List<Point2D_I32> contours = createSquare(10,12,20,30);
GrowQueue_I32 corners = createSquareCorners(10, 12, 20, 30);
corners.add(corners.get(3)+4);
MinimizeEnergyPrune alg = new MinimizeEnergyPrune(1);
GrowQueue_I32 output = new GrowQueue_I32();
alg.prune(contours, corners, output);
assertEquals(4, output.size());
// see if the two sets of corners are equivalent, taking in account the possibility of a rotation
checkMatched(corners, output);
}
@Test
public void energyRemoveCorner() {
List<Point2D_I32> contours = createSquare(10,12,20,30);
GrowQueue_I32 corners = createSquareCorners(10, 12, 20, 30);
MinimizeEnergyPrune alg = new MinimizeEnergyPrune(1);
alg.contour = contours;
alg.computeSegmentEnergy(corners);
// compute the energy with the skipped corner
double expected = 0;
for (int i = 0; i < 4; i++) {
expected += alg.energySegment[i];
}
// add the corner which is going to be skipped
corners.add(corners.get(3)+4);
alg.computeSegmentEnergy(corners);
double found = alg.energyRemoveCorner(4,corners);
assertEquals(expected,found,1e-8);
// add it in another location
corners.removeTail();
corners.insert(3,corners.get(2)+3);
alg.computeSegmentEnergy(corners);
found = alg.energyRemoveCorner(3,corners);
assertEquals(expected,found,1e-8);
// skip a different corner and the energy should go up
corners = createSquareCorners(10, 12, 20, 30);
for (int i = 0; i < 4; i++) {
assertTrue(expected<alg.energyRemoveCorner(i,corners));
}
}
@Test
public void computeSegmentEnergy() {
List<Point2D_I32> contours = createSquare(10,12,20,30);
GrowQueue_I32 corners = createSquareCorners(10, 12, 20, 30);
// test with everything perfectly lining up
MinimizeEnergyPrune alg = new MinimizeEnergyPrune(1);
alg.contour = contours;
double split = alg.splitPenalty;
double expected[] = new double[]{split/100.0,split/(18.0*18.0),split/100.0,split/(18.0*18.0)};
for (int i = 0, j = corners.size()-1; i < corners.size(); j=i,i++) {
double found = alg.computeSegmentEnergy(corners,j,i);
assertEquals(expected[j],found,1e-8);
}
// Now make the corners less than perfect and see if the energy increases
corners.set(1,corners.get(1)+1);
corners.set(3,corners.get(3)+1);
for (int i = 0, j = corners.size()-1; i < corners.size(); j=i,i++) {
double found = alg.computeSegmentEnergy(corners,j,i);
assertTrue(expected[j] < found);
}
}
@Test
public void removeDuplicates() {
List<Point2D_I32> contours = createSquare(10,12,20,30);
GrowQueue_I32 corners = createSquareCorners(10, 12, 20, 30);
corners.add(corners.get(0));
corners.add(corners.get(2));
MinimizeEnergyPrune alg = new MinimizeEnergyPrune(4);
alg.contour = contours;
alg.removeDuplicates(corners);
checkMatched(createSquareCorners(10, 12, 20, 30),corners);
}
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;
}
private void checkMatched(GrowQueue_I32 corners, GrowQueue_I32 output) {
boolean foundMatch = false;
for (int offset = 0; offset < 4; offset++) {
boolean matched = true;
for (int i = 0; i < 4; i++) {
if (corners.get(i) != output.get((offset+i)%4)) {
matched = false;
break;
}
}
if( matched ) {
foundMatch = true;
break;
}
}
assertTrue(foundMatch);
}
}