数字图像处理(四)大津二值化

题目:使用大津二值化算法进行二值化时阈值的确定。
采用国际标准测试图像Lena。

算法原理

我们在对图片进行二值化时,是希望将图片分割成两部分,称之为前景和背景。一般情况下,我们将感兴趣的部分称为前景,对于不感兴趣的称为背景。
大津算法的原理是:对图片二值化后,产生的前景和背景之间差异是比较大的,而前景部分中的像素是相似的,背景部分中的像素是相似的;可以理解为,同一类中差异小,不同类中差异大。那么如果存在一个阈值,使得图像分为了前景和背景,应当符合同一类中差异最小,不同类中差异最大,这个值就是最佳阈值。
“差异”用方差来表示:如果单个的数据其越偏离于中心,其方差也就越大。如果同一类中方差越小,那么表示同一类中差异也小;不同类之间方差越大,表示不同类中差异越大。

假设图像宽为 w w w,高为 h h h,灰度阈值为 t , 0 ≤ t ≤ 255 t,0\le t\le255 t0t255
像素值小于 t t t的像素个数为 n 0 n_0 n0(即前景),像素值大于等于 t t t的像素个数为 n 1 n_1 n1(即背景)。所以有公式: n 0 + n 1 = 像素总个数 = h × w n_0+n_1=像素总个数=h\times w n0+n1=像素总个数=h×w
前景像素个数 n 0 n_0 n0占像素总数的比例 w 0 w_0 w0 w 0 = n 0 / ( h × w ) w_0=n_0/(h\times w) w0=n0/(h×w)
背景像素个数 n 1 n_1 n1占像素总数的比例 w 1 w_1 w1 w 1 = n 1 / ( h × w ) w_1=n_1/(h\times w) w1=n1/(h×w)
前景的平均灰度 u 0 u_0 u0为前景的灰度和除以前景的像素个数 n 0 n_0 n0 u 0 = 前景的灰度和 / n 0 u_0=前景的灰度和/n_0 u0=前景的灰度和/n0
背景的平均灰度 u 1 u_1 u1为背景的灰度和除以背景的像素个数 n 1 n_1 n1 u 1 = 背景的灰度和 / n 1 u_1=背景的灰度和/n_1 u1=背景的灰度和/n1
那么全图的平均灰度 u u u为: u = ( 前景灰度和 + 背景灰度和 ) / ( h × w ) = w 0 × u 0 + w 1 × u 1 u=(前景灰度和+背景灰度和)/(h\times w)=w_0\times u_0+w_1\times u_1 u=(前景灰度和+背景灰度和)/(h×w)=w0×u0+w1×u1
类间方差 g g g g = w 0 × ( u − u 0 ) 2 + w 1 × ( u − u 1 ) 2 g=w_0\times (u-u_0)^2+w_1\times (u-u_1)^2 g=w0×(uu0)2+w1×(uu1)2
进一步推导: g = w 0 × w 1 × ( u 0 − u 1 ) 2 g=w_0\times w_1\times (u_0-u_1)^2 g=w0×w1×(u0u1)2
g g g最大时,此时对应的 t t t就为最佳阈值。
C++代码:

    cv::Mat image = cv::imread("Lena.bmp");
    cv::Mat src(image.size(), CV_8UC1);
    cv::Mat dst(image.size(), CV_8UC1);
    cv::cvtColor(image, src, CV_BGR2GRAY);

    int width = src.cols;
    int height = src.rows;
    
    vector<float>g;
    for (int t = 0; t < 256; t++)
    {
        double n0 = 0, n1 = 0;
        float w0 = 0, w1 = 0, u0 = 0, u1 = 0;
        for (int row = 0; row < height; row++)
        {
            uchar *currentData = src.ptr<uchar>(row);
            for (int col = 0; col < width; col++)
            {
                if (*(currentData + col) < t)
                {
                    n0 += 1;
                    u0 = u0 + *(currentData + col);
                }
                else
                {
                    n1 += 1;
                    u1 = u1 + *(currentData + col);
                }
            }

        }
        w0 = n0 / (width*height);
        w1 = n1 / (width*height);
        u0 = u0 / n0;
        u1 = u1 / n1;
        if (n0 == 0)
        {
            u0 = 0;
        }
        if (n1 == 0)
        {
            u1 = 0;
        }
        g.push_back(w0*w1*pow(u0 - u1, 2));
    }
    //最大值 int maxValue=*max_element(g.begin(),g.end())
    int thre = max_element(g.begin(), g.end()) - g.begin();
    cout << "阈值是" << thre << endl;

最终得出的阈值是118。
结果展示:
在这里插入图片描述
代码里有一点要注意的是:我们创建vector来保存不同阈值t下对应的g,最终遍历完所有的阈值t,需要求出vector g中的最大值的下标,此下标就是我们想要的最佳阈值t。我们使用max_element函数,得出最大值的地址,然后减去开始的地址得到的就是最大值的下标。

参考博客:https://blog.csdn.net/qq_40243750/article/details/117433179
https://zhuanlan.zhihu.com/p/95034826
https://blog.csdn.net/Song_Esther/article/details/86087655?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-86087655-blog-67638545.pc_relevant_multi_platform_whitelistv3&spm=1001.2101.3001.4242.1&utm_relevant_index=3

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值