图像处理之灰度图像转换与二值化

灰度图像与二值化

问题描述:将彩色图像转换为灰度图像并进行二值化处理

代码如下(示例):

#include <opencv2/opencv.hpp>

cv::Mat BGR2GRAY(cv::Mat img)
{ 
    int width = img.cols;
    int height = img.rows;
	
	// CV_8UC1代表单通道的8bit array数组
    cv::Mat new_image = cv::Mat::zeros(height, width, CV_8UC1);

    for (int i = 0; i < height; ++i){ 
        for (int j = 0; j < width; ++j){ 
        	// 转换公式 img_gray = 0.2126 * R + 0.7152 * G + 0.0722 * B;
        	// 需要注意图像格式输出的是整数
            new_image.at<uchar>(i, j) = 0.2126 * (float)img.at<cv::Vec3b>(i, j)[2]\
                    + 0.7152 * (float)img.at<cv::Vec3b>(i, j)[1]\
                    + 0.0722 * (float)img.at<cv::Vec3b>(i, j)[0];
        }
    }
    return new_image;
}

cv::Mat Binarize(cv::Mat img, int thres)
{ 
    int width = img.cols;
    int height = img.rows;

    cv::Mat new_image = cv::Mat::zeros(height, width, CV_8UC1);

    for (int i = 0; i < height; ++i){ 
        for (int j = 0; j < width; ++j){ 
            if (img.at<uchar>(i, j) > thres)
            { 
                new_image.at<uchar>(i, j) = 255;
            }
            else
            { 
                img.at<uchar>(i, j) = 0;
            }
        }
    }
    return new_image;
}

cv::Mat Binarize_Otsu(cv::Mat img)
{ 
    int width = img.cols;
    int height = img.rows;
    double count0 = 0, count1 = 0;
    double sum_val0 = 0, sum_val1 = 0;
    double sigma_max = 0, sigma = 0;
    int thres = 0;
    int val = 0;

    for (int th = 0; th < 255; ++th)
    { 
        for (int i = 0; i < height; ++i)
        { 
            count0 = 0;    // 每次循环重置参数
            count1 = 0;
            sum_val0 = 0;
            sum_val1 = 0;
            for (int j = 0; j < width; ++j)
            { 
                val = (int)img.at<uchar>(i, j);
                if (val < th)
                { 
                    count0++;
                    sum_val0 += val;
                }
                else
                { 
                    count1++;
                    sum_val1 += val;
                }
            }
        }
        sum_val0 /= count0;
        sum_val1 /= count1;
        count0 /= (height * width);
        count1 /= (height * width);
        sigma = count0 * count1 * pow((sum_val0 - sum_val1), 2);

        if (sigma > sigma_max)
        { 
            sigma_max = sigma;
            thres = th;
        }
    }

    std::cout << "threshold: " << thres << std::endl;

    cv::Mat new_image = cv::Mat::zeros(height, width, CV_8UC1);

    for (int i = 0; i < height; ++i){ 
        for (int j = 0; j < width; ++j){ 
            if (img.at<uchar>(i, j) > thres)
            { 
                new_image.at<uchar>(i, j) = 255;
            }
            else
            { 
                img.at<uchar>(i, j) = 0;
            }
        }
    }
    return new_image;
}

int main()
{ 
    cv::Mat img = cv::imread("../Neopolitan.png", cv::IMREAD_COLOR);

    // BGR2GRAY
    cv::Mat gray_image = BGR2GRAY(img);
    cv::Mat new_image = Binarize(gray_image, 128);
	// cv::Mat new_image = Binarize_Otsu(gray_image); // 大津二值化
	
    cv::imwrite("../1-10/Neopolitan2.png", gray_image);
    cv::imwrite("../1-10/Neopolitan3.png", new_image);
    // cv::imwrite("../1-10/Neopolitan4.png", new_image);
    cv::waitKey(0);
    cv::destroyAllWindows();

    return 0;
}
输入图像 (Neopolitan.png)灰度图像 (Neopolitan2.png)二值化图像 (Neopolitan3.png)大津二值化图像 (Neopolitan4.png)
《图像处理之灰度图像转换与二值化》《图像处理之灰度图像转换与二值化》《图像处理之灰度图像转换与二值化》《图像处理之灰度图像转换与二值化》

