Canny’s Filter for Edge Detection

25
Canny’s Filter for Edge Detection. A Java Implementation – José Iguelmar Miranda

description

The aim of this article is to present the Java implementation of the Canny’s filter for edge detection (Canny, 1986).

Transcript of Canny’s Filter for Edge Detection

Page 1: Canny’s Filter for Edge Detection

Canny’s Filter for Edge Detection. A Java Implementation – José Iguelmar Miranda

Page 2: Canny’s Filter for Edge Detection

12

1

Page 3: Canny’s Filter for Edge Detection

Canny’s Filter for Edge Detection. 2009

Canny’s Filter for Edge Detection. A Java Implementation – José Iguelmar Miranda

Introduction The aim of this article is to present the Java implementation of the Canny’s filter for

edge detection (Canny, 1986).

Edge detection is one of the most common processes when analyzing digital images,

counting with a great variety of algorithms. Edges carry useful information about

object boundaries, which can be used for image analysis, object identification and for

image filtering. Nevertheless, there is no precise and widely accepted mathematical

definition of an edge yet. This is so because the complexity of the image content and

by the interference of high-level vision mechanisms in the human perception of the

boundary of an object (Pitas, 2000). Edges, in the present approach, are transitions

regions between two homogeneous regions in a digital image having different values

for pixels intensity and generally define the borders between the object and its

background. This means that if one can detect the edges, then objects and some of

their features can be measured, like area, perimeter and shape. This is why the edge

detection process is qualified as an essential tool in image analysis.

Edge detection is part of a major procedure, known as segmentation – the

identification of regions inside an image. There are two techniques in digital image

processing that deal with edges: edge detection and edge enhancement. Technically,

edge detection aims to localize the pixels in the borders of an object, while edge

enhancement increases the contrast between edges and background, making them

more visible (Parker, 1997). In practice, both terms are used with the same subject,

Page 4: Canny’s Filter for Edge Detection

12

3

because the majority of the algorithms for edge detection assign to the pixels in the

borders values that make them more visible.

Theoretical Aspects There are some possible definitions for edges, applicable in differing situations. The

most common and generic is ideal step border (Fig. 1a). This is a one-dimensional

example, where the edge is simply a change in grey level occurring at one specific

location (x = 125), along the horizontal or abscissa axes. The bigger the gray level

difference in the transition zone, the easier to detect edges. But, in real world,

complexity happens. For instance, the quantization process realized by a satellite, the

scene sampling not always can make a precise match between real object edges and

the pixels that will represent them. Commonly, happens what is shown in Fig. 1b. In

this case, the position of the edge is considered to be the center of the ramp

connecting the low grey level to the high one.

Figure 1a. Ideal border example.

Page 5: Canny’s Filter for Edge Detection

Canny’s Filter for Edge Detection. 2009

Figure 1b. Ideal border example.

An additional difficulty has to do with the ubiquitous problem of noise. Due to some

factors, like sun light intensity, camera model and lens, satellite movement, air

temperature, atmospheric effects, among others, it is unlike that two pixels with the

same gray level in the ground will have the same gray level in the digital image.

Noises are random and systematic. Random noises are only characterized by a

statistical distribution. Systematic noises are easier to detect and to remove. The net

effect of noises in the image is to produce a random variation in the gray level values

of the pixels, such that the ideal edge shown in Fig. 1 is not found in real images.

That said, it is not possible to ignore the presence of random noise in images. The

problem is that this noise cannot be identified and measured precisely, since one

cannot differentiate its contribution in the pixels gray level values. Happily,

sometimes, the random noise can be characterized by its effect in the image,

expressed as a probability distribution with specific values of mean and standard

deviation (Parker, 1997). So, before working with an image, it is necessary to filter

this kind of noise, normally using an edge detection process.

Page 6: Canny’s Filter for Edge Detection

12

5

Since a border is defined as a change in the gray level, an operator that is sensible to

this change has a task to detect edge. Generally, edge operators can be classified in

