Example Fit Polygon

From BoofCV
Revision as of 17:14, 14 April 2013 by Peter (talk | contribs)
Jump to navigationJump to search

Demonstration for how to fit a polygon to object contours and edges. The input contours can be found from binary blobs and the edge sequence from Canny edge detector. This is often a useful preprocessing step before applying a higher level image processing algorithm.

Example Code:

Concepts:

  • Object contours/edges
  • Shape fitting

Relevant Applets:

Example Code

/**
 * Demonstration of how to convert a point sequence describing an objects outline/contour into a sequence of line
 * segments.  Useful when analysing shapes such as squares and triangles or when trying to simply the low level
 * pixel output.
 *
 * @author Peter Abeles
 */
public class ExampleFitPolygon {

	// Polynomial fitting tolerances
	static double toleranceDist = 2;
	static double toleranceAngle= Math.PI/10;

	/**
	 * Fits a polygons the found contours around binary blobs.  This demonstrates how it can be used to handle
	 * connected loops of points
	 */
	public static void fitBinaryImage(ImageFloat32 input) {

		ImageUInt8 binary = new ImageUInt8(input.width,input.height);
		BufferedImage polygon = new BufferedImage(input.width,input.height,BufferedImage.TYPE_INT_RGB);

		// the mean pixel value is often a reasonable threshold when creating a binary image
		double mean = ImageStatistics.mean(input);

		// create a binary image by thresholding
		ThresholdImageOps.threshold(input, binary, (float) mean, true);

		// reduce noise with some filtering
		ImageUInt8 filtered = BinaryImageOps.erode8(binary,null);
		filtered = BinaryImageOps.dilate8(filtered, null);

		// Find the contour around the shapes
		List<Contour> contours = BinaryImageOps.contour(filtered,8,null);

		// Fit a polygon to each shape and draw the results
		Graphics2D g2 = polygon.createGraphics();
		g2.setStroke(new BasicStroke(2));

		for( Contour c : contours ) {
			List<PointIndex_I32> vertexes = ShapeFittingOps.fitPolygon(c.external,true,
					toleranceDist,toleranceAngle,100);

			g2.setColor(Color.RED);
			VisualizeShapes.drawPolygon(vertexes,true,g2);

			g2.setColor(Color.BLUE);
			for( List<Point2D_I32> internal : c.internal ) {
				vertexes = ShapeFittingOps.fitPolygon(internal,true,toleranceDist,toleranceAngle,100);
				VisualizeShapes.drawPolygon(vertexes,true,g2);
			}
		}

		ShowImages.showWindow(polygon,"Binary Blob Contours");
	}

	/**
	 * Fits a sequence of line-segments into a sequence of points found using the Canny edge detector.  In this case
	 * the points are not connected in a loop. The canny detector produces a more complex tree and the fitted
	 * points can be a bit noisy compared to the others.
	 */
	public static void fitCannyEdges( ImageFloat32 input ) {

		BufferedImage displayImage = new BufferedImage(input.width,input.height,BufferedImage.TYPE_INT_RGB);

		// Finds edges inside the image
		CannyEdge<ImageFloat32,ImageFloat32> canny =
				FactoryEdgeDetectors.canny(2, true, true, ImageFloat32.class, ImageFloat32.class);

		canny.process(input,0.1f,0.3f,null);
		List<EdgeContour> contours = canny.getContours();

		Graphics2D g2 = displayImage.createGraphics();
		g2.setStroke(new BasicStroke(2));

		// used to select colors for each line
		Random rand = new Random(234);

		for( EdgeContour e : contours ) {
			g2.setColor(new Color(rand.nextInt()));

			for(EdgeSegment s : e.segments ) {
				// fit line segments to the point sequence.  Note that loop is false
				List<PointIndex_I32> vertexes = ShapeFittingOps.fitPolygon(s.points,false,
						toleranceDist,toleranceAngle,100);

				VisualizeShapes.drawPolygon(vertexes, false, g2);
			}
		}

		ShowImages.showWindow(displayImage,"Canny Trace");
	}

	/**
	 * Detects contours inside the binary image generated by canny.  Only the external contour is relevant. Often
	 * easier to deal with than working with Canny edges directly.
	 */
	public static void fitCannyBinary( ImageFloat32 input ) {

		BufferedImage displayImage = new BufferedImage(input.width,input.height,BufferedImage.TYPE_INT_RGB);
		ImageUInt8 binary = new ImageUInt8(input.width,input.height);

		// Finds edges inside the image
		CannyEdge<ImageFloat32,ImageFloat32> canny =
				FactoryEdgeDetectors.canny(2, false, true, ImageFloat32.class, ImageFloat32.class);

		canny.process(input,0.1f,0.3f,binary);

		List<Contour> contours = BinaryImageOps.contour(binary, 8, null);

		Graphics2D g2 = displayImage.createGraphics();
		g2.setStroke(new BasicStroke(2));

		// used to select colors for each line
		Random rand = new Random(234);

		for( Contour c : contours ) {
			List<PointIndex_I32> vertexes = ShapeFittingOps.fitPolygon(c.external,true,
					toleranceDist,toleranceAngle,100);

			g2.setColor(new Color(rand.nextInt()));
			VisualizeShapes.drawPolygon(vertexes,true,g2);
		}

		ShowImages.showWindow(displayImage,"Canny Contour");
	}


	public static void main( String args[] ) {
		// load and convert the image into a usable format
		BufferedImage image = UtilImageIO.loadImage("../data/applet/shapes02.png");
		ImageFloat32 input = ConvertBufferedImage.convertFromSingle(image, null, ImageFloat32.class);

		ShowImages.showWindow(image,"Original");

		fitCannyEdges(input);
		fitCannyBinary(input);
		fitBinaryImage(input);
	}
}