3

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: ver1;

ver2

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: ver1 ver

As you can see, for the 5x5 the results are quite bad and I don't know how to normalize the values. Any ideas?

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

Comments

3

You have to normalize the results of your computation. For that you have to find out how "big" is the filter with all absoltue values. So I do this:

      for(int i = 0; i < mask.length; i++)
        for(int j = 0; j < mask[i].length; j++)
            size += Math.abs(mask[i][j]);

Where mask is my sobel filter of each size. So after apply your sobel filter you have to normalize your value in your code it should look like:

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];
    }
}
x_weight /= size;
y_weight /= size;

After that for visualization you have to shift the values about 128. Just do that if you want to visualize the image. Otherwise you get problems with later calculations (gradient for example).

x_weight += 128;
y_weight += 128;

Hope it works and help.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.