Title: Image Gradients with OpenCV (Sobel and Scharr) - PyImageSearch

URL Source: https://pyimagesearch.com/2021/05/12/image-gradients-with-opencv-sobel-and-scharr/

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

Markdown Content:
In this tutorial, you will learn about image gradients and how to compute Sobel gradients and Scharr gradients using OpenCV’s `cv2.Sobel` function.

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

Image gradients are a fundamental building block of many computer vision and image processing routines.

*   We use gradients for detecting edges in images, which allows us to find contours and outlines of objects in images
*   We use them as inputs for quantifying images through feature extraction — in fact, highly successful and well-known image descriptors such as Histogram of Oriented Gradients and SIFT are built upon image gradient representations
*   Gradient images are even used to construct saliency maps, which highlight the subjects of an image

While image gradients are not often discussed in detail since other, more powerful and interesting methods build on top of them, we are going to take the time and discuss them in detail.

**To learn how to compute Sobel and Scharr gradients with OpenCV** **_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 image gradients, what they are used for, and how we can compute them manually (that way we have an intuitive understanding).

From there we’ll learn about Sobel and Scharr kernels, which are [convolutional filters](https://pyimagesearch.com/2016/07/25/convolutions-with-opencv-and-python/), allowing us to compute the image gradients automatically using OpenCV and the `cv2.Sobel` function (we simply pass in a Scharr-specific argument to `cv2.Sobel` to compute Scharr gradients).

We’ll then configure our development environment and review our project directory structure, where you’ll implement two Python scripts:

1.  One to compute the gradient magnitude
2.  And another to compute gradient orientation

Together, these computations power traditional computer vision techniques such as SIFT and Histogram of Oriented Gradients.

We’ll wrap up this tutorial with a discussion of our results.

### **What are image gradients?**

As I mentioned in the introduction, image gradients are used as the basic building blocks in many computer vision and image processing applications.

However, the main application of image gradients lies within **edge detection.**

As the name suggests, edge detection is the process of finding edges in an image, which reveals structural information regarding the objects in an image. Edges could therefore correspond to:

*   Boundaries of an object in an image
*   Boundaries of shadowing or lighting conditions in an image
*   Boundaries of “parts” within an object

Below is an image of edges being detected in an image:

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

**Figure 1:** An example of extracting edges from images. Computing image gradients is a pre-processing step to detecting edges in images.

On the _left,_ we have our original input image. On the _right,_ we have our image with detected edges — commonly called an **edge map.**

The image on the right clearly reveals the structure and outline of the objects in an image. Notice how the outline of the notecard, along with the words written on the notecard, are clearly revealed.

Using this outline, we could then apply contours to extract the actual objects from the region or quantify the shapes so we can identify them later. Just as image gradients are building blocks for methods like edge detection, edge detection is also a building block for developing a complete computer vision application.

### **Computing image gradients manually**

So how do we go about finding these edges in an image?

The first step is to compute the gradient of the image. **Formally, an image gradient is defined as a directional change in image intensity.**

Or put more simply, at each pixel of the input (grayscale) image, a gradient measures the change in pixel intensity in a given direction. By estimating the direction or _orientation_ along with the _magnitude_ (i.e. how strong the change in direction is), we are able to detect regions of an image that look like edges.

In practice, image gradients are estimated using [kernels or filters](https://pyimagesearch.com/2016/07/25/convolutions-with-opencv-and-python/), just like we did using [smoothing and blurring](https://pyimagesearch.com/2021/04/28/opencv-smoothing-and-blurring/) — but this time we are trying to find the structural components of the image. Our goal here is to find the change in direction to the central pixel marked in red in both the _x_ and _y_ direction:

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

**Figure 2:** When computing image gradients, we need to find the difference in image pixel intensities along both the _x_ and _y_ direction within the given kernel size.

However, before we dive into kernels for gradient estimation, let’s actually go through the process of computing the gradient manually. The first step is to simply find and mark the north, south, east and west pixels surrounding the center pixel:

![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:** Labeling the north, south, east, and west pixel surrounding the center red pixel.

In the image above, we examine the _3×3_ neighborhood surrounding the central pixel. Our _x_ values run from left to right, and our _y_ values from top to bottom. In order to compute any changes in direction we’ll need the north, south, east, and west pixels, which are marked on **Figure 3.**

If we denote our input image as _I_, then we define the north, south, east, and west pixels using the following notation:

Again, these four values are _critical_ in computing the changes in image intensity in both the _x_ and _y_ direction.

To demonstrate this, let’s compute the **vertical change** or the **_y_****\-change** by taking the difference between the south and north pixels:

_G__y_ = _I_(_x_, _y_ + 1) _–_ _I_(_x_, _y_ − 1)

Similarly, we can compute the **horizontal change** or the **_x_****\-change** by taking the difference between the east and west pixels:

_G__x_ = _I_(_x_ + 1, _y_) _–_ _I_(_x_ − 1, _y_)

Awesome — so now we have _G__x_ and _G__y_, which represent the change in image intensity for the central pixel in both the _x_ and _y_ direction.

So now the big question becomes: _what do we do with these values?_

To answer that, we’ll need to define two new terms — the **gradient magnitude** and the **gradient orientation.**

**The gradient magnitude is used to measure how strong the change in image intensity is.** The gradient magnitude is a real-valued number that quantifies the “strength” of the change in intensity.

**The gradient orientation is used to determine in which direction the change in intensity is pointing.** As the name suggests, the gradient orientation will give us an angle or ? that we can use to quantify the direction of the change.

For example, take a look at the following visualization of gradient orientation:

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

**Figure 4:** Computing change in orientation (in degrees) from the central pixel. _Left:_ The change in orientation from the central pixel (−90 degrees). _Right:_ Notice how the change this time is more angled (−45 degrees). We’ll review how to actually perform this calculation later in this tutorial.

On the left, we have a 3_×_3 region of an image, where the top half of the image is _white_ and the bottom half of the image is _black_. The gradient orientation is thus equal to ![Image 7: \theta=-90^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2064%2012'%3E%3C/svg%3E).

And on the _right_, we have another 3_×_3 neighborhood of an image, where the upper triangular region is _white_ and the lower triangular region is _black_. Here we can see the change in direction is equal to ![Image 8: \theta = -45^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2064%2014'%3E%3C/svg%3E).

So I’ll be honest — when I was first introduced to computer vision and image gradients, **Figure 4** confused the living hell out of me. I mean, _how in the world_ did we arrive at these calculations of ??! Intuitively, the changes in direction make sense since we can actually see and visualize the result.

**But how do we** **_actually_** **go about computing the gradient orientation and magnitude?**

I’m glad you asked. Let’s see if we can demystify the gradient orientation and magnitude calculation.

Let’s go ahead and start off with an image of our trusty 3_×_3 neighborhood of an image:

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

**Figure 5:** Our standard pixel neighborhood representation — the red pixel is the central pixel, with its 8-neighborhood surrounding it.

Here, we can see that the central pixel is marked in _red_. The next step in determining the gradient orientation and magnitude is actually to compute the changes in gradient in both the _x_ and _y_ direction. Luckily, we already know how to do this — they are simply the _G__x_ and _G__y_ values that we computed earlier!

Using both _Gx_ and _Gy_, we can apply some basic trigonometry to compute the gradient magnitude _G_, and orientation ?:

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

**Figure 6:** The basic trigonometry of computing the gradient magnitude and orientation.

Seeing this example is what really solidified my understanding of gradient orientation and magnitude. Inspecting this triangle you can see that the gradient magnitude _G_ is the hypotenuse of the triangle. Therefore, all we need to do is apply the Pythagorean theorem and we’ll end up with the gradient magnitude:

![Image 11: G = \sqrt{G_{x}^{2} + G_{y}^{2}}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20111%2021'%3E%3C/svg%3E)

The gradient orientation can then be given as the ratio of _Gy_ to _Gx_. Technically we would use the ![Image 12: \tan^{-1}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2037%2015'%3E%3C/svg%3E) to compute the gradient orientation, but this could lead to undefined values — since we are computer scientists, we’ll use the ![Image 13: \text{arctan2}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2052%2013'%3E%3C/svg%3E) function to account for the different quadrants instead (if you’re unfamiliar with the arc-tangent, you can read more about it [here](https://en.wikipedia.org/wiki/Atan2)):

![Image 14: \theta = \text{arctan2}(G_{y}, G_{x}) \times \left(\displaystyle\frac{180}{\pi}\right)](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20212%2041'%3E%3C/svg%3E)

The ![Image 15: \text{arctan2}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2052%2013'%3E%3C/svg%3E) function gives us the orientation in radians, which we then convert to degrees by multiplying by the ratio of 180/?.

Let’s go ahead and manually compute _G_ and ? so we can see how the process is done:

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

**Figure 7:** _Left:_ The pixel values of the 8-neighborhood. _Right:_ A visualization of the 90-degree gradient orientation.

In the above image, we have an image where the upper-third is white and the bottom two-thirds is black. Using the equations for _G__x_ and _G__y_, we arrive at:

_G__x_ = 0 − 0 = 0

and:

__Gy__ = 0 − 255 = −255

Plugging these values into our gradient magnitude equation we get:

![Image 17: G = \sqrt{0^{2} + (-255)^{2}} = 255](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20188%2021'%3E%3C/svg%3E)

As for our gradient orientation:

![Image 18: \theta = \text{arctan2}(-255, 0) \times \left(\displaystyle\frac{180}{\pi}\right) = -90^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20277%2041'%3E%3C/svg%3E)

See, computing the gradient magnitude and orientation isn’t too bad!

Let’s do another example for fun:

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

**Figure 8:** _Left:_ Our raw pixel intensity values. _Right:_ The gradient orientation of −135 degrees.

In this particular image, we can see that the lower-triangular region of the neighborhood is white, while the upper-triangular neighborhood is black. Computing both _Gx_ and _Gy_ we arrive at:

_Gx_ = 0 − 255 = −255 and _Gy_ = 255 − 0 = 255

which leaves us with a gradient magnitude of:

![Image 20: G = \sqrt{(-255)^{2} + 255^{2}} = 360.62](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20226%2021'%3E%3C/svg%3E)

and a gradient orientation of:

![Image 21: \theta = \text{arctan2}(255, -255) \times \left(\displaystyle\frac{180}{\pi}\right) = 135^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20288%2041'%3E%3C/svg%3E)

Sure enough, our gradient is pointing _down_ and to the _left_ at an angle of ![Image 22: 135^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2014'%3E%3C/svg%3E).

Of course, we have only computed our gradient orientation and magnitude for two unique pixel values: 0 and 255. Normally you would be computing the orientation and magnitude on a _grayscale_ image where the valid range of values would be _\[0, 255\]_.

### **Sobel and Scharr Filter**

Now that we have learned how to compute gradients manually, let’s look at how we can _approximate_ them using kernels, which will give us a tremendous boost in speed. Just like we used kernels to smooth and blur an image, we can also use kernels to compute our gradients.

We’ll start off with the **Sobel method or Sobel filter**, which actually uses two kernels/filters: one for detecting horizontal changes in direction and the other for detecting vertical changes in direction:

![Image 23: G_{x} = \begin{bmatrix}-1 & 0 & +1 \\  -2 & 0 & +2 \\  -1 & 0 & +1\end{bmatrix} \quad \text{and} \quad G_{y} = \begin{bmatrix}-1 & -2 & -1 \\  0 & 0 & 0 \\  +1 & +2 & +1\end{bmatrix}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20353%2060'%3E%3C/svg%3E)

Given an input image neighborhood below, let’s compute the Sobel approximation to the gradient:

![Image 24: I_{i,j} = \begin{bmatrix}93 & 139 & 101 \\  26 & 252 & 196 \\  135 & 230 & 18\end{bmatrix}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20160%2060'%3E%3C/svg%3E)

Therefore:

![Image 25: G_{x} = \sum\begin{bmatrix}-1 \times 93 & 0 \times 139 & 1 \times 101 \\  -2 \times 26 & 0 \times 252 & 2 \times 196 \\  -1 \times 135 & 0 \times 230 & 1 \times 18\end{bmatrix} = \sum\begin{bmatrix}-93 & 0 & 101 \\  -52 & 0 & 392 \\  -135 & 0 & 18\end{bmatrix} = 231](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20490%2060'%3E%3C/svg%3E)

and:

![Image 26: G_{y} = \sum\begin{bmatrix}-1 \times 93 & -2 \times 139 & -1 \times 101 \\  0 \times 26 & 0 \times 252 & 0 \times 196 \\  1 \times 135 & 2 \times 230 & 1 \times 18\end{bmatrix} ](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20296%2060'%3E%3C/svg%3E)

![Image 27: = \sum\begin{bmatrix}-93 & -278 & -101 \\  0 & 0 & 0 \\  135 & 460 & 18\end{bmatrix} = 141](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20236%2060'%3E%3C/svg%3E)

Given these values of _Gx_ and _Gy_, it would then be trivial to compute the gradient magnitude _G_ and orientation ?:

![Image 28: G = \sqrt{231^{2} + 141^{2}} = 270.63 \quad  \text{and}  \quad \theta = \text{arctan2}(141, 231) \times \displaystyle\frac{180}{\pi} = 31.4^{\circ}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20512%2035'%3E%3C/svg%3E)

We could also use the **Scharr kernel** instead of the Sobel filter which may give us better approximations to the gradient:

![Image 29: G_{x} = \begin{bmatrix}+3 & 0 & -3 \\  +10 & 0 & -10 \\  +3 & 0 & -3\end{bmatrix} \quad   \text{and}   \quad G_{y} = \begin{bmatrix}+3 & +10 & +3 \\  0 & 0 & 0 \\  -3 & -10 & -3\end{bmatrix}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20377%2060'%3E%3C/svg%3E)

The exact reasons as to why the Scharr kernel could lead to better approximations are heavily rooted in mathematical details and are well outside our discussion of image gradients.

If you’re interested in reading more about the Scharr versus Sobel filters and constructing an optimal image gradient approximation (and can read German), [I would suggest taking a look at Scharr’s dissertation on the topic](http://archiv.ub.uni-heidelberg.de/volltextserver/962/).

Overall, gradient magnitude and orientation make for excellent features and image descriptors when quantifying and abstractly representing an image.

But for edge detection, the gradient representation is extremely sensitive to local noise. We’ll need to add in a few more steps to create an actual robust edge detector — we’ll be covering these steps in detail in the next tutorial where we review the Canny edge detector.

### **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 30](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20674%20450'%3E%3C/svg%3E)](https://pyimagesearch.com/pyimagesearch-university/)

**Figure 9:** 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 implement image gradients with OpenCV, 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
│   ├── bricks.png
│   ├── clonazepam\_1mg.png
│   ├── coins01.png
│   └── coins02.png
├── opencv\_magnitude\_orientation.py
└── opencv\_sobel\_scharr.py

1 directory, 6 files

We will implement two Python scripts today:

1.  `opencv_sobel_scharr.py`: Utilizes the Sobel and Scharr filters to compute gradient information for an input image.
2.  `opencv_magnitude_orientation.py`: Takes the output of a Sobel/Scharr filter and then computes gradient magnitude and orientation information.

The `images` directory contains various example images that we’ll apply both of these scripts to.

### **Implementing Scharr and **Sobel** Filters with OpenCV**

Up until this point, we have been discussing a lot of the theory and mathematical details surrounding image kernels. But how do we actually _apply_ what we have learned using OpenCV?

I’m so glad you asked.

Open a new file, name it `opencv_sobel_scharr.py`, and let’s get coding:

\# 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")
ap.add\_argument("-s", "--scharr", type=int, default=0,
	help="path to input image")
args = vars(ap.parse\_args())

**Lines 2 and 3** import our required Python packages — all we need is `argparse` for command line arguments and `cv2` for our OpenCV bindings.

We then have two command line arguments:

1.  `--image`: The path to our input image residing on disk that we want to compute Sobel/Scharr gradients for.
2.  `--scharr`: Whether or not we are computing Scharr gradients. By default, we’ll compute Soble gradients. If we pass in a value _\> 0_ for this flag then we’ll compute Scharr gradients instead

Let’s now load our image and process it:

\# load the image, convert it to grayscale, and display the original
# grayscale image
image = cv2.imread(args\["image"\])
gray = cv2.cvtColor(image, cv2.COLOR\_BGR2GRAY)
cv2.imshow("Gray", gray)

**Lines 15-17** load our image from disk, convert it to grayscale (since we compute gradient representations on the grayscale version of the image), and display it on our screen.

\# set the kernel size, depending on whether we are using the Sobel
# filter or the Scharr operator, then compute the gradients along
# the x and y axis, respectively
ksize = -1 if args\["scharr"\] > 0 else 3
gX = cv2.Sobel(gray, ddepth=cv2.CV\_32F, dx=1, dy=0, ksize=ksize)
gY = cv2.Sobel(gray, ddepth=cv2.CV\_32F, dx=0, dy=1, ksize=ksize)

# the gradient magnitude images are now of the floating point data
# type, so we need to take care to convert them back a to unsigned
# 8-bit integer representation so other OpenCV functions can operate
# on them and visualize them
gX = cv2.convertScaleAbs(gX)
gY = cv2.convertScaleAbs(gY)

# combine the gradient representations into a single image
combined = cv2.addWeighted(gX, 0.5, gY, 0.5, 0)

# show our output images
cv2.imshow("Sobel/Scharr X", gX)
cv2.imshow("Sobel/Scharr Y", gY)
cv2.imshow("Sobel/Scharr Combined", combined)
cv2.waitKey(0)

Computing both the _Gx_ and _Gy_ values is handled on Lines **23 and 24** by making a call to `cv2.Sobel`. Specifying a value of `dx=1` and `dy=0` indicates that we want to compute the gradient across the _x_ direction. And supplying a value of `dx=0` and `dy=1` indicates that we want to compute the gradient across the _y_ direction.

**_Note:_** _In the event that we want to use the Scharr filter instead of the Sobel filter we simply specify our `--scharr` command line argument to be > 0_ — _from there the appropriate `ksize` is set (_**_Line 22_**_)._

However, at this point, both `gX` and `gY` are now of the floating point data type. If we want to visualize them on our screen we need to convert them back to 8-bit unsigned integers. **Lines 30 and 31** take the absolute value of the gradient images and then squish the values back into the range _\[0, 255\]._

Finally, we combine `gX` and `gY` into a single image using the `cv2.addWeighted` function, weighting each gradient representation equally.

**Lines 37-40** then show our output images on our screen.

### **Sobel and Scharr Filter results**

Let’s learn how to apply Sobel and Scharr kernels with OpenCV.

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

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

$ python opencv\_sobel\_scharr.py --image images/bricks.png

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

**Figure 10:** _Top:_ Our original input image of a brick wall. _Bottom Left:_ Computing the image gradient along the _x_ direction. _Bottom Center:_ The image gradients along the _y_ direction. _Bottom Right:_ Combining the _x_ and _y_ gradient representations together.

At the _top,_ we have our original image, which is an image of a brick wall.

The _bottom-left_ then displays the Sobel gradient image along the _x_ direction. Notice how computing the Sobel gradient along the _x_ direction reveals the vertical mortar regions of the bricks.

Similarly, the _bottom-center_ shows that Sobel gradient computed along the _y_ direction — now we can see the horizontal mortar regions of the bricks.

Finally, we can add `gX` and `gY` together and receive our final output image on the _bottom-right._

Let’s look at another image:

$ python opencv\_sobel\_scharr.py --image images/coins01.png

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

**Figure 11:** _Top:_ Our original input image of some coins. _Bottom Left:_ Computing the Sobel gradient along the _x_ direction. _Bottom Center:_ The Sobel gradients along the _y_ direction. _Bottom RIght:_ Combining the _x_ and _y_ gradient representations together.

This time we are investigating a set of coins. Much of the Sobel gradient information is found along the borders/outlines of the coins.

Now, let’s compute the Scharr gradient information for the same image:

$ python opencv\_sobel\_scharr.py --image images/coins01.png \\
	--scharr 1

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

**Figure 12:** Computing the Scharr gradient representation of our coins.

Notice how the Scharr operator contains more information than the previous example.

Let’s look at another Sobel example:

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

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

**Figure 13:** _Top:_ Our original input image of the frontside and backside of a pill. _Bottom Left:_ Computing the image gradient along the _x_ direction. _Bottom Center:_ The image gradients along the _y_ direction. _Bottom Right:_ Combining the _x_ and _y_ gradient representations together.

Here, we have computed the Sobel gradient representation of the pill. The outline of the pill is clearly visible, as well as the digits on the pill itself.

Now let’s look at the Scharr representation:

$ python opencv\_sobel\_scharr.py --image images/clonazepam\_1mg.png \\
	--scharr 1

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

**Figure 14:** Computing the Scharr gradient of our pill image.

The borders of the pill are just as defined, and what’s more, the Scharr gradient representation is picking up much more of the texture of the pill itself.

Whether or not you use the Sobel or Scharr gradient for your application is dependent on your project, but in general, keep in mind that the Scharr version is going to appear more “visually noisy,” but at the same time captures more nuanced details in the texture.

### **Computing gradient magnitude and orientation with OpenCV**

Up until this point, we’ve learned how to compute the Sobel Filter and Scharr operator, but we don’t have much of an intuition as to what these gradients actually represent.

In this section, we are going to compute the **gradient magnitude** and **gradient orientation** of our input grayscale image and visualize the results. Then, in the next section, we’ll review these results, allowing you to obtain a deeper understanding of what gradient magnitude and orientation actually represent.

With that said, open a new file, name it `opencv_magnitude_orienation.py`, and insert the following code:

\# import the necessary packages
import matplotlib.pyplot as plt
import numpy as np
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())

**Lines 2-5** import our required Python packages, including `matplotlib` for plotting, NumPy for numerical array processing, `argparse` for command line arguments, and `cv2` for our OpenCV bindings.

We then parse our command line arguments on **Lines 8-11.** We only need a single switch here, `--image`, which is the path to our input image residing on disk.

Let’s load our image now and compute the gradient magnitude and orientation:

\# load the input image and convert it to grayscale
image = cv2.imread(args\["image"\])
gray = cv2.cvtColor(image, cv2.COLOR\_BGR2GRAY)

# compute gradients along the x and y axis, respectively
gX = cv2.Sobel(gray, cv2.CV\_64F, 1, 0)
gY = cv2.Sobel(gray, cv2.CV\_64F, 0, 1)

# compute the gradient magnitude and orientation
magnitude = np.sqrt((gX \*\* 2) + (gY \*\* 2))
orientation = np.arctan2(gY, gX) \* (180 / np.pi) % 180

**Lines 14 and 15** load our `image` from disk and convert it to grayscale.

We then compute the Sobel gradients along the _x_ and _y_ axis, just like we did in the previous section.

However, unlike the previous section, we are not going to display the gradient images to our screen (at least not via the `cv2.imshow` function), thus we do not have to convert them back into the range _\[0, 255\]_ or use the `cv2.addWeighted` function to combine them together.

Instead, we continue with our gradient magnitude and orientation calculations on **Lines 22 and 23.** Notice how these two lines match our equations above _exactly._

The **gradient magnitude** is simply the square-root of the squared gradients in both the _x_ and _y_ direction added together.

And the **gradient orientation** is the arc-tangent of the gradients in both the _x_ and _y_ direction.

Let’s now visualize both the gradient magnitude and gradient orientation:

\# initialize a figure to display the input grayscale image along with
# the gradient magnitude and orientation representations, respectively
(fig, axs) = plt.subplots(nrows=1, ncols=3, figsize=(8, 4))

# plot each of the images
axs\[0\].imshow(gray, cmap="gray")
axs\[1\].imshow(magnitude, cmap="jet")
axs\[2\].imshow(orientation, cmap="jet")

# set the titles of each axes
axs\[0\].set\_title("Grayscale")
axs\[1\].set\_title("Gradient Magnitude")
axs\[2\].set\_title("Gradient Orientation \[0, 180\]")

# loop over each of the axes and turn off the x and y ticks
for i in range(0, 3):
	axs\[i\].get\_xaxis().set\_ticks(\[\])
	axs\[i\].get\_yaxis().set\_ticks(\[\])

# show the plots
plt.tight\_layout()
plt.show()

**Line 27** creates a figure with one row and three columns (one for the original image, gradient magnitude representation, and one for the gradient orientation representation, respectively).

We then add each of the grayscale, gradient magnitude, and gradient orientation images to the plot (**Lines 30-32**) while setting the titles for each of the axes (**Lines 35-37**).

Finally, we turn off axis ticks (**Lines 40-42**) and display the result on our screen.

### **Gradient magnitude and orientation results**

We are now ready to compute our gradient magnitude!

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

From there, execute the following command:

$ python opencv\_magnitude\_orientation.py --image images/coins02.png

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

**Figure 15:** _Left:_ Original input image. _Middle:_ Gradient magnitude. _Right:_ Gradient orientation.

On the _left,_ we have our original input image of coins.

The _middle_ displays the gradient magnitude using the Jet colormap.

Values that are closer to the blue range are _very small_. For example, the background of the image has a gradient of `0` because there is no gradient there.

Values that are closer to the yellow/red range are _quite large_ (relative to the rest of the values). Taking a look at the outlines/borders of the coins you can see that these pixels have a large gradient magnitude due to the fact that they contain edge information.

Finally, the image on the _right_ displays the gradient orientation information, again using the Jet colormap.

Values here fall into the range _\[0, 180\],_ where values closer to zero show as blue and values closer to 180 as red. Note that much of the orientation information is contained within the coin itself.

Let’s try another example:

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

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

**Figure 16:** _Left:_ Original input image of the front and back of a prescription pill. _Middle:_ Gradient magnitude. _Right:_ Gradient orientation.

The image on the _left_ contains our input image of a prescription pill meditation. We then compute the gradient magnitude in the _middle_ and display the gradient orientation on the _right_.

Similar to our coins example, much of the gradient magnitude information lies on the borders/boundaries of the pill while the gradient orientation is much more pronounced due to the texture of the pill itself.

In our next tutorial, you’ll learn how to use this gradient information to detect edges in an input image.

### 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 defined what an image gradient is: _a directional change in image intensity._

We also learned how to manually compute the change in direction surrounding a central pixel using nothing more than the neighborhood of pixel intensity values.

We then used these changes in direction to compute our **gradient orientation** — the direction in which the change in intensity is pointing — and the **gradient magnitude**, which is how strong the change in intensity is.

Of course, computing the gradient orientation and magnitude using our simple method is not the fastest way. Instead, we can rely on the **Sobel and Scharr Filters,** which allow us to obtain an _approximation_ to the image derivative. Similar to smoothing and blurring, our image kernels convolve our input image with a filter that is designed to approximate our gradient.

Finally, we learned how to use the `cv2.Sobel` OpenCV function to compute Sobel and Scharr gradient representations. Using these gradient representations we were able to pinpoint which pixels in the image had an orientation within the range ![Image 38: \min_{\theta} <= \theta <= \max_{\theta}](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20147%2015'%3E%3C/svg%3E).

Image gradients are one of the most important image processing and computer vision building blocks you’ll learn about. Behind the scenes, they are used for powerful image descriptor methods such as Histogram of Oriented Gradients and SIFT.

They are used to construct [saliency maps](https://pyimagesearch.com/2018/07/16/opencv-saliency-detection/) to reveal the most “interesting” regions of an image.

And as we’ll see in the next tutorial, we’ll see how image gradients are the cornerstone of the Canny edge detector for detecting edges in images.

Frequently Asked Questions:
---------------------------

The Sobel filter is generally faster but less accurate at edge detection, especially for higher frequency image features, compared to the Scharr filter, which provides better accuracy at the cost of computational efficiency.

window.rmpanda = window.rmpanda || {}; window.rmpanda.cmsdata = {“cms”:”wordpress”,”postId”:43911,”taxonomyTerms”:{“ufaq-category”:\[1145\],”ufaq-tag”:\[\]}};

While both filters are primarily used on grayscale images, they can be applied to color images by processing each color channel independently and then combining the results.

window.rmpanda = window.rmpanda || {}; window.rmpanda.cmsdata = {“cms”:”wordpress”,”postId”:43913,”taxonomyTerms”:{“ufaq-category”:\[1145\],”ufaq-tag”:\[\]}};

Beyond edge detection, these gradients are crucial in computer vision tasks like object recognition, image segmentation, and in developing more complex image descriptors, contributing to ongoing research in automated visual systems.

window.rmpanda = window.rmpanda || {}; window.rmpanda.cmsdata = {“cms”:”wordpress”,”postId”:43914,”taxonomyTerms”:{“ufaq-category”:\[1145\],”ufaq-tag”:\[\]}};

**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!