在c#中将double值转换为RGB Color

Dri*_*ill 3 c# colors

我试图将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,GB字段(每个占用1个字节)是连续布局的,并且AsDouble字段在它的前3个字节中重叠它们并继续它是自己的,专门保留5个字节.你是怎样做的 ?

您可以使用上述属性指定:

  1. 事实上,你正在控制你struct的布局(小心,有很大的力量,这是很大的责任)
  2. 事实是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²?的最小正double0(非常小)或者它是否更适合你:8.28904556439245E-317.

在该范围内,您将发现具有精确256 * 32²?"连续" double值,这些值以可能0的最小double距离开始并以其分隔.

通过数学(逻辑值)操作(而不是通过直接存储器寻址),您可以轻松地将该范围的2²?数字从原始数据0 .. double.Epsilon * (2²? - 1)扩展到0 .. 1.

这就是我所说的:

线性变换

不要将double.Epsilon(或?)误认为是指数字母e. double.Epsilon以某种方式表示它的微积分对应物,它可能意味着最大的实数大于0.

所以,为了确保我们为编码做好准备,让我们回顾一下这里发生的事情:

我们已N(N2²?)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,GB投入.

现在,转到线性变换位.

所有我们现在要做的是位数学(这将需要更长的时间来计算,因为它涉及到浮点运算,但将成功地扩展我们的双打范围内均匀分布的一个之间01):

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.