gpt4 book ai didi

c# - 在C#中将 double 值转换为RGB颜色

转载 作者:行者123 更新时间:2023-11-30 13:22:37 25 4
gpt4 key购买 nike

我正在尝试将双精度值(介于0和1之间)转换为RGB颜色。在下面的代码中,您可以看到Im试图执行的操作,但我认为该算法存在问题。我没有得到所有的颜色。当我从double转换为int或Im不确定时,信息可能会很松散。但是请查看一下,如果您有任何建议或任何其他方法(已验证的一种方法),请告诉我:

    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

[更新]

那就是我的做法,这似乎是一个很好的解决方案。请看看它,然后告诉我天气这是否可以更好地表示(也许那些对Color Spaces有更好了解的人可以提供有关它的反馈)

我从这里使用了HSVtoRGB转换算法: http://www.splinter.com.au/converting-hsv-to-rgb-colour-using-c/
知道我的值是在[0,1]间隔时,在我的情况下,使用s和v等于1的I m extending this interval to [0, 360] in order to use the algorithm for converting HSV to RGB. I m。这是用于更好解释的代码。

        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;
}

最佳答案

在您的评论中,您说过:“不,我的目的不是要包含所有颜色,我也不想支持它们中的任何一种。简单地说,我想将double值转换为RGB颜色的最佳方法”

因此,您不必担心doubleColor之间的实际关系是什么,并且您不想以某种方式与double对应项保持一致的方式对Color值进行操作。
在这种情况下,事情比您预期的要容易。

可能我提醒您,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个字节)连续排列,并且 字段在它们的前3个字节中与它们重叠,并继续其自身的内容(仅剩余5个字节)。你是怎样做的 ?

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


您正在控制 AsDouble的布局这一事实(请注意,功能强大,责任重大)
structRGB的第0、1和2个字节开始的事实(因为我们知道 struct占用1个字节),而 byte也从0处开始字节,在 AsDouble中。


这些属性在 struct命名空间下的 mscorlib.dll中找到,您可以在 StructLayoutFieldOffset处阅读有关它们的信息。

因此,您可以像这样实现所有这些目标:

[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;

}


这是 System.Runtime.InteropServices(种类)实例中的内存如下所示:



与几种扩展方法相比,还有什么比这更好的方法了:

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");

}
}
}


完成此操作不会产生任何错误。

编辑

在开始编辑之前:如果稍微研究一下 struct encoding standard(这在所有语言,框架和大多数处理器之间很常见),您将得出结论(我也测试了),该结论是通过迭代所有8字节双精度码中的3个最低有效字节(24个最低有效位),这就是我们在这里所做的,最终将得到 double值,该值在数学上受下限由 double和< cc>在另一端(包括两端)。当然,如果剩余的更高5个字节用 0填充,那是对的。

如果还不清楚, double.Epsilon * (256 * 3 - 1)是一个很小的数字,人们甚至都不会发音。
最好的发音是: 0和大于 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.Epsilon0NN个数字,它们从 2²⁴开始,以 double结尾(其中 0ε * (N-1)小于 ε的最小 double.Epsilon) 。

从某种意义上说,我们创建的 double实际上只是在帮助我们实现这一点:

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;

}


那么,为什么我们要解决 0的所有麻烦呢?
因为它要快得多,因为它不涉及任何数学运算,并且我们能够基于 structstructN输入来随机访问任何 R值。

现在,转到线性变换位。

现在,我们要做的只是一点数学运算(由于它涉及浮点运算,因此计算将花费更长的时间,但会成功地将我们的双精度范围扩展到 GB之间的均等分布):

在我们先前创建的 0中,我们将重命名 1字段,将其设置为私有,并创建一个名为 struct的新属性来处理转换(两种方式):

[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%,现在,双精度范围同样扩展到 AsDouble

关于c# - 在C#中将 double 值转换为RGB颜色,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20120781/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com