電腦視覺1->opencv4學習指南1 | 環境配置與常式

opencv雖然很有名,但是自己一直沒怎麼玩過,暑假的時候使用深度相機做項目,但負責的不是程式碼模組,也只是配好了環境,沒有繼續了解影像處理。最近電子實習老師有教這個東西,但是身邊不少同學遇到了麻煩,所以在此總結了一下,匯總了一些我行之有效的教程和官方資料,並且附上了兩個常式;方便ubuntu環境下的opencv新手快速上手。

00 opencv4.5.3

先放一個鏈接,當初在ubuntu1804上無痛配置好opencv4.5.3的環境,採用的是這個教程.

下面是我自己的總結,大家可以避免頁面跳轉。

00-1 安裝相關軟體包

1 sudo apt install  build-essential
2  
3 sudo apt install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev  
4  
5 sudo apt install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev

 

如果第三個命令行無法定位軟體包;

則

1 sudo apt install  build-essential
2  
3 sudo apt install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev  
4  
5 sudo apt install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev

 

libjasper1 是 libjasper-dev 的依賴包

00-2 源碼下載

opencv官網下載:Releases - OpenCV

找到自己想要的版本,這裡我使用的是4.5.3,暑假的時候還算比較新,現在已經out了。

下載成功後解壓,解壓到哪個文件夾都可以。

00-3 編譯源碼

採用cmake的編譯方法對下載解壓後的軟體包進行編譯。

進入解壓出來的OpenCV-4.5.3文件夾,創建一個新文件夾,我創建的叫 build

進入新文件夾,打開終端,進入這個文件夾

執行命令

1 cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/your_opencv_install_path
2 ​
3 sudo make
4 //為了快也可以直接 sudo make -j3並行編譯
5 ​
6 sudo make install 

 

完成後,OpenCV 就安裝好了,接下來要配置 OpenCV 的編譯環境;

00-4 配置環境

首先將OpenCV的庫添加到路徑,從而可以讓系統找到

打開opencv.conf ,打開後很可能是空白

 sudo gedit /etc/ld.so.conf.d/opencv.conf  

在文末添加

 /usr/local/lib 

接下來配置 bash

 sudo gedit /etc/bash.bashrc  

在文末添加

1 PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig  
2 ​
3 export PKG_CONFIG_PATH  

 

保存後退出刷新(source)

1 source /etc/bash.bashrc  
2 
3 sudo updatedb 
 

配置完成。

01 測試安裝成功

轉到 opencv-4.5.3/sample/cpp/example_cmake 目錄下,打開終端

運行如下命令:

1 cmake .
2 make
3 ./opencv_example

彈出該頁面:

然後把電腦自帶的攝影機打開,就會出現畫面。說明安裝、配置成功。

02 讀取jpg並顯示

02-1 工程建立

首先,在opencv4.5.3文件夾里建立一個我們學慣用的文件夾,日後所有的學習項目都會放在這裡,方便管理。

 mkdir opencv-learn 

然後,我們進入這個文件夾

 cd opencv-learn 

建立今天的第一個項目

1 mkdir learn1
2 cd learn1
3 touch DisplayImage.cpp

02-2 C++程式

編寫我們讀取jpg並顯示的程式

 1 #include <stdio.h>  
 2 #include <opencv2/opencv.hpp>  
 3 using namespace cv;  
 4 int main(int argc, char** argv )  
 5 {  
 6     if ( argc != 2 )  
 7     {  
 8         printf("usage: DisplayImage.out <Image_Path>\n");  
 9         return -1;  
10     }  
11     Mat image;  
12     image = imread( argv[1], 1 );  
13     if ( !image.data )  
14     {  
15         printf("No image data \n");  
16         return -1;  
17     }  
18     namedWindow("Display Image", WINDOW_AUTOSIZE );  
19     imshow("Display Image", image);  
20     waitKey(0);  
21     return 0;  
22 } 

 

02-3 程式碼解析

opencv的程式碼風格與arduino很相似,函數很多,且名字與功能強相關,初入門的編程主要是函數的調用,不涉及複雜數據結構與演算法。

前面的if-else是魯棒性保證,感興趣可以查一下argv與argc;這裡程式碼的意思是如果命令行編譯參數不是2,那麼報錯。

argv與argc簡介如下:

這兩個參數在使用命令行編譯的時候有用。

  1. argc 為整型,是命令行參數的個數;

  2. argv 是字元串型,為字元串數組,數組成員意義如下:

    argv[0]指向程式名;

    argv[1]指向程式名後的第一個字元串;

    ...以此類推


接下來:

 Mat image;  

定義一個Mat格式的矩陣image用來存儲下面會用到的圖片。

  image = imread( argv[1], 1 ); 

