All Downloads are FREE. Search and download functionalities are using the official Maven repository.

boofcv.alg.shapes.polygon.RefinePolygonCornersToImage Maven / Gradle / Ivy

/*
 * 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.shapes.corner.RefineCornerLinesToImage;
import boofcv.alg.shapes.edge.SnapToLineEdge;
import boofcv.misc.CircularIndex;
import boofcv.struct.distort.PixelTransform2_F32;
import boofcv.struct.image.ImageGray;
import georegression.struct.point.Point2D_F64;
import georegression.struct.point.Point2D_I32;
import georegression.struct.shapes.Polygon2D_F64;
import org.ddogleg.struct.GrowQueue_I32;

import java.util.List;

/**
 * Given a contour and the index of corner points it will fit the corners individually to sub-pixel precision
 * by locally fitting line segments near by each corner.  The length of each line segment is specified
 * by the number of pixels away it will traverse the contour.  It makes sure that the end point does not go
 * past the next corner.
 *
 * @author Peter Abeles
 */
public class RefinePolygonCornersToImage implements RefineBinaryPolygon {

	// number of pixels along the contour away should the end point be from the corner
	private int pixelsAway;

	// used to refine individual corners
	protected RefineCornerLinesToImage refineCorner;

	// storage for corner points
	protected Point2D_F64 cornerPt = new Point2D_F64();
	protected Point2D_F64 leftPt = new Point2D_F64();
	protected Point2D_F64 rightPt = new Point2D_F64();

	// reference ti input contour and corner indexes
	protected List contour;
	protected GrowQueue_I32 splits;

	/**
	 * Configures all parameters of the detector
	 *
	 * @param endPointDistance How many indexes away in the contour list should end points be.
	 * @param cornerOffset pixels this close to the corner will be ignored. Try 2
	 * @param maxLineSamples Number of points along the line which will be sampled.  try 10
	 * @param sampleRadius How far away from the line will it sample pixels.  ≥ 1
	 * @param maxIterations Maximum number of iterations it will perform.  Try 10
	 * @param convergeTolPixels When the corner changes less than this amount it will stop iterating. Try 1e-5
	 * @param maxCornerChangePixels maximum change in corner location allowed from previous iteration in pixels.  Try 2.0
	 * @param imageType Type of input image.
	 */
	public RefinePolygonCornersToImage(int endPointDistance,
									   double cornerOffset, int maxLineSamples, int sampleRadius,
									   int maxIterations, double convergeTolPixels,double maxCornerChangePixels,
									   Class imageType) {
		this.pixelsAway = endPointDistance;
		this.refineCorner = new RefineCornerLinesToImage<>(cornerOffset, maxLineSamples, sampleRadius, maxIterations,
				convergeTolPixels, maxCornerChangePixels, imageType);
	}

	/**
	 * Provides reasonable default values for all parameters that it can
	 *
	 * @param imageType Type of input image.
	 */
	public RefinePolygonCornersToImage(Class imageType) {
		this(12,2,10,1,10,1e-6,2,imageType);
	}

	/**
	 * Sets the input image
	 *
	 * @param input Input image-
	 */
	@Override
	public void setImage( T input ) {
		refineCorner.setImage(input);
	}

	@Override
	public void setLensDistortion(int width, int height, PixelTransform2_F32 distToUndist, PixelTransform2_F32 undistToDist) {
		this.refineCorner.getSnapToEdge().setTransform(undistToDist);
	}

	@Override
	public void clearLensDistortion() {
		this.refineCorner.getSnapToEdge().setTransform(null);
	}

	/**
	 * Refines the corner estimates in the contour.
	 *
	 * @param contour (Input) Shape's contour
	 * @param splits (Input) index of corners in the contour
	 * @param refined (Output) Refined polygon with sub-pixel accurate corners
	 * @return Number of corners it successfully optimized.
	 */
	@Override
	public boolean refine(Polygon2D_F64 input, List contour , GrowQueue_I32 splits, Polygon2D_F64 refined) {

		if( refined.size() != splits.size() )
			throw new IllegalArgumentException("Miss match between number of splits and polygon order");

		this.contour = contour;
		this.splits = splits;

		int numGood = 0;
		for (int cornerS = 0; cornerS < splits.size(); cornerS++) {
			int indexLeft  = pickEndIndex(cornerS,  1);
			int indexRight = pickEndIndex(cornerS, -1);

			Point2D_I32 contourCorner = contour.get(splits.get(cornerS));
			Point2D_I32 contourLeft = contour.get(indexLeft);
			Point2D_I32 contourRight = contour.get(indexRight);

			cornerPt.set(contourCorner.x,contourCorner.y);
			leftPt.set(contourLeft.x,contourLeft.y);
			rightPt.set(contourRight.x,contourRight.y);

			if( refineCorner.refine(cornerPt,leftPt,rightPt)) {
				refined.get(cornerS).set( refineCorner.getRefinedCorner());
				numGood++;
			} else {
				// use the original
				refined.get(cornerS).set(cornerPt);
			}
		}

		// allow partial success
		return numGood >= refined.size()-1;
	}

	/**
	 * Selects the index to use as the end point.  Makes sure that the index does not go past
	 * the next corner.
	 *
	 * @param cornerS index of corner in split list
	 * @param dir Specifies which corner is next.  can be -1 or 1.
	 */
	protected int pickEndIndex(int cornerS, int dir)
	{
		int cornerIndex = splits.get(cornerS);
		int endIndex0 = splits.get(CircularIndex.addOffset(cornerS, dir, splits.size));
		int endIndex1 = splits.get(CircularIndex.addOffset(cornerS, -dir, splits.size));

		// splits being in increasing or decreasing order isn't specified.  This make sure the correct
		// point is selected
		int dist0 = CircularIndex.distanceP(cornerIndex, endIndex0, contour.size());
		int dist1 = CircularIndex.distanceP(cornerIndex, endIndex1, contour.size());

		if( dir == 1 != dist1 > dist0 )
			dir = -dir;

		if( dir < 0 ) {
			int distance = CircularIndex.distanceP(endIndex0, cornerIndex, contour.size());
			distance = Math.min(distance,pixelsAway);
			return CircularIndex.addOffset(cornerIndex, -distance, contour.size());
		} else {
			int distance = CircularIndex.distanceP(cornerIndex, endIndex0, contour.size());
			distance = Math.min(distance,pixelsAway);
			return CircularIndex.addOffset(cornerIndex, distance, contour.size());
		}
	}

	public int getPixelsAway() {
		return pixelsAway;
	}

	public void setPixelsAway(int pixelsAway) {
		this.pixelsAway = pixelsAway;
	}

	public SnapToLineEdge getSnapToEdge() {
		return refineCorner.getSnapToEdge();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy