1. 程式人生 > >初識OpenCV(基礎篇)

初識OpenCV(基礎篇)

OpenCV其實就是一堆C和C++語言的原始碼檔案,這些原始碼檔案中實現了許多常用的計算機視覺演算法。例如C介面函式cvCanny()實現了Canny邊緣提取演算法。可以直接將這些原始碼新增到我們自己的軟體專案中,而不需要自己再去寫程式碼實現Canny演算法。

 由於OpenCV中原始碼檔案巨多,根據演算法的功能,將這些原始檔分到多個模組中:core、imgproc、highgui等。將每個模組中的原始檔編譯成一個庫檔案(如opencv_core.lib、opencv_imgproc.lib、opencv_highgui.lib等),使用者在使用時,僅將所需的庫檔案新增到自己的專案中,與自己的原始檔一起連線成可執行程式則可。

OpenCV主體分為五個模組:

OpenCV的CV模組包含基本的影象處理函式和高階的計算機視覺演算法

ML是機器學習庫,包含一些基於統計的分析和聚類工具。

HighGUI包含影象和視訊輸入/輸出的函式。

CXCore包含OpenCV的一些基本資料結構和相關函式。

CvAux模組,該模組一般存放一些即將被淘汰的演算法和函式(如嵌入式隱馬爾可夫模型的人臉識別演算法),同時還有一些新出現的實驗性的演算法(如背景和前景的分割)


C/C++語言中的main函式,經常帶有引數argc,argv,如下: 
int main(int argc, char** argv)
或者
int main(int argc, char* argv[])

在上面程式碼中,argc表示命令列輸入引數的個數(以空白符分隔),argv中儲存了所有的命令列引數。

下面的程式演示argc和argv的使用: 
#include <stdio.h>
int main(int argc, char ** argv) {
    int i;
    for (i=0; i < argc; i++)
        printf("Argument %d is %s.\n", i, argv[i]);     return 0; }

openCV常見編譯錯誤:

找不到標頭檔案往往會提示如下錯誤:
hello.cpp(2): fatal error C1083: Cannot open include file: 'opencv2/opencv.hppp

': No such file or directory
找不到標頭檔案一般有兩個原因:一個是標頭檔案的檔名拼寫錯誤;或者未將標頭檔案所在路徑新增到開發環境中

這個例子顯然是拼寫錯誤,但是更多的情況下都是第二種錯誤。

我們再來詳細的說說關於OpenCV

OpenCV採用BSD協議,這是一個非常寬鬆的協議。簡而言之,使用者可以修改OpenCV的原始碼,可以將OpenCV嵌入到自己的軟體中,可以將包含OpenCV的軟體銷售,可以用於商業產品,也可以用於科研領域。BSD協議並不具有“傳染性”,如果你的軟體中使用了OpenCV,你不需要公開程式碼。你可以對OpenCV做任何操作,協議對使用者的唯一約束是要在軟體的文件或者說明中註明使用了OpenCV,並附上OpenCV的協議

openCV小例子(讀取影象)

#include <stdio.h>
#include <opencv2/opencv.hpp>
using namespace cv;
int main(int argc, char** argv )
{
    if ( argc != 2 )
    {
        printf("usage: DisplayImage.out <Image_Path>\n");
        return -1;
    }
    Mat image;
    image = imread( argv[1], 1 );
    if ( !image.data )
    {
        printf("No image data \n");
        return -1;
    }
    namedWindow("Display Image", WINDOW_AUTOSIZE );
    imshow("Display Image", image);
    waitKey(0);
    return 0;
}

OpenCV中讀影象檔案:

將影象檔案讀入記憶體,可以使用 imread()函式;將 Mat 物件以影象檔案格式

寫入記憶體,可以使用 imwrite()函式

imread()函式返回的是 Mat 物件,如果讀取檔案失敗,則會返回一個空矩陣,

即 Mat::data 的值是 NULL。執行 imread()之後,需要檢查檔案是否成功讀入,你

可以使用 Mat::empty()函式進行檢查。imread()函式的宣告如下:

Mat imread(const string&filename, int flags=1 )

很明顯引數 filename 是被讀取或者儲存的影象檔名;在 imread()函式中,

flag 引數值有三種情況:

  flag>0,該函式返回 3通道影象,如果磁碟上的影象檔案是單通道的灰

度影象,則會被強制轉為 3通道;

 flag=0,該函式返回單通道影象,如果磁碟的影象檔案是多通道影象,則

會被強制轉為單通道;

  flag<0,則函式不對影象進行通道轉換。

imread()函式支援多種檔案格式,且該函式是根據影象檔案的內容來確定文

件格式,而不是根據檔案的副檔名來確定。所只是的檔案格式如下:

  Windows 點陣圖檔案 - BMP,DIB;

  JPEG 檔案 - JPEG,JPG, JPE;

  行動式網路圖片 - PNG;

  行動式影象格式 - PBM,PGM,PPM;

  Sun rasters - SR,RAS;

  TIFF 檔案 - TIFF,TIF;

  OpenEXR HDR 圖片 - EXR;

  JPEG 2000 圖片- jp2。

