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)




 
沒有留言:
張貼留言