OpenCV - Advanced


OpenCV_Advanced

or "CV with OpenCV"

 

 

1. Image Masking

Image Masking,图像掩模,用于提取ROI(Region of Interest)或屏蔽某些区域

例如要提取图中蓝色的部分,下图中,三个窗口从左到右依次是:原图,生成的mask,应用了mask的原图

image masking explained

 

1.1 制造Mask

  • cv2.inRange() 抓取色彩ROI

    该函数可以指定某种颜色的最低数值与最高数值,然后抓取该色彩范围内所有的像素生成新的图片(即所谓的Mask)

  • 手 搓~屏蔽特定区域

    即直接照着原图的尺寸和数据类型用np.zeros()摁造一个mask

  • WARNING

    这俩mask虽然你用mask.shape()一查,诶嘿,一模一样!

    但是它们实际上不一样

    cv2.inRange()产生的mask,打印出来格式是这样的:

    np.zeros()产生的mask,打印出来格式大致是这样的:

    只有np.zeros()通过复刻原图格式做出来的mask,才真正做到了与原图一模一样!!!

    就是这点区别导致这两种mask在使用的时候方法也不一样

 

1.2 施加Mask

cv2.bitwise_and() - 对两个数组进行位与运算,可以增加图像掩模进行masking

在2.1.1中介绍了两种mask在施加时要遵循不同的方法,以下便是示例:

当然,想想都知道,除了cv2.bitwise_and()还有些其他的位运算函数:

  • cv2.bitwise_or()

  • cv2.bitwise_not()

  • cv2.bitwise_xor()

 

 

2. Image Thresholding

Image Thresholding,图像阈值分割,经典的图像分割方法;常见的做法是在图像的灰度图上,通过划定一个或几个不同的灰度阈值来把属于同一个灰度级的像素归类为同一个物体。下图是阈值分割的一种(二值化)

thresholding example

2.1 固定阈值分割

设置一个色彩的阈值threshold,pixel值大于threshold的变成一类值,pixel值小于threshold的变成另一类值

不完全是“二值化”,而是“二类值化”

cv2.threshold()最基本的thresholding的实现方式

  • Threshold Types 阈值分割类型

    threshold demo

    阈值分割类型里还有些其他的不属于固定阈值分割的,比如大津算法自适应阈值cv.THRESH_OTSU,参考Otsu Method部分

    以下是5种不同Threshold Type的波形图与数学解释,附带与原图的对比

    threshold type math interpret

    所谓的maxval “填充色”,就是由阈值分割类型决定用法的指定色彩

    threshold types

 

2.2 自适应阈值分割

自适应阈值分割的核心是将图片分割为不同的“小区域”,每个区域都计算阈值,这样可以更好地处理复杂的图像:比如固定阈值分割就无法处理明暗分布不均的图像

cv2.adaptiveThreshold() - 自适应阈值分割函数

关于一些参数的解释:

  • adapt_method 自适应方法有两种

    • cv2.ADAPTIVE_THRESH_MEAN_C - 小区域内取均值

    • cv2.ADAPTIVE_THRESH_GAUSSIAN_C - 小区域内取高斯加权和

    一般推荐用高斯加权和

  • thresh_type 阈值分割类型只能设置为cv2.THRESH_BINARYcv2.THRESH_BINARY_INV两种

 

2.3 Otsu桑のAlgorithm

Otsu Algorithm是一种通过最大化类间方差求得灰度图的自适应阈值的阈值分割算法,提出者为大津展之

大津算法认定一张图由“前景色”“背景色”组成(需要分离的内容,即前文提到的“类”),下图是一种符合该描述的极端案例,如果在gray-scale space中分析pixel的分布(以灰度值为x-axis,pixel数量为y-axis),我们会发现这些图片都是所谓的“双峰图”

two-peak image

  • Pros:求全局阈值的最佳算法;速度快且不受亮度和对比度影响

  • Cons

    • 对图像噪点相当敏感(过滤下再用)

    • 前景与背景大小比例差距悬殊(会导致双峰不明显)时效果欠佳

    • 只能分割单一目标(双峰上手,多峰苦手)

