文章目录

  • 1. 色彩空间简介
  • 2. The RGB Color Space
  • 3. The LAB Color Space
  • 4. The YCrCb Color Space
  • 5. The HSV Color Space
  • 6. 获取鼠标位置所处的像素信息
  • 参考



1. 色彩空间简介

本文将介绍计算机视觉中常用到的色彩空间,并将其用于图像分割中。我们都知道在绘画时可以使用红色、黄色和蓝色这三种原色生成不同的颜色,这些颜色就定义了一个色彩空间。我们将品红色的量定义为X轴、青色的量定义为Y轴、黄色的量定义为Z轴,这样就得到一个三维空间,每种可能的颜色都在这个三维空间中对应于唯一的位置。

opencv bmp 调色板 opencv颜色空间_色彩空间

但是,这不是唯一的一个色彩空间。例如,当在计算机上显示颜色时,通常使用RGB(红色、绿色、蓝色)色彩空间,或使用色相、饱和度和明度表示的HSV色彩空间。首先来看本文将用到的样例图:

opencv bmp 调色板 opencv颜色空间_计算机视觉_02

上图是不同光照下得到的物体成像,左图(室外)和右图(室内)。


2. The RGB Color Space

在RGB色彩空间中,所有颜色都是分量R、G和B的线性组合,而某颜色的该三个分量值与物体表面的光照有关。现将三个通道分离:

#include<opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main(int argc, char** argv) {
	// OpenCV读取的默认格式为BGR
	Mat image = imread("figures/rub01.jpg");
	// 存放各通道值	
	vector<Mat> listChannel;	
	// 使用split函数分离通道
	split(image, listChannel);	
	for (int i = 0; i < listChannel.size(); ++i) {
		imwrite("channel" + to_string(i) + "_.jpg", listChannel[i]);
	}
	return 0;
}

输入图像及输出结果:

opencv bmp 调色板 opencv颜色空间_opencv bmp 调色板_03

在将原彩色图像的通道分离后,由于此时的图像是单通道的,所以以灰度图的形式显示。从上述结果我们可以观察到,在原图为蓝色的地方,蓝色通道图中对应位置接近于白色,其他颜色亦如此。


3. The LAB Color Space

在LAB色彩空间中含有三个分量:L(亮度)、A和B表示颜色对立度,A表示从绿色到洋红色的分量、B表示从蓝色到黄色的分量。现将三个通道分离:

#include<opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main(int argc, char** argv) {
	// OpenCV读取的默认格式为BGR
	Mat image = imread("figures/rub01.jpg");
	// 存放LAB色彩空间的图像
	Mat image_lab;
	// 通道转换
	cvtColor(image, image_lab, COLOR_BGR2Lab);
	// 存放各通道值
	vector<Mat> listChannel;
	// 使用split函数分离通道
	split(image_lab, listChannel);
	for (int i = 0; i < listChannel.size(); ++i) {
		imwrite("channel" + to_string(i) + "_.jpg", listChannel[i]);
	}
	return 0;
}

输入图像及输出结果:

opencv bmp 调色板 opencv颜色空间_#include_04

可以看到光照的变换主要影响了L分量的值,而对其他两个分量的值影响不大。


4. The YCrCb Color Space

YCrCb色彩空间是从BGR色彩空间派生而来,其中Y表示RGB色彩空间下亮度经伽马矫正后的值,Cr分量表示红色离亮度值有多远,Cb表示蓝色离亮度值有多远。现将三个通道分离:

#include<opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main(int argc, char** argv) {
	// OpenCV读取的默认格式为BGR
	Mat image = imread("figures/rub01.jpg");
	// 存放YCrCb色彩空间的图像
	Mat image_lab;
	// 通道转换
	cvtColor(image, image_lab, COLOR_BGR2YCrCb);
	// 存放各通道值
	vector<Mat> listChannel;
	// 使用split函数分离通道
	split(image_lab, listChannel);
	for (int i = 0; i < listChannel.size(); ++i) {
		imwrite("channel" + to_string(i) + "_.jpg", listChannel[i]);
	}
	return 0;
}

输入图像及输出结果:

opencv bmp 调色板 opencv颜色空间_opencv_05

如上图,Y通道的结果与LAB类似,我们以Cr为例说明,其表示红色离亮度值有多远,原红色位置在Cr通道的图像中的相应位置更白;Cb通道的值也具有类似性质。


5. The HSV Color Space

HSV色彩空间由H(色相)、S(饱和度)和V(明度)组成。现将三个通道分离:

#include<opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main(int argc, char** argv) {
	// OpenCV读取的默认格式为BGR
	Mat image = imread("figures/rub01.jpg");
	// 存放HSV色彩空间的图像
	Mat image_lab;
	// 通道转换
	cvtColor(image, image_lab, COLOR_BGR2HSV);
	// 存放各通道值
	vector<Mat> listChannel;
	// 使用split函数分离通道
	split(image_lab, listChannel);
	for (int i = 0; i < listChannel.size(); ++i) {
		imwrite("channel" + to_string(i) + "_.jpg", listChannel[i]);
	}
}

输入图像及输出结果:

opencv bmp 调色板 opencv颜色空间_opencv bmp 调色板_06


