OpenCV可移植圖形工具HighGUI實現圖像和視頻操作

OpenCV把用於操作系統、文件系統以及攝像機等硬件設備交換的函數納入了HighGUI(High-level Graphical User Interface)模塊中。有了HighGUI模塊,我們可以方便地打開窗口、顯示圖像、讀出或寫入圖像相關的文件、鼠標事件和鍵盤事件。下面將對三部分分別進行介紹。

HighGUI三部分的作用
部分 作用
硬件部分 最主要是對攝像機的操作
文件系統部分 主要工作是完成圖片的載入和保存
窗口系統GUI 創建窗口並將圖片放入窗口顯示,同時添加響應鼠標和鍵盤事件功能

圖像文件的處理

圖像的載入與保存

使用cv::imread()讀取圖片

cv::Mat cv::imread(const string& filename, int flags=cv::IMREAD_COLOR)

作用:載入圖像,若失敗不會拋出異常,返回的是空的cv::Mat(可以用cv::Mat::empty()==true來判斷)。

flags的取值
標誌 含義 默認值
cv::IMREAD_COLOR 讀取三通道圖像,即使輸入是灰度圖像,也會有三通道,只是每個通道擁有相同的數據
cv::IMREAD_GRAYSCALE 讀取單通道圖像
cv::IMREAD_ANYCOLOR 通道數由文件實際通道數(不超過3)
cv::IMREAD_ANYDEPTH 允許加載超過8bit深度
cv::IMREAD_UNCHANGED 相當於cv::IMREAD_ANYCOLOR和cv::IMREAD_ANYDEPTH組合使用,可保留alpha通道。

使用cv::imwrite()保存圖像

bool cv::imwrite(const string& filename, InputArray image, const vector<int>& params=vector<int>())

作用:保存圖像,成功返回true,否則返回false。

常用擴展名
擴展名 格式 大小 通道
.jpg或.jpeg baseline JPEG 8位 單通道或三通道
.jp2 JPEG2000 8位或16位 單通道或三通道
.tif或.tiff TIFF 8位或16位 單通道、三通道或四通道
.png PNG 8位或16位 單通道、三通道或四通道
.bmp BMP 8位 單通道、三通道或四通道
.ppm或.pgm NetPBM 8位 單通道(PGM)和三通道(PPM)
params的使用
標誌 含義 取值範圍 默認值
cv::IMWRITE_JPG_QUALITY JPEG的質量 0~100 95
cv::IMWRITE_PNG_COMPRESSION PNG壓縮值 0~9 3
cv::IMWRITE_PXM_BINARY 對PPM、PGM或PBM文件是否使用二值格式 0或1 1

關於codecs的一些註釋

用於圖像處理的庫通常被叫做codecs(co-mpression and dec-compress libraries-s)。OpenCV對一些圖片格式自帶編碼解碼庫,對於這些有一下三中選擇:

① 不使用這些編碼解碼庫;

② 使用OpenCV所提供的編碼解碼庫;

③ 使用相應的拓展庫(如libjpeg、libpng等)。

在Windows系統中默認選②選項,而在OS X或Linux系統上,默認選③選項。

圖片的編碼與解碼

使用cv::imencode()壓縮圖像

void cv::imencode(const string& ext, InputArray img, vector<uchar>& buf, const vector<int>& params=vector<int>())

作用:對圖像進行編碼。

參數列表
參數 含義
ext 文件擴展名,用於函數與對應的編碼壓縮方式相關聯。指明格式並幫助系統索引對應編碼庫
img 要被壓縮的圖像數據
buf 用來存儲編碼壓縮後的圖像數據的字符數組
params 指明要使用編碼器的輸入參數

使用cv::imdecode()解碼文件

cv::Mat cv::imdecode(InputArray buf, int flags=cv::IMREAD_COLOR)

作用:將字節流數據解碼成數組形式的圖像。

視頻處理

使用cv::VideoCapture對象讀取視頻流

cv::VideoCapture::VideoCapture(const string& filename)

cv::VideoCapture::VideoCapture(int device)

cv::VideoCapture::VideoCapture()

作用:打開視頻文件或攝像機。打開成功則cv::VideoCapture::isOpen()會返回true,否則返回false。

構造設備參數時,傳入的標識符是相機的域索引和相機索引之和。相機索引是指系統中攝像機設備的索引,從0開始計數。相機域表明我們用何種相機,如下表所示。

