/* * 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.polygon; import boofcv.alg.filter.binary.BinaryImageOps; import boofcv.alg.filter.binary.GThresholdImageOps; import boofcv.alg.shapes.ShapeFittingOps; import boofcv.struct.ConnectRule; import boofcv.struct.PointIndex_I32; import boofcv.struct.image.GrayU8; import boofcv.testing.BoofTesting; import georegression.struct.affine.Affine2D_F64; import georegression.struct.point.Point2D_I32; import georegression.struct.se.Se2_F64; import georegression.struct.shapes.Polygon2D_F64; import georegression.transform.ConvertTransform_F64; 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 TestRefinePolygonCornersToImage extends BaseFitPolygon{ List<Point2D_I32> contour; GrowQueue_I32 split; @Test public void perfectRectangle() { Polygon2D_F64 expected = new Polygon2D_F64(x0,y0 , x1,y0, x1,y1, x0,y1); Polygon2D_F64 found = new Polygon2D_F64(4); for (Class imageType : imageTypes) { for (int i = 0; i < 2; i++) { boolean black = i == 0; setup(new Affine2D_F64(), black, imageType); findContour(black); RefinePolygonCornersToImage alg = new RefinePolygonCornersToImage(imageType); alg.setImage(image); assertTrue(alg.refine(null, contour, split, found)); assertTrue(expected.isEquivalent(found, 0.01)); } } } @Test public void perfectRectangle_subimage() { Polygon2D_F64 expected = new Polygon2D_F64(x0,y0 , x1,y0, x1,y1, x0,y1); Polygon2D_F64 found = new Polygon2D_F64(4); for (Class imageType : imageTypes) { for (int i = 0; i < 2; i++) { boolean black = i == 0; setup(new Affine2D_F64(), black, imageType); findContour(black); RefinePolygonCornersToImage alg = new RefinePolygonCornersToImage(imageType); alg.setImage(BoofTesting.createSubImageOf_S(image)); assertTrue(alg.refine(null, contour, split, found)); assertTrue(expected.isEquivalent(found, 0.01)); } } } /** * See if it handles lines along the image border correctly */ @Test public void fitWithEdgeOnBorder() { for (Class imageType : imageTypes) { x0 = 0; x1 = 100; y0 = 100; y1 = 200; setup(null, true, imageType); RefinePolygonCornersToImage alg = new RefinePolygonCornersToImage(imageType); findContour(true); Polygon2D_F64 found = new Polygon2D_F64(4); alg.setImage(image); assertTrue(alg.refine(null, contour, split,found)); Polygon2D_F64 expected = createFromSquare(null); expected.flip(); assertTrue(expected.isEquivalent(found, 0.01)); } } @Test public void distortedRectangle() { double tol = 0.4; Polygon2D_F64 original = new Polygon2D_F64(x0,y0 , x1,y0, x1,y1, x0,y1); Polygon2D_F64 found = new Polygon2D_F64(4); Affine2D_F64 affines[] = new Affine2D_F64[2]; affines[0] = new Affine2D_F64(); affines[1] = new Affine2D_F64(1.3,0.05,-0.15,0.87,0.1,0.6); ConvertTransform_F64.convert(new Se2_F64(0, 0, 0.2), affines[0]); for (Class imageType : imageTypes) { for (int i = 0; i < 2; i++) { for(Affine2D_F64 affine : affines ) { boolean black = i == 0; setup(affine, black, imageType); findContour(black); RefinePolygonCornersToImage alg = new RefinePolygonCornersToImage( imageType); alg.setImage(BoofTesting.createSubImageOf_S(image)); assertTrue(alg.refine(null, contour, split, found)); Polygon2D_F64 expected = apply(affine,original); assertTrue(expected.isEquivalent(found, tol)); } } } } @Test public void pickEndIndex() { RefinePolygonCornersToImage alg = new RefinePolygonCornersToImage(GrayU8.class); alg.setPixelsAway(6); alg.contour = new ArrayList(); alg.splits = new GrowQueue_I32(); int away = alg.getPixelsAway(); int N = 20; for (int i = 0; i < N; i++) { alg.contour.add( new Point2D_I32()); } alg.splits.add(0); alg.splits.add(7); alg.splits.add(9); alg.splits.add(14); assertEquals(away, alg.pickEndIndex(0,1)); assertEquals(14 , alg.pickEndIndex(0,-1)); assertEquals(9 , alg.pickEndIndex(1, 1)); assertEquals(1 , alg.pickEndIndex(1,-1)); assertEquals(14 , alg.pickEndIndex(2, 1)); assertEquals(7 , alg.pickEndIndex(2,-1)); assertEquals(0 , alg.pickEndIndex(3, 1)); assertEquals(9 , alg.pickEndIndex(3,-1)); } private void findContour( boolean black ) { GrayU8 binary = new GrayU8(image.width,image.height); GThresholdImageOps.threshold(image,binary,40,black); contour = BinaryImageOps.contour(binary, ConnectRule.FOUR,null).get(0).external; List<PointIndex_I32> corners = ShapeFittingOps.fitPolygon(contour,true,0.05,0.1,10); split = new GrowQueue_I32(corners.size()); for (int i = 0; i < corners.size(); i++) { split.add(corners.get(i).index); } } /** * Reproduces a bug that was found in the wild where the same index was returned in both directions. It was * caused by the distance being incorrectly computed. */ @Test public void pickEndIndex_bug0() { RefinePolygonCornersToImage<GrayU8> alg = new RefinePolygonCornersToImage<>(GrayU8.class); alg.setPixelsAway(6); alg.contour = new ArrayList<>(); alg.splits = new GrowQueue_I32(); for (int i = 0; i < 100; i++) { alg.contour.add(new Point2D_I32(1,1)); } alg.splits.add(80); alg.splits.add(95); alg.splits.add(3); alg.splits.add(20); int found0 = alg.pickEndIndex(0,-1); int found1 = alg.pickEndIndex(0,1); assertTrue(found0!=found1); // quick test in the other direction alg.splits.reset(); alg.splits.add(20); alg.splits.add(3); alg.splits.add(95); alg.splits.add(80); int foundA = alg.pickEndIndex(3,1); int foundB = alg.pickEndIndex(3,-1); assertTrue(foundA!=foundB); assertEquals(foundA,found0); assertEquals(foundB,found1); } }