|
| 1 | +# --- |
| 2 | +# cover: assets/canny.gif |
| 3 | +# title: Canny Edge Detection |
| 4 | +# author: Ashwani Rathee; Johnny Chen |
| 5 | +# date: 2021-3-20 |
| 6 | +# --- |
| 7 | + |
| 8 | +# Canny filter is still a powerful edge detector even though it's invented in 1986 [1]. |
| 9 | +# This demo shows you how to use our newly developed package `ImageEdgeDetection` with |
| 10 | +# Canny filter as an example. |
| 11 | + |
| 12 | +using Images, ImageEdgeDetection, Noise |
| 13 | +using ImageEdgeDetection: Percentile |
| 14 | +using TestImages |
| 15 | + |
| 16 | +# !!! info |
| 17 | +# `ImageEdgeDetection.jl` rewrites many functions which previously exists in `Images.jl` and |
| 18 | +# will be reexported in `Images` in the future, so you don't need to `using ImageEdgeDetection` |
| 19 | +# explicitly after that. |
| 20 | + |
| 21 | +# In JuliaImages, any `AbstractArray` can be treated as an image. In this demo, we'll use a generated |
| 22 | +# image for illustration purpose. |
| 23 | + |
| 24 | +# First, we create our test image. |
| 25 | + |
| 26 | +function make_simple_image(sz) |
| 27 | + img_gray = zeros(Gray{Float64}, sz...) |
| 28 | + fill_region = map(x->x÷4:3x÷4, sz) |
| 29 | + img_gray[fill_region...] .= 1 |
| 30 | + img_rot = imrotate(img_gray, pi/4) |
| 31 | + |
| 32 | + ## Corrupt the image with blur and noise, it makes our canny edge detection |
| 33 | + ## function works a little harder since the canny filter is based on the idea |
| 34 | + ## of finding gradients. |
| 35 | + img_gauss = imfilter(img_rot, Kernel.gaussian(2)) |
| 36 | + |
| 37 | + ## We use `salt_pepper` filter from `Noise.jl`. Salt-and-pepper noise in general |
| 38 | + ## is a noise that modifies a pixel with two different values of noise. |
| 39 | + ## Here we only random set pixels to white. |
| 40 | + img_noise = salt_pepper(img_gauss, 0.05, salt_prob = 0, pepper = 0.9) |
| 41 | +end |
| 42 | +img = make_simple_image((200, 200)) |
| 43 | + |
| 44 | +# `ImageEdgeDetection` offers a unified API `detect_edges(img, alg)` with |
| 45 | +# various algorithms. In this demo we'll show how to use the `Canny` |
| 46 | +# operator [1]. First we'll need to create an algorithm instance of `Canny`. |
| 47 | + |
| 48 | +alg = Canny(spatial_scale=1, high=Percentile(80), low=Percentile(20)) |
| 49 | + |
| 50 | +# and then apply this instance as parameters to `detect_edges`, with the Julia |
| 51 | +# [multiple dispatch mechanism](https://docs.julialang.org/en/v1/manual/methods/#Methods), |
| 52 | +# `detect_edges` knows which algorithm implementation should be called with |
| 53 | +# the `alg` information. |
| 54 | + |
| 55 | +edges = detect_edges(img, alg) |
| 56 | +mosaicview(img, edges; nrow=1) |
| 57 | + |
| 58 | +# Now let's see how different Canny parameters changes the result, and also see |
| 59 | +# how it works on real world images: |
| 60 | + |
| 61 | +cameraman = testimage("cameraman") |
| 62 | +canny(σ) = Canny(spatial_scale=σ, high=Percentile(80), low=Percentile(20)) |
| 63 | +simple_results = map(σ->detect_edges(img, canny(σ)), 1:5) |
| 64 | +cameraman_results = map(σ->detect_edges(cameraman, canny(σ)), 1:5) |
| 65 | + |
| 66 | +mosaicview( |
| 67 | + mosaicview(img, cameraman), |
| 68 | + map(mosaicview, simple_results, cameraman_results)...; |
| 69 | + nrow=1 |
| 70 | +) |
| 71 | + |
| 72 | +# As you can see, higher `spatial_scale` tells the Canny operator to ignore small |
| 73 | +# details and thus gives a "clean" edge result; whether it is a correct/useful edge |
| 74 | +# result depends on how you interpret it. |
| 75 | + |
| 76 | +save("assets/canny.gif", cat(cameraman, cameraman_results...; dims=3); fps=2) #src |
| 77 | + |
| 78 | +# # References |
| 79 | + |
| 80 | +# [1] J. Canny, "A Computational Approach to Edge Detection," in _IEEE Transactions on Pattern Analysis and Machine Intelligence_, vol. |
| 81 | +# PAMI-8, no. 6, pp. 679-698, Nov. 1986, doi: 10.1109/TPAMI.1986.4767851. |
0 commit comments