相機域常量
相機捕捉常數 數值 相機捕捉常數 數值 相機捕捉常數 數值
cv::CAP_ANY 0 cv::CAP_DSHOW 700 cv::CAP_OPENNI2 1600
cv::CAP_MIL 100 cv::CAP_PVAPI 800 cv::CAP_OPENNI2_ASUS 1610
cv::CAP_VFW 200 cv::CAP_OPENNI 900 cv::CAP_GPHOTO2 1700
cv::CAP_V4L 200 cv::CAP_OPENNI_ASUS 910 cv::CAP_GSTREAMER 1800
cv::CAP_V4L2 200 cv::CAP_ANDROID 1000 cv::CAP_FFMPEG 1900
cv::CAP_FIREWIRE 300 cv::CAP_XIAPI 1100 cv::CAP_IMAGES 2000
cv::CAP_FIREWARE 300 cv::CAP_AVFOUNDATION 1200 cv::CAP_ARAVIS 2100
cv::CAP_IEEE11394 300 cv::CAP_GIGANETIX 1300 cv::CAP_OPENCV_MJPEG 2200
cv::CAP_DC1394 300 cv::CAP_MSMF 1400 cv::CAP_INTEL_MFX 2300
cv::CAP_QT 500 cv::CAP_WINRT 1410 cv::CAP_XINE 2400
cv::CAP_UNICAP 600 cv::CAP_INTELPERC 1500    

當傳入的標識符爲-1時,則OpenCV會彈出一個選擇窗口,可手動選擇希望使用的相機。

最後一種形式只是直接創建一個VideoCapture對象,無法直接使用,需要調用cv::VideoCapture::open()函數來指定文件名或設備號,和前兩種形式一樣。

使用cv::VideoCapture::read()從視頻流中讀取圖像

bool cv::VideoCapture::read(OutputArray img)

作用:從文件中讀取下一幀數據,並將結果存入img中,並會自動更新。讀取成功返回true,讀取失敗返回false。

使用cv::VideoCapture::operator>>()從視頻流讀取圖像數據

cv::VideoCapture& cv::VideoCapture::operator>>(Mat& img)

作用:重載輸入流操作符,從文件中讀取下一幀數據,並將結果存入img中。

使用cv::VideoCapture::grab()和cv::VideoCapture::retrieve()從視頻流中讀取圖像數據

bool cv::VideoCapture::grab()

作用:將當前可使用的圖像數據拷貝至用戶看不到的一個內存緩存區。抓取的圖像幀是未處理的,該函數只是設計用於將原始數據儘快獲取到電腦。

bool cv::VideoCapture::retrieve(OutputArray image, int channel)

作用:對內部緩存數據進行必要的圖像解碼、內存分配與拷貝,最後以Mat形式的序列將圖像幀返回。

相機屬性cv::VideoCapture::get()和cv::VideoCapture::set()

double cv::VideoCapture::get(int propid)

bool cv::VideoCapture::set(int propid, double value)

作用:獲取或設置元數據(meta data)。

視頻捕捉屬性常量
ID 是否只在攝像頭模式下可以使用 含義
cv::CAP_PROP_POS_MSEC   視頻文件中當前位置(毫秒)或視頻捕獲時間戳
cv::CAP_PROP_POS_FRAMES   從0開始下一幀的索引
cv::CAP_PROP_POS_AVI_RATIO   視頻的相對位置
cv::CAP_PROP_FRAME_WIDTH   視頻幀的像素寬度
cv::CAP_PROP_FRAME_HEIGHT   視頻幀的像素高度
cv::CAP_PROP_FPS   錄製視頻的幀速率
cv::CAP_PROP_FOURCC   四個字符代碼指示編解碼
cv::CAP_PROP_FRAME_COUNT   視頻文件的總幀數
cv::CAP_PROP_FORMAT   返回Mat對象的格式(如CV_8UC3)
cv::CAP_PROP_MODE   表示捕捉模式,值是特定於正在使用的視頻後端(如DC1394)
cv::CAP_PROP_BRIGHTNESS 相機的亮度設置
cv::CAP_PROP_CONCAT 相機的對比度設置
cv::CAP_PROP_SATURATION 相機的飽和度設置
cv::CAP_PROP_HUE 相機的色調設置
cv::CAP_PROP_GAIN 相機的增益設置
cv::CAP_PROP_EXPOSURE 相機的曝光設置
cv::CAP_PROP_CONVERT_RGB 如果非0,獲取的圖像將被轉換爲具有三個通道
cv::CAP_PROP_WHITE_BALANCE 相機的白平衡設置
cv::CAP_PROP_RECTIFICATION 立體相機整流標誌(僅適用於DC1934-2.x)

使用cv::VideoWriter對象寫入視頻

cv::VideoWriter::VideoWriter(const string& filename, int fourcc, double fps, Size frame_size, bool is_color=true)

cv::VideoWriter::VideoWriter()

