如何绘制CIE1931xy色度图

文章目录

  • 如何绘制CIE1931xy色度图
    • 一点点介绍
      • 色度学和CIE1931xyY色空间
      • 一点点补充说明
    • 获取途径和绘制方法
      • SVG格式
      • Matlab自带函数
      • Qt下用C++绘制的两种思路
    • 一点点小结
      • Python上用这个库

转载请注明出处

一点点介绍

色度学和CIE1931xyY色空间

既然要绘制这个图,大家应该对xy色品坐标有所了解。

如果不是很了解的可以参考这个CIE1931xyY色度图-复旦大学.ppt了解一下基本的色度学知识。

简单说明一下:

  • 色度图中的外形轮廓线是可见光范围里(约380nm-760nm)单色光颜色轨迹线
  • 等能白点E的坐标xy(0.3333,0.3333)

一点点补充说明

大家在网上应该可以找到五花八门的色度图,让人很难分辨到底哪个才是标准的。

对于部分问题,大家可能会有所疑惑的,在这里简单说明:

  • 真正意义上的色度图在现有的显示设备上根本显示不了,所以相比于纠结图中的颜色是否准确,应该更加关注坐标数据,这才是将颜色量化为xy坐标的意义。不过能画的好看一些确实有助于理解相关概念。
  • 颜色是三维的,色品图是二维的,所以色度图不包含亮度信息。等能白点周围的颜色并不是只有白色,所有灰色、黑色都位于这里。

这里附上几种常见色域在色品图上的位置,图片来源:深入理解 sRGB\Adobe RGB\NTSC\DCI-P3\REC.2020\ProPhoto RGB 色域

获取途径和绘制方法

这部分主要来介绍一下如何获取这个图以及如何在科研、项目里用这个图。

SVG格式

维基百科上提供的SVG格式的图片,以及不同分辨率的PNG图片,可以自行取用。

https://commons.wikimedia.org/wiki/File:CIE1931xy_blank.svg

Matlab自带函数

matlab 2017b及之后的版本中引入了函数plotChromaticity用于绘制色度图。

最简单的用法,直接调用函数,hold on之后自己就可以在上面涂涂画画了。

plotChromaticity
hold on
## your code ...

绘制结果如下图所示:

简单说下matlab的实现思路

matlab绘制色度图的方法是将上面的区域分割成一个个小四边形,从4个顶点的xy值计算颜色,并对内部进行插值填充。matlab使用patch函数绘制填充颜色,

patch('Vertices',v, 'Faces',f, 'EdgeColor','none', 'FaceVertexCData',xy4rgb(i:i+3,3:5),'FaceColor','interp');

我们将其中控制线条的'EdgeColor','none'参数改为'EdgeColor','k'可以得到以下结果,方便大家更加形象的了解渐变填充过程。

因为其实每个点只能得到xyz坐标(z=1−x−yz=1-x-yz=1−x−y),是计算不了对应的RGB颜色的,还缺少一个Y来控制亮度信息。matlab里实现时候调用了以下代码来将xyz值转为srgb数值:

rgb = images.internal.testchart.xyz2srgb(xyz');

所以可以看到,matlab版本的绘制结果在等能白点周围都是以灰色填充,这取决里matlab具体的换算实现。这个说明顺便帮忙解答我师弟博客下的提问:为什么matlab绘制的色度图白点周围不是白色填充?当然了,我在前面就说了,所有无彩色都集中在这个区域。

另外,这个函数还有几个带参数的重载版本,可以用于绘制一些额外的数据上去,有兴趣的朋友可以参考官方文档中的example学习。

顺便一提,在matlab里进入该函数,里面包含了单色光轨迹线上的63对xy坐标值可用。

更加详细的光谱色度坐标可以参考1931CIE-XYZ标准色度系统。

Qt下用C++绘制的两种思路

由于可能需要在项目里用到,就需要自己实现一下啦,这里给出了在两种在Qt框架下的绘制方法。

  1. 借助了Qt的线性渐变类QLinearGradient来进行颜色填充。

QGradient Class是Qt的渐变填充类,结合QBrush可对任意区域实现快速渐变填充。目前Qt支持三种渐变模式,这里采用线性渐变进行填充。

受matlab填充方式启发,这里选择从等能白点处,即xy(0.3333,0.3333)向单色光轮廓线进行三角区域填充。

QLinearGradient需要设置填充起点坐标和填充终点坐标,填充起点颜色和填充终点颜色,现有数据为:

  • 填充起点坐标:白点(已知)
  • 填充终点坐标:曲线轮廓点(已知,需要插值扩充,下方的直线在已知端点的情况下可自行选择填充点的数量)
  • 填充起点颜色:可以人为设定白色或者偏灰色,仅为了方便显示和理解(已知)
  • 填充终点颜色:人为选取单色光谱中近似对应的蓝色、青色、绿色、黄色、红色五个点,其余点通过插值计算。(已知+计算)

填充颜色的计算方法,即最简单的线性插值计算。但实际上轮廓线是曲线,线性插值会存在一点误差。颜色插值混合可以近似采用格拉斯曼颜色混合定律,假设曲线上蓝色BBB和青色CCC之间任意一点,与蓝色距离d1d_1d1​,与青色距离d2d_2d2​,相对位置p=d1d1+d2p=\frac{d_1}{d_1+d_2}p=d1​+d2​d1​​。那么该点的颜色为:

Xr,g,b=(1−p)∗Br,g,b+p∗Cr,g,bX_{r,g,b}=(1-p)*B_{r,g,b}+p*C_{r,g,b}Xr,g,b​=(1−p)∗Br,g,b​+p∗Cr,g,b​

有了上述计算数据,就可以开始绘图了,先把原始数据的63个点进行颜色插值,然后显示出来的效果如下:

这里说明一下为什么要在这些数据点之间进一步插值,因为当前颜色点的渐变幅度较大,直接计算会造成渐变不连续以及严重的边缘锯齿。

假设对已有的63对坐标中间,任意两个点之间再插值9个点,并计算对应的颜色,最后得到的单色光离散点轨迹线如下图所示。

同样基于这些点填充整个色度图,效果如下:

从效果上看基本可以达到要求,上述填充过程重载了paintEvent函数直接在背景上画,绘制代码如下:

void MainWindow::paintEvent(QPaintEvent*){QPainter p(this);p.fillRect(rect(),Qt::white);p.setRenderHint(QPainter::Antialiasing);//坐标缩放p.scale(width(), height());p.setPen(QPen(QColor(0,0,0,0),1));QLinearGradient linearGradient;//等能白点CPointF whiteP(1.0/3, 1.0/3, QColor(235,235,235));//ciePoints包含边界点和对应颜色for(int i=0;i<m_time-1;i++){//确定三角形绘制域点QPointF ps[]{whiteP.toQPointF(), ciePoints[i].toQPointF(), 2*ciePoints[i+1].toQPointF()-ciePoints[i].toQPointF()};//设置起始坐标,结束坐标,起始颜色,结束颜色linearGradient.setStart(whiteP.toQPointF());linearGradient.setFinalStop(ciePoints[i].toQPointF());linearGradient.setColorAt(0.0, whiteP.C());linearGradient.setColorAt(1.0, ciePoints[i].C());p.setBrush((QBrush(linearGradient)));p.drawPolygon(ps, 3);}
}

最后再加一个坐标轴以及自适应拉伸就可以使用了,这里其实可以明显看到蓝色和红色处在曲线轮廓线和直线交界处的过渡有点问题,需要特殊处理,再扫描一下覆盖这条白线 。不过说到底还是绘制方式的问题,所以这里还是推荐用第二种绘制方法。

  1. 自己计算每个像素位置的填充颜色,方便在各类语言和框架下实现。

绘图函数的主要流程都在下面的代码中了,也标注了每一步的功能,整体思路上应该比较容易理解。

QImage CIEDiagram::drawCIEDiagram(int picSize){//创建QImage对象QImage cieDiagram(picSize, picSize, QImage::Format_RGB32);cieDiagram.fill(Qt::white);//遍历像素for(int y=0;y<picSize;y++){QRgb* line = (QRgb *)cieDiagram.scanLine(y);for(int x=0;x<picSize;x++){CPointF curP(1.0*x/picSize,1.0-1.0*y/picSize);//将像素位置转换为xy逻辑坐标if(isPointInsideBound(curP)){//判断当前像素点是否在CIE色度曲线内CLineF whiteP(white, curP);//构造一个白点与当前像素点的射线CPointF crossPoint;areaFlag areaflag = crossArea(curP);//判断射线交曲线于哪个区域//根据相交区域计算射线与CIE轮廓曲线交点switch (areaflag) {case leftA:crossPoint = getCrossPoint(whiteP, colorPointIdx[bottom], colorPointIdx[top]);break;case rightA:crossPoint = getCrossPoint(whiteP, colorPointIdx[top], colorPointIdx[right]);break;default:crossPoint = getCrossPoint(whiteP, colorPointIdx[right], cieCurvePoints.size());break;}CLineF whiteToBoundLine(white, crossPoint);//构建白点与交点线段QColor c = whiteToBoundLine.getInterColor(curP);//根据白点、当前像素点、交点进行插值,计算当前像素点颜色line[x] = c.rgb();}}}return cieDiagram;
}

最后效果如下:

Qt的源码写的比较乱,最近比较忙,之后整理好了发出来,或者急需的,可以留邮箱

一点点小结

在很多颜色处理的地方都用到了近似颜色插值,从人眼效果上来看基本上是符合的。这主要还是因为实际上人眼能够感知并且分辨的颜色不过几千种,当颜色比较多的时候,这种微小变化对人眼就不会很敏感了,即使我们插值的结果不是很准。

所以在实际使用中,完全可以事先准备一副分辨率较高的色度图,然后要缩放的时候直接对图像线性插值即可,颜色效果完全可以满足需求。只需要把边界轮廓实时绘制一下消除锯齿即可。不过如果用的是矢量图那就完全OK啦。


工作比较忙,鸽了好久,更新一下,2022/01/21。

  • 修改了之前的部分代码,封装了一个纯C++导出图像buffer的库
  • 增加一个简易的CIE XY Chromatictiy PlotDemo
  • 增加了基于OpenGL实现的Demo

代码自取,在Github上。

简易的plot demo,应该用Qt画个边框,不然锯齿有点明显。。

这是基于qt封装的opengl绘制的,opengl的部分的代码基本是照着网上的教程来的。
opengl的片段着色器能够自动插值,绘图逻辑突然简单了,只要给出外围的光谱轨迹的顶点坐标和颜色就行。

Python上用这个库

有个colour-science的库,基本整合了颜色科学里的大部分工具,可以说是科研工作的好帮手,直接

pip install colour-scienceimport colour
import colour.plotting # 绘图的好像是导入这个

具体的介绍我之后抽时间再写写,这个文档挺详细的,大家搜一下应该就可以搜到

如何绘制CIE1931xy色度图相关推荐

  1. Qt 绘制CIE色域图

    重写窗体的paintEvent函数,绘制CIE色品图 QPainter提供绘制封闭图形方法drawPolygon,QBrush有渐变填充方法QLinearGradient,通过将两种方法有机结合便可绘 ...

  2. 照片、摄影处理中的基本知识

    文章目录 1.曝光 1.1 什么是曝光 1.2 不同曝光量的效果 1.3 怎么判定正确曝光度 2. 成像原理 2.1 小孔成像 2.2 透镜成像 2.3 单反相机结构 3. 对焦 3.1 什么是对焦? ...

  3. 播放器色觉辅助功能开发,助力提升色觉障碍用户的视频观看体验

    本文同时发布于团队博客:https://blog.csdn.net/avlabs/article/details/80470370 本文包含以下内容 1.简单介绍人眼彩色视觉的基本原理 2.介绍如何利 ...

  4. 提升色觉障碍用户的视频观看体验——播放器色觉辅助功能开发

    本文包含以下内容 1.简单介绍人眼彩色视觉的基本原理 2.介绍如何利用算法模拟色觉障碍用户所看到的画面 3.基于色觉障碍模拟算法,介绍几种色觉障碍辅助方法,这些方法可以帮助色觉障碍用户更好地分辨本来难 ...

  5. 【嵌入式经验系列】基于色坐标的RGB灯调光调色算法

    一.序言 如果想要驱动一个三基色LED灯亮出不同的颜色,只需要控制三路PWM的输出即可实现.但是如何控制三路的输出使三基色LED灯亮出我们想要得到的理想目标颜色并且只有很小的色差就没有那么简单了.本文 ...

  6. 几个颜色基本属性的个人理解

    在2018年下半年的时候,曾做过SDR2HDR的尝试(从以前的低/标准动态范围standard dynamic range到高动态范围high dynamic range),了解到平常熟视无睹的颜色概 ...

  7. 高通骁龙865之camera性能深度分析(二)

    7.Up to 64 MP single camera @ 30 FPS with Zero Shutter Lag 骁龙865单摄场景最大支持64M 30fps,ISP IP处理能力需求为64M*3 ...

  8. 广色域图片Android,广色域手机屏幕科普解析:所见不一定为实

    最近一段时间,不少朋友可能注意到关于AMOLED以及广色域屏的消息似乎一下子多了起来-- 比如坊间盛传iPhone之后将改用AMOLED屏幕,查询百度,也会发现不少显示器制造企业都在推广自己的广色域触 ...

  9. View绘制流程的入口

    View绘制流程的入口是WindowManager.add(decor, l),从Activity的创建开始分析,具体流程如下: Activity.onCreate() setContentView( ...

  10. Python数据挖掘:绘制直方图,设置上下限和步长,绘制子图

    有任何问题欢迎在评论区提出! 绘制直方图: ''' 来源:天善智能韦玮老师 课堂笔记 作者:Dust 直方图hist某一段数据出现的频数 ''' import numpy as npy import ...

最新文章

  1. 面试总结-腾讯产品群面
  2. 常见的上传绕过以及解析漏洞
  3. 计算机硬件价钱分配,电脑基础知识计算机硬件基础课件.ppt
  4. HTTP 错误 403.14 - Forbidden Web 服务器被配置为不列出此目录的内容
  5. python列表的内置方法_Python内置方法详解
  6. axios链接带参数_axios常见传参方式
  7. idea 提升幸福感 常用设置(重装机配置)
  8. 超干货议程发布 | 2021全球分布式云大会 · 上海站 重磅来袭
  9. Git学习文档之一 学习文档-发布
  10. ndows定时任务_百度经验,windows服务器:编写bat脚本,创建定时任务
  11. 谷歌浏览器linux 64怎么安装插件,Ubuntu 16.04下安装64位谷歌Chrome浏览器
  12. Java设计模式之工厂方法模式与抽象工厂模式
  13. Http协议详解(深入理解)
  14. 会议摘要怎么写?这篇论文手把手教你
  15. schedule-调度器
  16. numpy_absolute函数
  17. 关于微信小程序自定义交易组件升级处理的相关问题,及解决思路
  18. mysql创建表里主码和外码_SQL语言创建表时候怎么定义主码和外码
  19. Wu反走样算法介绍(简单易懂) -Xiaolin Wu’s Algorithm
  20. SQL语句执行顺序详解

热门文章

  1. hive面试题总结(大数据面试)
  2. Java基本数据类型有哪些
  3. 海信电视怎么删除自带应用
  4. python pytorch库_一个简单而强大的深度学习库—PyTorch
  5. 教育平台的线上课程 智能推荐策略
  6. PLC控制电机正反转原理图
  7. python安装numpy库教程_Python库之numpy库的安装教程
  8. 你还在用Rational Rose画图吗?来来来给你介绍一款开源免费上手容易的 BOUML UML画图工具。
  9. 炫龙dcpro黑苹果_毁灭者DC W650DC装黑苹果心得
  10. 2017秋季武汉工程大学全日制自考本科招生简章