global V.S. Otsu V.S. filtered Otsu

  • 用法

    直接在一般阈值分割函数cv2.threshold()的阈值分割类型选cv.THRESH_OTSU就好

  • 算法推导与解释

    我们以uint8图片为基础,pixel的灰度值取值范围为[0, 255]

    有阈值为 t 时,将图像中所有的pixel分类为Class_0(<t)与Class_1(>t),则可用 ω0,ω1 表示Class_0和Class_1的pixels分别在全图中出现的概率,或所有pixel中的占比:

    (a)ω0=0tpi=N0Nt
    (b)ω1=t+1255pi=N1Nt

     

    pi 代表灰度值为 i 的pixel在全图中出现的概率(或所有pixel中的占比)

    N0,N1,Nt 分别代表Class_0,Class_1和全图中pixel的数量

    那么Class_0,Class_1和全图的平均灰度值 μ0,μ1,μ 分别为:

    (c)μ0=0tipiω0
    (d)μ1=t+1255ipiω1
    (e)μ=0255ipi

    以上信息可以推得以下公式:

    (1)1=ω0+ω1
    (2)μt=ω0μ0+ω1μ1

    大津算法所使用的类间方差 σ2(t) 的定义如下:

    (3)σ2(t)=ω0(μ0μ)2+ω1(μ1μ)2

    将公式(1) (2)代入公式(3)化简可得:

    (4)σ2(t)=ω0ω1(μ0μ1)2

    然后你就能step through all possible thresholds t = 0, 1, ... , 255, 能maximize公式(4)的 t 就是我们要的答案

    你以为这就结束了?

    根据大津展之的原文,公式(4)能进一步化简变量:将到阈值 t 的pixel在全图中的累加均值记为 μt

    (5)μt=0tipi

    将公式(e) (1) (5)代入 μ0,μ1 后可得:

    (6)μ0=μtω0
    (7)μ1=μμt1ω0

    将公式(6) (7)代入公式(4)可得到Otsu桑の最终类间方差公式:

    (8)σ2=(μω0μt)2ω0(1ω0)

    配合公式(a) (e) (5),能maximize公式(8)的 t 就是所求的阈值

 

 

3. Image Convolution

二维图像卷积是用来给图片滤波/模糊化的,本部分是给2.5部分铺路

3.1 2D Convolution 二维卷积

二维卷积需要循环如下的步骤:

  • 在原图(Input)中框出卷积核(Kernel)同样大小的区域

  • 将该区域与卷积核逐个元素相乘后求和

  • 将该和放在结果图(Output)中与原图框出区域相同的位置

  • 将原图中的框移动一个像素

