/* * 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.ellipse; import boofcv.alg.distort.PixelTransformAffine_F32; import boofcv.alg.filter.binary.ThresholdImageOps; import boofcv.struct.distort.PixelTransform2_F32; import boofcv.struct.image.GrayU8; import georegression.struct.affine.Affine2D_F32; import georegression.struct.shapes.EllipseRotated_F64; 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 TestBinaryEllipseDetector { // edge intensity theshold for a valid ellipse public static int THRESHOLD = 20; /** * Simple test case with unambiguous detections */ @Test public void simpleCase() { List<EllipseRotated_F64> expected = new ArrayList<>(); expected.add( new EllipseRotated_F64(50,65,20,10,0.5)); expected.add( new EllipseRotated_F64(90,100,25,25,0)); GrayU8 image = TestBinaryEllipseDetectorPixel.renderEllipses(200,210, expected, 0); GrayU8 binary = image.createSameShape(); ThresholdImageOps.threshold(image,binary,30,true); BinaryEllipseDetector<GrayU8> alg = create(); alg.process(image, binary); List<EllipseRotated_F64> found = alg.getFoundEllipses().toList(); TestBinaryEllipseDetectorPixel.checkEquals(expected,found, 1.0, 0.1); } /** * Handle a situation where a shape should be filtered out based on its edge intensity */ @Test public void filterByEdge() { List<EllipseRotated_F64> expected = new ArrayList<>(); expected.add( new EllipseRotated_F64(50,65,20,10,0.5)); GrayU8 image = TestBinaryEllipseDetectorPixel.renderEllipses(200,210, expected, 0); GrayU8 binary = image.createSameShape(); ThresholdImageOps.threshold(image,binary,30,true); BinaryEllipseDetector<GrayU8> alg = create(); // pass once with it being a clear edge alg.process(image, binary); List<EllipseRotated_F64> found = alg.getFoundEllipses().toList(); TestBinaryEllipseDetectorPixel.checkEquals(expected,found, 1.0, 0.1); // now make the ellipse more dim so it shouldn't pass image = TestBinaryEllipseDetectorPixel.renderEllipses(200,210, expected, 255-THRESHOLD+5); alg.process(image, binary); assertEquals(0,alg.getFoundEllipses().size()); } /** * Input image is distorted */ @Test public void distortedImage() { List<EllipseRotated_F64> original = new ArrayList<>(); original.add( new EllipseRotated_F64(50,65,20,10,0.5)); original.add( new EllipseRotated_F64(90,100,25,25,0)); GrayU8 image = TestBinaryEllipseDetectorPixel.renderEllipses(200,210, original, 0); GrayU8 binary = image.createSameShape(); ThresholdImageOps.threshold(image,binary,30,true); BinaryEllipseDetector<GrayU8> alg = create(); PixelTransform2_F32 distToUndist = new PixelTransformAffine_F32(new Affine2D_F32(1,0,0,1,5,8)); PixelTransform2_F32 undistToDist = new PixelTransformAffine_F32(new Affine2D_F32(1,0,0,1,-5,-8)); alg.setLensDistortion(distToUndist, undistToDist); alg.process(image, binary); // adjust the ellipses using the transform List<EllipseRotated_F64> expected = new ArrayList<>(); for( EllipseRotated_F64 o : original ) { EllipseRotated_F64 e = new EllipseRotated_F64(o); e.center.x += 5; e.center.y += 8; expected.add( e ); } List<EllipseRotated_F64> found = alg.getFoundEllipses().toList(); TestBinaryEllipseDetectorPixel.checkEquals(expected,found, 1.0, 0.1); } /** * Turn off refinement and manually invoke it */ @Test public void autoRefineToggle() { List<EllipseRotated_F64> expected = new ArrayList<>(); expected.add( new EllipseRotated_F64(50,65,20,10,0.5)); expected.add( new EllipseRotated_F64(90,100,25,25,0)); GrayU8 image = TestBinaryEllipseDetectorPixel.renderEllipses(200,210, expected, 0); GrayU8 binary = image.createSameShape(); ThresholdImageOps.threshold(image,binary,30,true); BinaryEllipseDetector<GrayU8> alg = create(); alg.setAutoRefine(false); alg.process(image, binary); List<EllipseRotated_F64> found = alg.getFoundEllipses().toList(); List<EllipseRotated_F64> refined = new ArrayList<>(); for( EllipseRotated_F64 f : found ) { EllipseRotated_F64 r = new EllipseRotated_F64(f); assertTrue(alg.refine(r)); assertTrue( f.a != r.a ); assertTrue( f.b != r.b ); assertTrue( f.phi != r.phi ); refined.add( r ); } TestBinaryEllipseDetectorPixel.checkEquals(refined,found, 1.0, 0.1); } private static BinaryEllipseDetector<GrayU8> create() { BinaryEllipseDetectorPixel ellipseDetector = new BinaryEllipseDetectorPixel(); SnapToEllipseEdge<GrayU8> ellipseRefiner = new SnapToEllipseEdge<>(20,2,GrayU8.class); EdgeIntensityEllipse<GrayU8> intensityCheck = new EdgeIntensityEllipse<>(2.0,20,THRESHOLD,GrayU8.class); return new BinaryEllipseDetector<>(ellipseDetector,ellipseRefiner,intensityCheck,GrayU8.class); } }