一、概要
本文先给出色调、饱和度和亮度的定义,然后将其量化,利用空间解析几何推导出RGB色彩空间与HSI色彩空间的转换关系,并给出实现这一转换的代码,同时给出OpenCV的标准转换算法。
二、色调、饱和度及亮度的定义

三、RGB色彩空间与HSI色彩空间转换关系的推导




四、算法实现及分析
基于以上推导,利用C++实现RGB色彩空间到HSI色彩空间的转换。输入图像如下所示:

1、强度
利用本文推导的强度计算公式从RGB空间中提取图像的强度,代码如下所示:

#include<iostream>
#include<opencv2/opencv.hpp>
#include<math.h>
using namespace std;
using namespace cv;
int main()
{Mat src = imread("RGB.jpg");imshow("输入图像", src);Mat I = Mat::zeros(src.size(), CV_32FC1);for (int row = 0; row < src.rows; row++){for (int col = 0; col < src.cols; col++){I.at<float>(row, col) = (float)(src.at<Vec3b>(row, col)[0] + src.at<Vec3b>(row, col)[1] + src.at<Vec3b>(row, col)[2]) / 3;}}I.convertTo(I, CV_8UC1);imshow("强度(推导)", I);waitKey(0);system("pause");return 0;
}

将上述代码的运行结果与利用OpenCV的API的运行结果进行对比,对比结果如下所示:

由于输出的是单通道图像,所以显示的效果为灰度图,但依然可以从对比结果中验证推导结果。上图中,左侧是利用OpenCV的API的运行结果,右侧是根据本文推导的强度算法的运行结果。显然,两种方法的运行结果基本一致。
2、饱和度
利用本文推导的饱和度计算公式从RGB空间中提取图像的饱和度,代码如下所示:

#include<iostream>
#include<opencv2/opencv.hpp>
#include<math.h>
using namespace std;
using namespace cv;
int main()
{Mat src = imread("RGB.jpg");imshow("输入图像", src);Mat S = Mat::zeros(src.size(), CV_32FC1);for (int row = 0; row < src.rows; row++){for (int col = 0; col < src.cols; col++){int B_2 = pow(src.at<Vec3b>(row, col)[0], 2);//B^2int G_2 = pow(src.at<Vec3b>(row, col)[1], 2);//G^2int R_2 = pow(src.at<Vec3b>(row, col)[2], 2);//R^2int BG = src.at<Vec3b>(row, col)[0] * src.at<Vec3b>(row, col)[1];int GR = src.at<Vec3b>(row, col)[1] * src.at<Vec3b>(row, col)[2];int BR = src.at<Vec3b>(row, col)[0] * src.at<Vec3b>(row, col)[2];S.at<float>(row,col)= (float)sqrt(B_2 + G_2 + R_2 - BG - GR - BR);}}S.convertTo(S, CV_8UC1);imshow("饱和度(推导)", S);waitKey(0);system("pause");return 0;
}

将上述代码的运行结果与利用OpenCV的API的运行结果进行对比,对比结果如下所示:

上图中,左侧是利用OpenCV的API的运行结果,右侧是根据本文推导的强度算法的运行结果。从运行结果上来看,这似乎是说本文推导关于计算饱和度的公式有误。事实上,在任何文献中关于从RGB色彩空间而导出的饱和度定义是有缺陷的。再来回顾饱和度的数学定义:饱和度为RGB空间中任意一点与强度轴的距离。基于这个概念能够得到几乎所有色彩的饱和度,但是唯独无法得到关于灰色,黑色与白色的饱和度,亦即没有纯黑色,纯白色的定义,因为图像中灰黑白这类颜色的像素点反映至RGB空间上得到的是位于强度轴上的点(R=G=B),而这种位于强度轴上的点与强度轴上的距离永远都为零,因此正是基于这种饱和度的定义,从而计算出来图像中灰黑白三种颜色的像素点的饱和度永远为零。而本文推导出来的饱和度计算公式正是在归一化RGB色彩空间中,基于饱和度的定义严格推导出来的结果。因此,对比上图右侧的运行结果与输入图像容易发现:运行结果中的高亮度区正是输入图像中颜色较纯的区域(饱和度高),而灰黑白三种颜色的输出直接为黑色(饱和度=0)。
而OpenCV官方给出的则是另一种饱和度算法,如下所示:

另外,在冈萨雷斯所著的数字信号处理一书中关于饱和度的算法为:

最后再补充一点,这三种饱和度算法本质上都是基于饱和度的定义,算法之所以不同是因为它们是在不同的色彩空间模型下推导出来的结果。
利用OpenCV给出的饱和度算法,提取图像的饱和度,代码运行结果如下所示:

代码如下所示:

#include<iostream>
#include<opencv2/opencv.hpp>
#include<math.h>
using namespace std;
using namespace cv;
int Max(int B, int G, int R);
int Min(int B, int G, int R);
int main()
{Mat src = imread("RGB.jpg");imshow("输入图像", src);Mat S = Mat::zeros(src.size(), CV_32FC1);for (int row = 0; row < src.rows; row++){for (int col = 0; col < src.cols; col++){int B = src.at<Vec3b>(row, col)[0];int G = src.at<Vec3b>(row, col)[1];int R = src.at<Vec3b>(row, col)[2];int max = 0, min = 0;max = Max(B, G, R);min = Min(B, G, R);if (max != 0){S.at<float>(row, col) = 255 * (float)(max - min) / max;}elseS.at<float>(row, col) = 0;}}S.convertTo(S, CV_8UC1);imshow("S", S);waitKey(0);system("pause");return 0;
}
int Min(int B, int G, int R)
{int min = 0;if (B <= G){min = B;}else{min = G;}if (min > R){min = R;}return min;
}
int Max(int B, int G, int R)
{int max = 0;if (B >= G){max = B;}else{max = G;}if (max < R){max = R;}return max;
}

3、色调
利用本文推导的色调计算公式从RGB空间中提取图像的色调,代码如下所示:

#include<iostream>
#include<opencv2/opencv.hpp>
#include<math.h>
#define pi 3.1415926
using namespace std;
using namespace cv;
int main()
{Mat src = imread("RGB.jpg");Mat I = Mat::zeros(src.size(), CV_8UC1);Mat S = Mat::zeros(src.size(), CV_8UC1);Mat H = Mat::zeros(src.size(), CV_32FC1);for (int row = 0; row < src.rows; row++){for (int col = 0; col < src.cols; col++){int B = src.at<Vec3b>(row, col)[0];int G = src.at<Vec3b>(row, col)[1];int R = src.at<Vec3b>(row, col)[2];int B_2 = pow(B, 2);//B^2int G_2 = pow(G, 2);//G^2int R_2 = pow(R, 2);//R^2int BG = B*G;int GR = G*R;int BR = B*R;int temp = B_2 + G_2 + R_2 - BG - GR - BR;float alpha = (float)(R - 0.5*G - 0.5*B) / sqrt(temp);(alpha > 1) ? 1 : (alpha < -1) ? -1 : alpha;//将alpha的取值范围限定在[-1,1]上float theta = (float)180 * acos(alpha) / pi;//弧度转换为角度if (B > G){H.at<float>(row, col) = 360 - theta;}elseH.at<float>(row, col) = theta;}}H.convertTo(H, CV_8UC1);imshow("色调(推导)", H);waitKey(0);system("pause");return 0;
}

将上述代码的运行结果与利用OpenCV的API的运行结果进行对比,对比结果如下所示:

上图中,左侧是利用OpenCV的API的运行结果,右侧是根据本文推导的色调公式的运行结果。显然,该运行结果与左侧图像相比除了显得稍亮一点外,结果完全一致。下面解释右侧运行结果显得较亮的原因。
回顾本文推导的色调计算公式:

在该公式中,反余弦内部分式的分母在R≈G≈B的情况下趋于0,这就会导致分式的运算结果的绝对值远大于1,为了限制无穷大,给定分式运算结果超出±1的值为+1或者-1,那么反余弦的结果为0°或180°。故而图像存在较亮的区域。
最后给出OpenCV官方的色调算法,算法如下图所示:

图中的H为色调算法,代码如下所示:

#include<iostream>
#include<opencv2/opencv.hpp>
#include<math.h>
using namespace std;
using namespace cv;
int Min(int B, int G, int R);
int Max(int B, int G, int R);
int main()
{Mat src = imread("RGB.jpg");imshow("输入图像", src);Mat H = Mat::zeros(src.size(), CV_32FC1);for (int row = 0; row < src.rows; row++){for (int col = 0; col < src.cols; col++){int B = src.at<Vec3b>(row, col)[0];int G = src.at<Vec3b>(row, col)[1];int R = src.at<Vec3b>(row, col)[2];int max = 0, min = 0;max = Max(B, G, R);min = Min(B, G, R);/*************************************************************************************************/if (R == max){H.at<float>(row, col) = (float)60*(G - B) / (max - min);}if (G == max){H.at<float>(row, col) = (float)120 + (float)60 * (B - R) / (max - min);}if (B == max){H.at<float>(row, col) = (float)240 + (float)60 * (R - G) / (max - min);}if (H.at<float>(row, col) < 0){H.at<float>(row, col) = (H.at<float>(row, col) + 360);}}}H.convertTo(H, CV_8UC1);imshow("色调(官方)", H);waitKey(0);system("pause");return 0;
}
int Min(int B, int G, int R)
{int min = 0;if (B <= G){min = B;}else{min = G;}if (min > R){min = R;}return min;
}
int Max(int B, int G, int R)
{int max = 0;if (B >= G){max = B;}else{max = G;}if (max < R){max = R;}return max;
}

运行结果如下所示:

深刻理解RGB色彩空间与HSI色彩空间的转换关系相关推荐

  1. RGB色彩空间和HSV色彩空间的理解

    RGB色彩空间和HSV色彩空间的理解 本文的结构如下: 1.RGB色彩空间 2.HSV色彩空间(附HSV颜色分量范围表) 3.RGB到HSV的转换的Demo   使用OpenCV实现RGB转HSV,并 ...

  2. MATLAB 将RGB颜色空间转为HSI颜色空间、LAB颜色空间 原理及程序

    一张彩色图像是由R.G.B三个通道组成,所以首先需要将彩色图像分为三个通道的图像. 以下是对猫图进行单通道提取得到的图像. HSI颜色空间 HSI[Hue-Saturation-Intensity(L ...

  3. python-opencv之色彩空间,RGB2HSV色彩空间转换及应用

    python-opencv之色彩空间,RGB2HSV色彩空间转换及应用 一.Python-opencv中的色彩空间 二.为什么同样的图片用公式换了色彩空间显示出来的完全不一样? 三.cv2.inRan ...

  4. OpenCV实现RGB颜色空间和HSI颜色空间的相互转换

    核心的转换公式: RGB-->HSI 截图来自中科院刘定生老师的<数字图像处理与分析>课件. HSI-->RGB 具体的数学公式参照冈萨雷斯版<数字图像处理(第三版)&g ...

  5. 通过配置ssh深刻理解puppet的语法及工作机制

    通过配置ssh深刻理解puppet的语法及工作机制 需求分析 1).要求openssh-server处于被安装状态 2).要求在配置文件/etc/ssh/sshd_config正确的情况下,sshd服 ...

  6. F#学习之路(2) 深刻理解函数(上)

    函数在函数式编程语言中是一等公民,是函数式语言中最重要的基本组成元素,也是其名称的由来. F# 中的函数之如C#中的类,是组织程序结构的最基本单元.是命令式编程语言中函数或OO编程语言中方法的超集.超 ...

  7. 曲线均匀分布_曲线篇:深刻理解B 样条曲线(下)

    前两篇中讲解了贝塞尔曲线和B样条基础. FrancisZhao:曲线篇: 贝塞尔曲线​zhuanlan.zhihu.com FrancisZhao:曲线篇:深刻理解B 样条曲线(上)​zhuanlan ...

  8. matlab hsi颜色,RGB 颜色空间转 HSI 颜色空间的matlab程序实现

    RGB 颜色空间转 HSI 颜色空间的matlab程序实现 程序实现的时候注意把RGB像素值归一化就是了,然后千千万万要根据公式来,不能大意,我之前就有theta为复数的情况,是sqrt对负数进行运算 ...

  9. 【JavaScript】JavaScript模拟实现面向对象一张图帮助你深刻理解原型链和原型对象

    文章目录 一.JavaScript模拟面向对象 1.函数是类 2.函数中各种变量的声明 3.关于函数内的this 小结:JavaScript中函数是什么? 4.练习:面向对象思想编写Complex类 ...

最新文章

  1. Servlet的延迟加载和预加载
  2. 合理设置apache参数
  3. java中jsp页面foreach遍历输出的使用
  4. 打开数据库_数据库客户端navicat遇到问题怎么办?
  5. 【C++面向对象】类的数据成员:绑定、布局和存取
  6. todo项目开发_Facebook的TODO项目,巴西的Coursera,Drupal等
  7. python爬取b站搜索结果播放地址_如何利用Python快速爬取B站全站视频信息
  8. python文件管不了_Python对文件和文件路径的管理
  9. 2013-我的前事今生(完结篇)
  10. Java中的可变参数使用
  11. 推荐5款你用过之后不舍得卸载的小众软件
  12. ZebraDesigner-设计label
  13. Android 运行时权限
  14. 用计算机弹斗地主,单机斗地主
  15. Selenium应用中使用chrome浏览器时的新手安装教程
  16. 免密登录驾考网使用selenium自动爬取模拟试题
  17. 输入一个数n,然后打印出2的n次方
  18. linux proftpd mysql_proftpd mysql quota 配置完全指南
  19. Nginx全站开启HTTPS
  20. springboot 生成二维码

热门文章

  1. python解析can报文,Python实现Can接收发送 DBC分析报文 周立功ZLG 绘制曲线 支持离线回放.rar...
  2. 电商广告推荐系统案例
  3. 连接计算机与网络传输设备的接口设备,1394连接和本地连接有什么不同
  4. 基因编辑技术:能让人类永生下去?
  5. gdal无法读取中文标题的图片的解决办法(结合Qt)
  6. 如何求矩阵的若儿当标准型
  7. VHDL电话计费系统设计
  8. 1小时等于36000毫秒的计算方法
  9. 《C++新经典》第1章 C/C++语言
  10. 【网络安全】XXE漏洞详细解析