文章目录
- 一、ROI与泛洪填充
- 1. ROI操作
- 2. 彩色图像和二值图像的泛洪填充
- 二、模糊操作
- 1. 均值模糊
- 2. 中值模糊
- 3. 自定义模糊
- 三、高斯模糊
- 四、边缘保留滤波EPF
- 1. 高斯双边滤波
- 2. 均值迁移滤波
一、ROI与泛洪填充
1. ROI操作
ROI(Region Of Interest),感兴趣区域,从被处理的图像以方框、圆、椭圆、不规则多边形等方式勾勒出需要处理的区域,称为感兴趣区域,经常用来连接图像。
import cv2 as cv
src = cv.imread(r'./test/004.jpg')
cv.imshow('src', src)
# 获取RIO区域
sample = src[30:390, 80:400]
# 变为灰度图像
gray = cv.cvtColor(sample, cv.COLOR_BGR2GRAY)
# 还原回RGB三通道的
back_sample = cv.cvtColor(gray, cv.COLOR_GRAY2BGR)
# 变回去
src[30:390, 80:400] = back_sample
cv.imshow('sample', src)
cv.waitKey(0)
运行效果如下:
2. 彩色图像和二值图像的泛洪填充
泛洪填充:将指定颜色从指定位置开始填充一个连通区域,此时的连通性由像素值的接近程度来衡量。
import cv2 as cv
import numpy as np
# 彩色图像
def fill_color(image):
copy_image = image.copy()
height, width = image.shape[:2]
# 造一个mask 单通道的 np.uint8
mask = np.zeros([height+2, width+2], np.uint8)
# 泛洪填充 floodFill(image, mask, seedPoint, newVal, loDiff=None, upDiff=None, flags=None)
cv.floodFill(copy_image, mask, (30, 30), (0, 255, 255), (100, 100, 100), (50, 50, 50), cv.FLOODFILL_FIXED_RANGE)
cv.imshow('fill_color', copy_image)
src = cv.imread(r'./test/010.png')
fill_color(src)
cv.imshow('src', src)
cv.waitKey(0)
运行效果如下:
import cv2 as cv
import numpy as np
# 二值图像
def fill_binary():
# 造一个二值图像
image = np.zeros([400, 400, 3], np.uint8)
image[100:300, 100:300, :] = 255
cv.imshow('binary_image', image)
height, width = image.shape[:2]
print(height, width)
# 造mask
mask = np.ones([height+2, width+2], np.uint8)
mask[101:301, 101:301] = 0
# 泛洪填充
cv.floodFill(image, mask, (200, 200), (0, 0, 255), cv.FLOODFILL_MASK_ONLY)
cv.imshow('filled binary', image)
fill_binary()
cv.waitKey(0)
运行效果如下:
opencv里的mask为uin8类型的单通道阵列
泛洪填充算法也叫漫水填充算法:floodFill(image, mask, seedPoint, newVal, loDiff=None, upDiff=None, flags=None)
- image参数表示输入/输出1或3通道,8位或浮点图像。
- mask参数表示掩码,该掩码是单通道8位图像,比image的高度多2个像素,宽度多2个像素,填充时不能穿过输入掩码中的非零像素。
- seedPoint参数表示泛洪算法的起始点
- newVal参数表示在重绘区域像素的新值
- loDiff参数表示当前观察像素值与其部件邻域像素值或待加入该组件的种子像素之间的亮度或颜色之负差的最大值
- upDiff参数表示当前观察像素值与其部件邻域像素值或待加入该组件的种子像素之间的亮度或颜色之正差的最大值
个人理解是,参数seedPoint起始点的像素值减去参数loDiff的像素值表示的是从起始点开始搜索周边范围的像素最低值,参数seedPoint起始点的像素值加上参数upDiff的像素值表示的是从起始点开始搜索周边范围的像素最大值。有了这个范围,然后该函数就可以在这个连续像素范围内填充指定的颜色newVal参数值。
二、模糊操作
模糊操作的基本原理:
- 基于离散卷积,定义好每个卷积核。
- 不同卷积核得到不同的卷积效果,模糊是卷积的一种表象。
1. 均值模糊
import cv2 as cv
# 均值模糊 卷积原理 C++积分图运算 速度非常快
# cv2.blur(src, ksize, dst=None, anchor=None, borderType=None)
# 对随机噪声有很好的去噪效果
def mean_ambiguity(image):
# 水平方向
dst1 = cv.blur(image, (20, 1))
# 垂直方向
dst2 = cv.blur(image, (1, 20))
dst3 = cv.blur(image, (10, 10))
cv.imshow('dst1', dst1)
cv.imshow('dst2', dst2)
cv.imshow('dst3', dst3)
src = cv.imread(r'.\test\004.jpg')
# 缩小图片尺寸 便于展示
src = cv.resize(src, None, fx=0.5, fy=0.5)
mean_ambiguity(src)
cv.imshow('src', src)
cv.waitKey(0)
运行效果如下:
2. 中值模糊
import cv2 as cv
# cv2.medianBlur(src, ksize, dst=None)
def median_ambiguity(image):
# 中值模糊 对椒盐噪声有很好的去噪效果
dst = cv.medianBlur(image, 5)
cv.imshow('dst', dst)
src = cv.imread(r'.\test\012.png')
cv.imshow('src', src)
median_ambiguity(src)
cv.waitKey(0)
3. 自定义模糊
import cv2 as cv
import numpy as np
def custom_blur_demo(image):
# 自定义filter
kernel = np.ones([8, 8], np.float32) / 64
dst = cv.filter2D(image, ddepth=-1, kernel=kernel)
cv.imshow('custom_blur_demo', dst)
src = cv.imread(r'.\test\012.png')
cv.imshow('src', src)
custom_blur_demo(src)
cv.waitKey(0)
运行效果如下:
import cv2 as cv
import numpy as np
def custom_blur_demo(image):
# 自定义filter 卷积还可实现锐化 锐化算子 定义要符合原则 增强图像 更有立体感
kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32)
dst = cv.filter2D(image, ddepth=-1, kernel=kernel)
cv.imshow('custom_blur_demo', dst)
src = cv.imread(r'.\test\010.png')
cv.imshow('src', src)
custom_blur_demo(src)
cv.waitKey(0)
运行效果如下:
三、高斯模糊
高斯模糊本质上是低通滤波器,输出图像的每个像素点是原图像上对应像素点与周围像素点的加权和,原理并不复杂。利用高斯分布权值矩阵与原始图像矩阵做卷积运算,由于高斯分布的傅里叶变换仍然是高斯分布,使用高斯模糊就减少了图像的高频分量,因此高斯模糊是低通滤波器,数学上讲,对图像做高斯模糊等相当于将图像与高斯函数卷积。至于高斯分布权重矩阵,就是对二维正态分布的密度函数(也就是高斯函数)采样再做归一化的产物。
使用python与opencv实现高斯模糊,只需调用GaussianBlur函数,给出高斯矩阵的尺寸和标准差就可以。
cv2.GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None)
import cv2 as cv
import numpy as np
def clamp(pv):
if pv > 255:
return 255
elif pv < 0:
return 0
else:
return pv
# 加上高斯噪声
def gaussion_noise(image):
h, w, c = image.shape # 解构
for row in range(h):
for col in range(w):
# np.random.normal(loc=0.0, scale=1.0, size=None)
# 产生正态分布的随机数
# loc:float 此概率分布的均值 scale:float 此概率分布的标准差
# size:int or tuple of ints 输出的shape,默认为None,只输出一个值
s = np.random.normal(loc=1.0, scale=9.0, size=3)
b = image[row, col, 0] # blue
g = image[row, col, 1] # green
r = image[row, col, 2] # red
# 加上随机数后 有些像素点可能会超界
image[row, col, 0] = clamp(b + s[0])
image[row, col, 1] = clamp(g + s[1])
image[row, col, 2] = clamp(r + s[2])
cv.imshow('Gaussian noise', image)
return image
src = cv.imread(r'./test/004.jpg')
src = cv.resize(src, None, fx=0.5, fy=0.5)
cv.imshow('src', src)
# 高斯模糊 保留像素的主要特征
# 高斯模糊对这种高斯噪声 抑制效果好
dst = cv.GaussianBlur(gaussion_noise(src), (5, 5), 0)
cv.imshow('Gaussian blur', dst)
t1 = cv.getTickCount()
gaussion_noise(src)
t2 = cv.getTickCount()
time = (t2 - t1) / cv.getTickFrequency()
print(f'用时:{time}s')
cv.waitKey(0)
运行效果如下:
四、边缘保留滤波EPF
1. 高斯双边滤波
前面所用的高斯模糊只考虑了像素空间的分布,而没有考虑差异问题。高斯滤波在滤波时会将图像中各个颜色区域的边缘同区域本身一同模糊掉,而高斯双边滤波则是对各个区域的交界边缘有所保留
python与opencv实现高斯双边滤波,可以调用bilateralFilter这个API。
cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace, dst=None, borderType=None)
- src:输入图像。
- d:在过滤期间使用的每个像素邻域的直径。如果输入d非0,则sigmaSpace由d计算得出,如果sigmaColor没输入,则sigmaColor由sigmaSpace计算得出。
- sigmaColor:色彩空间的标准方差,一般尽可能大。较大的参数值意味着像素邻域内较远的颜色会混合在一起,从而产生更大面积的半相等颜色。-
- sigmaSpace::坐标空间的标准方差(像素单位),一般尽可能小。参数值越大意味着只要它们的颜色足够接近,越远的像素都会相互影响。当d > 0时,它指定邻域大小而不考虑sigmaSpace。 否则,d与sigmaSpace成正比。
import cv2 as cv
def bi_filter(image):
# 高斯双边滤波
dst = cv.bilateralFilter(image, d=0, sigmaColor=100, sigmaSpace=15)
cv.imshow('bi_filter', dst)
src = cv.imread(r'./test/009.png')
cv.imshow('src', src)
bi_filter(src)
cv.waitKey(0)
运行效果如下:
可以非常清楚地看见图像中的脸上的水大部分被滤掉了,皮肤的效果最为明显,就像换了一张皮,并且脸的轮廓都被保留了下来,而没有像均值模糊、中值模糊或者高斯模糊那样得到的图像有些模糊。
2. 均值迁移滤波
均值迁移模糊是图像边缘保留滤波算法中的一种,经常用在对图像进行分水岭分割之前去噪声,可以大幅度提升分水岭分割的效果。均值迁移模糊的主要思想如下: 就是在图像进行开窗的时候,考虑像素值空间范围分布,只有符合分布的像素点才参与计算,计算得到像素均值与空间位置均值,使用新的均值位置作为窗口中心位置继续基于给定像素值空间分布计算均值与均值位置,如此不断迁移中心位置直到不再变化位置(dx=dy=0),但是在实际情况中我们会人为设置一个停止条件比如迁移几次,这样就可以把最后的RGB均值赋值给中心位置。
在n维空间中,有一定数量的样本,我们选定其中的一个样本,以该样本为中心,给长度为半径画一个圆,求取该圆形区域内样本的质心,即密度最大的点,再以该点为中心继续执行上述迭代过程,直至最终收敛。
python与opencv实现均值迁移滤波,可以调用pyrMeanShiftFiltering这个API。
cv2.pyrMeanShiftFiltering(src, sp, sr, dst=None, maxLevel=None, termcrit=None)
- src:输入图像
- sp:定义迁移物理空间的半径大小
- sr:定义迁移色彩空间的半径大小
- maxLeval:表示金字塔的最大层数
- termcrit:表示漂移迭代终止条件
以上参数中最后两个很少用到,主要是sp和sr两个参数,二者设置的值越大,对图像色彩的平滑效果越明显,同时函数耗时越多。
import cv2 as cv
def pyr_meanshift_filter(image):
# 均值偏移
dst = cv.pyrMeanShiftFiltering(image, sp=15, sr=60)
cv.imshow('pyMS', dst)
src = cv.imread(r'./test/009.png')
cv.imshow('src', src)
pyr_meanshift_filter(src)
cv.waitKey(0)
运行效果如下:
与高斯双边滤波的效果比较来看,均值迁移模糊的效果给人一种磨皮过度的感觉,滤波后的图像就像油画那样。