Richardson-Lucy算法 盲反卷积算法可以同时恢复图像合点扩散函数(PSF)
LR算法是时域的迭代非线性复原算法,基于贝叶斯理论,泊松分布和最大似然估计算法对图像进行修复。
当下面这个迭代收敛时,模型的最大似然函数就可以得到一个令人满意的方程:
f ^ k + 1 ( x , y ) = f ^ k ( x , y ) [ g ( x , y ) h ( x , y ) ∗ f ^ k ( x , y ) ∗ h ( − x , − y ) ] \hat{f}_{k+1}(x,y) = \hat{f}_{k}(x,y) \left[ \frac{g(x,y)}{h(x,y)*\hat{f}_{k}(x,y)}* h(-x,-y) \right] f^k+1(x,y)=f^k(x,y)[h(x,y)∗f^k(x,y)g(x,y)∗h(−x,−y)]
其中:* 代表卷积, f ^ \hat{f} f^ 代表未退化的图像估计, h ( x , y ) h(x,y) h(x,y)为退化矩阵,
该函数求解过程用到了极大似然法和EM算法:详情请参考
[J,PSF] = deconvblind(I,INITPSF)使用最大似然算法对图像I解卷积,返回去模糊图像J和恢复的点扩散函数PSF。 生成的PSF是与INITPSF相同大小的正数组,归一化,所以它的总和增加到1。PSF的恢复受其初始猜测大小INITPSF的影响较大,而其值较小(一个数组是一个更安全的猜测)。
第一次恢复J1,P1 ,underPSF阵列大小比真正的PSF每一维都要小4个像素。
underPSF = np.ones(PSF.shape[0]-4,PSF.shape[1]-4)
[J1,P1] = deconvblind(Blurred,underPSF )
第二次恢复J2,P2,overPSF阵列大小比真正的PSF每一维都要小4个像素。
overPSF = np.ones(PSF.shape[0]+4,PSF.shape[1]+4)
[J2,P2] = deconvblind(Blurred,overPSF )
第三次恢复J3,P3,initPSF阵列与真正的PSF一样大。
initPSF= np.ones(PSF.shape[0],PSF.shape[1])
[J3,P3] = deconvblind(Blurred,initPSF)
分析恢复的PSF
可视化原PSF与三个PSF的图像,并分析其特征分布于平滑度。(比如,中心与边界分布,左右侧分布,上下侧分布,左上右下或者右上左下的特征分布)
改善恢复
上述三种PSF中,第三个恢复最好,但是出现了其他效应,如环状效应。
为解决该效应,我们使用边缘检测算子来找到‘尖锐’的像素,来纠错。阈值=0.3
weight = cv2.edge(I,sobel,0.3)
拓宽面积:imdilate 以及在结构元素中通过。
se = strel(‘disk’,2)
weight =idouble(imdilate(weight,se))
边缘像素置为零:
weight[3:-2,:] = 0
weight[:,3:-2] = 0
图像通过调用 deconvblind 的权重数组和迭代的数量增加(30),抑制环效应。
[J,P] = deconvblind(Blurred,initPSF,30,[],weight)
[J,PSF] = deconvblind(I,INITPSF,NUMIT)
[J,PSF] = deconvblind(I,INITPSF,NUMIT,DAMPAR)
[J,PSF] = deconvblind(I,INITPSF,NUMIT,DAMPAR,WEIGHT)
[J,PSF] = deconvblind(I,INITPSF,NUMIT,DAMPAR,WEIGHT,READOUT).
参数:
I :输入图像
PSF :点扩散函数
NUMIT :迭代次数
DAMPAR(可选)是一个数组,用于指定图像I(根据泊松噪声的标准偏差)的结果图像的阈值偏差,低于此值会发生阻尼。 对于在DAMPAR值内偏离其原始值的像素,迭代被抑制。 这可以抑制这些像素中的噪音,并在其他地方保留必要的图像细节。 默认值为0(无阻尼)。
WEIGHT(可选)分配给每个像素以反映相机的拍摄质量。 将一个坏像素分配给零权值,从而排除该像素。 您可以根据平场校正的数量来调整自己的权重,而不是给予好像素的权重。 默认值是与输入图像I大小相同的单位数组。
READOUT(可选)是对应于附加噪声(例如,背景,前景噪声)和读出相机噪声方差的阵列(或值)。READOUT必须以图像为单位。 默认值是0。
注意,输出图像J可能会出现由算法中使用的离散傅里叶变换引入的振铃。 在调用deconvblind之前,为了减少振铃使用 I = EDGETAPER(I,PSF)。
还要注意,deconvblind允许您从较早的解卷积运行结果开始恢复解卷积。 要启动此语法,输入I和INITPSF必须以单元数组{IN}和{INITPSF}的形式传入。 然后输出J和PSF变成单元阵列,并可以作为输入数组传递给下一个解卷积调用。 输入单元格数组可以包含一个数字数组(在初始调用时)或四个数值数组(当它是上一次解除绑定运行的输出时)。输出J包含四个元素,其中J {1} = I,J {2}是上一次迭代产生的图像,J {3}是前一次迭代前的图像,J {4}是 通过迭代算法内部使用的数组。
原理:https://blog.csdn.net/weixin_41923961/article/details/81157082
Matlab实现:https://blog.csdn.net/weixin_41923961/article/details/82469557
MATLAB中padarray函数:http://blog.sina.com.cn/s/blog_7f2d8e4e0102wuiq.html
circShift,psf2otf函数:https://blog.csdn.net/wsp_1138886114/article/details/97611270
Numpy.convolve2d: https://blog.csdn.net/m0_38007695/article/details/82794454
以下代码只是复现了deconvblind()的部分功能,并没有迭代估计PSF,希望大神指点,感激不尽。
import numpy as np
import cv2
from temp_004 import circShift,psf2otf
import matplotlib.pyplot as plt
from scipy.signal import convolve2d as conv2
def flip180(arr):
new_arr = arr.reshape(arr.size)
new_arr = new_arr[::-1]
new_arr = new_arr.reshape(arr.shape)
return new_arr
def RL_deconvblind(img,PSF,iterations):
img = img.astype(np.float64)
PSF = PSF.astype(np.float64)
init_img = img
PSF_hat = flip180(PSF)
for i in range(iterations):
est_conv = conv2(init_img,PSF,'same')
relative_blur = img / est_conv
error_est = conv2(relative_blur,PSF_hat, 'same')
init_img = init_img* error_est
return np.uint8(normal(init_img))
def fspecial_Gaussian(KernelWH,sigma):
r, c = KernelWH
return np.multiply(cv2.getGaussianKernel(r, sigma), (cv2.getGaussianKernel(c, sigma)).T)
def bluredImg(src):
GausBlurImg = cv2.GaussianBlur(src,(7 ,7), 3)
return GausBlurImg
def normal(array):
array = np.where(array < 0, 0, array)
array = np.where(array > 255, 255, array)
array = array.astype(np.int16)
return array
if __name__ == '__main__':
path = '../gggg/mohu.png'
image = cv2.imread(path)
b_gray, g_gray, r_gray = cv2.split(image.copy())
Result1 = []
iterations = 20 #迭代次数
PSF = fspecial_Gaussian((5, 5), 1)
for gray in [b_gray, g_gray, r_gray]:
channel1 = RL_deconvblind(gray, PSF, iterations)
Result1.append(channel1)
init_img_all = cv2.merge([Result1[0], Result1[1], Result1[2]])
plt.figure(figsize=(8, 5))
plt.gray()
imgNames = {"Original_Image": image,
"init_img_all": init_img_all,
}
for i, (key, imgName) in enumerate(imgNames.items()):
plt.subplot(121 + i)
plt.xlabel(key)
plt.imshow(np.flip(imgName, axis=2))
plt.show()
matlab中的ind2sub: 详情请点击百度
实现代码如下:
def sub2ind(array_shape, rows, cols):
ind = rows*array_shape[1] + cols
ind[ind < 0] = -1
ind[ind >= array_shape[0]*array_shape[1]] = -1
return ind
def ind2sub(array_shape, ind):
ind[ind < 0] = -1
ind[ind >= array_shape[0]*array_shape[1]] = -1
rows = (ind.astype(np.int32) / array_shape[1])
cols = ind % array_shape[1]
return (rows, cols)
其实就是对整个矩阵旋转180度
def flip180(arr):
new_arr = arr.reshape(arr.size)
new_arr = new_arr[::-1]
new_arr = new_arr.reshape(arr.shape)
return new_arr
鸣谢:
LR理论:https://blog.csdn.net/Du_Shuang/article/details/82899648
matlab的conv2、imfilter、filter2:https://www.cnblogs.com/hyb221512/p/9370367.html
https://github.com/pearu/iocbio
matlab中conv2函数:https://blog.csdn.net/majinlei121/article/details/50256049
盲去卷积:https://blog.csdn.net/qq_26499769/article/details/79397761
测试数据源:https://blog.csdn.net/bluecol/article/details/47359421