+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2019-04(1)

2019-06(2)

2019-07(2)

2019-08(87)

2019-09(90)

高斯模糊与图像失焦,附Python代码实现

发布于2020-07-29 22:24     阅读(75)     评论(0)     点赞(30)     收藏(4)


图像失焦与高斯模糊

图像失焦

散焦成像原理1
在这里插入图片描述

当成像点不在焦平面上时,其点会扩散成半径为r的模糊圆盘,有
r/r0=(v0v)/v r/r_0 =(|v_0-v|)/v
常用的点扩散函数包括圆柱形分布和高斯分布。其中高斯分布为
h(x,y)=1(2πσ2)exp[((x2+y2))(2σ2)] h(x,y)=\frac{1}{(2πσ^2 ) } exp⁡[\frac{(-(x^2+y^2))}{(2σ^2 )}]
均匀散焦图像无噪声可表示为
I(x,y)=f(x,y)r(x,y)=HR I(x,y)=f(x,y)*r(x,y)=HR


高斯模糊

既然提到了高斯分布,不由得想到了经常出现的高斯模糊,可以参考一下高斯模糊的原理

从数学的角度来看,图像的高斯模糊过程就是图像与正态分布做卷积。由于正态分布又叫作高斯分布,所以这项技术就叫作高斯模糊。

表达式

其二维空间下为

G(u,v)=12πσ2e(u2+v2)/(2σ2) G(u,v) = \frac{1}{2\pi\sigma^2}e^{-(u^2+v^2)/(2\sigma^2)}
其中r是模糊半径,σ是正态分布的标准偏差。
二维空间中,这个公式生成的曲面的等高线是从中心开始呈正态分布的同心圆。

理论上来讲,图像中每点的分布都不为零,这也就是说每个像素的计算都需要包含整幅图像。在实际应用中,在计算高斯函数的离散近似时,在大概3σ距离之外的像素都可以看作不起作用,这些像素的计算也就可以忽略。

线性可分

高斯模糊也可以在二维图像上对两个独立的一维空间分别进行计算,这叫作线性可分。这也就是说,使用二维矩阵变换得到的效果也可以通过在水平方向进行一维高斯矩阵变换加上竖直方向的一维高斯矩阵变换得到。

叠加效果

使用半径分别为6和8的两次高斯模糊变换得到的效果等同于一次半径为10的高斯模糊效果.

所以说,图像失焦其实就相当于是给图像加上了一层高斯模糊。


简单的高斯模糊模拟失焦程序

用python简单的写了写代码来添加这个高斯模糊

实现效果

测试图片用的是
在这里插入图片描述
若卷积核半径为1
在这里插入图片描述
变化基本不大
若卷积核半径为10,则有肉眼可见的模糊了
在这里插入图片描述

高斯分布函数

def KernelMaker(r):  ### r 是半径
    '''高斯分布卷积核'''
    
    kernel = np.empty((2*r+1,2*r+1))
    summat = 0 ## 矩阵求和,用来归一化
    for i in range(0,2*r+1):
        for j in range(0,2*r+1):
            dr2 = 2*(r**2) # double r^2 
            gaussp = (1/(pi*dr2)) * np.exp( -((i-r)**2 + (j-r)**2) / (dr2)  )
            kernel[i][j] = gaussp
            summat += gaussp
    
    kernel = kernel/summat## 归一化
    print("高斯函数矩阵为\n",kernel)
    return kernel

RGB通道的拆分与合并


def Channel_partial(img):
    width = img.size[0]
    height = img.size[1]
    nR = np.empty((width,height))
    nG = np.empty((width,height))
    nB = np.empty((width,height))

    # for i in range(0,width):
    #     for j in range(0,height):
    #         nR[i][j]=img.getpixel((i,j))[0] 
    #         nG[i][j]=img.getpixel((i,j))[1] 
    #         nB[i][j]=img.getpixel((i,j))[2] 
    imgArray = np.array(img)

    nR = imgArray[:,:,0]
    nG = imgArray[:,:,1]
    nB = imgArray[:,:,2]
    return nR,nG,nB


def Channel_Compound(nR,nG,nB):
    width = nR.shape[0]
    height = nR.shape[1]
    imgArray = np.empty((width,height,3))
    imgArray[:,:,0] = nR
    imgArray[:,:,1] = nG
    imgArray[:,:,2] = nB
    imgArray = np.asarray(imgArray, np.uint8)
    img = Image.fromarray(imgArray)
    return img

进行高斯模糊操作(卷积方法和FFT方法)