把讀取的圖片資訊,存進來。

  namedWindow("Display Image", WINDOW_AUTOSIZE );  

這裡是指定展現處理後圖片的視窗的名字

1    imshow("Display Image", image);  
2     waitKey(0);  

展示圖片(會彈出一個視窗,名字是上面定義的名字)

int waitKey(int delay=0)的功能是不斷刷新圖片,頻率由delay來衡量。

  1. 在顯示圖片和影片時,會在imshow()時,通常在後面加上while(WaitKey(n)==key),key為大於0的數即可,這樣程式將在此處循環運行直到按鍵響應為key。

  2. delay:為0時,則會一直顯示這一幀,這在顯示攝影機和影片時有用。這個參數用於設置在顯示完一幀影像後程式等待「delay」ms後再顯示下一幀圖片。

  3. 舉幾個例子

    1 if(waitKey(10)>=0)//是說10ms中按任意鍵進入if語句
    2 
    3 while(waitKey(2)!=27)//表示不按Esc按鍵則一直在2ms後顯示

02-4 CMakeLists.txt編寫

A 內容

接下來我們採用CMakeLists.txt的形式進行編譯。

這種形式可以避免引用庫不全面而造成的未定義報錯。//而這在opencv編程中是經常出現的。

因此CMakeLists.txt是一種很好的編譯方式,值得學習。

1 touch CMakeLists.txt
2 
3 gedit CMakeLists.txt

 

在彈出的頁面

1     cmake_minimum_required(VERSION 2.8)  
2     project( DisplayImage )  
3     find_package( OpenCV REQUIRED )  
4     add_executable( DisplayImage DisplayImage.cpp )  
5     target_link_libraries( DisplayImage ${OpenCV_LIBS} )

 

B 解釋

這裡這些語句的意思是:⬇

1.cmake_minimum_required,指定最小需要的版本。裡面的內容也是與版本資訊有關。

2.project,基本用法是:用於指定cmake工程的名字,比如此處就制定了當前工程的名字是DisplayImage

3.find_package

關於find_package:

當我們編譯一個需要使用第三方庫的程式時,我們需要知道的是:

目標對照
去哪兒找頭文件 .h GCC的 -I 參數
去哪兒找庫文件 (.so/.dll/.lib/.dylib/…) GCC的 -L參數
需要鏈接的庫文件的名字 GCC的 -l 參數

比如我們需要一個第三方庫 curl,那麼我們的 CMakeLists.txt 需要指定頭文件目錄,和庫文件,類似:

1 include_directiories(/usr/include/curl)
2 
3 target_link_libraries(myprogram path/curl.so)

而如果我們要藉助cmake語法的功能,如果藉助於cmake提供的finder會怎麼樣呢?使用cmake的Modules目錄下的FindCURL.cmake,相應的CMakeList.txt 文件:

1 find_package(CURL REQUIRED)
2 
3 include_directories(${CURL_INCLUDE_DIR})
4 
5 target_link_libraries(curltest ${CURL_LIBRARY})

cmake是如何查找這些列出的第三方庫的,在這裡就不談了

可以看出我們編寫的CMakeList.txt這句的意思是OpenCV的依賴庫的添加。

4.add_executable

使用指定的源文件來生成目標可執行文件。這裡的目標可執行文件分為三類:

  1. 普通可執行目標文件

  2. 導入可執行目標文件

  3. 別名可執行目標文件

分別對應的三種命令格式如下:

1 add_executable (<name> [WIN32] [MACOSX_BUNDLE]
2       [EXCLUDE_FROM_ALL]
3       [source1] [source2 ...])
4       
5 add_executable (<name> IMPORTED [GLOBAL])
6 ​
7 add_executable (<name> ALIAS <target>)

這裡使用的是最簡單的形式:

 1 #語法:add_executable(可執行程式名 要編譯的cpp) 

5.target_link_libraries

該指令的作用為將目標文件與庫文件進行鏈接。

這項的編寫需要注意:庫之間可能也存在著依賴關係,被依賴的庫放在後面。

02-5 編譯

編寫完成後,我們編譯這個項目:

1 cmake .
2 
3 make

 

 

 

 

記得在項目里(與程式碼同文件夾下)放入我們要處理的圖片(jpg)。

02-6 執行得到結果

然後我們執行

  ./DisplayImage TEST.jpg 

即可看到C++程式調用opencv處理後顯示的圖片,雖然在此例子中展示的是圖片原貌。

在這個程式基礎上可以實現灰度圖等等。

03 使用電腦攝影機初步

03-1 工程建立

我們建立一個新的文件夾learn2並建立文件