作用:創建一個VideoWriter對象,is_color爲true則傳入彩色圖,反之可以傳入灰度圖。同樣第二種形式不能直接使用,需要調用cv::VideoWriter::open()函數進行配置。

判斷是否初始化成功,可通過cv::VideoWriter::isOpen()的返回值進行判斷。

使用cv::VideoWriter::write()函數完成圖像幀的寫入

cv::VideoWriter::write(const Mat& img)

作用:完成圖像幀的寫入,圖像必須和cv::VideoWriter具有相同的圖像尺寸,如果是彩色必是三通道,反之使用單通道。

使用cv::VideoWriter::operator<<()將圖像寫入視頻

cv::VideoWriter& cv::VideoWriter::operator<<(Mat& img)

作用:重載輸出流操作符,將img存入視頻文件。

數據存儲

作爲對標準視頻壓縮方式的補充,OpenCV提供了一種序列化與反序列化的機制,用於將不同數據類型的數據已YAML或XML格式寫入磁盤或者從磁盤讀取。這些方法可以用來加載或者保存任何OpenCV的數值變量到一個文件中。

cv::FileStorage的寫入

cv::FileStorage::FileStorage(string filename, int flag)

cv::FileStorage::FileStorage()

作用:創建FileStorage對象(代表XML或YAML格式的數據文件),flag指定功能,寫入時可爲cv::FileStorage::WRITE和cv::FileStorage::APPEND。同樣第二種形式不能直接使用,需要調用cv::FileStorage::open()函數進行配置。

寫入時可以使用重載輸出操作符函數cv::FileStorage::operator<<()進行寫入。寫完時調用cv::FileStorage::release()關閉該文件。

cv::FileStorage內部數據的存儲形式主要有mapping和sequence,在最頂層是一個mapping,其中可以嵌套mapping和sequence。新建mapping或sequence分別使用{}和[]。

#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <time.h>


