如何通过平均无符号字符正确实现此操作 - C ++

时间:2016-02-29 18:04:19

标签: c++ algorithm c++11 floating-point

考虑以下看似简单的问题,我在解决涉及掩码和在光线追踪中实施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_typefloating_type应该是什么(后者需要在没有截断的情况下正确划分),或者您可能采用不同的方式编码?

第一条评论后编辑:set_size是一个大于0的整数,如果需要可以硬编码,但一般情况下必然会发生变化。这是例如AA因子,平方;或面具大小,等等。

4 个答案:

答案 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 < 2ba % b * 2 / ba%b < b/2为0,a%b >= b/2时为1 {/ 1>