three groups: (1) those based on partial derivatives, approximated by differences in

the discrete case, whose task is to identify spots where there are great intensity

changes; (2) those that model the border with a small dimension kernel; and (3)

those that use mathematical models for the borders, using partial differential

equations, or diffusion models, that search for the maxima and minima of a function.

The approach used here is a mix between (1) and (2).

The Canny’s Filter John Canny (1986) specified three issues that an edge detector must address:

1. Good detection – there should be a low probability of failing to mark real edge

points, and low probability of falsely marking non-edge points. This criterion

corresponds to maximizing signal-to-noise ratio.

2. Localization – the distance between the edge pixels as found by the edge

detector and the actual edge should be as small as possible.

3. Response – only one response to a single edge. Where there are two responses

to the same edge, one of them must be considered false.

According to the author, the challenge would be to transform these criteria in a

mathematical formalism, which is readily solvable. Canny assumed a step edge

subject to white Gaussian noise. The edge detector was assumed to be a convolution

filter, f, which would smooth the noise and locate the edge. The problem was to

identify the one filter that optimizes the three edge detection criteria. He started his

work analyzing the behavior of the above criteria in one dimension. Firstly, he

considered the signal-to-noise ratio and localization, letting the function f(x) be the

impulse response of the filter, and G(x), the edge itself; he assumed that the edge was

centered at x = 0.

The response of the filter to this edge, centered at HG, was given by a convolution

integral (Canny, 1986):

Page 7: Canny’s Filter for Edge Detection

Canny’s Filter for Edge Detection. 2009

(1)

Where the filter has a finite impulse response bounded by [-w, w], and zero out of

this interval. To the noise, n(x), consider the root-mean-squared response (Canny,

1986):

(2)

Where is the mean-squared noise amplitude per unit length. So, the first criterion,

the output signal-to-noise ration, can be formalized as the quotient of these two

responses (Canny, 1986):

(3)

For the second criterion, Canny considered some measure that increased as

localization improved, using the root-mean-squared distance between the marked

edges from the center of the true edge. Edges marking happened in local maxima in

the response of the operator f(x), i.e., where its first derivative was zero. The reader

can obtain further details in Canny’s article, for finding the localization measure:

(4)

Canny showed that using only the first two criteria, the optimal detector for step

edges with noise is a truncated step (Fig. 2a), similar to using a difference of boxes

filter (Fig. 2c).

Page 8: Canny’s Filter for Edge Detection

12

7

Figure 2. Signal-to-noise, DoG, and difference of boxes filters.

This filter, however, has a very high bandwidth and tends to exhibit many maxima in

its response to noisy step edges, which should be considered erroneous according to

the first criterion. The mean distance between adjacent maxima and the noise output

of f(x), called xmax, will restrict the selection of f(x) according to a single criterion, the

third:

xmax [f(x)] = kw (5)

Where k is some fraction of the operator width, w. Due to the complexity of the

formulation, no analytical solution was found. A variant was developed, enabling the

edge detection. For small values of xmax, the Canny’s operator becomes close to the

action of a difference of boxes filter (Fig. 1c). For bigger values of xmax, it becomes

close to the action of a filter of the first derivative of a Gaussian function (Fig. 1b),

also known as derivative of Gaussian (DoG).

In two dimensions, an edge also has an orientation, meaning the direction of the

direction of the tangent to the contour that the edge defines in two directions. Canny

created a two-dimensional mask for this orientation by convolving a linear edge

detection function aligned normal to the edge direction with a projection function

parallel to the edge direction. In two dimensions, the Gaussian function is given by:

(6)

Page 9: Canny’s Filter for Edge Detection

Canny’s Filter for Edge Detection. 2009

Hence, the approximation of the Canny’s optimal filter for edge detection is G’ (first

derivative), and so by convolving the input image with G’ we obtain an image E that

has enhanced edges, even in the presence of noise, which has been incorporated into

the model of the edge image.

A convolution is simple to implement, but is expensive computationally, especially a

