第7回 初めてのOpenCV開発 ― デバッグ機能およびデバッグ支援プラグイン【OpenCV 3.1.0】OpenCV入門【3.0対応】

OpenCVを用いたアプリケーションの開発で役立つにデバッグ機能やデバッグ支援プラグインを紹介する。また、問題が生じた場合の対処法について説明する。

» 2017年01月16日 05時00分 公開
「OpenCV入門【3.0対応】」のインデックス

連載目次

ご注意:本記事は、@IT/Deep Insider編集部(デジタルアドバンテージ社)が「www.buildinsider.net」というサイトから、内容を改変することなく、そのまま「@IT」へと転載したものです。このため用字用語の統一ルールなどは@ITのそれとは一致しません。あらかじめご了承ください。

1. はじめに

 前回の記事では、OpenCVのhighgui/imgcodecs/videoioモジュールについて解説しました。今回はOpenCVを用いたアプリケーション開発における以下の機能・プラグインについて紹介します。

 また、筆者が確認した環境は以下の通りです。

項目 内容
OpenCVバージョン OpenCV 3.1.0
Visual Studio Visual Studio 2013 Update5
ビルド構成 x64Release
OS Windows 10 Pro(64bit)
筆者が確認した環境

2. OpenCVデバッグ機能

 ここではOpenCVが備えているデバッグ機能のうち、以下の機能について紹介します。

2.1 画素値表示

 前回簡単に紹介しましたが、highguiモジュールは、GUIツールキット「Qt」を用いた下記の拡張機能を有効化できます(有効化の方法は次回紹介します)。有効化していない状態で本稿のサンプルを実行しても、以下に掲載している画像のようにはなりませんのでご注意ください)。また、これらの拡張機能の詳細は公式ドキュメント(英語)を参照ください。

  • コントロールパネル
  • ツールバー
  • ステータスバー
  • 画像の拡大表示&画素値表示
図1 QtによるGUI拡張機能 図1 QtによるGUI拡張機能
ウィンドウ上部にあるバーがツールバー、下部にあるのがステータスバー、「チェックボックス」や「ラジオボックス」と表示されているのがコントロールパネルです。画像の拡大表示&画素値表示については後述します。

 これらの拡張機能の一つである「画像の拡大表示&画素値表示」を用いることで、特定座標の画素値を簡単にチェックできます。

2.1.1 サンプルコード

 以降の「画像の拡大表示&画素値表示」を説明するためのファイルから画像を読み込んでウィンドウに表示するサンプルコードです。

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

int main(int argc, const char* argv[])
{
  // 画像データをファイル(この例では「lena.jpg」)から読み込む
  cv::Mat src = cv::imread("lena.jpg", cv::IMREAD_COLOR);

  // 画像の読み込みに失敗したらエラー終了する
  if (src.empty())
  {
    std::cerr << "Failed to open image file." << std::endl;
    return -1;
  }

  cv::namedWindow("image", cv::WINDOW_AUTOSIZE);
  cv::imshow("image", src);
  cv::waitKey(0);
  cv::destroyAllWindows();

  return 0;
}

QtによるGUI拡張機能を試すためのサンプルコード
このプログラムを実行するには、「lena.jpg」という名前で任意のJPEG画像ファイルを用意して、サンプルプログラムが実行されるカレントディレクトリ(デバッグ時なら作業ディレクトリ)に配置する必要があります。

2.1.1 画素値表示

 「2.1.1 サンプルコード」を実行すると、図2にあるようにウィンドウに画像が表示されます。

図2 ウィンドウに画像データを表示(※QtによるGUI拡張機能が有効な場合) 図2 ウィンドウに画像データを表示(QtによるGUI拡張機能が有効な場合)

 この状態でマウスのホイール操作を行うことで、画像の拡大・縮小と画素値表示を行うことができます(Qtを用いた拡張機能を有効にした場合のみ)。

 このとき、拡大を続けると、図3にあるようにピクセルごとの画素値が表示されるようになります。

図3 画素値表示(※Qt拡張機能が有効な場合) 図3 画素値表示(Qt拡張機能が有効な場合)
画像を各ピクセルが見えるレベルにまで拡大すると、このように画素値が表示されます。
  (1)マウスカーソルがある座標の画素値が表示されます。
  (2)ピクセルごとの画素値が表示されます。

2.2 YAML形式入出力

 OpenCVでは、cv::FileStorageクラスを用いることで、YAML形式のファイル入出力を行うことができます。YAMLとは、データ構造を文字列で表現したデータ形式で、この形式で書き出すことで画像データをテキストとして扱うことができます。

2.2.1 サンプルコード

 下記の処理を行うサンプルコードを以下に示します。


  (1)MatクラスのデータをYAML形式のファイルに書き出す
  (2)YAML形式のファイルを読み込んで、Matクラスにロードする

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

int main(int argc, const char* argv[])
{
  // 画像データをファイル(この例では「lena.jpg」)から読み込む
  cv::Mat src = cv::imread("lena.jpg", cv::IMREAD_COLOR);

  // 画像の読み込みに失敗したらエラー終了する
  if (src.empty())
  {
    std::cerr << "Failed to open image file." << std::endl;
    return -1;
  }

  cv::Mat img2;

  // (1)MatクラスのデータをYAML形式のファイルに書き出す
  cv::FileStorage fs_write("lena.yml", cv::FileStorage::WRITE);
  if (!fs_write.isOpened())
  {
    std::cerr << "Failed to open YAML file." << std::endl;
    return -1;
  }
  fs_write << "img" << src;
  fs_write.release();

  // (2)YAML形式のファイルを読み込んでMatクラスにロードする
  cv::FileStorage fs_read("lena.yml", cv::FileStorage::READ);
  if (!fs_read.isOpened())
  {
    std::cerr << "Failed to open YAML file." << std::endl;
    return -1;
  }
  fs_read["img"] >> img2;
  fs_read.release();

  return 0;
}

YAML形式ファイルの入出力を行うサンプルコード

2.2.2 YAML形式出力の例

 以下は「2.2.1 サンプルコード」で出力されるYAML形式ファイルの出力例(抜粋)です。

