Prefer Your Language

Search This Blog

Implementation Sobel operator in Matlab on YUV image and other extension image


Implementation Sobel operator in Matlab on YUV image and other extension image

Today, we discuss Sobel operator and how to apply on image with step by step discussion. If you are not familiar with the sobel operator or don’t know in detail, don’t worry, we first discuss what is sobel operator followed by its Matlab code.

Lets start our discussion.
First question is what is Sobel filter/operator?

The algorithm we will look at in this tutorial is an edge detection algorithm, specifically an edge detection algorithm based on the Sobel operator. This algorithm works by calculating the gradient of the intensity of the image at each point, finding the direction of the change from light to dark and the magnitude of the change. This magnitude corresponds to how sharp the edge is.
The Sobel Operator
To calculate the gradient of each point in the image, the image is convolved with the Sobel Kernel. Convolution is done by moving the kernel across the image, one pixel at a time. At each pixel, the pixel and its neighbours are weighted by the corresponding value in the kernel, and summed to produce a new value. This operation is shown in the following diagram.
~~~~~~~~~~~~~~~~~~~            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| k00 | k10 | k20 |            | i00 | i10 | i20 | i30 | ..
|~~~~~+~~~~~+~~~~~+            |~~~~~+~~~~~+~~~~~+~~~~~+~~~
| k01 | k11 | k21 |            | i01 | i11 | i21 | i31 | ..
|~~~~~+~~~~~+~~~~~+            |~~~~~+~~~~~+~~~~~+~~~~~+~~~
| k02 | k12 | k22 |            | i02 | i12 | i22 | i32 | ..
|~~~~~+~~~~~+~~~~~+            |~~~~~+~~~~~+~~~~~+~~~~~+~~~
                               | i03 | i13 | i23 | i33 | ..
Applying the kernel to the     |~~~~~+~~~~~+~~~~~+~~~~~+~~~
to the first pixel gives us    |  .. |  .. |  .. |  .. | .. 
an output value for that
pixel


     o00 = k11 * i00 + k12 * i01 + k21 * i10 + k22 * i11
                                                                                                                         

we treat it as though the kernel has been overlaid onto the image, with the center pixel of the kernel aligning we the first pixel in the image. Then we multiple each entry in the kernel by the value beneath it, and sum them to produce a single single output value from that pixel.

For the pixels on the boundary, we just ignore any entries in the kernel that fall outside.
Edge detection using the Sobel Operator applies two separate kernels to calculate the x and y gradients in the image. The length of this gradient is then calculated and normalized to produce a single intensity approximately equal to the sharpness of the edge at that position.
The kernels used for Sobel Edge Detection are shown below.
~~~~~~~~~~~~~
|-1 | 0 | 1 |
|~~~+~~~+~~~|
|-2 | 0 | 2 |
|~~~+~~~+~~~|
|-1 | 0 | 1 |
~~~~~~~~~~~~~
X gradient kernel

~~~~~~~~~~~~~
|-1 |-2 |-1 |
|~~~+~~~+~~~|
| 0 | 0 | 0 |
|~~~+~~~+~~~|
| 1 | 2 | 1 |
~~~~~~~~~~~~~
Y gradient kernel


The Algorithm
Now the algorithm can be broken down into its constituent steps
1 Iterate over every pixel in the image
2 Apply the x gradient kernel
3 Apply the y gradient kernel
4 Find the length of the gradient using pythagoras' theorem
5 Normalize the gradient length to the range 0-255
6 Set the pixels to the new values

This illustrates the main steps, though we miss out some specifics such as what we do when we meet the boundary of the image.

We need to do one tweak so that this will work for our edge detection. When we are iterating over the image, we will be changing value however when we move on to the next pixel, the just changed value will still be in the area of influence of our kernel. For this reason we need to make sure the image we read pixels from and the image we write pixels to are different, and we can do this by just created an empty image, which we will populate with the new values.
img_edge = Image(img.width, img.height)
 
