OpenCV學習筆記(六)——對XML和YAML檔案實現I/O操作

Aurora Polaris發表於2016-12-01

OpenCV學習筆記(六)(七)(八)(九)(十)

作者:GarfieldEr007

OpenCV學習筆記(六)——對XML和YAML檔案實現I/O操作


1. XML、YAML檔案的開啟和關閉

XML\YAML檔案在OpenCV中的資料結構為FileStorage,開啟操作例如:

[cpp] view plain copy
  1. string filename = "I.xml";  
  2. FileStorage fs(filename, FileStorage::WRITE);  
  3. \\...  
  4. fs.open(filename, FileStorage::READ);  

檔案關閉操作會在FileStorage結構銷燬時自動進行,但也可呼叫如下函式實現

[cpp] view plain copy
  1. fs.release();  

2.文字和數字的輸入和輸出

寫入檔案使用  <<  運算子,例如:

[cpp] view plain copy
  1. fs << "iterationNr" << 100;  

讀取檔案,使用 >> 運算子,例如

[cpp] view plain copy
  1. int itNr;  
  2. fs["iterationNr"] >> itNr;  
  3. itNr = (int) fs["iterationNr"];  


3. OpenCV資料結構的輸入和輸出,和基本的C++形式相同

[cpp] view plain copy
  1. Mat R = Mat_<uchar >::eye (3, 3),  
  2. T = Mat_<double>::zeros(3, 1);  
  3. fs << "R" << R; // Write cv::Mat  
  4. fs << "T" << T;  
  5. fs["R"] >> R; // Read cv::Mat  
  6. fs["T"] >> T;  


4. vector(arrays) 和 maps的輸入和輸出

