我试图将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;
}
Run Code Online (Sandbox Code Playgroud)
这是表达我想要做的最好的图像.
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在我的情况下使用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;
}
Run Code Online (Sandbox Code Playgroud)
Edu*_*tru 12
在你的一条评论中,你说:"不,我的目的是包括所有颜色,我不想赞成其中任何一种颜色.简单地说,我想要将双值转换为RGB颜色的最佳方法"
所以,你不关心实际的关系是什么之间double以及Color和你不希望在工作double方式,也就是用自己莫名其妙一致的值Color对应.在这种情况下,事情比你想象的容易.
我可能会提醒你RGB颜色由3个字节组成,但是出于组合的原因,.NET BCL类Color提供3个组件作为int值.
所以你有3个字节!A double占用8个字节.如果我的假设是正确的,那么在这个答案的最后你可能会考虑float作为一个更好的候选人(如果一个较小的足迹对你来说很重要,当然).
足够的聊天,对实际问题.我即将展示的方法与数学和内存管理和编码没有多大联系.
您是否听说过StructLayoutAttribute属性及其随行人员的FieldOffsetAttribute属性?如果你没有,你可能会被他们敬畏.
假设你有一个结构,让我们称它为CommonDenominatorBetweenColoursAndDoubles.假设它包含4个公共字段,如下所示:
public struct CommonDenominatorBetweenColoursAndDoubles {
public byte R;
public byte G;
public byte B;
public double AsDouble;
}
Run Code Online (Sandbox Code Playgroud)
现在,假设您想以这样的方式编排编译器和即将运行的运行符,因此R,G和B字段(每个占用1个字节)是连续布局的,并且AsDouble字段在它的前3个字节中重叠它们并继续它是自己的,专门保留5个字节.你是怎样做的 ?
您可以使用上述属性指定:
struct的布局(小心,有很大的力量,这是很大的责任)R,G并且B从第0个,第1个和第2个字节开始struct(因为我们知道byte占用1个字节)并且AsDouble也从第0个字节开始struct.这些属性mscorlib.dll位于System.Runtime.InteropServices命名空间下,您可以在此处阅读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;
}
Run Code Online (Sandbox Code Playgroud)
这是struct(有点)实例中的内存:

还有什么比一些扩展方法更好的方式来包装它:
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;
}
Run Code Online (Sandbox Code Playgroud)
我也对此进行了测试,以确保它是防弹的,据我所知,你不必担心一件事:
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");
}
}
}
Run Code Online (Sandbox Code Playgroud)
完成后不会产生任何错误.
编辑
在我开始编辑之前:如果你稍微研究double编码标准(这在所有语言,框架和最可能是大多数处理器之间是通用的),你将得出结论(我也测试过)通过迭代3的所有组合8字节双精度的最低有效字节(24个最低有效位),这就是我们在这里所做的事情,你最终会得到double数值0在下端和double.Epsilon * (256 * 3 - 1)另一端(包含)的数学界限.当然,如果剩余的更重要的5个字节用0s 填充,那也是如此.
如果它已经不清楚,double.Epsilon * (256 * 3 - 1)是一个令人难以置信的小数字,人们甚至无法发音.你对发音的最好的注意力是:它是介于两者之间2²?的最小正double数0(非常小)或者它是否更适合你:8.28904556439245E-317.
在该范围内,您将发现具有精确256 * 3的2²?"连续" double值,这些值以可能0的最小double距离开始并以其分隔.
通过数学(逻辑值)操作(而不是通过直接存储器寻址),您可以轻松地将该范围的2²?数字从原始数据0 .. double.Epsilon * (2²? - 1)扩展到0 .. 1.
这就是我所说的:

不要将double.Epsilon(或?)误认为是指数字母e.
double.Epsilon以某种方式表示它的微积分对应物,它可能意味着最大的实数大于0.
所以,为了确保我们为编码做好准备,让我们回顾一下这里发生的事情:
我们已N(N被2²?)double人数从开始0和结尾的? * (N-1)(其中?,或者double.Epsilon是最小double大于0).
从某种意义上说,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;
}
Run Code Online (Sandbox Code Playgroud)
那么,为什么我们经历了所有那些麻烦struct?因为它的速度快了很多,因为它不涉及任何数学运算,并且我们能够随机访问的任何N值的基础上R,G和B投入.
现在,转到线性变换位.
所有我们现在要做的是位数学(这将需要更长的时间来计算,因为它涉及到浮点运算,但将成功地扩展我们的双打范围内均匀分布的一个之间0和1):
在struct我们之前创建的内容中,我们将重命名该AsDouble字段,将其设置为private并创建一个名为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; }
}
}
Run Code Online (Sandbox Code Playgroud)
你会惊喜地发现我在编辑之前提出的测试仍然可以正常工作,这个新增加了,所以你有0%的信息丢失,现在双倍范围同样延伸0 .. 1.