二值图与灰度图概念
PS:opencv 中二值图像是基于背景是黑色!!!!
阈值分割之二值分割
- 五种阈值分割方法 : 输入图像 + 阈值T
- C++ API:
threshold
注意这个 API 只能支持灰度图
CV_EXPORTS_W double threshold( InputArray src, OutputArray dst,
double thresh, double maxval, int type );
//阈值分割(五种方式)
Mat gray, binary;
cvtColor(src, gray, COLOR_BGR2GRAY);//得到灰度图
imshow("gray", gray);
threshold(gray, binary, 127, 255, THRESH_BINARY);//这个 T = 127 暂时给定 不一定合理 后面分析怎么确定
imshow("binary", binary);//二值分割
threshold(gray, binary, 127, 255, THRESH_BINARY_INV);
imshow("threshold binary invert", binary);
threshold(gray, binary, 127, 255, THRESH_TRUNC);
imshow("threshold TRUNC", binary);
threshold(gray, binary, 127, 255, THRESH_TOZERO);
imshow("threshold to zero", binary);
threshold(gray, binary, 127, 255, THRESH_TOZERO_INV);
imshow("threshold to zero invert", binary);
- python API:
cv2.threshold
, 同样注意这个 API 输入图像为灰度图
参数说明:第一个是原图像矩阵,第二个是进行分类的阈值,第三个是高于(低于)阈值时赋予的新值,第四个是一个方法选择参数(分割方法),常用的有:
阈值模式有以下几种类型
0: THRESH_BINARY -当前点值大于阈值时,取Maxval,否则设置为0
1: THRESH_BINARY_INV -当前点值大于阈值时,设置为0,否则设置为Maxval
2: THRESH_TRUNC -当前点值大于阈值时,设置为阈值,否则不改变
3: THRESH_TOZERO -当前点值大于阈值时,不改变,否则设置为0
4: THRESH_TOZERO_INV -当前点值大于阈值时,设置为0,否则不改变
这个函数返回两个值,第一个值为阈值,第二个就是阈值处理后的图像矩阵
import cv2
#读入图像并转为灰度图
img = cv2.imread('.\\images\\keji.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
print(img.shape)
cv2.imshow('img', img)
ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
cv2.imshow('binary', thresh1)
ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
cv2.imshow('binary_inv', thresh2)
ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
cv2.imshow('trunc', thresh3)
ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
cv2.imshow('tozero', thresh4)
ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
cv2.imshow('tozero_inv', thresh5)
cv2.waitKey(0)
cv2.destroyAllWindows()
- 很明显,二值分割就是阈值分割中的一种,即THRESH_BINARY
获取阈值算法
前面对于阈值的设定上,我们选择的阈值都是127,在实际情况中,有的图像阈值不是127得到的图像效果更好。那么这里就需要算法自己去寻找一个阈值,那么怎么确定较好的阈值T呢,有很多计算阈值的算法!主要分为全局阈值和自适应阈值
全局阈值
- 全局阈值概念
全局固定阈值很容易理解,就是对整幅图像都是用一个统一的阈值来进行二值化! - 全局阈值分割方法: 均值法、 OTSU、 三角法(Triangle)
均值法:
//C++ 实现 均值法全局阈值
Scalar m = mean(gray);
printf("means:%.2f\n", m[0]);//计算得到均值为120.88
threshold(gray, binary, m[0], 255, THRESH_BINARY);
imshow("binary", binary);
OTSU:
- 相关概念:
灰度直方图:将数字图像中的所有像素,按照灰度值的大小,统计其出现的频率。其实就是每个值(0~255)的像素点个数统计,如下图。
Otsu算法假设这副图片由前景色和背景色组成,通过最大类间方差选取一个阈值,将前景和背景尽可能分开。下图说明中,以2作为分割点计算类内方差,然后计算其他点作为分割点的方差,选择方差最小的作为阈值分割点。数学思想是:相当于对数据进行分类,分成两类,那么相似的东西他们之间的差异肯定小,那么基于类内最小方差之和作为分割点就很科学。基本上双峰图片的阈值T在两峰之间的谷底。
三角法: 三角法介绍
- c++ API:
//OTSU
double t1 = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);//t1为otsu分割阈值
printf("otsu threshold T:%.2f\n", t1);
imshow("otsu binary", binary);
//三角法 triangle
double t2 = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_TRIANGLE);//t2为triangle分割阈值
printf("triangle threshold T:%.2f\n", t2);
imshow("triangle binary", binary);
- python API
ret, thresh6 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # Otsu 滤波
print('T:', ret)
cv2.imshow('otsu', thresh6)
ret, thresh7 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_TRIANGLE) # triangle 滤波
print('T:', ret)
cv2.imshow('triangle', thresh7)
PS:可以看出均值、otsu、triangle的各自效果,各自的阈值分别可以获取,其中 otsu 效果最好,因为otsu可以有效对抗多个峰的直方图阈值查找,三角法只有对单峰的图像效果较好!因此需要根据图像直方图的分布情况选择合适的方法,一般优先选择otsu,三角法主要应用于医学和生物中的比较单调的图像分割!
自适应阈值
- 自适应阈值概念
自适应阈值可以看成一种局部性的阈值,通过设定一个区域大小,比较这个点与区域大小里面像素点 的平均值(或者其他特征)的大小关系确定这个像素点的情况!全局阈值的局限就是对光照不均匀的图像容易错误分割,而自适应阈值可以改善这种缺点!
- 自适应阈值分割原理
它的思想不是计算全局图像的阈值,而是根据图像不同区域亮度分布,计算其局部阈值,所以对于图像不同区域,能够自适应计算不同的阈值,因此被称为自适应阈值法。如何确定局部阈值呢?可以计算某个邻域(局部)的均值、中值、高斯加权平均(高斯滤波)来确定阈值。
- C++ API:
adaptiveThreshold
CV_EXPORTS_W void adaptiveThreshold( InputArray src, OutputArray dst,
double maxValue, int adaptiveMethod,
int thresholdType, int blockSize, double C );
//自适应阈值
adaptiveThreshold(gray, binary, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 25, 10);
imshow("ada-gaussian binary", binary);
adaptiveThreshold(gray, binary, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 25, 10);
imshow("ada-mean binary", binary);
- python API
1 第一个参数为原始图像矩阵;
2 第二个参数为像素值上限;
3 第三个是自适应方法(adaptive method):
# -----cv2.ADAPTIVE_THRESH_MEAN_C:领域内均值
# -----cv2.ADAPTIVE_THRESH_GAUSSIAN_C:领域内像素点加权和,权重为一个高斯窗口
4 第四个值的赋值方法:只有cv2.THRESH_BINARY和cv2.THRESH_BINARY_INV
5 第五个Block size:设定领域大小(一个正方形的领域)
6 第六个参数C,阈值等于均值或者加权值减去这个常数(为0相当于阈值,就是求得领域内均值或者加权值)
thresh8 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 25, 2)
cv2.imshow('addp mean', thresh8)
thresh9 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 25, 2)
cv2.imshow('addp gauss', thresh9)