1

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.

Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
MochaJ
  • 11
  • 3
  • Could you show us a minimum working version of this and what you've tried so far? – Nico Albers Feb 17 '19 at 18:59
  • 1
    I've edited the question to show the code that I have written thus far. – MochaJ Feb 17 '19 at 19:31
  • 1
    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 Feb 17 '19 at 19:41
  • 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 Feb 18 '19 at 18:51
  • You can scale the kernel however you see fit. However, this won't change much. – Cris Luengo Feb 18 '19 at 23:50
  • I didn't see the problem the first time around. Now it's clear! – Cris Luengo Feb 18 '19 at 23:59

1 Answers1

0

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
Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
  • Thank you, that makes a lot more sense. But how would I deal with the negative values? When I tried using other kernels, my image came out blackish/very dark and contained negative pixel values. If I used y=abs(s); would that solve the issue? – MochaJ Feb 19 '19 at 02:59
  • @MochaJ: If you take the absolute values, you are throwing away relevant information. Instead, display with `imshow(img,[])`, which scales the image linearly to fit. See also [this other answer](https://stackoverflow.com/a/53028382/7328782). – Cris Luengo Feb 19 '19 at 13:51
  • As the image is declared as a double, would the negative pixels be displayed in the output? This is what I am getting confused about. – MochaJ Feb 19 '19 at 18:35
  • @MochaJ: If you display the image as I showed you, then yes. The lowest value will be black, the highest value will be white, and zero will be some intermediate grey. – Cris Luengo Feb 19 '19 at 18:37
  • So, I would cast all values of x to doubles, and then display the figure using imshow(y,[ ])? Would the imshow(y [ ]) show the true image regardless of which kernel is used? – MochaJ Feb 20 '19 at 15:38
  • @MochaJ: Yes, the `[]` argument to `imshow` tells it to map the full intensity range in the image (including any negative values) to the black-white range of the display. It doesn't matter what image you give it, you will always see the full intensity range, no data will be clipped. – Cris Luengo Feb 20 '19 at 17:48
  • Thank you for explaining everything to me! I understand it more clearly now. – MochaJ Feb 20 '19 at 17:59