2017-11-09 18 views
0

설정된 ROI 내에서 감지 된 기능을 모니터링 할 수있는 이미지를 수신하는 응용 프로그램이 있습니다. 이것은 ORB 검출기를 사용하여 수행됩니다. 첫 번째 이미지에서는 탐지기를 사용하여 주어진 ROI에 대한 "참조"키포인트와 설명자를 찾습니다. 이후 이미지의 경우 동일한 ROI에 대해 "테스트"키포인트와 설명자를 찾습니다. 그런 다음 knn 정규 표현식을 사용하여 참조 및 테스트 설명자 간의 일치를 찾습니다. 마지막으로 "최상의"일치를 찾고 관련 키포인트를 "일치하는 키포인트"컬렉션에 추가 한 다음 "일치 강도"를 계산합니다. 이 일치 강도는 참조 이미지에서 발견 된 키포인트가 테스트 이미지의 키포인트와 얼마나 잘 일치하는지 나타 내기위한 것입니다. OpenCV Cuda ORB 기능 감지기 사용

나는 몇 가지 질문이 있습니다

1 -이 기능 검출기의 유효한 사용하는 것입니다? 간단한 템플릿 매칭을 통해 유사한 결과를 얻을 수 있다고 생각합니다.하지만 조명이 약간 변경되면 문제를 피할 수 있기를 바랍니다.

2 - 나는 "좋은"성냥을 위해 나의 성냥을 제대로 스크린하고, 그 후에 그 성냥을 위해 정확하게 관련된 키포인트를 얻고 있는가?

3 - 내 코드가 그대로 작동하는 것처럼 보이지만 스트림을 사용하여 OpenCV 호출의 비동기 버전으로 이동하면 예외가 발생합니다. "cv :: cuda :: GpuMat의 잘못된 리소스 핸들 :: setTo "(ORB_Impl :: detectAndComputeAsync에서 호출 된) ORB_Impl :: buildScalePyramids에 대한 호출에서 발생합니다. 아래의 "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; 
} 

어떤 제안/조언을 감상 할 수있다.

감사합니다.

답변

0

몇 가지 시도 후에 이것이 실제로 ORB 탐지기의 합리적인 사용이며 Nearest-Neighbor Ratio 접근법을 사용하는 "선량"에 대한 내 테스트가 효과가있는 것으로 확신합니다. 위 질문 # 1과 # 2에 대한 대답입니다.

3 번 질문과 관련하여 필자는 실질적으로 나를 청소 한 몇 가지 발견을했습니다.

우선 cv :: cuda :: Stream 및 cpu threads로 충분히 조심하지 못했다. OpenCV 문서에 언급되어있는 많은 사람들에게 분명하지만, 특정 cv :: cuda :: Stream을 사용하는 것은 동일한 CPU 스레드에서 수행해야합니다. 그렇게하지 않아도 반드시 예외가 생성되는 것은 아니지만 예외가 포함될 수있는 미정 동작을 생성합니다.

둘째로, 나에겐 detectAndCompute와 knnMatch의 비동기 버전을 사용하면 멀티 스레딩에서 더 안정적이라는 것이 밝혀졌습니다. 이 으로 비동기 버전은 CPU 기반 벡터 매개 변수가있는 모든 GPU 기반 매개 변수를 사용한다는 점과 관련이 있습니다. 비동기 버전과 비동기 버전이 모두 작동하는 것처럼 보입니다. 단순한 단일 스레드 테스트 앱을 작성했습니다. 그러나 제 실제 응용 프로그램에는 다른 CUDA 커널과 CUDA 비디오 디코더가 다른 스레드에서 실행되므로 GPU에서는 상황이 복잡합니다.

어쨌든, 나에게 모든 것을 정리 한 비동기 함수 호출을 만드는 방법은 다음과 같습니다.ORB 검출기 및 디스크립터 일치 프로그램의 비동기/스트림 버전 사용을 보여줍니다. 전달 된 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       

       } 
      } 

     } 
}