1.前言:
在許多應用中,例如LED照明就需要利用色度圖來觀察發光的顏色,色溫等趨勢,本文討論如何利用C#程式繪製色度圖,為簡化程式,僅討論CIE 1931 標準色度2度視角觀察者的色度圖,色彩系統採用NTSC。

2.說明:
RGB色彩空間系統參考wiki的說明
http://en.wikipedia.org/wiki/RGB_color_space
http://en.wikipedia.org/wiki/Standard_illuminant#Illuminants_B_and_C

CIE1931色彩空間
http://en.wikipedia.org/wiki/CIE_1931_color_space
http://www.fourmilab.ch/documents/specrend/

CIE 1931 標準色度2度視角觀察者XYZ函數的資料集
http://cvrl.ioo.ucl.ac.uk/

CIE色度圖Delphi程式碼可參考
http://www.efg2.com/Lab/Graphics/Colors/Chromaticity.htm

CIE 1931 2° Standard Observer Color Matching Functions

private void DrawColorMatchingFunctions()
{
	//使用SteemaTeeChart繪製Color Matching Functions Curve
	tChart1.Chart.Aspect.View3D = false;
	Steema.TeeChart.Styles.Line line1 = new Steema.TeeChart.Styles.Line();
	Steema.TeeChart.Styles.Line line2 = new Steema.TeeChart.Styles.Line();
	Steema.TeeChart.Styles.Line line3 = new Steema.TeeChart.Styles.Line();
	line1.LinePen.Width = 3;
	line2.LinePen.Width = 3;
	line3.LinePen.Width = 3;
	line1.Color = Color.Red;
	line2.Color = Color.Green;
	line3.Color = Color.Blue;            
	tChart1.Chart.Series.Add(line1);
	tChart1.Chart.Series.Add(line2);
	tChart1.Chart.Series.Add(line3);
	
	//讀取資料
	DataTable dt = TxtConvertToDataTable(@"d:\tmp\da.csv", "tmp", ",");

	for (int i = 0; i < dt.Rows.Count; i++)
	{
		line1.Add(double.Parse(dt.Rows[i]["Lamda"].ToString()), double.Parse(dt.Rows[i]["Xt"].ToString()));
		line2.Add(double.Parse(dt.Rows[i]["Lamda"].ToString()), double.Parse(dt.Rows[i]["Yt"].ToString()));
		line3.Add(double.Parse(dt.Rows[i]["Lamda"].ToString()), double.Parse(dt.Rows[i]["Zt"].ToString()));
	}
	
	//設定繪圖區顯示設定
	tChart1.Chart.Series[0].Title = "CIE-X(Red)";
	tChart1.Chart.Series[1].Title = "CIE-Y(Green)";
	tChart1.Chart.Series[2].Title = "CIE-Z(Blue)";
	tChart1.Chart.Axes.Bottom.Title.Text = "Wavelength [nm]";
	tChart1.Chart.Axes.Left.Title.Text = "Tristimulus values";
	tChart1.Header.Text = "CIE 1931 2° Standard Observer Color Matching Functions";
	tChart1.Header.TextAlign = StringAlignment.Center;
	tChart1.Legend.Alignment = Steema.TeeChart.LegendAlignments.Top;
	tChart1.Legend.TopLeftPos = -16;
	tChart1.Chart.Axes.Left.SetMinMax(0, 2);
}

Color Matching Functions繪圖:

ColorMatchingFUnctions  

The CIE 1931 Color Space Chromaticity Diagram


NTSC系統類別

public class NTSCsystem
{
	public static double xRed = 0.67;
	public static double yRed = 0.33;
	public static double xGreen = 0.21;
	public static double yGreen = 0.71;
	public static double xBlue = 0.14;
	public static double yBlue = 0.08;
	public static double xWhite = 0.310;
	public static double yWhite = 0.316;
}

 

繪製馬蹄形圖

private void DrawHorseArea(DataTable dt, Graphics gg, Image img)
{
	double x = 0, y = 0, z = 0;
	double[] xy;
	int[] rgb = new Int32[3];

	for (int i = 0; i <= img.Width; i+=1)
	{
		for (int j = 0; j <= img.Height; j+=1)
		{
			xy = PixelToCoordinate(i, j, img.Width, img.Height);
			x = xy[0];
			y = xy[1];
			z = 1 - x - y;

			if (IsInsideHorseShape(dt, x, y) == true)
			{
				rgb = rgbData(x, y, z);
				SolidBrush s = new SolidBrush(Color.FromArgb(rgb[0], rgb[1], rgb[2]));
				RectangleF rec = new RectangleF(i, img.Height - j, 1, 1);
				gg.FillRectangle(s, rec);
			}                    
		}
	}
}

 

