Generally for an image of h rows and w,
and ignoring boundary considerations, we loop over rows and then over columns:
for (j=0; j<h; j++) {
for (i=0; i<w; i++) {
do_the_convolution;
save_the_pixel_value;
}
}
To do the convolution we might fill a data array the same size as the
convolution mask. For a convolution kernel mask[mh][mw], we
could store the pixel data under the mask in data[mh][mw].
Then we can generally do the convolution as
sum = 0;
for (j1=0; j1<mh; j1++) {
for (i1=0; i1<mw; i1++) {
sum = sum + mask[j1][i1]*data[j1][i1];
}
}
The method doConvolve
uses this algorithm.
For simple convolution masks, such as a 3 x 3 operator, we can save the pixel data under the mask as independent variables and the mask itself as constants. For example:
| pixel data | mask data |
|---|---|
p00 p01 p02 p10 p11 p12 p20 p21 p22 |
0 -1 0 -1 4 -1 0 -1 0 |
The convolution for a single pixel could be calculated in one line of code:
sum = (p11<<2) - (p01+p10+p12+p21);
The method doEdgeMag uses this approach.
To keep the output confined to abs(sum)≤1, we need to divide the result by 8.
For positive integers the code s>>3 is equivalent to s/8
For negative integers the results are generally different, as
shown by the example Java program test.java