图像处理-自适应二值化(大律法)

一、常规二值化

        再图像处理中,常规的二值化方法是设置一个阈值,图像中大于阈值的变为255(或0),小于阈值的变为0(或255)。此方法是最简单的一种二值化方法。但适应性不强,有着局限性,比如每个人在使用时需要根据所用环境的不同而设置不同的阈值,为使用者带来不变,鉴于此,学者们发明了自适应二值化方法。

二、自适应二值化

        顾名思义,自适应二值化能够根据图像的局部特征自动调整阈值,从而更好地适应图像中的局部变化。这种局部适应性使得自适应二值化在处理具有不同光照条件、对比度和纹理的图像时表现出色。通过自适应地选择阈值,自适应二值化能够增强图像的对比度,使图像中的细节更加清晰可见。这对于后续的图像分析和处理非常重要,因为它可以提高特征提取的准确性和可靠性。

2.1大律法

        在自适应二值化中,一般都采用大律法的方向进行求解阈值。

        原理:大律法中,记t为前景与背景的分割阈值,前景点数占图像比例为w0,平均灰度为u0;背景点数占图像比例为w1,平均灰度为u1。图像的总平均灰度为:u=w0u0+w1u1。从最小灰度值到最大灰度值遍历t,当t使得值g=w0(u0-u)²+w1(u1-u)²最大时t即为分割的最佳阈值。

        步骤:大律法的实现主要包括以下步骤。首先,计算图像的直方图,即将图像所有的像素点按照0~255共256个bin,统计落在每个bin的像素点数量。然后,归一化直方图,即将每个bin中像素点数量除以总的像素点。接着,从0到255遍历每一个可能的阈值,计算前景和背景的类间方差。最后,选择使类间方差最大的阈值作为最佳阈值

2.2 python实例

void cv2.adaptiveThreshold(src, dst, maxValue, adaptiveMethod, thresholdType, blockSize, C)

参数解读:

  • src:输入图像,通常为灰度图像。
  • dst:输出图像,与源图像尺寸和类型相同。
  • maxValue:用于设置二值化的最大值。
  • adaptiveMethod:计算阈值的方法,可以是ADAPTIVE_THRESH_MEAN_C(邻域平均值减去常数C)或ADAPTIVE_THRESH_GAUSSIAN_C(邻域高斯加权和减去常数C)。
  • thresholdType:二值化类型,可以是THRESH_BINARY(超过阈值设置为maxValue,否则为0)或THRESH_BINARY_INV(超过阈值设置为0,否则为maxValue)。
  • blockSize:用于计算阈值的邻域大小,必须是奇数且大于1。
  • C:从均值或加权和中减去的常数,影响阈值的大小。

2.3 C++示例

void cv::adaptiveThreshold(
    cv::InputArray src,          // 输入灰度图像
    cv::OutputArray dst,         // 输出二值图像
    double maxValue,             // 高于阈值的像素值
    int adaptiveMethod,          // 自适应阈值算法
    int thresholdType,           // 阈值类型
    int blockSize,               // 邻域大小
    double C                     // 从均值中减去的常数
);

src: 输入灰度图像。
dst: 输出二值图像。
maxValue: 高于阈值的像素值,通常为255。
adaptiveMethod: 自适应阈值算法,可以是cv::ADAPTIVE_THRESH_MEAN_C(根据邻域均值确定阈值)或cv::ADAPTIVE_THRESH_GAUSSIAN_C(根据邻域加权平均确定阈值)。
thresholdType: 阈值类型,通常为cv::THRESH_BINARY或cv::THRESH_BINARY_INV,分别表示二值化和反二值化。
blockSize: 邻域大小,用于计算自适应阈值。
C: 从均值中减去的常数,可以调整阈值的灵敏度。

三、自适应二值化进阶版

         上述二值化针对的图像对象均为前景+背景两类图片,但假如图像含有前景、中景与背景三类且将前景与背景二值化同一种,将中景二值化为另一种,此时采用内置二值化函数则无效,基于此,博主根据大律法原理写出了适用于此种情况的自适应二值化方法,代码如下:


