索引目录
1.手动阈值分割
1.1 threshold()
需要多次尝试,以经验加枚举的方式挨个测试,最终确定一个合适的阈值。但此阈值仅适用于这一特定的场景,光照等因素的改变可能导致阈值不再适用。所以,此方法适用与场景单一、固定的场合,如工业车间、流水线等机器人视觉上。
void threshold(img, threshold, maxval,ThresholdTypes)
其中type表示阈值分割的方法,支持如下五种:
THRESH_BINARY = 0 二值分割
THRESH_BINARY_INV = 1 反向二值分割
THRESH_TRUNC = 2 截断
THRESH_TOZERO = 3 取零
THRESH_TOZERO_INV = 4 反向取零
enum ThresholdTypes {
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
};
THRESH_OTSU和THRESH_TRIANGLE是作为优化算法配合THRESH_BINARY、THRESH_BINARY_INV、THRESH_TRUNC、THRESH_TOZERO以及THRESH_TOZERO_INV来使用的,效果更明显,单独使用效果差点。
当时用thresh_otsu或thresh_triangle,函数确定最优阈值使用Otsu(大津法)或三角形算法,而不是指定的阈值。
函数返回Otsu或三角形算法计算得到的阈值。目前,使用大津法和三角法,输入必须为单通道8位的图像。
Mat dst=threshold(img_gray, 0, 255, THRESH_TOZERO|THRESH_OTSU)
1.2 inRange
可以同时针对多通道进行操作
void inRange(InputArray src, InputArray lowerb,//scalar类型的像素值,单通道scalar取一个值就行,彩图3通道scalar三个值;
InputArray upperb,
OutputArray dst//与输入图像src 尺寸相同且为CV_8U 类型
);
针对单通道图像
dst(I) = lowerb(I)0 ≤ src(I)0 < upperb(I)0
如果一幅灰度图像的某个像素的灰度值在指定的高、低阈值范围之内,则在dst图像中令该像素值为255,否则令其为0,这样就生成了一幅二值化的输出图像。
针对三通道图像,每个通道的像素值都必须在规定的阈值范围内!
dst(I) = lowerb(I)0 ≤ src(I)0 < upperb(I)0 ∧ lowerb(I)1 ≤ src(I)1 < upperb(I)1 ∧lowerb(I)2 ≤ src(I)2 < upperb(I)2
下边界为Scalar(65, 65, 65), 上边界为Scalar(95, 95, 95)
2.自动阈值分割
2.1 基于灰度图均值的自动分割
将灰度图像的像素均值作为阈值T
Mat src = imread("E:/images/aaa.jpg");
Mat gray, gray_mean;
cvtColor(src, gray, COLOR_BGR2GRAY);
meanStdDev(gray, gray_mean, mat_stddev);
double m;
m = mat_mean.at<double>(0, 0);
binary = Mat::zeros(src.size(), CV_8UC1);
int height = gray.rows;
int width = gray.cols;
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int pv = gray.at<uchar>(row, col);
if (pv > m) {
binary.at<uchar>(row, col) = 255;
}
else {
binary.at<uchar>(row, col) = 0;
}
}
}
imshow("binary", binary);
waitKey(0);
return 0;
}
2.2基于直方图的自动分割
双峰还是单峰的分辨,可以看偏黑或偏白的占比,也可尝试运行两种方法,观察效果
2.2.1 OTSU-直方图出现双峰
Otsu分割方法求取阈值是求得使类间方差最大的阈值。这个算法可以用于OCR, 车牌分割等等。直方图阈值分割计数法能够较为有效的将背景何前景区分开来,比较完整的分割出图片中的目标物体。
其中p1(k),p2(k)分别是两类的概率密度,m1(k),m2(k)分别是C0和C1的均值(C0和C1都对应一堆像素值,直接求数学平均值),mg是图像的全局均值。
Otsu实现思路:
- 计算0~255各灰阶对应的像素个数,保存至一个数组中,该数组下标是灰度值,保存内容是当前灰度值对应像素数;
- 计算背景图像的平均灰度、背景图像像素数所占比例;
- 计算前景图像的平均灰度、前景图像像素数所占比例;
- 遍历0~255各灰阶,计算并寻找类间方差极大值;
2.2.2 Triangle-直方图出现单峰
该方法是使用直方图数据,基于纯几何方法来寻找最佳阈值,它的成立条件是假设直方图最大波峰在靠近最亮的一侧,然后通过三角形求得最大直线距离,根据最大直线距离对应的直方图灰度等级即为分割阈值,图示如下:
对上图的详细解释:
在直方图上从最高峰处bmx到最暗对应直方图bmin(p=0)%构造一条直线,从bmin处开始计算每个对应的直方图b到直线的垂直距离,知道bmax为止,其中最大距离对应的直方图位置即为图像二值化对应的阈值T。
扩展情况:
有时候最大波峰对应位置不在直方图最亮一侧,而在暗的一侧,这样就需要翻转直方图,翻转之后求得值,用255减去即得到为阈值T。扩展情况的直方图表示如下:
二:算法步骤
- 图像转灰度
- 计算图像灰度直方图
- 寻找直方图中两侧边界
- 寻找直方图最大值
- 检测是否最大波峰在亮的一侧,否则翻转
- 计算阈值得到阈值T,如果翻转则255-T
2.3 自适应阈值分割
OpenCV中的自适应阈值算法主要是基于均值实现,根据计算均值的方法不同分为box-filter模糊均值与高斯模糊均值,然后使用原图减去均值图像,得到的差值图像进行自适应分割,其一般步骤为:
void cv::adaptiveThreshold( InputArray src, OutputArray dst, double maxValue, int
adaptiveMethod, //ADAPTIVE_THRESH_GAUSSIAN_C = 1 , ADAPTIVE_THRESH_MEAN_C = 0
int thresholdType, //THRESH_BINARY 二值图像 = 原图 – 均值图像 > -C ? 255 : 0 ,
//THRESH_BINARY_INV 二值图像 = 原图 – 均值图像 > -C ? 0 : 255
int blockSize, //取值必须是奇数,如果输入图像较大,取127左右,对于小图像取25左右
double C //取值多少与效果有很大关系,不能取高,也不能取低,一般取值在10/15/25
)
局部自适应阈值则是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值。这样做的好处在于每个像素位置处的二值化阈值不是固定不变的,而是由其周围邻域像素的分布来决定的。亮度较高的图像区域的二值化阈值通常会较高,而亮度较低的图像区域的二值化阈值则会相适应地变小。不同亮度、对比度、纹理的局部图像区域将会拥有相对应的局部二值化阈值。
示例:
Mat src = imread("E:/images/aaa.jpg");
Mat gray, binary;
cvtColor(src, gray, COLOR_BGR2GRAY);
adaptiveThreshold(gray, binary, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 25, 10);
imshow("binary", binary);
waitKey(0);
return 0;
3.基于形态学梯度二值化
4.inRange二值化
5.基于Canny边缘二值化
参考
- 原文链接:https://blog.csdn.net/jia20003/article/details/53954092
- https://blog.csdn.net/weixin_43046653/article/details/83277827