%YAML:1.0
img: !!opencv-matrix
   rows: 512
   cols: 512
   dt: "3u"
   data: [ 128, 138, 225, 127, 137, 224, 126, 136, 223, 123, 135, 223,
     126, 137, 227, 120, 131, 221, 126, 137, 229, 123, 136, 228, 126,
     139, 231, 122, 135, 227, 122, 137, 229, 120, 136, 225, 113, 129,


YAML形式ファイルの出力例(抜粋)

 この例では、

  • OpenCVの画像データを格納(img: !!opencv-matrix
  • rowsが512ピクセル(rows: 512
  • colsが512ピクセル(cols: 512
  • チャンネル数が3(dt: "3u"
  • 画素値が128、138、225……(以下略)と並んでいる(data: [ 128, 138, 225

ことが分かります。

2.3 パフォーマンス計測

 OpenCVで提供されているパフォーマンス計測機能は以下の2つです。

  • bgcolor=gray◇cv::getTickCount◆関数を使う方法
  • bgcolor=gray◇cv::TickMeter◆クラスを使う方法

 cv::TickMeterクラスはOpenCV 3.0のタイミングでこのクラスが削除されましたが、OpenCV 3.2よりcv::TickMeterクラスが再度追加されたため、OpenCV 3.2以降であればこのクラスを用いてパフォーマンス計測を行うことができます。

 ただし、cv::TickMeterクラスを使用した場合、OpenCV 3.0、3.1との互換性が失われてしまう点にご注意ください。

2.3.1 cv::getTickCount関数を使う方法

 cv::getTickCount()を使って処理時間を計測する方法は以下の通りです。


  (1)測りたい処理の前にcv::getTickCount関数を呼ぶ
  (2)測りたい処理の後にcv::getTickCount関数を呼ぶ
  (3)(2)(1)の差分」を用いてミリ秒に変換する

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <iostream>

int main(int argc, const char* argv[])
{
  // 画像データをファイル(この例では「lena.jpg」)から読み込む
  cv::Mat src = cv::imread("lena.jpg", cv::IMREAD_COLOR);

  // 画像の読み込みに失敗したらエラー終了する
  if (src.empty())
  {
    std::cerr << "Failed to open image file." << std::endl;
    return -1;
  }

  cv::Mat dst;
  double f = 1000.0f / cv::getTickFrequency();

  // (1)測りたい処理の前にcv::getTickCount関数を呼ぶ
  int64 start = cv::getTickCount();

  // 測りたい処理
  cv::resize(src, dst, cv::Size(), 5.0, 5.0);

  // (2)測りたい処理の後にcv::getTickCount関数を呼ぶ
  int64 end = cv::getTickCount();

  // (3)(2)(1)の差分」を用いてミリ秒に変換する
  std::cout << (end - start) * f << "[ms]" << std::endl;

  return 0;
}

OpenCVでパフォーマンス計測するためのサンプルコード(cv::getTickCount関数)

2.3.2 cv::TickMeterクラスを使う方法

 cv::TickMeterクラスを使って処理時間を計測する方法は以下の通りです。


  (1)測りたい処理の前にcv::TickMeterクラスのstartメソッドを呼ぶ
  (2)測りたい処理の後にcv::TickMeterクラスのstopメソッドを呼ぶ
  (3)getTimeMilliメソッドを呼ぶ(ミリ秒単位の結果を取得できる)

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <iostream>

int main(int argc, const char* argv[])
{
  // 画像データをファイル(この例では「lena.jpg」)から読み込む
  cv::Mat src = cv::imread("lena.jpg", cv::IMREAD_COLOR);

  // 画像の読み込みに失敗したらエラー終了する
  if (src.empty())
  {
    std::cerr << "Failed to open image file." << std::endl;
    return -1;
  }

  cv::Mat dst;
  cv::TickMeter meter;

  // (1)測りたい処理の前にcv::TickMeterクラスのstartメソッドを呼ぶ
  meter.start();

  // 測りたい処理
  cv::resize(src, dst, cv::Size(), 5.0, 5.0);

  // (2)測りたい処理の後にcv::TickMeterクラスのstopメソッドを呼ぶ
  meter.stop();

  // (3)getTimeMilliメソッドを呼ぶ(ミリ秒単位の結果を取得できる)
  std::cout << meter.getTimeMilli() << "[ms]" << std::endl;

  return 0;
}

OpenCVでパフォーマンス計測するためのサンプルコード(cv::TickMeterクラス)

3. OpenCVデバッグ支援プラグイン

 マイクロソフトがOpenCVアプリケーション開発向けにおけるデバッグ支援のVisual Studioプラグイン「Image Watch」を配布しています。このプラグインを導入することでVisual Studioによるデバッグ中にMatデータの処理結果をランタイムで確認できます。

3.1 インストール、アンインストール

 Image Watchのインストール/アンインストール方法などは、以下のサイトを参照してください。

インストール、アンインストール

チュートリアル

公式ヘルプ

3.2 使い方

 ここでは、Image Watchプラグインの使い方について、以下のサンプルコードを用いて説明します。

 以下のサンプルコードは、読み込んだ画像に対してグレースケール変換を行い、その結果に対して二値化処理をしています。

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <iostream>

int main(int argc, const char** argv)
{
  // 画像データをファイル(この例では「lena.jpg」)から読み込む
  cv::Mat img = cv::imread("lena.jpg", cv::IMREAD_COLOR);

  // 画像の読み込みに失敗したらエラー終了する
  if (img.empty())
  {
    std::cerr << "Failed to open image file." << std::endl;
    return -1;
  }

  cv::Mat gray, bin;

  // (1)グレースケールに変換する
  cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);

  // (2)二値化処理をする
  cv::threshold(gray, bin, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);

  return 0;
}

Image Watchプラグインの使い方を示すためのサンプルコード

 以降、図を用いてImage Watchプラグインの使い方を説明します。

図4 メニューバーから[表示]−[その他のウィンドウ]−[Image Watch]を選択する 図4 メニューバーから[表示]−[その他のウィンドウ]−[Image Watch]を選択する

図5 ブレークポイントを作成してデバッグ実行すると、ローカル変数(Locals)のMatデータ一覧とサムネイルが[Image Watch]ウィンドウに表示される 図5 ブレークポイントを作成してデバッグ実行すると、ローカル変数(Locals)のMatデータ一覧とサムネイルが[Image Watch]ウィンドウに表示される

図6 着目したい画像を選択すると画像のプレビューが表示される(ホイール操作で画像の拡大・縮小も可能) 図6 着目したい画像を選択すると画像のプレビューが表示される(ホイール操作で画像の拡大・縮小も可能)

図7 右クリックして[Dump to File]を選択すると、画像データをファイルに書き出すことができる 図7 右クリックして[Dump to File]を選択すると、画像データをファイルに書き出すことができる

4. 困ったときには

 OpenCVを使ったアプリケーションを開発する上で問題が生じた場合の対処法をケースに応じて紹介します(参考サイトは、いずれも英語です)。

4.1 APIの使い方が分からない

 OpenCV APIの使い方が分からない場合、まずは以下のサイトが参考にするとよいでしょう。

 ただし、ドキュメントやチュートリアルだけでは使い方が分かりにくい場合もあるため、OpenCVに同梱されているサンプルコード(opencv-3.1.0\samples\cpp)やテストコード(coreモジュールの場合、opencv-3.1.0\modules\core\test)に含まれるコードを参照することで、APIの呼び出し例を知ることができます。

4.2 APIがどのモジュールで提供されているか分からない

 OpenCVのAPIがどのモジュールで提供されているか分からない場合は、公式ドキュメントから下記の手順で探すことができます。

  (1)公式ドキュメント(安定版)で関数名を検索する
  (2)検索結果から関数のページを開く
  (3)ページ上部に記載されているモジュール名を確認する

 ここではcv::imreadがどのモジュールで提供されているかを調べる例を示します。

図8 公式ドキュメントで関数名を検索してヒットした結果をクリックする 図8 公式ドキュメントで関数名を検索してヒットした結果をクリックする

図9 ページ上部に記載されているモジュール名を確認する 図9 ページ上部に記載されているモジュール名を確認する

4.3 意図通りの動作にならない

 OpenCVを用いた実装を行っていて意図通りの動作にならない場合は、以下のサイトで同様の内容の投稿がないかを探すとよいでしょう。

 また、公式フォーラムや公式issue trackerに投稿する際には、再現環境に関する情報を記載することが推奨されています。そのため、開発者に再現環境やOpenCVのバージョンなどを正確に伝えるために、第5回で紹介したcv::getBuildInformation関数の出力結果も併せて記載するとよいでしょう。

5. おわりに

 今回は、デバッグ機能およびデバッグ支援ツールを紹介しました。次回は、CMakeを使ったカスタマイズビルドの方法を紹介します。

「OpenCV入門【3.0対応】」のインデックス

OpenCV入門【3.0対応】

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。