OpenCV 滤波与卷积之 —— 边界与阈值化

本文最后更新于:2022年7月26日 上午

本文摘录OpenCV 中的卷积、滤波相关操作内容,重点介绍 Opencv 操作中处理边界卷积与阈值化相关的操作。

预备知识

滤波、核和卷积

滤波器指的是一种由一幅图像(x,y)根据像素点x,y附近的区域计算得到一幅新图像’(x,y)的算法。其中,模板规定了滤波器的形状以及这个区域内像素的值的组成规律,也称“滤波器”或者核。本章中出现的滤波器多数为线性核,也就是说I"(x,y)的像素的值由(x,y)及其周围的像素的值加权相加得来。这个过程可以用下面的方程表示:
$$
I^{\prime}(x, y)=\sum_{i, j \in \text { kermel }} k_{i, j} \cdot I(x+i, y+j)
$$

锚点

图中的每个核都有一个点的值加粗显示,这个点就称作“核的锚点”。它定义了核与源图像的对齐关系。例如图(D)所示的核,数字$41$加粗显示,这意味着用于计算$I’(x,y)$的累加项中,一个项就是$41/273$与$I(x,y)$相乘的结果(同样,$I(x-1,y)$和
$I(x+1,y)$与$26/273$相乘的结果是另外两个项)。

边界外推和边界处理

在对图像进行卷积操作时需要处理边界,常用的方法是在卷积真正像素时向外扩展出虚拟数据,之后再进行卷积。在卷积函数的处理过程中为源图像添加虚拟像素是非常必要的。那么,如何对缺少相邻像素点的边缘像素点计算出一个有效的结果?实际上,在没有公认方法的情况下,我们一般通过自定义的方式在某一场景中处理问题。

1. cv2.copyMakeBorder()

  • 一个为图像创建边框的函数,通过指定两幅图像,第一幅是源图像,第二幅是扩充之后的图像,同时指明填充方法,这个函数就会将第一幅图像填补后的结果保存在第二幅图像中。
  • 函数使用
1
2
3
4
5
6
7
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的别名

  • 代码示例
1
2
3
4
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()

计算扩充的像素对应原图哪个坐标的像素

1
cv2.borderInterpolate(10, 1000, cv2.BORDER_REFLECT)

阈值化操作

图像处理过程中经常会遇见这种情况:我们已经完成了多层处理步骤并需要做出一个最终决定,或者将高于或低于某一值的像素置零同时其他的像素保持不变。OpenCV中的函数cv2.threshold()实现了这些功能

其原理是对于数组中每个值,根据其高于或低于这个阈值做出相应的处理,给定一个数组和阈值。根据个人喜好,也可以把阈值化操作理解成一个用 $1×1$ 的核进行卷积,对每个像素进行一次非线性操作。

1. cv2.threshold()

  • 函数使用
1
2
3
4
5
6
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 算法选择阈值

  • 示例代码
1
2
3
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
1
2
3
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(),实现了这种方法

1
2
3
4
5
6
7
8
9
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 T(x,y) = mean(blockSize×blockSize neighborhood of (x,y)) - C
      cv2.ADAPTIVE_THRESH_GAUSSIAN_C T(x,y) = GaussianMean(blockSize×blockSize neighborhood of (x,y)) - C
  • thresholdType

    • THRESH_BINARY
    $$ \operatorname{dst}(x, y)=\left\{\begin{array}{ll}\operatorname{maxValue} & \text { if } \operatorname{src}(x, y)>T(x, y) \\ 0 & \text { otherwise }\end{array}\right. $$
    • THRESH_BINARY_INV
    $$ \operatorname{dst}(x, y)=\left\{\begin{array}{ll}0 & \text { if } \operatorname{src}(x, y)>T(x, y) \\ \operatorname{maxValue} & \text { otherwise }\end{array}\right. $$
  • 相对于一般的阈值化操作,当图像中出现较大的明暗差异时,自适应阈值时非常有效的。这个函数仅处理单通道8位或浮点型图像。

  • 示例代码

1
2
3
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)

示例源码

参考资料

  • 《学习 OpenCV3》 第十章

OpenCV 滤波与卷积之 —— 边界与阈值化
https://www.zywvvd.com/notes/study/image-processing/opencv/opencv-filter-conv/filter-conv/
作者
Yiwei Zhang
发布于
2022年3月12日
许可协议