vector要注意在第一個元素前加上“[”,在最後一個元素前加上"]"。例如:

[cpp] view plain copy
  1. fs << "strings" << "["// text - string sequence  
  2. fs << "image1.jpg" << "Awesomeness" << "baboon.jpg";  
  3. fs << "]"// close sequence  

對於map結構的操作使用的符號是"{"和"}",例如:

[cpp] view plain copy
  1. fs << "Mapping"// text - mapping  
  2. fs << "{" << "One" << 1;  
  3. fs << "Two" << 2 << "}";  

讀取這些結構的時候,會用到FileNode和FileNodeIterator資料結構。對FileStorage類的[]操作符會返回FileNode資料型別,對於一連串的node,可以使用FileNodeIterator結構,例如:

[cpp] view plain copy
  1. FileNode n = fs["strings"]; // Read string sequence - Get node  
  2. if (n.type() != FileNode::SEQ)  
  3. {  
  4. cerr << "strings is not a sequence! FAIL" << endl;  
  5. return 1;  
  6. }  
  7. FileNodeIterator it = n.begin(), it_end = n.end(); // Go through the node  
  8. for (; it != it_end; ++it)  
  9. cout << (string)*it << endl;  


5. 讀寫自己的資料結構

這部分比較複雜,參考最後的例項中的MyData結構自己領悟吧

最後,我這裡上一個例項,供大家參考。

原始檔裡填入如下程式碼:

[cpp] view plain copy
  1. #include <opencv2/core/core.hpp>  
  2. #include <iostream>  
  3. #include <string>  
  4.   
  5. using namespace cv;  
  6. using namespace std;  
  7.   
  8. void help(char** av)  
  9. {  
  10.     cout << endl   
  11.         << av[0] << " shows the usage of the OpenCV serialization functionality."         << endl  
  12.         << "usage: "                                                                      << endl  
  13.         <<  av[0] << " outputfile.yml.gz"                                                 << endl  
  14.         << "The output file may be either XML (xml) or YAML (yml/yaml). You can even compress it by "  
  15.         << "specifying this in its extension like xml.gz yaml.gz etc... "                  << endl  
  16.         << "With FileStorage you can serialize objects in OpenCV by using the << and >> operators" << endl  
  17.         << "For example: - create a class and have it serialized"                         << endl  
  18.         << "             - use it to read and write matrices."                            << endl;  
  19. }  
  20.   
  21. class MyData  
  22. {  
  23. public:  
  24.     MyData() : A(0), X(0), id()  
  25.     {}  
  26.     explicit MyData(int) : A(97), X(CV_PI), id("mydata1234"// explicit to avoid implicit conversion  
  27.     {}  
  28.     void write(FileStorage& fs) const                        //Write serialization for this class  
  29.     {  
  30.         fs << "{" << "A" << A << "X" << X << "id" << id << "}";  
  31.     }  
  32.     void read(const FileNode& node)                          //Read serialization for this class  
  33.     {  
  34.         A = (int)node["A"];  
  35.         X = (double)node["X"];  
  36.         id = (string)node["id"];  
  37.     }  
  38. public:   // Data Members  
  39.     int A;  
  40.     double X;  
  41.     string id;  
  42. };  
  43.   
  44. //These write and read functions must be defined for the serialization in FileStorage to work  
  45. void write(FileStorage& fs, const std::string&, const MyData& x)  
  46. {  
  47.     x.write(fs);  
  48. }  
  49. void read(const FileNode& node, MyData& x, const MyData& default_value = MyData()){  
  50.     if(node.empty())  
  51.         x = default_value;  
  52.     else  
  53.         x.read(node);  
  54. }  
  55.   
  56. // This function will print our custom class to the console  
  57. ostream& operator<<(ostream& out, const MyData& m)   
  58. {   
  59.     out << "{ id = " << m.id << ", ";  
  60.     out << "X = " << m.X << ", ";  
  61.     out << "A = " << m.A << "}";  
  62.     return out;  
  63. }  
  64.   
  65. int main(int ac, char** av)  
  66. {  
  67.     if (ac != 2)  
  68.     {  
  69.         help(av);  
  70.         return 1;  
  71.     }  
  72.   
  73.     string filename = av[1];  
  74.     { //write  
  75.         Mat R = Mat_<uchar>::eye(3, 3),  
  76.             T = Mat_<double>::zeros(3, 1);  
  77.         MyData m(1);  
  78.   
  79.         FileStorage fs(filename, FileStorage::WRITE);  
  80.   
  81.         fs << "iterationNr" << 100;  
  82.         fs << "strings" << "[";                              // text - string sequence  
  83.         fs << "image1.jpg" << "Awesomeness" << "baboon.jpg";  
  84.         fs << "]";                                           // close sequence  
  85.           
  86.         fs << "Mapping";                              // text - mapping  
  87.         fs << "{" << "One" << 1;  
  88.         fs <<        "Two" << 2 << "}";                 
  89.   
  90.         fs << "R" << R;                                      // cv::Mat  
  91.         fs << "T" << T;  
  92.   
  93.         fs << "MyData" << m;                                // your own data structures  
  94.   
  95.         fs.release();                                       // explicit close  
  96.         cout << "Write Done." << endl;  
  97.     }  
  98.   
  99.     {//read  
  100.         cout << endl << "Reading: " << endl;  
  101.         FileStorage fs;   
  102.         fs.open(filename, FileStorage::READ);  
  103.   
  104.         int itNr;   
  105.         //fs["iterationNr"] >> itNr;  
  106.         itNr = (int) fs["iterationNr"];  
  107.         cout << itNr;  
  108.         if (!fs.isOpened())  
  109.         {  
  110.             cerr << "Failed to open " << filename << endl;  
  111.             help(av);  
  112.             return 1;  
  113.         }  
  114.   
  115.         FileNode n = fs["strings"];                         // Read string sequence - Get node  
  116.         if (n.type() != FileNode::SEQ)  
  117.         {  
  118.             cerr << "strings is not a sequence! FAIL" << endl;  
  119.             return 1;  
  120.         }  
  121.   
  122.         FileNodeIterator it = n.begin(), it_end = n.end(); // Go through the node  
  123.         for (; it != it_end; ++it)  
  124.             cout << (string)*it << endl;  
  125.           
  126.           
  127.         n = fs["Mapping"];                                // Read mappings from a sequence  
  128.         cout << "Two  " << (int)(n["Two"]) << "; ";   
  129.         cout << "One  " << (int)(n["One"]) << endl << endl;   
  130.           
  131.   
  132.         MyData m;  
  133.         Mat R, T;  
  134.   
  135.         fs["R"] >> R;                                      // Read cv::Mat  
  136.         fs["T"] >> T;  
  137.         fs["MyData"] >> m;                                 // Read your own structure_  
  138.   
  139.         cout << endl   
  140.             << "R = " << R << endl;  
  141.         cout << "T = " << T << endl << endl;  
  142.         cout << "MyData = " << endl << m << endl << endl;  
  143.   
  144.         //Show default behavior for non existing nodes  
  145.         cout << "Attempt to read NonExisting (should initialize the data structure with its default).";    
  146.         fs["NonExisting"] >> m;  
  147.         cout << endl << "NonExisting = " << endl << m << endl;  
  148.     }  
  149.   
  150.     cout << endl   
  151.         << "Tip: Open up " << filename << " with a text editor to see the serialized data." << endl;  
  152.   
  153.     return 0;  
  154. }  

編譯後,在命令列進入到檔案目錄,執行test test.xml,執行結果如下,生成一個test . xml檔案,內容如下:

[html] view plain copy
  1.   <?xml version="1.0" ?>   
  2. <opencv_storage>  
  3.   <iterationNr>100</iterationNr>   
  4.   <strings>image1.jpg Awesomeness baboon.jpg</strings>   
  5. <Mapping>  
  6.   <One>1</One>   
  7.   <Two>2</Two>   
  8.   </Mapping>  
  9. <R type_id="opencv-matrix">  
  10.   <rows>3</rows>   
  11.   <cols>3</cols>   
  12.   <dt>u</dt>   
  13.   <data>1 0 0 0 1 0 0 0 1</data>   
  14.   </R>  
  15. <T type_id="opencv-matrix">  
  16.   <rows>3</rows>   
  17.   <cols>1</cols>   
  18.   <dt>d</dt>   
  19.   <data>0. 0. 0.</data>   
  20.   </T>  
  21. <MyData>  
  22.   <A>97</A>   
  23.   <X>3.1415926535897931e+000</X>   
  24.   <id>mydata1234</id>   
  25.   </MyData>  
  26.   </opencv_storage>  


OpenCV學習筆記(七)——影像處理之濾波器ImgProc


先介紹幾個最基本的核濾波器相關的類

2D影像濾波器基礎類BaseFilter:dst(x,y) = F(src(x,y), src(x+1,y)... src(x+wdith-1,y), src(y+1,x)... src(x+width-1, y+height-1) ); 相關的呼叫函式為getLinearFilter、getMorphologyFilter

單行核濾波器基礎類BaseRowFilter:dst(x,y) = F(src(x,y), src(x+1,y),...src(x+width-1,y));相關的呼叫函式為getLinearRowFilter、getMorphologyRowFilter

單列核濾波器基礎類BaseColumnFilter:dst(x,y) = F(src(x,y), src(x,y+1),...src(x,y+width-1));相關的呼叫函式為getColumnSumFilter、getLinearColumnFilter、getMorphologyColumnFilter

FilterEngine:該類可以應用在對影像的任意濾波操作當中,在OpenCV濾波器函式中扮演著很重要的角色,相關的函式有createBoxFitler、createDerivFitlter、createGaussianFilter、createLinearFilter、createMorphologyFilter、createSeparableLinearFilter

基於這些類有一些基本的濾波器bilateralFilter、blur、boxFilter

還有一些形態學操作如:dilate、erode、morphologyEx

還有基於核和影像卷積的濾波器filter2D

還有一些典型的濾波器如GaussianBlur、medianBlur、Laplacian、pyrMeanShiftFiltering、sepFilter2D

還有Sobel、Scharr運算子

其他一些函式有borderInterpolate、buildPyramid、copyMakeBorder、createBoxFilter、createDirivFilter、createGaussianFliter、createLinearFilter、createMorphologyFilter、createSeparableLinearFilter、getDerivKernels、getGaussianKernel、getKernelType、getStructuringElement、pyrDown、pyrUp

還老版本的濾波器cvSmooth

這裡介紹一下我使用Laplacian濾波的心得,這個函式的第三個引數為輸出的影像的深度,注意經過拉普拉斯運算元處理後得到的值是有正有負的,所以輸出影像的深度最好為輸入影像深度的2倍,才能有效防止資料溢位,如必須要使用8位的資料,可以再使用函式convertScaleAbs處理。而且要注意使用的拉普拉斯運算元掩膜的中心繫數為負。



OpenCV學習筆記(八)——影像處理之直方圖ImgProc

直方圖histograms也是影像處理中經常用到的一種手段。新版本對直方圖不再使用之前的histogram的形式,而是用統一的Mat或者MatND的格式來儲存直方圖,可見新版本Mat資料結構的優勢。先介紹下其相關的函式

calcHist、calcBackProject、compareHist、EMD、equalizeHist。除了這幾個常用的函式以為,還有一些c函式寫的直方圖類CvHistogram的相關操作,如下:cvCalcBackProjectPatch、cvCalcProbDensity、cvClearHist、cvCopyHist、cvCreateHist、cvGetHistValue_XD、cvGetMinMaxHistValue、cvMakeHistHeaderForArray、cvNormalizeHist、QueryHistValue_XD、cvReleaseHist、cvSetHistBinRanges、cvThreshHist、cvCalcPGH

calcHist函式為計算影像的直方圖,使用方法如下:

[cpp] view plain copy
  1. // C++:   
  2. void calcHist(const Mat* arrays, int narrays, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=truebool accumulate=false )  
  3. // C++:   
  4. void calcHist(const Mat* arrays, int narrays, const int* channels, InputArray mask, SparseMat& hist, int dims, const int* histSize, const float** ranges, bool uniform=truebool accumulate=false )  

arrays為輸入影像指標,narrays為輸入影像的個數,channels為用來計算直方圖的通道列表,mask為掩膜矩陣,不為空的時候,只計算arrays中的掩膜區域的直方圖,hist為輸出的直方圖矩陣,dims為直方圖矩陣的維度,histSize為每一維直方圖矩陣的大小,ranges為每一維直方圖元素的取值範圍,是一個2維陣列的地址,uniform為直方圖是否為統一模式,統一模式下會拉伸為range的大小,accumulate為累計標誌,方便直方圖的更新,不需要重新計算

舉幾個例項方便大家理解:

對於影像為灰度圖,呼叫方式如下:

[cpp] view plain copy
  1. int histSize = 255;  
  2. float ranges[] = {0, 255};  
  3. const float* histRange = {ranges};  
  4. calcHist(&img, 1, 0, Mat(), hist, 1, &histSize, &histRange);  

直方圖的歸一化已經不再適合cvNormalizeHist這個函式了,只需要用對矩陣的歸一化函式normalize就可以實現了。

直方圖均衡化函式為equalizeHist,這個函式比較簡單,這裡就不詳細介紹了

直方圖的比較函式為compareHist,函式返回值為兩矩陣的相似程度,相似度衡量的辦法目前支援4種

– CV_COMP_CORREL Correlation相關係數,相同為1,相似度範圍為[ 1, 0 )

– CV_COMP_CHISQR Chi-Square卡方,相同為0,相似度範圍為[ 0, +inf )

– CV_COMP_INTERSECT Intersection直方圖交,數越大越相似,,相似度範圍為[ 0, +inf )

– CV_COMP_BHATTACHARYYA Bhattacharyya distance做常態分別比對的Bhattacharyya 距離,相同為0,,相似度範圍為[ 0, +inf )

計算反向投影圖函式為calcBackProject。所謂反向投影圖就是一個概率密度圖。calcBackProject的輸入為影像及其直方圖,輸出與待跟蹤影像大小相同,每一個畫素點表示該點為目標區域的概率。這個點越亮,該點屬於物體的概率越大。關於反向直方圖,可以參考一下這篇文章http://blog.163.com/thomaskjh@126/blog/static/370829982010112810358501/,這個函式使我們利用特徵直方圖尋找圖片中的特徵區域變得更加方便容易。這裡舉一個比較常用的例子:如果已經有一個膚色的特徵直方圖,則可以在待檢測影像中利用直方圖方向投影圖找出圖片中的膚色區域。


OpenCV學習筆記(九)——2維特徵Feature2D

基於特徵點的影像匹配是影像處理中經常會遇到的問題,手動選取特徵點太麻煩了。比較經典常用的特徵點自動提取的辦法有Harris特徵、SIFT特徵、SURF特徵。

先介紹利用SURF特徵的特徵描述辦法,其操作封裝在類SurfFeatureDetector中,利用類內的detect函式可以檢測出SURF特徵的關鍵點,儲存在vector容器中。第二部利用SurfDescriptorExtractor類進行特徵向量的相關計算。將之前的vector變數變成向量矩陣形式儲存在Mat中。最後強行匹配兩幅影像的特徵向量,利用了類BruteForceMatcher中的函式match。程式碼如下:

[cpp] view plain copy
  1. /** 
  2.  * @file SURF_descriptor 
  3.  * @brief SURF detector + descritpor + BruteForce Matcher + drawing matches with OpenCV functions 
  4.  * @author A. Huaman 
  5.  */  
  6.   
  7. #include <stdio.h>  
  8. #include <iostream>  
  9. #include "opencv2/core/core.hpp"  
  10. #include "opencv2/features2d/features2d.hpp"  
  11. #include "opencv2/highgui/highgui.hpp"  
  12.   
  13. using namespace cv;  
  14.   
  15. void readme();  
  16.   
  17. /** 
  18.  * @function main 
  19.  * @brief Main function 
  20.  */  
  21. int main( int argc, char** argv )  
  22. {  
  23.   if( argc != 3 )  
  24.   { return -1; }  
  25.   
  26.   Mat img_1 = imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE );  
  27.   Mat img_2 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE );  
  28.     
  29.   if( !img_1.data || !img_2.data )  
  30.   { return -1; }  
  31.   
  32.   //-- Step 1: Detect the keypoints using SURF Detector  
  33.   int minHessian = 400;  
  34.   
  35.   SurfFeatureDetector detector( minHessian );  
  36.   
  37.   std::vector<KeyPoint> keypoints_1, keypoints_2;  
  38.   
  39.   detector.detect( img_1, keypoints_1 );  
  40.   detector.detect( img_2, keypoints_2 );  
  41.   
  42.   //-- Step 2: Calculate descriptors (feature vectors)  
  43.   SurfDescriptorExtractor extractor;  
  44.   
  45.   Mat descriptors_1, descriptors_2;  
  46.   
  47.   extractor.compute( img_1, keypoints_1, descriptors_1 );  
  48.   extractor.compute( img_2, keypoints_2, descriptors_2 );  
  49.   
  50.   //-- Step 3: Matching descriptor vectors with a brute force matcher  
  51.   BruteForceMatcher< L2<float> > matcher;  
  52.   std::vector< DMatch > matches;  
  53.   matcher.match( descriptors_1, descriptors_2, matches );  
  54.   
  55.   //-- Draw matches  
  56.   Mat img_matches;  
  57.   drawMatches( img_1, keypoints_1, img_2, keypoints_2, matches, img_matches );   
  58.   
  59.   //-- Show detected matches  
  60.   imshow("Matches", img_matches );  
  61.   
  62.   waitKey(0);  
  63.   
  64.   return 0;  
  65. }  
  66.   
  67. /** 
  68.  * @function readme 
  69.  */  
  70. void readme()  
  71. { std::cout << " Usage: ./SURF_descriptor <img1> <img2>" << std::endl; }  