two dimensional convolution. However, a convolution with a two dimensional

Gaussian can be separated into two convolutions with one-dimensional Gaussians,

and the differentiation can be done afterwards. Indeed, the differentiation can also be

done by convolutions in one dimension, giving two images: one is the x component of

the convolution with G’ and the other is the y component. The reader interested in

details can take a look at the comprehensive Canny’s article (1986).

Fig. 3 shows the approximated effect of the DoG filter (Fig. 2a, and difference of

boxes, Fig. 2c) in the signal shown in Fig. 2a. The filtered signal with the difference of

boxes filter shows local maxima problems.

Figure 3. Effects of the filters DoG and difference of boxes in the signal shown in Fig. 2a.

The Java Implementation Java language is an alternative for implementation of applications not only for the

web, but for others uses. The facility to run Java applications in whatever operating

system is a strong appeal to use Java. At least, this advantage motivated the present

Page 10: Canny’s Filter for Edge Detection

12

9

work. In the following sections, the reader is presented with the whole source code of

the application. You can add it to an IDE, like NetBeans or Eclipse, or simply compile

and execute through a DOS/Term prompt. Initially, it was developed to run in

Windows environment, using a DOS prompt. The filter implementation was

developed as a pure Java application, containing only the constructor, called from the

main() method. In Windows, the compilation is done calling:

C:[instalation dir]>javac -deprecation FiltroCanny.java

To execute it:

C:[instalation dir]>java FiltroCanny <imagem> <dp> <inf> <sup>

Where the parameters are:

• <imagem>– the image to be filtered.

• <dp>– the standard deviation.

• <inf> – low hysteresis threshold value.

• <sup> – high hysteresis threshold value.

The user must test values of the standard deviation and thresholds, till he founds an

output image with good result. At this point, we suggest the construction of a

graphical interface that allows dynamical change in these values, as of using the

JSlider.

The constructor of the filter is defined as:

public CannyFilter(String aFile, double s, int inf, int sup) {

}

Where:

1. aFile: input image archive.

2. s: standard deviation.

3. inf, sup: low and high hysteresis threshold values. Any pixel in the image that

has a value greater than highTh is presumed to be an edge pixel, and is marked

Page 11: Canny’s Filter for Edge Detection

Canny’s Filter for Edge Detection. 2009

as such. Then, pixels that are connected to this edge pixel and that have a value

greater than lowTh are also selected as edge pixels, and are marked too.

The constructor has four tasks: (1) read the input image to be filtered; (2) call the

canny method, that implements the Canny filter; (3) define the edge pixels, using the

parameters values (lowTh and highTh); and (4) shows the output (filtered) image. The

canny method implements the main steps of the algorithm, described as (Parker,

1997):

1. Read in the image to be processed, I.

2. Create a 1D Gaussian mask G to convolve with I. The standard deviation s of

this Gaussian is a parameter to the edge detector.

double funcGauss[]

3. Create a 1D mask for the first derivative of the Gaussian in the x and y

directions; call these Gx and Gy. The same s value is used as in step 2.

double derivadaGauss[]

4. Convolve the image I with G along the rows to give the x component image Ix,

and down the columns to give the y component image Iy.

convolveImagemXY(imagem, funcGauss, width, componenteX,

componenteY);

5. Convolve Ix with Gx to give Ix’, the x component of I convolved with the

derivative of the Gaussian, and convolve Iy with Gy to give Iy’.

derivadaX = convolveDerivadaXY(componenteX, nr, nc,

derivadaGauss, width, 1);

derivadaY = convolveDerivadaXY(componenteY, nr, nc,

derivadaGauss, width, 0);

6. The magnitude of the result is computed at each pixel (x, y) as:

z = norma(derivadaX[j][i], derivadaY[j][i]);

Which corresponds to the following equation:

7. Finally, suppress pixels that are not local maxima.

Page 12: Canny’s Filter for Edge Detection

12

11

