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[0]*data[idx - w - 4] + p[1]*data[idx - 4] + p[2]*data[idx + w - 4]
-p[0]*data[idx - w + 4] - p[1]*data[idx + 4] - p[2]*data[idx + 4 + 4]);
}
function conv3y(data, idx, w, p){
return (p[0]*data[idx - w - 4] + p[1]*data[idx - w] + p[2]*data[idx - w + 4]
-(p[0]*data[idx + w - 4] + p[1]*data[idx + w] + p[2]*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 *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[0] +
GET_PIXEL(-1, 0)*u_kernel[1] +
GET_PIXEL(-1, +1)*u_kernel[2]) -
length(GET_PIXEL(+1, -1)*u_kernel[0] +
GET_PIXEL(+1, 0)*u_kernel[1] +
GET_PIXEL(+1, +1)*u_kernel[2]));
float dy = (length(GET_PIXEL(-1, -1)*u_kernel[0] +
GET_PIXEL(0, -1)*u_kernel[1] +
GET_PIXEL(+1, -1)*u_kernel[2]) -
length(GET_PIXEL(-1, +1)*u_kernel[0] +
GET_PIXEL(0, +1)*u_kernel[1] +
GET_PIXEL(+1, +1)*u_kernel[2]));
///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);
}
```

#### Visit FivekoGFX on GitHub for more image processing examples

## Sobel Edge Detection Demo

## References

- Irwin Sobel, 2014,
*History and Definition of the Sobel Operator*