你所安裝的 OpenCV 並不一定能支援上述所有格式,檔案格式的支援需要特

定的庫,只有在編譯 OpenCV添加了相應的檔案格式庫,才可支援其格式。

OpenCV中寫影象檔案:

將影象寫入檔案,可使用 imwrite()函式,該函式的宣告如下:

bool imwrite(const string&filename, InputArray image,

const vector<int>&params=vector<int>())

檔案的格式由 filename引數指定的副檔名確定。推薦使用 PNG檔案格

。BMP 格式是無損格式,但是一般不進行壓縮,檔案尺寸非常大;JPEG 格式

的檔案嬌小,但是 JPEG 是有失真壓縮,會丟失一些資訊。PNG是無失真壓縮格式,

推薦使用。

imwrite()函式的第三個引數 params可以指定檔案格式的一些細節資訊。這

個引數裡面的數值是跟檔案格式相關的:

  JPEG:表示影象的質量,取值範圍從 0 到 100。數值越大表示影象質量

越高,當然檔案也越大。預設值是 95。

  PNG:表示壓縮級別,取值範圍是從 0 到 9。數值越大表示檔案越小,

但是壓縮花費的時間也越長。預設值是 3。

  PPM,PGM 或 PBM:表示檔案是以二進位制還是純文字方式儲存,取值為

0 或 1。如果取值為 1,則表示以二進位制方式儲存。預設值是 1。

另外需要注意的是,在儲存檔案時,如果檔案已經存在,imwrite()函式不會

進行提醒,將直接覆蓋掉以前的檔案。

並不是所有的 Mat 物件都可以存為影象檔案,目前支援的格式只有 8U 型別

的單通道和 3 通道(顏色順序為 BGR)矩陣;如果需要要儲存 16U 格式影象,只

能使用 PNG、JPEG 2000 和 TIFF 格式。如果希望將其他格式的矩陣儲存為影象文

件,可以先用 Mat::convertTo()函式或者 cvtColor()函式將矩陣轉為可以儲存的格

式。

下面例程展示瞭如何讀入一副影象,然後對影象進行 Canny 邊緣操作,最後

將結果儲存到影象檔案中。

#include <iostream>

#include"opencv2/opencv.hpp"

using namespace std;

using namespace cv;

int main(int argc, char*argv[])

{

//讀入影象,並將之轉為單通道影象

Mat im =imread("lena.jpg", 0);

//請一定檢查是否成功讀圖

if( im.empty() )

{

cout << "Can notload image." << endl;

return -1;

}

//進行 Canny 操作,並將結果存於 result

Mat result;

Canny(im, result, 50, 150);

//儲存結果

imwrite("lena-canny.png",result);

return 0;

}

將 lena.jpg 檔案放在當前目錄,執行該例程後,lena-canny.png 將會出現在當

前目錄

下面我們介紹OpenCV讀寫視訊:

介紹 OpenCV 讀寫視訊之前,先介紹一下編解碼器(codec)。

視訊的格式主要由壓縮演算法決定。壓縮演算法稱之為編碼器(coder),解壓算

法稱之為解碼器(decoder),編解碼演算法可以統稱為編解碼器(codec)。視訊文

件能讀或者寫,關鍵看是否有相應的編解碼器。

OpenCV 2 中提供了兩個類來實現視訊的讀寫。讀視訊的類是 VideoCapture,

寫視訊的類是 VideoWriter。

OpenCV讀視訊:

VideoCapture 既可以從視訊檔案讀取影象,也可以從攝像頭讀取影象。可以

使用該類的建構函式開啟視訊檔案或者攝像頭。如果 VideoCapture 物件已經創

建,也可以使用 VideoCapture::open()開啟,VideoCapture::open()函式會自動呼叫

VideoCapture::release()函式,先釋放已經開啟的視訊,然後再開啟新視訊。

如果要讀一幀,可以使用 VideoCapture::read()函式。VideoCapture類過載了>>

操作符,實現了讀視訊幀的功能。下面的例程演示了使用 VideoCapture 類讀視

頻。

#include <iostream>

#include"opencv2/opencv.hpp"

using namespace std;

using namespace cv;

int main(int argc, char**argv)

{

//開啟第一個攝像頭

//VideoCapture cap(0);

//開啟視訊檔案

VideoCapturecap("video.short.raw.avi");

//檢查是否成功開啟

if(!cap.isOpened())

{

cerr << "Can notopen a camera or file." << endl;

return -1;

}

Mat edges;

//建立視窗

namedWindow("edges",1);

for(;;)

{

Mat frame;

//從 cap 中讀一幀,存到 frame

cap >> frame;

//如果未讀到影象

if(frame.empty())

break;

//將讀到的影象轉為灰度圖

cvtColor(frame, edges,CV_BGR2GRAY);

//進行邊緣提取操作

Canny(edges, edges, 0, 30, 3);

//顯示結果

imshow("edges",edges);

//等待 30 秒,如果按鍵則推出迴圈

if(waitKey(30) >= 0)

break;

}

//退出時會自動釋放 cap 中佔用資源

return 0;

}