當然,進行強匹配的效果不夠理想,這裡再介紹一種FLANN特徵匹配演算法。前兩步與上述程式碼相同,第三步利用FlannBasedMatcher類進行特徵匹配,並只保留好的特徵匹配點,程式碼如下:

[cpp] view plain copy
  1. //-- Step 3: Matching descriptor vectors using FLANN matcher  
  2. FlannBasedMatcher matcher;  
  3. std::vector< DMatch > matches;  
  4. matcher.match( descriptors_1, descriptors_2, matches );  
  5.   
  6. double max_dist = 0; double min_dist = 100;  
  7.   
  8. //-- Quick calculation of max and min distances between keypoints  
  9. forint i = 0; i < descriptors_1.rows; i++ )  
  10. double dist = matches[i].distance;  
  11.   if( dist < min_dist ) min_dist = dist;  
  12.   if( dist > max_dist ) max_dist = dist;  
  13. }  
  14.   
  15. printf("-- Max dist : %f \n", max_dist );  
  16. printf("-- Min dist : %f \n", min_dist );  
  17.   
  18. //-- Draw only "good" matches (i.e. whose distance is less than 2*min_dist )  
  19. //-- PS.- radiusMatch can also be used here.  
  20. std::vector< DMatch > good_matches;  
  21.   
  22. forint i = 0; i < descriptors_1.rows; i++ )  
  23. if( matches[i].distance < 2*min_dist )  
  24.   { good_matches.push_back( matches[i]); }  
  25. }    
  26.   
  27. //-- Draw only "good" matches  
  28. Mat img_matches;  
  29. drawMatches( img_1, keypoints_1, img_2, keypoints_2,   
  30.              good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),   
  31.              vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );   
  32.   
  33. //-- Show detected matches  
  34. imshow( "Good Matches", img_matches );  

在FLANN特徵匹配的基礎上,還可以進一步利用Homography對映找出已知物體。具體來說就是利用findHomography函式利用匹配的關鍵點找出相應的變換,再利用perspectiveTransform函式對映點群。具體程式碼如下:

[cpp] view plain copy
  1. //-- Localize the object from img_1 in img_2   
  2. std::vector<Point2f> obj;  
  3. std::vector<Point2f> scene;  
  4.   
  5. forint i = 0; i < good_matches.size(); i++ )  
  6. {  
  7.   //-- Get the keypoints from the good matches  
  8.   obj.push_back( keypoints_1[ good_matches[i].queryIdx ].pt );  
  9.   scene.push_back( keypoints_2[ good_matches[i].trainIdx ].pt );   
  10. }  
  11.   
  12. Mat H = findHomography( obj, scene, CV_RANSAC );  
  13.   
  14. //-- Get the corners from the image_1 ( the object to be "detected" )  
  15. Point2f obj_corners[4] = { cvPoint(0,0), cvPoint( img_1.cols, 0 ), cvPoint( img_1.cols, img_1.rows ), cvPoint( 0, img_1.rows ) };  
  16. Point scene_corners[4];  
  17.   
  18. //-- Map these corners in the scene ( image_2)  
  19. forint i = 0; i < 4; i++ )  
  20. {  
  21.   double x = obj_corners[i].x;   
  22.   double y = obj_corners[i].y;  
  23.   
  24.   double Z = 1./( H.at<double>(2,0)*x + H.at<double>(2,1)*y + H.at<double>(2,2) );  
  25.   double X = ( H.at<double>(0,0)*x + H.at<double>(0,1)*y + H.at<double>(0,2) )*Z;  
  26.   double Y = ( H.at<double>(1,0)*x + H.at<double>(1,1)*y + H.at<double>(1,2) )*Z;  
  27.   scene_corners[i] = cvPoint( cvRound(X) + img_1.cols, cvRound(Y) );  
  28. }    
  29.    
  30. //-- Draw lines between the corners (the mapped object in the scene - image_2 )  
  31. line( img_matches, scene_corners[0], scene_corners[1], Scalar(0, 255, 0), 2 );  
  32. line( img_matches, scene_corners[1], scene_corners[2], Scalar( 0, 255, 0), 2 );  
  33. line( img_matches, scene_corners[2], scene_corners[3], Scalar( 0, 255, 0), 2 );  
  34. line( img_matches, scene_corners[3], scene_corners[0], Scalar( 0, 255, 0), 2 );  
  35.   
  36. //-- Show detected matches  
  37. imshow( "Good Matches & Object detection", img_matches );  