繪製馬蹄形邊界圖

private void DrawBoundary(DataTable dt, Graphics gg, Image img)
{
	int lamda = 0;
	double StartPointX = 0, StartPointY = 0, StartPointZ = 0;
	double EndPointX = 0, EndPointY = 0, EndPointZ = 0;
	double PositionX = 0, PositionY = 0;
	int[] rgb = new Int32[3];

	//繪製馬蹄形邊界
	for (int i = 0; i < dt.Rows.Count - 1; i++)
	{
		StartPointX = double.Parse(dt.Rows[i]["x"].ToString());
		StartPointY = double.Parse(dt.Rows[i]["y"].ToString());
		StartPointZ = double.Parse(dt.Rows[i]["z"].ToString());

		EndPointX = double.Parse(dt.Rows[i + 1]["x"].ToString());
		EndPointY = double.Parse(dt.Rows[i + 1]["y"].ToString());
		EndPointZ = double.Parse(dt.Rows[i + 1]["z"].ToString());

		rgb = rgbData(StartPointX, StartPointY, StartPointZ);

		SolidBrush s = new SolidBrush(Color.FromArgb(rgb[0], rgb[1], rgb[2]));
		gg.DrawLine(new Pen(s, 1), (float)StartPointX * img.Width, img.Height - (float)StartPointY * img.Height, (float)EndPointX * img.Width, img.Height - (float)EndPointY * img.Height);
	}

	//波長從420nm到680nm,繪製mark
	for (int i = 60; i <= 320; i++)
	{
		lamda = int.Parse(dt.Rows[i]["Lamda"].ToString());

		if (lamda % 10 == 0)//每10nm繪製mark
		{
			if (lamda >= 460 && lamda <= 630)//430nm到450nm以及640nm到670nm不繪製,避免重疊
			{
				PositionX = double.Parse(dt.Rows[i]["x"].ToString());
				PositionY = double.Parse(dt.Rows[i]["y"].ToString());
				SolidBrush s = new SolidBrush(Color.White);
				gg.DrawLine(new Pen(s), (float)PositionX * img.Width, img.Height - (float)PositionY * img.Height, (float)PositionX * img.Width, img.Height - (float)PositionY * img.Height - 10);
				gg.DrawString(lamda.ToString(), new Font("Arial", 7), s, (float)PositionX * img.Width - 5, img.Height - (float)PositionY * img.Height - 20);
			}

			if (lamda == 420 || lamda == 680)
			{
				PositionX = double.Parse(dt.Rows[i]["x"].ToString());
				PositionY = double.Parse(dt.Rows[i]["y"].ToString());
				SolidBrush s = new SolidBrush(Color.White);
				gg.DrawLine(new Pen(s), (float)PositionX * img.Width, img.Height - (float)PositionY * img.Height, (float)PositionX * img.Width, img.Height - (float)PositionY * img.Height - 10);
				gg.DrawString(lamda.ToString(), new Font("Arial", 7), s, (float)PositionX * img.Width - 5, img.Height - (float)PositionY * img.Height - 20);
			}
		}
	}
}

 

繪製馬蹄形邊界底線圖

private void DrawBottomLine(DataTable dt, Graphics gg, Image img)
{
	double x = 0, y = 0, z = 0;
	double x1 = 0, y1 = 0, z1 = 0;
	int[] rgb = new Int32[3];

	double minWavelengthXCoordinate = 0;
	double minWavelengthYCoordinate = 0;
	double maxWavelengthXCoordinate = 0;
	double maxWavelengthYCoordinate = 0;

	//360nm (x,y)
	minWavelengthXCoordinate = double.Parse(dt.Rows[0]["x"].ToString());
	minWavelengthYCoordinate = double.Parse(dt.Rows[0]["y"].ToString());

	//830nm (x,y)
	maxWavelengthXCoordinate = double.Parse(dt.Rows[dt.Rows.Count - 1]["x"].ToString());
	maxWavelengthYCoordinate = double.Parse(dt.Rows[dt.Rows.Count - 1]["y"].ToString());

	double XCoordinateDistance = maxWavelengthXCoordinate - minWavelengthXCoordinate;
	double YCoordinateDistance = maxWavelengthYCoordinate - minWavelengthYCoordinate;
	double Slope = YCoordinateDistance / XCoordinateDistance;

	for (double i = minWavelengthXCoordinate; i < maxWavelengthXCoordinate; i += 0.002)
	{
		//直線方程式: y = (x - xmin) * Slope + ymin
		x = i;
		y = (x - minWavelengthXCoordinate) * Slope + minWavelengthYCoordinate;
		z = 1 - x - y;

		x1 = i + 0.002;
		y1 = (x1 - minWavelengthXCoordinate) * Slope + minWavelengthYCoordinate;
		z1 = 1 - x1 - y1;

		rgb = rgbData(x, y, z);

		SolidBrush s = new SolidBrush(Color.FromArgb(rgb[0], rgb[1], rgb[2]));
		gg.DrawLine(new Pen(s, 1), (float)x * img.Width, img.Height - (float)y * img.Height, (float)x1 * img.Width, img.Height - (float)y1 * img.Height);
	}
}

 