卷积核的边长一般为奇数(不是奇数会很怪不是吗

2D image convolution

以上图为例,结果图红框中的“5”由原图中蓝色实线框的区域与卷积核计算得来:

5=11+20+10+00+10+10+30+00+22

而红框中的“5”右侧的“4”则是由原图中蓝色虚线框以相同的步骤计算的来的

 

3.2 Padding “填充”

  • 你干嘛~嗨害哎呦~

    上面那张图里可以看出:如果我们用一个 3×3 的kernel去卷一个 6×6 的原图,输出的是一个 4×4 的结果图,这是极坏的(

    如何保证卷完了尺寸还是原来的大小?简单,在原图外加“边框”

    原图为 n×n,kernel为 f×f,那么结果图就是 (nf+1)×(nf+1),所以扩充的“边框”层数为 (f1)

    padding

    然后卷积过程就会变得肥肠nice:

    padding anime

  • “加个相框叭”

    cv2.copyMakeBorder() - 复制图像,并在该副本上添加padding

    padding有如下几种类型:

     

3.3 如何用OpenCV卷积

cv2.filter2D()可以用于实现卷积操作(实际上就是滤波/模糊化)

比如定义具备如下效果的卷积核:3×3 区域内元素的和除以10(模糊化)

kernel=110[111111111]

具体效果:

convolution example

 

 

4. Image Filtering

  • 关于滤波器

    滤波属于卷积,对于线性滤波而言,不同的滤波方法的区别在于使用的卷积核不同

    • 低通滤波器 - 图片模糊化 - 允许低频信号通过,而图像的边缘和噪点都属于高频信号,因此能平滑/模糊图像

    • 高通滤波器 - 图片锐化 - 与低通滤波器相反,能强化图像边缘,突出细节

  • 关于图像噪声

    主要是以下两种:

    • Salt-and-pepper Noise 椒盐噪声

      一般是出现在随机位置的白点或者黑点,噪点的深度基本固定

      salt-and-pepper noise

    • Gaussian Noise 高斯噪声

      与椒盐噪声特性相反,高斯噪声几乎出现在每个点上,噪点深度完全随机,其概率密度函数服从高斯正态分布

      gaussian noise

  • 关于下面介绍的滤波函数

    • 每个滤波函数都有一个可选参数borderType,这个详情请见2.4.2卷积的padding

    • 线性滤波

      均值滤波,方框滤波,高斯滤波

    • 非线性滤波

      中值滤波,双边滤波

 

4.1 均值滤波cv2.blur()

取卷积核区域内元素的均值,以 3×3 的卷积核为例:

kernel=19[111111111]

 

4.2 方框滤波cv2.boxFilter()

与均值滤波有相似性,以 3×3 的卷积核为例:

kernel=a[111111111]
a={1k.x×k.ynormalize = True1normalize = False

如果可选参数normalizeTrue时,方框滤波就是均值滤波;为False时,方框滤波的输出相当于区域内元素的和

 

4.3 高斯滤波cv2.GaussianBlur()

4.1/2不同,高斯滤波的卷积核内并非每个值都一样,而是以pixel离中心的距离为标准进行对它们加权,离中心越远的pixel权重越小,和正态分布曲线是一样的,但是一般的高斯滤波会模糊掉图像的边缘

gaussian weight function curve

OpenCV中默认的 3×3 高斯卷积核如下:

kernel=[0.06250.1250.06250.1250.250.1250.06250.1250.0625]

 

4.4 中值滤波cv2.medianBlur()

将卷积核区域内的数排序后取中位数(median)作为输出值,这个原理使0和255这样的极端值很容易被消除,因此中值滤波可谓是对椒盐/斑点噪声の宝具(顺带一提它也能很好地保存边缘)

由于是非线性滤波,处理速度比其它几种线性的滤波方式要慢

 

4.5 双边滤波cv2.bilateralFilter()

双边滤波能在干死噪点的同时仍然keeping edge sharp,高斯滤波会糊边,因为it is just a function of space,它不考虑pixel的intensity,也不考虑pixel是不是在边缘

双边滤波除了有空间上的Gaussian Filter以外,还多了一个pixel (intensity) difference上的Gaussian Filter,以确保只有与central pixel颜色值相近的pixel被模糊化,而图像边缘上的pixel会有比较大的intensity variation

也是非线性滤波,也贼慢

 

4.6 补充信息:Gaussian Kernel

OpenCV分两步计算二维的高斯卷积:

  • 计算一维高斯卷积核

    OpenCV的一维卷积公式类似于一维高斯函数:

    G(i)=αe(iksize12)22σ2α is a constant such that 0ksize1G(i)=1

    cv2.getGaussianKernel会计算并返回 ksize×1 的Gaussian Filter Coefficient

     

  • 分别进行水平/垂直卷积

    水平:

    [0.250.50.25][0.250.50.250.250.50.250.250.50.25]

    垂直:

    [0.250.50.25][0.250.250.250.50.50.50.250.250.25]
  • 卷出来的矩阵乘起来

    kernel=[0.250.50.250.250.50.250.250.50.25]×[0.250.250.250.50.50.50.250.250.25]=[0.06250.1250.06250.1250.250.1250.06250.1250.0625]

    或者换种写法:

    kernel=116[121242121]

OpenCV中对小于 7×7 的高斯卷积核是直接计算好塞在数组里的,用这些会更快