Title: OpenCV Edge Detection ( cv2.Canny ) - PyImageSearch

URL Source: https://pyimagesearch.com/2021/05/12/opencv-edge-detection-cv2-canny/

Published Time: 2021-05-12T14:00:00+00:00

Markdown Content:
In this tutorial, you will learn how to perform edge detection using OpenCV and the Canny edge detector.

![Image 1](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20700%20450'%3E%3C/svg%3E)

Previously, we discussed [image gradients](https://pyimagesearch.com/2021/05/12/image-gradients-with-opencv-sobel-and-scharr/) and how they are one of the fundamental building blocks of computer vision and image processing.

A diverse image dataset is vital for understanding edge detection using the Canny Edge Detector. It provides a broad perspective on how edges can be detected in different types of images and under various conditions.

[Roboflow](https://roboflow.com/?ref=pyimagesearch) has free tools for each stage of the computer vision pipeline that will streamline your workflows and supercharge your productivity.

Sign up or Log in to your [Roboflow account](https://roboflow.com/?ref=pyimagesearch) to access state of the art dataset libaries and revolutionize your computer vision pipeline.

You can start by choosing your own datasets or using our [PyimageSearch’s assorted library of useful datasets](https://universe.roboflow.com/pyimagesearch?ref=pyimagesearch).

Bring data in any of 40+ formats to [Roboflow](https://roboflow.com/?ref=pyimagesearch), train using any state-of-the-art model architectures, deploy across multiple platforms (API, NVIDIA, browser, iOS, etc), and connect to applications or 3rd party tools.

Today, we are going to see just how important image gradients are; specifically, by examining the **Canny edge detector.**

**The Canny edge detector is arguably the most well known and the most used edge detector in all of computer vision and image processing.** While the Canny edge detector is not exactly “trivial” to understand, we’ll break down the steps into bite-sized pieces so we can understand what is going on under the hood.

Fortunately for us, since the Canny edge detector is so widely used in almost all computer vision applications, OpenCV has already implemented it for us in the `cv2.Canny` function.

We’ll also explore how to use this function to detect edges in images of our own.

**To learn how to perform edge detection with OpenCV and the Canny edge detector,** **_just keep reading._**

![Image 2](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20128%20128'%3E%3C/svg%3E)

#### Looking for the source code to this post?

[Jump Right To The Downloads Section](#download-the-code)

In the first part of this tutorial, we’ll discuss what edge detection is and why we use it in our computer vision and image processing applications.

We’ll then review the types of edges in an image, including:

*   Step edges
*   Ramp edges
*   Ridge edges
*   Roof edges

With these reviewed we can discuss the four step process to Canny edge detection

1.  Gaussian smoothing
2.  Computing the gradient magnitude and orientation
3.  Non-maxima suppression
4.  Hysteresis thresholding

We’ll then learn how to implement the Canny edge detector using OpenCV and the `cv2.Canny` function.

### **What is edge detection?**

As we discovered in the previous blog post on [image gradients](https://pyimagesearch.com/2021/05/12/image-gradients-with-opencv-sobel-and-scharr/), the gradient magnitude and orientation allow us to reveal the structure of objects in an image.

However, for the process of edge detection, the gradient magnitude is _extremely_ sensitive to noise.

For example, let’s examine the gradient representation of the following image:

![Image 3](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20700%20283'%3E%3C/svg%3E)

**Figure 1:** _Left:_ Our original input image. _Right:_ Computing the image gradient for the image on the left. Notice how the outline of the pills is revealed, but we’re also detecting “noise” inside the pill themselves.

On the _left,_ we have our original input image of a frontside and backside. And on the _right,_ we have the image gradient representation.

**As you can see, the gradient representation is a bit noisy.** Sure, we have been able to detect the actual outline of the pills. But we’re also left with a lot of “noise” inside the pills itself representing the pill imprint.

So what if we wanted to detect _just the outline_ of the pills?

That way, if we had _just_ the outline, we could extract the pills from the image using something like contour detection. Wouldn’t that be nice?

Unfortunately, simple image gradients are not going to allow us to (easily) achieve our goal.

Instead, we’ll have to use the image gradients as building blocks to create a more robust method to detect edges — the Canny edge detector.

### **The Canny edge detector**

The Canny edge detector is a multi-step algorithm used to detect a wide range of edges in images. The algorithm itself was introduced by John F. Canny in his 1986 paper, [_A Computational Approach to Edge Detection_](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.420.3300&rep=rep1&type=pdf).

If you look inside many image processing projects, you’ll most likely see the Canny edge detector being called somewhere in the source code. Whether we are [finding the distance from our camera to an object](https://pyimagesearch.com/2015/01/19/find-distance-camera-objectmarker-using-python-opencv/), [building a document scanner](https://pyimagesearch.com/2014/09/01/build-kick-ass-mobile-document-scanner-just-5-minutes/), or [finding a Game Boy screen in an image](https://pyimagesearch.com/2014/04/21/building-pokedex-python-finding-game-boy-screen-step-4-6/), the Canny edge detector will often be found as an important preprocessing step.

**More formally, an** **_edge_** **is defined as** **_discontinuities in pixel intensity,_** **or more simply, a** **_sharp_** **difference and change in pixel values.**

Here is an example of applying the Canny edge detector to detect edges in our pill image from above:

![Image 4](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20700%20283'%3E%3C/svg%3E)

**Figure 2:** Computing a Canny edge map for our pill image.

On the _left,_ we have our original input image. And on the _right,_ we have the output, or what is commonly called the **edge map**. Notice how we have only the outlines of the pill as a clear, thin white line — there is no longer any “noise” inside the pills themselves.

Before we dive deep into the Canny edge detection algorithm, let’s start by looking what types of edges there are in images:

#### **Step edge**

A **step edge** forms when there is an abrupt change in pixel intensity from one side of the discontinuity to the other. Take a look at the following graph for an example of a step edge:

![Image 5](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20500'%3E%3C/svg%3E)

**Figure 3:** An example of a step edge, as if we are taking the next step on a flight of stairs.

As the name suggests, the graph actually looks like a step — there is a sharp ![Image 6: \theta^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2013%2011'%3E%3C/svg%3E) step in the graph, indicating an abrupt change in pixel value. These types of edges tend to be easy to detect.

#### **Ramp edge**

A **ramp edge** is like a step edge, only the change in pixel intensity is not instantaneous. Instead, the change in pixel value occurs a short, but finite distance.

Here, we can see an edge that is slowly “ramping” up in change, but the change in intensity is not immediate like in a step edge:

![Image 7](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20500'%3E%3C/svg%3E)

**Figure 4:** An example of a ramp edge, as if we are driving our car up a steep hill.

#### **Ridge edge**

A **ridge edge** is similar to combining two ramp edges, one bumped right against another. I like to think of ramp edges as driving up and down a large hill or mountain:

![Image 8](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20500'%3E%3C/svg%3E)

**Figure 5:** A ridge edge is like a ramp edge, only as our car gets to the top of the hill, it drives along the plateau a bit, before driving back down the hill.

First, you slowly ascend the mountain. Then you reach the top where it levels out for a short period. And then you’re riding back down the mountain.

In the context of edge detection, a ramp edge occurs when image intensity abruptly changes, but then returns to the initial value after a short distance.

#### **Roof edge**

Lastly we have the **roof edge,** which is a type of ridge edge:

![Image 9](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20500'%3E%3C/svg%3E)

**Figure 6:** In a roof edge, there is no plateau at the top. We simply drive to the top of the hill and immediately head back down.

Unlike the ridge edge where there is a short, finite plateau at the top of the edge, the roof edge has no such plateau. Instead, we slowly ramp up on either side of the edge, but the very top is a pinnacle and we simply fall back down the bottom.

### **Canny edge detection in a nutshell**

Now that we have reviewed the various types of edges in an image, let’s discuss the actual Canny edge detection algorithm, which is a multi-step process consisting of:

1.  Applying Gaussian smoothing to the image to help reduce noise
2.  Computing the ![Image 10: G_{x}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2020%2014'%3E%3C/svg%3E) and ![Image 11: G_{y}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2019%2016'%3E%3C/svg%3E) image gradients using the Sobel kernel
3.  Applying non-maxima suppression to keep only the local maxima of gradient magnitude pixels that are pointing in the direction of the gradient
4.  Defining and applying the ![Image 12: T_\text{upper}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2039%2016'%3E%3C/svg%3E) and ![Image 13: T_\text{lower}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2036%2015'%3E%3C/svg%3E) thresholds for Hysteresis thresholding

Let’s discuss each of these steps.

#### **Step #1: Gaussian smoothing**

This step is fairly intuitive and straightforward. As we learned from our tutorial on [smoothing and blurring](https://pyimagesearch.com/2021/04/28/opencv-smoothing-and-blurring/), smoothing an image allows us to ignore much of the detail and instead focus on the actual structure.

This also makes sense in the context of edge detection — we are not interested in the actual _detail_ of the image. Instead, we want to apply edge detection to find the _structure_ and _outline_ of the objects in the image so we can further process them.

#### **Step #2: Gradient magnitude and orientation**

Now that we have a smoothed image, we can compute the gradient orientation and magnitude, just like we did in the [previous post](https://pyimagesearch.com/2021/05/12/image-gradients-with-opencv-sobel-and-scharr/).

However, as we have seen, the gradient magnitude is quite susceptible to noise and does not make for the best edge detector. We need to add two more steps on to the process to extract better edges.

#### **Step #3: Non-maxima suppression**

Non-maxima suppression sounds like a complicated process, but it’s really not — it’s simply an **edge thinning** process.

After computing our gradient magnitude representation, the edges themselves are still quite noisy and blurred, but in reality there should only be _one_ edge response for a given region, not a whole clump of pixels reporting themselves as edges.

To remedy this, we can apply edge thinning using non-maxima suppression. To apply non-maxima suppression we need to examine the gradient magnitude ![Image 14: G](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2013%2011'%3E%3C/svg%3E) and orientation ![Image 15: \theta](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%208%2011'%3E%3C/svg%3E) at each pixel in the image and:

*   Compare the current pixel to the ![Image 16: 3 \times 3](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2036%2012'%3E%3C/svg%3E) neighborhood surrounding it
*   Determine in which direction the orientation is pointing:
    *   If it’s pointing towards the north or south, then examine the north and south magnitude
    *   If the orientation is pointing towards the east or west, then examine the east and west pixels
*   If the center pixel magnitude is _greater than_ both the pixels it is being compared to, then preserve the magnitude; otherwise, discard it

Some implementations of the Canny edge detector round the value of ![Image 17: \theta](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%208%2011'%3E%3C/svg%3E) to either ![Image 18: 0^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2013%2012'%3E%3C/svg%3E), ![Image 19: 45^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2021%2014'%3E%3C/svg%3E), ![Image 20: 90^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2021%2012'%3E%3C/svg%3E), or ![Image 21: 135^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2014'%3E%3C/svg%3E), and then use the rounded angle to compare not only the north, south, east, and west pixels, but also the corner top-left, top-right, bottom-right, and bottom-left pixels as well.

But, let’s keep things simple and view an example of applying non-maxima suppression for an angle of ![Image 22: \theta=90^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2051%2012'%3E%3C/svg%3E) degrees:

![Image 23](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20500'%3E%3C/svg%3E)

**Figure 7:** Let’s pretend the gradient orientation for this image is 90 degrees. Applying non-maxima suppression, we would need to examine the north and south pixels and take the maximum value — which is 162.

In the example above, we are going to pretend that the gradient orientation is ![Image 24: \theta=90^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2051%2012'%3E%3C/svg%3E) (it’s actually not, but that’s okay, this is only an example).

Given that our gradient orientation is pointing north, we need to examine _both_ the north and south pixels. The central pixel value of _93_ is greater than the south value of _26_, so we’ll discard the _26_. However, examining the north pixel we see that the value is _162_ — we’ll keep this value of _162_ and suppress (i.e., set to _0_) the value of _93_ since _93 < 162._

Here’s another example of applying non-maxima suppression for when ![Image 25: \theta=180^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2059%2013'%3E%3C/svg%3E):

![Image 26](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20500'%3E%3C/svg%3E)

**Figure 8:** Applying non-maxima suppression in this image ends in us discarding the 93 and keeping the 104 and 139 values.

Notice how the central pixel is _less than both_ the east and west pixels. According to our non-maxima suppression rules above (rule #3), we need to discard the pixel value of _93_ and keep the east and west values of _104_ and _139_, respectively.

As you can see, non-maxima suppression for edge detection is not as hard as it seems!

#### **Step #4: Hysteresis thresholding**

Finally, we have the hysteresis thresholding step. Just like non-maxima suppression, it’s actually much easier than it sounds.

Even after applying non-maxima suppression, we may need to remove regions of an image that are not technically edges, but still respond as edges after computing the gradient magnitude and applying non-maximum suppression.

To ignore these regions of an image, we need to define two thresholds: ![Image 27: T_\text{upper}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2039%2016'%3E%3C/svg%3E) and ![Image 28: T_\text{lower}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2036%2015'%3E%3C/svg%3E).

Any gradient value ![Image 29: G > T_\text{upper}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2074%2016'%3E%3C/svg%3E) **_is sure to be an edge._**

Any gradient value ![Image 30: G < T_\text{lower}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2071%2015'%3E%3C/svg%3E) **_is definitely not an edge,_** so immediately discard these regions.

And any gradient value that falls into the range ![Image 31: T_\text{lower} < G < T_\text{upper}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20133%2016'%3E%3C/svg%3E) needs to undergo additional tests:

1.  If the particular gradient value is connected to a **strong edge** (i.e., ![Image 32: G > T_\text{upper}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2074%2016'%3E%3C/svg%3E)), then mark the pixel as an edge.
2.  If the gradient pixel is _not_ connected to a strong edge, then discard it.

Hysteresis thresholding is actually better explained visually:

![Image 33](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20500'%3E%3C/svg%3E)

**Figure 9:** An example of applying hysteresis to a set of edges.

Setting these threshold ranges is not always a trivial process.

If the threshold range is _too wide,_ then we’ll get many false edges instead of being about to find _just_ the structure and outline of an object in an image.

Similarly, if the threshold range is _too tight_, we won’t find many edges at all and could be at risk of missing the structure/outline of the object entirely!

Later on in this series of posts, I’ll demonstrate how we can _automatically_ tune these threshold ranges with practically zero effort. But for the time being, let’s see how edge detection is actually performed inside OpenCV.

### **Configuring your development environment**

To follow this guide, you need to have the OpenCV library installed on your system.

Luckily, OpenCV is pip-installable:

$ pip install opencv-contrib-python

**If you need help configuring your development environment for OpenCV, I _highly recommend_ that you read my** [**_pip install OpenCV_ guide**](https://pyimagesearch.com/2018/09/19/pip-install-opencv/) — it will have you up and running in a matter of minutes.

### **Having problems configuring your development environment?**

[![Image 34](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20334'%3E%3C/svg%3E)](https://pyimagesearch.com/pyimagesearch-university/)

**Figure 10:** Having trouble configuring your dev environment? Want access to pre-configured Jupyter Notebooks running on Google Colab? Be sure to join [PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/) — you’ll be up and running with this tutorial in a matter of minutes.

All that said, are you:

*   Short on time?
*   Learning on your employer’s administratively locked system?
*   Wanting to skip the hassle of fighting with the command line, package managers, and virtual environments?
*   **Ready to run the code** **_right now_** **on your Windows, macOS, or Linux system?**

Then join [PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/) today!

**Gain access to Jupyter Notebooks for this tutorial and other PyImageSearch guides that are** **_pre-configured_** **to run on Google Colab’s ecosystem right in your web browser!** No installation required.

And best of all, these Jupyter Notebooks will run on Windows, macOS, and Linux!

### **Project structure**

Before we can compute edges using OpenCV and the Canny edge detector, let’s first review our project directory structure.

Be sure to access the **_“Downloads”_** section of this tutorial to retrieve the source code and example images:

$ tree . --dirsfirst
.
├── images
│   ├── clonazepam\_1mg.png
│   └── coins.png
└── opencv\_canny.py

1 directory, 3 files

We have a single Python script to review, `opencv_canny.py`, which will apply the Canny edge detector.

Inside the `images` directory, we have two example images that we’ll apply the Canny edge detector to.

### **Implementing the Canny edge detector with OpenCV**

We are now ready to implement the Canny edge detector using OpenCV and the `cv2.Canny` function!

Open the `opencv_cann.py` file in your project structure and let’s review the code:

\# import the necessary packages
import argparse
import cv2

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add\_argument("-i", "--image", type=str, required=True,
	help="path to input image")
args = vars(ap.parse\_args())

We start on **Lines 2 and 3** by importing our required Python packages — we need only `argparse` for command line arguments and `cv2` for our OpenCV bindings.

Command line arguments are parsed on **Lines 6-9**. A single switch is required, `--image`, which is the path to the input image we wish to apply edge detection to.

Let’s now load our image and preprocess it:

\# load the image, convert it to grayscale, and blur it slightly
image = cv2.imread(args\["image"\])
gray = cv2.cvtColor(image, cv2.COLOR\_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)

# show the original and blurred images
cv2.imshow("Original", image)
cv2.imshow("Blurred", blurred)

While Canny edge detection can be applied to an RGB image by detecting edges in each of the separate Red, Green, and Blue channels separately and combining the results back together, we _almost always_ want to apply edge detection to a single channel, grayscale image (**Line 13**) — this ensures that there will be less noise during the edge detection process.

Secondly, while the Canny edge detector does apply blurring prior to edge detection, we’ll also want to (normally) apply extra blurring prior to the edge detector to further reduce noise and allow us to find the objects in an image (**Line 14**).

**Lines 17 and 18** then display our original and blurred images on our screen.

We’re now ready to perform edge detection:

\# compute a "wide", "mid-range", and "tight" threshold for the edges
# using the Canny edge detector
wide = cv2.Canny(blurred, 10, 200)
mid = cv2.Canny(blurred, 30, 150)
tight = cv2.Canny(blurred, 240, 250)

# show the output Canny edge maps
cv2.imshow("Wide Edge Map", wide)
cv2.imshow("Mid Edge Map", mid)
cv2.imshow("Tight Edge Map", tight)
cv2.waitKey(0)

Applying the cv2.Canny function to detect edges is performed on **Lines 22-24**.

The first parameter to `cv2.Canny` is the image we want to detect edges in — in this case, our grayscale, blurred image. We then supply the ![Image 35: T_\text{lower}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2036%2015'%3E%3C/svg%3E) and ![Image 36: T_\text{upper}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2039%2016'%3E%3C/svg%3E) thresholds, respectively.

On **Line 22,** we apply a _wide_ threshold, a _mid-range_ threshold on **Line 23**, and a _tight_ threshold on **Line 24**.

**_Note:_** _You can convince yourself that these are wide, mid-range, and tight thresholds by plotting the threshold values on_ **_Figure 11_** _and_ **_Figure 12_**_._

Finally, **Lines 27-30** display the output edge maps on our screen.

### **Canny edge detection results**

Let’s put the Canny edge detector to work for us.

Start by accessing the **_“Downloads”_** section of this tutorial to retrieve the source code and example images.

From there, open a terminal and execute the following command:

$ python opencv\_canny.py --image images/coins.png

![Image 37](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20700%20324'%3E%3C/svg%3E)

**Figure 11:** Applying Canny edge detection to a collection of coins using a wide range, mid range, and tight range of thresholds.

In the above figure, the _top-left_ image is our input image of coins. We then blur the image slightly to help smooth details and aid in edge detection on the _top-right._

The wide range, mid-range, and tight range edge maps are then displayed on the _bottom_, respectively.

Using a wide edge map captures the outlines of the coins, but also captures many of the edges of faces and symbols inside the coins.

The mid-range edge map also performs similarly.

Finally, the tight range edge map is able to capture just the outline of the coins while discarding the rest.

Let’s look at another example:

$ python opencv\_canny.py --image images/clonazepam\_1mg.png

![Image 38](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20700%20382'%3E%3C/svg%3E)

**Figure 12:** Using OpenCV and the Canny edge detector to compute our edge map.

Unlike **Figure 11**, the Canny thresholds for **Figure 12** give us nearly reversed results.

Using the wide range edge map, we are able to find the outlines of the pills.

The mid-range edge map also gives us the outlines of the pills, but also some of the digits imprinted on the pill.

Finally, the tight edge map does not help us at all — the outline of the pills is nearly completely lost.

### **How do we choose optimal Canny edge detection parameters?**

As you can tell, depending on your input image you’ll need dramatically different hysteresis threshold values — and tuning these values can be a real pain. You might be wondering, is there a way to _reliably_ tune these parameters without simply guessing, checking, and viewing the results?

**The answer is** **_yes!_**

We’ll be discussing that very technique in our next lesson.

### What's next? We recommend [PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/?utm_source=blogPost&utm_medium=bottomBanner&utm_campaign=What%27s%20next%3F%20I%20recommend).

**Course information:**  
84 total classes • 114+ hours of on-demand code walkthrough videos • Last updated: February 2024  
★★★★★ 4.84 (128 Ratings) • 16,000+ Students Enrolled

**I strongly believe that if you had the right teacher you could _master_ computer vision and deep learning.**

Do you think learning computer vision and deep learning has to be time-consuming, overwhelming, and complicated? Or has to involve complex mathematics and equations? Or requires a degree in computer science?

That’s _not_ the case.

All you need to master computer vision and deep learning is for someone to explain things to you in _simple, intuitive_ terms. _And that’s exactly what I do_. My mission is to change education and how complex Artificial Intelligence topics are taught.

If you're serious about learning computer vision, your next stop should be PyImageSearch University, the most comprehensive computer vision, deep learning, and OpenCV course online today. Here you’ll learn how to _successfully_ and _confidently_ apply computer vision to your work, research, and projects. Join me in computer vision mastery.

**Inside PyImageSearch University you'll find:**

*   ✓ **84 courses** on essential computer vision, deep learning, and OpenCV topics
*   ✓ **84 Certificates** of Completion
*   ✓ **114+ hours** of on-demand video
*   ✓ **Brand new courses released _regularly_**, ensuring you can keep up with state-of-the-art techniques
*   ✓ **Pre-configured Jupyter Notebooks in Google Colab**
*   ✓ Run all code examples in your web browser — works on Windows, macOS, and Linux (no dev environment configuration required!)
*   ✓ Access to **centralized code repos for _all_ 536+ tutorials** on PyImageSearch
*   ✓ **Easy one-click downloads** for code, datasets, pre-trained models, etc.
*   ✓ **Access** on mobile, laptop, desktop, etc.

[Click here to join PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/?utm_source=blogPost&utm_medium=bottomBanner&utm_campaign=What%27s%20next%3F%20I%20recommend)

**Summary**
-----------

In this lesson, we learned how to use image gradients, one of the most fundamental building blocks of computer vision and image processing, to create an edge detector.

Specifically, we focused on the **Canny edge detector,** the most well known and most used edge detector in the computer vision community.

From there, we examined the steps of the Canny edge detector including:

1.  Smoothing
2.  Computing image gradients
3.  Applying non-maxima suppression
4.  Utilizing hysteresis thresholding

We then took our knowledge of the Canny edge detector and used it to apply OpenCV’s `cv2.Canny` function to detect edges in images.

However, one of the biggest drawbacks of the Canny edge detector is tuning the upper and lower thresholds for the hysteresis step. If our threshold was too wide, we would get too many edges. And if our threshold was too tight, we would not detect many edges at all!

To aid us in parameter tuning you’ll learn how to apply _automatic_ Canny edge detection in the next tutorial in this series.

**To download the source code to this post (and be notified when future tutorials are published here on PyImageSearch), _simply enter your email address in the form below!_**

![Image 39](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20410%20567'%3E%3C/svg%3E)

#### Download the Source Code and FREE 17-page Resource Guide

Enter your email address below to get a .zip of the code and a **FREE 17-page Resource Guide on Computer Vision, OpenCV, and Deep Learning.** Inside you'll find my hand-picked tutorials, books, courses, and libraries to help you master CV and DL!