两幅图像的H分量十分接近,表明光照色相的影响不大,S分量亦如此。而V表示明度,在不同光照条件下V分量的差别较大。


6. 获取鼠标位置所处的像素信息

#include<iostream>
#include<opencv2/opencv.hpp>

using namespace cv;
using namespace std;

// 全局变量
Mat image, placeholder;

// 鼠标响应事件
void onMouse(int event, int x, int y, int flags, void* userdata) {
	// 移动
	if (event == EVENT_MOUSEMOVE) {
		// 存放bgr各通道的值,Vec3b表示8U类型的RGB彩色图像格式(0-255)
		// 读取位置为(y,x)处的像素值,并将各通道值存放在向量bgrPixel中
		// 然后将向量转换成Mat格式
		Vec3b bgrPixel(image.at<Vec3b>(y, x));
		Mat3b bgr(bgrPixel);
		// 存放LAB、YCrCb、HSV各通道的值
		Mat3b lab, ycrcb, hsv;
		// 将颜色通道由BGR转换到其它色彩空间
		cvtColor(bgr, lab, COLOR_BGR2Lab);
		cvtColor(bgr, ycrcb, COLOR_BGR2YCrCb);
		cvtColor(bgr, hsv, COLOR_BGR2HSV);
		// 同上
		Vec3b labPixel(lab.at<Vec3b>(0, 0));
		Vec3b ycrcbPixel(ycrcb.at<Vec3b>(0, 0));
		Vec3b hsvPixel(hsv.at<Vec3b>(0, 0));
		// 定义占位符,根据原图大小定义高,宽为400,格式为三通道的8U图像格式
		placeholder = Mat::zeros(image.rows, 400, CV_8UC3);
		// 填充占位符,分别显示各通道的值
		putText(placeholder, format("BGR [%d, %d, %d]", bgrPixel[0], bgrPixel[1], bgrPixel[2]), Point(20, 70), FONT_HERSHEY_COMPLEX, .9, Scalar(255, 255, 255), 1);
		putText(placeholder, format("LAB [%d, %d, %d]", labPixel[0], labPixel[1], labPixel[2]), Point(20, 140), FONT_HERSHEY_COMPLEX, .9, Scalar(255, 255, 255), 1);
		putText(placeholder, format("YCrCb [%d, %d, %d]", ycrcbPixel[0], ycrcbPixel[1], ycrcbPixel[2]), Point(20, 210), FONT_HERSHEY_COMPLEX, .9, Scalar(255, 255, 255), 1);
		putText(placeholder, format("HSV [%d, %d, %d]", hsvPixel[0], hsvPixel[1], hsvPixel[2]), Point(20, 280), FONT_HERSHEY_COMPLEX, .9, Scalar(255, 255, 255), 1);
		// size
		Size size1 = image.size();
		Size size2 = placeholder.size();
		// 合并结果并使其显示在单幅图像上
		Mat combinedResult(size1.height, size1.width + size2.width, CV_8UC3);
		// 左边显示图像
		Mat left(combinedResult, Rect(0, 0, size1.width, size1.height));
		image.copyTo(left);
		// 右边显示像素值信息
		Mat right(combinedResult, Rect(size1.width, 0, size2.width, size2.height));
		placeholder.copyTo(right);
		// 显示
		imshow("Press P for Previous, N for Next Image", combinedResult);
	}
}

int main(int argc, char** argv) {
	// 定义图像索引、图像数量、格式化输出
	int image_ind = 0;
	int num_images = 10;
	char filename[20];
	// 格式化输出
	sprintf_s(filename, "figures/rub%02d.jpg", image_ind % num_images);
	// 读取图像
	image = imread(filename);
	// resize
	Size rsize(400, 400);
	resize(image, image, rsize);
	// 创建空窗体
	namedWindow("Press P for Previous, N for Next Image", WINDOW_AUTOSIZE);
	// 创建鼠标的回调函数
	setMouseCallback("Press P for Previous, N for Next Image", onMouse);
	// 显示图像
	imshow("Press P for Previous, N for Next Image", image);
	// 获取键盘操作
	while (1)
	{
		// 键盘响应
		char k = waitKey(1) & 0xFF;
		// 退出
		if (k == 27) {
			break;
		}
		// 下一幅图像
		if (k == 'n') {
			// 索引加1
			++image_ind;
			// 格式化输出
			sprintf_s(filename, "figures/rub%02d.jpg", image_ind % num_images);
			// 重新读取图像
			image = imread(filename);
			// resize
			resize(image, image, rsize);
		}
		// 上一幅图像
		else if (k == 'p') {
			// 索引减1
			--image_ind;
			// 格式化输出
			sprintf_s(filename, "figures/rub%02d.jpg", image_ind % num_images);
			// 重新读取图像
			image = imread(filename);
			// resize
			resize(image, image, rsize);
		}
	}
	return 0;
}

输出结果:

opencv bmp 调色板 opencv颜色空间_opencv_07

在结果中,左图显示输入图像,右图显示鼠标位置处像素各通道的值。注意:由于程序书写的缘故,必须移动鼠标后才能切换到上一幅或下一幅图像,否则会出错。