繁体   English   中英

使用OpenCV Cuda ORB功能检测器

[英]Using OpenCV Cuda ORB feature detector

我有一个应用程序,在其中我要接收图像流,我想在其中监视设置的ROI中检测到的功能。 这是使用ORB检测器完成的。 在第一个图像中,我使用检测器为给定的ROI查找“参考”关键点和描述符。 对于后续图像,我发现相同ROI的“测试”关键点和描述符。 然后,我使用knn匹配器在参考描述符和测试描述符之间找到匹配项。 最后,我尝试找到“最佳”匹配项,将关联的关键点添加到“匹配的关键点”集合中,然后计算“匹配强度”。 该匹配强度旨在指示参考图像中找到的关键点与测试图像中的关键点匹配的程度。

我有几个问题:

1-这是否有效使用特征检测器? 我知道简单的模板匹配可能会给我带来相似的结果,但是我希望避免照明发生轻微变化的问题。

2-我是否可以正确地为“好”匹配项筛选我的匹配项,然后获取与该匹配项正确关联的关键点?

3-我的代码似乎按原样工作,但是,如果我尝试使用流转移到OpenCV调用的异步版本,则会出现异常:“函数cv :: cuda :: GpuMat :: setTo中的无效资源句柄”发生在对ORB_Impl :: buildScalePyramids的调用中(从ORB_Impl :: detectAndComputeAsync调用)。 请参阅下面的“ NewFrame”函数的异步版本。 这只是让我认为我没有正确设置所有这些设置。

这是我的代码:

void Matcher::Matcher()
{
    // create ORB detector and descriptor matcher
    m_b = cuda::ORB::create(500, 1.2f, 8, 31, 0, 2, 0, 31, 20, true);   
    m_descriptorMatcher =       cv::cuda::DescriptorMatcher::createBFMatcher(cv::NORM_HAMMING); 
}

void Matcher::Configure(int imageWidth, int imageHeight, int roiX, int roiY, int roiW, int roiH)
{
    // set member variables
    m_imageWidth = imageWidth;
    m_imageHeight = imageHeight;
    m_roiX = roiX;
    m_roiY = roiY;
    m_roiW = roiW;
    m_roiH = roiH;

    m_GpuRefSet = false; // set flag indicating reference not yet set

    // create mask for specified ROI
    m_mask = GpuMat(imageHeight,imageWidth, CV_8UC1, Scalar::all(0));
    cv::Rect rect = cv::Rect(m_roiX, m_roiY, m_roiW, m_roiH);
    m_mask(rect).setTo(Scalar::all(255));       
}


double Matcher::NewFrame(void *pImagedata)
{
    // pImagedata = pointer to BGRA byte array
    // m_imageHeight and m_imageWidth have already been set
    // m_b is a pointer to the ORB detector

    if (!m_GpuRefSet)
    { // 1st time through (after call to Matcher::Configure), set reference keypoints and descriptors

        cv::cuda::GpuMat mat1(m_imageHeight, m_imageWidth, CV_8UC4, pImagedata);  // put image data into GpuMat

        cv::cuda::cvtColor(mat1, m_refImage, CV_BGRA2GRAY); // convert to grayscale as required by ORB

        m_keyRef.clear(); // clear the vector<KeyPoint>, keypoint vector for reference image

        m_b->detectAndCompute(m_refImage, m_mask, m_keyRef, m_descRef, false); // detect keypoints and compute descriptors

        m_GpuRefSet = true;     
    }

    cv::cuda::GpuMat mat2(m_imageHeight, m_imageWidth, CV_8UC4, pImagedata);  // put image data into GpuMat

    cv::cuda::cvtColor(mat2, m_testImage, CV_BGRA2GRAY, 0);  // convert to grayscale as required by ORB

    m_keyTest.clear(); // clear vector<KeyPoint>, keypoint vector for test image

    m_b->detectAndCompute(m_testImage, m_mask, m_keyTest, m_descTest, false);  // detect keypoints and compute descriptors


    double value = 0.0f;  // used to store return value ("match intensity")

        // calculate best match for each descriptor
        if (m_descTest.rows > 0)
        {   
            m_goodKeypoints.clear(); // clear vector of "good" KeyPoints, vector<KeyPoint> 

            m_descriptorMatcher->knnMatch(m_descTest, m_descRef, m_matches, 2, noArray());  // find matches

            // examine all matches, and collect the KeyPoints whose match distance mets given criteria
            for (int i = 0; i<m_matches.size(); i++){
                if (m_matches[i][0].distance < m_matches[i][1].distance * m_nnr){ // m_nnr = nearest neighbor ratio (typically 0.6 - 0.8)
                    m_goodKeypoints.push_back(m_keyRef.at(m_matches[i][0].trainIdx));  // not sure if getting the correct keypoint here
                }
            }

            // calculate "match intensity", i.e. percent of the keypoints found in the reference image that are also in the test image
            value = ((double)m_goodKeypoints.size()) / ((double)m_keyRef.size());
        }
        else
        {
            value = 0.0f;
        }

    return value;
}

这是失败的NewFrame函数的流/异步版本:

