/* * LinefitHoughHoriz.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 <http://www.gnu.org/licenses/>. * */ package jjil.algorithm; import java.util.Enumeration; import java.util.Vector; import jjil.core.Error; import jjil.core.Point; /** * Finds a line in an array of points using Hough transform. Not a pipeline * stage. Returns the most likely line as slope and Y-intercept through * member access functions. The line search for must be oriented more or less * horizontally (within the slope range specified). * @author webb */ public class LinefitHoughHoriz { /** @var cHoughAccum the Hough accumulator array */ int[][] 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 cMaxY the maximum allowable y-intercept */ final int cMaxY; /** @var cMinSlope the minimum allowable slope, times 256 */ final int cMinSlope; /** @var cMinY the minimum allowable y-intercept */ final int cMinY; /** @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 cYInt the y-intercept of the line that was found */ int cYInt; /** Creates a new instance of LinefitHoughHoriz * * @param cMinY minimum Y value * @param cMaxY maximum Y 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 jjil.core.Error if Y or slope range is empty, or * cSteps is not positive. */ public LinefitHoughHoriz( int cMinY, int cMaxY, int cMinSlope, int cMaxSlope, int cSteps) throws jjil.core.Error { if (cMaxY < cMinY) { throw new Error( Error.PACKAGE.ALGORITHM, ErrorCodes.PARAMETER_RANGE_NULL_OR_NEGATIVE, new Integer(cMinY).toString(), new Integer(cMaxY).toString(), null); } this.cMinY = cMinY; this.cMaxY = cMaxY; 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, yIntStart) to (cMaxSlope>>8, yIntEnd), where * yIntStart is the y-intercept assuming the slope is at the minimum, * and yIntEnd is the y-intercept assuming the slope is maximal. * * @param p the point to add to the accumulator array */ private void addPoint(Point p) { /** work along the line from (0,yIntStart) to (cSteps,yIntEnd), * incrementing the Hough accumulator. */ int nStep = (this.cMaxSlope - this.cMinSlope) / this.cSteps; int nSlopePos = 0; for (int slope = this.cMinSlope; nSlopePos < this.cSteps; slope+=nStep, nSlopePos++) { int yInt = (p.getY() * 256 - p.getX() * slope) / 256; /** check if the current position falls inside the Hough * accumulator. */ if (yInt >= this.cMinY && yInt < this.cMaxY) { this.cHoughAccum[nSlopePos][yInt-this.cMinY]++; } }; } /** Find the peak in the Hough array. Updates cCount, cSlope, and cYInt. */ private void findPeak() { this.cCount = Integer.MIN_VALUE; for (int slope=0; slope<this.cSteps; slope++) { for (int y=0; y<this.cMaxY-this.cMinY; y++) { if (this.cHoughAccum[slope][y] > this.cCount) { this.cCount = this.cHoughAccum[slope][y]; this.cSlope = slope * (this.cMaxSlope - this.cMinSlope) / this.cSteps + this.cMinSlope; this.cYInt = y + this.cMinY; } } } } /** Returns the count of points on the line that was found. * * @return the point count. */ public int getCount() { return this.cCount; } /** Returns the slope of the line that was found. * * @return the line slope (*256) */ public int getSlope() { return this.cSlope; } /** Returns the y-intercept of the line that was found. * * @return the y-intercept. */ public int getY() { return this.cYInt; } /** Finds the most likely line passing through the points in the Vector. * * @param points the input Vector of point positions * @throws jjil.core.Error if points is not a Vector of * point objects. */ public void push(Vector points) throws jjil.core.Error { /* create Hough accumulator */ this.cHoughAccum = new int[this.cSteps][this.cMaxY-this.cMinY]; /* 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 cYInt, 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.cMinY + "," + this.cMaxY + "," + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.cMinSlope + "," + this.cMaxSlope + "," + this.cSteps + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } }