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

org.jgrasstools.gears.modules.r.houghes.HoughCircles Maven / Gradle / Ivy

package org.jgrasstools.gears.modules.r.houghes;
/** houghCircles_.java:
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 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 General Public License for more details.
 
 @author Hemerson Pistori ([email protected]) and Eduardo Rocha Costa
 @created 18 de Mar�o de 2004
 
 The Hough Transform implementation was based on 
 Mark A. Schulze applet (http://www.markschulze.net/)
 
*/

//package sigus.templateMatching;
//import sigus.*;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;
import javax.media.jai.iterator.RandomIter;
import javax.media.jai.iterator.RandomIterFactory;

import com.vividsolutions.jts.geom.Coordinate;

/**
 * A Hough Transform implementation.
 * 
 * @author Hemerson Pistori ([email protected]) and Eduardo Rocha Costa
 * @author Mark A. Schulze applet (http://www.markschulze.net/)
 * @author Andrea Antonello (www.hydrologis.com)
 */
public class HoughCircles {

    public int radiusMin; // Find circles with radius grater or equal radiusMin
    public int radiusMax; // Find circles with radius less or equal radiusMax
    public int radiusInc; // Increment used to go from radiusMin to radiusMax
    public int maxCircles; // Numbers of circles to be found
    public int threshold = -1; // An alternative to maxCircles. All circles with
    // a value in the hough space greater then threshold are marked. Higher thresholds
    // results in fewer circles.
    byte imageValues[]; // Raw image (returned by ip.getPixels())
    public int width; // Hough Space width (depends on image width)
    public int height; // Hough Space heigh (depends on image height)
    public int depth; // Hough Space depth (depends on radius interval)
    public int offset; // Image Width
    public int offx; // ROI x offset
    public int offy; // ROI y offset
    private int vectorMaxSize = 500;
    boolean useThreshold = false;
    int lut[][][]; // LookUp Table for rsin e rcos values
    private BufferedImage raster;

    public static void main( String[] args ) throws Exception {

        int[] i = {10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30};
        for( int index : i ) {
            String inImage = "/home/hydrologis/data/rilievo_tls/slice_" + index + ".0rgb.png";
            String outImage = "/home/hydrologis/data/rilievo_tls/slice_" + index + ".0bw_out.png";

            BufferedImage src = ImageIO.read(new File(inImage));
            HoughCircles h = new HoughCircles(src, 10, 40, 1, 300);
            h.run();
            ImageIO.write(src, "png", new File(outImage));
        }

    }

    public HoughCircles( BufferedImage raster, int radiusMin, int radiusMax, int radiusIncrement, int circleCount ) {
        this.raster = raster;
        this.radiusMin = radiusMin;
        this.radiusMax = radiusMax;
        maxCircles = circleCount;
        radiusInc = radiusIncrement;
        depth = ((radiusMax - radiusMin) / radiusInc) + 1;

        // TODO support threshold
        // threshold = (int) gd.getNextNumber();
        // if (maxCircles > 0) {
        // useThreshold = false;
        // threshold = -1;
        // } else {
        // useThreshold = true;
        // if (threshold < 0) {
        // IJ.showMessage("Threshold must be greater than 0");
        // return (false);
        // }
        // }
    }

    public void run() {
        offx = 0;
        offy = 0;
        width = raster.getWidth();
        height = raster.getHeight();
        offset = width;

        imageValues = new byte[width * height];
        int count = 0;
        RandomIter renderedImageIterator = RandomIterFactory.create(raster, null);
        for( int r = 0; r < height; r++ ) {
            for( int c = 0; c < width; c++ ) {
                int sample = renderedImageIterator.getSample(c, r, 0);
                imageValues[count++] = (byte) sample;
            }
        }
        renderedImageIterator.done();

        double[][][] houghValues = houghTransform();

        // Create image View for Hough Transform.
        // ImageProcessor newip = new ByteProcessor(width, height);
        // byte[] newpixels = (byte[]) newip.getPixels();
        // createHoughPixels(houghValues, newpixels);
        //
        // // Create image View for Marked Circles.
        // ImageProcessor circlesip = new ByteProcessor(width, height);
        // byte[] circlespixels = (byte[]) circlesip.getPixels();

        // Mark the center of the found circles in a new image
        // if (useThreshold)
        // getCenterPointsByThreshold(threshold);
        // else
        Coordinate[] centerPoints = getCenterPoints(houghValues, maxCircles);
        // drawCircles(houghValues, circlespixels);

        Graphics2D g2d = (Graphics2D) raster.getGraphics();
        g2d.setColor(Color.red);
        g2d.setStroke(new BasicStroke(2));
        for( Coordinate point : centerPoints ) {
            int size = (int) point.z * 2;
            g2d.drawOval((int) point.x - size / 2, (int) point.y + -size / 2, size, size);
        }
    }

    /** The parametric equation for a circle centered at (a,b) with
        radius r is:

    a = x - r*cos(theta)
    b = y - r*sin(theta)

    In order to speed calculations, we first construct a lookup
    table (lut) containing the rcos(theta) and rsin(theta) values, for
    theta varying from 0 to 2*PI with increments equal to
    1/8*r. As of now, a fixed increment is being used for all
    different radius (1/8*radiusMin). This should be corrected in
    the future.

    Return value = Number of angles for each radius
       
    */
    private int buildLookUpTable() {

        int i = 0;
        int incDen = Math.round(8F * radiusMin); // increment denominator

        lut = new int[2][incDen][depth];

        for( int radius = radiusMin; radius <= radiusMax; radius = radius + radiusInc ) {
            i = 0;
            for( int incNun = 0; incNun < incDen; incNun++ ) {
                double angle = (2 * Math.PI * (double) incNun) / (double) incDen;
                int indexR = (radius - radiusMin) / radiusInc;
                int rcos = (int) Math.round((double) radius * Math.cos(angle));
                int rsin = (int) Math.round((double) radius * Math.sin(angle));
                if ((i == 0) | (rcos != lut[0][i][indexR]) & (rsin != lut[1][i][indexR])) {
                    lut[0][i][indexR] = rcos;
                    lut[1][i][indexR] = rsin;
                    i++;
                }
            }
        }

        return i;
    }

    private double[][][] houghTransform() {
        int lutSize = buildLookUpTable();
        double[][][] houghValues = new double[width][height][depth];
        int k = width - 1;
        int l = height - 1;

        for( int y = 1; y < l; y++ ) {
            for( int x = 1; x < k; x++ ) {
                for( int radius = radiusMin; radius <= radiusMax; radius = radius + radiusInc ) {
                    if (imageValues[(x + offx) + (y + offy) * offset] != 0) {// Edge pixel found
                        int indexR = (radius - radiusMin) / radiusInc;
                        for( int i = 0; i < lutSize; i++ ) {
                            int a = x + lut[1][i][indexR];
                            int b = y + lut[0][i][indexR];
                            if ((b >= 0) & (b < height) & (a >= 0) & (a < width)) {
                                houghValues[a][b][indexR] += 1;
                            }
                        }
                    }
                }
            }
        }

        return houghValues;

    }

    // Convert Values in Hough Space to an 8-Bit Image Space.
    private void createHoughPixels( double[][][] houghValues, byte houghPixels[] ) {
        double d = -1D;
        for( int j = 0; j < height; j++ ) {
            for( int k = 0; k < width; k++ ) {
                if (houghValues[k][j][0] > d) {
                    d = houghValues[k][j][0];
                }
            }
        }
        for( int l = 0; l < height; l++ ) {
            for( int i = 0; i < width; i++ ) {
                houghPixels[i + l * width] = (byte) Math.round((houghValues[i][l][0] * 255D) / d);
            }
        }
    }

    // Draw the circles found in the original image.
    public void drawCircles( double[][][] houghValues, byte[] circlespixels ) {

        // Copy original input pixels into output
        // circle location display image and
        // combine with saturation at 100
        int roiaddr = 0;
        for( int y = offy; y < offy + height; y++ ) {
            for( int x = offx; x < offx + width; x++ ) {
                // Copy;
                circlespixels[roiaddr] = imageValues[x + offset * y];
                // Saturate
                if (circlespixels[roiaddr] != 0)
                    circlespixels[roiaddr] = 100;
                else
                    circlespixels[roiaddr] = 0;
                roiaddr++;
            }
        }
        // Copy original image to the circlespixels image.
        // Changing pixels values to 100, so that the marked
        // circles appears more clear. Must be improved in
        // the future to show the resuls in a colored image.
        // for(int i = 0; i < width*height ;++i ) {
        // if(imageValues[i] != 0 )
        // if(circlespixels[i] != 0 )
        // circlespixels[i] = 100;
        // else
        // circlespixels[i] = 0;
        // }
        // if (centerPoints == null) {
        // if (useThreshold)
        // getCenterPointsByThreshold(threshold);
        // else
        Coordinate[] centerPoints = getCenterPoints(houghValues, maxCircles);
        // }
        byte cor = -1;
        // Redefine these so refer to ROI coordinates exclusively
        int offset = width;
        int offx = 0;
        int offy = 0;

        for( int l = 0; l < maxCircles; l++ ) {
            int i = (int) centerPoints[l].x;
            int j = (int) centerPoints[l].y;
            // Draw a gray cross marking the center of each circle.
            for( int k = -10; k <= 10; ++k ) {
                int p = (j + k + offy) * offset + (i + offx);
                if (!outOfBounds(j + k + offy, i + offx))
                    circlespixels[(j + k + offy) * offset + (i + offx)] = cor;
                if (!outOfBounds(j + offy, i + k + offx))
                    circlespixels[(j + offy) * offset + (i + k + offx)] = cor;
            }
            for( int k = -2; k <= 2; ++k ) {
                if (!outOfBounds(j - 2 + offy, i + k + offx))
                    circlespixels[(j - 2 + offy) * offset + (i + k + offx)] = cor;
                if (!outOfBounds(j + 2 + offy, i + k + offx))
                    circlespixels[(j + 2 + offy) * offset + (i + k + offx)] = cor;
                if (!outOfBounds(j + k + offy, i - 2 + offx))
                    circlespixels[(j + k + offy) * offset + (i - 2 + offx)] = cor;
                if (!outOfBounds(j + k + offy, i + 2 + offx))
                    circlespixels[(j + k + offy) * offset + (i + 2 + offx)] = cor;
            }
        }
    }

