OpenCV3でアップコンバート(超解像)する

刑事ドラマやSF映画では、防犯カメラの映像を拡大して犯人の顔がアップになっていくシーンがあります。もともと10x10ピクセルしかない画像を10倍に拡大するとモザイク(最近傍補完)やぼやけた画像(線形補間)になりますが、ノイズ除去や鮮明化を行うことで滑らかに拡大する『超解像』という画像処理があります。(超解像の詳細は林昌希氏の記事(コンピュータビジョンのセカイ | マイナビニュース)などを参考ください。)

ここでは、映像の一部を切り出して超解像処理を行う例をOpenCV3で実装していきます。

OpenCVには超解像処理を行うクラスSuperResolutionがnamespace superresで提供されています。超解像に必要なinclude文は

#include <opencv2/superres.hpp>

のみですが、超解像は連続したフレームから補完すべき画素を求めるため、基本的に動画(ファイルまたはカメラからのキャプチャ)を入力に用います。このため、動画の入出力用に

#include <opencv2/videoio.hpp>

も加えています。

指定の動画を超解像処理して出力

#include <opencv2/core.hpp>
#include <opencv2/superres.hpp>
#include <opencv2/videoio.hpp>

int main(int argc, const char* argv[])
{
  //読み込む動画のパス
  cv::String source_path = "C:/source.mpg";
  //超解像動画を書き出すのパス
  cv::String dest_path = "C:/dest.mpg";
  //書き出す動画のFPS
  double outputFps = 29.97;
  //書き出す動画のCODEC
  int outputCodec = cv::VideoWriter::fourcc('X','V','I','D');
  //超解像する倍率(初期設定=4)
  int superScale = 2;
  //超解像のその他設定
  //初期設定(TemporalAreaRadius=4, Iterations=180)のままだと長時間を要するので質を下げる
  int superTemporalAreaRadius = 1;
  int superIterations = 2;
  
  //超解像度処理クラス生成
  cv::Ptr<cv::superres::SuperResolution> superRes = cv::superres::createSuperResolution_BTVL1();
  //処理する動画を指定
  superRes->setInput(cv::superres::createFrameSource_Video(source_path));
  //超解像処理の設定
  superRes->setScale(superScale);
  superRes->setTemporalAreaRadius(superTemporalAreaRadius);
  superRes->setIterations(superIterations);


  //1フレーム読み込む
  cv::Mat supered;
  superRes->nextFrame(supered);
  if (! supered.empty())
  {
    //動画書き出しクラス生成
    cv::VideoWriter writer;
    writer.open(dest_path, outputCodec, outputFps, supered.size());
    //フレームが有効な限り出力
    do
    {
      //合成画像を出力
      writer << supered;

      //次のフレームを読み込む
      superRes->nextFrame(supered);
    }
    while(!supered.empty());

    //書き出し終了
    writer.release();
  }

  return 0;
}
※依存ライブラリ(リンカーへ設定)
  • opencv_core300.lib
  • opencv_imgproc300.lib
  • opencv_superres300.lib
  • opencv_video300.lib
  • opencv_videoio300.lib
  • opencv_videostab300.lib
※参照するDLL
  • opencv_core300.dll
  • opencv_imgcodecs300.dll
  • opencv_imgproc300.dll
  • opencv_superres300.dll
  • opencv_video300.dll
  • opencv_videoio300.dll
  • opencv_videostab300.dll
  • opencv_ffmpeg300_64.dll

超解像には複雑な計算を必要とするため、リアルタイム処理には向きません。
超解像処理の設定を変更し、質を落とすことである程度は高速化できますが、入力映像が100x100px以上の場合はかなり長時間の処理になります。冒頭の例のように、元映像の一部分を拡大するという目的であれば、超解像処理する領域は小さくできるかと思います。

映像の一部を切り出して超解像

実際に、元映像の一部を超解像してみましょう。
SuperResolutionは元映像全体を処理するよう実装されていますので、以下の例ではまず元映像から超解像したい一部を切り出した映像を作り、その切り出した映像に超解像処理をかけていきます。
また、元映像と超解像結果、さらに超解像ではなく単順にピクセル等倍で拡大した場合の映像を縦に並べた比較映像を出力しています。

#include <opencv2/core.hpp>
#include <opencv2/superres.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/imgproc.hpp>

