开篇

这篇文章开始前,我们需要安装个python包:matplotlib

Matplotlib 可能是 Python 2D-绘图领域使用最广泛的套件。它能让使用者很轻松地将数据图形化,并且提供多样化的输出格式。我们在后续的文章中都使用这个图形库来将图片展示出来。

具体安装命令如下:

1
2
3
4
# 没有 anaconda,直接命令行安装
pip install matplotlib
# 有 anaconda
conda install -n python3 matplotlib

几个重要的函数:

1
2
3
4
5
6
7
8
9
10
11
12
from matplotlib import pyplot as plt

# 下面 subplot 和 imshow 结合使用,所以写法都如下,一行中用逗号隔开
# subplot方法传一个整数参数,必须是3位的,像下边 331,表示3行3列的第1个位置
# subplot传三个整数,如(3,3,1),也表示3行3列的第1个位置
# 下面一行表示:窗体内创建一个 3行3列的表格,将img放在第一格内
# 也就是3行3列,最多就9张图片放9个格子
plt.subplot(331),plt.imshow(img)
# 整个窗体的 标题
plt.suptitle('Some Title')
# 将上边的绘图配置展示在窗口内
plt.show()

图像分割

图像分割是指将图像分成若干具有相似性质的区域的过程,主要有基于阈值、基于区域、基于边缘、基于聚类、基于图论和基于深度学习的图像分割方法等。

分割的原则就是使划分后的子图在内部保持相似度最大,而子图之间的相似度保持最小。

阈值分割算法

阈值分割的核心就是如何选取阈值, 选取正确的阈值是分割成功的关键。
阈值分割分为两类:全局阈值分割局部阈值分割

全局阈值分割

全局阈值分割指的是将灰度值大于阈值的像素设为白色,小于或者等于阈值的像素设为黑色; 或者反过来, 将大于阈值的像素设为黑色, 小于或者等于阈值的像素设为白色, 两者的区别只是呈现形式不同。
opencv 默认提供了一些常用的方法:

  • THRESH_BINARY = 0
  • THRESH_BINARY_INV = 1
  • THRESH_TRUNC = 2
  • THRESH_TOZERO = 3
  • THRESH_TOZERO_INV = 4
  • THRESH_MASK = 7
  • THRESH_OTSU = 8
  • THRESH_TRIANGLE = 16
    使用方法:
    1
    2
    3
    4
    5
    6
    7
    # 参数:
    # src: 原图像
    # thresh: 对像素值进行分类的阈值
    # maxval: 当像素值高于(小于)阈值时,应该被赋予的新的像素值
    # type: 第四个参数是阈值方法
    # 返回值: 函数有两个返回值,一个为retVal, 一个阈值化处理之后的图像
    cv2.threshold(src, thresh, maxval, type)

常用的5种使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('pic.jpg')

plt.subplot(331);plt.imshow(img)

#5种阈值法图像分割
ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
plt.subplot(332), plt.imshow(thresh1)

ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
plt.subplot(333), plt.imshow(thresh2)

ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
plt.subplot(334), plt.imshow(thresh3)

ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
plt.subplot(335), plt.imshow(thresh4)

ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
plt.subplot(336), plt.imshow(thresh5)

plt.subplot(337), plt.hist(img.ravel(), 256)

plt.suptitle('threshold')
plt.show()

输出结果:

这里我使用的是一张 灰度值 差别不是很大的图片,所以出来的效果没有那么好。
第一张是原图,最后一张是直方图,25一次是上边04五种常用方法。

局部阈值分割

局部阈值分割的核心是计算阈值矩阵,比较常用的有自适应阈值算法(又称移动平均值算法) , 是一种简单但是高效的局部阈值算法,其核心思想就是把每一个像素的邻域的“平均值”作为该位置的阈值。

在说明分割算法前,我们先了解一下直方图双峰法,很多阈值化分割都是基于这个算法实现的。

直方图双峰法

60年代中期提出的直方图双峰法(也称: 2-Mode Method) 是典型的全局单阈值分方法。
基本思想: 假设图像中有明显的目标和背景,则其灰度直方图呈双峰分布,当灰度级直方图具有双峰特性时,选取两峰之间的最低谷对应的灰度级作为阈值。

上图中可以看到直方图中有明显的双峰状,那么以图中Zt为阈值进行二值化分割,就可以将目标和背景分割开。

当然,这个方法的缺点也很明显,就是当图像的直方图出现波峰间的波谷平坦、各区域直方图的波形重叠等情况时,用双峰法就很难找到合适的阈值。

对于这个可以做些优化。一种常用的方式是先对直方图进行高斯平滑处理,逐渐增大高斯滤波器的标准差,直到能从平滑后的直方图中得到两个唯一的波峰和它们之间唯一的最小值。
下面是一个c++的实现方案:

灰度平均值法

使用整幅图像的灰度平均值作为二值化的阈值,一般该方法可作为其他方法的初始猜想值。


其他

用 RGB 值获取 HSV 值

非常简单,可以使用相同的函数 cv2.cvtColor()。您无需传递图像,只需传递所需的 BGR 值即可。例如,要查找 Green 的 HSV 值,请在 Python 终端中尝试以下命令:

1
2
3
4
5
green = np.uint8([[[0,255,0]]])
hsv_green = cv2.cvtColor(green, cv2.COLOR_BGR2HSV)
print(hsv_green)
# 输出
[[[ 60 255 255]]]

现在你分别取 [H-10, 100,100][H+10, 255, 255] 作为下限和上限。
除了这种方法,还可以使用任何图像编辑工具(如 GIMP)或任何在线转换器来查找这些值,但不要忘记调整 HSV 范围。

OpenCV中的HSV颜色体系

如果直接使用OpenCV中cvtColor函数,并设置参数为CV_BGR2HSV,那么所得的H、S、V值范围分别是[0,180),[0,255),[0,255),而非[0,360],[0,1],[0,1];这时我们可以查下面的表格来确定颜色的大致区间:

绿
hmin 0 0 0 0 or 156 11 26 35 78 100 125
hmax 180 180 180 10 or 180 25 34 77 99 124 155
smin 0 0 0 43 43 43 43 43 43 43
smax 255 43 30 255 255 255 255 255 255 255
vmin 0 46 221 46 46 46 46 46 46 46
vmax 46 220 255 255 255 255 255 255 255 255