//自适应找间断点
int therr(Mat Rois1)
{
    int ones,twos;
    double var, stds, q1, q2, q3;//q为自适应时三个区域比重
    double minVal, maxVal;//定义G通道最小值 最大值
    Point minLoc, maxLoc;
    minMaxLoc(Rois1, &minVal, &maxVal, &minLoc, &maxLoc);
    //初始256位的全像素最小值数组 用来存放存在的像素 间隔点在这里选择
    int ing_min = static_cast<int>(minVal); //G通道最小值
    int ing_max = static_cast<int>(maxVal);//G通道最大值
    int initial[256] = { ing_min };
    for (int i = 0; i < 256; i++)
    {
        initial[i] = minVal;
    }
    //更新保存存在的数组且将二维变一维
    //G通道顺序排列 定义变长的数组
    //cout <<"minVal:"<<minVal<<"  maxVal:"<<maxVal<<endl;
    int len = Rois1.cols * Rois1.rows;//二维展开后一维数组长度
    //qDebug() << "len长度:" << len << endl;
    vector<int>   array(len);//声明变长数组
    for (int i = 0; i <  (Rois1.rows); i++)
    {
        for ( int j = 0; j < (Rois1.cols); j++)
        {
            //更新保存存在元素的数组
            unsigned int temp = (i * Rois1.cols + j);
            initial[(Rois1.data[temp + 1])] = Rois1.data[temp + 1];
            //二维图像变一维数组
            array[i * Rois1.cols + j] = Rois1.data[temp + 1];
        }
    }
    //qDebug()<< "排序开始:" << endl;
    //元素进行从小到大排列 公式法
    sort(array.begin(), array.end());
    //qDebug() << "排序完毕:" << endl;
    vector <int> ::iterator it;
    vector <int> ::iterator jt;

    //qDebug()<< "计算间断点:" << endl;
    //利用分割点计算方差
    //(int ing_min,int ing_max,int *p_array,int initial[256]) 像素最小 像素最大  像素数组 存在像素的数组(256位)
    for (int i = ing_min + 1; i < ing_max; i = i + 3)
    {
        for (int j = i + 4; j < ing_max + 1; j = j + 5)
        {
            int ii = initial[i], jj = initial[j];
            if ((jj < ii) || (ii == 0))
                continue;
            //array = array;
            //int one = find_index(array, 0, len - 1, ii);
            //int two = find_index(array, 0, len - 1, jj);

            it = std::find(array.begin(), array.end(), ii); //find找到的值 索引位置:it - array.begin()
            jt = std::find(array.begin(), array.end(), jj); //find找到的值 索引位置:jt - array.begin()

            //对数组三分类
            double one = it - array.begin(), two = jt - array.begin();
            vector<int>   data_1(one);//声明变长数组1
            vector<int>   data_2(two - one);//声明变长数组2
            vector<int>   data_3(len - two);//声明变长数组3
            //复制1
            for (int i = 0; i < one; i++)
            {
                data_1[i] = array[i];
            }
            //cout << "前复制完毕:" << endl;
            //复制2
            for (int i = one; i < two; i++)
            {
                data_2[i - one] = array[i];
            }
            //cout << "中复制完毕:" << endl;
            //复制3
            for (int i = two; i < len; i++)
            {
                data_3[i - two] = array[i];
            }
            //cout << "后复制完毕:" << endl;

            //一
            double sum = 0;
            double std_1;
            for (int i = 0; i < one; i++)
            {
                if (i == 0)
                {
                    sum = data_1[i];
                }
                else
                {
                    sum = sum + data_1[i];
                }
            }
            double ave_1 = sum / one;
            //cout << "ave_1:" << ave<< endl;
            for (int i = 0; i < one; i++)
            {
                if (i == 0)
                {
                    sum = (data_1[i] - ave_1) * (data_1[i] - ave_1);
                }
                else
                {
                    sum = sum + (data_1[i] - ave_1) * (data_1[i] - ave_1);
                }
            }
            q1 = one / len;
            std_1 = sum * q1 / one;
            //cout << "std_1:" << std_1 << endl;

            //二
            double std_2;
            for (int i = 0; i < (two - one); i++)
            {
                if (i == 0)
                {
                    sum = data_2[i];
                }
                else
                {
                    sum = sum + data_2[i];
                }
            }
            double ave_2 = (sum / (two - one));
            //cout << "ave_2:" << ave<< endl;
            for (int i = 0; i < (two - one); i++)
            {
                if (i == 0)
                {
                    sum = (data_2[i] - ave_2) * (data_2[i] - ave_2);
                }
                else
                {
                    sum = sum + (data_2[i] - ave_2) * (data_2[i] - ave_2);
                }
            }
            q2 = (two - one) / len;
            std_2 = sum * q2 / (two - one);
            //cout << "std_2:" << std_2 << endl;

            //三
            double std_3;
            for (int i = 0; i < (len - two); i++)
            {
                if (i == 0)
                {
                    sum = data_3[i];
                }
                else
                {
                    sum = sum + data_3[i];
                }
            }
            double ave_3 = (sum / (len - two));
            //cout << "ave_2:" << ave<< endl;
            for (int i = 0; i < (len - two); i++)
            {
                if (i == 0)
                {
                    sum = (data_3[i] - ave_3) * (data_3[i] - ave_3);
                }
                else
                {
                    sum = sum + (data_3[i] - ave_3) * (data_3[i] - ave_3);
                }
            }
            q3 = (len - two) / len;
            std_3 = sum * q3 / (len - two);
            //cout << "std_3:" << std_3 << endl;
            stds = std_3 + std_2 + std_1;
            //cout << "std:" << std << endl;
            //迭代间隔与最小类内方差 其中间隔可以用i j也可以用one two置换 但one是位置索引 需要array[ones]得到分解值
            if (i == ing_min + 1)
            {
                ones = i;
                twos = j;
                var = stds;
            }
            else
            {
                if (stds < var)
                {
                    ones = i;
                    twos = j;
                    var = stds;
                }
            }
            //间隔点 第一个:array[ones] 第二个:array[twos]
            //cout << i << " " << j << " " << ones << " " << twos << " " << var << endl;
        }
    }
    return ones, twos;
}
//利用间断点去二值化
Mat Binarization(Mat img)
{
    Mat Rois[3], Rois0, Rois1;
    split(img, Rois);//彩图通道分离 int channels = image.channels();
    Rois1 = Rois[1];//分离第二个通道

    //三分类 划分出间断点
    int ones,twos =therr( Rois1);

    //根据间断点二值化
    Mat mask1, mask2, Mask;
    threshold(Rois1, mask1, ones, 255, THRESH_BINARY_INV);
    threshold(Rois1, mask2, twos, 255, THRESH_BINARY);
    Mask = mask1 + mask2;
    return Mask;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值