// 使用cv::FileStorage創建一個test.yml參數文件
int main()
{
	// 創建FileStorage對象,實現其寫入功能
	cv::FileStorage fs("test.yml", cv::FileStorage::WRITE);

	// 寫入內容
	fs << "frameCount" << 5;
	
	// 獲取當前時間並寫入
	time_t rawtime;
	time(&rawtime);
	fs << "calibrationDate" << asctime(localtime(&rawtime));

	// 生成矩陣,並寫入
	cv::Mat cameraMatrix = (cv::Mat_<double>(3, 3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1);
	cv::Mat distCoeffs = (cv::Mat_<double>(5, 1) << 0.1, 0.01, -0.001, 0, 0);
	fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs;

	// 隨機生成數及數組並寫入
	fs << "features" << "[";
	for (int i = 0; i < 3; i++) {
		int x = rand() % 640;
		int y = rand() % 480;
		uchar lbp = rand() % 256;
		fs << "{:" << "x" << x << "y" << y << "lbp" << "[:";
		for (int j = 0; j < 8; j++) {
			fs << ((lbp >> j) & 1);
		}
		fs << "]" << "}";
	}
	fs << "]";

	// 關閉文件
	fs.release();
    return 0;
}

 用記事本打開test.yml文件,內容如下:

cv::FileStorage讀取文件

cv::FileStorage::FileStorage(string filename, int flag)

cv::FileStorage::FileStorage()

作用:創建FileStorage對象(代表XML或YAML格式的數據文件),flag指定功能,讀取時可爲cv::FileStorage::READ。同樣第二種形式不能直接使用,需要調用cv::FileStorage::open()函數進行配置。

讀取時可以使用重載輸出操作符函數cv::FileStorage::operator>>()進行讀取,得到的不是直接的數據,而是FileNode對象。若該值是基本數據類型,則可以通過強制類型轉換直接獲取;若該值是mapping,則通過重載操作符[]並給定所需關鍵字訪問;若該值是sequence,則通過重載操作符[]並給定整型參數訪問,也可使用cv::FileStorageIterator迭代器進行訪問。

讀完時調用cv::FileStorage::release()關閉該文件。

cv::FileNode

FileNode的基本使用在上述內容中已做過介紹,下面將主要針對cv::FileStorageIterator進行介紹。

給定一個FileNode對象,通過cv::FileNode::begin()和cv::FileNode::end()可以分別返回該mapping或者sequence的首元素的迭代器和末尾首元素的迭代器。該迭代器對象可以通過cv::FileStorageIterator::operator*()來進行解引用操作並返回迭代器對應的位置的FileNode對象,並通過自增和自減來控制迭代器。

若其對一個mapping進行遍歷,返回的FileNode對象將會有名字屬性,可通過cv::FileNode::name()得到。同樣,通過cv::FileNode::type()可獲得cv::FileNode中定義的枚舉類型。

cv::FileNode的成員函數
示例 說明
cv::FileNode fn 文件節點對象的默認構造函數
cv::FileNode fn1(fn0) 文件節點對象的複製構造函數,從節點fn0創建節點fn1
cv::FileNode fn1(fs,node) 文件節點對象構造函數,從C風格的CvFileStorage*指針和C風格的CvFileNode*指針節點創建FileNode對象

fn[ (string) key ]

fn[ (const char*) key ]

命名子串(映射節點)的STL字符串或C字符串存取器,將祕鑰轉換爲適當的子節點。
fn[ (int) id ] 子編號(序列節點)的訪問;將ID轉換爲相應的子節點
fn.type() 返回節點類型enum
fn.empty() 確定節點是否爲空
fn.isNone() 確定節點是否爲None
fn.isSeq() 確定節點是否爲序列
fn.isMap() 確定節點是否爲映射

fn.isInt()

fn.isReal()

fn.isString()

確定節點是否爲整數

確定節點是否爲浮點數

確定節點是否爲字符串

fn.name() 如果節點是映射的子節點,返回節點名稱
size_t sz= fn.size() 如果是序列或映射,返回節點中的元素數量,否則返回1。

(int) fn

(float) fn

(double) fn

(string) fn

從包含整數的節點中提取

從包含32位浮點數的節點中提取

從包含64位浮點數的節點中提取

從包含字符串的節點中提取

cv::FileNode::type()可能的返回值
示例 說明
cv::FileNode::NONE = 0 節點類型爲None
cv::FileNode::INT = 1 節點包含一個整數

cv::FileNode::REAL = 2

cv::FileNode::FLOAT = 2

節點包含一個浮點數(不分精度)

cv::FileNode::STR = 3

cv::FileNode::STRING = 3

節點包含一個字符串
cv::FileNode::REF = 4 節點包含參考(即複合對象)
cv::FileNode::SEQ = 5 節點本身是其他節點的序列
cv::FileNode::MAP = 6 節點本身是其他節點的映射
cv::FileNode::FLOW = 8 節點是序列或映射的緊湊表示
cv::FileNode::USER = 16 節點是註冊對象(如矩陣)
cv::FileNode::EMPTY = 32 節點沒有複製給它
cv::FileNode::NAMED = 64 節點是映射的子節點(即它有一個名字)

注:一個FileNode除了擁有前8種屬性之外還可以同時擁有最後四個中的一個或全部屬性。

#include "stdafx.h"
#include <opencv2/opencv.hpp>


// 使用FileStorage來讀取test.yml文件
int main()
{
	// 創建FileStorage對象,實現讀取功能
	cv::FileStorage fs("D:\\personal-data\\opencv\\example8-2\\example8-2\\test.yml", cv::FileStorage::READ);

	// 對FileNode進行強制類型轉換,得到數據
	int frameCount = (int)fs["frameCount"];

	// 使用重載>>操作符
	std::string date;
	fs["calibrationDate"] >> date;
	
	cv::Mat cameraMatrix, distCoeffs;
	fs["cameraMatrix"] >> cameraMatrix;
	fs["distCoeffs"] >> distCoeffs;

	std::cout << "frameCount" << frameCount << std::endl;
	std::cout << "calibrationDate" << date << std::endl;
	std::cout << "cameraMatrix" << cameraMatrix << std::endl;
	std::cout << "distCoeffs" << distCoeffs << std::endl;

	cv::FileNode features = fs["features"];
	cv::FileNodeIterator it = features.begin(), it_end = features.end();
	int idx = 0;
	std::vector<uchar> lbpval;

	// 使用迭代器遍歷序列
	for (; it != it_end; ++it, idx++)
	{
		std::cout << "features #" << idx << ": ";
		std::cout << "x=" << (int)(*it)["x"] << ", y=" << (int)(*it)["y"] << ", lbp: (";
		(*it)["lbp"] >> lbpval;
		for (int i = 0; i < (int)lbpval.size(); i++)
		{
			std::cout << " " << (int)lbpval[i];
		}
		std::cout << ")" << std::endl;
	}

	//關閉文件
	fs.release();
    return 0;
}

運行結果:

frameCount5
calibrationDateMon Aug 20 20:20:03 2018

cameraMatrix[1000, 0, 320;
 0, 1000, 240;
 0, 0, 1]
distCoeffs[0.1;
 0.01;
 -0.001;
 0;
 0]
features #0: x=41, y=227, lbp: ( 0 1 1 1 1 1 0 1)
features #1: x=260, y=449, lbp: ( 0 0 1 1 0 1 1 0)
features #2: x=598, y=78, lbp: ( 0 1 0 0 1 0 1 0)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章