平均 ARGB 颜色整数的最快/最简单的方法?

发布于 2024-07-27 18:14:41 字数 151 浏览 3 评论 0原文

我有五种颜色以无符号整数的格式存储在#AARRGGBB 中,我需要取所有五种颜色的平均值。 显然我不能简单地将每个 int 除以 5 然后将它们相加,到目前为止我想到的唯一方法是对它们进行位掩码,分别执行每个通道,然后再次将它们组合在一起。 有没有一种聪明或简洁的方法来平均所有五个?

I have five colors stored in the format #AARRGGBB as unsigned ints, and I need to take the average of all five. Obviously I can't simply divide each int by five and just add them, and the only way I thought of so far is to bitmask them, do each channel separately, and then OR them together again. Is there a clever or concise way of averaging all five of them?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

墨落成白 2024-08-03 18:14:41

你的(OP)提出的解决方案和帕特里克的解决方案之间的中间看起来很整洁:

Color colors[5]={ 0xAARRGGBB,...};

unsigned long sum1=0,sum2=0;
for (int i=0;i<5;i++)
{
  sum1+= colors[i]    &0x00FF00FF; // 0x00RR00BB
  sum2+=(colors[i]>>8)&0x00FF00FF; // 0x00AA00GG
}
unsigned long output=0;
output|=(((sum1&0xFFFF)/5)&0xFF);
output|=(((sum2&0xFFFF)/5)&0xFF)<<8;
sum1>>=16;sum2>>=16; // and now the top halves
output|=(((sum1&0xFFFF)/5)&0xFF)<<16;
output|=(((sum2&0xFFFF)/5)&0xFF)<<24;

我不认为你真的可以将 sum1/sum2 除以 5,因为上半部分的位会溢出......

如果近似值有效,您可以尝试乘以 0.1875 (0.125+0.0625) 之类的值,(这意味着:乘以 3 并向下移动 4 位。您可以通过位掩码和维护来完成此操作。)
问题是,0.2 的二进制表示很糟糕,所以乘以它是很糟糕的。

一如既往,准确性或速度。 你的选择。

Half way between your (OP) proposed solution and Patrick's solution looks quite neat:

Color colors[5]={ 0xAARRGGBB,...};

unsigned long sum1=0,sum2=0;
for (int i=0;i<5;i++)
{
  sum1+= colors[i]    &0x00FF00FF; // 0x00RR00BB
  sum2+=(colors[i]>>8)&0x00FF00FF; // 0x00AA00GG
}
unsigned long output=0;
output|=(((sum1&0xFFFF)/5)&0xFF);
output|=(((sum2&0xFFFF)/5)&0xFF)<<8;
sum1>>=16;sum2>>=16; // and now the top halves
output|=(((sum1&0xFFFF)/5)&0xFF)<<16;
output|=(((sum2&0xFFFF)/5)&0xFF)<<24;

I don't think you could really divide sum1/sum2 by 5, because the bits from the top half would spill down...

If an approximation would be valid, you could try a multiplication by something like, 0.1875 (0.125+0.0625), (this means: multiply by 3 and shift down by 4 places. This you could do with bitmasking and care.)
The problem is, 0.2 has a crappy binary representation, so multiplying by it is an ass.

As ever, accuracy or speed. Your choice.

肤浅与狂妄 2024-08-03 18:14:41

当使用至少具有 SSE 的 x86 机器时,如果您只需要近似,则可以使用汇编指令 PAVGB(打包平均字节),该指令对字节进行平均。 请参阅 http://www.tommesani.com/SSEPrimer.html 了解说明。

由于您有 5 个值,因此您在调用 PAVGB 时需要发挥创意,因为 PAVGB 一次只会执行两个值。

When using x86 machines with at least SSE, and if you need to approximate only, you could use the assembly instruction PAVGB (Packed Average Byte), which averages bytes. See http://www.tommesani.com/SSEPrimer.html for explanation.

Since you've got 5 values, you would need to be creative in calling PAVGB, since PAVGB will only do two values at a time.

思慕 2024-08-03 18:14:41

我找到了你的问题的聪明解决方案,遗憾的是,它仅适用于颜色数量是 2 的幂的情况。我将在两种颜色的情况下展示它:

mask = 01010101

pom = ~(a^b & mask) # ^ means xor here, ~ negation

a = a & pom
b = b & pom

avg = (a+b) >> 1

此方法的技巧是 - 当你计算平均值时, LSB 总和(如果有两个数字)没有任何意义,因为它会在除法中被删除(我们是当然,这里谈论的是整数)。 在您的问题中,部分和的 LSB 同时携带相邻颜色之和的位。 假设每种颜色总和的LSB将为0,您可以安全地将这两个整数相加——相加不会互相干扰。 位移位将每种颜色除以二。

此方法也可以用于 4 种颜色,但您必须实现找出由每种颜色的最后两位组成的数字之和的进位标志。 也可以省略这部分,只将每种颜色的最后两位归零——这种省略所犯的最大错误是每个组件 1。

I found smart solution of your problem, sadly it is only applicable if number of colors is power of 2. I'll show it in case of two colors:

mask = 01010101

pom = ~(a^b & mask) # ^ means xor here, ~ negation

a = a & pom
b = b & pom

avg = (a+b) >> 1

The trick of this method is — when you count average, LSB of sum (in case of two numbers) has no meaning, as it will be dropped in division (we're talking integers here, of course). In your problem, LSB of partial sums is at the same moment carry bit of sum of adjacent color. Provided, that LSB of every color sum will be 0 you can safely add those two integers — additions won't interfere with each other. Bit shift divides every color by two.

This method can be used with 4 colors as well, but you have to implement finding out the carry flag of sum of numbers made of two last bits of every color. It is also possible to omit this part and just zero last two bits of every color — biggest mistake made with this omission is 1 for every component.

时光病人 2024-08-03 18:14:41

编辑我会将这次尝试留给后代,但请注意,这是不正确的并且不会起作用。

一种“聪明”的方法是在组件之间插入零,解析为无符号长整数,对数字求平均值,转换回十六进制字符串,删除零,最后解析为无符号整数。

即将#AARRGGBB 转换为#AA00RR00GG00BB

这种方法涉及解析和字符串操作,因此无疑会比您提出的方法慢。

如果您仔细考虑自己的解决方案,它本身可能看起来非常聪明。

EDIT I'll leave this attempt for posterity, but please note that it is incorrect and will not work.

One "clever" way you could do it would be to insert zeros between the components, parse into an unsigned long, average the numbers, convert back to a hex string, remove the zeros and finally parse into an unsigned int.

i.e. convert #AARRGGBB to #AA00RR00GG00BB

This method involves parsing and string manipulations, so will undoubtedly be slower than the method you proposed.

If you were to factor your own solution carefully, it might actually look quite clever itself.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文