贤者之路,cuda版本convertto实现(与OPENCV 3.4 CPU版本数值一致)

【引言】: 将一个float32精度的矩阵砍到uchar精度,每个库都会根据自己算法目标类型做一些加速的优化从而导致结果不一样,比如在OpenCV3.4 cpu版本的convertto中, 1.5f的浮点数会变成2, 2.5f也会变成2。 跟传统的四舍五入计算不一样

uchar val_char = (uchar)(val_float + 0.5);//传统的四舍五入 输入值为正数

【问题测试】: 这种问题一般是因为在算法优化中,cpu计算会涉及到2进制移位和补位的优化而导致误差,最快的方法可以不需要看opencv源码,直接将一堆float convertto之后打印出来找规律,下图是converto前和converto后的数据 贴上测试代码

 

cv::Mat a = cv::Mat(10, 10, CV_32FC1);
for (int i = 0; i < 100; i++)
{
	a.at<float>(i / 10, i % 10) = i + 0.5;
}
std::cout << a << std::endl;


a.convertTo(a, CV_8UC1);

std::cout << a << std::endl;

贤者之路,cuda版本convertto实现(与OPENCV 3.4 CPU版本数值一致)

会发现,convert出来之后的数只有偶数没有奇数,所以可以做一些逻辑控制避免误差。


【实现】:


__global__ void convertoKernel(float* d_img_in, uchar* d_img_out, int inCol, int inRow, int inCh)
{
        int idx = blockIdx.x * blockDim.x + threadIdx.x;
        int idy = blockIdx.y * blockDim.y + threadIdx.y;
        long int pos = idx + idy * inCol;

        if(idx < inCol && idy < inRow )
        {
            float val_float = d_img_in[pos];

            //convertto
            float sub_val_float = val_float - (int)val_float;

            if (sub_val_float == 0.5f)
            {
                if ((int)val_float % 2 == 0)
                    val_char = (uchar)(val_float);
                else
                    val_char = (uchar)(mean_r) + 1;
            }
            else
            {
                val_char = (uchar)(mean_r + .5f);
            }
            d_img_out[pos] = val_char;
        }    
        
        

}

【终】:

虽然方法很直接,但是, 是比较快,好实现的解题方法。