OpenCV寫視訊:

使用 OpenCV 建立視訊也非常簡單,與讀視訊不同的是,你需要在建立視訊

時設定一系列引數,包括:檔名,編解碼器,幀率,寬度和高度等。編解碼器

使用四個字元表示,可以是CV_FOURCC('M','J','P','G')、CV_FOURCC('X','V','I','D')及

CV_FOURCC('D','I','V','X')等。如果使用某種編解碼器無法建立視訊檔案,請嘗試其

他的編解碼器。

將影象寫入視訊可以使用 VideoWriter::write()函式,VideoWriter類中也過載

<<操作符,使用起來非常方便。另外需要注意:待寫入的影象尺寸必須與建立

視訊時指定的尺寸一致。

下面例程演示瞭如何寫視訊檔案。本例程將生成一個視訊檔案,視訊的第 0

幀上是一個紅色的“0”,第 1 幀上是個紅色的“1”,以此類推,共 100 幀。

#include <stdio.h>

#include <iostream>

#include"opencv2/opencv.hpp"

using namespace std;

using namespace cv;

int main(int argc, char**argv)

{

//定義視訊的寬度和高度

Size s(320, 240);

//建立 writer,並指定 FOURCC 及 FPS 等引數

VideoWriter  writer = VideoWriter("myvideo.avi",

CV_FOURCC('M','J','P','G'),25, s);

//檢查是否成功建立

if(!writer.isOpened())

{

cerr << "Can notcreate video file.\n" << endl;

return -1;

}

//視訊幀

Mat frame(s, CV_8UC3);

for(int i = 0; i < 100; i++)

{

//將影象置為黑色

frame = Scalar::all(0);

//將整數 i 轉為 i 字串型別

char text[128];

snprintf(text, sizeof(text),"%d", i);

//將數字繪到畫面上

putText(frame,  text, Point(s.width/3,  s.height/3),

FONT_HERSHEY_SCRIPT_SIMPLEX,3,

Scalar(0,0,255), 3, 8);

//將影象寫入視訊

writer << frame;

}

//退出程式時會自動關閉視訊檔案

return 0;

}

Opencv支援的功能描述
影象資料操作(記憶體分配與釋放,影象複製、設定和轉換) 
影象/視訊的輸入輸出(支援檔案或攝像頭的輸入,影象/視訊檔案的輸出) 
矩陣/向量資料操作及線性代數運算(矩陣乘積、矩陣方程求解、特徵值、奇異值分解)  支援多種動態資料結構(連結串列、佇列、資料集、樹、圖) 
基本影象處理(去噪、邊緣檢測、角點檢測、取樣與插值、色彩變換、形態學處理、直方圖、影象金字塔結構)

結構分析(連通域/分支、輪廓處理、距離轉換、影象矩、模板匹配、霍夫變換、多項式逼近、曲線擬合、橢圓擬合、狄勞尼三角化) 
攝像頭定標(尋找和跟蹤定標模式、引數定標、基本矩陣估計、單應矩陣估計、立體視覺匹配) 
運動分析(光流、動作分割、目標跟蹤) 

目標識別(特徵方法、HMM模型) 
基本的GUI(顯示影象/視訊、鍵盤/滑鼠操作、滑動條)  影象標註(直線、曲線、多邊形、文字標註)

OK,哈哈。最後我們來看看那個

萊娜·瑟德貝里


萊娜·瑟德貝里(瑞典文:Lena Soderberg),1951年3月31日出生於瑞典,在1972年11月期的《花花公子》雜誌中,她化名為萊娜·舍布洛姆,成為了當期的玩伴女郎。她的中間摺頁照片由Dwight Hooker拍攝。她的照片(即萊娜圖)後來被數字影象處理領域所廣泛使用。1997年,在影象科學和技術協會(英語:Society for Imaging Science and Technology)的第50屆會議上,她被邀為貴賓出席。在會議上,她忙於簽名、拍照以及介紹自我。

關於Lena Soderberg (ne Sjooblom)的報道說她居住在她的本國瑞典,有著幸福的婚姻並是三個孩子的媽媽,在liquor monopoly州有一份工作。1988年,她被某個瑞典計算機相關雜誌採訪,因為她的照片而發生的一切令她很高興。這是她第一次得知她的照片在計算機領域被使用。

為何要使用Lenna影象? David C. Munson. 在“A Note on Lena” 中給出了兩條理由:
首先,Lenna影象包含了各種細節、平滑區域、陰影和紋理,這些對測試各種影象處理演算法很有用。它是一副很好的測試影象!
第二,Lena影象裡是一個很迷人的女子。所以不必奇怪影象處理領域裡的人(大部分為男性)被一副迷人的影象吸引。

我知道你一定特別想要看原圖,那麼請看!!!

參考資料:《OpenCV入門教程》 作者:於仕琪,博士,助理研究員,深圳大學副教授,OpenCV中文站站長。