com.github.ojil.algorithm.LinefitHoughVert Maven / Gradle / Ivy
Show all versions of ojil-core Show documentation
/*
* LinefitHoughVert.java
*
* Created on September 9, 2006, 1:15 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*
* Copyright 2007 by Jon A. Webb
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the Lesser GNU General Public License
* along with this program. If not, see .
*
*/
package com.github.ojil.algorithm;
import java.util.Enumeration;
import java.util.Vector;
import com.github.ojil.core.Error;
import com.github.ojil.core.Point;
/**
* Finds a line in an array of points using Hough transform. Not a pipeline
* stage. Returns the most likely line as y-slope and x-intercept through
* member access functions. The line search for should be oriented more or less
* vertically (within the slope range specified).
* Lines are traditionally represented in terms of slope m and x-intercept b, i.e.,
* y = m * x + b. In this code we are concerned with vertical or nearly vertical
* lines so we change the representation to x = m * y + b. b is then the x-intercept
* (y = 0 gives x = b) and m is the "y-slope". m = 0 gives a vertical line at x = b.
* @author webb
*/
public class LinefitHoughVert {
/** @var cHoughAccum the Hough accumulator array */
Integer[][] cHoughAccum;
/** @var cCount the number of points on the line that was found */
int cCount = 0;
/** @var cMaxSlope the maximum allowable slope, times 256 */
final int cMaxSlope;
/** @var cMaxX the maximum allowable x-intercept */
final int cMaxX;
/** @var cMinSlope the minimum allowable slope, times 256 */
final int cMinSlope;
/** @var cMinX the minimum allowable x-intercept */
final int cMinX;
/** @var cSlope the slope of the line that was found, times 256 */
int cSlope;
/** @var cSteps the number of steps to take from cMinSlope to cMaxSlope */
final int cSteps;
/** @var cXInt the x-intercept of the line that was found */
int cXInt;
/** Creates a new instance of LinefitHoughVert
*
* @param cMinX minimum X value
* @param cMaxX maximum X value
* @param cMinSlope minimum slope (multiplied by 256)
* @param cMaxSlope maximum slope (multiplied by 256)
* @param cSteps steps taken in Hough accumulator between minimum and
* maximum slope.
* @throws com.github.ojil.core.Error if X or slope range is empty, or
* cSteps is not positive.
*/
public LinefitHoughVert(
int cMinX,
int cMaxX,
int cMinSlope,
int cMaxSlope,
int cSteps) throws com.github.ojil.core.Error {
if (cMaxX < cMinX) {
throw new Error(
Error.PACKAGE.ALGORITHM,
ErrorCodes.PARAMETER_RANGE_NULL_OR_NEGATIVE,
new Integer(cMinX).toString(),
new Integer(cMaxX).toString(),
null);
}
this.cMinX = cMinX;
this.cMaxX = cMaxX;
if (cMaxSlope < cMinSlope) {
throw new Error(
Error.PACKAGE.ALGORITHM,
ErrorCodes.PARAMETER_RANGE_NULL_OR_NEGATIVE,
new Integer(cMinSlope).toString(),
new Integer(cMaxSlope).toString(),
null);
}
if (cSteps <= 0) {
throw new Error(
Error.PACKAGE.ALGORITHM,
ErrorCodes.PARAMETER_OUT_OF_RANGE,
new Integer(cSteps).toString(),
new Integer(1).toString(),
new Integer(Integer.MAX_VALUE).toString());
}
this.cMinSlope = cMinSlope;
this.cMaxSlope = cMaxSlope;
this.cSteps = cSteps;
}
/** Add a new point to the Hough accumulator array. We increment along the
* line in the array
* from (cMinSlope>>8, xIntStart) to (cMaxSlope>>8, xIntEnd), where
* xIntStart is the x-intercept assuming the slope is at the minimum,
* and xIntEnd is the x-intercept assuming the slope is maximal.
*
* @param p the point to add to the accumulator array
*/
private void addPoint(Point p) {
// Remember the line we are fitting is
// x = slope * x + intercept
// compute initial intercept. cMinSlope is the real slope minimum
// * 256.
int xIntStart = (p.getX() * 256 - p.getY() * this.cMinSlope) / 256;
// compute final intercept. cMaxSlope is the real slope maximum
// * 256.
int xIntEnd = (p.getX() * 256 - p.getY() * this.cMaxSlope) / 256;
/** work along the line from (0,xIntStart) to (cSteps,xIntEnd),
* incrementing the Hough accumulator.
*/
for (int slope = 0; slope < this.cSteps; slope++) {
int xInt = (xIntEnd - xIntStart) * slope / this.cSteps +
xIntStart;
/** check if the current position falls inside the Hough
* accumulator.
*/
if (xInt >= this.cMinX && xInt < this.cMaxX) {
this.cHoughAccum[slope][xInt-this.cMinX]++;
}
};
}
/** Find the peak in the Hough array. Updates cCount, cSlope, and cXInt.
*/
private void findPeak() {
this.cCount = Integer.MIN_VALUE;
for (int slope=0; slope this.cCount) {
this.cCount = this.cHoughAccum[slope][x];
this.cSlope = slope * (this.cMaxSlope - this.cMinSlope)
/ this.cSteps + this.cMinSlope;
this.cXInt = x + this.cMinX;
}
}
}
}
/** Returns the count of points on the line that was found.
*
* @return the point count.
*/
public int getCount() {
return this.cCount;
}
/** Returns the y-slope of the line that was found.
*
* @return the line slope (*256)
*/
public int getSlope() {
return this.cSlope;
}
/** Returns the x-intercept of the line that was found.
*
* @return the x-intercept.
*/
public int getX() {
return this.cXInt;
}
/** Finds the most likely line passing through the points in the Vector.
*
* @param points the input Vector of point positions
* @throws com.github.ojil.core.Error if points is not a Vector of
* point objects.
*/
public void push(Vector> points) throws com.github.ojil.core.Error {
/* create Hough accumulator */
this.cHoughAccum =
new Integer[this.cSteps][this.cMaxX-this.cMinX];
/* fill the Hough accumulator
*/
for (Enumeration> e = points.elements(); e.hasMoreElements();) {
Object o = e.nextElement();
if (!(o instanceof Point)) {
throw new Error(
Error.PACKAGE.ALGORITHM,
ErrorCodes.OBJECT_NOT_EXPECTED_TYPE,
o.toString(),
"Point",
null);
}
Point p = (Point) o;
addPoint(p);
}
findPeak(); // sets cXInt, cSlope, cCount for access by caller
this.cHoughAccum = null; // free memory
}
/** Return a string describing the current instance, giving the values
* of the constructor parameters.
*
* @return the string describing the current instance.
*/
public String toString() {
return super.toString() + "(" + this.cMinX + "," + this.cMaxX + "," + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
this.cMinSlope + "," + this.cMaxSlope + "," + this.cSteps + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}