double Matcher::NewFrame(void *pImagedata)
{
    if (m_b.empty()) return 0.0f;

    if (!m_GpuRefSet)
    {
        try
        {
            cv::cuda::GpuMat mat1(m_imageHeight, m_imageWidth, CV_8UC4, pImagedata);

            cv::cuda::cvtColor(mat1, m_refImage, CV_BGRA2GRAY);

            m_keyRef.clear();

            m_b->detectAndComputeAsync(m_refImage, m_mask, m_keyRef, m_descRef, false,m_stream);  // FAILS HERE

            m_stream.waitForCompletion();

            m_GpuRefSet = true;
        }
        catch (Exception e)
        {
            string msg = e.msg;
        }

    }

    cv::cuda::GpuMat mat2(m_imageHeight, m_imageWidth, CV_8UC4, pImagedata);

    cv::cuda::cvtColor(mat2, m_testImage, CV_BGRA2GRAY, 0, m_stream);

    m_keyTest.clear();

    m_b->detectAndComputeAsync(m_testImage, m_mask, m_keyTest, m_descTest, false, m_stream);

    m_stream.waitForCompletion();

    double value = 0.0f;

    // calculate best match for each descriptor

    if (m_descTest.rows > 0)
    {
        m_goodKeypoints.clear();
        m_descriptorMatcher->knnMatchAsync(m_descTest, m_descRef, m_matches, 2, noArray(), m_stream);

        m_stream.waitForCompletion(); 

        for (int i = 0; i<m_matches.size(); i++){
            if (m_matches[i][0].distance < m_matches[i][1].distance * m_nnr) // m_nnr = nearest neighbor ratio
            {
                m_goodKeypoints.push_back(m_keyRef.at(m_matches[i][0].trainIdx));
            }
        }

        value = ((double)m_goodKeypoints.size()) / ((double)m_keyRef.size());
    }
    else
    {
        value = 0.0f;
    }


    if (value > 1.0f) value = 1.0f;

    return value;
}

任何建议/建议将不胜感激。

谢谢!!

经过一些试验,我确信这确实是对ORB检测器的合理使用,并且我使用“最近邻居比率”方法进行的“良好”测试似乎也可以正常工作。 这回答了上面的问题#1和#2。

与问题3有关,我确实做了一些发现,这些发现为我带来了很大的收获。

首先,事实证明我对cv :: cuda :: Stream和cpu线程不够谨慎。 尽管我确定这对许多人来说都是显而易见的,并且已在OpenCV文档中提到,但是放置在特定cv :: cuda :: Stream上的所有内容都应在同一cpu线程上进行。 不这样做不一定会创建异常,但是会创建不确定的行为,其中可能包含异常。

其次,对我来说,事实证明,使用多异步处理的detectAndCompute和knnMatch异步版本更可靠。 似乎与以下事实有关:异步版本使用所有基于GPU的参数,而非异步版本具有基于CPU的矢量参数。 我编写了简单的单线程测试应用程序,异步和非异步版本似乎都可以使用。 但是,我的实际应用程序具有其他CUDA内核,并且CUDA视频解码器在其他线程上运行,因此GPU上拥挤不堪。

无论如何,这是我如何进行Async函数调用的版本,该函数可以为我清理所有内容。 它演示了ORB检测器和描述符匹配器的Async / Stream版本的使用。 传递给它的cv :: cuda :: Stream可以是cv :: cuda :: Stream :: NullStream()或您创建的cv :: cuda :: Stream。 只要记住要在使用它的同一cpu线程上创建流即可。

我仍然对改进的建议感兴趣,但是以下方法似乎可行。

orb = cuda::ORB::create(500, 1.2f, 8, 31, 0, 2, 0, 31, 20, true);   
matcher = cv::cuda::DescriptorMatcher::createBFMatcher(cv::NORM_HAMMING);  

// process 1st image
GpuMat imgGray1;  // load this with your grayscale image
GpuMat keys1; // this holds the keys detected
GpuMat desc1; // this holds the descriptors for the detected keypoints
GpuMat mask1; // this holds any mask you may want to use, or can be replace by noArray() in the call below if no mask is needed
vector<KeyPoint> cpuKeys1;  // holds keypoints downloaded from gpu

//ADD CODE TO LOAD imgGray1

orb->detectAndComputeAsync(imgGray1, mask1, keys1, desc1, false, m_stream);
stream.waitForCompletion();
orb->convert(keys1, cpuKeys1); // download keys to cpu if needed for anything...like displaying or whatever

// process 2nd image
GpuMat imgGray2;  // load this with your grayscale image
GpuMat keys2; // this holds the keys detected
GpuMat desc2; // this holds the descriptors for the detected keypoints
GpuMat mask2; // this holds any mask you may want to use, or can be replace by noArray() in the call below if no mask is needed
vector<KeyPoint> cpuKeys2;  // holds keypoints downloaded from gpu

//ADD CODE TO LOAD imgGray2

orb->detectAndComputeAsync(imgGray2, mask2, keys2, desc2, false, m_stream);
stream.waitForCompletion();
orb->convert(keys2, cpuKeys2); // download keys to cpu if needed for anything...like displaying or whatever

if (desc2.rows > 0)
{
    vector<vector<DMatch>> cpuKnnMatches;
    GpuMat gpuKnnMatches;  // holds matches on gpu
    matcher->knnMatchAsync(desc2, desc1, gpuKnnMatches, 2, noArray(), *stream);  // find matches
    stream.waitForCompletion();

    matcher->knnMatchConvert(gpuKnnMatches, cpuKnnMatches); // download matches from gpu and put into vector<vector<DMatch>> form on cpu

    vector<DMatch> matches;         // vector of good matches between tested images

    for (std::vector<std::vector<cv::DMatch> >::const_iterator it = cpuKnnMatches.begin(); it != cpuKnnMatches.end(); ++it) {
                if (it->size() > 1 && (*it)[0].distance / (*it)[1].distance < m_nnr) {  // use Nearest-Neighbor Ratio to determine "good" matches
            DMatch m = (*it)[0];
            matches.push_back(m);       // save good matches here                           

                }
            }

        }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM