【Qt】绘制CIE色度图

发布时间:2025-12-10 11:40:46 浏览次数:11

   最近看《计算机视觉——算法与应用》一书,看到CIE色度图时,便突发奇想——用Qt将色度图绘制出来,于是有了这篇博客的内容。不过书中只是提到XYZ的计算公式,并没有说马蹄形的轮廓是怎么来的。于是在网上找了CIE1931XYZ标准数据,结合书中给出的公式(如下所示)
x=XX+Y+Z,y=YX+Y+Z,z=y=ZX+Y+Zx=\frac{X}{X+Y+Z}, y=\frac{Y}{X+Y+Z}, z=y=\frac{Z}{X+Y+Z} x=X+Y+ZX​,y=X+Y+ZY​,z=y=X+Y+ZZ​
   用matlab进行了绘制,以便确认是否为色度图马蹄形轮廓数据,得到的结果如下图(a)所示。

   此处请注意下方的棕色线条,不然会与本人一样,多走不少弯路。由于刚开始没有注意到下方的棕色线条,所以用Qt绘制出来的结果如上图(b)所示,存在很明显的问题。而图(a)中的棕色线条是因为此部分没有数据,matlab为了封闭图形绘制出来的。因此,在用Qt绘制色品图之前,我们需要将棕色线条部分的数据插值出来。不难看出棕色线条是一条直线,可以用y=kx+by=kx+by=kx+b计算出来。插值完数据后,重新用Qt绘制出来的CIE色度图如图(c)所示。
   其实图(c)所示的色度图依旧有很大的问题,比如有很明显的线条感,色彩过渡不够自然,数据密集的地方有摩尔纹等诸多需要优化的地方。这些问题可以在马蹄形轨迹数据上下功夫,让每个数据点的间隔更均匀,从而让绘制出的色品图更加好看。

Qt绘制色品图的步骤如下:

  • 创建工程;
  • 准备xyz数据;
  • 写xyz2rgb函数,计算xy对应坐标的RGB值;
  • 重写窗体的paintEvent函数,绘制CIE色品图。

0.创建工程
   用Qt绘制色品图采用VS2013+Qt5.8开发环境简单创建一个QtDrawCIE1931工程即可。

1.准备xyz数据
   将网上搜到的CIE1931XYZ标准数据,用matlab转换插值后得到的数据,放到ciestandardxyz.h文件内,便于后续绘图时,直接读取数据。由于数据较多,在此只简单的贴了几个数据,有需要的联系我获取完整数据。

#ifndef _CIESTANDARD_H#define _CIESTANDARD_H#define CIEDATALEN 3#define CIEDATANUM 501// 存储matlab处理后的CIE xyz数据const float ciedata[CIEDATANUM][CIEDATALEN] = {{ 0.17555952, 0.005297870, 0.8191426 },{ 0.17543226, 0.005282220, 0.8192855 },{ 0.17540666, 0.005279333, 0.8193140 },...{ 0.21281756, 0.022649340, 0.7645331 },{ 0.19418854, 0.013973605, 0.7918379 },{ 0.17555952, 0.005297870, 0.8191426 },};#endif

2.写xyz2rgb函数,计算xy对应坐标的RGB值

#pragma once#include <QtWidgets/QMainWindow>#include "ui_QtDrawCIE1931.h"#include "ciestandardxyz.h"class QtDrawCIE1931 : public QMainWindow{Q_OBJECTpublic:QtDrawCIE1931(QWidget *parent = Q_NULLPTR);~QtDrawCIE1931();protected:void paintEvent(QPaintEvent *event);private:Ui::QtDrawCIE1931Class *ui;void xyz2rgb(int &r, int &g, int &b, float x, float y, float z);};

   xyz2rgb函数声明在QtDrawCIE1931.h文件内,具体实现过程在QtDrawCIE1931.cpp文件内。主要是将xy坐标点对应的RGB值计算出来,以便于绘制色度图时填充色彩。

void QtDrawCIE1931::xyz2rgb(int &r, int &g, int &b, float x, float y, float z){// 利用xyz数据计算RGB数据double dr = 0.4185 * x - 0.1587 * y - 0.0828 * z;double dg = -0.0912 * x + 0.2524 * y + 0.0157 * z;double db = 0.0009 * x - 0.0025 * y + 0.1786 * z;double max = 0.0;max = dr > dg ? dr : dg;max = max > db ? max : db;// 将数据转换为int型,便于显示使用r = (int)((dr / max) * 255 + 0.5);g = (int)((dg / max) * 255 + 0.5);b = (int)((db / max) * 255 + 0.5);// 限制数据所属范围r = r > 255 ? 255 : r;g = g > 255 ? 255 : g;b = b > 255 ? 255 : b;r = r < 0 ? 0 : r;g = g < 0 ? 0 : g;b = b < 0 ? 0 : b;}

3.重写窗体的paintEvent函数,绘制CIE色品图
   有一定Qt基础的人知道,QPainter提供绘制封闭图形方法drawPolygon,QBrush有渐变填充方法QLinearGradient,通过将两种方法有机结合便可绘制出色度图。具体实现过程如下。

void QtDrawCIE1931::paintEvent(QPaintEvent *event){// 基本设置QPainter painter(this);painter.fillRect(this->rect(), Qt::white);painter.setRenderHint(QPainter::Antialiasing);painter.scale(this->width(), this->height());QLinearGradient linearGradient;QPointF writePoint(1.0 / 3, 1.0 - 1.0 / 3); // 注意坐标系变化 for (int i = 0; i < CIEDATANUM; i++){// 准备封闭三角形数据float x0 = ciedata[i][0];float y0 = ciedata[i][1];float z0 = ciedata[i][2];int next = i + 1;next = next < CIEDATANUM ? next : 0;float x1 = ciedata[next][0];float y1 = ciedata[next][1];QPointF point[] = {writePoint,QPointF(x0, 1.0 - y0),QPointF(x1, 1.0 - y1)};// 计算xy坐标对应的rgb数据int r = 0;int g = 0;int b = 0;xyz2rgb(r, g, b, x0, y0, z0);// 设置渐变填充的起始点和起始颜色linearGradient.setStart(writePoint);linearGradient.setFinalStop(QPointF(x0, 1.0 - y0));linearGradient.setColorAt(0.0, QColor(235, 235, 235));linearGradient.setColorAt(1.0, QColor(r, g, b));// 绘制封闭三角形painter.setPen(QPen(QColor(r, g, b, 0), 1));painter.setBrush(QBrush(linearGradient));painter.drawPolygon(point, 3);}QMainWindow::paintEvent(event);}

   加上构造函数和析构函数,就是完整绘制程序。根据实际需要,也可以将色度图绘制在Graphics View控件上,便于增加坐标轴。需要注意的是,QWidget的坐标系统和我们平常用的坐标系不一致,Y轴方向和我们平常用的坐标系刚好相反,因此在绘制数据的时候,纵坐标做了相应处理。绘制完成后,在网络上又看到一篇不错的博客,在此安利给看此篇博客的人——《如何绘制CIE1931xy色度图》

参考文献:
   [1] 王维波,2018. Qt 5.9 C++开发指南[M]. 北京:人民邮电出版社

个人声明:
   以上内容,纯属个人观点,不喜勿喷。未经本人同意,不得私自转载。博客中出现的代码仅供学习参考,不得有其他用途。若文中存在纰漏,或读者有更好的建议,欢迎留言探讨。也可邮箱联系:yxyx_0212@163.com

需要做网站?需要网络推广?欢迎咨询客户经理 13272073477