> 技术文档 > OpenCV二值图像——二值分析_二值图像函数

OpenCV二值图像——二值分析_二值图像函数


二值图像介绍

定义:二值图像首先是单通道,其次是非黑(0)即白(255)。
应用:二值图像在图像处理、对象检测与测量、缺陷检测、模式识别、机器人视觉等方面都有很重要的应用

图像阈值化分割

图像阈值化分割是指使用阈值对图像进行分割,基于一个简单的阈值T实现对图像二分类的分割,这是最简单的阈值化分割方法。
基于这个阈值,用阈值化分割方法来实现对图像的二分类分割,即像素值大于T的设为白色(255),像素值小于或等于T的设为黑色(0),最终得到二值图像。
OpenCV中的阈值化分割函数

double threshold( InputArray src, OutputArray dst,  double thresh, double maxval, int type );

其中:
src与dst分别表示输入图像和输出图像,支持单通道与多通道,数据类型支持CV_8U与CV_32F;
thresh表示阈值;
maxval表示最大值,当图像数据类型为CV_8U时,其最大值为255,当图像数据类型为CV_32F时,其最大值为1.0;
type表示阈值化方法,❑THRESH_BINARY:二值化。❑THRESH_BINARY_INV:二值化反。❑THRESH_TRUNC:阈值截断。❑THRESH_TOZERO:阈值取零。❑THRESH_TOZERO_INV:阈值取零反。一般选THRESH_BINARY,具体效果看下图;
返回值:如果使用Otsu或Triangle方法,则返回计算出的阈值。
代码演示:

 cv::Mat src = cv::imread(\"./img/mini2_castle.jpg\"); if (src.empty()) {  qDebug() << \"image load failed!\"; return 0; } cv::Mat gray; cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); cv::Mat binary; // thresh怎么得来?127? cv::threshold(gray, binary, 127, 255, cv::THRESH_BINARY); cv::imshow(\"THRESH_BINARY\", binary); cv::threshold(gray, binary, 127, 255, cv::THRESH_BINARY_INV); cv::imshow(\"THRESH_BINARY_INV\", binary); cv::threshold(gray, binary, 127, 255, cv::THRESH_TRUNC); cv::imshow(\"THRESH_TRUNC\", binary); cv::threshold(gray, binary, 127, 255, cv::THRESH_TOZERO); cv::imshow(\"THRESH_TOZERO\", binary); cv::threshold(gray, binary, 127, 255, cv::THRESH_TOZERO_INV); cv::imshow(\"THRESH_TOZERO_INV\", binary);

效果:
在这里插入图片描述此处src为单通道灰度图片,如果src为三通道彩色图片(代码修改如下):

 cv::Mat gray = src.clone();// cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); cv::Mat binary;

,则效果如下图:
在这里插入图片描述由此可见,想要得到二值图片,则输入必须为为灰度图片。

如代码中所见thresh是怎么确定的呢??图像二值化的阈值计算方法有很多,单从算法类型上来说,可以分为全局阈值计算和局部阈值计算(自适应阈值计算)两大类。OpenCV支持两种经典的全局阈值计算方法,分别是大津法与三角法。自适应阈值计算方法支持均值与高斯自适应两种。

全局阈值之大律法

该方法最早是由大津展之提出的,所以通常称为大津法。该方法主要基于图像灰度直方图信息,通过计算最小类内方差来寻找阈值T,并根据阈值T将图像分为前景(白色)或者背景(黑色)。
OpenCV可以通过threshold函数的type参数来设置是否支持大津法,当type参数设置为THRESH_OTSU,表示支持大律法。当使用大律法自动计算阈值时,threshold函数的输入图像(src)必须是单通道灰度图像
首先需要把输入的图像转换为灰度图像,然后调用threshold函数完成二值化。
代码实现:

 cv::Mat src = cv::imread(\"./img/mini2_castle.jpg\"); if (src.empty()) {  qDebug() << \"image load failed!\"; return 0; } cv::Mat gray = src; cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); cv::Mat binary; cv::threshold(gray, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); cv::imshow(\"binary\", binary);

效果:
在这里插入图片描述使用大律法时,手动输入的阈值不会起作用,自动计算得到的阈值是threshold函数的返回值。

全局阈值之三角法

三角法与大津法都是基于直方图数据计算,最终实现阈值查找。三角法使用纯几何方法来寻找最佳阈值,它的成立条件是假设直方图的最大波峰在靠近最亮的一侧,然后通过三角形求得最大直线距离,根据最大直线距离所对应的直方图灰度等级即可求得分割阈值。
以上叙述过于专业,可以略过。,在OpenCV中调用三角法可以通过threshold函数的type参数来设置。当type参数设置为THRESH_TRIANGLE时,表示调用三角法。
代码实现:

 cv::threshold(gray, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_TRIANGLE); cv::imshow(\"triangle\", binary);

效果:
在这里插入图片描述**在实际应用中,三角法适用于有单峰直方图的图像,大津法适用于有双峰直方图的图像。**单双峰可以通过绘制色彩直方图获得,此处不赘述。

自适应阈值

全局阈值计算方法对光照方向一致的图像会有很好的二值化效果,但是当光照方向不一致的时候,全局阈值计算会破坏图像的原有信息,导致二值图像的大量信息丢失,此时就需要一个更加智能的二值化分割算法,可以在亮度不均匀的情况下,抵消亮度对图像的影响,完成二值化分割。这个算法就是OpenCV中的自适应阈值分割。
OpenCV中的自适应阈值分割并不会真正产生一个局部阈值,而是先对输入的图像进行模糊处理,然后使用原图减去模糊图像得到一个差值图像,再使用常量阈值C来与每个差值进行比较,大于-C的则赋值为白色,否则为0。
根据选择的模糊方式不同,自适应阈值分割可以分为均值自适应分割与高斯自适应分割

自适应阈值函数:

void adaptiveThreshold( InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C );

其中,src与dst分别是输入图像与输出图像,而且它们必须是8位单通道的。maxValue是赋值给满足自适应条件的前景图像灰度值,一般将maxValue设置为255即可。adaptiveMethod自适应方法当前支持均值自适应与高斯自适应两种方法thresholdType是阈值化类型。blockSize表示计算时候的窗口大小,而且必须是奇数,参数C为常量。
代码实现:

 cv::Mat src = cv::imread(\"./img/mini2_castle.jpg\"); if (src.empty()) {  qDebug() << \"image load failed!\"; return 0; } cv::Mat gray = src; cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); cv::Mat binary; cv::adaptiveThreshold(gray, binary, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, 25, 10); cv::imshow(\"mean\", binary); cv::adaptiveThreshold(gray, binary, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 25, 10); cv::imshow(\"gauss\", binary);

在这里插入图片描述

去噪与二值化

图像二值化很容易受到图像噪声的影响,有时候噪声会影响图像二值化的后续处理。常见的解决方法是在图像二值化之前对图像进行去噪预处理,处理完成之后再进行图像二值化操作。
inRange实现二值化
在OpenCV中,inRange函数用于根据颜色范围从图像中提取特定的颜色区域。这个函数检查输入图像中的每个像素,如果像素值位于指定的范围内,则在输出图像(或掩码)中对应位置的像素被设置为白色(或者说是255),否则被设置为黑色(0)。这种方法在处理颜色过滤、颜色识别等任务时非常有用。
在RGB色彩空间中,一些特定颜色的背景物体或者对象因为区分度过低而无法通过inRange函数进行划分时,可以尝试先转换到HSV或者其他色彩空间,再通过inRange函数处理,这样通常能取得比较好的效果。

void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst);

其中,src:输入图像,通常是经过颜色空间转换后的图像,比如从BGR转换到HSV颜色空间的图像;
lowerb:颜色范围的下界,使用Scalar类型表示;
upperb:颜色范围的上界,使用Scalar类型表示;
dst:输出图像,是一个二值图像,其中符合颜色范围的像素被设置为255,不符合的被设置为0。
代码实现:

 cv::Mat img = cv::imread(\"./img/bgr.jpg\"); if (img.empty()) return 0; cv::imshow(\"img\", img); cv::Mat hsv, mask; cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV); // 求得蓝色掩膜 cv::inRange(hsv, cv::Scalar(110, 50, 50), cv::Scalar(130, 255, 255), mask); cv::imshow(\"blue mask\", mask); // 找出原图蓝色区域 cv::Mat blue; cv::bitwise_and(img, img, blue, mask); cv::imshow(\"blue area\", blue);

效果:
在这里插入图片描述

总结1

图像二值化方法有:
1.基于全局阈值的threshold函数;
2.基于自适应阈值的adaptiveThreshold函数;
3.鲫鱼像素值范围的inRange函数;
4.基于边缘检测的Canny函数。

二值图像分析

1.连通组件标记(CCL)
CCL(Connected Component Labeling,连通组件标记)算法是图像分析中最常用的算法之一。CCL算法的实质是扫描一幅图像的每个像素,将位置相邻且值相同的像素点归为相同的组(group),最终得到图像中所有像素的连通组件。完成对每个前景对象的合并与定位,为后续的分析与测量做好准备。
OpenCV中的连通组件扫描算法是在以上两步法的基础上改进的快速版本,实现了快速扫描算法BBDT(基于块的决策树)​。相关的函数有两个:
一个是基础版本的连通组件扫描,不包含每个组件的相关统计信息:

int connectedComponents(InputArray image, OutputArray labels, int connectivity = 8, int ltype = CV_32S);

其中,image为输入二值图像,黑色背景;labels为输出的标记;connectivity为连通域,默认是8连通域;输出的labels类型默认为CV_32S。
另一个是带有统计信息的连通组件扫描,在完成连通组件扫描的同时,输出每个扫描组件的统计信息:

int connectedComponentsWithStats(InputArray image, OutputArray labels,  OutputArray stats, OutputArray centroids,  int connectivity, int ltype, int ccltype);

其中,centroids是每个连通组件的中心坐标,stats参数表示的是连通组件