def GaussianBlur(array, kernel , mode):
    '''高斯模糊'''
    Timer = clock()
    width = array.shape[0]
    height = array.shape[1]
    if mode == 'fft':
        F_array = np.fft.fft2(array)
        F_kernel = np.fft.fft2(kernel,s = (width,height))
        new_F_array = F_array * F_kernel
        new_array = np.fft.ifft2(new_F_array)
        new_array = np.asarray(new_array, np.uint8)
        print("耗费时间:",Timer)
        return new_array
    
    if mode == 'conv':
        new_array = np.empty(array.shape)
        r = (kernel.shape[0]-1)//2
        for i in range(r+1,width - r):
            for j in range(r + 1,height - r):
                o = 0
                for x in range(i-r,i+r+1):
                    p = 0
                    for y in range(j-r,j+r+1):
                        new_array[i][j] += array[x][y] * kernel[o][p]
                        p += 1
                    o += 1
        new_array = np.asarray(new_array, np.uint8)
        print("耗费时间:",Timer)
        return new_array

完整代码

from PIL import Image
from matplotlib.pyplot import imsave
import numpy as np
from numpy import pi
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt

from time import clock

def KernelMaker(r):  ### r 是半径
    '''高斯分布卷积核'''
    
    kernel = np.empty((2*r+1,2*r+1))
    summat = 0 ## 矩阵求和,用来归一化
    for i in range(0,2*r+1):
        for j in range(0,2*r+1):
            dr2 = 2*(r**2) # double r^2 
            gaussp = (1/(pi*dr2)) * np.exp( -((i-r)**2 + (j-r)**2) / (dr2)  )
            kernel[i][j] = gaussp
            summat += gaussp
    
    kernel = kernel/summat## 归一化
    print("高斯函数矩阵为\n",kernel)
    return kernel

def test_kernelCreate():
    r = 10
    kernel = KernelMaker(r)
    x_values = np.arange(-r,r+1,1)
    y_values = np.arange(-r,r+1,1)
    X,Y = np.meshgrid(x_values,y_values)
    fig = plt.figure()
    ax = Axes3D(fig)
    ax.plot_surface(X,Y,kernel)
    plt.show()

def Channel_partial(img):
    width = img.size[0]
    height = img.size[1]
    nR = np.empty((width,height))
    nG = np.empty((width,height))
    nB = np.empty((width,height))

    # for i in range(0,width):
    #     for j in range(0,height):
    #         nR[i][j]=img.getpixel((i,j))[0] 
    #         nG[i][j]=img.getpixel((i,j))[1] 
    #         nB[i][j]=img.getpixel((i,j))[2] 
    imgArray = np.array(img)

    nR = imgArray[:,:,0]
    nG = imgArray[:,:,1]
    nB = imgArray[:,:,2]
    return nR,nG,nB

def Channel_Compound(nR,nG,nB):
    width = nR.shape[0]
    height = nR.shape[1]
    imgArray = np.empty((width,height,3))
    imgArray[:,:,0] = nR
    imgArray[:,:,1] = nG
    imgArray[:,:,2] = nB
    imgArray = np.asarray(imgArray, np.uint8)
    img = Image.fromarray(imgArray)
    return img

def GaussianBlur(array, kernel , mode):
    '''高斯模糊'''
    Timer = clock()
    width = array.shape[0]
    height = array.shape[1]
    if mode == 'fft':
        F_array = np.fft.fft2(array)
        F_kernel = np.fft.fft2(kernel,s = (width,height))
        new_F_array = F_array * F_kernel
        new_array = np.fft.ifft2(new_F_array)
        new_array = np.asarray(new_array, np.uint8)
        print("耗费时间:",Timer)
        return new_array
    
    if mode == 'conv':
        new_array = np.empty(array.shape)
        r = (kernel.shape[0]-1)//2
        for i in range(r+1,width - r):
            for j in range(r + 1,height - r):
                o = 0
                for x in range(i-r,i+r+1):
                    p = 0
                    for y in range(j-r,j+r+1):
                        new_array[i][j] += array[x][y] * kernel[o][p]
                        p += 1
                    o += 1
        new_array = np.asarray(new_array, np.uint8)
        print("耗费时间:",Timer)
        return new_array


if __name__ == "__main__":
    r = 1  ## 卷积核半径
    kernel = KernelMaker(r)
    ## 测试一下生成卷积核
    # test_kernelCreate() 
    path = r'E:\Xilinx失焦\Blur\1.jpg'
    img = Image.open(path)
    nR,nG,nB = Channel_partial(img)

    FnR = GaussianBlur(nR,kernel,'fft')
    FnG = GaussianBlur(nG,kernel,'fft')
    FnB = GaussianBlur(nB,kernel,'fft')

    newimg = Channel_Compound(FnR,FnG,FnB)
    newimg.show()

  1. 吴秋峰. 面向散焦图像的去模糊与深度估计研究[D].哈尔滨工业大学,2013. ↩︎

原文链接:https://blog.csdn.net/kzz6991/article/details/107614466



所属网站分类: 技术文章 > 博客

作者:智慧星辰

链接: https://www.pythonheidong.com/blog/article/466763/

来源: python黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

30 0
收藏该文
已收藏

评论内容:(最多支持255个字符)