Now we will read pixels from the original image, but write the edge values to img_edge
Before we start fiddling with the pixel values, we need to decide how we will handle the image boundaries. There are a few different ways to do this, one way is to check each pixel to see if it is on the boundary of the image, and ignore the non-existant pixels on that side. An alternative way would be to wrap the pixels, so that a non-existant pixel on the left edge actually maps to the pixel on the same row on the right edge. For simplicities sake I am simply going to ignore the boundary pixels, instead choosing to start one pixel in from the starting point, and 1 pixel away from the finishing edge. To do this, change the iterators to start at 1, and finish at img.width - 1 and img.height - 1 respectively. Now we can safely index adjacent pixels without falling off the edge of the image.

Right, lets move on to applying the gradients. For the x gradient, we need the column of pixels to the left and right, for each of the 6 pixels we index we will add up the red, green and blue intensities, multiply it by the kernel, and accumulated it into Gx. We will also do the same for the y gradient. As Gx and Gy both use the same pixels on occasions, it is best to apply the two kernels simultaneously.
Now that the kernels are applied, Gx and Gy contain un-normalised values, which equal the relative length of the gradient in their respective axis. We wish to calculate the length of the gradient, and normalise it to a range suitable for displaying.
Lets calculate the relative length of each gradient. We can do this with pythagoras' theorem:
Pythagoras' theorem
 
sqr(a) + sqr(b) = sqr(c)
c = sqrt( sqr(a) + sqr(b) )
 
length = math.sqrt((Gx * Gx) + (Gy * Gy))
 
This gives us an un-normalised length, to get the length normalised, we need to know the range of length

We know that each pixel's intensity value has a range of 0 to 765, due to it being the addition of 3 variables with range 0 to 255. Also, from looking at the kernels, we can see the maximum amount you can accumulate is +/- 4 intensities. Therefore the total range of Gx and Gy is between 0 and 3060. These values are then squared, added and square rooted, giving us a final range of 0 to 4328 ( sqrt(2 * sqr(3060)) ). We can renormalise by dividing by the upper bounds and multiplying by 255.
Right at the end of the code we need to change displayImage to display our new image rather than the original. Other than that, the script is finished, and we can test it to see the results. The script should produce a new grayscale image, highlighting edges from the original image.
This concludes this tutorial. Below are the code listings for this tutorial. The C code will actually continually capture images from the webcam, and you will find that on the Raspberry Pi it is capable of displaying the edge detected image in realtime.
Let’s formulate the Formula;

So, we reach the required formula;
Here it is;

Gx = ( z7 + 2z8 + z9) – ( z1 + 2Z2 + z3 )

&

Gy = (z3 + 2z6 + z9) – ( z1 + 2Z4 + z7)



Now time to move ahead, now we are looking for Matlab code, right?
Ok let’s start coding in Matlab;
For understand below code, you must be aware to the following function’s.
Here is the complete Code; But this code have some Logical error. First you try to figure out and in the next post we will discuss its logical error and correct them.

Incorrect Solution
%Procedure for Sobel operator

Original_Image = imread('a.bmp');
Gray_Edge_Image = rgb2gray (Original_Image);
Temp_Image = double (Gray_Edge_Image) ;
%Start iterate from first corner to last corner
% size(Temp_Image,1) gives number of rows; we subtract 2 bec. of leaving  boundry
for i = 1 : size(Temp_Image,1) - 2
    for j = 1 : size (Temp_Image, 2) - 2
        %calculating gradient in X direction
        X_Gradient = ( ( Temp_Image ( i+2, j ) + 2*Temp_Image( i+2, j+1 ) +
                                    Temp_Image ( i+2 ,j+2 ) ) - ( Temp_Image( I , j ) +
                                    2*Temp_Image( I , j+1 ) + Temp_Image ( I , j+2 ) ) );
        %calculating gradient in Y direction
        Y_Gradient = ( ( Temp_Image( I , j+2 ) + 2*Temp_Image( i+1, j+2 ) +
                                    Temp_Image ( i+2, j+2 ) ) - ( Temp_Image( I , j )
                                     + 2*Temp_Image( i+1, j ) + Temp_Image ( i+2, j ) ) );
        %Subtracting 256 bec. of brightness ; you may leave it
        Gray_Edge_Image (i , j) = 256 - sqrt ( Y_Gradient * Y_Gradient
                                                                   + X_Gradient*X_Gradient );
    end
end
 figure, imshow(Original_Image), figure, imshow(Gray_Edge_Image);
       