然後再看一下Harris特徵檢測,在計算機視覺中,通常需要找出兩幀影像的匹配點,如果能找到兩幅影像如何相關,就能提取出兩幅影像的資訊。我們說的特徵的最大特點就是它具有唯一可識別這一特點,影像特徵的型別通常指邊界、角點(興趣點)、斑點(興趣區域)。角點就是影像的一個區域性特徵,應用廣泛。harris角點檢測是一種直接基於灰度影像的角點提取演算法,穩定性高,尤其對L型角點檢測精度高,但由於採用了高斯濾波,運算速度相對較慢,角點資訊有丟失和位置偏移的現象,而且角點提取有聚簇現象。具體實現就是使用函式cornerHarris實現。

除了利用Harris進行角點檢測,還可以利用Shi-Tomasi方法進行角點檢測。使用函式goodFeaturesToTrack對角點進行檢測,效果也不錯。也可以自己製作角點檢測的函式,需要用到cornerMinEigenVal函式和minMaxLoc函式,最後的特徵點選取,判斷條件要根據自己的情況編輯。如果對特徵點,角點的精度要求更高,可以用cornerSubPix函式將角點定位到子畫素。



OpenCV學習筆記(十)——圖形互動和媒體介面HighGUI


OpenCV提供一個功能強大的UI介面,可以在MFC、Qt、WinForms、Cocoa等平臺下使用,甚至不需要其他的平臺。新版本的HighGUI介面包括:

建立並控制視窗,該視窗可以顯示圖片並記錄其內容

為視窗新增了trackbars控制元件,可以方便利用滑鼠進行控制而不是之前版本的只能利用鍵盤

讀寫硬碟和記憶體的圖片

讀取攝像頭的視訊、讀寫視訊檔案

先來介紹UI,包括函式createTrackbar、getTrackbarPos、setTrackbarPos、imshow、namedWindow、destroyWindow、destroyAllWindows、MoveWindow、ResizeWindow、SetMouseCallback、waitKey。這些函式保證了影像的基本處理、tarckbar的控制和滑鼠鍵盤的響應

介紹一下讀寫影像視訊的函式:影像相關的函式有imdecode、imencode、imread、imwrite。讀取視訊相關為VideoCapture類,負責捕捉檔案和攝像頭的視訊,該類內有成員函式VideoCapture、open、isOpened、release、grab、retrieve、read、get、set,寫視訊的類為VideoWriter,類內有成員函式VideoWriter、open、isOpened、write

新版本還為Qt做了新函式,這裡就不介紹了,有興趣的朋友可以自己看一下參考手冊的第四章第三節。

這裡介紹幾個常用的新功能,首先介紹一下新增滑桿控制元件Trackbar。呼叫函式為:

[cpp] view plain copy
  1. createTrackbar( TrackbarName, "Linear Blend", &alpha_slider, alpha_slider_max, on_trackbar );  

