# Sobel Filter – Sobel-Feldman Edge Detection and Gradient Extraction

The Sobel filter (also called Sobel-Feldman operator) is an edge detection filter, that results in image emphasizing edges. Computer vision and image processing applications frequently use this filter to extract image gradients and contours. The Sobel operator is applicable in many algorithms such as Hough transform, Harris corners detection and many more.

The Sobel operator is a separable filter that consists of two 3×3 convolution masks. Both masks have the same coefficients as they are rotated 90 degrees to each other. The Sobel masks are with such design that they provide maximum value at horizontal or vertical edge orientation. Occasional it is desirable to retrieve only one of the gradient orientation – horizontal or vertical.

The image blow is an example result of Sobel operator and point out the enhanced objects outlines.

## How Sobel Filter works?

The Sobel filter works through a simple 3×3 convolution thus it is efficient for both CPU and GPU parallel computing. Furthermore the Sobel kernels are separable, which is  an additional optimization option.

Each image pixel is processed by each kernel in order to produce the final gradient value using equation (2). Additionally to gain performance it is acceptable to use the sum of absolute directional values as in equation (3).

After calculating the vertical and horizontal gradients, it is possible to retrieve the edge orientation, as described in (4):

Now as we have ways to calculate gradient magnitude and orientation, it is possible to get thin and strong edges. This will help for further image analysis and objects recognition. You may also check edge thinning by non-maximum suppression for further steps.

## Sobel Filter source code

### 2D Canvas edge detection

First we define two helper functions, which we use for convolution operation.

• conv3x – convolution function that calculates vertical gradient Gx
• conv3y – convolution function that calculates horizontal gradient Gy.
``````
function conv3x(data, idx, w, p){
return (p*data[idx - w - 4] + p*data[idx - 4] + p*data[idx + w - 4]
-p*data[idx - w + 4] - p*data[idx + 4] - p*data[idx + 4 + 4]);
}

function conv3y(data, idx, w, p){
return (p*data[idx - w - 4] + p*data[idx - w] + p*data[idx - w + 4]
-(p*data[idx + w - 4] + p*data[idx + w] + p*data[idx + w + 4]));
}
``````

Next we perform image iteration, so at each pixel we calculate Gx and Gy as described above.

``````
for (j = 0, i = w + 4; i < l; i+=4, j++){
var dx = conv3x(data, i, w, mask);
var dy = conv3y(data, i, w, mask);
//var val = Math.sqrt(dx*dx + dy*dy);
var val = Math.abs(dx) + Math.abs(dy);
buff[j] = val;
}
``````

Note that for Sobel operator the values in our mask are: [1, 2, 1].

### Edge Detection by WebGL Fragment Shader

The source code below represents a way to calculate gradient magnitude and orientation using Graphics Processing Unit (GPU) by WebGL/OpenGL API. The u_kernel is an input parameter holding the Sobel-Feldman coefficients. Using the above description we can calculate dx and dy by math (1). As a next step the GLSL shader calculates the gradient orientation theta as in (4). Note that we should scale the orientation angle from range [0.0, 2.0*PI] to [0.0, 1.0]. Finally the gradient magnitude and orientation are set to the first two components of gl_FragColor vector. We keep derivatives orientation values for further processing with Non Maximum Suppression, that produce thin edges.

``````
precision mediump float;

#define KERNEL_SIZE 3
// our texture
uniform sampler2D u_image;
uniform vec2 u_textureSize;
uniform float u_kernel[KERNEL_SIZE];
#define M_PI 3.1415926535897932384626433832795
#define GET_PIXEL(_x, _y) (texture2D(u_image, textCoord + onePixel*vec2(_x, _y)))

void main() {
vec2 onePixel = vec2(1.0, 1.0) / u_textureSize;
vec2 textCoord = gl_FragCoord.xy / u_textureSize;
float dx = (length(GET_PIXEL(-1, -1)*u_kernel +
GET_PIXEL(-1,  0)*u_kernel +
GET_PIXEL(-1, +1)*u_kernel) -
length(GET_PIXEL(+1, -1)*u_kernel +
GET_PIXEL(+1,  0)*u_kernel +
GET_PIXEL(+1, +1)*u_kernel));
float dy = (length(GET_PIXEL(-1, -1)*u_kernel +
GET_PIXEL(0, -1)*u_kernel +
GET_PIXEL(+1, -1)*u_kernel) -
length(GET_PIXEL(-1, +1)*u_kernel +
GET_PIXEL(0, +1)*u_kernel +
GET_PIXEL(+1, +1)*u_kernel));

///gl_FragColor = vec4(vec3(length(vec2(dx, dy))), 1.0);
float theta = (atan(dy, dx) + M_PI) / (2.0*M_PI);
gl_FragColor = vec4(length(vec2(dx, dy)), theta, 0.0, 1.0);
}
``````

Sigma: 3