OpenCV把用於操作系統、文件系統以及攝像機等硬件設備交換的函數納入了HighGUI(High-level Graphical User Interface)模塊中。有了HighGUI模塊,我們可以方便地打開窗口、顯示圖像、讀出或寫入圖像相關的文件、鼠標事件和鍵盤事件。下面將對三部分分別進行介紹。
部分 | 作用 |
---|---|
硬件部分 | 最主要是對攝像機的操作 |
文件系統部分 | 主要工作是完成圖片的載入和保存 |
窗口系統GUI | 創建窗口並將圖片放入窗口顯示,同時添加響應鼠標和鍵盤事件功能 |
圖像文件的處理
圖像的載入與保存
使用cv::imread()讀取圖片
cv::Mat cv::imread(const string& filename, int flags=cv::IMREAD_COLOR)
作用:載入圖像,若失敗不會拋出異常,返回的是空的cv::Mat(可以用cv::Mat::empty()==true來判斷)。
標誌 | 含義 | 默認值 |
---|---|---|
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) |
標誌 | 含義 | 取值範圍 | 默認值 |
---|---|---|---|
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 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::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)