sobel filter algorithm thresholding (no external libs used)

c++, edge-detection, fpga, image-processing

Solution

Think about the range of values that your filtered values can take.

For the Sobel 3x3, the highest X/Y value is obtained when the pixels with a positive coefficient are white (255), and the ones with a negative coefficient are black (0), which gives a total of 1020. Symmetrically, the lowest value is -1020. After taking the absolute value, the range is from 0 to 1020 = 4 x 255.

For the magnitude, Abs(X)+Abs(Y), the computation is a little more complicated as the two components cannot reach 1020 at the same time. If I am right, the range is from 0 to 1530 = 6 x 255.

Similar figures for the 5x5 are 48 x 255 and 66 x 255.

Knowing that, you should rescale the values to a smaller range (apply a reduction coefficient), and adjust the thresholds. Logically, if you apply a coefficient 3/66 to the Sobel 5x5, you will return to similar conditions.

It all depends on the effect that you want to achieve.

Anyway, the true question is: how are the filtered values statistically distributed for typical images ? Because it is unnecessary to keep the far tails of the distribution.

Problem

I am writing my own implementation of the sobel egde detection. My function's interface is ``` void sobel_filter(volatile PIXEL * pixel_in, FLAG *EOL, volatile PIXEL * pixel_out, int rows, int cols) ``` (PIXEL being an 8bit greyscale pixel) For testing I changed the interface to: void sobel_filter(PIXEL pixels_in[MAX_HEIGHT][MAX_WIDTH],PIXEL pixels_out[MAX_HEIGHT][MAX_WIDTH], int rows,int cols); But Still, the thing is I get to read one pixel at a time, which brings me to the problem of managing the output values of sobel when they are bigger then 255 or smaller then 0. If I had the whole picture from the start, I could normalize all sobel output values with their min and max values. But this is not possible for me. This is my sobel operator code, ver1: ``` PIXEL sobel_op(PIXEL_CH window[KERNEL_SIZE][KERNEL_SIZE]){ const char x_op[KERNEL_SIZE][KERNEL_SIZE] = { {-1,0,1}, {-2,0,2}, {-1,0,1}}; const char y_op[KERNEL_SIZE][KERNEL_SIZE] = { {1,2,1}, {0,0,0}, {-1,-2,-1}}; short x_weight=0; short y_weight=0; PIXEL ans; for (short i=0; i<KERNEL_SIZE; i++){ for(short j=0; j<KERNEL_SIZE; j++){ x_weight+=window[i][j]*x_op[i][j]; y_weight+=window[i][j]*y_op[i][j]; } } short val=ABS(x_weight)+ABS(y_weight); //make sure the pixel value is between 0 and 255 and add thresholds if(val>200) val=255; else if(val<100) val=0; ans=255-(unsigned char)(val); return ans; ``` } this is ver 2, changes are made only after summing up the weights: ``` short val=ABS(x_weight)+ABS(y_weight); unsigned char char_val=(255-(unsigned char)(val)); //make sure the pixel value is between 0 and 255 and add thresholds if(char_val>200) char_val=255; else if(char_val<100) char_val=0; ans=char_val; return ans; ``` Now, for a 3x3 sobel both seem to be giving OK results: ; But when I try with a 5x5 sobel ``` const char x_op[KERNEL_SIZE][KERNEL_SIZE] = { {1,2,0,-2,-1}, {4,8,0,-8,-4}, {6,12,0,-12,-6}, {4,8,0,-8,-4}, {1,2,0,-2,-1}}; const char y_op[KERNEL_SIZE][KERNEL_SIZE] = { {-1,-4,-6,-4,-1}, {-2,-8,-12,-8,-2}, {0,0,0,0,0}, {2,8,12,8,2}, {1,4,6,4,1}}; ``` it gets tricky: As you can see, for the 5x5 the results are quite bad and I don't know how to normalize the values. Any ideas?

Original source