Fork me on GitHub

【图像处理】图像灰度化

写在前面的话

老式黑白电视只有一个通道的图像数据,通过灰度值在黑白电视上显示灰度图像,即图像的亮度,是Y通道数据。

后来出现了彩色电视,为了兼容老式黑白电视,使用YCrCb(YUV)方式传输图像。

如下分析一下彩色图像转成灰度图的方法和原理。

 

彩色图和灰度图说明

彩色图像可以有4个通道,的BGR-[A](Blue Green Red Alpha)蓝,绿,红和透明4个通道。

也可以是BGR(Blue Green Red)蓝,绿,红3个通道三通道。

BGR的数据存储格式如下所示:

 

 灰度图是单通道图像,数据存储格式如下所示:

 

转化分析

在opencv中可以使用API将彩色图像转成灰度图。

将彩色图像转成灰度图的方式有如下两种方式

方式一:

RGB  YCrCb JPEG (or YCC)

Y = 0.299R+0.587G+0.114B

Cr = (RY)0.713+delta

Cb = (BY)0.564+delta

 

方式二:

RGB ↔ CIE

 Y = 0.212671R+0.715160G+0.072169B

 

 

opencv默认将RGB图像转成Gray的方式是

RGB  GRAY

RGB[A] to Gray: Y = 0.299R+0.587G+0.114B

 

效果如下:

 

 

使用opencv的API进行灰度化处理代码

    void toGray(cv::Mat& inImage, cv::Mat& outGrayImage) {
        cv::cvtColor(inImage, outGrayImage, cv::COLOR_RGB2GRAY);

        cv::imshow("raw-image", inImage);
        cv::imshow("gray-image", outGrayImage);
    }

遍历图像矩阵,使用CIE方式将BGR转Gray的公式计算如下:

    void toGrayImpl(cv::Mat &inImage, cv::Mat &outGrayImage) {
        outGrayImage = cv::Mat::zeros(inImage.rows, inImage.cols, CV_8UC1);
        for (int i = 0; i < inImage.rows; ++i) {
            unsigned char *ptSrcRow = inImage.ptr<uchar>(i);
            unsigned char *ptDstRow = outGrayImage.ptr<uchar>(i);
            for (int j = 0; j < inImage.cols; ++j) {
                int pos = j * inImage.channels();
                // Y = B * 0.072169f + G * 0.715160f + R * 0.212671f;
                ptDstRow[j] = cv::saturate_cast<uchar>(0.212671f * (float) ptSrcRow[pos + 2] +
                                                       0.71516f * (float) ptSrcRow[pos + 1] +
                                                       0.072169f * (float) ptSrcRow[pos]);
            }
        }

        cv::imshow("raw-image", inImage);
        cv::imshow("gray-image", outGrayImage);
    }

 通过YCrCb方式转成灰度图:

    void toGrayImplByYCrCb(cv::Mat &inImage, cv::Mat &outGrayImage) {
        outGrayImage = cv::Mat::zeros(inImage.rows, inImage.cols, CV_8UC1);
        for (int i = 0; i < inImage.rows; ++i) {
            for (int j = 0; j < inImage.cols; ++j) {
                // BGR -> Gray
                // Y = B * 0.114 + G * 0.587 + R * 0.299;
                outGrayImage.at<uchar>(i, j) = 0.299 * (float) inImage.at<cv::Vec3b>(i, j)[2]
                                               + 0.587 * (float) inImage.at<cv::Vec3b>(i, j)[1]
                                               + 0.114 * (float) inImage.at<cv::Vec3b>(i, j)[0];
            }
        }

        cv::imshow("raw-image", inImage);
        cv::imshow("gray-image-YCrCb", outGrayImage);
    }

另外一种方式,直接使用opencv的API将BGR图像转成YCrCb格式,提取Y通道的数据imageList[0]

    void toGrayByYCrCbSplit(cv::Mat &inImage, cv::Mat &outGrayImage) {
        cv::cvtColor(inImage, outGrayImage, cv::COLOR_BGR2YCrCb);
        std::vector<cv::Mat> imageList;
        cv::split(outGrayImage, imageList);

        cv::imshow("raw-image", inImage);
        cv::imshow("gray-image-Y", imageList[0]);
        cv::imshow("gray-image-Cr", imageList[1]);
        cv::imshow("gray-image-Cb", imageList[2]);
    }

将彩色图转成YCrCb,如下是Y通道的灰度图,Cr是R通道和Y的差值,Cb是B通道和Y的差值。

  

参考文档:

https://docs.opencv.org/3.3.1/de/d25/imgproc_color_conversions.html#color_convert_rgb_gray

posted @ 2021-09-07 17:53  Mr.YF  阅读(1508)  评论(0编辑  收藏  举报