[英]Convert double value to RGB Color in c#
我試圖將double值(介於0和1之間)轉換為RGB Color。 在下面的代碼中,您可以看到我嘗試做什么,但我覺得這個算法有問題。 我沒有得到所有的顏色。 當我從double轉換為int或者我不確定時,可能有松散的信息......但是請看看它,如果你有任何建議或任何其他方法(經過驗證的方法),請告訴我:
private Color generateRGB(double X)
{
Color color;
if (X >= 0.5) //red and half of green colors
{
int Red = (int)((2 * X - 1) * 255);
int Green = (int)((2 - 2 * X) * 255);
int Blue = 0;
color = Color.FromArgb(Red, Green, Blue);
}
else // blue and half of green colors
{
int Red = 0;
int Green = (int)((2 * X) * 255);
int Blue = (int)((1 - 2 * X) * 255);
color = Color.FromArgb(Red, Green, Blue);
}
return color;
}
這是表達我想要做的最好的圖像。
https://www.dropbox.com/s/bvs3a9m9nc0rk5e/20131121_143044%20%281%29.jpg
[更新]
這就是我的方式,這似乎是一個很好的解決方案。 請看看它,並告訴我天氣這是更好的表示與否(也許那些對Color Spaces有更好了解的人可以給出反饋)
我從這里使用了HSVtoRGB轉換算法: http : //www.splinter.com.au/converting-hsv-to-rgb-colour-using-c/ 。 知道我的值是[0,1]間隔,我m extending this interval to [0, 360] in order to use the algorithm for converting HSV to RGB. I
m extending this interval to [0, 360] in order to use the algorithm for converting HSV to RGB. I
M在我的情況下,使用S和V等於1。 以下是更好解釋的代碼。
private Color generateRGB(double X)
{
Color color;
int red;
int green;
int blue;
HsvToRgb(X*360,1,1,out red,out green,out blue);
color = Color.FromArgb(red, green, blue);
return color;
}
在你的一條評論中,你說:“不,我的目的是包括所有顏色,我不想贊成其中任何一種顏色。簡單地說,我希望將雙重值轉換為RGB顏色的最佳方法”
所以,你不關心實際的關系是什么之間double
和Color
,你不希望在操作double
方式,也就是用自己莫名其妙一致的值Color
對應。 在這種情況下,事情比你想象的容易。
我可能會提醒你RGB顏色由3個字節組成,但是出於組合的原因,.NET BCL類Color
將3個組件作為int
值提供。
所以你有3個字節! double
占用8個字節。 如果我的假設是正確的,那么在這個答案的最后你可能會考慮float
作為一個更好的候選人(當然,如果一個較小的足跡對你很重要)。
足夠的聊天,對實際問題。 我即將展示的方法與數學和內存管理和編碼沒有多大聯系。
您是否聽說過StructLayoutAttribute
屬性及其隨行人員, FieldOffsetAttribute
屬性? 如果你沒有,你可能會被他們敬畏。
假設你有一個結構,我們稱之為CommonDenominatorBetweenColoursAndDoubles
。 假設它包含4個公共字段,如下所示:
public struct CommonDenominatorBetweenColoursAndDoubles {
public byte R;
public byte G;
public byte B;
public double AsDouble;
}
現在,假設您想以這樣的方式編排編譯器和即將運行的運行AsDouble
,以便R
, G
和B
字段(每個占用1個字節)連續布局,並且AsDouble
字段在它的前3個重疊它們字節並繼續使用它自己的,專門保留5個字節。 你是怎樣做的 ?
您可以使用上述屬性指定:
struct
的布局(小心,有很大的力量,這是很大的責任) R
G
並B
在內的第0,第一和第二字節開始struct
(因為我們知道byte
占用1個字節),並且AsDouble
也開始於第0字節中,內struct
。 這些屬性位於System.Runtime.InteropServices
命名空間下的mscorlib.dll
,您可以在此處閱讀StructLayout和此處的FieldOffset 。
所以你可以實現所有這些:
[StructLayout(LayoutKind.Explicit)]
public struct CommonDenominatorBetweenColoursAndDoubles {
[FieldOffset(0)]
public byte R;
[FieldOffset(1)]
public byte G;
[FieldOffset(2)]
public byte B;
[FieldOffset(0)]
public double AsDouble;
}
這是struct
(kinda)實例中的內存如下所示:
還有什么比一些擴展方法更好的方式來包裝它:
public static double ToDouble(this Color @this) {
CommonDenominatorBetweenColoursAndDoubles denom = new CommonDenominatorBetweenColoursAndDoubles ();
denom.R = (byte)@this.R;
denom.G = (byte)@this.G;
denom.B = (byte)@this.B;
double result = denom.AsDouble;
return result;
}
public static Color ToColor(this double @this) {
CommonDenominatorBetweenColoursAndDoubles denom = new CommonDenominatorBetweenColoursAndDoubles ();
denom.AsDouble = @this;
Color color = Color.FromArgb (
red: denom.R,
green: denom.G,
blue: denom.B
);
return color;
}
我也對此進行了測試,以確保它是防彈的,據我所知,你不必擔心一件事:
for (int x = 0; x < 255; x++) {
for (int y = 0; y < 255; y++) {
for (int z = 0; z < 255; z++) {
var c1 = Color.FromArgb (x, y, z);
var d1 = c1.ToDouble ();
var c2 = d1.ToColor ();
var x2 = c2.R;
var y2 = c2.G;
var z2 = c2.B;
if ((x != x2) || (y != y2) || (z != z2))
Console.Write ("1 error");
}
}
}
完成后不會產生任何錯誤。
編輯
在我開始編輯之前:如果你研究一下double
編碼標准 (這在所有語言,框架和最可能是大多數處理器之間是通用的),你將得出結論(我也測試過)通過迭代所有組合的8字節雙double.Epsilon * (256 * 3 - 1)
3個最低有效字節(24個最低有效位),這就是我們在這里所做的,你將得到double
值,它們在數學上以低端0
和double.Epsilon * (256 * 3 - 1)
在另一端(包括)。 當然,如果剩下的更重要的5個字節用0
秒填充,那也是如此。
如果已經不清楚了, double.Epsilon * (256 * 3 - 1)
是一個令人難以置信的小數字,人們甚至無法發音。 你發音的最佳鏡頭是:它是2²⁴
和最小正double
大於0
(非常小)或者它更適合你的產品: 8.28904556439245E-317
。
在該范圍內,你會發現你有精確的256 * 3
,這是2²⁴
“連續”的double
值,從0
開始,以最小的double
距離分開。
通過數學(邏輯值)操作(不通過直接存儲器尋址),您可以輕松地將原始0 .. double.Epsilon * (2²⁴ - 1)
的2²⁴
數字范圍2²⁴
到0 .. 1
。
這就是我所說的:
不要將double.Epsilon
(或ε
)誤double.Epsilon
是指數字母e
。 double.Epsilon
在某種程度上是它的微積分對應物的表示,它可能意味着最大的實數大於0
。
所以,為了確保我們為編碼做好准備,讓我們回顧一下這里發生的事情:
我們有N
( N
為2²⁴
) double
數,從0
開始,以ε * (N-1)
(其中ε
或double.Epsilon
是大於0
最小double
數)。
從某種意義上說,我們創建的struct
實際上只是幫助我們做到這一點:
double[] allDoubles = new double[256 * 256 * 256];
double cursor = 0;
int index = 0;
for (int r = 0; r < 256; r++)
for (int g = 0; g < 256; g++)
for (int b = 0; b < 256; b++) {
allDoubles[index] = cursor;
index++;
cursor += double.Epsilon;
}
那么,為什么我們經歷了struct
所有麻煩? 因為它更快,因為它不涉及任何數學運算,並且我們能夠基於R
, G
和B
輸入隨機訪問N
值中的任何一個。
現在,轉到線性變換位。
我們現在要做的只是一些數學運算(計算需要更長的時間,因為它涉及浮點運算,但會成功地將我們的雙精度范圍擴展到0
到1
之間的均勻分布):
在我們之前創建的struct
,我們將重命名AsDouble
字段,將其AsDouble
私有並創建一個名為AsDouble
的新屬性來處理轉換(兩種方式):
[StructLayout(LayoutKind.Explicit)]
public struct CommonDenominatorBetweenColoursAndDoubles {
[FieldOffset(0)]
public byte R;
[FieldOffset(1)]
public byte G;
[FieldOffset(2)]
public byte B;
// we renamed this field in order to avoid simple breaks in the consumer code
[FieldOffset(0)]
private double _AsDouble;
// now, a little helper const
private const int N_MINUS_1 = 256 * 256 * 256 - 1;
// and maybe a precomputed raw range length
private static readonly double RAW_RANGE_LENGTH = double.Epsilon * N_MINUS_1;
// and now we're adding a property called AsDouble
public double AsDouble {
get { return this._AsDouble / RAW_RANGE_LENGTH; }
set { this._AsDouble = value * RAW_RANGE_LENGTH; }
}
}
你會驚喜地發現我在編輯之前提出的測試仍然可以正常使用這個新增功能,所以你有0%的信息丟失,現在雙倍范圍同樣在0 .. 1
之間拉伸。
如上面評論中所述,您繪制的公式不滿足您統一跨越整個顏色范圍的條件。 我相信這應該有效(不是迄今為止唯一可行的解決方案):
* 編輯:修正了公式,之前沒有生成所有可能的顏色
int red = Math.Min((int)(X * 256), 255);
int green = Math.Min((int)((X * 256 - red) * 256), 255);
int blue = Math.Min((int)(((X * 256 - red) * 256 - green) * 256), 255);
Math.Min
用於將邊界場景修復為X -> 1D
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.