C++ Opencv 静止画から動画を作成する

まずはソース

#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <opencv2/opencv.hpp>

int image_to_video(std::string result, std::string image_name, std::string ext, int frame_num, double frame_rate) {

  // {image_name}_00x.ext にするための桁数の取得
  int digit = std::to_string(frame_num).length();
  
  std::stringstream base;
  base << image_name << "_" << std::setw(digit) << std::setfill('0') << 0 << ext;
  cv::Mat Img = cv::imread(base.str().c_str());

  int fourcc = cv::VideoWriter::fourcc('m', 'p', '4', 'v');
  cv::VideoWriter writer(result, fourcc, frame_rate, cv::Size(Img.cols, Img.rows), true);

  for (int i = 0; i < frame_num; i++) {

    // {image_name}_00x.extの文字列作成
    std::stringstream ss;
    ss << image_name << "_" << std::setw(digit) << std::setfill('0') << i << ext;
    std::cout << ss.str() << std::endl;

    // フレームを取得する
    Img = cv::imread(ss.str().c_str());

    if (Img.empty()) {
      return -1;
    }

    // フレームの書き出し
    writer << Img;

  }
  return 0;
}

関数解説

int digit = std::to_string(frame_num).length();はimage_name_00x.jpgにするために、0埋めを何桁分するかというのを求めるための桁数の計算です。
フレーム数が10枚なら00.ext~, 100枚なら000.ext~になるように桁数を予め求めておきます。

std::stringstream base;
base << image_name << "_" << std::setw(digit) << std::setfill('0') << 0 << ext;
cv::Mat Img = cv::imread(base.str().c_str());

ここでは読み込むフレームの名前を求めています。
引数のimage_nameで、パスを含めて名前の番号の前までを指定しおきます。
例えばimage_000.jpg~image_999.jpgというフレームがあったとすると、ここの引数はimageになっています。
その名前に0埋めした番号とext 拡張子を追加して名前を生成しています。
ここではimage_name_0.jpgを求めています。理由はこの後。

int fourcc = cv::VideoWriter::fourcc('m', 'p', '4', 'v');
cv::VideoWriter writer(result, fourcc, frame_rate, cv::Size(Img.cols, Img.rows), true);

ここではVideoWriteクラスのインスタンスを生成しています。writerがインスタンス名になります。
fourccは生成する動画の拡張子で、指定はいくつかできるのですが今回はmp4限定でやっています。
Sizeで動画のサイズを決定できるのですが、ここではフレーム画像のサイズをそのまま動画のサイズにしています。
なので、先ほど1枚目のフレームを読み込んでいました。
1枚目のフレームをベース画像として、そのサイズに依存して動画を作成します。
最後のtrueはカラーか否かの設定ができます。

    Img = cv::imread(ss.str().c_str());

あとはループで読み込む画像の名前を0~frame_numまで変化させつつ画像を読み込みます。
画像の読み込みはimreadを使うのですが、引数がchar型なので型変換を行います。
stringstreamからstring, stringからcharへこの1行では変換しています。

    writer << Img;

最後に読み込んだ画像を動画へ追加していって、一つの動画を生成します。
ここでは読み込んだフレームを動画へ書き出しています。

まとめ

動画の書き出しは、静止画の書き出しの真逆なので、理解しやすかったです。

github.com メインも含めた関数の使用例をここに置いておくので、試しに動かしたい人はご利用ください。