removeFalsoMax(derivadaX, derivadaY, nr, nc, imagemMag,

imagemOrig);

The Java Source Code

/********************************************************************* FiltroCanny.java – implements the Canny filter. WRITTEN BY: José Iguelmar Miranda & João Camargo Neto. DATE: September 2008 Copyright (c) 2009 Embrapa Informática Agropecuária PERMISSION TO COPY: This program is free software, under the GNU General Public License (GPL); permission to use, copy and modify this software and its documentation for NON-COMMERCIAL purposes is granted, without fee, provided that an acknowledgement to the authors, José Iguelmar Miranda & João Camargo Neto, at www.cnptia.embrapa.br, appears in all copies. Embrapa Informática Agropecuária makes no representations about the suitability or fitness of the software for any or for a particular purpose. Embrapa Informática Agropecuária shall not be liable for any damages suffered as a result of using, modifying or distributing this software or its derivatives. For a copy of GNU General Public License, write to: Free Software Foundation, Inc., 59 Temple Street, Suite 330, Boston, MA 02111-1307 USA. ********************************************************************* Description: This program implements the Canny filter. Reference paper: CANNY, J. A computational approach to edge detection. IEEE Transactions

on Pattern Analysis and Machine Intelligence. 8(6):679-698, 1986. *********************************************************************/ // Generic packages import java.io.File; // AWT packages import java.awt.image.WritableRaster; import java.awt.image.BufferedImage; import java.awt.image.Raster; import java.awt.GridLayout;

Page 13: Canny’s Filter for Edge Detection

Canny’s Filter for Edge Detection. 2009

// Swing packages for graphical interface import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.ImageIcon; import javax.swing.JScrollPane; // Immediate mode reading - J2SE 1.4+ import javax.imageio.ImageIO; public class FiltroCanny extends JFrame { // Attributes // Image scale and magnitude static double ORI_SCALE = 40.0; static double MAG_SCALE = 20.0; // Kernel mask maximum size static int MAX_MASK_SIZE = 20; // Fraction of pixels that should be above the HIGH threshold double ratio = 0.1; int LARGURA = 0; public static void main(String args[]) { double dp = 1.0; int inf = 0, sup = 0; // Parameters ok? if (args.length != 4) { String msga = "Uso: java -cp . FiltroCanny <imagem> <dp> <inf> <sup>"; String msgb = "\n dp => standard deviation."; String msgc = "\n inf => LOW threshold."; String msgd = "\n sup => HIGH threshold."; System.out.println(msga + msgb + msgc + msgd); System.exit(0); } // Show JFrame decorated by Swing JFrame.setDefaultLookAndFeelDecorated(true); try { dp = Double.parseDouble(args[1]); } catch (NumberFormatException e) { System.out.println("Valor do parametro <dp> invalido"); System.exit(0); }

Page 14: Canny’s Filter for Edge Detection

12

13

try { inf = Integer.parseInt(args[2]); } catch (NumberFormatException e) { System.out.println("Valor do parametro <inf> invalido"); System.exit(0); } try { sup = Integer.parseInt(args[3]); } catch (NumberFormatException e) { System.out.println("Valor do parametro <sup> invalido"); System.exit(0); } // Call the constructor if (dp <= 0.0) dp = 1.0; if (inf < 0) inf = 0; if (sup > 255) sup = 255; long eq_time = System.currentTimeMillis(); FiltroCanny fc = new FiltroCanny(args[0], dp, inf, sup); eq_time = System.currentTimeMillis() - eq_time; String msg = "Canny: tempo de execucao "; System.out.println(msg + eq_time + " milisseg."); // If “X” clicked, close the application fc.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); fc.setVisible(true); } public FiltroCanny(String aFile, double s, int inf, int sup) { BufferedImage imagem = null, imagemMagnitude = null, imagemOriginal = null; int w, h, tipo; JLabel img1; // Does the image file exist? File file = new File(aFile); try { imagem = ImageIO.read(file); } catch(Exception e) { System.out.println("Imagem '" + aFile + "' nao existe."); System.exit(0); } String msga = "\nParametros para Canny:\n"; String msgb = "==> limiar inferior " + inf + "\n"; String msgc = "==> limiar superior " + sup + "\n"; String msgd = "==> desvio padrao " + s + "\n";

