2017-11-09 3 views
0

Ich habe eine Anwendung, wo ich einen Strom von Bildern, innerhalb denen ich erkannte Funktionen innerhalb einer festgelegten ROI überwachen möchte. Dies wird unter Verwendung eines ORB-Detektors erreicht. Im ersten Bild benutze ich den Detektor, um "Referenz" - Schlüsselpunkte und Deskriptoren für die gegebene ROI zu finden. Für nachfolgende Bilder finde ich Schlüsselpunkte und Deskriptoren für denselben ROI. Ich benutze dann einen Knn Matcher, um Übereinstimmungen zwischen Referenz- und Testdeskriptoren zu finden. Schließlich versuche ich, die "besten" Übereinstimmungen zu finden, füge die zugehörigen Schlüsselpunkte zu einer Sammlung "übereinstimmender Schlüsselpunkte" hinzu und berechne dann eine "Übereinstimmungsintensität". Diese Übereinstimmungsintensität soll angeben, wie gut die in dem Referenzbild gefundenen Schlüsselpunkte mit den Schlüsselpunkten in dem Testbild übereinstimmen.Verwenden von OpenCV Cuda ORB Feature Detektor

Ich habe ein paar Fragen:

1 - ist dies eine gültige Verwendung eines Merkmalsdetektor? Ich verstehe, dass ein einfacher Template-Abgleich mir ähnliche Ergebnisse liefern könnte, aber ich hatte gehofft, Probleme mit leichten Änderungen in der Beleuchtung zu vermeiden.

2 - überprüfe ich meine Übereinstimmungen richtig für "gute" Übereinstimmungen, und bekomme ich dann den korrekten zugehörigen Schlüsselpunkt für diese Übereinstimmung?

3 - mein Code scheint zu funktionieren, aber wenn ich versuche, zu den asynchronen Versionen der OpenCV-Aufrufe mit Streams zu bewegen, erhalte ich eine Ausnahme: "ungültige Ressource in der Funktion cv :: cuda :: GpuMat :: setTo ", was in einem Aufruf von ORB_Impl :: buildScalePyramids (das von ORB_Impl :: detectAndComputeAsync aufgerufen wurde) geschieht. Sehen Sie sich die asynchrone Version meiner "NewFrame" -Funktion an. Das lässt mich nur glauben, dass ich das alles nicht richtig einrichten kann.

Hier ist mein Code:

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; 
} 

Und hier ist der Strom/Asynchron-Version der NEWFRAME Funktion, die fehlschlägt:

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; 
} 

Irgendwelche Vorschläge/Rat würde geschätzt.

Danke !!

Antwort

0

Nach einigen Versuchen bin ich überzeugt, dass dies in der Tat eine vernünftige Verwendung des ORB-Detektors ist und dass mein Test für "Güte" unter Verwendung des Nearest-Neighbor-Ratio-Ansatzes ebenfalls zu funktionieren scheint. Dies beantwortet die Fragen 1 und 2 oben.

In Bezug auf Frage # 3 habe ich einige Entdeckungen gemacht, die Dinge wesentlich für mich gereinigt.

Zuerst stellt sich heraus, dass ich mit cv :: cuda :: Stream und CPU Threads nicht vorsichtig genug war. Obwohl ich sicher bin, dass es für viele offensichtlich ist und in OpenCV-Dokumenten erwähnt wird, sollte alles, was auf einen bestimmten cv :: cuda :: Stream geschrieben wird, aus demselben CPU-Thread stammen. Wenn Sie dies nicht tun, entsteht nicht unbedingt eine Ausnahme, sondern es wird ein unbestimmtes Verhalten erzeugt, das Ausnahmen enthalten kann.

Zweitens stellte sich heraus, dass die Async-Versionen von detectAndCompute und knnMatch mit Multi-Threading zuverlässiger waren. Diese scheint mit der Tatsache verwandt zu sein, dass die Async-Version alle GPU-basierten Parameter verwendet, wobei die Nicht-Async-Versionen einen CPU-basierten Vektorparameter haben. Sowohl Async- als auch Nicht-Async-Versionen scheinen zu funktionieren. Ich habe einfache Testanwendungen mit einem einzigen Thread geschrieben. Allerdings hat meine reale Anwendung andere CUDA-Kernel und der CUDA-Video-Decoder läuft auf anderen Threads, so dass die Dinge auf der GPU voll sind.

Wie auch immer, hier ist meine Version, wie man die Async-Funktionsaufrufe macht, die alles für mich aufgeräumt haben.Es demonstriert die Verwendung der Async/Stream-Versionen des ORB-Detektors und Deskriptor-Matcher. Der cv :: cuda :: Stream kann entweder cv :: cuda :: Stream :: NullStream() oder ein cv :: cuda :: Stream sein, den Sie erstellen. Denken Sie daran, den Stream auf demselben CPU-Thread zu erstellen, wenn er verwendet wird.

Ich bin immer noch an Verbesserungsvorschlägen interessiert, aber Folgendes scheint zu funktionieren.

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       

       } 
      } 

     } 
}