/*
* Copyright 2005, 2009 Cosmin Basca.
* e-mail: cosmin.basca@gmail.com
*
* This 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 2.1
* of the License, or (at your option) any later version.
*
* Please see COPYING for the complete licence.
*/
package robo.vision;
import java.awt.Rectangle;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import javax.media.jai.ImageLayout;
import javax.media.jai.UntiledOpImage;
/** Find lines in an image with a Hough transform.
*/
@SuppressWarnings("unchecked")
public class HoughCirclesOpImage extends UntiledOpImage
{
private int houghThreshold = 5, magnitudeThreshold = 5, greyOut = 255;
private double maxR = 12., minR = 4.;
/** Constructs HoughCirclesOpImage. Image dimensions are copied from the
* source image. The tile grid layout, SampleModel, and ColorModel may
* optionally be specified by an ImageLayout object
* @param source a RenderedImage
* @param layout an ImageLayout optionally containing the tile grid layout,
* SampleModel, and ColorModel or null.
*/
public HoughCirclesOpImage(RenderedImage source, ImageLayout layout, Integer edgeThreshold, Integer maximaThreshold, Integer outputIntensity, Integer minRadius, Integer maxRadius)
{
super(source, null, layout);
this.magnitudeThreshold = edgeThreshold.intValue();
this.houghThreshold = maximaThreshold.intValue();
this.greyOut = outputIntensity.intValue();
this.minR = (double) minRadius.intValue();
this.maxR = (double) maxRadius.intValue();
}
//public String toString();
//public boolean equals(Object obj);
//protected Object clone() throws CloneNotSupportedException;
//protected void finalize() throws Throwable;
/** Computes the destination image.
* @param src the source raster.
* @param dst the resultant image.
* @param destRect the rectangle within the OpImage to be computed */
protected void computeImage(Raster[] srcarr, WritableRaster dst, Rectangle destRect)
{
Raster src = srcarr[0];
RoboRaster source = new RoboRaster(src);
WritableRoboRaster dest = new WritableRoboRaster(dst);
int width = source.getWidth(), height = source.getHeight();
int length = width * height;
/* Implement op here */
double direction;
double minA = 0., maxA = width;
double minB = 0., maxB = width;
double rangeA = maxA - minA, rangeB = maxB - minB, rangeR = maxR - minR;
int accum[][] = new int[(int) rangeR + 1][(int)(rangeA * rangeB)];
int thresh[][] = new int[(int) rangeR + 1][(int)(rangeA * rangeB)];
int gradient;
dest.setRect(getSourceImage(0).getData());
for (int v = (int) maxR; v < height - (int) maxR; v++)
{
for (int u = (int) maxR; u < width - (int) maxR; u++)
{
//do Sobel operator
double sy = (source.grey(u - 1, v) * -2 + source.grey(u + 1, v) * 2 + source.grey(u - 1, v - 1) * -1 + source.grey(u + 1, v - 1) + source.grey(u - 1, v + 1) * -1 + source.grey(u + 1, v - 1)) / 8.0;
double sx = (source.grey(u - 1, v - 1) + source.grey(u, v - 1) * 2 + source.grey(u + 1, v - 1) + source.grey(u - 1, v + 1) * -1 + source.grey(u, v + 1) * -2 + source.grey(u + 1, v - 1) * -1) / 8.0;
//compute the gradient image from the original image
gradient = (int) Math.sqrt(sx * sx + sy * sy);
//compute the direction
if (sy == 0)
{
if (sx == 0)
direction = 0.;
else
direction = (sx > 0) ? 0.5 * Math.PI : -0.5 * Math.PI;
}
else if (sx == 0)
{
direction = 0.0;
}
else
{
direction = Math.atan(sy / sx);
}
direction += Math.PI / 2.0;
//force direction to go from PI/2 to -PI/2
if (direction > Math.PI / 2.0)
direction -= Math.PI;
if (direction < Math.PI / -2.0)
direction += Math.PI;
//if the mag is bigger than threshold, increment accumulator array
if (gradient > magnitudeThreshold)
{
double theta = direction;
for (double r = minR; r <= maxR; r += 1.)
{
double a = u - r * Math.cos(theta);
double b = v - r * Math.sin(theta);
try
{
accum[(int)(r - minR)][(int) b * (int) rangeA + (int) a]++;
}
catch (ArrayIndexOutOfBoundsException e)
{
}
}
}
}
}
//threshold the hough space; find the local maxima
for (int r = 0; r <= rangeR; r++)
{
for (int x = 0; x < rangeA * rangeB; x++)
{
if (x % rangeA > 1 && x % rangeA < rangeA - 1 && x > rangeA && x < rangeA * rangeB - rangeA && accum[r][x] >= houghThreshold && accum[r][x] > accum[r][x + 1] && accum[r][x] > accum[r][x - 1] &&
accum[r][x] > accum[r][x + (int) rangeA] && accum[r][x] > accum[r][x - (int) rangeA] && accum[r][x] > accum[r][x + (int) rangeA + 1] && accum[r][x] > accum[r][x - (int) rangeA + 1] &&
accum[r][x] > accum[r][x + (int) rangeA - 1] && accum[r][x] > accum[r][x - (int) rangeA - 1])
{
thresh[r][x] = 255;
}
}
}
for (int r = (int) minR; r <= maxR; r++)
{
for (int x = 0; x < rangeA * rangeB; x++)
{
if (thresh[r - (int) minR][x] != 0)
{
for (double theta = 0.; theta < 2.*Math.PI; theta += Math.PI / 30.)
{
int x0 = (int)(r * Math.cos(theta)) + (int)(x % rangeA);
int y0 = (int)(r * Math.sin(theta)) + (int)(x / rangeA);
if (y0 * width + x0 >= 0 && y0 * width + x0 < length)
{
dest.setGrey(x0, y0, greyOut);
}
}
}
}
}
}
}