Page 15: Canny’s Filter for Edge Detection

Canny’s Filter for Edge Detection. 2009

System.out.println(msga + msgb + msgc + msgd); // Atribui nome ao frame setTitle("Canny: " + file.getName()); // Cria imagem local w = imagem.getWidth(); h = imagem.getHeight(); tipo = BufferedImage.TYPE_BYTE_GRAY; imagemMagnitude = new BufferedImage(w, h, tipo); imagemOriginal = new BufferedImage(w, h, tipo); WritableRaster imWR = imagem.getRaster(); // Call the Canny method canny(s, imagem, imagemMagnitude, imagemOriginal); // Define edge pixels using threshold values HIGH and LOW linhasBordas(sup, inf, imagem, imagemMagnitude, imagemOriginal); for (int i = 0; i < LARGURA; i++) for (int j = 0; j < w; j++) imWR.setSample(j, i, 0, 255); for (int i = h - 1; i > h - 1 - LARGURA; i--) for (int j = 0; j < w; j++) imWR.setSample(j, i, 0, 255); for (int i = 0; i < h; i++) for (int j = 0; j < LARGURA; j++) imWR.setSample(j, i, 0, 255); for (int i = 0; i < h; i++) for (int j = w - LARGURA - 1; j < w; j++) imWR.setSample(j, i, 0, 255); // Create gridLayout de 1 x 1 getContentPane().setLayout(new GridLayout(1, 1)); img1 = new JLabel(new ImageIcon(imagem)); setSize(w, h); getContentPane().add(new JScrollPane(img1)); } private void canny(double s, BufferedImage imagem, BufferedImage imagemMag, BufferedImage imagemOrig) { int width = 0, k, n, nc, nr; double[][] componenteX, // x component of the original image convolved // with the Gaussian.

Page 16: Canny’s Filter for Edge Detection

12

15

componenteY, // y component of the original image convolved // with the Gaussian. derivadaX, // x component of the convolved image // (componenteX) with Gaussian derivative derivadaY; // y component of the convolved image // (componenteY) with Gaussian derivative double funcGauss[], // Gaussian values derivadaGauss[], // Values of the Gaussian first derivative z; funcGauss = new double[MAX_MASK_SIZE]; derivadaGauss = new double[MAX_MASK_SIZE]; nc = imagem.getWidth(); nr = imagem.getHeight(); // Create a mask of the Gaussian filter and its derivative for (int i = 0; i < MAX_MASK_SIZE; i++) { funcGauss[i] = mediaGauss((double)i, s); if (funcGauss[i] < 0.005) { width = i; break; } derivadaGauss[i] = dGauss((double)i, s); } n = 2*width + 1; LARGURA = (int)width/2; System.out.println("Suavizando com uma Gaussiana (largura = " + n + ") ...\n"); componenteX = new double[nc][nr]; componenteY = new double[nc][nr]; // Convolve original image with Gaussian mask in x and y directions convolveImagemXY(imagem, funcGauss, width, componenteX, componenteY); // Convolve smoothed image with derivative System.out.println("Convolucao com a derivada da Gaussiana...\n"); derivadaX = convolveDerivadaXY(componenteX, nr, nc, derivadaGauss, width, 1); derivadaY = convolveDerivadaXY(componenteY, nr, nc, derivadaGauss, width, 0); WritableRaster magWR = imagemMag.getRaster(); // Create magnitude image from the x and y derivatives (gradient) for (int i = 0; i < nr; i++) for (int j = 0; j < nc; j++) { z = norma(derivadaX[j][i], derivadaY[j][i]); magWR.setSample(j, i, 0, (int)z*MAG_SCALE); } // Suppress false maxima

Page 17: Canny’s Filter for Edge Detection

