/* * 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.WritableRaster; import java.util.Vector; import javax.media.jai.ImageLayout; import javax.media.jai.UntiledOpImage; import javax.media.jai.iterator.RandomIter; import javax.media.jai.iterator.RandomIterFactory; @SuppressWarnings("unchecked") public class CannyEdgeOpImage extends UntiledOpImage { @SuppressWarnings("unused") private int[] mag; @SuppressWarnings("unused") private int[] gradx; @SuppressWarnings("unused") private int[] grady; int bands; int height; int width; public CannyEdgeOpImage(Vector sources, java.util.Map configuration, ImageLayout layout) { super(sources, configuration, layout); } //public String toString(); //public boolean equals(Object obj); //protected Object clone() throws CloneNotSupportedException; //protected void finalize() throws Throwable; /** computes mean values for a tile of image data */ protected void computeImage(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster src = sources[0]; bands = src.getNumBands(); height = src.getHeight(); width = src.getWidth(); mag = new int[width * height * bands]; gradx = new int[width * height * bands]; grady = new int[width * height * bands]; /* buildMagAndGrad(src); Raster nms = nullmaxSupp(dest); applyHysteresis(nms, 0.33F, 0.9F, dest);*/ } // Build mag and grad from derivative function @SuppressWarnings("unused") private void buildMagAndGrad(Raster src) { /* for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { for (int j = 0; j < height; j++) { sum1 = 0; sum2 = 0; sum11 = 0; sum22 = 0; sum12 = 0; for (int k = -dv; k < dv ; k++) { for (int l = -dh; l < dh ; l++) { if (i + k >= 0 && i + k < ww && j + l >= 0 && j + l < hh) { pix = db_zone.getElem( i + k + (j + l) * ww); } } } } } }*/ } @SuppressWarnings("unused") private Raster nullmaxSupp(Raster mag, Raster gradx, Raster grady, WritableRaster dest) { // used to access the source image RandomIter itermag = RandomIterFactory.create(mag, null); RandomIter itergradx = RandomIterFactory.create(gradx, null); RandomIter itergrady = RandomIterFactory.create(grady, null); int m00, gx, gy, z1, z2; float mag1, mag2, xperp, yperp; int mw, mnw, mn, mne, me, mse, ms, msw; for (int band = 0; band < bands; band++) for (int samp = 0; samp < width; samp++) { dest.setSample(samp, 0, band, 0); dest.setSample(samp, height - 1, band, 0); } for (int band = 0; band < bands; band++) for (int line = 0; line < height; line++) { dest.setSample(0, line, band, 0); dest.setSample(width - 1, line, band, 0); } for (int band = 0; band < bands; band++) for (int samp = 1; samp < width - 2; samp++) for (int line = 1; line < height - 2; line++) { m00 = itermag.getSample(samp, line, band); mw = itermag.getSample(samp - 1, line, band); mnw = itermag.getSample(samp - 1, line - 1, band); mn = itermag.getSample(samp, line - 1, band); mne = itermag.getSample(samp + 1, line - 1, band); me = itermag.getSample(samp + 1, line, band); mse = itermag.getSample(samp + 1, line + 1, band); ms = itermag.getSample(samp, line + 1, band); msw = itermag.getSample(samp - 1, line + 1, band); if (m00 == 0) { dest.setSample(samp, line, band, 255); } else { gx = itergradx.getSample(samp, line, band); gy = itergrady.getSample(samp, line, band); xperp = -gx / ((float) m00); yperp = gy / ((float) m00); if (gx >= 0) { if (gy >= 0) { if (gx >= gy) { /* 111 */ /* Left point */ z1 = mw; z2 = mnw; mag1 = (m00 - z1) * xperp + (z2 - z1) * yperp; /* Right point */ z1 = me; z2 = mse; mag2 = (m00 - z1) * xperp + (z2 - z1) * yperp; } else { /* 110 */ /* Left point */ z1 = mn; z2 = mnw; mag1 = (z1 - z2) * xperp + (z1 - m00) * yperp; /* Right point */ z1 = ms; z2 = mse; mag2 = (z1 - z2) * xperp + (z1 - m00) * yperp; } } else { if (gx >= -gy) { /* 101 */ /* Left point */ z1 = mw; z2 = msw; mag1 = (m00 - z1) * xperp + (z1 - z2) * yperp; /* Right point */ z1 = me; z2 = mne; mag2 = (m00 - z1) * xperp + (z1 - z2) * yperp; } else { /* 100 */ /* Left point */ z1 = ms; z2 = msw; mag1 = (z1 - z2) * xperp + (m00 - z1) * yperp; /* Right point */ z1 = mn; z2 = mne; mag2 = (z1 - z2) * xperp + (m00 - z1) * yperp; } } } else { gy = itergrady.getSample(samp, line, band); if (gy >= 0) { if (-gx >= gy) { /* 011 */ /* Left point */ z1 = me; z2 = mne; mag1 = (z1 - m00) * xperp + (z2 - z1) * yperp; /* Right point */ z1 = mw; z2 = msw; mag2 = (z1 - m00) * xperp + (z2 - z1) * yperp; } else { /* 010 */ /* Left point */ z1 = mn; z2 = mne; mag1 = (z2 - z1) * xperp + (z1 - m00) * yperp; /* Right point */ z1 = ms; z2 = msw; mag2 = (z2 - z1) * xperp + (z1 - m00) * yperp; } } else { if (-gx > -gy) { /* 001 */ /* Left point */ z1 = me; z2 = mse; mag1 = (z1 - m00) * xperp + (z1 - z2) * yperp; /* Right point */ z1 = mw; z2 = mnw; mag2 = (z1 - m00) * xperp + (z1 - z2) * yperp; } else { /* 000 */ /* Left point */ z1 = me; z2 = mne; mag1 = (z2 - z1) * xperp + (m00 - z1) * yperp; /* Right point */ z1 = mn; z2 = mnw; mag2 = (z2 - z1) * xperp + (m00 - z1) * yperp; } } } /* Now determine if the current point is a maximum point */ if ((mag1 > 0.0) || (mag2 > 0.0)) { dest.setSample(samp, line, band, 255); } else { if (mag2 == 0.0) dest.setSample(samp, line, band, 255); else dest.setSample(samp, line, band, 128); } } } return dest; } /******************************************************************************* * PROCEDURE: follow_edges * PURPOSE: This procedure edges is a recursive routine that traces edgs along * all paths whose magnitude values remain above some specifyable lower * threshhold. * NAME: Mike Heath * DATE: 2/15/96 *******************************************************************************/ private void followEdges(WritableRaster edge, Raster mag, int samp, int line, int band, int lowval) { int pix, pixmag; int[] x = {1, 1, 0, -1, -1, -1, 0, 1}; int[] y = {0, 1, 1, 1, 0, -1, -1, -1}; for (int i = 0; i < 8; i++) { pix = edge.getSample(samp + x[i], line - y[i], band); pixmag = mag.getSample(samp + x[i], line - y[i], band); if ((pix == 128) && (pixmag > lowval)) { edge.setSample(samp + x[i], line - y[i], band, 0); followEdges(edge, mag, samp + x[i], line - y[i], band, lowval); } } } /******************************************************************************* * PROCEDURE: apply_hysteresis * PURPOSE: This routine finds edges that are above some high threshhold or * are connected to a high pixel by a path of pixels greater than a low * threshold. * NAME: Mike Heath * DATE: 2/15/96 *******************************************************************************/ @SuppressWarnings("unused") private void applyHysteresis(Raster mag, Raster nms, float tlow, float thigh, WritableRaster edge) { int pix, pixmag, r, c, pos, numedges, lowcount, highcount, i, rr, cc; int maximum_mag, sumpix; RandomIter itermag = RandomIterFactory.create(mag, null); RandomIter iternms = RandomIterFactory.create(nms, null); RandomIter iteredge = RandomIterFactory.create(edge, null); int[] hist = new int[32768]; int lowthreshold; int highthreshold; /**************************************************************************** * Initialize the edge map to possible edges everywhere the non-maximal * suppression suggested there could be an edge except for the border. At * the border we say there can not be an edge because it makes the * follow_edges algorithm more efficient to not worry about tracking an * edge off the side of the image. ****************************************************************************/ edge.setRect(nms); /**************************************************************************** * Compute the histogram of the magnitude image. Then use the histogram to * compute hysteresis thresholds. ****************************************************************************/ for (int band = 0; band < bands; band++) { for (r = 0; r < 32768; r++) hist[r] = 0; pos = 0; for (int samp = 0; samp < width; samp++) for (int line = 0; line < height; line++) { pix = edge.getSample(samp, line, band); if (pix == 128) { pixmag = mag.getSample(samp, line, band); hist[pixmag]++; } pos++; } /**************************************************************************** * Compute the number of pixels that passed the nonmaximal suppression. ****************************************************************************/ numedges = 0; maximum_mag = 0; for (r = 1; r < 32768; r++) { if (hist[r] != 0) maximum_mag = r; numedges += hist[r]; } highcount = (int)(numedges * thigh + 0.5); /**************************************************************************** * Compute the high threshold value as the (100 * thigh) percentage point * in the magnitude of the gradient histogram of all the pixels that passes * non-maximal suppression. Then calculate the low threshold as a fraction * of the computed high threshold value. John Canny said in his paper * "A Computational Approach to Edge Detection" that "The ratio of the * high to low threshold in the implementation is in the range two or three * to one." That means that in terms of this implementation, we should * choose tlow ~= 0.5 or 0.33333. ****************************************************************************/ r = 1; numedges = hist[1]; while ((r < (maximum_mag - 1)) && (numedges < highcount)) { r++; numedges += hist[r]; } highthreshold = r; lowthreshold = (int)(highthreshold * tlow + 0.5); /* printf("The input low and high fractions of %f and %f computed to\n", tlow, thigh); printf("magnitude of the gradient threshold values of: %d %d\n", lowthreshold, highthreshold); */ /**************************************************************************** * This loop looks for pixels above the highthreshold to locate edges and * then calls follow_edges to continue the edge. ****************************************************************************/ pos = 0; for (int samp = 0; samp < width; samp++) for (int line = 0; line < height; line++) { pix = edge.getSample(samp, line, band); pixmag = mag.getSample(samp, line, band); if ((pix == 128) && (pixmag >= highthreshold)) { edge.setSample(samp, line, band, 0); followEdges(edge, mag, samp, line, band, lowthreshold); } pos++; } /**************************************************************************** * Set all the remaining possible edges to non-edges. ****************************************************************************/ for (int samp = 0; samp < width; samp++) for (int line = 0; line < height; line++) { pix = edge.getSample(samp, line, band); if (pix != 0) edge.setSample(samp, line, band, 255); } } } }