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

boofcv.alg.shapes.corner.RefineCornerLinesToImage 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.corner;

import boofcv.alg.shapes.edge.SnapToLineEdge;
import boofcv.struct.distort.PixelTransform2_F32;
import boofcv.struct.image.ImageGray;
import georegression.geometry.UtilLine2D_F64;
import georegression.metric.Intersection2D_F64;
import georegression.struct.line.LineGeneral2D_F64;
import georegression.struct.point.Point2D_F64;
import georegression.struct.point.Vector2D_F64;

/**
 * 

* Refines the estimate of a corner. A corner is defined as the intersection of two straight edges. The inside * edge is either all darker or lighter than the background. The optimization function when refining the edge * seeks to maximize the difference between the inside and outside of the edge. Internally {@link SnapToLineEdge} is * used to perform this optimization. *

* *

The corner is defined using three points. One corner and two line end points. One is to the left of the right * line. Left is defined as counter-clockwise. Optimization is done by fitting a line with that initial seed. It * is then iteratively updated by finding new end points with the new line that are the same distance away. Pixels * near the corner are excluded from the optimization because the edge is less clear at that point. *

* *

For input polygons which are in undistorted coordinates by with a distorted image call {@link #getSnapToEdge()} * and invoke {@link SnapToLineEdge#setTransform(PixelTransform2_F32)}}.

* * @author Peter Abeles */ public class RefineCornerLinesToImage { // How far away from a corner will it sample the line double cornerOffset; // maximum number of points it will sample along the line int maxLineSamples; // maximum number of iterations private int maxIterations; // convergence tolerance in pixels private double convergeTolPixels; // fits the line to the edge private SnapToLineEdge snapToEdge; //---------- storage for local work space // adjusted corner points which have been offset from the true corners private Point2D_F64 adjA = new Point2D_F64(); // storage for the previous corner and the refined corner Point2D_F64 previous = new Point2D_F64(); Point2D_F64 refined = new Point2D_F64(); // line end point workspace Point2D_F64 _endLeft = new Point2D_F64(); Point2D_F64 _endRight = new Point2D_F64(); // storage for fitted lines LineGeneral2D_F64 lineLeft = new LineGeneral2D_F64(); LineGeneral2D_F64 lineRight = new LineGeneral2D_F64(); // storage for original pointing vector for determining sign of slope Vector2D_F64 directionLeft = new Vector2D_F64(); Vector2D_F64 directionRight = new Vector2D_F64(); // the input image protected T image; Class imageType; // the maximum change in corner location allowed from previous iteration // used to prevent divergence double maxCornerChange; /** * Constructor which provides full access to all parameters. See code documents * value a description of these variables. * * @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 maxCornerChange maximum change in corner location allowed from previous iteration in pixels. Try 2.0 */ public RefineCornerLinesToImage(double cornerOffset, int maxLineSamples, int sampleRadius, int maxIterations, double convergeTolPixels,double maxCornerChange, Class imageType) { if( sampleRadius < 1 ) throw new IllegalArgumentException("Sample radius must be >= 1 to work"); this.cornerOffset = cornerOffset; this.maxIterations = maxIterations; this.convergeTolPixels = convergeTolPixels; this.snapToEdge = new SnapToLineEdge<>(maxLineSamples, sampleRadius, imageType); this.maxLineSamples = maxLineSamples; this.imageType = imageType; this.maxCornerChange = maxCornerChange; } /** * Simplified constructor which uses reasonable default values for most variables * @param imageType Type of input image it processes */ public RefineCornerLinesToImage( Class imageType) { this(2.0, 10, 2, 10, 1e-5,4, imageType); } /** * Sets the image which is going to be processed. If a transform is to be used * {@link SnapToLineEdge#setTransform} should be called before this. */ public void setImage(T image) { this.image = image; this.snapToEdge.setImage(image); } /** * Refines the fit a polygon by snapping it to the edges. * * @param corner (input) Initial estimate of polygon corner. Not modified. * @param endLeft (input) End point of left line. Not modified. * @param endRight (input) End point of right line. Not modified. */ public boolean refine( Point2D_F64 corner , Point2D_F64 endLeft , Point2D_F64 endRight ) { // local copy to avoid modifying the input _endLeft.set(endLeft); _endRight.set(endRight); // save the pointing direction from corner to end point directionLeft.minus(endLeft, corner); directionLeft.normalize(); directionRight.minus(endRight, corner); directionRight.normalize(); // original line length is used to update end point location double lengthLeft = corner.distance(endLeft); double lengthRight = corner.distance(endRight); // if the lines are too small exit if( lengthLeft < 2*cornerOffset || lengthRight < 2*cornerOffset) return false; // compute the number of points it will actually sample. Doing too many is pointless int samplesLeft = Math.min(maxLineSamples,(int)Math.ceil(lengthLeft)); int samplesRight = Math.min(maxLineSamples,(int)Math.ceil(lengthRight)); refined.set(corner); previous.set(corner); // pixels squares is faster to compute double convergeTol = convergeTolPixels*convergeTolPixels; for (int iteration = 0; iteration < maxIterations; iteration++) { // snapping to edge can fail when its along the image border. However, the corner can still // be optimized as long as one of the edges isn't along the image border boolean failedAlready = false; snapToEdge.setLineSamples(samplesLeft); if( !optimize(refined, _endLeft, lineLeft) ) { UtilLine2D_F64.convert(refined,_endLeft,lineLeft); failedAlready = true; } snapToEdge.setLineSamples(samplesRight); if( !optimize(refined,_endRight, lineRight) ) { if( failedAlready ) return false; UtilLine2D_F64.convert(refined,_endRight,lineRight); } // intersect the two lines to fine the new corner if( null == Intersection2D_F64.intersection(lineLeft,lineRight,refined) ) return false; // see if it has converged if( refined.distance2(previous) < convergeTol ) { break; } else if( refined.distance2(previous) > maxCornerChange*maxCornerChange ) { // it diverged, roll back and abort refined.set(previous); break; } // find new line end points updateEndPoints(lengthLeft, lengthRight); // save the current corner previous.set(refined); } return true; } /** * Sets the location of the end point to be along the estimated line. Need to determine which direction * to do the addition since the line's slope can point in either direction. */ private void updateEndPoints(double lengthLeft, double lengthRight) { double sgn; lineLeft.normalize(); if( directionLeft.x*lineLeft.B - directionLeft.y*lineLeft.A > 0 ) sgn = 1; else sgn = -1; _endLeft.x = refined.x + sgn*lineLeft.B*lengthLeft; _endLeft.y = refined.y - sgn*lineLeft.A*lengthLeft; lineRight.normalize(); if( directionRight.x*lineRight.B - directionRight.y*lineRight.A > 0 ) sgn = 1; else sgn = -1; _endRight.x = refined.x + sgn*lineRight.B*lengthRight; _endRight.y = refined.y - sgn*lineRight.A*lengthRight; } /** * Fits a line defined by the two points. When fitting the line the weight of the edge is used to determine * how influential the point is * @param a Corner point in image coordinates. * @param b Corner point in image coordinates. * @param found (output) Line in image coordinates * @return true if successful or false if it failed */ protected boolean optimize( Point2D_F64 a , Point2D_F64 b , LineGeneral2D_F64 found) { double slopeX = (b.x - a.x); double slopeY = (b.y - a.y); double r = Math.sqrt(slopeX*slopeX + slopeY*slopeY); // vector of unit length pointing in direction of the slope double unitX = slopeX/r; double unitY = slopeY/r; // offset from corner because the gradient because unstable around there adjA.x = a.x + unitX*cornerOffset; adjA.y = a.y + unitY*cornerOffset; return snapToEdge.refine(adjA,b,found); } public Point2D_F64 getRefinedCorner() { return refined; } public Point2D_F64 getRefinedEndLeft() { return _endLeft; } public Point2D_F64 getRefinedEndRight() { return _endRight; } public SnapToLineEdge getSnapToEdge() { return snapToEdge; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy