C#绘制CIE1931色度图

CIE 1931 色度图
CIE 1931 色度图是一个理想的图形,如图 2.所示。理论上的马蹄形曲线内区域包括了一切物理上能实
现的颜色。在此二维色度图中,X 轴色度坐标相当于红原色的比例,Y 轴色度坐标相当于绿原色的比例,并
且有 X+Y+Z=1,由 X、Y 值可得出 Z 值。图中没有 Z 轴色度坐标,但 Z 相当于蓝原色的比例。

描绘马蹄形曲线
描绘马蹄形曲线的方法:选取 46 个光谱轨迹色度坐标。
由于马蹄形曲线并非随波长变化而均匀变化,所以选取坐标时并未按照等波长间隔选取,而是根据观
察马蹄形曲线,得出马蹄形曲线上长度大概相同的点,对照 CIE 1931 光谱轨迹色度坐标表选取。
将波长 380nm 点作为纯蓝色点。实际上该点应该为蓝色与红色的混合色:紫色点,但由于紫色是混合
色,绘制时不好控制计算机上 R、B 的比例,所以绘制马蹄形曲线的时候将该点作为纯蓝色点。设色度图中
纯蓝色点为 B 点、纯绿色点(520nm)为 G 点、纯红色点(780nm)为 R 点;则 B 点在计算机上的颜色设为
(0,0,255)、G 点为(0,255,0)、R 点为(255,0,0)。
在波长 380nm 到 520nm 之间,选取 25 个点,然后,用 B 样条曲线算法用圆滑连续曲线连接这 25 个点。
连接的时候,颜色变化规律为:B 点到 498nm(由光谱轨迹色度坐标表得到此处为 B、G 比例近似相同点)
颜色从(0,0,255)变化到(0,255,255);498nm 到 G 点颜色从(0,255,255)变化到(0,255,0)。
同理,在波长 520nm 到 780nm 之间,选取 20 个点,然后,用 B 样条曲线算法用圆滑连续曲线连接这 20
个点。连接的时候,颜色变化规律为: G 点到 575nm(由光谱轨迹色度坐标表得到此处为 G、R 比例近似相
同点)颜色从(0,255,0)变化到(255,255,0);575nm 到 R 点颜色从(255,255,0)变化到(255,0,
0)。
由于连接波长 780 到 380nm 两点的是直线,所以在这两点间设 511 个象素点,然后以画点的方式画从
红到蓝的直线。其变化规律相似,颜色从(255,0,0)变化到(255,0,255);再从(255,0,255)变
化到(0,0,255)。
为了表现尽可能多的颜色, 在颜色渐变过程中采用了一种“准”线性变换的办法。即颜色在 B 点到 498nm、
498nm 到 G 点、G 点到 575nm、575nm 到 R 点以及 R 点到 B 点变化时,均为均匀变化。
这样,便绘出了马蹄形曲线。有了曲线后,以等能白点 C 点(255,255,255)(色度坐标为 X=0.3333,
Y=0.3333)为中心,向色度图边缘上的各点作射线,射线上的点的颜色则在白色与这条射线和马蹄形曲
线边界交点的颜色之间连续变化。此时的变化仍采用线性均匀变化。为了去除中间不连续部分,射线扫描
先由绿→蓝、绿→红→蓝,再由红→蓝、红→绿做了两次扫描,基本做到了连续。

private void XYZ2RGB(float x, float y, float z, ref int r, ref int g, ref int b)
{
    double dr, dg, db;
    dr = 0.4185 * x - 0.1587 * y - 0.0828 * z;
    dg = -0.0912 * x + 0.2524 * y + 0.0157 * z;
    db = 0.0009 * x - 0.0025 * y + 0.1786 * z;
    double max = 0;
    max = dr > dg ? dr : dg;
    max = max > db ? max : db;
    dr = dr / max * 255;
    dg = dg / max * 255;
    db = db / max * 255;
    dr = dr > 0 ? dr : 0;
        dg = dg > 0 ? dg : 0;
        db = db > 0 ? db : 0;
        if (dr > 255)
        {
            dr = 255;
        }
        if (dg > 255)
        {
            dg = 255;
        }
        if (db > 255)
        {
            db = 255;
        }
        r = (int)(dr + 0.5);
        g = (int)(dg + 0.5);
        b = (int)(db + 0.5);
    }