第一個引數為字串作為標籤,第二個引數為所在視窗的名字,第三個引數為儲存滑桿位置的值地址,其範圍為0~alpha_slider_max(第四個引數),最後一個引數為移動滑桿時呼叫的回撥函式名。
OpenCV2.0版本加強了對視訊處理的支援,不再需要對一組連續的圖片進行處理,可以進行實時的影像採集和記錄以及儲存。視訊的操作基本都被封裝在VideoCapture類中。開啟視訊可以可以通過如下程式碼實現:
[cpp] view plain copy
  1. VideoCapture captRefrnc(sourceReference);  
  2. // or  
  3. VideoCapture captUndTst;  
  4. captUndTst.open(sourceCompareWith);  

其中sourceReference和sourceCompareWith為string型,為檔名。還可以通過isOpened函式檢測視訊是否成功開啟。也可以呼叫release函式提前關閉視訊。還可以講VideoCapture放到Mat結構中,因為視訊流是一連串的,可以通過read函式或>>操作符逐幀的讀取,例如:
[cpp] view plain copy
  1. Mat frameReference, frameUnderTest;  
  2. captRefrnc >> frameReference;  
  3. captUndTst.open(frameUnderTest);  

read函式只能逐幀的抓取,如果要抓取某一幀,可以成對的呼叫grab函式和retrieve函式。get函式可以獲取視訊相關資訊。set函式可以控制視訊的一些值,比如是指視訊的當前位置或幀數。

可以使用VideoWriter類建立新視訊,其open,isOpened函式呼叫方法類似,write函式或<<運算子向視訊寫入內容,可以使用split函式和merge函式單獨調整RGB通道的值

今日,被一個網友指出,說OpenCV以前提供的讀寫功能採用VFW,效率低下且有些格式支援不好。而 OpenCV 2.0 內建了videoInput Library,可以自動在VFW和DirectShow間切換。videoInput是老外寫的功能強大的開源視訊處理庫。是一個第三方庫,2.0~2.2的版本專門有一個3rdparty對該庫進行支援,而在最新的2.3版本中,已經講videoInput庫整合到highgui中了,想使用它的話,只需要在cmake中設定巨集WITH_VIDEOiNPUT=OFF/ON即可。

以後有新學到的東西都會陸續補充進來。



from: http://blog.csdn.net/yang_xian521/article/category/910716

看過本文的人也看了:

相關文章