使用OpenCV实现RGB、HSI、CMYK颜色空间的转换

RGB to HSI、CMYK的代码实现

前言:

在之前博文的基础上,我使用OpenCV2实现了RGB颜色空间向HIS、CMYK转换的代码。下列链接为各种经典颜色空间的介绍及转换公式的介绍。

http://blog.youkuaiyun.com/solomon1558/article/details/43772147

1. RGB to HIS

    HSI与RGB颜色空间可以进行相互转换。RGB转换到HSI 的计算公式如下:首先给定RGB颜色空间的值(R,G,B),其中R,G,B∈[ 0,2 5 5],则转换到HSI  空间的(H,S,I)值的计算如下:设将(R ,G ,B)归一化得(R',G',B')为:

 

int rgb2hsi(Mat &image,Mat &hsi){
	if(!image.data){
		cout<<"Miss Data"<<endl;
		return -1;
	}
	int nl = image.rows;
	int nc = image.cols;
	if(image.isContinuous()){
		nc = nc*nl;
		nl = 1;
	}
	for(int i = 0;i < nl;i++){
		uchar *src = image.ptr<uchar>(i);
		uchar *dst = hsi.ptr<uchar>(i);
		for(int j = 0;j < nc;j++){
			float b = src[j*3]/255.0;
			float g = src[j*3+1]/255.0;
			float r = src[j*3+2]/255.0;
			float num = (float)(0.5*((r-g)+(r-b)));
			float den = (float)sqrt((r-g)*(r-g)+(r-b)*(g-b));
			float H,S,I;
			if(den == 0){	//分母不能为0
				H = 0;
			}
			else{
				double theta = acos(num/den);
				if(b <= g)
					H = theta/(PI*2);
				else
					H = (2*PI - theta)/(2*PI);
			}
			double minRGB = min(min(r,g),b);
			den = r+g+b;
			if(den == 0)	//分母不能为0
				S = 0;
			else
				S = 1 - 3*minRGB/den;
			I = den/3.0;
			//将S分量和H分量都扩充到[0,255]区间以便于显示;
			//一般H分量在[0,2pi]之间,S在[0,1]之间
			dst[3*j] = H*255;
			dst[3*j+1] = S*255;
			dst[3*j+2] = I*255;
		}
	}
	return 0;
}

【注】:

    程序中将S分量和H分量都扩充到[0,255]区间以便于显示;

    一般H分量在[0,2pi]之间,S在[0,1]之间。


2.   RGB to CMYK

    给定RGB颜色空间的值(R,G,B),其中R,G ,B∈ [0, 2 5 5],则转换到CMYK 空间的(C,M,Y,K)值的计算如下:

 

【注】式中,maxG是每个矢量分量的最大允许值(255);C , M , Y , K ∈ [ 0,255]。

int rgb2cmyk( Mat &image,Mat &cmyk){
	if(!image.data){
		cout<<"Miss Data"<<endl;
		return -1;
	}
	int nl = image.rows;	//行数
	int nc = image.cols;	//列数
	if(image.isContinuous()){	//没有额外的填补像素
		nc = nc*nl;
		nl = 1;					//It is now a 1D array
	}
	//对于连续图像,本循环只执行1次
	for(int i=0;i<nl;i++){
		uchar *data = image.ptr<uchar>(i);
		uchar *dataCMYK = cmyk.ptr<uchar>(i);
		for(int j = 0;j < nc;j++){
			uchar b = data[3*j];
			uchar g = data[3*j+1];
			uchar r = data[3*j+2];
			uchar c = 255 - r;
			uchar m = 255 - g;
			uchar y = 255 - b;
			uchar k = min(min(c,m),y);
			dataCMYK[4*j] = c - k;
			dataCMYK[4*j+1] = m  - k;
			dataCMYK[4*j+2] = y  - k;
			dataCMYK[4*j+3] = k;
		}
	}
	return 0;
}


3. 完整的工程

#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\opencv.hpp>
#include<vector>
#define PI 3.1416
#define min(a,b) (a<b?a:b)
using namespace std;
using namespace cv;

int rgb2hsi(Mat &image,Mat &hsi){
	if(!image.data){
		cout<<"Miss Data"<<endl;
		return -1;
	}
	int nl = image.rows;
	int nc = image.cols;
	if(image.isContinuous()){
		nc = nc*nl;
		nl = 1;
	}
	for(int i = 0;i < nl;i++){
		uchar *src = image.ptr<uchar>(i);
		uchar *dst = hsi.ptr<uchar>(i);
		for(int j = 0;j < nc;j++){
			float b = src[j*3]/255.0;
			float g = src[j*3+1]/255.0;
			float r = src[j*3+2]/255.0;
			float num = (float)(0.5*((r-g)+(r-b)));
			float den = (float)sqrt((r-g)*(r-g)+(r-b)*(g-b));
			float H,S,I;
			if(den == 0){	//分母不能为0
				H = 0;
			}
			else{
				double theta = acos(num/den);
				if(b <= g)
					H = theta/(PI*2);
				else
					H = (2*PI - theta)/(2*PI);
			}
			double minRGB = min(min(r,g),b);
			den = r+g+b;
			if(den == 0)	//分母不能为0
				S = 0;
			else
				S = 1 - 3*minRGB/den;
			I = den/3.0;
			//将S分量和H分量都扩充到[0,255]区间以便于显示;
			//一般H分量在[0,2pi]之间,S在[0,1]之间
			dst[3*j] = H*255;
			dst[3*j+1] = S*255;
			dst[3*j+2] = I*255;
		}
	}
	return 0;
}

int rgb2cmyk( Mat &image,Mat &cmyk){
	if(!image.data){
		cout<<"Miss Data"<<endl;
		return -1;
	}
	int nl = image.rows;	//行数
	int nc = image.cols;	//列数
	if(image.isContinuous()){	//没有额外的填补像素
		nc = nc*nl;
		nl = 1;					//It is now a 1D array
	}
	//对于连续图像,本循环只执行1次
	for(int i=0;i<nl;i++){
		uchar *data = image.ptr<uchar>(i);
		uchar *dataCMYK = cmyk.ptr<uchar>(i);
		for(int j = 0;j < nc;j++){
			uchar b = data[3*j];
			uchar g = data[3*j+1];
			uchar r = data[3*j+2];
			uchar c = 255 - r;
			uchar m = 255 - g;
			uchar y = 255 - b;
			uchar k = min(min(c,m),y);
			dataCMYK[4*j] = c - k;
			dataCMYK[4*j+1] = m  - k;
			dataCMYK[4*j+2] = y  - k;
			dataCMYK[4*j+3] = k;
		}
	}
	return 0;
}
int main(){
	Mat img = imread("E:\\CV视频处理工作室\\Test_Photo\\lena_1.jpg");
	if(!img.data){
		cout<<"Miss Data"<<endl;
		return -1;
	}
	Mat img_cmyk,img_hsi;
	Mat img_hsv;
	vector <Mat> vecRgb,vecHsi,vecHls,vecHsv,vecCmyk;
	img_hsv.create(img.rows,img.cols,CV_8UC3);
	Mat img_hls;
	img_hls.create(img.rows,img.cols,CV_8UC3);
	//生成与输入图像尺寸一样的4通道cmyk图像
	img_cmyk.create(img.rows,img.cols,CV_8UC4);
	img_hsi.create(img.rows,img.cols,CV_8UC3);
	rgb2cmyk(img,img_cmyk);
	rgb2hsi(img,img_hsi);
	cvtColor(img,img_hsv,CV_BGR2HSV);
	cvtColor(img,img_hls,CV_BGR2HLS);
	split(img_cmyk,vecCmyk);
	split(img_hsi,vecHsi);
	cout<<"pixel(0,0) in RGB"<<endl;
	for(int i=0;i<3;i++){
		cout<<(int)img.at<Vec3b>(0,0)[i]<<" ";
	}
	cout<<endl<<"pixel(0,0) in CMYK"<<endl;
	for(int i=0;i<4;i++){
		cout<<(int)img_cmyk.at<Vec4b>(0,0)[i]<<" ";
	}
	int a = min(min(24,32),16);
	cout<<endl<<a;
	namedWindow("RGB_Image");
	namedWindow("CMYK_Image");
	//namedWindow("HSV_Image");
	//namedWindow("HLS_Image");
	namedWindow("HSI_Image");
	namedWindow("CMYK_C");
	namedWindow("CMYK_M");
	namedWindow("CMYK_Y");
	namedWindow("CMYK_K");
	imshow("CMYK_C",vecCmyk[0]);
	imshow("CMYK_M",vecCmyk[1]);
	imshow("CMYK_Y",vecCmyk[2]);
	imshow("CMYK_K",vecCmyk[3]);
	imshow("HSI_H",vecHsi[0]);
	imshow("HSI_S",vecHsi[1]);
	imshow("HSI_I",vecHsi[2]);
	imshow("RGB_Image",img);
	imshow("CMYK_Image",img_cmyk);
	//imshow("HSV_Image",img_hsv);
	//imshow("HLS_Image",img_hls);
	imshow("HSI_Image",img_hsi);
	waitKey();
	return 0;
}


### 色彩空间转换的概念 色彩空间是一种用于表示颜色的方式,不同的设备可能采用不同类型的色彩空间来存储和显示图像数据。常见的色彩空间包括 RGB、HSV、YUV 和 Lab 等[^1]。 #### OpenCV 的 `cvtColor` 函数 在 OpenCV 中,可以利用 `cv2.cvtColor()` 函数完成多种色彩空间之间的相互转换。该函数支持大量的预定义标志位,这些标志位指定了输入和输出的颜色模型组合。例如: - **RGB 到 HSV**: 使用标志 `cv2.COLOR_RGB2HSV` - **BGR 到 Gray**: 使用标志 `cv2.COLOR_BGR2GRAY` 以下是基于 Python 的简单代码示例展示如何使用 OpenCV 进行色彩空间转换: ```python import cv2 import numpy as np # 加载一张图片 (默认读取的是 BGR 颜色模式) image_bgr = cv2.imread('example.jpg') # 将 BGR 图像转换为灰度图 image_gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY) # 将 BGR 图像转换为 HSV 图像 image_hsv = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2HSV) # 显示原始图像以及转换后的图像 cv2.imshow('Original Image', image_bgr) cv2.imshow('Gray Scale Image', image_gray) cv2.imshow('HSV Color Space', image_hsv[:, :, 0]) # 只显示 H 通道作为例子 cv2.waitKey(0) cv2.destroyAllWindows() ``` 这段代码展示了如何加载一幅彩色图像并将其分别转化为灰度图像和 HSV 彩色空间中的图像。 #### 自定义实现颜色空间转换 除了调用库函数外,还可以通过手动计算来进行某些特定颜色空间间的转换。比如从 RGB 转换CMYK 或者 HSI 是一种常见需求[^2]。下面是一个简单的 RGBHSI 转换算法的伪码描述: 假设给定一个像素 `(R,G,B)` 值范围均为 `[0,1]`, 计算其对应的 HSI 表达形式如下所示: ```python def rgb_to_hsi(r, g, b): r /= 255. g /= 255. b /= 255. num = 0.5 * ((r-g) + (r-b)) den = ((r-g)**2 + (r-b)*(g-b))**0.5 theta = np.arccos(num / den) if den != 0 else 0 h = theta if b <= g else 360-theta i = (r+g+b)/3. s = 1 - min([r,g,b])/i if i!=0 else 0 return h,i,s ``` 此段代码提供了另一种途径去理解颜色空间之间关系,并允许开发者根据实际应用场景调整细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值