user
Task

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

A possible algorithm consists of the following steps:

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

   G

   x
   {\displaystyle {\displaystyle G\_{x}}}
    and 

   G

   y
   {\displaystyle {\displaystyle G\_{y}}}
   )   and its **magnitude**   

   G
   {\displaystyle {\displaystyle G}}
   :  
              

   G
   =

   G

   x

   2
   +

   G

   y

   2
   {\displaystyle {\displaystyle G={\sqrt {G\_{x}^{2}+G\_{y}^{2}}}}}
     
   May be performed by convolution of an image with Sobel operators.
3. **Non-maximum suppression.**     
   For each pixel compute the orientation of intensity gradient vector:   

   θ
   =


   a
   t
   a
   n
   2

   (


   G

   y
   ,


   G

   x
   )
   {\displaystyle {\displaystyle \theta ={\rm {atan2}}\left(G\_{y},\,G\_{x}\right)}}
   .       
   Transform   angle 

   θ
   {\displaystyle {\displaystyle \theta }}
      to one of four directions:   0, 45, 90, 135 degrees.       
   Compute new array   

   N
   {\displaystyle {\displaystyle N}}
   :     if         

   G

   (

   p

   a
   )
   <
   G

   (
   p
   )
   <
   G

   (

   p

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

   p
   {\displaystyle {\displaystyle p}}
      is the current pixel,   

   p

   a
   {\displaystyle {\displaystyle p\_{a}}}
      and   

   p

   b
   {\displaystyle {\displaystyle p\_{b}}}
      are the two neighbour pixels in the direction of gradient,     
   then     

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

   N
   (
   p
   )
   =
   0
   {\displaystyle {\displaystyle N(p)=0}}
   .     
   Nonzero pixels in resulting array correspond to local maxima of   

   G
   {\displaystyle {\displaystyle G}}
      in direction   

   θ
   (
   p
   )
   {\displaystyle {\displaystyle \theta (p)}}
   .
4. **Tracing edges with hysteresis.**     
   At this stage two thresholds for the values of   

   G
   {\displaystyle {\displaystyle G}}
      are introduced:   

   T

   m
   i
   n
   {\displaystyle {\displaystyle T\_{min}}}
      and   

   T

   m
   a
   x
   {\displaystyle {\displaystyle T\_{max}}}
   .     
   Starting from pixels with   

   N
   (
   p
   )
   ⩾

   T

   m
   a
   x
   {\displaystyle {\displaystyle N(p)\geqslant T\_{max}}}
   ,     
   find all paths of pixels with   

   N
   (
   p
   )
   ⩾

   T

   m
   i
   n
   {\displaystyle {\displaystyle N(p)\geqslant T\_{min}}}
      and put them to the resulting image.
assistant
Admittedly laziness always prevails so an off-the-shelf function from ImageMagick is used instead.

cannyedge.c

```
#include <stdio.h>
#include <string.h>
#include <magick/MagickCore.h>

int CannyEdgeDetector(
   const char *infile, const char *outfile,
   double radius, double sigma, double lower, double upper ) {

   ExceptionInfo   *exception;
   Image           *image, *processed_image, *output;
   ImageInfo       *input_info;

   exception   = AcquireExceptionInfo();
   input_info  = CloneImageInfo((ImageInfo *) NULL);
   (void) strcpy(input_info->filename, infile);
   image       = ReadImage(input_info, exception);
   output      = NewImageList();
   processed_image = CannyEdgeImage(image,radius,sigma,lower,upper,exception);
   (void) AppendImageToList(&output, processed_image);
   (void) strcpy(output->filename, outfile);
   WriteImage(input_info, output);
                                    // after-party clean up 
   DestroyImage(image);
   output=DestroyImageList(output);
   input_info=DestroyImageInfo(input_info);
   exception=DestroyExceptionInfo(exception);
   MagickCoreTerminus();

   return 0;
}

```

cannyedge.raku

```
# 20220103 Raku programming solution
 
use NativeCall;
 
sub CannyEdgeDetector(CArray[uint8], CArray[uint8], num64, num64, num64, num64 
) returns int32 is native( '/home/hkdtam/LibCannyEdgeDetector.so' ) {*};

CannyEdgeDetector( # imagemagick.org/script/command-line-options.php#canny 
   CArray[uint8].new(  'input.jpg'.encode.list, 0), # pbs.org/wgbh/nova/next/wp-content/uploads/2013/09/fingerprint-1024x575.jpg
   CArray[uint8].new( 'output.jpg'.encode.list, 0),
   0e0, 2e0, 0.05e0, 0.05e0
)

```

Output:

```
export PKG_CONFIG_PATH=/usr/lib/pkgconfig
gcc -Wall -fPIC -shared -o LibCannyEdgeDetector.so  cannyedge.c `pkg-config --cflags --libs MagickCore`
raku -c cannyedge.raku && ./cannyedge.raku
```

Output: (Offsite image file)