加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-16 00:10:01 所属栏目:百科 来源:网络整理
导读:我试图将double值(介于0和1之间)转换为RGB Color.在下面的代码中,您可以看到我尝试做什么,但我觉得这个算法有问题.我没有得到所有的颜色.当我从double转换为int或者我不确定时,可能有松散的信息……但是请看看它,如果你有任何建议或任何其他方法(经过验证的
我试图将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,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]间隔,我将此间隔扩展到[0,360],以便使用该算法将HSV转换为RGB.在我的情况下,我使用s和v等于1.以下是更好解释的代码.

private Color generateRGB(double X)
        {
            Color color;

            int red;
            int green;
            int blue;
            HsvToRgb(X*360,1,out red,out green,out blue);

            color = Color.FromArgb(red,green,blue);

            return color;
        }

解决方法

在你的一条评论中,你说:“不,我的目的是包括所有颜色,我不想赞成其中任何一种颜色.简单地说,我希望将双重值转换为RGB颜色的最佳方法”

因此,您不关心double和Color之间的实际关系,并且您不希望以某种方式对Double值进行操作.
在这种情况下,事情比你想象的容易.

我可能会提醒你RGB颜色由3个字节组成,但是出于组合的原因,.NET BCL类Color将3个组件作为int值提供.

所以你有3个字节!
双重占用8个字节.
如果我的假设是正确的,那么在这个答案的最后你可能会考虑浮动作为一个更好的候选人(当然,如果一个较小的足迹对你很重要).

足够的聊天,对实际问题.
我即将展示的方法与数学和内存管理和编码没有多大联系.

您是否听说过StructLayoutAttribute属性及其随行人员,FieldOffsetAttribute属性?
如果你没有,你可能会被他们敬畏.

假设你有一个结构,我们称之为CommonDenominatorBetweenColoursAndDoubles.
假设它包含4个公共字段,如下所示:

public struct CommonDenominatorBetweenColoursAndDoubles {
    public byte R;
    public byte G;
    public byte B;

    public double AsDouble;
}

现在,假设您想以这样的方式编排编译器和即将运行的运行符,以便R,G和B字段(每个占用1个字节)连续布局,并且AsDouble字段在它的前3个重叠它们字节并继续使用它自己的,专门保留5个字节.你是怎样做的 ?

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

>你正在控制结构的布局这一事实(小心,强大的力量带来了很大的责任)
> R,G和B从结构中的第0个,第1个和第2个字节开始(因为我们知道该字节占用1个字节)并且AsDouble也从结构内的第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(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 encoding standard(这在所有语言,框架和最可能是大多数处理器之间是常见的),你将得出结论(我也测试过)通过迭代3个的所有组合8字节双精度的有效字节(24个最低有效位),这就是我们在这里所做的,你最终会得到双值,它们在数学上以低端0和double.Epsilon *(256 *)为界. 3 – 1)在另一端(包括).当然,如果剩下的更重要的5个字节用0填充,那也是如此.

如果已经不清楚了,double.Epsilon *(256 * 3 – 1)是一个令人难以置信的小数字,人们甚至无法发音.
你发音的最佳镜头是:它是22?和最小正双倍大于0(非常小)或者它更适合你的产品:8.28904556439245E-317.

在该范围内,你会发现你有精确的256 * 3,这是2“连续”的双倍值,从0开始,以最小的双倍距离分开.

通过数学(逻辑值)操作(不通过直接存储器寻址),您可以轻松地将原始0 … double.Epsilon *(22? – 1)的22?数字范围扩展到0 .. 1.

这就是我所说的:

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

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

我们有N(N为22?)双数,以ε*(N-1)结束(其中ε或double.Epsilon是大于0的最小双数).

从某种意义上说,我们创建的结构实际上只是帮助我们做到这一点:

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;

}

那么,为什么我们经历了结构的所有麻烦?
因为它更快,因为它不涉及任何数学运算,并且我们能够基于R,G和B输入随机访问N个值中的任何一个.

现在,转到线性变换位.

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

在我们之前创建的结构中,我们将重命名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之间拉伸.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读