Canny’s Filter for Edge Detection. 2009

removeFalsoMax(derivadaX, derivadaY, nr, nc, imagemMag, imagemOrig); } // Compute mean of Gaussian function private double mediaGauss(double x, double sigma) { double z; z = (gauss(x,sigma)+gauss(x+0.5,sigma)+gauss(x-0.5,sigma))/3.0; z = z/(Math.PI*2.0*sigma*sigma); return z; } // Compute value for Gaussian function private double gauss(double x, double sigma) { double expoente; if (sigma == 0) return 0.0; expoente = Math.exp(((-x*x)/(2*sigma*sigma))); return expoente; } // Compute first derivative value of Gaussian function private double dGauss(double x, double sigma) { return (-x/(sigma*sigma)*gauss(x, sigma)); } // Convolve components x and y of the image. private void convolveImagemXY(BufferedImage imagem, double[] funcGauss, int width, double[][] compX, double[][] compY) { int i1, i2, nr, nc; double x, y; nc = imagem.getWidth(); nr = imagem.getHeight(); Raster imR = imagem.getRaster(); for (int i = 0; i < nr; i++) for (int j = 0; j < nc; j++) { x = funcGauss[0]*imR.getSample(j, i, 0); y = funcGauss[0]*imR.getSample(j, i, 0); for (int k = 1; k < width; k++) { i1 = (i+k)%nr; i2 = (i-k+nr)%nr; y += funcGauss[k]*imR.getSample(j, i1, 0) + funcGauss[k]*imR.getSample(j, i2, 0);

Page 18: Canny’s Filter for Edge Detection

12

17

i1 = (j+k)%nc; i2 = (j-k+nc)%nc; x += funcGauss[k]*imR.getSample(i1, i, 0) + funcGauss[k]*imR.getSample(i2, i, 0); } compX[j][i] = x; compY[j][i] = y; } } // Do the convolution in the derivatives of the x and y components of the // convolved image private double[][] convolveDerivadaXY(double[][] imagem, int nr, int nc, double[] funcGauss, int width, int compXY) { int i1, i2; double x; double[][] componente = new double[nc][nr]; for (int i = 0; i < nr; i++) for (int j = 0; j < nc; j++) { x = 0.0; for (int k = 1; k < width; k++) { switch (compXY){ case 0: // y component i1 = (i+k)%nr; i2 = (i-k+nr)%nr; x += -funcGauss[k]*imagem[j][i1] + funcGauss[k]*imagem[j][i2]; break; case 1: // x component i1 = (j+k)%nc; i2 = (j-k+nc)%nc; x += -funcGauss[k]*imagem[i1][i] + funcGauss[k]*imagem[i2][i]; break; } } componente[j][i] = x; } return componente; } // Suppress false maxima private void removeFalsoMax(double[][] derivX, double[][] derivY, int nr, int nc, BufferedImage imagemMag, BufferedImage imagemOrig) { int k, n, m, top, bottom, left, right; double xx, yy, grad1, grad2, grad3, grad4, gradiente, compX, compY; nc = imagemMag.getWidth(); nr = imagemMag.getHeight(); WritableRaster magWR = imagemMag.getRaster();

Page 19: Canny’s Filter for Edge Detection

Canny’s Filter for Edge Detection. 2009

