使用SSE计算OpenCV灰度图像的平方根

3

给定一个灰度的 cv::Mat (CV_8UC1),我想返回另一个包含元素平方根的 cv::Mat (CV_32FC1),并且我想使用 SSE2 指令集来完成。但是在将 8 位值转换为 32 浮点值以执行平方根时,我遇到了一些问题。我真的很感激任何帮助。这是我的代码(它不会给出正确的值):

uchar *source = (uchar *)cv::alignPtr(image.data, 16);
float *sqDataPtr = cv::alignPtr((float *)Squared.data, 16); 
for (x = 0; x < (pixels - 16); x += 16) {
    __m128i a0 = _mm_load_si128((__m128i *)(source + x));

    __m128i first8 = _mm_unpacklo_epi8(a0, _mm_set1_epi8(0));
    __m128i last8 = _mm_unpackhi_epi8(a0, _mm_set1_epi8(0));

    __m128i first4i = _mm_unpacklo_epi16(first8, _mm_set1_epi16(0));
    __m128i second4i = _mm_unpackhi_epi16(first8, _mm_set1_epi16(0));
    __m128 first4 = _mm_cvtepi32_ps(first4i);
    __m128 second4 = _mm_cvtepi32_ps(second4i);

    __m128i third4i = _mm_unpacklo_epi16(last8, _mm_set1_epi16(0));
    __m128i fourth4i = _mm_unpackhi_epi16(last8, _mm_set1_epi16(0));
    __m128 third4 = _mm_cvtepi32_ps(third4i);
    __m128 fourth4 = _mm_cvtepi32_ps(fourth4i);

    //  Store
    _mm_store_ps(sqDataPtr + x, _mm_sqrt_ps(first4));
    _mm_store_ps(sqDataPtr + x + 4, _mm_sqrt_ps(second4));
    _mm_store_ps(sqDataPtr + x + 8, _mm_sqrt_ps(third4));
    _mm_store_ps(sqDataPtr + x + 12, _mm_sqrt_ps(fourth4));
}
2个回答

1
SSE代码看起来没问题,只是你没有处理最后16个像素:
for (x = 0; x < (pixels - 16); x += 16)

should be:

for (x = 0; x <= (pixels - 16); x += 16)

请注意,如果您的图像宽度不是16的倍数,则需要注意最后一个完整向量后剩余的任何像素。
还要注意,您正在对0..255范围内的值进行平方根运算。可能您想要在0..1.0范围内获得归一化的值,此时您需要相应地缩放这些值。

非常感谢您的回答,但为了简单起见,我没有复制最后几个像素的剩余计算。我不知道为什么这段代码没有得出正确的值。是的,我想要0...255之间的值的平方根。我也不知道这是否完全有效。 - user3696558
好的 - 不过循环仍然需要修复,出于性能考虑,因为你错过了处理最后一个完整向量的机会。至于功能性,我怀疑问题可能不在SSE代码中 - 也许可以尝试用简单的标量循环替换SSE代码,看看是否有效,或者在单独的测试工具中测试SSE循环以进行验证? - Paul R
还有一件事 - 我把你的SSE代码放到了一个测试框架中,它通过了测试(一旦修复了for循环上限)。 - Paul R
好的!非常感谢!我会尝试找出为什么我的结果不一样! - user3696558

1

我没有使用SSE2的经验,但我认为如果性能是问题,你应该使用查找表。创建查找表很快,因为你只有256个可能的值。从查找表中复制4个字节到目标矩阵应该是一个非常高效的操作。


1
使用查找表只能处理一点,需要两次读取和一次写入才能处理一个输入值。SSE版本每个循环迭代可以处理16个点,仅需1次读取和4次写入即可处理16个输入值。 - Paul R
非常感谢您的回答,但是是的,那样做会慢得多。 - user3696558
即使您使用OpenCV LUT函数,也可以吗? - Micka
2
@Micka,据我所知,LUT函数要求输入和输出具有相同的深度。在这种情况下,输入为8U,输出为32F。 - Michael Burdinov
1
@Paul R,根据我的经验,使用查找表填充目标图像的值与在结果图像上执行memcpy操作的速度相当。它能比那更快多少呢? - Michael Burdinov
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接