考虑以下看似简单的问题,我在解决涉及掩码和在光线追踪中实施AA的CV中的分配时遇到过。
RGB格式的颜色有三个通道,每个通道由unsigned char
表示,因此0 <= channel value <= 255
。出于对称的原因,我们考虑单个通道。我想在某一组点上总结通道值,然后平均并舍入(不仅仅是截断)并存储回unsigned char
。请注意,在所有问题中,结果平均值保证在[0, 255]
范围内,它是算法的不变量。
这看起来很简单,但我关注以下内容:
P.1 即可。最重要的是:类型选择,请参阅下面的伪代码,了解我需要的类型。更确切地说,我收到了一个缩小的转换错误,它会在设置-pedantic-errors
时变为错误,我不想简单地将其挥手。
P.2 即可。从可以累积许多uchar
的更广泛类型返回到uchar
类型的想法。我确信该算法可以保证产生uchar
范围内的值,但这还够吗?我应该担心吗?
伪代码(cv::Vec3b
是OpenCV中unsigned char
的固定大小3x1数组,用于图像存储和输出):
// this returns a value in the range [0, 255]
cv::Vec3b processing_func(const point&) { ... }
accum_type acc_r = 0, acc_g = 0 , acc_b = 0;
for (point p: point_set) {
const cv::Vec3b color = processing_func(p);
acc_r += color[0]; acc_g += color[1]; acc_b += color[2];
}
cv::Vec3b result_color{roundf(acc_r / (floating_type)set_size),
roundf(acc_g / (floating_type)set_size),
roundf(acc_b / (floating_type)set_size)};
// hello narrowing conversion ^^^
请参阅第1页,accum_type
和floating_type
应该是什么(后者需要在没有截断的情况下正确划分),或者您可能采用不同的方式编码?
第一条评论后编辑:set_size
是一个大于0的整数,如果需要可以硬编码,但一般情况下必然会发生变化。这是例如AA因子,平方;或面具大小,等等。
答案 0 :(得分:2)
p.1:使用显式强制转换。
第2页:一般来说还不够。每次使用浮点类型时,都会失去精度。这取决于您对浮点类型的操作。为防止出现这种情况,您可以检查饱和度,例如:
uchar v = (uchar)(value < 255.0 ? value : 255);
但我认为你不需要它。
答案 1 :(得分:1)
我很想使用无符号整数: -
uint acc_r = set_size / 2, acc_g = set_size / 2, acc_b = set_size / 2;
for (point p: point_set) {
const cv::Vec3b color = processing_func(p);
acc_r += color[0]; acc_g += color[1]; acc_b += color[2];
}
cv::Vec3b result_color{acc_r / set_size, acc_g / set_size, acc_b / set_size}
请注意,我将RGB值初始化为1/2(set_size / 2),以便除法轮而不是截断。
答案 2 :(得分:1)
我会将unsigned
用于累加器类型(假设您累积的值少于2 ^ 24),并使用double
作为浮点类型(因为它是文字十进制值的类型)
然后,您只是错过了static_cast<unsigned char>(rounded_floating_point_value)
,以便将浮点数舍入的结果恢复为所需的无警告类型。
答案 3 :(得分:1)
我会使用unsigned int
cv::Vec3b processing_func(const point&) { ... }
unsigned int acc_r, acc_g, acc_b;
for (point p: point_set) {
const cv::Vec3b color = processing_func(p);
acc_r += color[0]; acc_g += color[1]; acc_b += color[2];
}
然后对于舍入,我将以下列方式使我们成为%运算符
cv::Vec3b result_color{acc_r / set_size + acc_r % set_size * 2 / set_size,
acc_g / set_size + acc_g % set_size * 2 / set_size,
acc_b / set_size + acc_b % set_size * 2 / set_size};
请注意0 <= a % b * 2 < 2b
,a % b * 2 / b
时a%b < b/2
为0,a%b >= b/2
时为1 {/ 1>