/* * 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.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.ArrayList; import java.util.List; import java.util.Vector; import javax.media.jai.ImageLayout; import javax.media.jai.UntiledOpImage; /** Find ellipses in an image with a Hough transform. */ @SuppressWarnings("unchecked") class HoughEllipseOpImage extends UntiledOpImage { private double minA; // major axis private double maxA; private double minB; // minor axis private double maxB; private boolean debug; private float maxPairs; private int MIN_VOTES_TO_DETECT_ELLIPSE; private int IDLE_STOP_SEARCH; private int[] accumulator; private List<EllipseDescriptor> ellipses; //private int quantizationBlockWidth; //private int quantizationBlockHeigth; public HoughEllipseOpImage(RenderedImage source, ImageLayout layout, Integer minMajorAxis, Integer maxMajorAxis , Integer minMinorAxis ,Integer maxMinorAxis,Integer minVotes,Integer idleStop, Float maxPairs, Boolean debug) { super(source, null, layout); this.minA = (double)minMajorAxis.intValue(); this.minB = (double)minMinorAxis.intValue(); this.maxA = (double)maxMajorAxis.intValue(); this.maxB = (double)maxMinorAxis.intValue(); this.maxPairs = maxPairs; this.debug = debug; this.MIN_VOTES_TO_DETECT_ELLIPSE = minVotes.intValue(); this.IDLE_STOP_SEARCH = idleStop.intValue(); this.ellipses = new ArrayList<EllipseDescriptor>(); } protected void computeImage(Raster[] srcarr, WritableRaster dst, Rectangle destRect) { Raster src = srcarr[0]; // get edge points double edge[][]; ArrayList<Point> _edge = new ArrayList<Point>(); int width = src.getWidth(); int heigth = src.getHeight(); for(int i=0;i<width;i++) { for(int j=0;j<heigth;j++) { int sample = src.getSample(i,j,0); if(sample==RoboRaster.WHITE) //if(sample>=100) _edge.add(new Point(i,j)); } } if(_edge.isEmpty()) return; int edgePoints = _edge.size(); edge = new double[_edge.size()][3]; for(int i=0;i<_edge.size();i++) { edge[i][0] = ((Point)_edge.get(i)).x; edge[i][1] = ((Point)_edge.get(i)).y; edge[i][2] = 1; // punct activ } if(maxB > maxA) maxB = maxA; // RHT - Randomized Hough Transform // max nr of pairs about 50 % of img if(this.debug) { System.out.println("Max Points : "+this.maxPairs+" %"); } int maxPairs = (int)((double)edgePoints * this.maxPairs);// - (int)((double)edgePoints * 0.15); //if(edgePoints > 3000) // maxPairs = 1300; int currentPair = 0; int accLength = (int)(this.maxA - this.minB); accumulator = new int[accLength]; clear(accumulator); if(this.debug) { System.out.println("Searching : "+maxPairs); System.out.println("Total : "+edgePoints); System.out.println("Acc len : "+accLength); } // for speed - // fara prea multe alocari de mem int p1 = 0; int p2 = 0; double dP1P2 = 0.0; double cx = 0.0; double cy = 0.0; double a = 0.0; double d = 0.0; double f = 0.0; double cos = 0.0; double b = 0.0; int bIDX = 0; int maxData[]; int maxAccElement = 0; int houghMinorLength = 0; //****************************************************************************** int idleStopCounter = 0; while(currentPair <= maxPairs) { if(idleStopCounter++ >= this.IDLE_STOP_SEARCH) break; p1 = (int)Math.round(Math.random()*(double)(edgePoints-1)); while(edge[p1][2]==0) // has been removed p1 = (int)Math.round(Math.random()*(double)(edgePoints-1)); p2 = p1; while(p2==p1 || edge[p2][2]==0) p2 = (int)Math.round(Math.random()*(double)(edgePoints-1)); // am punctele dP1P2 = Point2D.distance(edge[p1][0],edge[p1][1],edge[p2][0],edge[p2][1]); if( dP1P2 >= 2*minA && dP1P2 <= 2*maxA ) { currentPair++; dst.setSample((int)edge[p1][0],(int)edge[p1][1],0,RoboRaster.WHITE); dst.setSample((int)edge[p2][0],(int)edge[p2][1],0,RoboRaster.WHITE); cx = (edge[p1][0] + edge[p2][0]) / 2.0; cy = (edge[p1][1] + edge[p2][1]) / 2.0; a = dP1P2 / 2.0; for(int p3 =0 ;p3<edgePoints ; p3++) { if(p3==p1 || p3==p2 || edge[p3][2]==0) continue; d = Point2D.distance(edge[p3][0],edge[p3][1],cx,cy); if( d >= this.minB && d <= a ) { f = Point2D.distance(edge[p3][0],edge[p3][1],edge[p2][0],edge[p2][1]); cos = (a*a + d*d - f*f) / (2*a*d); b = Math.sqrt( (a*a*d*d*(1-cos*cos)) / (a*a - d*d*cos*cos)); if( b > maxB ) continue; bIDX = (int)Math.round(b - minB); if(bIDX < 0 || bIDX >= accumulator.length) continue; accumulator[bIDX]++; //dst.setSample((int)edge[p3][0],(int)edge[p3][1],2,bIDX*16+50); //dst.setSample((int)edge[p3][0],(int)edge[p3][1],1,bIDX*8+50); //dst.setSample((int)edge[p3][0],(int)edge[p3][1],0,bIDX*2+50); } } //dst.setSample((int)edge[p1][0],(int)edge[p1][1],0,RoboRaster.WHITE); //dst.setSample((int)edge[p2][0],(int)edge[p2][1],0,RoboRaster.WHITE); // print acc //for(int i=0;i<accumulator.length;i++) // System.out.println(" ["+(i+(int)minB)+"] = "+accumulator[i]); //if(1==1) // break; // find max in acc array maxData = this.max(accumulator); maxAccElement = maxData[1]; houghMinorLength = maxData[0] + (int)minB; // am elipsa posibila cu val = raza mica if( maxAccElement >= this.MIN_VOTES_TO_DETECT_ELLIPSE ) { // output ellipse //System.out.println(maxAccElement); EllipseDescriptor ellipse = new EllipseDescriptor( new Point2D.Double(edge[p1][0],edge[p1][1]), // vertex 1 new Point2D.Double(edge[p2][0],edge[p2][1]), // vertex 2 new Point2D.Double(cx,cy), // center (int)a, // major axis houghMinorLength); // minor axis ellipses.add(ellipse); //System.out.println(maxAccElement); //drawPixel(0,(int)cx,(int)cy,dst,5); //drawPixel(1,(int)cx,(int)cy,dst,5); //drawPixel(1,(int)edge[p1][0],(int)edge[p1][1],dst,5); //drawPixel(1,(int)edge[p2][0],(int)edge[p2][1],dst,5); // rem pixels of ellipse from edge array //removeEllipse(edge,ellipse); idleStopCounter = 0; } clear(accumulator); } } //****************************************************************************** // get Clustered Ellipses if(this.debug) { System.out.println("Total Ellipses : "+this.ellipses.size()+" "); } /* for(int i=0;i<this.ellipses.size();i++) { EllipseDescriptor desc = ellipses.elementAt(i); System.out.println(desc.toString()); } */ List<EllipseDescriptor> centroids = EllipseDescriptor.getCentroidalEllipses(ellipses); ellipses.clear(); ellipses.addAll(centroids); this.setProperty(EllipseDescriptor.DETECTED_ELLIPSES,ellipses); //for(int i=0;i<centroids.size();i++) // ellipses.addElement(centroids.elementAt(i)); // draw ellipse center if(this.debug) { System.out.println("Ellipses : "+this.ellipses.size()+" ellipses"); } for(int i=0;i<this.ellipses.size();i++) { EllipseDescriptor desc = ellipses.get(i); drawPixel(0,(int)desc.getCenter().getX(),(int)desc.getCenter().getY(),dst,5); drawPixel(1,(int)desc.getCenter().getX(),(int)desc.getCenter().getY(),dst,5); drawPixel(2,(int)desc.getCenter().getX(),(int)desc.getCenter().getY(),dst,5); drawPixel(1,(int)desc.getVertex1().getX(),(int)desc.getVertex1().getY(),dst,5); drawPixel(1,(int)desc.getVertex2().getX(),(int)desc.getVertex2().getY(),dst,5); if(this.debug) { System.out.println(desc.toString()); } } } private void drawPixel(int band,int x,int y,WritableRaster dst,int w) { int _x = x-w/2; int _y = y-w/2; for(int i=0;i<w;i++) for(int j=0;j<w;j++) dst.setSample(_x+i,_y+j,band,RoboRaster.WHITE); } @SuppressWarnings("unused") private void removeEllipse(double edge[][],EllipseDescriptor ellipse) { double a = ellipse.getHalfMajorAxis(); double b = ellipse.getHalfMinorAxis(); double cx= ellipse.getCenter().getX(); double cy= ellipse.getCenter().getY(); //double x1= ellipse.getVertex1().getX(); //double y1= ellipse.getVertex1().getY(); double x2= ellipse.getVertex2().getX(); double y2= ellipse.getVertex2().getY(); for(int p3=0; p3<edge.length; p3++) { if(edge[p3][2]==0) continue; double d = Point2D.distance(edge[p3][0],edge[p3][1],cx,cy); double f = Point2D.distance(edge[p3][0],edge[p3][1],x2,y2); double cos = (a*a + d*d - f*f) / (2*a*d); double calcB = (int)Math.round( Math.sqrt( (a*a*d*d*(1-cos*cos)) / (a*a - d*d*cos*cos)) ); if(calcB == b) // remove edge[p3][2] = 0; } } private int[] max(int elements[]) { int max = 0; int idx = -1; for(int i=0;i<elements.length;i++) if( elements[i] > max ) { max = elements[i]; idx = i; } return new int[]{idx,max}; } private void clear(int elements[]) { for(int i=0;i<elements.length;i++) elements[i] = 0; } }