本文摘录OpenCV 中的卷积、滤波相关操作内容,重点介绍 Opencv 操作中处理边界卷积与阈值化相关的操作。
预备知识
滤波、核和卷积
滤波器指的是一种由一幅图像(x,y)根据像素点x,y附近的区域计算得到一幅新图像’(x,y)的算法。其中,模板规定了滤波器的形状以及这个区域内像素的值的组成规律,也称“滤波器”或者核。本章中出现的滤波器多数为线性核,也就是说I"(x,y)的像素的值由(x,y)及其周围的像素的值加权相加得来。这个过程可以用下面的方程表示:
锚点
图中的每个核都有一个点的值加粗显示,这个点就称作“核的锚点”。它定义了核与源图像的对齐关系。例如图(D)所示的核,数字41加粗显示,这意味着用于计算I’(x,y)的累加项中,一个项就是41/273与I(x,y)相乘的结果(同样,I(x-1,y)和 I(x 1,y)与26/273相乘的结果是另外两个项)。
边界外推和边界处理
在对图像进行卷积操作时需要处理边界,常用的方法是在卷积真正像素时向外扩展出虚拟数据,之后再进行卷积。在卷积函数的处理过程中为源图像添加虚拟像素是非常必要的。那么,如何对缺少相邻像素点的边缘像素点计算出一个有效的结果?实际上,在没有公认方法的情况下,我们一般通过自定义的方式在某一场景中处理问题。
1. cv2.copyMakeBorder()
- 一个为图像创建边框的函数,通过指定两幅图像,第一幅是源图像,第二幅是扩充之后的图像,同时指明填充方法,这个函数就会将第一幅图像填补后的结果保存在第二幅图像中。
- 函数使用
cv2.copyMakeBorder( src, # 输入图像
top, # 上方 padding 像素数
bottom, # 下方 padding 像素数
left, # 左侧 padding 像素数
right, # 右侧 padding 像素数
borderType, # 像素拓展方法
value) # 如果为固定值拓展,需要设置该参数
- borderType 参数说明
borderType | 含义 |
---|---|
cv2.BORDER_CONSTANT | 复制指定的常量扩展边界 |
cv2.BORDER_WRAP | 复制对边的像素扩展边界 |
cv2.BORDER_REPLICATE | 复制边缘的像素扩展边界 |
cv2.BORDER_REFLECT | 通过镜像复制扩展边界 |
cv2.BORDER_REFLECT_101 | 通过镜像复制扩展边界,边界像素除外 |
cv2.BORDER_DEFAULT | cv2.B0_RDER_REFLECT101的别名 |
- 代码示例
image = mt.cv_rgb_imread('img1.jpg')
res = cv2.copyMakeBorder(image, 1, 1, 1, 1, borderType=cv2.BORDER_CONSTANT, value=(255, 255, 0))
res = cv2.copyMakeBorder(res, 180, 180, 180, 180, borderType=cv2.BORDER_REFLECT)
PIS(res)
2. cv2.borderInterpolate()
代码语言:javascript复制计算扩充的像素对应原图哪个坐标的像素
cv2.borderInterpolate(10, 1000, cv2.BORDER_REFLECT)
阈值化操作
图像处理过程中经常会遇见这种情况:我们已经完成了多层处理步骤并需要做出一个最终决定,或者将高于或低于某一值的像素置零同时其他的像素保持不变。OpenCV中的函数cv2.threshold()
实现了这些功能
其原理是对于数组中每个值,根据其高于或低于这个阈值做出相应的处理,给定一个数组和阈值。根据个人喜好,也可以把阈值化操作理解成一个用 1×1 的核进行卷积,对每个像素进行一次非线性操作。
1. cv2.threshold()
- 函数使用
cv2.threshold(
src, # 输入图像
thresh, # 阈值
maxValue, # 超过阈值数据转换的最大值
thresholdType # 阈值化方法
)
thresholdType
阈值类型 | 操作 |
---|---|
cv2.THRESH_BINARY | DST = (SRC > thresh) ? MAXVALUE : 0 |
cv2.THRESH_BINARY_INV | DST = (SRC > thresh) ? 0 : MAXVALUE |
cv2.THRESH_TRUNC | DST = (SRC > thresh) ? THRESH : SRC |
cv2.THRESH_TOZERO | DST = (SRC > thresh) ? SRC : 0 |
cv2.THRESH_TOZERO_INV | DST = (SRC > thresh) ? 0 : SRC |
cv2.THRESH_OTSU | Otsu 算法选择阈值 |
- 示例代码
img = mt.cv_rgb_imread('img1.jpg')
res = cv2.threshold(img, 120, 200, cv2.THRESH_BINARY)
PIS(res[1])
- 函数
cv2.threshold()
也可以自动决定最优的阈值,你只需对参数thresholdType
传递值cv2.THRESH_OTSU
即可。 - Otsu 算法的思路为遍历所有可能的阈值,选择加权方差最大的那一个作为结果,具体参考 Otsu
img = mt.cv_rgb_imread('img1.jpg')
res = cv2.threshold(img[:,:,0], 0, 255, cv2.THRESH_OTSU)
PIS(res[1])
2. cv2.adaptiveThreshold()
有一种与之前不同的阈值化方法,这种方法中阈值在整个过程中自动产生变化。在OpenCV中,函数cv2.adaptiveThreshold(),实现了这种方法
- 官方文档:https://docs.opencv.org/4.5.5/d7/d1b/group__imgproc__misc.html#ga72b913f352e4a1b1b397736707afcde3
- 函数使用
cv.adaptiveThreshold(
src, # 输入图像
maxValue, # 分配给满足条件的像素的非零值
adaptiveMethod, # 要使用的自适应阈值算法
# 用 BORDER_REPLICATE | BORDER_ISOLATED 来处理边界。
thresholdType, # 阈值类型必须是 THRESH_BINARY 或 THRESH_BINARY_INV
blockSize, # 用于计算像素阈值的像素邻域的大小, 3,5,7 等
C[, dst] # 用于减去的常数
) -> dst
- adaptiveMethod cv2.adaptiveThreshold()根据adaptiveMethod的设置,允许两种不同的自适应阈值方法。两种方法都是逐个像素地计算自适应阈值T(x,y),方法是通过计算每个像素位置周围的b×b区域的加权平均值然后减去常数C,其中b由blockSize给定。 不同的是,如果选择的均值方法是cv2.ADAPTIVE_THRESH_MEAN_C,那么均值时取得权值是相等的;如果选择的均值方法是cv2.ADAPTIVE_THRESH_GAUSSIAN_C,(x,y) 周围的像素的权值则根据其到中心点的距离通过高斯方程得到。
- 不同的是,如果选择的均值方法是
cv2.ADAPTIVE_THRESH_MEAN_C
,那么均值时取得权值是相等的;如果选择的均值方法是cv2.ADAPTIVE_THRESH_GAUSSIAN_C
,(x,y) 周围的像素的权值则根据其到中心点的距离通过高斯方程得到。
thresholdType
- 相对于一般的阈值化操作,当图像中出现较大的明暗差异时,自适应阈值时非常有效的。这个函数仅处理单通道8位或浮点型图像。
- 示例代码
img = mt.cv_rgb_imread('img1.jpg', gray=True)
res = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 5, 0)
PIS(res)
示例源码
- https://github.com/zywvvd/Python_Practise
参考资料
- 《学习 OpenCV3》 第十章