WritableRaster oriWR = imagemOrig.getRaster(); for (int i = 1; i < nr - 1; i++) { for (int j = 1; j < nc - 1; j++) { magWR.setSample(j, i, 0, 0); // x and y derivatives are components of a vector (gradient) compX = derivX[j][i]; compY = derivY[j][i]; if (Math.abs(compX) < 0.01 && Math.abs(compY) < 0.01) continue; gradiente = norma(compX, compY); // Fallow gradient direction, vector (compX, compY). // Keep edge pixels (local maxima). if (Math.abs(compY) > Math.abs(compX)) { // First case: y component is bigger. Gradient direction is // upward or downward. xx = Math.abs(compX)/Math.abs(compY); yy = 1.0; grad2 = norma(derivX[j][i-1], derivY[j][i-1]); grad4 = norma(derivX[j][i+1], derivY[j][i+1]); if (compX*compY > 0.0) { grad1 = norma(derivX[j-1][i-1], derivY[j-1][i-1]); grad3 = norma(derivX[j+1][i+1], derivY[j+1][i+1]); } else { grad1 = norma(derivX[j+1][i-1], derivY[j+1][i-1]); grad3 = norma(derivX[j-1][i+1], derivY[j-1][i+1]); } } else { // Second case: y component is bigger. Gradient direction is // left or right. xx = Math.abs(compY)/Math.abs(compX); yy = 1.0; grad2 = norma(derivX[j+1][i], derivY[j+1][i]); grad4 = norma(derivX[j-1][i], derivY[j-1][i]); if (compX*compY > 0.0) { grad1 = norma(derivX[j+1][i+1], derivY[j+1][i+1]); grad3 = norma(derivX[j-1][i-1], derivY[j-1][i-1]); } else { grad1 = norma(derivX[j+1][i-1], derivY[j+1][i-1]); grad3 = norma(derivX[j-1][i+1], derivY[j-1][i+1]); } } // Compute the interpolated value of the gradient magnitude if ((gradiente > (xx*grad1 + (yy-xx)*grad2)) && (gradiente > (xx*grad3 + (yy-xx)*grad4))) {

Page 20: Canny’s Filter for Edge Detection

12

19

if (gradiente*MAG_SCALE <= 255) magWR.setSample(j, i, 0, (int)gradiente*MAG_SCALE); else magWR.setSample(j, i, 0, 255); oriWR.setSample(j, i, 0, (int)Math.atan2(compY, compX)*ORI_SCALE); } else { magWR.setSample(j, i, 0, 0); oriWR.setSample(j, i, 0, 0); } } } } // Define edge pixels private void linhasBordas(int sup, int inf, BufferedImage imagem, BufferedImage imagemMag, BufferedImage imagemOrig) { int nr, nc; nc = imagem.getWidth(); nr = imagem.getHeight(); WritableRaster imWR = imagem.getRaster(); Raster imR = imagem.getRaster(); Raster magR = imagemMag.getRaster(); System.out.println("Iniciando corte com limiares...\n"); for (int i = 0; i < nr; i++) for (int j = 0; j < nc; j++) imWR.setSample(j, i, 0, 0); if (sup < inf) { estimaLimiar(imagemMag, sup, inf); String str = "Limiar de corte (da imagem): SUPERIOR "; System.out.println(str + sup + " INFERIOR\n" + inf); } // For each edge with magnitude above HIGH threshold, draw the edge // pixels that are above the LOW threshold for (int i = 0; i < nr; i++) for (int j = 0; j < nc; j++) if (magR.getSample(j, i, 0) >= sup) trace(i, j, inf, imagem, imagemMag, imagemOrig); // Make the edge black for (int i = 0; i < nr; i++) for (int j = 0; j < nc; j++) if (imR.getSample(j, i, 0) == 0) imWR.setSample(j, i, 0, 255); else imWR.setSample(j, i, 0, 0); }

Page 21: Canny’s Filter for Edge Detection

Canny’s Filter for Edge Detection. 2009

// Estimate the HIGH threshold private void estimaLimiar(BufferedImage imagemMag, int hi, int inf) { int histograma[], count, nr, nc, i, j, k; nc = imagemMag.getWidth(); nr = imagemMag.getHeight(); histograma = new int[256]; Raster magR = imagemMag.getRaster(); // Image histogram for (k = 0; k < 256; k++) histograma[k] = 0; for (i = LARGURA; i < nr - LARGURA; i++) for (j = LARGURA; j < nc - LARGURA; j++) histograma[magR.getSample(j, i, 0)]++; // O limiar superior deveria ser maior que 80 ou 90% dos pixels j = nr; if (j < nc) j = nc; j = (int)(0.9*j); k = 255; count = histograma[255]; while (count < j) { k--; if (k < 0) break; count += histograma[k]; } hi = k; i = 0; while (histograma[i] == 0) i++; inf = (int)(hi+i)/2; } // Trace, recursively, the edge pixels private int trace(int i, int j, int inf, BufferedImage imagem, BufferedImage imagemMag, BufferedImage imagemOrig) { int n, m; int flag = 0;

