写在前面的话
老式黑白电视只有一个通道的图像数据,通过灰度值在黑白电视上显示灰度图像,即图像的亮度,是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.299⋅R+0.587⋅G+0.114⋅B
Cr = (R−Y)⋅0.713+delta
Cb = (B−Y)⋅0.564+delta
方式二:
RGB ↔ CIE
Y = 0.212671⋅R+0.715160⋅G+0.072169⋅B
opencv默认将RGB图像转成Gray的方式是
RGB ↔ GRAY
RGB[A] to Gray: Y = 0.299⋅R+0.587⋅G+0.114⋅B
效果如下:
使用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