    private boolean outOfBounds( int y, int x ) {
        if (x >= width)
            return (true);
        if (x <= 0)
            return (true);
        if (y >= height)
            return (true);
        if (y <= 0)
            return (true);
        return (false);
    }

    /** Search for a fixed number of circles.

    @param maxCircles The number of circles that should be found.  
     * @param houghValues 
    */
    private Coordinate[] getCenterPoints( double[][][] houghValues, int maxCircles ) {

        Coordinate[] centerPoints = new Coordinate[maxCircles];
        int xMax = 0;
        int yMax = 0;
        int rMax = 0;

        for( int c = 0; c < maxCircles; c++ ) {
            double counterMax = -1;
            for( int radius = radiusMin; radius <= radiusMax; radius = radius + radiusInc ) {

                int indexR = (radius - radiusMin) / radiusInc;
                for( int y = 0; y < height; y++ ) {
                    for( int x = 0; x < width; x++ ) {
                        if (houghValues[x][y][indexR] > counterMax) {
                            counterMax = houghValues[x][y][indexR];
                            xMax = x;
                            yMax = y;
                            rMax = radius;
                        }
                    }

                }
            }

            centerPoints[c] = new Coordinate(xMax, yMax, rMax);

            clearNeighbours(houghValues, xMax, yMax, rMax);
        }
        return centerPoints;
    }

    /** Search circles having values in the hough space higher than a threshold

    @param threshold The threshold used to select the higher point of Hough Space
    */
    // private void getCenterPointsByThreshold( int threshold ) {
    //
    // centerPoint = new Point[vectorMaxSize];
    // int xMax = 0;
    // int yMax = 0;
    // int countCircles = 0;
    //
    // for( int radius = radiusMin; radius <= radiusMax; radius = radius + radiusInc ) {
    // int indexR = (radius - radiusMin) / radiusInc;
    // for( int y = 0; y < height; y++ ) {
    // for( int x = 0; x < width; x++ ) {
    //
    // if (houghValues[x][y][indexR] > threshold) {
    //
    // if (countCircles < vectorMaxSize) {
    //
    // centerPoint[countCircles] = new Point(x, y);
    //
    // clearNeighbours(xMax, yMax, radius);
    //
    // ++countCircles;
    // } else
    // break;
    // }
    // }
    // }
    // }
    //
    // maxCircles = countCircles;
    // }

    /** Clear, from the Hough Space, all the counter that are near (radius/2) a previously found circle C.
        
    @param x The x coordinate of the circle C found.
    @param x The y coordinate of the circle C found.
    @param x The radius of the circle C found.
    */
    private void clearNeighbours( double[][][] houghValues, int x, int y, int radius ) {

        // The following code just clean the points around the center of the circle found.

        double halfRadius = radius / 2.0F;
        double halfSquared = halfRadius * halfRadius;

        int y1 = (int) Math.floor((double) y - halfRadius);
        int y2 = (int) Math.ceil((double) y + halfRadius) + 1;
        int x1 = (int) Math.floor((double) x - halfRadius);
        int x2 = (int) Math.ceil((double) x + halfRadius) + 1;

        if (y1 < 0)
            y1 = 0;
        if (y2 > height)
            y2 = height;
        if (x1 < 0)
            x1 = 0;
        if (x2 > width)
            x2 = width;

        for( int r = radiusMin; r <= radiusMax; r = r + radiusInc ) {
            int indexR = (r - radiusMin) / radiusInc;
            for( int i = y1; i < y2; i++ ) {
                for( int j = x1; j < x2; j++ ) {
                    if (Math.pow(j - x, 2D) + Math.pow(i - y, 2D) < halfSquared) {
                        houghValues[j][i][indexR] = 0.0D;
                    }
                }
            }
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy