Wiener filtering on RGB image - python

I am trying to implement the wiener filter on the CIFAR10 dataset which consists of RGB images.
But this filter can only be used for Gray-scaled images.
I tried to implement it on each R/G/B channel and then combine them, but the resulting RGB image was not even close to the initial image.
Any ideas?
(I am using scipy.signal.signaltools.wiener)
Thanks in advance

Ok, how about skimage (scikit-image)? Have a look here:
https://scikit-image.org/docs/dev/api/skimage.restoration.html#skimage.restoration.wiener
The example given on an rgb image is the following:
from skimage import color, data, restoration
img = color.rgb2gray(data.astronaut())
from scipy.signal import convolve2d
psf = np.ones((5, 5)) / 25
img = convolve2d(img, psf, 'same')
img += 0.1 * img.std() * np.random.standard_normal(img.shape)
deconvolved_img = restoration.wiener(img, psf, 1100)

Related

Why cv2.write saves black images?

hi folks, greetings
am using this code that I found on the web, to apply a wiener filter on an image, the code :
from scipy.signal.signaltools import deconvolve
from skimage import color, data, restoration
img = color.rgb2gray(img)
from scipy.signal import convolve2d
psf = np.ones((5, 5)) / 25
img = convolve2d(img, psf, 'same')
img += 0.1 * img.std() * np.random.standard_normal(img.shape)
deconvolved_img = restoration.wiener(img, psf, 1100)
f, (plot1, plot2) = plt.subplots(1, 2)
plot1.imshow(img)
plot2.imshow(deconvolved_img)
plt.show()
cv2.imwrite("wiener result 2.jpeg",deconvolved_img)
the issue is when I plot the result using Matplotlib I get this :
but when I type cv2.imwrite("wiener result 2.jpeg",deconvolved_img) to save the image, I get this :
why do I get a black image when I save it ??
There are two ways to save your images as a file:
Method 1: Using matplotlib
Since you are using matplotlib library to show the image plt.show(), you can use the same library to save your plots as well using plt.savefig()
plot1.imshow(img)
plot2.imshow(deconvolved_img)
plt.savefig('path_to_save) # mention the path you want to save the plots
plt.show()
Method 2: Using OpenCV
You can also save your file using OpenCV.
But prior to saving your image there is a conversion required. The image variable deconvolved_img is of float data type, with values ranging between [0 - 1]. Hence when you save such images they are perceived as a black image.
In OpenCV you can convert your image to int data type and scaling the pixel intensities between the expected [0 - 255] using cv2.normalize() function:
result = cv2.normalize(deconvolved_img, dst=None, alpha=0, beta=255,norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
cv2.imwrite('path_to_save', result) # mention the path you want to save the result

How to add synthetic noise in an image with specified error probability

I want to create synthetic noise within an image. How will I degrade the black and white image with errors, with an independent probability of error at each point. How will I do that in Python (e.g. Error probability = 0.0011)?
Here's a vectorized approach using OpenCV + skimage.util.random_noise. You can experiment with noise modes such as localvar, pepper, s&p, and speckle to obtain the desired result. You can set the proportion of noise with the amount parameter. Here's an example using s&p with amount=0.011:
import cv2
import numpy as np
from skimage.util import random_noise
# Load the image
image = cv2.imread('1.png', 0)
# Add salt-and-pepper noise to the image
noise = random_noise(image, mode='s&p', amount=0.011)
# The above function returns a floating-point image in the range [0, 1]
# so need to change it to 'uint8' with range [0,255]
noise = np.array(255 * noise, dtype=np.uint8)
cv2.imshow('noise',noise)
cv2.imwrite('noise.png',noise)
cv2.waitKey()
Here's an example program simply replacing the "degraded" pixels with black, using the Pillow library
from PIL import Image
import random
img = Image.open('text.png')
pixels = img.load()
for x in range(img.size[0]):
for y in range(img.size[1]):
if random.random() < 0.011:
pixels[x,y] = 0 # only 1 number given since the image is grayscale
img.save('text_degraded.png')
I've increased the probability to 0.011 to make it more noticeable, here's the output

How to create synthetic blurred image from sharp image using PSF kernel (in image format)

Update as suggestion from #Fix that I should BGR to RGB, but the outputs are still not the same as the paper's output.
(Small note: this post already post on https://dsp.stackexchange.com/posts/60670 but since I need help quickly so I think I reposted here, hope this doesn't violate to any policy)
I tried to create synthetic blurred image from ground-truth image using PSF kernels (in png format), some paper only mentioned that I need to do convolve operation on it, but it's seem to be I need more than that.
What I did
import matplotlib.pyplot as plt
import cv2 as cv
import scipy
from scipy import ndimage
import matplotlib.image as mpimg
import numpy as np
img = cv.imread('../dataset/text_01.png')
norm_image = cv.normalize(img, None, alpha=-0.1, beta=1.8, norm_type=cv.NORM_MINMAX, dtype=cv.CV_32F)
f = cv.imread('../matlab/uniform_kernel/kernel_01.png')
norm_f = cv.normalize(f, None, alpha=0, beta=1, norm_type=cv.NORM_MINMAX, dtype=cv.CV_32F)
result = ndimage.convolve(norm_image, norm_f, mode='nearest')
result = np.clip(result, 0, 1)
imgplot = plt.imshow(result)
plt.show()
And this only give me a white-entire image.
I tried to decrease the beta to lower number like this here norm_f = cv.normalize(f, None, alpha=0, beta=0.03, norm_type=cv.NORM_MINMAX, dtype=cv.CV_32F) and the image is appeared but it's very different in the color of it.
The paper I got idea how to do it and dataset (images with ground-truth and PSF kernels in PNG format) are here
This is what they said:
We create the synthetic saturated images in a way similar to [3, 10].
Specifically, we first stretch the intensity range of the latent image
from [0,1] to [−0.1,1.8], and convolve the blur kernels with the
images. We then clip the blurred images into the range of [0,1]. The
same process is adopted for generating non-uniform blurred images.
This is some images I got from my source.
And this is the ground-truth image:
And this is the PSF kernel in PNG format file:
And this is their output (synthetic image):
Please help me out, it doesn't matter solution, even it's a software, another languages, another tools. I only care eventually I have synthetic blurred image from original (sharp) image with PSF kernel with good performance (I tried on Matlab but suffered similar problem, I used imfilter, and one more problem with Matlab is they're slow).
(please not judge for only care about the output of the process, I'm not using deconvol method to deblur blurred back to the original image one so I want to have enough datasets (original&blurred) pairs to test my hypothesis/method)
Thanks.
OpenCV reads / writes images in BGR format, and Matplotlib in RGB. So if you want to display the right colours, you should first convert it to RGB :
result_rgb = cv.cvtColor(result, cv.COLOR_BGR2RGB)
imgplot = plt.imshow(result)
plt.show()
Edit: You could convolve each channel separately and normalise your convolve image like this:
f = cv.cvtColor(f, cv.COLOR_BGR2GRAY)
norm_image = img / 255.0
norm_f = f / 255.0
result0 = ndimage.convolve(norm_image[:,:,0], norm_f)/(np.sum(norm_f))
result1 = ndimage.convolve(norm_image[:,:,1], norm_f)/(np.sum(norm_f))
result2 = ndimage.convolve(norm_image[:,:,2], norm_f)/(np.sum(norm_f))
result = np.stack((result0, result1, result2), axis=2).astype(np.float32)
Then you should get the right colors. This though uses a normalisation between 0.0 and 1.0 for both the image and the kernel (unlike between -0.1 and 1.8 for the image as the paper suggests).

Low Pass Filter for blurring an image

I'm trying to blur an image using fft by passing a low pass filter that I created but the output yields to be an image full of gray noise. I'm just trying to follow the basics here but it seems like there is something wrong with my implementation:
from scipy import fftpack
import numpy as np
import imageio
from PIL import Image, ImageDraw
image1 = imageio.imread('image.jpg',as_gray=True)
#convert image to numpy array
image1_np=np.array(image)
#fft of image
fft1 = fftpack.fftshift(fftpack.fft2(image1_np))
#Create a low pass filter image
x,y = image1_np.shape[0],image1_np.shape[1]
#size of circle
e_x,e_y=50,50
#create a box
bbox=((x/2)-(e_x/2),(y/2)-(e_y/2),(x/2)+(e_x/2),(y/2)+(e_y/2))
low_pass=Image.new("L",(image1_np.shape[0],image1_np.shape[1]),color=0)
draw1=ImageDraw.Draw(low_pass)
draw1.ellipse(bbox, fill=255)
low_pass_np=np.array(low_pass)
low_pass_fft=fftpack.fftshift(fftpack.fft2(low_pass))
#multiply both the images
filtered=np.multiply(fft1,low_pass_fft)
#inverse fft
ifft2 = abs(fftpack.ifft2(fftpack.ifftshift(filtered)))
#save the image
imageio.imsave('fft-then-ifft.png', ifft2.astype(np .uint8))
As mentioned in comments by Cris Luengo, there are a few things that need to be corrected:
The provided elliptical shape for the low-pass filter makes sense in the frequency-domain, so you shouldn't be computing its FFT.
The filter magnitude of 255 scales the results by the same amount. As you store such large values, the uint8 type wraps around to keep only the 8 least significant bits, resulting in something that looks like noise. This can be fixed by simply changing the value of the filter:
draw1.ellipse(bbox, fill=1)
After readjusting the scaling, there computed filtered may still get slightly out of the desired 0-255 range in some areas of the image. This creates wrap-around spots (black areas in regions surrounded by white pixels, white areas in regions surrounded by black pixels, or even gradient bands where the image goes from white to black to white). To avoid this is common to clip the values to the 0-255 range with the following:
ifft2 = np.real(fftpack.ifft2(fftpack.ifftshift(filtered)))
ifft2 = np.maximum(0, np.minimum(ifft2, 255))
After making these corrections, you should have the following code:
from scipy import fftpack
import numpy as np
import imageio
from PIL import Image, ImageDraw
image1 = imageio.imread('image.jpg',as_gray=True)
#convert image to numpy array
image1_np=np.array(image1)
#fft of image
fft1 = fftpack.fftshift(fftpack.fft2(image1_np))
#Create a low pass filter image
x,y = image1_np.shape[0],image1_np.shape[1]
#size of circle
e_x,e_y=50,50
#create a box
bbox=((x/2)-(e_x/2),(y/2)-(e_y/2),(x/2)+(e_x/2),(y/2)+(e_y/2))
low_pass=Image.new("L",(image1_np.shape[0],image1_np.shape[1]),color=0)
draw1=ImageDraw.Draw(low_pass)
draw1.ellipse(bbox, fill=1)
low_pass_np=np.array(low_pass)
#multiply both the images
filtered=np.multiply(fft1,low_pass_np)
#inverse fft
ifft2 = np.real(fftpack.ifft2(fftpack.ifftshift(filtered)))
ifft2 = np.maximum(0, np.minimum(ifft2, 255))
#save the image
imageio.imsave('fft-then-ifft.png', ifft2.astype(np .uint8))
And the following filtered image:

greyscale image normalization issue with MINMAX

I am trying to normalize a bunch of images which I have scaled to 32x32 pixel size. I was initially wanting to use x-median/std for normalization, but I found some code to use MINMAX instead so I am trying that. I need to get the image into the 0 to 1 range, so I assume that dtype 32F would do that, so I think this is where the problem lies. When I run the code, the normalized image is completely black. Any advice on how to solve that?
Here is the code:
import cv2
import numpy as np
from PIL import Image
image = cv2.imread("image.png", cv2.IMREAD_UNCHANGED) # uint8 image
norm_image = np.zeros((32, 32))
norm_image = cv2.normalize(image, norm_image, alpha=0, beta=1, norm_type = cv2.NORM_MINMAX, dtype=cv2.CV_32F)
im = Image.fromarray(norm_image)
if im != 'RGB':
im = im.convert('RGB')
im.save("image_norm.png")
cv2.waitKey(0)
cv2.destroyAllWindows()
Sample image

Categories

Resources