我有五个颜色以无符号整数的形式存储,格式为#AARRGGBB
,我需要计算这五个颜色的平均值。显然,我不能简单地将每个整数除以五并相加,目前我想到的唯一方法是对它们进行位掩码操作,分别处理每个通道,然后再将它们合并。是否有更聪明或更简洁的方法来计算这五个颜色的平均值?
我有五个颜色以无符号整数的形式存储,格式为#AARRGGBB
,我需要计算这五个颜色的平均值。显然,我不能简单地将每个整数除以五并相加,目前我想到的唯一方法是对它们进行位掩码操作,分别处理每个通道,然后再将它们合并。是否有更聪明或更简洁的方法来计算这五个颜色的平均值?
在你(OP)提出的解决方案和Patrick的解决方案之间,有一个看起来相当不错的折中方案:
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;
如果您使用至少支持SSE的x86机器,且仅需要近似值,则可以使用汇编指令PAVGB(打包平均字节)来计算平均值。请参见http://www.tommesani.com/SSEPrimer.html以了解更多信息。
由于您有5个值,因此需要在调用PAVGB时进行创意,因为PAVGB一次只能处理两个值。
我找到了一个聪明的解决方案,但遗憾的是它只适用于颜色数量为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。