我正在尝试将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
[更新]
这就是我的表现,这似乎是一个很好的解决方案。请看看它并告诉我天气这是更好的代表性(也许那些对色彩空间有更好了解的人可以给出反馈)
我从这里使用了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使用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;
}
答案 0 :(得分:11)
在你的一条评论中,你说:" no我的目的是包括所有的颜色,我不想赞成他们中的任何一种。简单地说,我想要将双值转换为RGB颜色的最佳方法"
因此,您并不关心double
和Color
之间的实际关系,并且您不希望对double
中的Color
值进行操作一种与他们的Color
同行一致的方式。
在这种情况下,事情比你预期的要容易。
我可能会提醒您,RGB颜色由3个字节组成,但由于组合原因,.NET BCL类int
提供3个组件作为double
值。
所以你有3个字节!
float
占用8个字节。
如果我的假设是正确的,那么在这个答案的最后你可能会考虑StructLayoutAttribute
作为一个更好的候选人(当然,如果一个较小的足迹对你很重要)。
足够的聊天,对实际问题。 我即将布局的方法与数学和内存管理和编码没有那么多联系。
您是否听说过FieldOffsetAttribute
属性及其随行人员CommonDenominatorBetweenColoursAndDoubles
属性?
万一你没有被他们敬畏。
假设你有一个结构,我们称之为public struct CommonDenominatorBetweenColoursAndDoubles {
public byte R;
public byte G;
public byte B;
public double AsDouble;
}
。
让我们说它包含4个公共字段,如下所示:
R
现在,假设您希望以这样的方式编排编译器和即将发生的运行时,以便G
,B
和AsDouble
字段(每个占用1个字节)连续布局并且struct
字段在它们的前3个字节中重叠它们并继续它自己的,仅保留5个字节。你是怎么做到的?
使用上述属性指定:
R
布局的事实(小心,功能强大,责任重大)G
,B
和struct
从byte
内的第0,1第1和第2个字节开始的事实(因为我们知道AsDouble
占据了struct
1个字节)mscorlib.dll
也从System.Runtime.InteropServices
内的第0个字节开始。属性位于[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
,您可以在此处StructLayout和FieldOffset了解这些属性。
所以你可以实现所有这些:
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
我也对此进行了测试,以确保它具有防弹功能,据我所知,您不必担心任何事情:
0
完成后不会产生任何错误。
修改强>
在我开始编辑之前:如果你研究double
encoding standard一点(这在所有语言,框架和最可能的大多数处理器之间是通用的)你将得出结论(我也测试过)通过迭代通过8字节双精度的3个最低有效字节(24个最低有效位)的所有组合,这就是我们在这里正在做的事情,你将得到double.Epsilon * (256 * 3 - 1)
值,这些值在数学上受到0
位于低端,double.Epsilon * (256 * 3 - 1)
位于另一端(包含)。当然,如果剩下的更重要的5个字节用2²⁴
s填充,那就是这样。
如果它已经不清楚,double
是一个非常小的数字,人们甚至无法发音。
你对发音的最佳看法是:它是0
和最小正8.28904556439245E-317
大于256 * 3
(非常小)之间的产品,或者它是否更适合你:2²⁴
。
在该范围内,您会发现您的double
正是0
&#34;连续&#34; 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;
}
)。
从某种意义上说,我们创建的struct
实际上只是帮助我们做到这一点:
N
那么,为什么我们会遇到R
的所有麻烦?
因为它更快,因为它不涉及任何数学运算,并且我们能够根据G
,{{1}随机访问B
值中的任何一个}}和0
输入。
现在,转到线性转换位。
我们现在要做的只是一些数学运算(因为它涉及浮点运算需要更长时间才能计算,但是会成功地将我们的双精度范围扩展到1
和{之间的均匀分布范围。 {1}}):
在我们之前创建的struct
中,我们将重命名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; }
}
}
您会惊喜地发现我在 EDIT 之前提出的测试仍然正常工作,这个新增功能,因此您有0%的信息丢失,现在双打的范围是同等地延伸到0 .. 1
。
答案 1 :(得分: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