I am trying to implement the Wiener Filter to perform deconvolution on blurred image. My implementation is like this
import numpy as np
from numpy.fft import fft2, ifft2
def wiener_filter(img, kernel, K = 10):
dummy = np.copy(img)
kernel = np.pad(kernel, [(0, dummy.shape[0] - kernel.shape[0]), (0, dummy.shape[1] - kernel.shape[1])], 'constant')
# Fourier Transform
dummy = fft2(dummy)
kernel = fft2(kernel)
kernel = np.conj(kernel) / (np.abs(kernel) ** 2 + K)
dummy = dummy * kernel
dummy = np.abs(ifft2(dummy))
return np.uint8(dummy)
This implementation is based on the Wiki Page.
The TIFF image used is from : http://www.ece.rice.edu/~wakin/images/lena512color.tiff
But here is a PNG version:
I have a input image motion blurred by a diagonal kernel and some gaussian additive noise is added to it. The lena picture is 512x512 and the blurring kernel is 11x11.
When I apply my wiener_filter to this image the result is like this.
.
I think this deblurred image is not of good quality. So I would like to ask if my implementation is correct.
Update the way I add noise.
from scipy.signal import gaussian, convolve2d
def blur(img, mode = 'box', block_size = 3):
# mode = 'box' or 'gaussian' or 'motion'
dummy = np.copy(img)
if mode == 'box':
h = np.ones((block_size, block_size)) / block_size ** 2
elif mode == 'gaussian':
h = gaussian(block_size, block_size / 3).reshape(block_size, 1)
h = np.dot(h, h.transpose())
h /= np.sum(h)
elif mode == 'motion':
h = np.eye(block_size) / block_size
dummy = convolve2d(dummy, h, mode = 'valid')
return np.uint8(dummy), h
def gaussian_add(img, sigma = 5):
dummy = np.copy(img).astype(float)
gauss = np.random.normal(0, sigma, np.shape(img))
# Additive Noise
dummy = np.round(gauss + dummy)
# Saturate lower bound
dummy[np.where(dummy < 0)] = 0
# Saturate upper bound
dummy[np.where(dummy > 255)] = 255
return np.uint8(dummy)
Use skimage.restoration.wiener, which is usually used like:
>>> 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)
I have also used it in: Deblur an image using scikit-image.
For data comparison, you can find a sample implementation of Wiener filtering and unsupervisived Wiener filtering at
http://scikit-image.org/docs/dev/auto_examples/plot_restoration.html
If you give your original image data, we may be able to help further.
EDIT: Original link seems to be down, try this one:
http://scikit-image.org/docs/dev/auto_examples/filters/plot_restoration.html
We could try unsupervised weiner too (deconvolution with a Wiener-Hunt approach, where the hyperparameters are automatically estimated, using a stochastic iterative process (Gibbs sampler), as described here):
deconvolved, _ = restoration.unsupervised_wiener(im, psf)
Related
I struggled to restore the image using Python. I tried these types of filters which are listed below. Could you help me to overcome this problem, please?
Original Image
Gaussian Filter estimation for degredation function.
The code is;
import numpy as np
from skimage import io, color
import matplotlib.pyplot as plt
def gaussian_filter(k=3, sigma=10):
''' Gaussian filter
:param k: defines the lateral size of the kernel/filter, default 5
:param sigma: standard deviation (dispersion) of the Gaussian distribution
:return matrix with a filter [k x k] to be used in convolution operations
'''
arx = np.arange((-k // 2) + 1.0, (k // 2) + 1.0)
x, y = np.meshgrid(arx, arx)
filt = np.exp(-(1/2) * (np.square(x) + np.square(y)) / np.square(sigma))
return filt / np.sum(filt)
f = io.imread("q2.jpg")
f = color.rgb2gray(f);
io.imsave("deneme1.jpg",f)
h = gaussian_filter(k=7, sigma=1)
# computing the number of padding on one side
hx = int(f.shape[0]//2 - h.shape[0]//2)
hy = int(f.shape[1]//2 - h.shape[1]//2)
print(f.shape[1]//2)
print(h.shape[0]//2)
print(f.shape)
h_pad = np.pad(h, ((hx,hx-1),(hy,hy)), 'constant', constant_values=(0))
# computing the Fourier transforms
F = np.fft.fft2(f)
H = np.fft.fft2(h_pad)
#H[np.abs(H)<=] = 1
plt.subplot(121)
plt.imshow(np.fft.fftshift(np.log(np.abs(F)+1)), cmap="gray")
plt.subplot(122)
plt.imshow(np.fft.fftshift(np.log(np.abs(H)+1)), cmap="gray")
# Filtering
G = F/H
# Inverse Transform
# - we have to perform FFT shift before reconstructing the image in the space domain
g = np.fft.ifftshift(np.fft.ifft2(G))
plt.figure(figsize=(12,5))
plt.subplot(121)
plt.imshow(f, cmap="gray");
plt.title("original image")
plt.subplot(122)
plt.imshow(abs(g), cmap="gray");
plt.title("degraded/blurred image")
The output is;
Fourier domain of original image and gaussian filter
Orginal gray image and restored image
Global and Local histogram equalization.
#LOCAL AND GLOBAL EQUALIZATION
import numpy as np
import matplotlib.pyplot as plt
from skimage import data,io,color, filters, exposure
import skimage.morphology as morp
# Original image
img = io.imread("q2.jpg")
img = color.rgb2gray(img)
# Global equalize
img_global = exposure.equalize_hist(img)
# Local Equalization, disk shape kernel
# Better contrast with disk kernel but could be different
kernel = morp.disk(75)
img_local = filters.rank.equalize(img, selem=kernel)
fig, (ax_img, ax_global, ax_local) = plt.subplots(1, 3,figsize=(15,15))
ax_img.imshow(img, cmap=plt.cm.gray)
ax_img.set_title('Low contrast image')
ax_img.set_axis_off()
ax_global.imshow(img_global, cmap=plt.cm.gray)
ax_global.set_title('Global equalization')
ax_global.set_axis_off()
ax_local.imshow(img_local, cmap=plt.cm.gray)
ax_local.set_title('Local equalization')
ax_local.set_axis_off()
The output is;
The output
I have also tried sobel filter to export edges, unsharpening filters. The result was not satisfied.
The noise might be gamma or rayleigh noise but I don't know how I assume degradation function and also how I implement these noise functions on Python.
I want to demonstrate the Gaussian Kernel used in openCV. cv2.GaussianBlurr(img, kernel_size, sigma) for explanation purposes.
I know how to demonstrate the image which results after applying the blur, and that is not my objective here.
My objective is to demonstrate the kernel automatically for any used sigma, and any used kernel size!
I have seen a code(mentioned down) but I prefer to use something more related to instruction used in OpenCV, rather than just a general mathematical dependent approach.
The expected output kernel is something like this:
import cv2
import numpy as np
# Read Image
img_path = 'image.jpg'
img = cv2.imread(img_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Gaussian Blurr
Kernel = np.ones((15,15))
sigma = 2
Blurred_Image = cv2.GaussianBlur(img, (Kernel.shape[0], Kernel.shape[1]), sigma)
Gaussian Kernel Manual Code:
def dnorm(x, mu, sd):
return 1 / (np.sqrt(2 * np.pi) * sd) * np.e ** (-np.power((x - mu) / sd, 2) / 2)
def gaussian_kernel(size, sigma=1, verbose=False):
kernel_1D = np.linspace(-(size // 2), size // 2, size)
for i in range(size):
kernel_1D[i] = dnorm(kernel_1D[i], 0, sigma)
kernel_2D = np.outer(kernel_1D.T, kernel_1D.T)
kernel_2D *= 1.0 / kernel_2D.max()
if verbose:
plt.imshow(kernel_2D, interpolation='none',cmap='gray')
plt.title("Image")
plt.show()
return kernel_2D
Here is one way in Python/OpenCV.
- Read the input
- Create a delta image (one white pixel in the center of a black background)
- Blur the image
- List item
- Resize the image to enlarge it
- Stretch the image to full dynamic range
- Save the result
import cv2
import numpy as np
import skimage.exposure as exposure
# create delta image
dims = 30
dims2 = 30 // 2
delta = np.zeros((dims,dims,3), dtype=np.float32)
delta[dims2:dims2+1, dims2:dims2+1] = (255,255,255)
# blur image
blur = cv2.GaussianBlur(delta, (0,0), sigmaX=5, sigmaY=5)
# resize 16x
dims4x = dims * 16
resized = cv2.resize(blur, (dims4x,dims4x), interpolation = cv2.INTER_AREA)
# stretch to full dynamic range
result = exposure.rescale_intensity(resized, in_range='image', out_range=(0,255)).astype(np.uint8)
# save image
cv2.imwrite('delta.png',delta)
cv2.imwrite('gaussian_blur_view.png',result)
# show the images
cv2.imshow("delta", delta)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Delta image:
Result:
I have written a simple code to add noise to image:
import cv2
from skimage.util import *
img = cv2.imread("./A/0030050944.jpg")
img = random_noise(img, mode='poisson', seed=42, clip=False)
cv2.imwrite("test.jpg", img)
But this only gives a blank black image.
Check this code. It adds gaussian , salt-pepper , poisson and speckle noise in an image.
Parameters
----------
image : ndarray
Input image data. Will be converted to float.
mode : str
One of the following strings, selecting the type of noise to add:
'gauss' Gaussian-distributed additive noise.
'poisson' Poisson-distributed noise generated from the data.
's&p' Replaces random pixels with 0 or 1.
'speckle' Multiplicative noise using out = image + n*image,where
n is uniform noise with specified mean & variance.
import numpy as np
import os
import cv2
def noisy(noise_typ,image):
if noise_typ == "gauss":
row,col,ch= image.shape
mean = 0
var = 0.1
sigma = var**0.5
gauss = np.random.normal(mean,sigma,(row,col,ch))
gauss = gauss.reshape(row,col,ch)
noisy = image + gauss
return noisy
elif noise_typ == "s&p":
row,col,ch = image.shape
s_vs_p = 0.5
amount = 0.004
out = np.copy(image)
# Salt mode
num_salt = np.ceil(amount * image.size * s_vs_p)
coords = [np.random.randint(0, i - 1, int(num_salt))
for i in image.shape]
out[coords] = 1
# Pepper mode
num_pepper = np.ceil(amount* image.size * (1. - s_vs_p))
coords = [np.random.randint(0, i - 1, int(num_pepper))
for i in image.shape]
out[coords] = 0
return out
elif noise_typ == "poisson":
vals = len(np.unique(image))
vals = 2 ** np.ceil(np.log2(vals))
noisy = np.random.poisson(image * vals) / float(vals)
return noisy
elif noise_typ =="speckle":
row,col,ch = image.shape
gauss = np.random.randn(row,col,ch)
gauss = gauss.reshape(row,col,ch)
noisy = image + image * gauss
return noisy
From How to add noise (Gaussian/salt and pepper etc) to image in Python with OpenCV
possion noise
from PIL import Image
import numpy as np
from skimage.util import random_noise
im = Image.open("test.jpg")
# convert PIL Image to ndarray
im_arr = np.asarray(im)
# random_noise() method will convert image in [0, 255] to [0, 1.0],
# inherently it use np.random.normal() to create normal distribution
# and adds the generated noised back to image
noise_img = random_noise(im_arr, mode='possion', var=0.05**2)
noise_img = (255*noise_img).astype(np.uint8)
img = Image.fromarray(noise_img)
img.show()
Forgive me if I am unable to explain well because I am not native speaker.
I am working on blurring the part of image according to the white part of segmentation map. For example here is my segmentation image ( bmp image ).
.
Now what I want is to blur the part of original image where the pixels are white in the segmentation map. I just wrote the following code to so.
mask = mask >= 0.5
mask = np.reshape(mask, (512, 512))
mh, mw = 512, 512
mask_n = np.ones((mh, mw, 3))
mask_n[:,:,0] *= mask
mask_n[:,:,1] *= mask
mask_n[:,:,2] *= mask
# discard padded area
ih, iw, _ = image_n.shape
delta_h = mh - ih
delta_w = mw - iw
top = delta_h // 2
bottom = mh - (delta_h - top)
left = delta_w // 2
right = mw - (delta_w - left)
mask_n = mask_n[top:bottom, left:right, :]
# addWeighted
image_n = image_n *1 + cv2.blur(mask_n * 0.8, (800, 800))
Please help me, Thanks.
You can do it in the following steps:
Load original image and mask image.
Blur the whole original image and save it in a different variable.
Use np.where() method to select the pixels from the mask where you want blurred values and then replace it.
See the sample code below:
import cv2
import numpy as np
img = cv2.imread("./image.png")
blurred_img = cv2.GaussianBlur(img, (21, 21), 0)
mask = cv2.imread("./mask.png")
output = np.where(mask==np.array([255, 255, 255]), blurred_img, img)
cv2.imwrite("./output.png", output)
Here's an alternative to the solution proposed by #Chris Henri. It relies on scipy.ndimage.filters.gaussian_filter and NumPy's boolean indexing:
from skimage import io
import numpy as np
from scipy.ndimage.filters import gaussian_filter
import matplotlib.pyplot as plt
mask = io.imread('https://i.stack.imgur.com/qJiKf.png')
img = np.random.random(size=mask.shape[:2])
idx = mask.min(axis=-1) == 255
blurred = gaussian_filter(img, sigma=3)
blurred[~idx] = 0
fig, axs = plt.subplots(1, 3, figsize=(12, 4))
for ax, im in zip(axs, [img, mask, blurred]):
ax.imshow(im, cmap='gray')
ax.set_axis_off()
plt.show(fig)
Here is yet another alternative to do so, useful though when you have a 2D segmentation array indicating the segmented object class of pixel (mutually exclusive) for every index (i,j), and a 3D image on which you want to apply the blur.
def gaussian_blur(image: np.ndarray,
segmentation: np.ndarray,
classes_of_interest: list,
gaussian_variance: float = 10) -> np.ndarray:
'''
Function that applies a gaussian filter to the image,
specifically to the pixels contained in the possible segmented classes.
Returns an image (np.ndarray) where the gaussian blur intensity is
regulated by the parameter gaussian_variance.
'''
#Apply masking to select only the indices where the specific class is present
mask = np.isin(segmentation, classes_of_interest)
#Creating a 3D mask for all the channels and place it at channel axis
mask_3d = np.stack([mask,mask,mask], axis=2)
#Mask the image according to the 3D mask
img_masked = np.where(mask_3d, img, 0).astype(np.int8)
#Define gaussian blur noisy function
def noisy(image):
row,col,ch= image.shape
mean = 0
var = gaussian_variance
sigma = np.sqrt(var)
gauss = np.random.normal(mean,sigma,(row,col,ch))
gauss = gauss.reshape(row,col,ch)
#Sums up gaussian noise to img
noisy = image + gauss
return noisy.astype(np.uint8)
#Blurs the masked segmentation
img_masked_noisy = noisy(img_masked)
#Puts the blurred part back in the original image as substitution
img[mask_3d] = img_masked_noisy[mask_3d]
return img
And here is a toy example:
import numpy as np
possible_classes = [1,2,3]
#Setting up a toy example with a small image,
#shape (N, N, 3)
img = np.floor(np.random.random(size=(8,8,3)) * 256).astype(np.uint8)
#Setting up a fake segmentation with 3 mutually exclusive possible classes,
#shape (N, N)
segmentation = np.random.choice(possible_classes, size=(8,8))
new_img_blurred = gaussian_blur(img,
segmentation= segmentation,
classes_of_interest= possible_classes[:2])
i have a set of grayscale images (96,96) and i want to apply gabor filter to the entire images pixel by pixel. i heard that gabor filters give maximum variance in data and insensitive to orientation and lightning.
Here is a piece of code i use. I applied the filter to one image and output data is Image+Gabor filter applied.
I tried to feed this gabor filter applied image to machine learning algorithm and there is no change or improvement in the classification accuracy. Why?
import cv2
import numpy as np
import pandas as pd
import pylab as pl
df = pd.read_csv('test.csv',header=0)
df['Image'] = df['Image'].apply(lambda im: np.fromstring(im, sep=' ') )
X = np.vstack (df['Image'].values)
X = X.astype(np.uint8)
X = X.reshape(-1,96,96)
def image_histogram_equalization(image, number_bins=256):
# from http://www.janeriksolem.net/2009/06/histogram-equalization-with-python-and.html
# get image histogram
image_histogram, bins = np.histogram(image.flatten(), number_bins, normed=True)
cdf = image_histogram.cumsum() # cumulative distribution function
cdf = 255 * cdf / cdf[-1] # normalize
# use linear interpolation of cdf to find new pixel values
image_equalized = np.interp(image.flatten(), bins[:-1], cdf)
return image_equalized.reshape(image.shape), cdf
def build_filters():
filters = []
ksize = 31
for theta in np.arange(0, np.pi, np.pi / 16):
kern = cv2.getGaborKernel((ksize, ksize), 4.0, theta, 10.0, 0.5, 0, ktype=cv2.CV_32F)
kern /= 1.5*kern.sum()
filters.append(kern)
return filters
def process(img, filters):
accum = np.zeros_like(img)
for kern in filters:
fimg = cv2.filter2D(img, cv2.CV_8UC3, kern)
np.maximum(accum, fimg, accum)
return accum
filters = []
for k in xrange(5):
img = X[k]
X[k, :, :] = image_histogram_equalization(X[k, :,:])[0]
filters = build_filters()
filters = np.asarray(filters)
print filters.shape
res1 = process(img, filters)
cv2.imshow('result', res1)
cv2.waitKey(0)
cv2.destroyAllWindows()
f = np.asarray(filters)
print 'Filters', f.shape
It outputs the filters with 16 orientations (for one particular frequency)
(16, 31, 31) - Image 1
(16, 31, 31) - Image 2
(16, 31, 31) - Image 3
..
Sorry my question may be premature, but i am curious to know more.
what should i feed to the machine learning classifier? is it only gabor filter data or the filter itself applied to all images.
How do i tweak the above code to get filter responses for 10 different frequencies? i want to extract 16x10=160 filters for every image.
i have set the gabor kernel size to 31x31 for 96x96 pixel images. Is this correct?