opencv自适应二值化的应用


前言

最近在工作中,要实现自动绘制ROI的功能,但是在代码实现的过程中,遇到了不小的问题,现已解决。


一、二值化是什么?

图像的二值化,就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的只有黑和白的视觉效果。
二值化是图像分割的一种最简单的方法。二值化可以把灰度图像转换成二值图像。把大于某个临界灰度值的像素灰度设为灰度极大值,把小于这个值的像素灰度设为灰度极小值,从而实现二值化。
根据阈值选取的不同,二值化的算法分为固定阈值和自适应阈值。

二、自适应二值化

1.为什么要用自适应二值化

在处理从一个视频流中获取的图片时,往往因为光线的变化,而导致固定的阈值不适用,这样就会导致图片处理的结果大打折扣,现做如下比较:
原图_1:
在这里插入图片描述
在图中我们可以看到,只有这个水杯我们希望是黑色的,背景应该处理成白色。那么进行如下不同阈值的尝试。
阈值_100:

	cvtColor(srcClone, srcClone, CV_BGR2GRAY);
	threshold(srcClone, srcClone, 100, 255, CV_THRESH_BINARY);
	cv::imwrite("D:/阈值_100.jpg", srcClone);

在这里插入图片描述
阈值_110:

	cvtColor(srcClone, srcClone, CV_BGR2GRAY);
	threshold(srcClone, srcClone, 110, 255, CV_THRESH_BINARY);
	cv::imwrite("D:/阈值_110.jpg", srcClone);

在这里插入图片描述

阈值_120:

	cvtColor(srcClone, srcClone, CV_BGR2GRAY);
	threshold(srcClone, srcClone, 120, 255, CV_THRESH_BINARY);
	cv::imwrite("D:/阈值_120.jpg", srcClone);

在这里插入图片描述
还是同样的环境(拍摄角度改变了,但还是这个水杯),改变亮度,再来看这个水杯:
原图_2:
在这里插入图片描述

阈值_50:

	cvtColor(srcClone, srcClone, CV_BGR2GRAY);
	threshold(srcClone, srcClone, 50, 255, CV_THRESH_BINARY);
	cv::imwrite("D:/阈值_50.jpg", srcClone);

在这里插入图片描述

阈值_100:

	cvtColor(srcClone, srcClone, CV_BGR2GRAY);
	threshold(srcClone, srcClone, 100, 255, CV_THRESH_BINARY);
	cv::imwrite("D:/阈值_100.jpg", srcClone);

在这里插入图片描述

很明显可以看到,对于亮度改变的原图_2,原先的阈值_100已经不能完全适用,如果将阈值提升到110甚至再往上就会出现大面积黑色的结果,这就是要使用自适应二值化的原因,可以明显降低亮度变化带来的影响(当然不仅仅用于处理亮度变化)。
接下来看看自适应二值化的结果:
原图_1的自适应二值化:
在这里插入图片描述
原图_2的自适应二值化:
在这里插入图片描述
从以上结果我们可以看出,对于不同亮度的图片,使用自适应二值化后的结果是相差不大的,但是将两种不同的二值化结果相比较可得出,当背景没有过多的影子以及整张图的亮度没有明显变化(比如从左到右变暗)时,自适应二值化后的结果不一定比得过参数调好的固定阈值二值化,这是因为背景光源信息过于简单(大智若愚),但在处理视频流信息的时候,背景光源总会发生不可控的变化,我们肯定不能每次都通过人工来调整阈值,这是不符合自动化工艺的,因此就要使用一个比较好的自适应二值化。
再给出一个例子:
在这里插入图片描述
自适应二值化结果:
在这里插入图片描述
在这里就不浪费篇幅与固定阈值二值化做比较,读者可自行比较(本人已经试过,效果没那么好)。

2.自适应二值化代码实现(c++)