1 mkdir learn2
2 
3 cd learn2
4 
5 touch testopencv_camera.cpp

 

03-2 C++程式

 1 //兩個功能:如果直接運行程式./exe文件,就是打開電腦攝影機並顯示;如果./exe avi影片,那會讀取這個avi影片,並在窗口裡顯示出來。
 2 #include "opencv/highgui.h"
 3 #include "opencv/cv.h"
 4 using namespace std;
 5 using namespace cv;
 6  
 7 int main(int argc, char** argv){
 8   cvNamedWindow("testcamera", CV_WINDOW_AUTOSIZE);
 9   CvCapture* capture;
10   if (argc == 1){
11     capture=cvCaptureFromCAM(0);
12     printf("capture 0\n");
13   }
14   else {
15     capture = cvCreateFileCapture(argv[1]);
16     printf("capture argv1\n");
17   }
18   assert(capture != NULL);
19   IplImage* frame;
20   frame = cvCreateImage(cvSize(640, 320), IPL_DEPTH_16U, 3);
21   while(1){
22     frame = cvQueryFrame(capture);
23     if (!frame)
24       break;
25     cvShowImage("testcamera", frame);
26     char c=cvWaitKey(33);//顯示間隔是33ms
27     if (c==27)////27ms內按下任意建進入if
28       break;
29   }
30   cvReleaseCapture(&capture);
31   cvDestroyWindow("testcamera");
32   return 0;
33 }
34 ​

03-3 程式碼解析

常規庫、工作空間準備;

1 #include "opencv/highgui.h"
2 #include "opencv/cv.h"
3 using namespace std;
4 using namespace cv;

接下來,如果我們的命令行編譯參數只有一個(argc==1)那麼調用cvCaptureFromCAM()函數初始化CvCapture結構的 capture變數。

這是OpenCV庫中的一個函數。

初始化從攝影機中獲取的影片

 CvCapture* cvCaptureFromCAM( int index ); 
  • index是個整數;

  • 要使用的攝影機索引。如果只有一個攝影機或者用哪個攝影機也無所謂,那使用參數-1應該便可以。


這個函數用「從攝影機獲取的影片流「分配和初始化CvCapture結構。

  1. 目前在Windows下可使用兩種介面:Video forWindows(VFW)和Matrox Imaging Library(MIL);

  2. Linux下也有兩種介面:V4L和FireWire(IEEE1394)。(現在不需要深入了解這個)

釋放這個結構,使用函數cvReleaseCapture。

接著,否則的話,我們這個程式會判定我們將使用下面這個功能:

讀取一段影片(avi格式)並顯示出來;

我們選取參數中的第二個參數(avi影片)讀取到capture變數里。

接下來是一個魯棒性過濾:

1  assert(capture != NULL);
2 //意為如果表達式為假,整個程式將推出,不再運行,如果表達式成立,則繼續運行。
3 ​
4 //使用assert()的缺點是,頻繁的調用會極大的影響程式的性能,增加額外的開銷。 

接下來我們要創建一個3通道影像變數(RGB影像)

1   IplImage* frame;//創建變數
2 ​
3   frame = cvCreateImage(cvSize(640, 320), IPL_DEPTH_16U, 3);
4 //寬為640,高為360,影像像素的位深度為16位無符號整數的影像
5 //儘管在一般IPL影像格式中可以以非交叉的方式存儲,並且一些OpenCV可以處理它,但此函數只能創建交叉存儲的影像。

函數cvCreateImage創建影像首地址,並分配存儲空間。

IplImage* cvCreateImage(CvSize cvSize(int width, int height), int depth, int channels);

接下來進入循環

攝影機或者文件中抓取並解壓返回這一幀

   frame = cvQueryFrame(capture); 

這個函數:

 1 IplImage* cvQueryFrame( CvCapture* capture ); 

這個函數僅僅是函數cvGrabFrame和函數cvRetrieveFrame在一起調用的組合。

返回的影像不可以被用戶釋放或者修改。

抓取後,capture被指向下一幀,可用cvSetCaptureProperty調整capture到合適的幀。

如果影像存儲空間沒有批下來,則退出循環;

1  if (!frame)、
2       break;

批下來了,就展示出來影片的一幀frame

  cvShowImage("testcamera", frame); 

下面是退出程式的控制程式;

1     char c=cvWaitKey(33);
2     if (c==27)//27ms
3       break;
4   }
5   cvReleaseCapture(&capture);
6   cvDestroyWindow("testcamera");
7   return 0;

03-4 編譯運行結果

1 # 編譯
2 g++ testopencv_camera.cpp -o first `pkg-config --libs --cflags opencv` -ldl
3 #運行
4 ./first
5 #就可以看到電腦攝影機顯示的"影片"