由于种种原因,图像中难免会存在噪声,需要对其去除。噪声可以理解为灰度值的随机变化,即拍照过程中引入的一些不想要的像素点。噪声可分为椒盐噪声,高斯噪声,加性噪声和乘性噪声等,参见:https://zhuanlan.zhihu.com/p/52889476

  噪声主要通过平滑进行抑制和去除,包括基于二维离散卷积的高斯平滑,均值平滑,基于统计学的中值平滑,以及能够保持图像边缘的双边滤波,导向滤波算法等。下面介绍其具体使用

1. 二维离散卷积

  理解卷积:https://www.zhihu.com/question/22298352/answer/637156871

       https://www.zhihu.com/question/22298352/answer/228543288

  学习图像平滑前,有必要了解下卷积的知识,看完上述连接,对于图像处理中卷积应该了解几个关键词:卷积核,锚点,步长,内积,卷积模式

    卷积核(kernel):用来对图像矩阵进行平滑的矩阵,也称为过滤器(filter)

    锚点:卷积核和图像矩阵重叠,进行内积运算后,锚点位置的像素点会被计算值取代。一般选取奇数卷积核,其中心点作为锚点

    步长:卷积核沿着图像矩阵每次移动的长度

    内积:卷积核和图像矩阵对应像素点相乘,然后相加得到一个总和,如下图所示。(不要和矩阵乘法混淆)

        

  

    卷积模式:卷积有三种模式,FULL, SAME,VALID,实际使用注意区分使用的那种模式。(参考:https://zhuanlan.zhihu.com/p/62760780)

      Full:全卷积,full模式的意思是,从filter和image刚相交开始做卷积,白色部分为填0,橙色部分为image, 蓝色部分为filter,filter的运动范围如图所示。

          

      Same卷积:当filter的锚点(K)与image的边角重合时,开始做卷积运算,可见filter的运动范围比full模式小了一圈,same mode为full mode 的子集,即full mode的卷积结果包括same mode。

          

      valid卷积:当filter全部在image里面的时候,进行卷积运算,可见filter的移动范围较same更小了,同样valid mode为same mode的子集。valid mode的卷积计算,填充边界中的像素值不会参与计算,即无效的填充边界不影响卷积,所以称为valid mode。

          

   python的scipy包中提供了convolve2d()函数来实现卷积运算,其参数如下:

from scipy import signal

signal.convolve2d(src,kernel,mode,boundary,fillvalue)

src: 输入的图像矩阵,只支持单通的(即二维矩阵)
kernel:卷积核
mode:卷积类型:full, same, valid
boundary:边界填充方式:fill,wrap, symm
fillvalue: 当boundary为fill时,边界填充的值,默认为0

  opencv中提供了flip()函数翻转卷积核,filter2D进行same 卷积, 其参数如下:

   dst = cv2.flip(src,flipCode)
src: 输入矩阵
flipCode:0表示沿着x轴翻转,1表示沿着y轴翻转,-1表示分别沿着x轴,y轴翻转
dst:输出矩阵(和src的shape一样) cv2.filter2D(src,dst,ddepth,kernel,anchor=(-,-),delta=,borderType=cv2.BORDER_DEFAULT)
src: 输入图像对象矩阵
dst:输出图像矩阵
ddepth:输出矩阵的数值类型
kernel:卷积核
anchor:卷积核锚点,默认(-,-)表示卷积核的中心位置
delat:卷积完后相加的常数
borderType:填充边界类型

2 图像平滑

  2.1 高斯平滑

    高斯平滑即采用高斯卷积核对图像矩阵进行卷积操作。高斯卷积核是一个近似服从高斯分布的矩阵,随着距离中心点的距离增加,其值变小。这样进行平滑处理时,图像矩阵中锚点处像素值权重大,边缘处像素值权重小,下为一个3*3的高斯卷积核:

    opencv中提供了GaussianBlur()函数来进行高斯平滑,其对应参数如下:

dst = cv2.GaussianBlur(src,ksize,sigmaX,sigmay,borderType)
src: 输入图像矩阵,可为单通道或多通道,多通道时分别对每个通道进行卷积
dst:输出图像矩阵,大小和数据类型都与src相同
ksize:高斯卷积核的大小,宽,高都为奇数,且可以不相同
sigmaX: 一维水平方向高斯卷积核的标准差
sigmaY: 一维垂直方向高斯卷积核的标准差,默认值为0,表示与sigmaX相同
borderType:填充边界类型

   代码使用示例和效果如下:(相比于原图,平滑后图片变模糊)

#coding:utf-

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np img = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg")
img_gauss = cv.GaussianBlur(img,(,),)
cv.imshow("img",img)
cv.imshow("img_gauss",img_gauss)
cv.waitKey()
cv.destroyAllWindows()

GaussianBlur()  

   对于上面的高斯卷积核,可以由如下两个矩阵相乘进行构建,说明高斯核是可分离卷积核,因此高斯卷积操作可以分成先进行垂直方向的一维卷积,再进行一维水平方向卷积。

          

    opencv中getGaussianKernel()能用来产生一维的高斯核,分别获得水平和垂直的高斯核,分两步也能完成高斯卷积,获得和GaussianBlur一样的结果。其参数如下:

cv2.getGaussianKernel(ksize,sigma,ktype)
ksize:奇数,一维核长度
sigma:标准差
ktype:数据格式,应该为CV_32F 或者 CV_64F 返回矩阵如下:垂直的矩阵
[[ 0.27406862]
 [ 0.45186276]
 [ 0.27406862]   
#coding:utf-

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
from scipy import signal #convolve2d只是对单通道进行卷积,若要实现cv.GaussianBlur()多通道高斯卷积,需要拆分三个通道进行,再合并 def gaussianBlur(img,h,w,sigma,boundary="fill",fillvalue=):
kernel_x = cv.getGaussianKernel(w,sigma,cv.CV_64F) #默认得到的为垂直矩阵 kernel_x = np.transpose(kernel_x) #转置操作,得到水平矩阵 #水平方向卷积
gaussian_x = signal.convolve2d(img,kernel_x,mode="same",boundary=boundary,fillvalue=fillvalue) #垂直方向卷积
kernel_y = cv.getGaussianKernel(h,sigma,cv.CV_64F)
gaussian_xy = signal.convolve2d(gaussian_x,kernel_y,mode="same",boundary=boundary,fillvalue=fillvalue) #cv.CV_64F数据转换为uint8
gaussian_xy = np.round(gaussian_xy)
gaussian_xy = gaussian_xy.astype(np.uint8) return gaussian_xy if __name__=="__main__":
img = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg",)
img_gauss = gaussianBlur(img,,,)
cv.imshow("img",img)
cv.imshow("img_gauss",img_gauss)
cv.waitKey()
cv.destroyAllWindows()

先水平卷积,再垂直卷积

        

   2.2 均值平滑

      高斯卷积核,对卷积框中像素值赋予不同权重,而均值平滑赋予相同权重,一个3*5的均值卷积核如下,均值卷积核也是可分离的。

                

    opencv的boxFilter()函数和blur()函数都能用来进行均值平滑,其参数如下:

  cv2.boxFilter(src,ddepth,ksize,dst,anchor,normalize,borderType)
src: 输入图像对象矩阵,
ddepth:数据格式,位深度
ksize:高斯卷积核的大小,格式为(宽,高)
dst:输出图像矩阵,大小和数据类型都与src相同
anchor:卷积核锚点,默认(-,-)表示卷积核的中心位置
normalize:是否归一化 (若卷积核3*,归一化卷积核需要除以15)
borderType:填充边界类型 cv2.blur(src,ksize,dst,anchor,borderType)
src: 输入图像对象矩阵,可以为单通道或多通道
ksize:高斯卷积核的大小,格式为(宽,高)
dst:输出图像矩阵,大小和数据类型都与src相同
anchor:卷积核锚点,默认(-,-)表示卷积核的中心位置
borderType:填充边界类型

    示例代码和使用效果如下:

#coding:utf-

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np img = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg")
img_blur = cv.blur(img,(,))
# img_blur = cv.boxFilter(img,-,(,))
cv.imshow("img",img)
cv.imshow("img_blur",img_blur)
cv.waitKey()
cv.destroyAllWindows()

blur()和boxFilter()

  

   2.3 中值平滑

      中值平滑也有核,但并不进行卷积计算,而是对核中所有像素值排序得到中间值,用该中间值来代替锚点值。opencv中利用medianBlur()来进行中值平滑,中值平滑特别适合用来去除椒盐噪声,其参数如下:

    cv2.medianBlur(src,ksize,dst)
src: 输入图像对象矩阵,可以为单通道或多通道
ksize:核的大小,格式为 #注意不是(,)
dst:输出图像矩阵,大小和数据类型都与src相同

    其使用代码及效果如下:(加上的白点噪声都被平滑掉了)

#coding:utf-

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import random img = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg")
rows,cols = img.shape[:] #加入椒盐噪声
for i in range():
r = random.randint(,rows-)
c = random.randint(,cols-)
img[r,c]= img_medianblur = cv.medianBlur(img,) cv.imshow("img",img)
cv.imshow("img_medianblur",img_medianblur)
cv.waitKey()
cv.destroyAllWindows()

medianBlur()

中值滤波自己实现代码如下:

#coding:utf-

import heapq
import cv2 #实现图片的中值滤波算法
# 2D median filter with no stride, zero padding def medain_filter(img, kernel_w, kernel_h):
"""
img: cv2 matrix
kernel_w: kernel width
kernel_h: kernel height
"""
rows, cols = img.shape[:]
for i in range(rows):
for j in range(cols):
left = max(, j-kernel_w//2)
right = min(cols-, j+kernel_w//2)
upper = max(, i-kernel_h//2)
lower = min(rows-, i+kernel_h//2) #kernel中的像素点放入堆中
hq = []
for m in range(upper, lower+):
for n in range(left, right+):
heapq.heappush(hq, img[m, n])
size = (right-left+)*(lower-upper+)
if size&: #奇数个元素,取中间值
for k in range(, (size+)//2):
median = heapq.heappop(hq)
img[i, j] = median
else: #偶数个元素,取中间两个元素平均值
for k in range(, (size+)//2):
median1 = heapq.heappop(hq)
median2 = heapq.heappop(hq)
img[i, j] = (median1+median2)//
return img if __name__ == "__main__":
img = cv2.imread(r"C:\Users\Administrator\Desktop\dog.jpg", )
img2 = medain_filter(img, , )
cv2.imshow("img", img)
cv2.imshow("img2", img2)
cv2.waitKey()
cv2.destroyAllWindows()

Medain Filter算法实现

   2.4 双边滤波

      相比于上面几种平滑算法,双边滤波在平滑的同时还能保持图像中物体的轮廓信息。双边滤波在高斯平滑的基础上引入了灰度值相似性权重因子,所以在构建其卷积核核时,要同时考虑空间距离权重和灰度值相似性权重。在进行卷积时,每个位置的邻域内,根据和锚点的距离d构建距离权重模板,根据和锚点灰度值差异r构建灰度值权重模板,结合两个模板生成该位置的卷积核。opencv中的bilateralFilter()函数实现了双边滤波,其参数对应如下:

dst = cv2.bilateralFilter(src,d,sigmaColor,sigmaSpace,borderType)
src: 输入图像对象矩阵,可以为单通道或多通道
d:用来计算卷积核的领域直径,如果d<=,从sigmaSpace计算d
sigmaColor:颜色空间滤波器标准偏差值,决定多少差值之内的像素会被计算(构建灰度值模板)
sigmaSpace:坐标空间中滤波器标准偏差值。如果d>,设置不起作用,否则根据它来计算d值(构建距离权重模板)

    其使用代码及效果如下:

#coding:utf-

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import random
import math img = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg")
img_bilateral = cv.bilateralFilter(img,,0.2,) cv.imshow("img",img)
cv.imshow("img_bilateral",img_bilateral)
cv.waitKey()
cv.destroyAllWindows()

bilateralFilter

    同样,利用numpy也可以自己实现双边滤波算法,同样需要对每个通道进行双边滤波,最后进行合并,下面代码只对单通道进行了双边滤波,代码和效果如下图:

#coding:utf-

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import random
import math def getDistanceWeight(sigmaSpace,H,W):
r,c = np.mgrid[:H:,:W:] r = r-(H-)/
c =c-(W-)/
distanceWeight = np.exp(-0.5*(np.power(r,)+np.power(c,))/math.pow(sigmaSpace,))
return distanceWeight def bilateralFilter(img,H,W,sigmaColor,sigmaSpace):
distanceWeight = getDistanceWeight(sigmaSpace,H,W)
cH = (H-)/
cW = (W-)/
rows,cols = img.shape[:]
bilateralImg = np.zeros((rows,cols),np.float32)
for r in range(rows):
for c in range(cols):
pixel = img[r,c]
rTop = if r-cH< else r-cH
rBottom = rows- if r+cH>rows- else r+cH
cLeft = if c-cW< else c-cW
cRight = cols- if c+cW>cols- else c+cW #权重模板作用区域
region=img[rTop:rBottom+,cLeft:cRight+] #灰度值差异权重
colorWeight = np.exp(0.5*np.power(region-pixel,2.0)/math.pow(sigmaColor,))
print(colorWeight.shape)
#距离权重
distanceWeightTemp = distanceWeight[cH-(r-rTop):rBottom-r+cH+,cW-(c-cLeft):cRight-c+cW+]
print(distanceWeightTemp.shape)
#权重相乘并归一化
weightTemp = colorWeight*distanceWeightTemp
weightTemp = weightTemp/np.sum(weightTemp)
bilateralImg[r][c]=np.sum(region*weightTemp)
return bilateralImg if __name__=="__main__":
img = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg",)
img_temp = img/255.0
img_bilateral = bilateralFilter(img_temp,,,0.2,)*
img_bilateral[img_bilateral>] =
img_bilateral = img_bilateral.astype(np.uint8)
cv.imshow("img",img)
cv.imshow("img_bilateral",img_bilateral)
cv.waitKey()
cv.destroyAllWindows()

python实现双边滤波

    2.5 联合双边滤波

      双边滤波是根据原图中不同位置灰度相似性来构建相似性权重模板,而联合滤波是先对原图进行高斯平滑,然后根据平滑后的图像灰度值差异建立相似性模板,再与距离权重模板相乘得到最终的卷积核,最后再对原图进行处理。所以相比于双边滤波,联合双边滤波只是建立灰度值相似性模板的方法不一样。

      联合双边滤波作为边缘保留滤波算法时,进行joint的图片即为自身原图片,如果将joint换为其他引导图片,联合双边滤波算法还可以用来实现其他功能。opencv 2中不支持联合双边滤波,opencv 3中除了主模块,还引入了contrib,其中的ximgproc模块包括了联合双边滤波的算法。因此如果需要使用opencv的联合双边滤波,需要安装opencv-contrib-python包。

安装opencv主模块和contrib附加模块步骤:
  pip uninstall opencv-python (如果已经安装opencv-python包,先卸载)
  pip install opencv-contrib-python

    联合双边滤波: cv2.xmingproc.jointBilateralFilter(), 其相关参数如下:

dst = cv2.xmingproc.jointBilateralFilter(joint,src,d,sigmaColor,sigmaSpace,borderType)
joint: 进行联合滤波的导向图像,可以为单通道或多通道,保持边缘的滤波算法时常采用src
src: 输入图像对象矩阵,可以为单通道或多通道
d:用来计算卷积核的领域直径,如果d<,从sigmaSpace计算d
sigmaColor:颜色空间滤波器标准偏差值,决定多少差值之内的像素会被计算(构建灰度值模板)
sigmaSpace:坐标空间中滤波器标准偏差值。如果d>,设置不起作用,否则根据它来计算d值(构建距离权重模板)

  下面是联合双边滤波的使用代码和效果:(采用src的高斯平滑图片作为joint)

#coding:utf-
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import random
import math src = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg")
joint = cv.GaussianBlur(src,(,),,)
dst = cv.ximgproc.jointBilateralFilter(joint,src,,,)
# dst = cv.ximgproc.jointBilateralFilter(src,src,,,) #采用src作为joint cv.imshow("img",src)
cv.imshow("joint",joint)
cv.imshow("dst",dst)
cv.waitKey()
cv.destroyAllWindows()

cv2.ximgproc.jointBilateralFilter()

    2.6 导向滤波

      导向滤波也是需要一张图片作为引导图片,来表明边缘,物体等信息,作为保持边缘滤波算法,可以采用自身作为导向图片。opencv 2中也暂不支持导向滤波, 同样在opencv-contrib-python包的ximgproc模块提供了导向滤波函。

      导向滤波具体原理可以参考:https://blog.csdn.net/baimafujinji/article/details/74750283

      opencv中导向滤波cv2.ximgproc.guidedFilter()的参数如下:

导向滤波
cv2.ximgproc.guidedFilter(guide,src,radius,eps,dDepth)
guide: 导向图片,单通道或三通道
src: 输入图像对象矩阵,可以为单通道或多通道
radius:用来计算卷积核的领域直径
eps:规范化参数, eps的平方类似于双边滤波中的sigmaColor(颜色空间滤波器标准偏差值)
(regularization term of Guided Filter. eps2 is similar to the sigma in the color space into bilateralFilter.)
dDepth: 输出图片的数据深度

      其代码使用和效果如下:

#coding:utf-
import cv2 as cv src = cv.imread(r"C:\Users\Administrator\Desktop\timg.jpg")
dst = cv.ximgproc.guidedFilter(src,src,,,-)
cv.imshow("img",src)
cv.imshow("dst",dst)
cv.waitKey()
cv.destroyAllWindows()

cv2.ximgproc.guidedFilter

  

(三)OpenCV-Python学习—图像平滑的更多相关文章

  1. (三)Python 学习第三天--GUI桌面项目

    (代码参考了别人的代码,只做学习用途!!!最近因为写论文,好久没有记录,好内疚...今天学习了一个小案例,做一下) 主要使用模块:tkinter 代码如下: from tkinter import * ...

  2. 三、python学习-常用模块

    一.常用模块 1.math数学模块 在计算机中,所有数值在计算机底层都是约等于机制,并不是精确地 import math #ceil() 向上取整操作 math.ceil(3.1)=>4 #fl ...

  3. OpenCV之Python学习笔记

    OpenCV之Python学习笔记 直都在用Python+OpenCV做一些算法的原型.本来想留下发布一些文章的,可是整理一下就有点无奈了,都是写零散不成系统的小片段.现在看 到一本国外的新书< ...

  4. python学习心得第三章

    python学习心得第三章 1.三元运算 变量=值1 if 条件 else 值2 由图如果条件成立则赋值1给变量,如果条件不成立则赋值2给变量. 2.数据类型 集合:set() class set(o ...

  5. python 学习(三)

    按照上次python 学习(二)的思路,第一步要实现从一个网站的页面上自动获取指定列表中的信息.折腾数日,得到一段可以正常运行的代码,如下: #web2.py import re import url ...

  6. Python 学习日记(第三周)

    知识回顾 在上一周的学习里,我学习了一些学习Python的基础知识下面先简短的回顾一些: 1Python的版本和和安装 Python的版本主要有2.x和3.x两个版本这两个版本在语法等方面有一定的区别 ...

  7. python学习笔记——第三章 串

    第三章 字符串学习 1.字符串不灵活, 它不能被分割符值 >>> format = "hello, %s. %s enough for ya?" >> ...

  8. python学习第三次记录

    python学习第三次记录 python中常用的数据类型: 整数(int) ,字符串(str),布尔值(bool),列表(list),元组(tuple),字典(dict),集合(set). int.数 ...

  9. Python学习 —— 阶段综合练习三

    Python学习 —— 阶段综合练习三 综合之前文件与文件夹操作的学习,做以下实例练习:(建议先不要看代码,自己先试着写:代码仅供参考,有多种实现方法) 1. 目录文件遍历(二层目录结构) 1).  ...

随机推荐

  1. Java 函数调用是传值还是传引用? 从字节码角度来看看!

    原文地址:点击打开

  2. Python语言程序设计:Lab6

    Reversing a List If you have time, you can try to write a function which will reverse a list recursi ...

  3. AD 复制状态检查

      微软提供了一下工具进行AD复制状态检查 Repadmin: http://technet.microsoft.com/en-us/library/cc811551%28v=ws.10%29.asp ...

  4. Song Form

    First of all, song form is an indepentent concept from the boxes, boxes simply describe the way the ...

  5. 为LPC1549 LPCXpresso评估板开发基于mbed的项目

    本文将主要介绍如何使用Visual Studio和VisualGDB为LPC1549 LPCXpresso开发板创建一个使用mbed框架的基础项目. LPC1549 LPCXpresso开发板载一个L ...

  6. 利其器:无法在 ".vscode" 文件夹()内创建 "launch.json" 文件。

    无法在 ".vscode" 文件夹()内创建 "launch.json" 文件. https://www.cnblogs.com/lidabo/p/588899 ...

  7. Oracle中split功能的实现

    原始需求: 有这样的表:tb和pk两列 PK是将表的多个主键用“|”进行分隔,我想把它变成多行 如 fundamentals_asia1_af_out ID_BB_GLOBAL|BC_DT|BC_EQ ...

  8. Docker那些事儿之镜像创建

    之前已经了解了docker的基本使用方式,简单的上手,也能让大部分人了解到这个技术的使用方法,今天继续说明docker如何构建自己所需要的镜像,开发人员掌握使用基础即可,有兴趣的可以自行深入研究 前言 ...

  9. java中的AIO

    AIO(异步非阻塞)AIO采用了Proactor模式,AIO与NIO的不同之处在于当AIO在进行读写操作时,不用先等通知,可直接调用相应的read/write方法,这两种方法均为异步的,对于读操作而言 ...

  10. @EnableCircuitBreaker熔断超时机制

    客户端请求服务端的时候总是报超时,默认熔断机制是1S