Vec3b

Vec3b可以看作是vector<uchar, 3>,简单而言就是一个uchar类型的,长度为3的vector向量。

由于在OpenCV中,使用imread读取到的Mat图像数据,都是用uchar类型的数据存储,对于RGB三通道的图像,每个点的数据都是一个Vec3b类型的数据。使用at定位方法如下:

img.at<Vec3b>(row, col)[0] = 255;  // 这是指修改B通道数据
img.at<Vec3b>(row, col)[1] = 255;  // 这是指修改G通道数据
img.at<Vec3b>(row, col)[2] = 255;  // 这是指修改R通道数据

大津二值化

大津算法,也被称作最大类间方差法,是一种可以自动确定二值化中阈值的算法。即,使得类内方差类间方差的比值最大的阈值即为大津二值化阈值。

我们可以通过以下的参数来计算大津二值化阈值:

  • 我们可以通过阈值 t h th th将图像灰度值分为两类,小于阈值 t h th th的类记作 0 0 0,大于阈值 t h th th的类记作 1 1 1
  • w 0 w_0 w0 w 1 w_1 w1分别代表 0 0 0类和 1 1 1类中的灰度值数目占总灰度值数目(也即像素个数)的比率(满足 w 0 + w 1 = 1 w_0+w_1=1 w0+w1=1);
  • S 0 2 {S_0}^2 S02 S 1 2 {S_1}^2 S12分别代表 0 0 0类和 1 1 1类中灰度值的方差;
  • M 0 M_0 M0 M 1 M_1 M1分别代表 0 0 0类和 1 1 1类中灰度值的平均值;

基于上述定义,我们便可以根据下述公式计算出类内方差类间方差,即:

  • 类内方差: S w 2 = w 0   S 0 2 + w 1   S 1 2 {S_w}^2=w_0\ {S_0}^2+w_1\ {S_1}^2 Sw2=w0 S02+w1 S12
  • 类间方差: S b 2 = w 0   ( M 0 − M t ) 2 + w 1   ( M 1 − M t ) 2 = w 0   w 1   ( M 0 − M 1 ) 2 {S_b}^2 = w_0 \ (M_0 – M_t)^2 + w_1\ (M_1 – M_t)^2 = w_0\ w_1\ (M_0 – M_1) ^2 Sb2=w0 (M0Mt)2+w1 (M1Mt)2=w0 w1 (M0M1)2
  • 图像所有灰度值的方差: S t 2 = S w 2 + S b 2 = 常数 {S_t}^2 = {S_w}^2 + {S_b}^2 = \text{常数} St2=Sw2+Sb2=常数

根据上述公式,我们可以类内方差类间方差的比值,并用分离度 X X X来表示:

X = S b 2 S w 2 = S b 2 S t 2 − S b 2 X = \frac{ {S_b}^2}{ {S_w}^2} = \frac{ {S_b}^2}{ {S_t}^2 – {S_b}^2} X=Sw2Sb2=St2Sb2Sb2

也就是说:
arg ⁡ max ⁡ t   X = arg ⁡ max ⁡ t   S b 2 \arg\max\limits_{t}\ X=\arg\max\limits_{t}\ {S_b}^2 argtmax X=argtmax Sb2
换言之,如果使 S b 2 = w 0   w 1   ( M 0 − M 1 ) 2 {S_b}^2={w_0}\ {w_1}\ (M_0 – M_1)^2 Sb2=w0 w1 (M0M1)2最大,就可以得到最好的二值化阈值 t h th th

    原文作者:hahahanba
    原文地址: https://blog.csdn.net/hahahanba/article/details/123319714
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