void thresholdIntegral(cv::Mat &inputMat, cv::Mat &outputMat)
{
    // accept only char type matrices
    CV_Assert(!inputMat.empty());
    CV_Assert(inputMat.depth() == CV_8U);
    CV_Assert(inputMat.channels() == 1);
    CV_Assert(!outputMat.empty());
    CV_Assert(outputMat.depth() == CV_8U);
    CV_Assert(outputMat.channels() == 1);

    // rows -> height -> y
    int nRows = inputMat.rows;
    // cols -> width -> x
    int nCols = inputMat.cols;

    // create the integral image
    cv::Mat sumMat;
    cv::integral(inputMat, sumMat);

    CV_Assert(sumMat.depth() == CV_32S);
    CV_Assert(sizeof(int) == 4);

    int S = MAX(nRows, nCols)/8;
    double T = 0.15;

    // perform thresholding
    int s2 = S/2;
    int x1, y1, x2, y2, count, sum;

    // CV_Assert(sizeof(int) == 4);
    int *p_y1, *p_y2;
    uchar *p_inputMat, *p_outputMat;

    for( int i = 0; i < nRows; ++i)
    {
        y1 = i-s2;
        y2 = i+s2;

        if (y1 < 0){
            y1 = 0;
        }
        if (y2 >= nRows) {
            y2 = nRows-1;
        }

        p_y1 = sumMat.ptr<int>(y1);
        p_y2 = sumMat.ptr<int>(y2);
        p_inputMat = inputMat.ptr<uchar>(i);
        p_outputMat = outputMat.ptr<uchar>(i);

        for ( int j = 0; j < nCols; ++j)
        {
            // set the SxS region
            x1 = j-s2;
            x2 = j+s2;

            if (x1 < 0) {
                x1 = 0;
            }
            if (x2 >= nCols) {
                x2 = nCols-1;
            }

            count = (x2-x1)*(y2-y1);

            // I(x,y)=s(x2,y2)-s(x1,y2)-s(x2,y1)+s(x1,x1)
            sum = p_y2[x2] - p_y1[x2] - p_y2[x1] + p_y1[x1];

            if ((int)(p_inputMat[j] * count) < (int)(sum*(1.0-T)))
                p_outputMat[j] = 255;
            else
                p_outputMat[j] = 0;
        }
    }
}
---
参考自:[添加链接描述](https://www.cnblogs.com/polly333/p/7269153.html)
这套算法以W*W为模板的矩形区域的二维平滑值来代替一维加权值,从而抛开了一维平滑的方向性问题,是Wellner 自适应滤波阈值的升级版。
若有人对算法原理感兴趣或对源码中有不理解的部分可移步下一篇博客,其中会根据自己的理解进行详细介绍。
本文如有问题请积极指出,万分感谢。
### 自适应二值化算法简介 在图像处理领域,自适应二值化是一种重要的预处理方法。这种方法能够根据局部区域内的像素灰度分布自动调整阈值,从而更好地保留图像中的细节特征[^1]。 对于Python编程环境而言,可以借助`opencv-python`库高效地完成这一过程。该库提供了丰富的API接口用于执行各类计算机视觉任务,其中包括了多种实现自适应二值化的途径。 ### 使用OpenCV-Python 实现自适应二值化 下面展示了一种基于OpenCV的简单而有效的自适应二值化方案: ```python import cv2 import numpy as np def adaptive_threshold(image_path): # 加载原始图像并将其转换为灰度模式 img = cv2.imread(image_path, 0) # 应用高斯模糊减少噪声影响 blurred_img = cv2.GaussianBlur(img, (5, 5), 0) # 进行自适应阈值处理 thresh = cv2.adaptiveThreshold(blurred_img, maxValue=255, adaptiveMethod=cv2.ADAPTIVE_THRESH_GAUSSIAN_C, thresholdType=cv2.THRESH_BINARY_INV, blockSize=11, C=2) return thresh if __name__ == "__main__": result = adaptive_threshold('example.jpg') cv2.imshow('Adaptive Threshold', result) cv2.waitKey(0) cv2.destroyAllWindows() ``` 上述代码片段展示了如何读取一张图片文件,并通过调用`cv2.adaptiveThreshold()`函数应用自适应二值化操作。这里采用了高斯加权均值法作为自适应策略(`ADAPTIVE_THRESH_GAUSSIAN_C`),同时指定了邻域大小(blockSize)以及常量C来微调最终效果。 值得注意的是,在实际应用场景下可能还需要针对具体需求进一步优化参数设置,比如调整blockSize或者改变滤波器类型等措施以获得更理想的结果。 ### 结合PIL与NumPy进行自定义实现 除了依赖于第三方库提供的内置功能外,也可以考虑采用基础工具如Pillow(PIL Fork)配合NumPy来自行构建简易版的自适应二值化逻辑: ```python from PIL import Image import numpy as np def custom_adaptive_threshold(pil_image, block_size=31, offset=-10): image_array = np.array(pil_image.convert('L'), dtype=np.float64) mean_filter = np.ones((block_size, block_size)) / (block_size ** 2) local_means = cv2.filter2D(image_array, -1, mean_filter) binary_result = ((image_array >= local_means + offset).astype(np.uint8)) * 255 return Image.fromarray(binary_result) pil_img = Image.open("example.png") custom_binary = custom_adaptive_threshold(pil_img) custom_binary.show() ``` 这段脚本首先将输入图像转化为灰度形式,并计算每个像素位置处对应的局部平均亮度;接着依据设定好的偏移量(offset),判断当前像素是否大于其所在窗口内所有像素强度的平均值加上offset,以此决定输出黑白两色之一。最后再转回PIL.Image对象以便后续显示或保存。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

麻将牌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值