寫在前面的話
老式黑白電視只有一個通道的圖像數據,通過灰度值在黑白電視上顯示灰度圖像,即圖像的亮度,是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
方式二:
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
