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)
沒有留言:
張貼留言