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繪圖:
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);
}
}
}
繪圖:
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;
文章標籤
全站熱搜

感謝, 獲益良多....
不好意思, 想請問您在繪製馬蹄型圖的PixelToCoordinate及IsInsideHorseShape和rgbData是什麼東西啊?
你好: 程式碼如下,希望可以助你了解,謝謝你的支持。 PixelToCoordinate: private double[] PixelToCoordinate(int x, int y, int width, int height) { double[] coordinate = new double[2]; double xc, yc; xc = (double)x / (double)width; yc = (double)y / (double)height; coordinate[0] = xc; coordinate[1] = yc; return coordinate; } IsInsideHorseShape: private bool IsInsideHorseShape(DataTable dt, double xc, double yc) { bool inside = false; double dx = 0, dy = 0; double n = 0, y = 0; double x1 = 0, x2 = 0, y1 = 0, y2 = 0; for (int i = 0; i < dt.Rows.Count; i++) { x1 = double.Parse(dt.Rows[i]["x"].ToString()); y1 = double.Parse(dt.Rows[i]["y"].ToString()); x2 = double.Parse(dt.Rows[(i + 1) % dt.Rows.Count]["x"].ToString()); y2 = double.Parse(dt.Rows[(i + 1) % dt.Rows.Count]["y"].ToString()); dx = x2 - x1; dy = y2 - y1; //m = dy / dx; //通過兩點的直線方程式:P1=(x1,y1),P2=(x2,y2),Slope=m //m * (x-x1) - (y-y1) = 0; //m = (y-y1)/(x-x1) = (y2-y1)/(x2-x1) //相似三角形比例n = (x-x1)/(x2-x1) = (y-y1)/(y2-y1) = (x-x1)/dx = (y-y1)/dy if ((xc < x1 || xc < x2) && (yc >= y1 || yc >= y2)) { n = (xc - x1) / dx; y = (n * dy) + y1; if ((y <= yc) && n <= 1 && n >= 0) inside = !inside; } } return inside; } rgbData: private int[] rgbData(double x, double y, double z) { int[] rgb = new Int32[3]; int r, g, b; double R, G, B; ArrayList aryRGB = new ArrayList(); aryRGB = XYZtoRGB(x, y, z); R = Clamp((double)aryRGB[0], 0.0, 1.0); G = Clamp((double)aryRGB[1], 0.0, 1.0); B = Clamp((double)aryRGB[2], 0.0, 1.0); double maxRGB = MaxRGB(R, G, B); r = (Int32)Math.Round((255 * R / maxRGB)); g = (Int32)Math.Round((255 * G / maxRGB)); b = (Int32)Math.Round((255 * B / maxRGB)); rgb[0] = r; rgb[1] = g; rgb[2] = b; return rgb; }
感謝!! 另外是我有一個技術上的問題要請教您, 若是此 CIE 圖面要做放大及縮小? 可以利用什麼方式來做? 那放大後解析度會變嗎?
你好, 網路上還蠻多討論將圖檔JPEG格式放大或縮小的方法,放大後解析度會變差 若是想要更改圖片大小,在程式開始定義BMP大小時就先調整成所需要的大小,才不致失真 謝謝 einboch
您好,我想請問一下您的da.csv與db.csv內容是甚麼呢? 方便提供載點嗎?
您好 方便留email到我的信箱einboch@gmail.com,我寄給您 einboch
我找到了~
版主您好: 請問您最後的CIE 1931 色品圖裡面的色彩是如何套上去的,煩請您指導一下好嗎? 謝謝~
版主您好:請問一下da.csv與db.csv是甚麼?
您好, da,db分別是邊界座標及色溫座標資料 einboch
版主您好:請問一下您的da.csv與db.csv內容是甚麼?
您好, 如果需要檔案,請留下email,再將檔案寄給您,謝謝! einboch
*****
版主您好, 請問能否提供da.csv與db.csv檔案呢? 謝謝
您好 檔案已寄到您的郵箱,謝謝您支持 einboch
版主您好, 請問能否提供da.csv與db.csv檔案呢? 謝謝
版主您好, 請問能否提供da.csv與db.csv檔案呢? 麻煩你了,謝謝
*****
請需要檔案留下email,以便寄出da, db檔案,謝謝 einboch
*****
*****
版主您好,感謝您的分享,我也需要da db檔案,麻煩你了 derek0928@hotmail.com
可以順便給我執行檔嗎 謝謝。如果可以的話,麻煩了 protondavid@hotmail.com
Hello , 大大,請問一下 XYZtoRGB 、Clamp 、MaxRGB的內容能否提供一下,謝謝 "derek0928@hotmail.com"
版主您好,感謝分享,請問能否提供 da.csv 與 db.csv 檔案呢? 麻煩您了,Thanks hubert307@gmail.com
您好,請問一下 XYZtoRGB 、Clamp 、MaxRGB的內容能否提供一下,感恩! guan810613@hotmail.com
private static double MaxRGB(double red, double green, double blue) { return Math.Max(Math.Max(red, green), blue); } private static double Clamp(double value, double low, double high) { if (value < low) return low; if (value > high) return high; return value; } private static double[] XYZtoRGB(double xc, double yc, double zc) { var xr = NTSCsystem.xRed; var yr = NTSCsystem.yRed; var zr = 1.0 - xr - yr; var xg = NTSCsystem.xGreen; var yg = NTSCsystem.yGreen; var zg = 1.0 - xg - yg; var xb = NTSCsystem.xBlue; var yb = NTSCsystem.yBlue; var zb = 1.0 - xb - yb; var d = xr * yg * zb - xg * yr * zb - xr * yb * zg + xb * yr * zg + xg * yb * zr - xb * yg * zr; var R = (-xg * yc * zb + xc * yg * zb + xg * yb * zc - xb * yg * zc - xc * yb * zg + xb * yc * zg) / d; var G = ( xr * yc * zb - xc * yr * zb - xr * yb * zc + xb * yr * zc + xc * yb * zr - xb * yc * zr) / d; var B = ( xr * yg * zc - xg * yr * zc - xr * yc * zg + xc * yr * zg + xg * yc * zr - xc * yg * zr) / d; return new [] { R, G, B }; }
版主您好: 請問您最後的CIE 1931 的3.應用是如何使用呢?是不是缺少private void{}什麼的? 感謝分享,請問能否提供 da.csv 與 db.csv 檔案呢? 麻煩您了,謝謝您 wate9123@yahoo.com.tw
我是樓上#23 更正信箱wate9123@yahoo.com.tw
請問可以提供da.csv與db.csv檔案嗎?感謝你 charles7668@yahoo.com.tw
我也需要 da.csv 與 db.csv 檔,可以提供嗎? 謝謝你~ Email : gag19800328@gmail.com
*****
*****
版主您好,感謝分享,請問能否提供 da.csv 與 db.csv 檔案呢? 都還沒有收到檔案 拜託了
版主您好,我也需要da.csv與db.csv檔案,可以提供嗎? 謝謝你 Email : william_8201@yahoo.com.tw
Can you please send me the csv files? My email is marko.watzak@hotmail.com Thank you,
版主您好,我也需要da.csv與db.csv檔案,可以提供嗎? 謝謝你 Email : spadeb47339@gmail.com
版主好, 我正需要da.csv/db.csv, 可否方便提供呢? roface@livemail.tw 謝謝
版主好, 我正需要da.csv/db.csv, 可否方便提供呢? kwanyutimmychu@hotmail.com 謝謝
版主好, 我也需要da.csv、db.csv ezpstmb2013@gmail.com 謝謝
Can you please send me the da.csv and db.csv files? My email is lephuongthao2597@gmail.com Thank you,
版主好, 我正需要da.csv 與 db.csv, 可否方便提供呢? kkcheah318@gmail.com 謝謝
版主好, 我正需要da.csv 與 db.csv, 可否方便提供呢? ctlee1@hotmail.com 謝謝
西夏普 大大 : 學習了 可否跟您要 da.csv 與 db.csv 呢? 感謝!! s89205053@gmail.com
西夏普 大大 你好 : 感謝你無私的分享!! 關於此 CIE 1931 色度圖,想跟您請教: ------------ 從 NTSC System ,白點定義 x= 0.310, y = 0.316 我是否可依照畫色溫點的公式 去得出 白點 的像素座標呢 ? 如下: Px = 500(W) * 0.310 = 155 Px Py = 500(H) - (0.316) = 342 Px *** 但如上的計算,從色度圖中的像素座標對應的卻是 '藍色' ??? *** 想請教 西夏普 大大,此 色度圖 的設計,是允許我代入 色座標 xy,然後可得出 對應圖上的像素座標嗎? 感謝 Orz
版主你好,我也需要da、db的csv,可否與您要一份呢?非常感恩,謝謝 email : ph0981620209@gmail.com
版主您好 我也想要da db的檔案 是否方便發給我呢謝謝 it5606@gmail.com
版主您好, 請問能否提供da.csv與db.csv檔案呢? 謝謝您 vitohhh0810@gmail.com
Nice job ! Can you send me da.csv and db.csv ? leonnaej@gmail.com Many thank
版主您好, 請問能否提供da.csv與db.csv檔案呢? 謝謝您 yywang001.msn.com
版主您好, 請問能否提供da.csv與db.csv檔案呢? 謝謝您 yep0810@gmail.com
如果可以的話,請把C# sln專案內的所有檔案寄給我好嗎? 謝謝
版主,你好: 拜讀你的經驗分享後,因為網路上找不到da.csv and db.csv;不知道有沒有機會得到這兩個檔案資料。 感謝。 email account: andrewu0920@gmail.com
您好,我想請問一下您的da.csv與db.csv內容是甚麼呢? 方便提供載點嗎? david1210712@gmail.com