图像处理-005模糊

图像是获取信息的重要来源,但图像存在着噪声(过多的干扰信息),清除噪声有利于后续图像信息获取及特征提取。图像处理中,去噪的过程即模糊的过程。

图像模糊也称图像平滑处理。

在数字图像中,图像表示为像素 的二维数组,像素是组成图像的最基本单元,由亮度intensity(灰色图像仅有单个像素亮度)或色彩color(3个像素亮度值)表示。在表示图像的像素数组中,邻近像素点像素值的不同导致图像的不平滑,图像平滑便是指图像邻近像素点像素值趋近。趋近后图像的某些区域会丢失详细信息,表现出图像变模糊。图像模糊去除了图像中的高频信息,达到消除图像噪声、边缘的目标。

图像模糊是图像像素取周围像素平均值。

图像模糊是通过低通滤波卷积核与图像做卷积实现的,背后的实质是图像的卷积运算。卷积运算后图像边缘会变得模糊。所谓卷积是对图像像素的操作,使图像中的每一个像素点均为源图像与卷积核的乘积。如图所示:

卷积指两个函数通过乘法运算得到第三个函数的过程,运算时两个函数必需具有相同的维数。其物理意义是:一个函数(单位响应)在另一个函数(输入图像~~~~)上的加权叠加。

OpenCV提供了四种主要类型的模糊方式: Averaging, Gaussian Blurring,Median Blurring,Bilateral Filtering.

Averaging(均值模糊)

均值模糊也称均值滤波,邻域平均,是图像模糊处理中最简单的一种,处理时将原图中的一个像素值和它周围临近的N个像素值与卷积核做乘法运算,然后求得平均值,获得新图中该点的像素值。

opencv提供 cv.blur() 和 cv.boxFilter()两种函数用于均值模糊计算。

c:
void cv::blur(InputArray     src, #输入图像OutputArray     dst, #与输入图像同大小,同类型的输出图像Size     ksize,  #卷积核大小Point     anchor = Point(-1,-1), #卷积核锚点位置int     borderType = BORDER_DEFAULT #边框类型
)
Python:
cv.blur(src, ksize[, dst[, anchor[, borderType]]]) -> dst

blur函数使用的卷积核公式:

K=1ksize.width*ksize.height[111⋯11111⋯11⋮⋮⋮⋱⋮⋮111⋯11]\texttt{K} = \frac{1}{\texttt{ksize.width * ksize.height}} \begin{bmatrix} 1 & 1 & 1 & \cdots & 1 & 1 \\ 1 & 1 & 1 & \cdots & 1 & 1 \\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ 1 & 1 & 1 & \cdots & 1 & 1 \\ \end{bmatrix} K=ksize.width * ksize.height1​⎣⎡​11⋮1​11⋮1​11⋮1​⋯⋯⋱⋯​11⋮1​11⋮1​⎦⎤​

C:
void cv::boxFilter(InputArray     src, #输入图像OutputArray     dst, #与输入图像同大小,同类型的输出图像int     ddepth, #输出图像深度,默认值为-1 此时同输入图像Size     ksize, #卷积核大小Point     anchor = Point(-1,-1), #锚点 默认(-1,-1)指卷积核中心位置bool     normalize = true,  #卷积核是否归一化int     borderType = BORDER_DEFAULT #卷积时边界处理
)
Python:
cv.boxFilter(src, ddepth, ksize[, dst[, anchor[, normalize[, borderType]]]]) ->    dst

boxFilter函数使用的公式为:

K=α[111⋯11111⋯11⋮⋮⋮⋱⋮⋮111⋯11]\texttt{K} = \alpha \begin{bmatrix} 1 & 1 & 1 & \cdots & 1 & 1 \\ 1 & 1 & 1 & \cdots & 1 & 1 \\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ 1 & 1 & 1 & \cdots & 1 & 1 \end{bmatrix} K=α⎣⎡​11⋮1​11⋮1​11⋮1​⋯⋯⋱⋯​11⋮1​11⋮1​⎦⎤​

其中:

