1
votes

I've declared a function that will be used to calculate the convolution of an image using an arbitrary 3x3 kernel. I also created a script that will prompt the user to select both an image as well as enter the convolution kernel of their choice. However, I do not know how to go about dealing with negative pixel values that will arise for various kernels. How would I implement a condition into my script that will deal with these negative values?

This is my function:

    function y = convul(x,m,H,W)
    y=zeros(H,W);

    for i=2:(H-1)
        for j=2:(W-1)
         Z1=(x(i-1,j-1))*(m(1,1));
         Z2=(x(i-1,j))*(m(1,2));
         Z3=(x(i-1,j+1))*(m(1,3));
         Z4=(x(i,j-1))*(m(2,1));
         Z5=(x(i,j))*(m(2,2));
         Z6=(x(i,j+1))*(m(2,3));
         Z7=(x(i+1,j-1))*(m(3,1));
         Z8=(x(i+1,j))*(m(3,2));
         Z9=(x(i+1,j+1))*(m(3,3));
         y(i,j)=Z1+Z2+Z3+Z4+Z5+Z6+Z7+Z8+Z9;
        end 
    end 

And this is the script that I've written that prompts the user to enter an image and select a kernel of their choice:

    [file,path]=uigetfile('*.bmp');
    x = imread(fullfile(path,file));        
    x_info=imfinfo(fullfile(path,file));    

    W=x_info.Width;                 
    H=x_info.Height;                
    L=x_info.NumColormapEntries;    
    prompt='Enter a convulation kernel m: ';
    m=input(prompt)/9;
    y=convul(x,m,H,W);
    imshow(y,[0,(L-1)]);

I've tried to use the absolute value of the convolution, as well as attempting to locate negatives in the output image, but nothing worked.

This is the original image:

Original Image

This is the image I get when I use the kernel [-1 -1 -1;-1 9 -1; -1 -1 -1]:

Edited image

I don't know what I'm doing wrong.

1
Could you show us a minimum working version of this and what you've tried so far? - Nico Albers
I've edited the question to show the code that I have written thus far. - MochaJ
MATLAB’s conv returns doubles. It looks like your function does too. This is correct, you can store negative values there. What is then the problem? You can use imshow(y,[]) to scale all values in y to the output range. - Cris Luengo
Would the image display the negative values correctly? Because it seems like my images are not displaying correctly. Also, I have a few other questions. Is it appropriate to divide each 3x3 kernel by 9 to average it's values out? - MochaJ
You can scale the kernel however you see fit. However, this won't change much. - Cris Luengo

1 Answers

0
votes

MATLAB is rather unique in how it handles operations between different data types. If x is uint8 (as it likely is in this case), and m is double (as it likely is in this case), then this operation:

Z1=(x(i-1,j-1))*(m(1,1));

returns a uint8 value, not a double. Arithmetic in MATLAB always takes the type of the non-double argument. (And you cannot do arithmetic between two different types unless one of them is double.)

MATLAB does integer arithmetic with saturation. That means that uint8(5) * -1 gives 0, not -5, because uint8 cannot represent a negative value.

So all your Z1..Z9 are uint8 values, negative results have been set to 0. Now you add all of these, again with saturation, leading to a value of at most 255. This value is assigned to the output (a double). So it looks like you are doing your computations correctly and outputting a double array, but you are still clamping your result in an odd way.

A Correct implementation would cast each of the values of x to double before multiplying by a potentially negative number. For example:

for i = 2:H-1
   for j = 2:W-1
      s = 0;
      s = s + double(x(i-1,j-1))*m(1,1);
      s = s + double(x(i-1,j))*m(1,2);
      s = s + double(x(i-1,j+1))*m(1,3);
      s = s + double(x(i,j-1))*m(2,1);
      s = s + double(x(i,j))*m(2,2);
      s = s + double(x(i,j+1))*m(2,3);
      s = s + double(x(i+1,j-1))*m(3,1);
      s = s + double(x(i+1,j))*m(3,2);
      s = s + double(x(i+1,j+1))*m(3,3);
      y(i,j) = s;
   end 
end

(Note that I removed your use of 9 different variables, I think this is cleaner, and I also removed a lot of your unnecessary brackets!)

A simpler implementation would be:

for i = 2:H-1
   for j = 2:W-1
      s = double(x(i-1:i+1,j-1:j+1)) .* m;
      y(i,j) = sum(s(:));
   end 
end