我刚刚开始使用SSE优化我的计算机视觉项目代码,旨在检测图像中的肤色。以下是我的功能。该函数采用彩色图像并查看每个像素并返回概率图。注释掉的代码是我最初的C ++实现,其余的是SSE版本。我对它们进行了计时,并且发现SSE并不比我原来的C ++代码快得多。有关正在发生的事情或如何进一步优化功能的任何建议?
void EvalSkinProb(const Mat& cvmColorImg, Mat& cvmProb)
{
std::clock_t ts = std::clock();
Mat cvmHSV = Mat::zeros(cvmColorImg.rows, cvmColorImg.cols, CV_8UC3);
cvtColor(cvmColorImg, cvmHSV, CV_BGR2HSV);
std::clock_t te1 = std::clock();
float fFG, fBG;
double dp;
__declspec(align(16)) int frgb[4] = {0};
__declspec(align(16)) int fBase[4] = {g_iLowHue, g_iLowSat, g_iLowVal, 0};
__declspec(align(16)) int fIndx[4] = {0};
__m128i* pSrc1 = (__m128i*) frgb;
__m128i* pSrc2 = (__m128i*) fBase;
__m128i* pDest = (__m128i*) fIndx;
__m128i m1;
for (int y = 0; y < cvmColorImg.rows; y++)
{
for (int x = 0; x < cvmColorImg.cols; x++)
{
cv::Vec3b hsv = cvmHSV.at<cv::Vec3b>(y, x);
frgb[0] = hsv[0];hsv[1] = hsv[1];hsv[2] =hsv[2];
m1 = _mm_sub_epi32(*pSrc1, *pSrc2);
*pDest = _mm_srli_epi32(m1, g_iSValPerbinBit);
// c++ code
//fIndx[0] = ((hsv[0]-g_iLowHue)>>g_iSValPerbinBit);
//fIndx[1] = ((hsv[1]-g_iLowSat)>>g_iSValPerbinBit);
//fIndx[2] = ((hsv[2]-g_iLowVal)>>g_iSValPerbinBit);
fFG = m_cvmSkinHist.at<float>(fIndx[0], fIndx[1], fIndx[2]);
fBG = m_cvmBGHist.at<float>(fIndx[0], fIndx[1], fIndx[2]);
dp = (double)fFG/(fBG+fFG);
cvmProb.at<double>(y, x) = dp;
}
}
std::clock_t te2 = std::clock();
double dSecs1 = (double)(te1-ts)/(CLOCKS_PER_SEC);
double dSecs2 = (double)(te2-te1)/(CLOCKS_PER_SEC);
}
答案 0 :(得分:8)
这里的第一个问题是你正在为大量的数据移动做很少的SSE工作。您将花费大部分时间在SSE寄存器中打包/解包数据以获取2条指令......
其次,此代码中会出现非常微妙的性能损失。
您正在使用缓冲区在变量和SSE寄存器之间传输数据。这是 BIG NO-NO 。
原因在于CPU加载/存储单元。当您将数据写入内存位置,然后立即尝试以不同的字大小读回数据时,它通常会强制将数据一直刷新到缓存并重新读取。这可能会导致20多个惩罚周期。
这是因为CPU加载/存储单元未针对此类异常访问进行优化。
答案 1 :(得分:1)
我对OpenCV并不太熟悉,但我怀疑如果你确保你访问的数据已经在外面循环,而不是加载它,你只会获得不错的吞吐量在循环内部未对齐。
答案 2 :(得分:0)
未经测试,但它应该给你一些想法:
auto base = _mm_set_epi32(g_iLowHue, g_iLowSat, g_iLowVal, 0);
for (int y = 0; y < cvmColorImg.rows; y++)
{
for (int x = 0; x < cvmColorImg.cols; x++)
{
auto hsv = _mm_loadu_si128(&cvmHSV.at<cv::Vec3b>(y, x)[0]); // Would be better if cvmHSV was aligned in which case _mm_load_si128 is faster
auto m1 = _mm_sub_epi32(hsv, base);
auto m2 = _mm_srli_epi32(m1, g_iSValPerbinBit);
auto fFG = static_cast<double>(m_cvmSkinHist.at<float>(m2.m128i_i32[0], m2.m128i_i32[1], m2.m128i_i32[2]));
auto fBG = static_cast<double>(m_cvmBGHist.at<float>(m2.m128i_i32[0], m2.m128i_i32[1], m2.m128i_i32[2]));
cvmProb.at<double>(y, x) = fFG/(fBG+fFG);
}
}