α={1ksize.width*ksize.heightwhennormalize=true1otherwise\alpha = \begin{cases} \frac{1}{\texttt{ksize.width * ksize.height}} & \texttt{when } \texttt{normalize=true} \\1 & \texttt{otherwise}\end{cases} α={ksize.width * ksize.height1​1​when normalize=trueotherwise​

从公式可见,boxFilter函数提供了比blur更丰富的形式,在卷积核做归一化时两函数是一致的。

实现代码: C++

void ImageBlur::image_blur(const Mat &img) {logger_info("======image_blur===========");
//  均值模糊averaging_blur(img);averaging_boxFilter(img);averaging_boxFilter_unnormalize(img);}void ImageBlur::averaging_blur(const Mat &img) {logger_info("======averaging_blur===========");Mat dst;
//  卷积和大小Size kernel_size = Size(5, 5);
//  锚点 默认卷积核中心点Point anchor = Point(-1, -1);
//  均值模糊blur(img, dst, kernel_size, anchor);imshow("averaging_blur", dst);
}void ImageBlur::averaging_boxFilter(const Mat &img) {logger_info("======averaging_boxFilter===depth=%d========", img.depth());Mat dst;//  卷积和大小Size kernel_size = Size(5, 5);//  锚点 默认卷积核中心点Point anchor = Point(-1, -1);//  均值模糊boxFilter(img, dst, img.depth(), kernel_size, anchor);imshow("averaging_boxFilter", dst);
}/*** 卷积核不做归一化* @param img*/
void ImageBlur::averaging_boxFilter_unnormalize(const Mat &img) {logger_info("======averaging_boxFilter_unnormalize===depth=%d========", img.depth());Mat dst;//  卷积和大小Size kernel_size = Size(13, 13);//  锚点 默认卷积核中心点Point anchor = Point(-1, -1);//  均值模糊boxFilter(img, dst, img.depth(), kernel_size, anchor, false);imshow("averaging_boxFilter_unnormalize", dst);
}

实现代码: python

def image_blur(origin_image):# 均值模糊average_blur_kernel_5 = averaging_blur_kernel_5(origin_image)average_blur_kernel_11 = averaging_blur_kernel_11(origin_image)average_boxfilter_kernel_11 = averaging_boxfilter_kernel_11(origin_image)average_boxfilter_kernel_11_unnormalize = averaging_boxfilter_kernel_11_unnormalize(origin_image)titles = ['Original Image',"averaging_blur_kernel_5","averaging_blur_kernel_11","average_boxfilter_kernel_11","average_boxfilter_kernel_11_unnormalize"]images = [cv.cvtColor(origin_image, cv.COLOR_BGR2RGB),cv.cvtColor(average_blur_kernel_5, cv.COLOR_BGR2RGB),cv.cvtColor(average_blur_kernel_11, cv.COLOR_BGR2RGB),cv.cvtColor(average_boxfilter_kernel_11, cv.COLOR_BGR2RGB),cv.cvtColor(average_boxfilter_kernel_11_unnormalize, cv.COLOR_BGR2RGB)]for i in range(5):plt.subplot(2, 3, i + 1)plt.imshow(images[i], 'gray', vmin=0, vmax=255)plt.title(titles[i])plt.xticks([]), plt.yticks([])plt.show()key = cv.waitKey(0)while key != ord('q'):key = cv.waitKey(0)cv.destroyAllWindows()def averaging_blur_kernel_5(origin_img):logger.log.info("averaging_blur_kernel_5")# 卷积核大小kernel_size = (5, 5)# 锚点anchor = (-1, -1)dst = cv.blur(origin_img, kernel_size, None, anchor)cv.imshow("averaging_blur_kernel_5", dst)return dstdef averaging_blur_kernel_11(origin_img):logger.log.info("averaging_blur_kernel_11")# 卷积核大小kernel_size = (11, 11)# 锚点anchor = (-1, -1)dst = cv.blur(origin_img, kernel_size, None, anchor)cv.imshow("averaging_blur_kernel_5", dst)return dstdef averaging_boxfilter_kernel_11(origin_img):logger.log.info("averaging_boxfilter_kernel_11")# 卷积核大小kernel_size = (11, 11)# 锚点anchor = (-1, -1)dst = cv.boxFilter(origin_img, -1, kernel_size, None, anchor)cv.imshow("averaging_boxfilter_kernel_11", dst)return dstdef averaging_boxfilter_kernel_11_unnormalize(origin_img):logger.log.info("averaging_boxfilter_kernel_11_unnormalize")# 卷积核大小kernel_size = (11, 11)# 锚点anchor = (-1, -1)dst = cv.boxFilter(origin_img, -1, kernel_size, None, anchor, 0)cv.imshow("averaging_boxfilter_kernel_11_unnormalize", dst)return dst

处理效果如下图所示:

Gaussian Blurring(高斯模糊)

均值模糊中确定某个像素点的值取周围像素点的像素均值,未考虑周围像素点远近的影响。理论上讲,离得越近影响因子越大,反之亦然。为解决均值模糊未考虑周围距离的远近带来的影响引入高斯模糊。

高斯模糊特征指对图像应用高斯函数后使图像变得平滑以消除图像噪声的现象。与均值模糊不同的是高斯模糊采用了一种非均匀低通滤波器。更高地保留了图像的边缘效果,降低图像噪声,忽略图像中的细节,高斯模糊通常将图像与高斯卷积核做卷积实现。

高斯卷积核使用正态分布来确立卷积核的系数,其高斯卷积核系数公式如下所示:

G2D(x,y,σ)=12πσ2e−x2+y22σ2G_{2D}(x,y,\sigma) = \frac{1}{2\pi\sigma^2} e^{ - \frac{x^2 + y^2}{2\sigma^2}} G2D​(x,y,σ)=2πσ21​e−2σ2x2+y2​

其中,x, y是卷积核元素的位置,σ\sigmaσ是卷积核元素分布的标准差。

opencv提供了 cv.getGaussianKernel()函数来获取高斯卷积核系数矩阵,矩阵大小为ksize×1\texttt{ksize} \times 1ksize×1,其公式如下:

Gi=α∗e−(i−(ksize−1)/2)2/(2σ2)G_i= \alpha *e^{-(i-( \texttt{ksize} -1)/2)^2/(2 \sigma^2)} Gi​=α∗e−(i−(ksize−1)/2)2/(2σ2)

其中ksize−1\texttt{ksize}-1ksize−1与α\alphaα为系数,使得∑iGi=1\sum_i G_i = 1∑i​Gi​=1, 即卷积核系数和为1。

Mat cv::getGaussianKernel(int     ksize,     # 卷积核大小,为正奇数double     sigma, #高斯分布标准差,若为负数,则置为:sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8int     ktype = CV_64F #高斯卷积类型  CV_32F  CV_64F .)
Python:
cv.getGaussianKernel(ksize, sigma[, ktype]) ->    retval

对图像做高斯模糊时opencv提供了 cv.GaussianBlur()。其详细说明如下:

void cv::GaussianBlur(InputArray     src,  #源图像OutputArray     dst, #目标图像 与源图像同大小同类型Size     ksize,      #卷积核大小ksize.width ksize.height均需是正的奇数, 若为0则通过sigma计算得到double     sigmaX,   #X轴高斯标准差double     sigmaY = 0, #Y轴高斯标准差int     borderType = BORDER_DEFAULT # 卷积时边界处理方式)
Python:
cv.GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]]) ->    dst

实现代码: C++

/*** 高斯模糊 kernel* @param img*/
void ImageBlur::gaussian_blur_kernel(const Mat &img) {logger_info("======gaussian_blur===========");Mat dst;for (int i = 1; i < 31; i = i + 2) {//卷积核越大 模糊越明显GaussianBlur(img, dst, Size(i, i), 0, 0);Mat kernel = getGaussianKernel(i, 0);imshow("gaussian_blur_kernel" + i, dst);}
}

实现代码: python

# 高斯模糊 X轴标准差0
def gaussian_blur_sigma_0(origin_image):logger.log.info("gaussian_blur")titles = ["origin_image"]images = [cv.cvtColor(origin_image, cv.COLOR_BGR2RGB)]for i in range(1, 31, 2):dst = cv.GaussianBlur(origin_image, (i, i), 0)titles.append("gaussian_blur_" + str(i))images.append(cv.cvtColor(dst, cv.COLOR_BGR2RGB))for i in range(16):plt.subplot(4, 4, i + 1)plt.imshow(images[i], 'gray', vmin=0, vmax=255)plt.title(titles[i])plt.xticks([]), plt.yticks([])plt.show()return dst# 高斯模糊 X轴标准差5
def gaussian_blur_sigma_5(origin_image):logger.log.info("gaussian_blur")titles = ["origin_image"]images = [cv.cvtColor(origin_image, cv.COLOR_BGR2RGB)]for i in range(1, 31, 2):dst = cv.GaussianBlur(origin_image, (i, i), 5)titles.append("gaussian_blur_" + str(i))images.append(cv.cvtColor(dst, cv.COLOR_BGR2RGB))for i in range(16):plt.subplot(4, 4, i + 1)plt.imshow(images[i], 'gray', vmin=0, vmax=255)plt.title(titles[i])plt.xticks([]), plt.yticks([])plt.show()return dst

不同卷积核下,X轴标准差为0时的效果:

不同卷积核下,X轴标准差为5时的效果:

Median Blurring(中值模糊)

中值模糊与均值类似,不同的是中值模糊图像中卷积核锚点对应位置的像素值使用卷积核覆盖区域下所有元素中值替换,中值模糊对处理图像中的椒盐噪声非常有效。椒盐噪声指一种随机出现的黑点(胡椒)或者白点(盐),前者是高灰度噪声,后者是低灰度噪声,一般两者同时出现在图像中。在均值模糊和高斯模糊中卷积核锚点对应图像位置的像素值是重新计算的,中值模糊中该处的值为卷积核覆盖图像区域下的某个点的值。

OpenCV提供了中值模糊函数cv.medianBlur()

void cv::medianBlur(InputArray     src,     #输入图像OutputArray     dst, #输出图像,与输入图像同类型,同大小int     ksize)         #卷积核大小,非负的奇数
Python:
cv.medianBlur(src, ksize[, dst]    )->dst

实现代码: C++

/*** 中值模糊* @param img*/
void ImageBlur::median_blur(const Mat &img) {logger_info("======median_blur===========");Mat dst;medianBlur(img, dst, 15);imshow("median_blur", dst);
}

实现代码: Python

# 中值模糊
def median_blur(origin_image):logger.log.info("median_blur")titles = ["origin_image"]images = [cv.cvtColor(origin_image, cv.COLOR_BGR2RGB)]for i in range(1, 31, 2):dst = cv.medianBlur(origin_image, i)titles.append("median_blur_" + str(i))images.append(cv.cvtColor(dst, cv.COLOR_BGR2RGB))for i in range(16):plt.subplot(4, 4, i + 1)plt.imshow(images[i], 'gray', vmin=0, vmax=255)plt.title(titles[i])plt.xticks([]), plt.yticks([])plt.show()return dst

Bilateral Filtering(双边滤波)

双边滤波能在保留图像边缘的一种模糊方式,避免边缘信息丢失,保持图像轮廓的完整。与其它模糊相比,双边滤波比较耗时。

高斯卷积核计算像素时,仅考虑与卷积核叠加的像素邻域,并依据此计算出高斯加权均值,未考虑周围像素具有相同亮度的情况,也未考虑像素处于图像边缘的场景。

图像边缘的像素亮度变化明显,双边滤波与高斯模糊相比考虑像素邻域间的差异,只对与中心像素亮度相近的进行模糊处理,因而保留边缘。

OpenCV提供cv.bilateralFilter()函数做双边滤波。

void cv::bilateralFilter(InputArray     src,#8bit 单通道/三通道图像OutputArray     dst,#与输入图像同类型,同大小的图像int     d,  #像素邻域直径,double     sigmaColor,#色彩空间的标准差 值越大表示模糊时涉及邻域内越远的色彩double     sigmaSpace,#坐标空间的标准差 值越大表示邻域中涉及越远的母亲节不像像素int     borderType = BORDER_DEFAULT #卷积时边界处理方式)
Python:
cv.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]]) ->    dst

实现代码: C++

/*** 双边模糊* @param img*/
void ImageBlur::bilateral_filter(const Mat &img) {logger_info("======bilateral_filter===========");Mat dst;bilateralFilter(img, dst, 20, 40, 10);imshow("bilateral_filter", dst);
}

实现代码: Python

# 双边模糊
def bilateral_blur(origin_image):logger.log.info("bilateral_blur")titles = ["origin_image"]images = [cv.cvtColor(origin_image, cv.COLOR_BGR2RGB)]for i in range(1, 31, 2):dst = cv.bilateralFilter(origin_image, i, i * 2, i / 2)titles.append("bilateral_" + str(i))images.append(cv.cvtColor(dst, cv.COLOR_BGR2RGB))for i in range(16):plt.subplot(4, 4, i + 1)plt.imshow(images[i], 'gray', vmin=0, vmax=255)plt.title(titles[i])plt.xticks([]), plt.yticks([])plt.show()return dst

可以明显看出,与高斯模糊相比,图像的边缘得到很好地保留。

参考文献:

  1. http://szeliski.org/Book/

  2. https://en.wikipedia.org/wiki/Convolution

  3. http://www.songho.ca/dsp/convolution/convolution.html#definition

  4. http://www.cs.csi.cuny.edu/~gu/teaching/courses/csc76010/slides/Parallel_Longlong.pdf

  5. https://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html

图像处理-005模糊相关推荐

  1. 模糊图像处理 去除模糊_图像模糊如何工作

    模糊图像处理 去除模糊 定义 (Definition) Roughly speaking, blurring an image is make the image less sharp. This c ...

  2. python图像处理模糊_Python+OpenCV图像处理之模糊操作

    模糊操作是图像处理中最简单和常用的操作之一,该使用的操作之一原因就为了给图像预处理时减低噪声,基于数学的卷积操作 均值模糊,函数 cv2.blur(image,(5,5)),这是一个平滑图片的函数,它 ...

  3. Python+OpenCV图像处理之模糊操作

    模糊操作是图像处理中最简单和常用的操作之一,该使用的操作之一原因就为了给图像预处理时减低噪声,基于数学的卷积操作 均值模糊,函数 cv2.blur(image,(5,5)),这是一个平滑图片的函数,它 ...

  4. 图像处理之模糊集合原理

    我的小程序: 待办计划:给自己立个小目标吧! 举个例子: 我们习惯处理的集合是一种"干脆的"集合,成员要么属于这个集合(真或1),要么不属于(假或0).例如,用Z表示所有人员的集合 ...

  5. 跳板机连接数据库_跳板数据科学职业生涯回顾

    跳板机连接数据库 When I completed the Springboard Data Science Career Track curriculum earlier this year, mu ...

  6. 图像分割过分割和欠分割_使用图割的图像分割

    图像分割过分割和欠分割 Image segmentation technology is an important research direction in the field of compute ...

  7. jupyter 共享_可共享的Jupyter笔记本!

    jupyter 共享 Alright, so it called Colaboratory, and it's available in Google Drive. 好吧,所以它叫做Colaborat ...

  8. 人口预测和阻尼-增长模型_使用分类模型预测利率-第2部分

    人口预测和阻尼-增长模型 We are back! This post is a continuation of the series "Predicting Interest Rate w ...

  9. 深度学习cnn人脸检测_用于对象检测的深度学习方法:解释了R-CNN

    深度学习cnn人脸检测 介绍 (Introduction) CNN's have been extensively used to classify images. But to detect an ...

最新文章

  1. 使用Combox控件的一个问题
  2. 虚拟化技术KVM的搭建
  3. 深耕大数据“试验田” 发掘新经济“钻石矿”
  4. Python__random库基本介绍
  5. 攻克视频AI界难题,直通芒果TV offer,这场算法大赛你一定不能错过
  6. Data for set COM_LOCAT_ADDR may only be changed in the original system CRQ_100
  7. oracle tnsnames.ora文件用法说明
  8. 5.4.2 Using Hibernate templates
  9. Windows 命令行基础(博主推荐)
  10. mysql中chr_MySQL常见函数
  11. 怎么制定合理的开发计划
  12. codeforces 918 D MADMAX 记忆化搜索
  13. TP5加支付宝手机网站支付配置
  14. WDS桥接副路由器有线上网方法
  15. 13. 查询表orders——统计销售总量大于50的各类商品的ID和销售总量
  16. Hybrid App开发 四大主流平台
  17. PCB板设计布局原则有哪些?
  18. Gartner:数字伦理登上Gartner 2021年隐私技术成熟度曲线期望膨胀期顶点
  19. WIn10 1909 Windows Hello 指纹:出现错误,请稍后再试一次
  20. # PPT进阶——文字环绕

热门文章

  1. 安装计算机软件教学设计,四年级信息技术《应用软件的安装》教学设计
  2. 云数据库 Redis 版连接数据库-DMS登录云数据库
  3. 3月27日即时★分析
  4. 【免费】中国省级行政单位ISO 3166-2对照表
  5. 契约测试概念以及契约测试框架SCC VS PACT对比
  6. Android MMS APN工作原理以及PDP知识摘要
  7. bat如何创建多级文件夹
  8. Android网络请求框架之Retrofit(二)
  9. 交换机服务器维护,网络交换机日常维护技巧
  10. UG二次开发GRIP添加属性