int main(int argc, const char* argv[])
{
  //読み込む動画のパス
  cv::String source_path = "C:/source.mpg";
  //超解像する範囲を切り出した動画を書き出すパス
  cv::String croped_path = "C:/croped.mpg";
  //超解像動画を書き出すのパス
  cv::String dest_path = "C:/dest.mpg";
  //書き出す動画のFPS
  double outputFps = 29.97;
  //書き出す動画のCODEC
  int outputCodec = cv::VideoWriter::fourcc('X','V','I','D');
  //超解像する倍率(初期設定=4)
  int superScale = 5;
  //超解像のその他設定
  //初期設定(TemporalAreaRadius=4, Iterations=180)のままだと長時間を要するので質を下げる
  int superTemporalAreaRadius = 1;
  int superIterations = 2;
  //超解像で切り出す範囲
  cv::Rect superRect(80,100,80,80);
  

  //まず超解像する部分を切り出した動画を用意する
  cv::VideoCapture originalVideo;
  cv::Mat original;

  //元動画を読み込む
  originalVideo.open(source_path);
  //1フレームずつ切り出して書き出していく
  originalVideo >> original;
  if (! original.empty())
  {
    cv::VideoWriter writer;
    writer.open(croped_path, outputCodec, outputFps, superRect.size());
    do
    {
      writer << cv::Mat(original, superRect);
      originalVideo >> original;
    }
    while(! original.empty());
    
    writer.release();
    originalVideo.release();
  }

  //合成のため元動画をもう一度開く
  originalVideo.open(source_path);

  //超解像度処理クラス生成
  cv::Ptr<cv::superres::SuperResolution> superRes = cv::superres::createSuperResolution_BTVL1();
  //処理する動画を指定
  superRes->setInput(cv::superres::createFrameSource_Video(source_path));
  //超解像処理の設定
  superRes->setScale(superScale);
  superRes->setTemporalAreaRadius(superTemporalAreaRadius);
  superRes->setIterations(superIterations);


  //1フレーム読み込む
  cv::Mat supered;
  superRes->nextFrame(supered);
  originalVideo >> original;
  if (! supered.empty())
  {
    //元映像と超解像版、単純拡大版を縦につなげた映像を作る
    cv::Mat dest(cv::Size(MAX(original.size().width, supered.size().width), original.size().height + supered.size().height*2), CV_8UC3);
    cv::Mat destOriginalROI(dest, cv::Rect(0, 0, original.size().width, original.size().height));
    cv::Mat destSuperROI(dest, cv::Rect(0, original.size().height, supered.size().width, supered.size().height));
    cv::Mat destResizeROI(dest, cv::Rect(0, original.size().height+supered.size().height, supered.size().width, supered.size().height));
    cv::Mat resized(supered.size(), CV_8UC3);
    cv::Scalar lineColor(0,250,0);

    //動画書き出しクラス生成
    cv::VideoWriter writer;
    writer.open(dest_path, outputCodec, outputFps, dest.size());

    //フレームが有効な限り出力
    do
    {
      //超解像前の映像を描画
      original.copyTo(destOriginalROI);
      //超解像後の映像を描画
      supered.copyTo(destSuperROI);
      //超解像ではなく単順拡大
      cv::resize(cv::Mat(original, superRect), resized, resized.size(), 0, 0, cv::INTER_NEAREST);
      resized.copyTo(destResizeROI);

      //切り出し部分に対応する矩形を描画
      cv::rectangle(dest, superRect, lineColor);
      //切り出し部分の左下から超解像画像の上端へ線を引く
      cv::line(dest, cv::Point(superRect.x, superRect.y+superRect.height), cv::Point(0, original.size().height), lineColor);
      //切り出し部分の右下から超解像画像の下端へ線を引く
      cv::line(dest, cv::Point(superRect.x+superRect.width, superRect.y+superRect.height), cv::Point(supered.size().width, original.size().height), lineColor);

      //合成画像を出力
      writer << dest;


      //次のフレームを読み込む
      superRes->nextFrame(supered);
      originalVideo >> original;
    }
    while(!supered.empty());

    //書き出し終了
    writer.release();
    originalVideo.release();
  }

  return 0;
}
※依存ライブラリ(リンカーへ設定)
  • opencv_core300.lib
  • opencv_imgproc300.lib
  • opencv_superres300.lib
  • opencv_video300.lib
  • opencv_videoio300.lib
  • opencv_videostab300.lib
※参照するDLL
  • opencv_core300.dll
  • opencv_imgcodecs300.dll
  • opencv_imgproc300.dll
  • opencv_superres300.dll
  • opencv_video300.dll
  • opencv_videoio300.dll
  • opencv_videostab300.dll
  • opencv_ffmpeg300_64.dll
出力結果

1段目が元映像、2段目が超解像、3段目が単純に拡大(最近傍補完)した映像です。
動画を再生するには、videoタグをサポートしたブラウザが必要です。

単純な拡大と比較して、超解像により滑らかな出力が得られています。
ただし、最近傍補完による拡大ではピクセルの粗さが目立つものの、バイリニア補完でもある程度は滑らかな拡大が可能です。超解像処理は長時間を要しますので、少しでも鮮明な拡大映像が必要な場合に、パラメータを調整しながら使用するのが適当かと思います。