1 图像滤波介绍
滤波是信号和图像处理中的基本任务之一,其旨在有选择地提取图像的某些特征,可以用于在给定应用程序的上下文中传达重要信息,例如,去除图像中的噪声、提取所需的视觉特征、图像重采样等。
1.1 图像滤波理论
图像滤波即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作。消除图像中的噪声又叫做图像滤波或平滑,滤波的目的有两个,一是突出特征以方便处理,二是抑制噪声。
空间域滤波就是在图像平面上对像素进行操作。空间域滤波大体分为两类:平滑、锐化。
-
平滑滤波:模糊处理,用于减小噪声,实际上是低通滤波,典型的滤波器是高斯滤波。
-
锐化滤波:提取边缘突出边缘及细节、弥补平滑滤波造成的边缘模糊。实际上是高通滤波。
空间域处理可由下式表示:
g(x,y)=T[f(x,y)]
式中,f(x,y)是输入图像,g(x,y)是处理后的图像,T是在点(x,y)的邻域上定义的关于f的一种算子,算子可应用于单幅图像或图像集合。
1.2 频域分析
频域分析 (frequency domain analysis
) :不同图像具有不同的灰度分布,可以用图像的灰度分布作为表征图像的一种方式,但同时,还存在另一种分析图像的观点。观察图像中的灰度变化,可以发现,某些图像包含强度几乎恒定的大面积区域(例如,蓝天),而某些图像上的灰度强度变化很快(例如,拥挤的街道)。因此,可以用图像中的变化频率作为表征图像的另一种方式,这种方式被称为频域,而通过观察图像的灰度分布来表征图像的方式则被称为空间域。
频域分析将图像从最低频率到最高频率分解为其频率内容,图像强度缓慢变化的区域仅包含低频,而高频是由强度的快速变化产生的。有多种用于计算图像的频率内容的变换,例如傅立叶变换或余弦变换。需要注意的是,由于图像是二维平面,因此频率由垂直频率(垂直方向的变化)和水平频率(水平方向的变化)组成。
在频域分析中,滤波器是一种放大图像某些频带同时减少其他频带的操作。因此,低通滤波器 (low-pass filters
) 是消除图像高频成分的滤波器,而高通滤波器 (high-pass filters
) 消除图像的低频成分。
1.3 邻域滤波算子
空间滤波器由一个邻域(通常是一个较小的矩形)和对该邻域所包围图像像素执行的预定义操作组成。对预定义的点(x,y)为中心的领域内的像素进行计算。
滤波产生一个新像素,用计算后的新像素值代替点(x,y)的值。
循环步骤1和2,滤波器的中心遍历图像中的每个像素后,就生成了滤波后的图像。
如果在图像像素上执行的是线性操作,则该滤波器称为线性空间滤波器,否则,称为非线性空间滤波器。
一般来说,使用大小为 m×n的滤波器对大小为 M×N的图像进行线性空间滤波,可由下式表示:
2 opencv图像滤波
2.1 opencv滤波概述
滤波处理分为两大类:线性滤波和非线性滤波。OpenCV里有这些滤波的函数,使用起来非常方便。
2.1.1 线性滤波
方框滤波BoxBlur:模糊图像
均值滤波Blur:模糊图像
高斯滤波GaussianBlur:信号的平滑处理,去除符合正太分布的噪声
平滑滤波:一般来说,图像具有局部连续的性质,即相邻的像素的值相近,而噪声使得噪点处产生像素跳跃,所以通过平滑噪点可以减少噪声,去除图像中的不相关细节。
方框滤波BoxBlur和均值滤波Blur都是对邻域内做平均值来滤波,属于平滑滤波。滤波的输出是包含在滤波器模板邻域内的像素的平均值,方框滤波做归一化之后就变为均值滤波,这两个滤波器都是低通滤波器。
高斯滤波:高斯滤波对于图像来说就是一个低通滤波,广泛用于消除高斯噪声,高速滤波就是一种加权滤波,只不过模板中的系数由高斯分布来确定的,高斯滤波器根据高斯函数的形状来选择滤波模板权值的线性平滑滤波器。高斯平滑滤波器对于抑制服从正态分布的噪声非常有效。
2.1.2 非线性滤波:
中值滤波mediaBlur:去除椒盐噪声
双边滤波BilateralFilter:保边去噪
中值滤波:中值滤波属于非线性滤波,其思想用滤波模板邻域内的像素的平均值来代替像素点的灰度值。中值滤波器是一种统计排序滤波器,图像上点(x,y),中值滤波以该点为中心,领域内所有像素的统计排序中值作为此点的响应,中值滤波是非线性滤波。相比与均值滤波和高斯滤波,中值滤波可以有效的降低随机噪声,直接忽略掉噪声点,把噪声引起的模糊降到最低。线性滤波器在滤波的同时会造成图像细节模糊,中值滤波可以避免这个问题,其典型的应用就是中值滤波消除斑点噪声、椒盐噪声。
双边滤波:高斯滤波属于加权平均滤波,距离中心点越近的点越有较大权重,这种方法符合图像的平滑变化的特征,但是在边缘区域,像素值出现突变,这种方法反而会滤掉边缘轮廓,损失掉有用的边缘信息。边缘保护滤波方法,双边滤波就是最常用的边缘保护滤波方法,就是为了处理这种情况而发明的。
双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。双边滤波将高斯滤波中通过各个点到中心点的空间临近度计算的各个权值进行优化,将其优化为空间临近度计算的权值和像素值相似度计算的权值的乘积,优化后的权值再与图像作卷积运算,从而达到保边去噪的效果。
2.2 opencv核心概念
-
低通滤波:低通滤波可以去除图像的噪音或平滑图像。
-
高通滤波:可以帮助查找图像的边缘。
-
噪音:即对一幅图像的产生负面效果,过暗或过亮的部分,一幅图像中,低于或高于某个像素点的值,都可以认为是噪音。
-
卷积核:即用来滤波的矩阵,卷积核一般为奇数,如3×3、5×5、7×7等;
-
锚点:卷积核最中间的坐标点。
-
卷积核越大,卷积的效果越好,但是计算量随之也会增大。
-
边界扩充:当卷积核大于1,并且不进行边界扩充,输出尺寸相应缩小、当卷积核一标准方式进行边界扩充,则输出的空间尺寸与输入相等。
-
图像卷积
3 opencv图像滤波实现
2.1 filter2D:图像卷积
函数原型:
dst = cv2.filter2D(src, ddepth, kernel, anchor, delta, borderType)
src:原图像
ddepth:输出图像的尺寸,默认为-1
kernel:卷积核(是一个矩阵)
anchor:锚点,默认随卷积核变化
delta:卷积后加一个值,默认为0
borderType:有映射类型,加一个黑边,默认不设置
示例代码:
import cv2
import numpy as np
# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
"""
:param scale: float 原图缩放的尺度
:param imglist: list 待显示的图像序列
:param order: list or tuple 显示顺序 行×列
:param border: int 图像间隔距离
:param border_color: tuple 间隔区域颜色
:return: 返回拼接好的numpy数组
"""
if order is None:
order = [1, len(imglist)]
allimgs = imglist.copy()
ws , hs = [], []
for i, img in enumerate(allimgs):
if np.ndim(img) == 2:
allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
ws.append(allimgs[i].shape[1])
hs.append(allimgs[i].shape[0])
w = max(ws)
h = max(hs)
# 将待显示图片拼接起来
sub = int(order[0] * order[1] - len(imglist))
# 判断输入的显示格式与待显示图像数量的大小关系
if sub > 0:
for s in range(sub):
allimgs.append(np.zeros_like(allimgs[0]))
elif sub < 0:
allimgs = allimgs[:sub]
imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
imgblank = imgblank.astype(np.uint8)
for i in range(order[0]):
for j in range(order[1]):
imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
return imgblank
original = cv2.imread('../data/girl03.jpeg')
# 创建一个5*5的卷积核
kernel = np.ones((5, 5), np.float32) / 25
filter2d = cv2.filter2D(original, -1, kernel)
img = show_multi_imgs(2, [original, filter2d], (1, 2))
cv2.namedWindow('origi&filter2D', 0)
cv2.imshow('origi&filter2D', img)
cv2.waitKey(0)
运行代码显示如下:
经过图像处理后,看着变模糊了,图像更平滑了
2.2 低通滤波boxFilter:方盒滤波
函数原型:
dst = cv2.boxFilter(src, ddepth, ksize , anchor, normalize, borderType)
src:输入图像
ddepth:输出图像的尺寸,默认为-1
kernel:卷积核大小(x, y)
anchor:锚点,默认随卷积核变化
normalize:布尔类型默认为True;True:a为1/W*H(均值滤波),false:a=1borderType:有映射类型,加一个黑边,默认不设置
示例代码:
import cv2
import numpy as np
# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
"""
:param scale: float 原图缩放的尺度
:param imglist: list 待显示的图像序列
:param order: list or tuple 显示顺序 行×列
:param border: int 图像间隔距离
:param border_color: tuple 间隔区域颜色
:return: 返回拼接好的numpy数组
"""
if order is None:
order = [1, len(imglist)]
allimgs = imglist.copy()
ws , hs = [], []
for i, img in enumerate(allimgs):
if np.ndim(img) == 2:
allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
ws.append(allimgs[i].shape[1])
hs.append(allimgs[i].shape[0])
w = max(ws)
h = max(hs)
# 将待显示图片拼接起来
sub = int(order[0] * order[1] - len(imglist))
# 判断输入的显示格式与待显示图像数量的大小关系
if sub > 0:
for s in range(sub):
allimgs.append(np.zeros_like(allimgs[0]))
elif sub < 0:
allimgs = allimgs[:sub]
imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
imgblank = imgblank.astype(np.uint8)
for i in range(order[0]):
for j in range(order[1]):
imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
return imgblank
img = cv2.imread('../data/girl03.jpeg')
# 方盒滤波(当为True时)变成均值滤波,当为False时,就只加和不变化,超过255的结果设置为255
img2 = cv2.boxFilter(img, -1, (5, 5), normalize=True)
img3 = cv2.boxFilter(img, -1, (5, 5), normalize=False)
new_image = show_multi_imgs(4, [img, img2, img3], (1, 3))
cv2.namedWindow('img&img2&img3', 0)
cv2.imshow('img&img2&img3', new_image)
cv2.waitKey(0)
运行代码显示如下:
blur():均值滤波
函数原型
方盒滤波的参数为True时,就是均值滤波,所以这个API用的不多。
dst = cv2.blur(scr, ksize, anchor, borderType)
scr:源图像
kernel:卷积核大小(x,y)
anchor:锚点
borderType:有映射类型,加一个黑边,默认不设置
2.3 低通滤波GaussianBlur:高斯滤波(去高斯噪音)
适用于有高斯噪点的图片,函数原型:
dst = cv2.GaussianBlur(img, ksize, sigmaX, sigmaY, …)
img:输入的图像
ksize:卷积核大小
sigmaX:表示高斯核函数在X方向的的标准偏差。
sigmaY:表示高斯核函数在Y方向的的标准偏差。重点关注前三个参数
示例代码:
import cv2
import numpy as np
# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
"""
:param scale: float 原图缩放的尺度
:param imglist: list 待显示的图像序列
:param order: list or tuple 显示顺序 行×列
:param border: int 图像间隔距离
:param border_color: tuple 间隔区域颜色
:return: 返回拼接好的numpy数组
"""
if order is None:
order = [1, len(imglist)]
allimgs = imglist.copy()
ws , hs = [], []
for i, img in enumerate(allimgs):
if np.ndim(img) == 2:
allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
ws.append(allimgs[i].shape[1])
hs.append(allimgs[i].shape[0])
w = max(ws)
h = max(hs)
# 将待显示图片拼接起来
sub = int(order[0] * order[1] - len(imglist))
# 判断输入的显示格式与待显示图像数量的大小关系
if sub > 0:
for s in range(sub):
allimgs.append(np.zeros_like(allimgs[0]))
elif sub < 0:
allimgs = allimgs[:sub]
imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
imgblank = imgblank.astype(np.uint8)
for i in range(order[0]):
for j in range(order[1]):
imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
return imgblank
img = cv2.imread('../data/gauss2.jpeg')
# 高斯去噪
img2 = cv2.GaussianBlur(img, (3, 3), 0)
new_image = show_multi_imgs(4, [img, img2], (1, 2))
cv2.namedWindow('img&img2', 0)
cv2.imshow('img&img2', new_image)
cv2.waitKey(0)
运行代码显示如下:
2.4 低通滤波medianBlur:中值滤波(去胡椒噪音)
函数原型:
对胡椒噪音去噪明显,取中间的值作为卷积结果
dst = cv2.medianBlur(img, ksize)
img:输入图像
ksize:卷积核大小一个数字
示例代码:
import cv2
import numpy as np
# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
"""
:param scale: float 原图缩放的尺度
:param imglist: list 待显示的图像序列
:param order: list or tuple 显示顺序 行×列
:param border: int 图像间隔距离
:param border_color: tuple 间隔区域颜色
:return: 返回拼接好的numpy数组
"""
if order is None:
order = [1, len(imglist)]
allimgs = imglist.copy()
ws , hs = [], []
for i, img in enumerate(allimgs):
if np.ndim(img) == 2:
allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
ws.append(allimgs[i].shape[1])
hs.append(allimgs[i].shape[0])
w = max(ws)
h = max(hs)
# 将待显示图片拼接起来
sub = int(order[0] * order[1] - len(imglist))
# 判断输入的显示格式与待显示图像数量的大小关系
if sub > 0:
for s in range(sub):
allimgs.append(np.zeros_like(allimgs[0]))
elif sub < 0:
allimgs = allimgs[:sub]
imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
imgblank = imgblank.astype(np.uint8)
for i in range(order[0]):
for j in range(order[1]):
imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
return imgblank
img = cv2.imread('../data/girl01.jpg')
# 高斯去噪
img2 = cv2.medianBlur(img, 5)
new_image = show_multi_imgs(4, [img, img2], (1, 2))
cv2.namedWindow('img&img2', 0)
cv2.imshow('img&img2', new_image)
cv2.waitKey(0)
运行代码显示如下:
2.5 低通滤波bilateralFilter:双边滤波
函数原型
双边滤波的主要应用场景是视频美颜
cv2.bilateralFilter(img, d, sigmaColor, sigmaSpace, …)
img:输入图像
d:直径,与卷积核中心点的距离,一般取5
sigmaColor:颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
sigmaSpace:sigmaSpace坐标空间中滤波器的sigma值,坐标空间的标注方差。他的数值越大,意味着越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。当d>0,d指定了邻域大小且与sigmaSpace无关。否则,d正比于sigmaSpace。
双边滤波的作用::图像去噪保边,对相关分析的结果有较大的影响,对于裂缝比较强,噪声比较少的图像来说,可以将去噪的程度放大,对以后的相关分析的结果就会有更少的噪声。对于噪声不是很集中的图像,并有较多细节的图像,增加保边的效果,让相关分析及后续进行进一步的结构处理,去噪。
示例代码:
import cv2
import numpy as np
# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
"""
:param scale: float 原图缩放的尺度
:param imglist: list 待显示的图像序列
:param order: list or tuple 显示顺序 行×列
:param border: int 图像间隔距离
:param border_color: tuple 间隔区域颜色
:return: 返回拼接好的numpy数组
"""
if order is None:
order = [1, len(imglist)]
allimgs = imglist.copy()
ws , hs = [], []
for i, img in enumerate(allimgs):
if np.ndim(img) == 2:
allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
ws.append(allimgs[i].shape[1])
hs.append(allimgs[i].shape[0])
w = max(ws)
h = max(hs)
# 将待显示图片拼接起来
sub = int(order[0] * order[1] - len(imglist))
# 判断输入的显示格式与待显示图像数量的大小关系
if sub > 0:
for s in range(sub):
allimgs.append(np.zeros_like(allimgs[0]))
elif sub < 0:
allimgs = allimgs[:sub]
imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
imgblank = imgblank.astype(np.uint8)
for i in range(order[0]):
for j in range(order[1]):
imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
return imgblank
img = cv2.imread('../data/biteral.jpg')
# 双边滤波
img2 = cv2.bilateralFilter(img, 5, 20, 50)
new_image = show_multi_imgs(4, [img, img2], (1, 2))
cv2.namedWindow('img&img2', 0)
cv2.imshow('img&img2', new_image)
cv2.waitKey(0)
运行代码显示如下:
2.6 高通滤波Sobel
只能一次在x方向上或者y方向上求导,然后把结果相加。
dst1 = cv2.Sobel(src, ddepth, dx, dy, ksize = 3, scale = 1, delta = 0, borderType = BORDER_DEFAULT )
src:输入原图像
ddepth:位深,默认为-1
dx,dy:只能选择一个方向上要么0、1,要么1、0
ksize:卷积核大小,默认为3,当-1时为沙尔
scale:缩放大小,一般就用默认值
delta:偏移量,一般就用默认值
borderType:边界扩充类型,一般就用默认值
示例代码:
import cv2
import numpy as np
# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
"""
:param scale: float 原图缩放的尺度
:param imglist: list 待显示的图像序列
:param order: list or tuple 显示顺序 行×列
:param border: int 图像间隔距离
:param border_color: tuple 间隔区域颜色
:return: 返回拼接好的numpy数组
"""
if order is None:
order = [1, len(imglist)]
allimgs = imglist.copy()
ws , hs = [], []
for i, img in enumerate(allimgs):
if np.ndim(img) == 2:
allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
ws.append(allimgs[i].shape[1])
hs.append(allimgs[i].shape[0])
w = max(ws)
h = max(hs)
# 将待显示图片拼接起来
sub = int(order[0] * order[1] - len(imglist))
# 判断输入的显示格式与待显示图像数量的大小关系
if sub > 0:
for s in range(sub):
allimgs.append(np.zeros_like(allimgs[0]))
elif sub < 0:
allimgs = allimgs[:sub]
imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
imgblank = imgblank.astype(np.uint8)
for i in range(order[0]):
for j in range(order[1]):
imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
return imgblank
img = cv2.imread('../data/biteral.jpg')
# Sobel
dx = cv2.Sobel(img, -1, 1, 0, ksize=3)
dy = cv2.Sobel(img, -1, 0, 1, ksize=3)
# dst = dx+dy
dst = cv2.add(dx, dy)
new_image = show_multi_imgs(4, [img, dx, dy, dst], (1, 4))
cv2.namedWindow('img1-4', 0)
cv2.imshow('img1-4', new_image)
cv2.waitKey(0)
运行代码显示如下:
一幅图的边缘被很好的分割出来。
2.7 高通滤波Scharr
与Sobel类似,只不过使用的ksize值不同,Scharr不能改变卷积核的大小,只能是3*3的。同样只能求一个方向上的边缘。
cv2.Scharr(src, ddepth, dx, dy, scale = 1, delta = 0, borderType = BORDER_DEFAULT).
src:输入原图像
ddepth:位深,默认为-1
dx,dy:只能选择一个方向上要么0、1,要么1、0
scale:缩放大小,一般就用默认值
delta:偏移量,一般就用默认值
borderType:边界扩充类型,一般就用默认值
示例代码:
import cv2
import numpy as np
# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
"""
:param scale: float 原图缩放的尺度
:param imglist: list 待显示的图像序列
:param order: list or tuple 显示顺序 行×列
:param border: int 图像间隔距离
:param border_color: tuple 间隔区域颜色
:return: 返回拼接好的numpy数组
"""
if order is None:
order = [1, len(imglist)]
allimgs = imglist.copy()
ws , hs = [], []
for i, img in enumerate(allimgs):
if np.ndim(img) == 2:
allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
ws.append(allimgs[i].shape[1])
hs.append(allimgs[i].shape[0])
w = max(ws)
h = max(hs)
# 将待显示图片拼接起来
sub = int(order[0] * order[1] - len(imglist))
# 判断输入的显示格式与待显示图像数量的大小关系
if sub > 0:
for s in range(sub):
allimgs.append(np.zeros_like(allimgs[0]))
elif sub < 0:
allimgs = allimgs[:sub]
imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
imgblank = imgblank.astype(np.uint8)
for i in range(order[0]):
for j in range(order[1]):
imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
return imgblank
img = cv2.imread('../data/biteral.jpg')
# Sobel,当ksize=-1时,就是Scharr
# dx = cv2.Sobel(img, -1, 1, 0, ksize=-1)
# dy = cv2.Sobel(img, -1, 0, 1, ksize=-1)
dx = cv2.Scharr(img, -1, 1, 0)
dy = cv2.Scharr(img, -1, 0, 1)
# dst = dx+dy
dst = cv2.add(dx,dy)
new_image = show_multi_imgs(4, [img, dx, dy, dst], (1, 4))
cv2.namedWindow('img1-4', 0)
cv2.imshow('img1-4', new_image)
cv2.waitKey(0)
运行代码显示如下:
2.8 高通滤波Laplacian(拉普拉斯)
Laplacian可以同时求两个方向上的边缘,但是对噪音比较敏感,一般需要先进行去噪再调用Laplacian。
dst = cv2.Laplacian(src, ddepth, ksize = 1 ,scale = 1, borderType = BORDER_DEFAULT)
src:输入原图像
ddepth:位深,默认为-1
ksize:卷积核大小,默认为1
scale:缩放大小,一般就用默认值
delta:偏移量,一般就用默认值
borderType:边界扩充类型,一般就用默认值
示例代码:
import cv2
import numpy as np
# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
"""
:param scale: float 原图缩放的尺度
:param imglist: list 待显示的图像序列
:param order: list or tuple 显示顺序 行×列
:param border: int 图像间隔距离
:param border_color: tuple 间隔区域颜色
:return: 返回拼接好的numpy数组
"""
if order is None:
order = [1, len(imglist)]
allimgs = imglist.copy()
ws , hs = [], []
for i, img in enumerate(allimgs):
if np.ndim(img) == 2:
allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
ws.append(allimgs[i].shape[1])
hs.append(allimgs[i].shape[0])
w = max(ws)
h = max(hs)
# 将待显示图片拼接起来
sub = int(order[0] * order[1] - len(imglist))
# 判断输入的显示格式与待显示图像数量的大小关系
if sub > 0:
for s in range(sub):
allimgs.append(np.zeros_like(allimgs[0]))
elif sub < 0:
allimgs = allimgs[:sub]
imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
imgblank = imgblank.astype(np.uint8)
for i in range(order[0]):
for j in range(order[1]):
imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
return imgblank
img = cv2.imread('../data/biteral.jpg')
dst = cv2.Laplacian(img, -1, ksize=5)
new_image = show_multi_imgs(4, [img, dst], (1, 2))
cv2.namedWindow('img&dst', 0)
cv2.imshow('img&dst', new_image)
cv2.waitKey(0)
卷积核大小为5*5的结果
运行代码结果显示:
2.8 高通滤波Canny
使用5*5高斯滤波消除噪声,可以计算图像的四个方向上的边缘(0,45,90,135),取局部的最大值,多了一个阈值计算。高于阈值我们认为是边缘,低于阈值就不是边缘,显然A为边缘,如果,但是B和C介于最大值最小值之间,BC既不是边缘也是边缘,但是C与A在一条直线上,所以C也是边缘。
dst = cv2.Canny(img, minVal, maxVal)
img:原图像
minVal:最小阈值
maxVal:最大阈值
低于最小阈值就不是边缘,高于最大阈值是边缘。
示例代码:
import cv2
import numpy as np
# 一个窗口显示多张图片
def show_multi_imgs(scale, imglist, order=None, border=10, border_color=(255, 255, 0)):
"""
:param scale: float 原图缩放的尺度
:param imglist: list 待显示的图像序列
:param order: list or tuple 显示顺序 行×列
:param border: int 图像间隔距离
:param border_color: tuple 间隔区域颜色
:return: 返回拼接好的numpy数组
"""
if order is None:
order = [1, len(imglist)]
allimgs = imglist.copy()
ws , hs = [], []
for i, img in enumerate(allimgs):
if np.ndim(img) == 2:
allimgs[i] = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
allimgs[i] = cv2.resize(img, dsize=(0, 0), fx=scale, fy=scale)
ws.append(allimgs[i].shape[1])
hs.append(allimgs[i].shape[0])
w = max(ws)
h = max(hs)
# 将待显示图片拼接起来
sub = int(order[0] * order[1] - len(imglist))
# 判断输入的显示格式与待显示图像数量的大小关系
if sub > 0:
for s in range(sub):
allimgs.append(np.zeros_like(allimgs[0]))
elif sub < 0:
allimgs = allimgs[:sub]
imgblank = np.zeros(((h+border) * order[0], (w+border) * order[1], 3)) + border_color
imgblank = imgblank.astype(np.uint8)
for i in range(order[0]):
for j in range(order[1]):
imgblank[(i * h + i*border):((i + 1) * h+i*border), (j * w + j*border):((j + 1) * w + j*border), :] = allimgs[i * order[1] + j]
return imgblank
img = cv2.imread('../data/biteral.jpg')
# canny
dst = cv2.Canny(img, 100, 200)
dst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)
new_image = show_multi_imgs(4, [img, dst], (1, 2))
cv2.namedWindow('img&dst', 0)
cv2.imshow('img&dst', new_image)
cv2.waitKey(0)
运行代码显示如下: