行远-自迩

导航

2. 二值化

一、 二值化

     二值化就是让图像的像素点矩阵中的每个像素点的灰度值为0(黑色)或者255(白色),也就是让整个图像呈现只有黑和白的效果。在灰度化的图像中灰度值的范围为0~255,在二值化后的图像中的灰度值范围是0或者255。

      黑色:

               二值化后的R =  0

               二值化后的G =  0

               二值化后的B =  0

      白色:

               二值化后的R =  255

               二值化后的G =  255

               二值化后的B =  255

       那么一个像素点在灰度化之后的灰度值怎么转化为0或者255呢?比如灰度值为100,那么在二值化后到底是0还是255?这就涉及到取一个阀值的问题。

三、阈值求取算法

      1. 傻瓜法

       

 

      2. 大律二值算法

        将图像理解成255个图层,每一层分布了不同的像素,这些像素垂直叠加合成了一张完整的灰度图。就是找到一个合适的灰度值,大于这个值的我们将它称之为背景(灰度值越大像素越黑),小于这个值的我们

 将它称之为前景(灰度值越小像素越白)。

h:图像的宽度

w:图像的高度(h*w 得到图像的像素数量) 

t :灰度阈值(我们要求的值,大于这个值的像素我们将它的灰度设置为255,小于的设置为0)

n0:小于阈值的像素,前景

n1:大于等于阈值的像素,背景

n0 + n1 == h * w

w0:前景像素数量占总像素数量的比例

w0 = n0 / (h * w)

w1:背景像素数量占总像素数量的比例

w1 = n1 / (h * w)

w0 + w1 == 1 

u0:前景平均灰度

u0 = n0灰度累加和 / n0

u1:背景平均灰度

u1 = n1灰度累加和 / n1 

u:平均灰度

u = (n0灰度累加和 + n1灰度累加和) / (h * w) 根据上面的关系

u = w0 * u0 + w1 * u1

g:类间方差(那个灰度的g最大,哪个灰度就是需要的阈值t)

g = w0 * (u0 - u)^2 + w1 * (u1 - u)^2

根据上面的关系,可以推出:(这个一步一步推导就可以得到)

g = w0 * w1 * (u0 - u1) ^ 2

然后,遍历每一个灰度值,找到这个灰度值对应的 g

找到最大的 g 对应的 t;

 

算法实现

Mat Binary_OSTU(Mat img)
{
    // get height and width
    int width = img.cols;
    int height = img.rows;
    Mat out = Mat::zeros(height, width, CV_8UC1);
    double p0 = 0;
    double u0 = 0;
    double p1 = 0;
    double u1 = 0;
    double n0 = 0;
    double n1 = 0;
    int val;
    double max_sb = 0, sb = 0;
    int threshold = 0;
    for (int k = 0; k < 255; k++)
    {
        p0 = 0;
        u0 = 0;
        p1 = 0;
        u1 = 0;
        n0 = 0;
        n1 = 0;
        //分为两类
        for (int i = 0; i < height; i++)
        {
            for (int j = 0; j < width; j++)
            {
                val = (int)(img.at<uchar>(i, j));
                if (val < k)
                {
                    n0++;
                    u0 += val;
                }
                else
                {
                    n1++;
                    u1 += val;
                }
            }
        }
        //求出概率,均值
        u0 = u0 / n0;  //第一类均值
        u1 = u1 / n1;

        p0 = n0 / (width* height);
        p1 = n1 / (width* height);
        sb = p0 * p1*pow((u0 - u1), 2);

        if (sb > max_sb)
        {
            max_sb = sb;
            threshold = k;
        }
    }

    cout << "threshold:  " << threshold << endl;

    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            if ((int)img.at<uchar>(i, j) > threshold)
                out.at<uchar>(i, j) = 255;
            else
                out.at<uchar>(i, j) = 0;

        }
    }

    return out;
}

 参考文献:

                Otsu N. A threshold selection method from gray-level histogram. IEEE Trans,1979;SMC-9;62-66

                https://www.cnblogs.com/funfei/p/6943665.html

                https://zhuanlan.zhihu.com/p/95034826

posted on 2020-07-05 17:18  行远-自迩  阅读(1322)  评论(0编辑  收藏  举报