在剛入門opencv的階段,讀取圖片以及視頻是作爲初學者最需要掌握的方法。最近在上智能視頻分析這門課程,通過幾次實驗課,發現針對不同的數據分別對應着不同的載入方式,特此開貼,記錄下來,一是可以與人分享,共同進步,二是權當學習筆記記錄下來,以供以後回顧。若有謬誤,還望各位大佬指正,定不勝感激!!!
一.普通視頻的載入方式
這個是入門級的教程,就不多說了。主要是先定義 VedioCapture 這個類,在將視頻的每一幀都賦值給一張圖片。對視頻的處理,就是對每一幀圖片循環處理,接處理後的圖片顯示出來。圖像處理是單幅圖,視頻處理就是多幅圖的處理。
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img;
VideoCapture cap(0); // 打開電腦攝像頭 創建 cap
//VideoCapture cap("1.avi"); // 打開 “1.avi”文件 創建cap
if (!cap.isOpened())
{
cout << "cannot open camera" << endl;
return 0;
}
while (true)
{
cap >> img; //將視視頻的每一幀圖片賦值給 img
///////////////////////////////////////////////////////////
////// 可以在此處編寫對 img 進行各種圖像處理的代碼
//////
///////////////////////////////////////////////////////////
imshow("image", img);
char key = waitKey(30);
if (key == 27)// 按鍵“ESC”退出
break;
}
return 0;
}
效果圖如下:
二. 讀取以YUV形式保存的視頻文件,並將其轉爲RGB圖片,然後顯示
首先先簡單介紹下YUV數據數據格式。YUV,分爲三個分量,“Y”表示明亮度(Luminance或Luma),也就是灰度值;而“U”和“V” 表示的則是色度(Chrominance或Chroma),作用是描述影像色彩及飽和度,用於指定像素的顏色。
YUV碼流的存儲格式其實與其採樣的方式密切相關,主流的採樣方式有三種,YUV4:4:4,YUV4:2:2,YUV4:2:0,關於其詳細原理,可以通過網上其它文章瞭解,這裏我想強調的是如何根據其採樣格式來從碼流中還原每個像素點的YUV值,因爲只有正確地還原了每個像素點的YUV值,才能通過YUV與RGB的轉換公式提取出每個像素點的RGB值,然後顯示出來。
用三個圖來直觀地表示採集的方式吧,以黑點表示採樣該像素點的Y分量,以空心圓圈表示採用該像素點的UV分量。
先記住下面這段話,以後提取每個像素的YUV分量會用到
1.YUV 4:4:4採樣,每一個Y對應一組UV分量。
2.YUV 4:2:2採樣,每兩個Y共用一組UV分量。
3.YUV 4:2:0採樣,每四個Y共用一組UV分量。
個人覺得YUV格式主要的優點就是可以壓縮圖片的數據量,節省內存。可能對單幅圖來說節省的內存不是很多,但是對於一段視頻來說,省下的內存就是很大了。
下面就以一副RGB24圖片爲例,如果將其以RGB形式保存的話,需要的size=width*height*3bit . 若是以YUV420保存的話,size=width*height*1.5bit 。
對於一副8*4的圖片來說,size=width*height+1/2 *width*height=3/2*width*height=3/2*8*4
不過缺點就是在將YUV420數據轉換爲RGB時,會出現圖像像素點信息的丟失,不能完全復現出原圖。(YUV444可以完全復現,但是數據不會被壓縮)
具體代碼如下:
#include <opencv/cv.h>
#include <fstream>
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/background_segm.hpp>
using namespace std;
using namespace cv;
const int width = 352;
const int height = 288;
const int framesize = width * height * 3 / 2; //一副圖所含的像素個數
typedef struct planet
{
char name[framesize];
double population;
double g;
} PLANET;
int main()
{
/////////////////////////////////////////////////////////////
// 計算視頻的幀數,怎樣替換成c語言形式的?
PLANET pl;
ifstream fin;
fin.open("hall_cif.yuv", ios_base::in | ios_base::binary);
if (fin.fail())
{
cout << "the file is error" << endl;
return -1;
}
fin.seekg(0, ios::end); //設置文件指針到文件流的尾部
streampos ps = fin.tellg(); //指出當前的文件指針
unsigned long NumberPixe = ps;
cout << "file size: " << ps << endl; //輸出指針的位置
unsigned FrameCount = ps / framesize; //幀大小
cout << "frameNuber: " << FrameCount; //輸出幀數
fin.close();
/////////////////////////////////////////////////////////////////
Ptr<BackgroundSubtractorMOG2> pBgmodel = createBackgroundSubtractorMOG2();
pBgmodel->setVarThreshold(20);
FILE* fileIn = fopen("hall_cif.yuv", "rb+");
unsigned char* pYuvBuf = new unsigned char[framesize]; //一幀數據大小
//存儲到圖像
namedWindow("yuv", 1);
for (int i = 0; i < FrameCount; ++i)
{
//從數據流中讀取 按長度讀取數據 下一次循環直接從上一幀的末尾開始讀取
fread(pYuvBuf, framesize*sizeof(unsigned char), 1, fileIn); //在整個yuv中截取一幀圖像讀入
Mat yuvImg;
yuvImg.create(height * 3 / 2, width, CV_8UC1); //創建新的圖片的大小
memcpy(yuvImg.data, pYuvBuf, framesize*sizeof(unsigned char));
Mat rgbImg;
cvtColor(yuvImg, rgbImg, CV_YUV2BGR_I420);
char key = waitKey(100);
if (key == 27)// 按鍵“ESC”退出
break;
imshow("yuv", yuvImg); //只顯示y分量
imshow("rgbImg", rgbImg);
printf("第 %d 幀\n", i);
}
fclose(fileIn);
cvDestroyWindow("yuv");
cin.get();
cin.get();
return 0;
}
實驗效果圖:
三.讀取圖片序列
圖片序列的格式:
圖片的 文件名爲 000000xx 所以寬度爲8
VideoCapture 的功能真的很強大啊,可以讀視頻,可以讀圖片序列
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
string first_file = "girl/%8d.jpg"; //%8d代表文件名的寬度 將寬度爲8的文件名都讀出來
VideoCapture sequence(first_file); // V
Mat image;
namedWindow("Image sequence");
while (1){
sequence >> image;
if (image.empty()) break;
imshow("Image sequence", image);
char key=waitKey(5);//每隔5毫秒再讀下一幅圖像
if (key == 27)// 按鍵“ESC”退出
break;
}
cout << "End of Sequence" << endl;
waitKey();
return 0;
}
實驗結果: