使用 SSE 的 OpenCV 灰度图像的平方根
Square root of a OpenCV's grey image using SSE
给定一个灰色 cv::Mat (CV_8UC1) 我想 return 另一个 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));
}
SSE 代码看起来不错,只是您没有处理最后 16 个像素:
for (x = 0; x < (pixels - 16); x += 16)
应该是:
for (x = 0; x <= (pixels - 16); x += 16)
请注意,如果您的图像宽度不是 16 的倍数,那么您需要处理最后一个完整矢量之后的所有剩余像素。
另请注意,您正在使用 0..255 范围内的 sqrt
个值。您可能需要 0..1.0 范围内的归一化值,在这种情况下,您需要相应地缩放值。
我没有使用 SSE2 的经验,但我认为如果性能是问题,您应该使用查找 table。查找 table 的创建速度很快,因为您只有 256 个可能的值。从查找 table 复制 4 个字节到目标矩阵应该是一个非常有效的操作。
给定一个灰色 cv::Mat (CV_8UC1) 我想 return 另一个 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));
}
SSE 代码看起来不错,只是您没有处理最后 16 个像素:
for (x = 0; x < (pixels - 16); x += 16)
应该是:
for (x = 0; x <= (pixels - 16); x += 16)
请注意,如果您的图像宽度不是 16 的倍数,那么您需要处理最后一个完整矢量之后的所有剩余像素。
另请注意,您正在使用 0..255 范围内的 sqrt
个值。您可能需要 0..1.0 范围内的归一化值,在这种情况下,您需要相应地缩放值。
我没有使用 SSE2 的经验,但我认为如果性能是问题,您应该使用查找 table。查找 table 的创建速度很快,因为您只有 256 个可能的值。从查找 table 复制 4 个字节到目标矩阵应该是一个非常有效的操作。