Task

Write a program that performs so-called canny edge detection on an image.



A possible algorithm consists of the following steps:

 Noise reduction.   May be performed by Gaussian filter. 
  
 Compute intensity gradient   (matrices 




G

x




{\displaystyle G_{x}}

 and 




G

y




{\displaystyle G_{y}}

)   and its magnitude   



G


{\displaystyle G}

:
           



G
=



G

x


2


+

G

y


2






{\displaystyle G={\sqrt {G_{x}^{2}+G_{y}^{2}}}}


May be performed by convolution of an image with Sobel operators. 
  
 Non-maximum suppression.   
For each pixel compute the orientation of intensity gradient vector:   



θ
=


a
t
a
n
2



(

G

y


,


G

x


)



{\displaystyle \theta ={\rm {atan2}}\left(G_{y},\,G_{x}\right)}

.     
Transform   angle 



θ


{\displaystyle \theta }

   to one of four directions:   0, 45, 90, 135 degrees.     
Compute new array   



N


{\displaystyle N}

:     if         



G

(

p

a


)

<
G

(
p
)

<
G

(

p

b


)



{\displaystyle G\left(p_{a}\right)<G\left(p\right)<G\left(p_{b}\right)}


where   



p


{\displaystyle p}

   is the current pixel,   




p

a




{\displaystyle p_{a}}

   and   




p

b




{\displaystyle p_{b}}

   are the two neighbour pixels in the direction of gradient,   
then     



N
(
p
)
=
G
(
p
)


{\displaystyle N(p)=G(p)}

,       otherwise   



N
(
p
)
=
0


{\displaystyle N(p)=0}

.   
Nonzero pixels in resulting array correspond to local maxima of   



G


{\displaystyle G}

   in direction   



θ
(
p
)


{\displaystyle \theta (p)}

. 
  
 Tracing edges with hysteresis.   
At this stage two thresholds for the values of   



G


{\displaystyle G}

   are introduced:   




T

m
i
n




{\displaystyle T_{min}}

   and   




T

m
a
x




{\displaystyle T_{max}}

.   
Starting from pixels with   



N
(
p
)
⩾

T

m
a
x




{\displaystyle N(p)\geqslant T_{max}}

,   
find all paths of pixels with   



N
(
p
)
⩾

T

m
i
n




{\displaystyle N(p)\geqslant T_{min}}

   and put them to the resulting image.


--
-- demo\rosetta\Canny_Edge_Detection.exw
-- =====================================
--
without js -- imImage, im_width, im_height, im_pixel, IupImageRGB, 
            -- imFileImageLoadBitmap, and IupImageFromImImage()
include pGUI.e
constant TITLE = "Canny Edge Detection",
         IMGFILE = "Valve.png",
         C_E_D = {{-1, -1, -1},
                  {-1,  8, -1},
                  {-1, -1, -1}}

function detect_edges(imImage img)
    integer width = im_width(img),
            height = im_height(img)
    sequence original = repeat(repeat(0,width),height)
    integer fh = length(C_E_D),  hh=(fh-1)/2,
            fw = length(C_E_D[1]), hw=(fw-1)/2,
            divisor = max(sum(C_E_D),1)

    -- read original pixels and make them grey,
    for y=height-1 to 0 by -1 do
        for x=0 to width-1 do
            integer {c1,c2,c3} = im_pixel(img, x, y),
                    grey = floor((c1*114+c2*587+c3*299)/1000)
            original[height-y,x+1] = {grey,grey,grey}
        end for
    end for

    -- then apply an edge detection filter
    sequence new_image = original
    for y=hh+1 to height-hh-1 do
        for x=hw+1 to width-hw-1 do
            sequence newrgb = {0,0,0}
            for i=-hh to +hh do
                for j=-hw to +hw do
                    newrgb = sq_add(newrgb,sq_mul(C_E_D[i+hh+1,j+hw+1],original[y+i,x+j]))
                end for
            end for
            new_image[y,x] = sq_max(sq_min(sq_floor_div(newrgb,divisor),255),0)
        end for
    end for

    new_image = flatten(new_image) -- (as needed by IupImageRGB)
    Ihandle new_img = IupImageRGB(width, height, new_image) 
    return new_img
end function

IupOpen()
imImage im1 = imFileImageLoadBitmap(IMGFILE)
assert(im1!=NULL,"error opening "&IMGFILE)

Ihandle label1 = IupLabel(),
        label2 = IupLabel()
IupSetAttributeHandle(label1, "IMAGE", IupImageFromImImage(im1))
IupSetAttributeHandle(label2, "IMAGE", detect_edges(im1))

Ihandle dlg = IupDialog(IupHbox({label1, label2}),`TITLE="%s"`,{TITLE})
IupShow(dlg)
IupMainLoop()
IupClose()