Page 22: Canny’s Filter for Edge Detection

12

21

Raster magR = imagemMag.getRaster(); Raster imR = imagem.getRaster(); WritableRaster imWR = imagem.getRaster(); if (imR.getSample(j, i, 0) == 0) { imWR.setSample(j, i, 0, 255); flag = 0; for (n = -1; n <= 1; n++) { for(m = -1; m <= 1; m++) { if (i == 0 && m == 0) continue; if ((range(imagemMag, i+n, j+m) == 1) && (magR.getSample(j+m, i+n, 0)) >= inf) if (trace(i+n,j+m,inf,imagem,imagemMag,imagemOrig) == 1) { flag = 1; break; } } if (flag == 1) break; } return 1; } return 0; } // Certificate that the pixel belongs to the image private int range(BufferedImage imagem, int i, int j) { int nc, nr; nc = imagem.getWidth(); nr = imagem.getHeight(); if ((i < 0) || (i >= nr)) return 0; if ((j < 0) || (j >= nc)) return 0; return 1; } // Compute the gradient magnitude private double norma(double x, double y) { return Math.sqrt(x*x + y*y); } }

Case Study The following pictures show the application of the Canny’s filter for edge detection. In

all pictures, the threshold values were inf = 10 and sup = 20. Only the standard

Page 23: Canny’s Filter for Edge Detection

Canny’s Filter for Edge Detection. 2009

deviation values were changed, showing a different behavior of the filter. The first

picture shows edge detection for simple objects.

Figure 4. (a) Input image; (b) standard deviation = 1.5; (c) standard deviation = 2.6

Fig. 4(a) shows the source image. In Fig. 4(b) the standard deviation value was 1.5,

and in Fig. 4(c), the standard deviation was 2.6. The first value of the standard

deviation allows the filter to detect edges of the objects, but has some side effects,

because a lot of noise, from the background, is by passing. Increasing the standard

deviation value, the noise is filtered, yielding a better result.

Fig. 5 shows a different image, with agricultural exploitation. In this image one can

see two center pivot (circle area), bare soil (white), sugar cane and riparian vegetation

along some creeks. The first image was filtered with a standard deviation of 1.5. As

happened with the previous image, this standard deviation value allows the present of

noise, which, actually, are edges of small objects present in the image. Visually, the

result is not good. Increasing the standard deviation value permits to filter the edges

of the small objects, “cleaning” the final image, with a better visual appeal. As one

increases the standard deviation value, the output image becomes much clean, but at

the expense of obtaining an image with less edges.

Page 24: Canny’s Filter for Edge Detection

12

23

Figure 5. (a) Input image; (b) standard deviation = 1.5; (c) standard deviation = 2.2

Fig. 6 is a medical image, with a set of globules. The first filter used a standard

deviation of 1.5, with result similar to the previous images. The second filter used a

standard deviation of 2.6, with a better visual output image. In this image, with only

simple objects, the result is better, as happened with Fig. 4. When the image presents

a different set of objects with more nuances, the filtering process must be conducted

with care to identify the targets.

Figure 6. (a) Input image; (b) standard deviation = 1.5; (c) standard deviation = 2.6

Bibliography

PARKER, J.R. Algorithms for image processing and computer vision. New

York, NY: John Wiley & Sons, 1997. 417p.

Page 25: Canny’s Filter for Edge Detection

Canny’s Filter for Edge Detection. 2009

PITAS, I. Digital image processing algorithms and applications. New York,

NY: John Wiley & Sons, 2000. 419p.