2

I have image that contains many no data pixels. The image is 2d numpy array and the no-data values are "None". Whenever I try to apply on it filters, seems like the none values are taken into account into the kernel and makes my pixels dissapear.

For example, I have this image:
enter image description here

I have tried to apply on it the lee filter with this function (taken from Speckle ( Lee Filter) in Python):

from scipy.ndimage.filters import uniform_filter
from scipy.ndimage.measurements import variance

def lee_filter(img, size):
    img_mean = uniform_filter(img, (size, size))
    img_sqr_mean = uniform_filter(img**2, (size, size))
    img_variance = img_sqr_mean - img_mean**2

    overall_variance = variance(img)

    img_weights = img_variance / (img_variance + overall_variance)
    img_output = img_mean + img_weights * (img - img_mean)
    return img_output

but the results looks like this:
enter image description here

with the warnning:

UserWarning: Warning: converting a masked element to nan. dv = np.float64(self.norm.vmax) - np.float64(self.norm.vmin)

I have also tried to use the library findpeaks.


from findpeaks import findpeaks
import findpeaks
#lee enhanced filter
image_lee_enhanced = findpeaks.lee_enhanced_filter(img, win_size=3, cu=0.25)

but I get the same blank image. When I used median filter on the same image with ndimage is worked no problem.

My question is how can I run those filters on the image without letting the None values interrupt the results?

edit: I prefer not to set no value pixels to 0 because the pixel range value is between -50-1 (is an index values). In addition i'm afraid that if I change it to any other value e.g 9999) it will also influence the filter (am I wrong?)

Edit 2: I have read Cris Luengo answer and I have tried to apply something similar with the scipy.ndimage median filter as I have realized that the result is disorted as well.

This is the original image:

enter image description here

I have tried masking the Null values:


idx = np.ma.masked_where(img,img!=None)[:,1]
median_filter_img = ndimage.median_filter(img[idx].reshape(491, 473), size=10)
zeros = np.zeros([img.shape[0],img.shape[1]])

zeros[idx] = median_filter_img

The results looks like this (color is darker to see the problem in the edges):

enter image description here

As it can bee seen, seems like the edges values are inflluences by the None values. I have done this also with img!=0 but got the same problem.

(just to add: the pixels vlues are between 1 to -35)

Reut
  • 1,555
  • 4
  • 23
  • 55
  • can't the none values be substituted by `0` or will it hamper with the filter operations? Also, what is your main objective here? – sai Dec 10 '20 at 15:59
  • @sai I can't have it as zero. my goal is to smooth the image and get rid of noise (also in other images that have more nosie than this) – Reut Dec 10 '20 at 16:00
  • **1.** replace empty values with `0`, **2.** create mask of the empty pixels, **3.** copy empty roi, **4.** apply filter to image with having 0, **5.** set masked values again to 0 – Grzegorz Krug Dec 16 '20 at 07:18
  • @GrzegorzKrig I'm afraid that changing non values to 0 will interrupt the results. In addition, I believe I did something similar to your idea (appears in the post) – Reut Dec 16 '20 at 10:49

2 Answers2

2

If you want to apply a linear smoothing filter, then you can use the Normalized Convolution.

The basic recipe is:

  1. Create a mask image that is 1 for the pixels with data, and 0 for the pixels without data.
  2. Set the pixels without data to any number, for example 0. NaN is not valid because it spreads in the computations.
  3. Apply the linear smoothing filter to the image multiplied by the mask.
  4. Apply the linear smoothing filter to the mask.
  5. Divide the two results.

Basically, we normalize the result of the linear smoothing filter (convolution) by the number of pixels with data within the filter window.

In regions where the smoothed mask is 0 (far away from data), we will divide 0 by 0, so special care needs to be taken there.

Note that normalized convolution can be used also for uncertain data, where the mask image gets values in between 0 and 1 indicating the confidence we have in each pixel. Pixels thought to be noisy can be set to a value closer to 0 than the other pixels, for example.


The recipe above is only valid for linear smoothing filters. Normalized convolution can be done with other linear filters, for example derivative filters, but the resulting recipe is different. See for example here the equation for Normalized Convolution to compute the derivative.


For non-linear filters, other approaches are necessary. Non-linear smoothing filters, for example, will often avoid affecting edges, and so will work quite well in images with missing data, if the missing pixels are set to 0, or some value far outside of the data range. The concept of keeping a mask image that indicates which pixels have data and which don't is always a good idea.

Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
  • Thank you for your answer, I think I ahve tried something similar to what you suggest but got still problem with the edges, you are welcome to read the edited part on my original post that is based on your answer – Reut Dec 13 '20 at 09:02
  • 1
    @Reut: The median filter is not a linear filter, it is not a convolution. The mean filter is linear. The Gaussian smoothing filter is linear. What problem are you trying to solve? Remove the speckle noise? Have you tried a bilateral filter with a small tonal sigma and small spatial sigma? You can set the no-sample pixels to 200 and just run the filter. – Cris Luengo Dec 13 '20 at 14:54
  • thank you for your answer, i'll try the bilateral filter as well. as i'm a beginner in this I would like to ask - why only linear filters work in this situation? – Reut Dec 16 '20 at 10:50
  • @Reut: Because the math doesn’t work out otherwise. A linear smoothing filter is a weighted average. Normalized convolution modified the weights used in the averaging to be zero for the pixels not in the mask. – Cris Luengo Dec 16 '20 at 14:51
0

Seems like a simple solution is to set the non values to zero. I don't know how you would get around this, because most image processing kernels require some value to for you to apply. a[numpy.argwhere(a==None)] = 0

byshiny
  • 9
  • 1