【Qt】绘制CIE色度图

   最近看《计算机视觉——算法与应用》一书,看到CIE色度图时,便突发奇想——用Qt将色度图绘制出来,于是有了这篇博客的内容。不过书中只是提到XYZ的计算公式,并没有说马蹄形的轮廓是怎么来的。于是在网上找了CIE1931XYZ标准数据,结合书中给出的公式(如下所示)
x = X X + Y + Z , y = Y X + Y + Z , z = y = Z X + Y + Z x=\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】绘制CIE色度图_第1张图片

   此处请注意下方的棕色线条,不然会与本人一样,多走不少弯路。由于刚开始没有注意到下方的棕色线条,所以用Qt绘制出来的结果如上图(b)所示,存在很明显的问题。而图(a)中的棕色线条是因为此部分没有数据,matlab为了封闭图形绘制出来的。因此,在用Qt绘制色品图之前,我们需要将棕色线条部分的数据插值出来。不难看出棕色线条是一条直线,可以用 y = k x + b y=kx+b y=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 
#include "ui_QtDrawCIE1931.h"
#include "ciestandardxyz.h"

class QtDrawCIE1931 : public QMainWindow
{
	Q_OBJECT

public:
	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]. 北京:人民邮电出版社

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

你可能感兴趣的:(【程序设计】,qt,色度图,CIE)