(三)OpenCV-Python学习—图像平滑
由于种种原因,图像中难免会存在噪声,需要对其去除。噪声可以理解为灰度值的随机变化,即拍照过程中引入的一些不想要的像素点。噪声可分为椒盐噪声,高斯噪声,加性噪声和乘性噪声等,参见: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学习—图像平滑的更多相关文章
- (三)Python 学习第三天--GUI桌面项目
(代码参考了别人的代码,只做学习用途!!!最近因为写论文,好久没有记录,好内疚...今天学习了一个小案例,做一下) 主要使用模块:tkinter 代码如下: from tkinter import * ...
- 三、python学习-常用模块
一.常用模块 1.math数学模块 在计算机中,所有数值在计算机底层都是约等于机制,并不是精确地 import math #ceil() 向上取整操作 math.ceil(3.1)=>4 #fl ...
- OpenCV之Python学习笔记
OpenCV之Python学习笔记 直都在用Python+OpenCV做一些算法的原型.本来想留下发布一些文章的,可是整理一下就有点无奈了,都是写零散不成系统的小片段.现在看 到一本国外的新书< ...
- python学习心得第三章
python学习心得第三章 1.三元运算 变量=值1 if 条件 else 值2 由图如果条件成立则赋值1给变量,如果条件不成立则赋值2给变量. 2.数据类型 集合:set() class set(o ...
- python 学习(三)
按照上次python 学习(二)的思路,第一步要实现从一个网站的页面上自动获取指定列表中的信息.折腾数日,得到一段可以正常运行的代码,如下: #web2.py import re import url ...
- Python 学习日记(第三周)
知识回顾 在上一周的学习里,我学习了一些学习Python的基础知识下面先简短的回顾一些: 1Python的版本和和安装 Python的版本主要有2.x和3.x两个版本这两个版本在语法等方面有一定的区别 ...
- python学习笔记——第三章 串
第三章 字符串学习 1.字符串不灵活, 它不能被分割符值 >>> format = "hello, %s. %s enough for ya?" >> ...
- python学习第三次记录
python学习第三次记录 python中常用的数据类型: 整数(int) ,字符串(str),布尔值(bool),列表(list),元组(tuple),字典(dict),集合(set). int.数 ...
- Python学习 —— 阶段综合练习三
Python学习 —— 阶段综合练习三 综合之前文件与文件夹操作的学习,做以下实例练习:(建议先不要看代码,自己先试着写:代码仅供参考,有多种实现方法) 1. 目录文件遍历(二层目录结构) 1). ...
随机推荐
- springboot 打包发布(war包)
版本关系: 软件名称 版本号 软件名称 版本号 spring boot 2.x jdk 1.8 tomcat 9.x springboot中的pom.xml文件 打包:右键点击项目,选择如下图: 填写 ...
- javascript之位置
1.offset()获取匹配元素在相对浏览器窗口的偏移量 返回一个对象,包括两个属性.left:相对浏览器窗口左边的距离.top:相对浏览器顶部的距离. $("#div1").o ...
- Entity Framework 在MySQL中执行SQL语句,关于参数问题
在Entity Framework中添加MySQL模型,在写代码的过程中需要直接执行SQL语句. 在SQL语句中用到了@curRank := 0 这样在SQL语句中定义参数,同时还会有传入参数:ai. ...
- Spring MVC 学习笔记(一)
• 1.SpringMVC概述 • 2.SpringMVC的HelloWorld • 3.使用@RequestMapping映射请求 • 4.映射请求参数&请求头 • 5.处理模型数据 • 6 ...
- C#当前运行所在目录集合
//获取当前进程的完整路径,包含文件名(进程名).string str = this.GetType().Assembly.Location;result: X:\xxx\xxx\xxx.exe (. ...
- django命令行安装和卸载
1. 在dos命令行中输入 pip 如下命令进行安装: 安装最新的版本的 Django 命令如下: pip install django 安装 指定版本的 Django 命令如下: pip insta ...
- WEB知识补充 支付宝 支付
- post提交主订单数据(gateway)实现httpapi
models.proto syntax = "proto3"; package services; import "google/protobuf/timestamp.p ...
- LeetCode按照解题方法分类题目
解题方法分类 1. 滑动窗口. 2. 双指针. 3. 快慢指针. 4. 区间合并. 5. 循环排序. 6. 原地反转链表. 7. 树上的BFS. 8. 树上的DFS. 9. 双堆. 10. 子集. 1 ...
- 学到了林海峰,武沛齐讲的Day23-完
10月11号生了儿子,很高心..不好的是孩子住院了,14号出院,晚上外公去世了,15号赶回老家.....20号回贵阳,21号回公司办事....我要坚定的学习下去...以前几乎是卡在这里就学不下去了.加 ...