private void DrawCIE()
{
    List<float> mySmallx = new List<float>();
    List<float> mySmally = new List<float>();
    List<float> mySmallz = new List<float>();
    using (FileStream fs = new FileStream("轮廓坐标.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        lock (fs)
        {
            StreamReader sr = new StreamReader(fs, Encoding.UTF8);
            while (!sr.EndOfStream)
            {
                string str = sr.ReadLine();
                string[] temp = str.Split(new char[] { }, StringSplitOptions.RemoveEmptyEntries);
                if (temp.Length < 3) continue;
                mySmallx.Add(float.Parse(temp[1]));
                mySmally.Add(float.Parse(temp[2]));
            }
        }
    }

    //1:选择3个顶点
    PointF p1 = new PointF(); // 代表最下面的点
    PointF p2 = new PointF(); // 代表最上面的点
    PointF p3 = new PointF(); // 代表最右边的点
    //代表最下面的点
    p1.X = 0.173101f;
    p1.Y = 0.004774f;

    //代表最上面的点
    p2.X = 0.082053f;
    p2.Y = 0.83409f;

    //代表最右边的点
    p3.X = 0.73469f;
    p3.Y = 0.26531f;

    float k3 = 0, b3 = 0;
    k3 = (p3.Y - p1.Y) / (p3.X - p1.X);
    b3 = p3.Y - p3.X * k3;

    //左边数组
    List<PointF> leftArray = new List<PointF>();
    List<PointF> rightArray = new List<PointF>();
    PointF tempPonit = new PointF();
    for (int i = 0; i < (int)mySmallx.Count(); i++)
    {
        tempPonit.X = mySmallx[i];
        tempPonit.Y = mySmally[i];
        if (i < 161)
        {
            leftArray.Add(tempPonit);
            if (i == 160)
            {
                rightArray.Add(tempPonit);
            }
        }
        else if (i >= 161 && i < 339)
        {
            rightArray.Add(tempPonit);
        }
    }

    List<PointF> downArray = new List<PointF>();
    for (float y = 0.001f; y < 0.265; y += 0.001f)
    {
        if (y > p1.Y && y < p3.Y)
        {
            tempPonit.X = (y - b3) / k3;
            tempPonit.Y = y;
            downArray.Add(tempPonit);
        }
    }
    Bitmap bitmap = new Bitmap(2000, 2000, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
    Rectangle dimension = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
    BitmapData picData = bitmap.LockBits(dimension, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    
    float tempx = 0, tempy = 0;
    float miny = 0, maxy = 0;
    float minx = 0, maxx = 0;
    float middlex = 0;
    double minvalue1 = 999999;
    double minvalue2 = 999999;
    int maxindex = 0, minindex = 0;
    for (int i = 0; i < picData.Height; i++)
    {
        tempy = (float)i / (float)picData.Height;
        if (tempy < 0.004774 || tempy > 0.83409)
        {
            continue;
        }
        for (int j = 0; j < picData.Width; j++)
        {
            tempx = (float)j / (float)picData.Width;
            if (tempx < 0.003636 || tempx > 0.73469)
            {
                continue;
            }
            //1、判断左边的数组
            maxindex = 0;
            minindex = 0;
            minvalue2 = 999999;
            minvalue1 = 999999;
            for (int k = 0; k < (int)leftArray.Count(); k++)
            {
                double value1 = tempy - leftArray[k].Y;
                double value2 = leftArray[k].Y - tempy;
                if (value1 < minvalue1 && value1 > 0)
                {
                    minvalue1 = value1;
                    minindex = k;
                }
                if (value2 < minvalue2 && value2 > 0)
                {
                    minvalue2 = value2;
                    maxindex = k;
                }
            }
            miny = leftArray[minindex].Y;
            minx = leftArray[minindex].X;
            maxy = leftArray[maxindex].Y;
            maxx = leftArray[maxindex].X;
            middlex = minx - (tempy - miny) / (maxy - miny) * (minx - maxx);
            if (tempx < middlex || maxindex == minindex)
            {
                continue;
            }

            //2、判断右边的数组
            maxindex = 0;
            minindex = 0;
            minvalue2 = 999999;
            minvalue1 = 999999;
            for (int k = 0; k < (int)rightArray.Count(); k++)
            {
                double value1 = tempy - rightArray[k].Y;
                double value2 = rightArray[k].Y - tempy;

                if (value1 < minvalue1 && value1 > 0)
                {
                    minvalue1 = value1;
                    minindex = k;
                }
                if (value2 < minvalue2 && value2 > 0)
                {
                    minvalue2 = value2;
                    maxindex = k;
                }
            }
            miny = rightArray[minindex].Y;
            minx = rightArray[minindex].X;
            maxy = rightArray[maxindex].Y;
            maxx = rightArray[maxindex].X;
            middlex = minx - (tempy - miny) / (maxy - miny) * (minx - maxx);
            if (tempx > middlex || maxindex==minindex)
            {
                continue;
            }

            //3、判断下边的数组
            maxindex = 0;
            minindex = 0;
            minvalue2 = 999999;
            minvalue1 = 999999;
            for (int k = 0; k < (int)downArray.Count(); k++)
            {
                double value1 = tempy - downArray[k].Y;
                double value2 = downArray[k].Y - tempy;
                if (value1 < minvalue1 && value1 > 0)
                {
                    minvalue1 = value1;
                    minindex = k;
                }
                if (value2 < minvalue2 && value2 > 0)
                {
                    minvalue2 = value2;
                    maxindex = k;
                }
            }
            miny = downArray[minindex].Y;
            minx = downArray[minindex].X;
            maxy = downArray[maxindex].Y;
            maxx = downArray[maxindex].X;
            middlex = minx - (tempy - miny) / (maxy - miny) * (minx - maxx);
            if (tempx > middlex || maxindex == minindex)
            {
                continue;
            }
            float x = 0, y = 0, z = 0;
            x = tempx;
            y = tempy;
            z = 1 - x - y;
            int R = 0, G = 0, B = 0;
            XYZ2RGB(x, y, z, ref R, ref G, ref B);
            unsafe
            {
                byte* target = (byte*)picData.Scan0.ToPointer();
                target[(picData.Width - i) * picData.Stride + 3 * j] = (byte)B;
                target[(picData.Width - i) * picData.Stride + 3 * j + 1] = (byte)G;
                target[(picData.Width - i) * picData.Stride + 3 * j + 2] = (byte)R;
            }
        }
    }
    bitmap.UnlockBits(picData);
    bitmap = bitmap.Clone(new Rectangle(0, 200, 1600, 1800), bitmap.PixelFormat);
    bitmap.Save("Cie1931.bmp", ImageFormat.Bmp);
}

结果:
在这里插入图片描述

项目源码下载地址

  • 10
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇云雨巷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值