OpenCV—Python 盲反卷积模糊图像恢复算法

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/wsp_1138886114/article/details/97683219

一、前言

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 ^ \hat{f} 代表未退化的图像估计, h ( x , y ) h(x,y) 为退化矩阵,
该函数求解过程用到了极大似然法和EM算法:详情请参考

[J,PSF] = deconvblind(I,INITPSF)使用最大似然算法对图像I解卷积,返回去模糊图像J和恢复的点扩散函数PSF。 生成的PSF是与INITPSF相同大小的正数组,归一化,所以它的总和增加到1。PSF的恢复受其初始猜测大小INITPSF的影响较大,而其值较小(一个数组是一个更安全的猜测)。

二、算法流程解析:

  1. 读取图像
  2. 模拟模糊
    通过高斯模糊模拟模糊
  3. 恢复模糊图像使用不同的PSF
执行3次使用不同的PSF:

第一次恢复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)

  1. 分析恢复的PSF
    可视化原PSF与三个PSF的图像,并分析其特征分布于平滑度。(比如,中心与边界分布,左右侧分布,上下侧分布,左上右下或者右上左下的特征分布)

  2. 改善恢复
    上述三种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).

扫描二维码关注公众号,回复: 7207610 查看本文章

参数:
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

deconvblind() python 实现

以下代码只是复现了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()
ind2sub() python 实现

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)

退化函数 h(-x,-y) 实现代码:

其实就是对整个矩阵旋转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

猜你喜欢

转载自blog.csdn.net/wsp_1138886114/article/details/97683219