/*
* Gray8ZeroCrossingHoriz.java
*
* Created on September 9, 2006, 3:19 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.Gray8Image;
/**
* Computes an array of zero crossing positions in the input gray image.
* Not a pipeline stage. The output is an array of arrays of exact positions of
* zero crossings, one array per row of the input image. A threshold
* parameter lets you set the zero crossing strength. The zero crossings
* are returned as an array of arrays of ints, one array per row, each
* integer referring to the position of a zero crossing in the row,
* multiplied by 256 so fractional positions can be represented.
* @author webb
*/
public class Gray8ZeroCrossingHoriz {
private int wThreshold;
/** Creates a new instance of Gray8ZeroCrossingHoriz. Gray8ZeroCrossingHoriz computes
* the horizontal zero crossings of a signed byte image.
*
* @param wThreshold -- the minimum strength for a zero crossing to
* be considered significant.
* @throws jjil.core.Error if the threshold is less than 0. Use
* 0 if you want all zero crossings.
*/
public Gray8ZeroCrossingHoriz(int wThreshold) throws jjil.core.Error {
setThreshold(wThreshold);
}
/** Returns the current threshold.
*
* @return the current threshold.
*/
public int getThreshold() {
return this.wThreshold;
}
/** We have two pixel values, the first negative, the second positive.
* Compute the position of the zero crossing on the line
* (0,negPix)->(256,posPix).
*
* @param negPix the negative pixel
* @param posPix the positive pixel
* @return the zero crossing position
*/
private int computeZeroCrossing(
int leftPos,
int leftPix,
int rightPos,
int rightPix) {
/* adjust x coordinate to increase scale and allow fractional positions
* to be calculated
*/
leftPos <<= 8;
rightPos <<= 8;
int b = (leftPix * rightPos - rightPix * leftPos)/(rightPos-leftPos);
/* Taking the line as y = mx + b we have
* y = leftPix when x = leftPos
* y = rightPix when x = rightPos
* Hence b = (leftPix*rightPos - rightPix*leftPos)/(rightPos-leftPos)
* and m = (rightPix - b) / rightPos
* y = 0 when x = -b / m
* So x = -b / ((rightPos - b) / rightPos)
* and reordering for accurate computation gives
*/
return b * rightPos / (b - rightPix);
}
/** Copy a vector of Integer objects into an array of ints.
*
* @param v the Vector of Integers
* @return the array of ints
*/
private int[] copyVectorToArray(Vector v) {
/* copy v into an array of ints
*/
int[] resultRow = new int[v.size()];
int elem=0;
for (Enumeration e = v.elements(); e.hasMoreElements();) {
resultRow[elem++] = ((Integer)e.nextElement()).intValue();
}
return resultRow;
}
/** Computes the zero crossings of an input gray image that are greater
* than a threshold.
*
* @param image the input image.
* @return an array of arrays of zero crossings. There is one array
* for each row in the input. The array elements are the zero crossing
* positions within the rows, multiplied by 256 so fractional values
* can be represented. A value of null means there were no zero crossings
* in the row.
*/
public int[][] push(Gray8Image image) {
byte[] data = image.getData();
int[][] result = new int[image.getHeight()][];
for (int i=0; i<image.getHeight(); i++) {
/* for holding the variable number of zero crossings
* as we find them.
*/
Vector v = new Vector();
int cLastPos = -1;
byte wLastEdge = 0;
for (int j=0; j<image.getWidth(); j++) {
byte bThisPix = data[i*image.getWidth()+j];
if (Math.abs(bThisPix) > this.wThreshold) {
/* we found an edge. see if the last edge was
* of opposite sign. This also tests if there
* was a last edge, because if there wasn't
* wLastEdge will be 0 and the test will fail.
*/
if (bThisPix*wLastEdge < 0) {
/* the last edge was of opposite sign.
* Compute the exact zero crossing.
*/
int posEdge =
computeZeroCrossing(cLastPos, wLastEdge,
j, bThisPix);
// test for negative or positive zero crossing
if (bThisPix < 0) posEdge = -posEdge;
v.addElement(new Integer(posEdge));
}
cLastPos = j;
wLastEdge = bThisPix;
}
}
if (!v.isEmpty()) {
result[i] = copyVectorToArray(v);
}
}
return result;
}
/**
* Returns a string describing this instance of Gray8ZeroCrossingHoriz,
* including the minimum strength parameter.
* @return the string -- looks like
* "jjil.algorithm.Gray8ZeroCrossingHoriz@xxx (number)"
*/
public String toString() {
return super.toString() + " (" + this.wThreshold + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
/** Changes the zero crossing threshold.
*
* @param wThreshold the new threshold.
* @throws jjil.core.Error if wThreshold is less than 0. Use 0
* if you want all zero crossings.
*/
public void setThreshold(int wThreshold) throws jjil.core.Error {
if (wThreshold < 0) {
throw new Error(
Error.PACKAGE.CORE,
ErrorCodes.THRESHOLD_NEGATIVE,
new Integer(wThreshold).toString(),
null,
null);
}
this.wThreshold = wThreshold;
}
}