基于灰度值的匹配方法研究(二)

发布时间 2023-07-15 08:22:21作者: 兜尼完

在上一篇文章中我们已经了解到灰度匹配的整体流程。基于对代码的理解,滑动窗口遍历运行时图像得到模板的初始姿势是此算法最耗时的部分。现在我们将用OpenCL加速这部分代码。下面直接给出一个例子供读者参考。测试所用电脑与上一篇文章相同。

//---------------------------------------------------------------------------------------
// 初始化GPU核函数。Up<>其实就是std::unique_ptr<>
//---------------------------------------------------------------------------------------
Up<KernelFunc> GrayMatch::initKernel()
{
    string fillAllScoreStr(R"CLC(
        typedef struct
        {
            int x;
            int y;
        } Point2i;

        typedef struct
        {
            Point2i pos;
            float gray;
        } Gray2i;
        
        kernel void fillAllScore(global const uchar* imageData, int rows, int cols, 
            global const Gray2i *samples, int count, 
            int minx, int maxx, int miny, int maxy, 
            global float* output)
        {
            int x = minx + get_global_id(0);
            int y = miny + get_global_id(1);

            float iavr = 0;
            int last = count - 1;
            for (int i = 0; i < last; i++)
            {
                Point2i pos = { samples[i].pos.x + x, samples[i].pos.y + y };
                iavr += imageData[pos.y * cols + pos.x];
            }
            iavr /= last;
            float hcor = 0, lcor = 0;
            for (int i = 0; i < last; i++)
            {
                Point2i pos = { samples[i].pos.x + x, samples[i].pos.y + y };
                float s = fabs(imageData[pos.y * cols + pos.x] - iavr);
                hcor += s * samples[i].gray;
                lcor += s * s;
            }
            float ccor = samples[last].gray;
            float score = hcor / (sqrt(lcor) * ccor);
            output[y * cols + x] = score;
        }
    )CLC");

    cl::Program program(fillAllScoreStr);
    if (program.build("-cl-std=CL2.0") != CL_SUCCESS)
    {
        cl_int buildErr = CL_SUCCESS;
        auto buildInfo = program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(&buildErr);
        for (auto &pair : buildInfo)
        {
            qDebug() << TQ(pair.second);
        }
        ASSERT(false, "核函数有错务必修改");
    }
    return std::make_unique<KernelFunc>(program, "fillAllScore");
}

//---------------------------------------------------------------------------------------
// 入口函数大致流程
//---------------------------------------------------------------------------------------
void GrayMatch::execute()
{
    /* 第一层遍历搜索的大致流程 */
    /* tplSample是第N层的模板数据,数据类型是vector<Gray2f> */
    /* runtime是第N层的运行时图像,数据类型是Mat */
    /* myGpuFunc的数据类型是Up<KernelFunc>,它在构造函数中初始化 */
    cl::Buffer inputImage(std::begin(runtime), std::end(runtime), true);
    while (每一个旋转角度)
    {
        Mat scoreMap = Mat::zeros(runtime.rows, runtime.cols, CV_32FC1);
        vector<Gray2i> rotatedPoints;
        rotateGrayBorder(tplSample, angle, rotatedPoints); /* 旋转模板数据 */
        ...
        int miny, maxy; /* 它是滑动窗口y轴的范围 */
        int minx, maxx; /* 它是滑动窗口x轴的范围 */
        cl::Buffer inputSample(rotatedPoints.begin(), rotatedPoints.end(), true);
        cl::Buffer output(std::begin<float>(scoreMap), std::end<float>(scoreMap), false);

        (*myGpuFunc)(cl::EnqueueArgs(cl::NDRange(maxx - minx + 1, maxy - miny + 1), cl::NDRange(/*maxx - minx, 1*/)),
            inputImage, runtime.rows, runtime.cols, inputSample, (int)rotatedPoints.size(), minx, maxx, miny, maxy, output);
        cl::copy(output, std::begin<float>(scoreMap), std::end<float>(scoreMap));
        ...
        /* 从scoreMap中找最大相似度的姿势 */
        /* 保存全局最优姿势供后续使用 */
    }
    ...
    /* 根据初始姿势逐层求精 */
}

上述代码在算法整体耗时在几百毫秒时有2~4倍的加速效果;在算法整体耗时在一秒以上时可达到十几倍的加速效果。这里不赘述关于OpenCL相关的知识了。有不清楚的可以查阅相关书籍。