OpenCV的彩色图像的各通道显示
RGB图像
RGB彩色空间主要有两个彩色模型,一个是“加色(RGB)模型”,一个是“减色(CMY)模型”。加色模型又称“三基色模型”:RGB(Red/Green/Blue,红绿蓝),三基色可以混合成任意颜色,如下图示。减色模型主要是为了解决RGB模型对无源物体图像处理的复杂(特别是黑色)。自然界物体按照颜色光可分为:发光物体和无源物体。举个例子,在彩色印刷和彩色打印中,纸张不能发射光线而只能反射光线,因此,彩色印刷机和彩色打印机只能通过一些能够吸收特定光波和反射其他光波的油墨和颜料以及它们的不同的比例混合出来印出千变万化的颜色。油墨和颜料的三基色是CMY(Cyan/Magenta/Yellow,青/洋红/黄),其特点是用料越多颜色越深。理论上讲,等量的CMY可以合成黑色,但是实际上纯黑色很难合成。因此打印机或印刷机要专门提供黑色油墨。这也称为四色印刷,其模型也叫CMKY模型。如下图示。
本文使用OpenCV写了一个显示一张彩色图像的三通道分量图像。
1 #include<opencv2/opencv.hpp> 2 #include<opencv2/highgui.hpp> 3 #include<opencv2/imgproc.hpp> 4 #include<iostream> 5 6 using namespace std; 7 using namespace cv; 8 9 const int blue[] = { 0,0 }, green[] = { 1,1 }, red[] = { 2,2 }; // 通道索引 10 11 int main(int argc, char** argv) 12 { 13 Mat img = imread("1.jpg"); 14 if (img.empty()) 15 { 16 cout << "could not load image!" << endl; 17 } 18 imshow("src_image", img); 19 20 /* 方法1 */ 21 Mat Rimg(img.rows, img.cols, CV_8UC3, Scalar(0, 0, 0)); 22 Mat Gimg(img.rows, img.cols, CV_8UC3, Scalar(0, 0, 0)); 23 Mat Bimg(img.rows, img.cols, CV_8UC3, Scalar(0, 0, 0)); 24 25 double time0 = static_cast<double>(getTickCount()); 26 27 mixChannels(&img, 1, &Rimg, 1, red, 1); 28 mixChannels(&img, 1, &Gimg, 1, green, 1); 29 mixChannels(&img, 1, &Bimg, 1, blue, 1); 30 31 time0 = ((double)getTickCount() - time0) / getTickFrequency(); 32 cout << "\t此方法运行时间为: " << time0 << "秒" << endl; //输出运行时间 33 34 imshow("1.红色分量图像", Rimg); 35 imshow("1.绿色分量图像", Gimg); 36 imshow("1.蓝色分量图像", Bimg); 37 38 /* 方法2 */ 39 Mat rimg(img.rows, img.cols, CV_8UC3, Scalar(0, 0, 0)); 40 Mat gimg(img.rows, img.cols, CV_8UC3, Scalar(0, 0, 0)); 41 Mat bimg(img.rows, img.cols, CV_8UC3, Scalar(0, 0, 0)); 42 43 double time1 = static_cast<double>(getTickCount()); 44 45 for (int i = 0; i < img.rows; i++) { 46 uchar* data = img.ptr<uchar>(i); //获取第i行的首地址 47 uchar* rdata = rimg.ptr<uchar>(i); 48 uchar* gdata = gimg.ptr<uchar>(i); 49 uchar* bdata = bimg.ptr<uchar>(i); 50 for (int j = 0; j < img.cols*3; j += 3) { 51 bdata[j] = data[j]; 52 gdata[j + 1] = data[j + 1]; 53 rdata[j + 2] = data[j + 2]; 54 } 55 } 56 57 time1 = ((double)getTickCount() - time1) / getTickFrequency(); 58 cout << "\t此方法运行时间为: " << time1 << "秒" << endl; //输出运行时间 59 60 imshow("2.红色分量图像", rimg); 61 imshow("2.绿色分量图像", gimg); 62 imshow("2.蓝色分量图像", bimg); 63 64 waitKey(0); 65 return EXIT_SUCCESS; 66 }
运行示例如下:
HSI彩色空间
上述的RGB模型是工业界的一种颜色标准,它由颜色的发光原理设定,红绿蓝每个颜色通道各分为256阶亮度。除此之外,从人类的色视觉机理出发提出的HSI彩色模型,对开发基于彩色描述的图像处理方法也是一个较为理想的工具。HSI彩色模型指采用色调(Hue)、饱和度(Saturation)、强度(Intensity)来描述颜色的模型。该彩色空间是一个圆锥形的空间模型,圆锥型空间的竖直轴表示光强I,顶部最亮表示白色,底部最暗表示表示黑色,中间的为灰度。圆锥型空间中部的水平面圆周是表示色调H的角坐标。为了处理方便,经常要把RGB三基色表示的图像数据转换成HSI数据,其转换公式如下:
借此说一句,与HSI彩色模型十分接近的HSV模型。HSV模型指色调(hue)、饱和度(saturation)、明度(value)。二者区别在于,一种纯色的明度等于白色的明度,而纯色的亮度等于中度灰的亮度。RGB数据转换为HSV数据的公式如下:
上述两个模型的一些图表说明如下:
HSI彩色模型:
HSV模型:
OpenCV把RGB图像转化为HLS(HSI)图像,并输出HLS各通道的图像,其代码如下:
#include <opencv2/opencv.hpp> #include<iostream> using namespace std; using namespace cv; int main() { Mat img; img = imread("1.jpg"); imshow("原图像", img); Mat hlsimg; //转换为 HLS(HIS) 通道图像 cvtColor(img, hlsimg, COLOR_RGB2HLS); imshow("hlsImg", hlsimg); Mat hImg = Mat(img.size(), CV_8UC1); Mat lImg = Mat(img.size(), CV_8UC1); Mat sImg = Mat(img.size(), CV_8UC1); Mat dst[] = { hImg,lImg,sImg }; int fromTo[] = { 0,0,1,1,2,2 }; mixChannels(&hlsimg, 1, dst, 3, fromTo, 3); imshow("hImg", hImg); imshow("lImg", lImg); imshow("sImg", sImg); waitKey(); }
运行示例如下:
OpenCV把RGB图像转化为HSV图像,并输出HSV各通道的图像,其代码如下:
#include <opencv2/opencv.hpp> #include<iostream> using namespace std; using namespace cv; int main() { Mat img; img = imread("1.jpg"); imshow("原图像", img); Mat hsvImg; //转换为 HSV 通道图像 cvtColor(img, hsvImg, COLOR_BGR2HSV); imshow("hsvImg", hsvImg); Mat hImg = Mat(img.size(), CV_8UC1); Mat sImg = Mat(img.size(), CV_8UC1); Mat vImg = Mat(img.size(), CV_8UC1); Mat dst[] = { hImg,sImg,vImg }; int fromTo[] = { 0,0,1,1,2,2 }; mixChannels(&hsvImg, 1, dst, 3, fromTo, 3); imshow("hImg", hImg); imshow("sImg", sImg); imshow("vImg", vImg); waitKey();
}
运行示例:
【注】下面有一个颇为有趣的代码,有兴趣的可以试着玩一下:
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> using namespace cv; using namespace std; int main() { int key = 0; Mat srcImage; Mat logoImage; vector<Mat> channels; Mat imageBlueChannel; // logoImage = imread("dota_logo.jpg", 0); logoImage = imread("dota_jugg.jpg",0); // 灰度 srcImage = imread("dota_jugg.jpg"); if (!logoImage.data) { printf("Oh,no,读取logoImage错误~! \n"); return false; } if (!srcImage.data) { printf("Oh,no,读取srcImage错误~! \n"); return false; } split(srcImage, channels);//分离色彩通道 imageBlueChannel = channels.at(2); addWeighted(imageBlueChannel(Rect(0, 0, logoImage.cols, logoImage.rows)), 1, logoImage, 1.0, 0, imageBlueChannel(Rect(0, 0, logoImage.cols, logoImage.rows))); merge(channels, srcImage); imshow("BlueImage", imageBlueChannel); namedWindow(" <1>游戏原画+logo蓝色通道"); imshow(" <1>游戏原画+logo蓝色通道", srcImage); waitKey(key); return 0; }
附录
DICOM格式文件的资源网站: