EmguCV Image Process: Process Video Sequences
參考OpenCV 2 Computer Vision Application Programming Cookbook第十章
介紹內容如下:
Reading video sequences
Processing the video frames
Writing video sequences
Tracking feature points in video
Extracting the foreground objects in video
承接上一篇所介紹的內容
一樣透過VideoProcessor這個類別
加入把處理過的影像寫檔的功能
在這個類別中加入以下這些欄位
//The OpenCV video witer object VideoWriter writer; //output filename string outputFile; //current index for output images int currentIndex; //number of digits in output image filename int digits; //extension of output images string extension;
這裡新增一個VideoWriter的類別
這就是EmguCV用來寫檔的類別
其他欄位像是儲存寫檔的檔名
儲存成影像檔的index、副檔名等等
接著加入以下的方法
/// <summary>
/// set the output video file by default the same
/// parameters than input video will be used
/// </summary>
/// <param name="filename">file name</param>
/// <param name="codec">video codec</param>
/// <param name="framerate">video FPS</param>
/// <param name="isColor">is color</param>
/// <returns>success or fail</returns>
public bool SetOutput(string filename, bool isColor)
{
double framerate = GetFrameRate();//same as input
Size size = GetFrameSize();
this.outputFile = filename;
this.extension = string.Empty;
this.writer = new VideoWriter(this.outputFile, GetCodec(), (int)framerate, size.Width, size.Height, isColor);
return true;
}
/// <summary>
/// set output as a series of image files
/// extension must be ".jpg", ".bmp" ...
/// </summary>
/// <param name="filename">prefix</param>
/// <param name="ext">image file extension</param>
/// <param name="numberOfDigits">number of digits</param>
/// <param name="startIndex">start index</param>
/// <returns>success or fail</returns>
public bool SetOutput(string filename, string ext)
{
//filenames and their common extension
this.outputFile = filename;
this.extension = ext;
//number of digits in the file numbering scheme
this.digits = 3;
//start numbering at this index
this.currentIndex = 0;
return true;
}
/// <summary>
/// Get the codec of input video
/// </summary>
/// <returns>the codec</returns>
private int GetCodec()
{
return CvInvoke.CV_FOURCC('X', 'V', 'I', 'D');
}
/// <summary>
/// Get the frame size of input video
/// </summary>
/// <returns>the frame size</returns>
private Size GetFrameSize()
{
Size size = new Size()
{
Height = this.capture.Height,
Width =this.capture.Width
};
return size;
}
/// <summary>
/// to write the output frame
/// could be video or image
/// </summary>
/// <param name="frame">output frame</param>
private void WriteNextFrame(IImage frame)
{
if (!string.IsNullOrWhiteSpace(this.extension))
{
string imageFile = this.outputFile + this.currentIndex + this.extension;
this.currentIndex++;
frame.Save(imageFile);
}
else
{
if (frame.NumberOfChannels == 1)
{
this.writer.WriteFrame((Image<Gray, Byte>)frame);
}
else
{
this.writer.WriteFrame((Image<Bgr, Byte>)frame);
}
}
}
這裡分別加入兩個設定output的方法採用多載的方式(相同名稱的方法SetOutput)
一個做為output為video的設定
一個做為output為image的設定
而output為video的部分
要取得codec,因此加入一個GetCodec的方法
這裡採用一般電腦都會有安裝的XVID這個codec
然後GetFrameSize取得capture所擷取的影像的大小
WriteNextFrame就判斷是要出輸出video還是image
然後在原本的Run這個方法中
加入寫檔的操作
/// <summary>
/// to grab (and process) the frame of the sequence
/// </summary>
public void Run()
{
//current frame
IImage frame;
//output frame
IImage output;
//if no catpure device has been set
if (!this.IsOpened)
{
return;
}
this.stop = false;
while (!IsStopped)
{
//read next frame if any
frame = this.capture.QueryFrame();
if (frame == null)
{
break;
}
//display input frame
if(!string.IsNullOrWhiteSpace(this.windowNameInput))
{
CvInvoke.cvShowImage(this.windowNameInput, frame.Ptr);
}
//calling the process function
if (this.callIt)
{
//process the frame
output = process(frame);
//increment frame number
this.fnumber++;
}
else
{
output = frame;
}
//write output sequence
if (!string.IsNullOrWhiteSpace(this.outputFile))
{
WriteNextFrame(output);
}
//display output frame
if (!string.IsNullOrWhiteSpace(this.windowNameOutput))
{
CvInvoke.cvShowImage(this.windowNameOutput, output.Ptr);
}
//introduce a delay
if (this.delay >= 0 && CvInvoke.cvWaitKey(delay) >= 0)
{
StopIt();
}
//check if we should stop
if (this.frameToStop >= 0 && GetFrameNumber() == this.frameToStop)
{
StopIt();
}
}
this.writer.Dispose();
}
在output接收到處理過的frame後
判斷outputFile是否有值,有的話則進行WriteNextFrame的動作
並把處理後的frame(output)帶入即可
操作的方式也很簡單
如下
//Create instance VideoProcessor processor = new VideoProcessor(); //Open the video file processor.SetInput(@"E:\2012 Taipei Time Lapse.mov"); processor.SetFrameProcessor(Canny); processor.SetOutput(@"E:\output.avi", false); //processor.SetOutput(@"E:\output\output", ".jpg"); //Start the process processor.Run();
執行後就會開始讀取E:\2012 Taipei Time Lapse.mov這個影片檔
並且把這個檔案寫入E:\output.avi
若是下面那行註解的就是將影像一張一張存入E:\output\資料夾中
結果如下
這是存成圖片的結果
這是轉錄成影片,由media player播放的開頭
這是轉錄成影片,由media player播放的龍山寺
這是轉錄成影片,由media player播放的國父紀念館
接著下一篇將會介紹如何去追蹤影片中的特徵值(feature)




沒有留言:
張貼留言