Are you able to identify the logical error? No.
Ok let’s identify and correct them .
Identifying logical error
We have already documents the Algorithm. In the documented algorithm we missed one step and code it incorrectly, steps is

Normalize the gradient length to the range 0-255


Just subtracting the resultant value from 255 does not ensure that gradient length to the range 0-255. Now, question is how to resolve it, Very easy, Here the steps;
Steps to Resolve above logical error
·         First find the maximum value of pixel and store in Max_Value.
·         Afterword’s divide each pixel value by Max_Value.
Now this step ensure that the pixel values is in between 0 and 1, means in the range of [0 1].
·         Now, multiply the each pixel value to by 255 so that range become
255*[0 1] = [0 255]. This solve our problem.
Above steps are easy to implement, here is the complete code
Correct Solution


%Modified code for Sobel filter
Original_Image = imread('a.bmp');
Gray_Edge_Image = rgb2gray (Original_Image);
Temp_Image = double (Gray_Edge_Image) ;
%variable for finding maximum value across Matrix
max_value = -1 ;
%Start iterate from first corner to last corner
% size(Temp_Image,1) gives number of rows; we subtract 2 bec. of leaving  boundry
for i = 2 : size(Temp_Image,1) - 1
    for j = 2 : size (Temp_Image, 2) - 1
        %calculating gradient in X direction
        X_Gradient = ( ( Temp_Image( I + 1 , j1 ) + 2*Temp_Image(i+1,j)
                              + Temp_Image (i+1,j+1) ) - ( Temp_Image(i-1,j-1)
                              + 2*Temp_Image(i-1,j) + Temp_Image (i-1,j+1) ) );
        %calculating gradient in Y direction
        Y_Gradient = ( ( Temp_Image(i-1,j+1) + 2*Temp_Image(i,j+1)
                                + Temp_Image (i+1,j+1) ) - ( Temp_Image(i-1,j-1)
                          + 2*Temp_Image(i,j-1) + Temp_Image (i+1,j-1)));
        Gray_Edge_Image (i-1 , j-1) = sqrt ( Y_Gradient*Y_Gradient
                                                   + X_Gradient*X_Gradient );
       
       
 if (max_value < Gray_Edge_Image ( i-1, j-1 ) )
            max_value = Gray_Edge_Image ( i-1 ,j-1 )  ;
        end
    end
end


%converting whole image value in between 0 to 255; division makes it in range of [0,1]
for i = 2 : size(Temp_Image,1)-1 
    for j = 2 : size (Temp_Image, 2)-1
        Gray_Edge_Image ( i-1 ,j-1 ) =  
                              ((Gray_Edge_Image ( i-1 ,j-1 )/ max_value ) * 255 );
       
    end
end
figure, imshow(Original_Image), figure, imshow(Gray_Edge_Image);
                             
       

Output



Got Questions? 
Feel free to ask me any question because I'd be happy to walk you through step by step! 


References and External Links

For Contact us….. Click on Contact us Tab

8 comments:

  1. Seriously that little modification for taking a value for Gray_Edge_Image ( i-1 ,j-1 ) with respect to max_value for 255 is an most remarkable thing! that little modification changes the whole output deeply! thank u so so so much for such amazing work! keep it up! i have damn impressed with your way of thinking for extracting tiniest errors! your doing an amazing job sir!

    ReplyDelete
  2. Hi i have a doubt. The max_value in this code never goes beyond 255, so i am not getting the idea of normalisation. Applying that changes the picture drastically.

    ReplyDelete
  3. Why not jsut use uint8 conversion?

    ReplyDelete
  4. Canyou do an example but with prewitt edge detection???

    ReplyDelete
  5. I must say you had done A tremendous job, I appreciate all your efforts. Thanks a lot

    ReplyDelete
  6. Wonderful article. I love your style of writing. It’s inspiring, and I am happy

    ReplyDelete
  7. Wow, this post is pleasant, I will share this with my all friends and keep it up.

    ReplyDelete
  8. Really very interesting and very easy to understand, I really like this!

    ReplyDelete

Help us to improve our quality and become contributor to our blog

Become a contributor to this blog. Click on contact us tab
Blogger Template by Clairvo