OpenCVで動画読み込み&書き込み --- C++/CLIでラップ

下記記事にてOpenCVで動画を読み書きする方法を調べました。
OpenCVで動画読み込み&書き込み - 何でもプログラミング

今回は.Netで利用できるよう、C++/CLIでラップしてみたいと思います。

尚、Matクラス(Mat8UC1クラス)は下記記事のものを利用しています。
OpenCVをC++/CLIでラップ - 何でもプログラミング

VideoCapture

FourccはStringで返すようにしてみました。

Mat8UC1を利用しているため、Captureの中で8UC1に変換しています。ここは適宜変更してください。

using namespace msclr::interop;
using namespace System;

public ref class VideoCapture
{
public:
    property int Width  { int get()    { return (int)_capture->get(CV_CAP_PROP_FRAME_WIDTH); } }
    property int Height { int get()    { return (int)_capture->get(CV_CAP_PROP_FRAME_HEIGHT); } }
    property double Fps { double get() { return      _capture->get(CV_CAP_PROP_FPS); } }
    property int Count  { int get()    { return (int)_capture->get(CV_CAP_PROP_FRAME_COUNT); } }
    property String^ Fourcc
    {
        String^ get()
        {
            int i = (int)_capture->get(CV_CAP_PROP_FOURCC);
            char fourcc[] =
            {
                (char)(i & 0XFF),
                (char)((i & 0XFF00) >> 8),
                (char)((i & 0XFF0000) >> 16),
                (char)((i & 0XFF000000) >> 24),
                0
            };
            return gcnew String(fourcc);
        }
    }
    property bool IsOpened { bool get() { return _capture->isOpened(); } }
    VideoCapture(String^ path)
    {
        _capture = new cv::VideoCapture(marshal_as<std::string>(path));
    }
    !VideoCapture()
    {
        if (_capture != nullptr)
        {
            delete _capture;
            _capture = nullptr;
        }
    }
    ~VideoCapture() { this->!VideoCapture(); }
    Mat8UC1^ Capture()
    {
        cv::Mat src;
        *_capture >> src;

        auto dst = gcnew Mat8UC1(src.rows, src.cols);
        if (src.channels() == 1)
            src.copyTo(*dst->_mat);
        else if (src.channels() == 3 || src.channels() == 4)
            cv::cvtColor(src, *dst->_mat, CV_RGB2GRAY);
            
        return dst;
    }
    void Seek(int frame)
    {
        _capture->set(CV_CAP_PROP_POS_FRAMES, frame);
    }
private:
    cv::VideoCapture* _capture;
};


VideoWriter

Fourcc文字列からIntに変換するにはcv::VideoWriter::fourccを利用します。

Write関数内でカラー化していますが、ここは適宜変更してください。

public ref class VideoWriter
{
public:
    property bool IsOpened { bool get() { return _writer->isOpened(); } }
    VideoWriter(String^ path, int width, int height, String^ fourcc, double fps)
    {
        _writer = new cv::VideoWriter(
            marshal_as<std::string>(path), 
            cv::VideoWriter::fourcc(fourcc[0], fourcc[1], fourcc[2], fourcc[3]), 
            fps, 
            cv::Size(width, height));
    }
    !VideoWriter()
    {
        if (_writer != nullptr)
        {
            delete _writer;
            _writer = nullptr;
        }
    }
    ~VideoWriter() { this->!VideoWriter(); }
    void Write(Mat8UC1^ mat)
    {
        cv::Mat color;
        cv::cvtColor(*mat->_mat, color, CV_GRAY2BGR);

        *_writer << color;
    }
private:
    cv::VideoWriter* _writer;
};


F#で利用

動画はopenCVのサンプルMegamind.aviを利用しています。

動画をそのまま保存しているだけですが、Mat8UC1を介しているため出力はグレースケールになっています。

[<EntryPoint>]
let main argv = 
    let capture = new CV.VideoCapture(@"C:\lib\opencv3.2\sources\samples\data\megamind.avi")

    let writer = new CV.VideoWriter(@"output.avi", capture.Width, capture.Height, capture.Fourcc, capture.Fps);

    capture 
    |> Seq.unfold (fun x -> Some(x.Capture(), x)) 
    |> Seq.takeWhile (fun x -> not x.IsEmpty)
    |> Seq.iter writer.Write

    0

f:id:any-programming:20170308171514p:plain