【问题标题】:Convert double value to RGB Color in c#在c#中将double值转换为RGB颜色
【发布时间】:2013-12-05 21:53:11
【问题描述】:

我正在尝试将双精度值(介于 0 和 1 之间)转换为 RGB 颜色。在下面的代码中,您可以看到我试图做什么,但我觉得这个算法有问题。我没有得到所有的颜色。当我从 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] 区间内,在我的情况下,我使用 s 和 v 等于 1m extending this interval to [0, 360] in order to use the algorithm for converting HSV to RGB. Im。这是更好解释的代码。

        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 颜色有 3 个独立的轴。双倍只有 1。你想通过颜色立方体的哪条路径?
  • 知道如果 X 的值在 0.5 到 1 之间,它可以是任何蓝色等于 0 的颜色。否则它将是任何红色等于 0 的颜色。
  • Knowing that if the value X is between 0.5 to 1 it can be any color with blue equal to 0. Otherwise it will be any color with red equal to 0。这并不容易理解。尝试更好地制定这个,也许你可以编写正确的代码。
  • 你会例如永远不要在代码中使用白色作为颜色,因为蓝色为 0 或红色为 0。就像 AShelly 所说,我们至少需要知道预期行为的一些界限。
  • 另外——你在图纸上提供的公式倾向于偏绿色——这是你的意图吗?

标签: c# colors


【解决方案1】:

正如上面在 cmets 中提到的,您绘制的公式不满足您均匀跨越整个颜色范围的条件。我相信这应该可行(不是迄今为止唯一可能的解决方案):

*编辑:修正公式,之前没有生成所有可能的颜色

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

【讨论】:

  • 我要试试。谢谢
  • 不幸的是,我已经尝试过了,但仍然不是所有颜色都显示在节点地图中(我从双精度值中知道,除了使用此公式显示的颜色之外,还应该有一些其他颜色: S)。在 matlab 中的类似实现中,我看到的颜色比在我的应用程序中更多,但我不知道他们是如何做到的......
  • 请忽略之前的版本,睡眠不足。现在应该可以正常工作了。
  • 嗯,肯定有问题,因为它仍然无法正常工作。我确定这应该是一种方法,但我仍然找不到。我希望这里有人可以知道这样的方式......
  • 这个“不工作”到底是怎么回事?你声称有一些颜色没有被这个代表?你能确定它们的RGB值吗?另外 - 你确定这些数字没有一些在这里可能很重要的特定分布吗?
【解决方案2】:

在您的一个 cmets 中,您说:“不,我的意图是包含所有颜色,我不想偏爱其中任何一种。只是我想要将双精度值转换为 RGB 颜色的最佳方法”

因此,您不必关心doubleColor 之间的实际关系,并且您不想以某种与其@987654332 一致的方式对double 值进行操作@同行。 在这种情况下,事情比你想象的要容易。

我可以提醒您,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;
}

现在,假设您想以这样一种方式编排编译器和即将到来的运行时,以便将 RGB 字段(每个字段占用 1 个字节)连续布局,并且AsDouble 字段在前 3 个字节中与它们重叠,并继续使用它自己的,仅剩下 5 个字节。你是怎么做到的?

您使用上述属性来指定:

  1. 您正在控制struct 的布局这一事实(小心,权力越大责任越大)
  2. RGBstruct内的第0、第1和第2个字节开始(因为我们知道byte占用1个字节)并且AsDouble也开始struct 内的第 0 个字节。

属性位于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(有点)实例中的内存的样子:

还有什么比几个扩展方法更好的方法来包装它:

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 encoding standard(这在所有语言、框架和很可能大多数处理器之间都很常见),您将得出结论(我也测试过),通过遍历所有8 字节双精度的 3 个最低有效字节(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(或ε)误认为是指数字母edouble.Epsilon 在某种程度上代表了它的微积分对应物,这可能意味着大于 0 的最小实数。

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

我们有NN2²⁴double 数字从0 开始并以ε * (N-1) 结尾(其中εdouble.Epsilon 是最小的double 大于@ 987654391@).

从某种意义上说,我们创建的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 上遇到这么多麻烦呢? 因为它要快得多,因为它不涉及任何数学运算,而且我们能够根据RGB 输入随机访问N 值中的任何一个。

现在,进入线性变换位。

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

在我们之前创建的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

【讨论】:

  • 有了所有这些出色的解释,我必须接受这个答案,即使我也必须接受我想要一些不同的东西(我可以在解释中很好地解释的东西)。您的回答是正确的,因为我的问题可能不够清楚:S。确实感谢您的回答。我真的从这个答案中学到了一些很棒的东西,我稍后会使用这些
  • 也许解释我的需求的最好方法是使用从 matlab 中获取的一些结果......以及我想在 C# 中实现的那些结果。在 matlab 中,如果我有一个介于 0 和 1 之间的矩阵 od double 值,我可以轻松地将其显示为 HSV 图像。示例:im = [0 .5 1; .7 .6 .2; .9 .3 .4]; cm = colormap('hsv'); cdata = interp1(linspace(0,1,length(cm)),cm,im);图;图像(cdata)。使用矩阵(或 256 个双精度值的数组)我得到了这个结果:dropbox.com/s/d6dnbyfzgookjl1/… 如您所见,它是一个 16 X 16 的地图,其节点的值介于 0 和 1 之间。
  • ...我什至使用 c# 通过使用我的问题中解释的 HSVtoRGB 算法实现了这一点。我想要完全相同的东西,但不是 HSV。我想要它与 RGB 颜色空间。因为 HSV 不包括某些颜色。我希望我很清楚......如果我没有,那么请跳过我在这里问过的所有问题,对于给您带来的不便,我们深表歉意。
  • 对于 SOM,必须在彼此接近的双精度值之间存在关系(EX 0.001 和 0.002),并且在表示节点时也应该映射两个接近值之间的关系(每个节点都有自己的自己的双值)使用颜色......但是用你的代码我无法实现它。我知道有一种方法可以实现它,因为人们在我之前就这样做了,我正在研究如何做到这一点,但最好的结果仍然是使用 H = 双值、S = 1 和 V = 1 的 HSVtoRGB 算法;
  • 使用来自 0...256 (0, 1 ,2 ,3 ,4 ,5...256) 的双精度值我从 matlab 得到这个结果:dropbox.com/s/d6dnbyfzgookjl1/… ...并使用你的代码我得到了这个结果...dropbox.com/s/nf684xeialxn52g/…
猜你喜欢
  • 2013-12-10
  • 2017-09-11
  • 1970-01-01
  • 2012-07-27
  • 1970-01-01
  • 2019-02-08
  • 2013-06-15
  • 2014-06-05
  • 2017-06-24
相关资源
最近更新 更多