繪製色溫曲線

private void DrawColorTemperatureCurve(DataTable dt, Graphics gg, Image img)
{
	string temp = "";
	double StartPointX = 0, StartPointY = 0;
	double EndPointX = 0, EndPointY = 0;
	double PositionX = 0, PositionY = 0;

	for (int i = 0; i < dt.Rows.Count - 1; i++)
	{
		temp = dt.Rows[i]["Temp"].ToString();

		StartPointX = double.Parse(dt.Rows[i]["x"].ToString());
		StartPointY = double.Parse(dt.Rows[i]["y"].ToString());

		EndPointX = double.Parse(dt.Rows[i + 1]["x"].ToString());
		EndPointY = double.Parse(dt.Rows[i + 1]["y"].ToString());

		PositionX = double.Parse(dt.Rows[i]["x"].ToString());
		PositionY = double.Parse(dt.Rows[i]["y"].ToString());

		SolidBrush s = new SolidBrush(Color.FromArgb(255, 255, 255));
		gg.DrawLine(new Pen(s, 1), (float)StartPointX * img.Width, img.Height - (float)StartPointY * img.Height, (float)EndPointX * img.Width, img.Height - (float)EndPointY * img.Height);

		if (i != 4 && i != 8 && i != 10 && i != 12)
		{
			gg.DrawString(temp.ToString(), new Font("Arial", 5), s, (float)PositionX * img.Width - 5, img.Height - (float)PositionY * img.Height + 10);
			SolidBrush ss = new SolidBrush(Color.FromArgb(255, 0, 0));
			RectangleF rec = new RectangleF((float)PositionX * img.Width - 2, img.Height - (float)PositionY * img.Height - 2, 4, 4);
			gg.FillEllipse(ss, rec);
		}
		else//將重疊的文字置於色溫線上方
		{
			gg.DrawString(temp.ToString(), new Font("Arial", 5), s, (float)PositionX * img.Width - 10, img.Height - (float)PositionY * img.Height - 15);
			SolidBrush ss = new SolidBrush(Color.FromArgb(255, 0, 0));
			RectangleF rec = new RectangleF((float)PositionX * img.Width - 2, img.Height - (float)PositionY * img.Height - 2, 4, 4);
			gg.FillEllipse(ss, rec);
		}
	}
}

繪圖:

CIEColorTemperature  

CIE1931Chart  

 

3.應用:

//讀取檔案
DataTable da = TxtConvertToDataTable(@"D:\TMP\da.csv", "tmp", ",");
DataTable db = TxtConvertToDataTable(@"D:\TMP\db.csv", "tmp", ",");
//定義影像檔
Image img = new Bitmap(500, 500);
//在image上繪圖
Graphics gg = Graphics.FromImage(img);
//繪圖品質設定
gg.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
gg.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
gg.CompositingQuality = CompositingQuality.HighQuality;
gg.SmoothingMode = SmoothingMode.AntiAlias;
gg.InterpolationMode = InterpolationMode.High;
//先將背景色設定為黑色
gg.FillRectangle(new SolidBrush(Color.Black), 0, 0, 500, 500);

//繪製馬蹄形圖
DrawHorseArea(da, gg, img);
//繪製馬蹄形邊界圖
DrawBoundary(da, gg, img);
//繪製馬蹄形邊界底線圖
DrawBottomLine(da, gg, img);
//繪製色溫曲線
DrawColorTemperatureCurve(db, gg, img);

//顯示影像
pictureBox1.Image = img;
arrow
arrow
    文章標籤
    C# CIE 1931 色度圖
    全站熱搜

    西夏普 發表在 痞客邦 留言(49) 人氣()