第7回 初めてのOpenCV開発 ― デバッグ機能およびデバッグ支援プラグイン【OpenCV 3.1.0】:OpenCV入門【3.0対応】
OpenCVを用いたアプリケーションの開発で役立つにデバッグ機能やデバッグ支援プラグインを紹介する。また、問題が生じた場合の対処法について説明する。
ご注意:本記事は、@IT/Deep Insider編集部(デジタルアドバンテージ社)が「www.buildinsider.net」というサイトから、内容を改変することなく、そのまま「@IT」へと転載したものです。このため用字用語の統一ルールなどは@ITのそれとは一致しません。あらかじめご了承ください。
1. はじめに
前回の記事では、OpenCVのhighgui/imgcodecs/videoioモジュールについて解説しました。今回はOpenCVを用いたアプリケーション開発における以下の機能・プラグインについて紹介します。
- OpenCVデバッグ機能(「2. OpenCVデバッグ機能」で紹介)
- OpenCVデバッグ支援プラグイン(「3. OpenCVデバッグ支援プラグイン」で紹介)
- 困ったときには(「4. 困ったときには」で紹介)
また、筆者が確認した環境は以下の通りです。
項目 | 内容 |
---|---|
OpenCVバージョン | OpenCV 3.1.0 |
Visual Studio | Visual Studio 2013 Update5 |
ビルド構成 | x64Release |
OS | Windows 10 Pro(64bit) |
筆者が確認した環境 |
2. OpenCVデバッグ機能
ここではOpenCVが備えているデバッグ機能のうち、以下の機能について紹介します。
- 画素値表示(「2.1 画素値表示」で紹介)
- YAML形式入出力(「2.2 YAML形式入出力」で紹介)
- パフォーマンス計測(「2.3 パフォーマンス計測」で紹介)
2.1 画素値表示
前回簡単に紹介しましたが、highguiモジュールは、GUIツールキット「Qt」を用いた下記の拡張機能を有効化できます(※有効化の方法は次回紹介します)。有効化していない状態で本稿のサンプルを実行しても、以下に掲載している画像のようにはなりませんのでご注意ください)。また、これらの拡張機能の詳細は公式ドキュメント(英語)を参照ください。
- コントロールパネル
- ツールバー
- ステータスバー
- 画像の拡大表示&画素値表示
図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;
}
このプログラムを実行するには、「lena.jpg」という名前で任意のJPEG画像ファイルを用意して、サンプルプログラムが実行されるカレントディレクトリ(デバッグ時なら作業ディレクトリ)に配置する必要があります。
2.1.1 画素値表示
「2.1.1 サンプルコード」を実行すると、図2にあるようにウィンドウに画像が表示されます。
この状態でマウスのホイール操作を行うことで、画像の拡大・縮小と画素値表示を行うことができます(※Qtを用いた拡張機能を有効にした場合のみ)。
このとき、拡大を続けると、図3にあるようにピクセルごとの画素値が表示されるようになります。
図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;
}
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,
この例では、
- 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つです。
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;
}
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;
}
3. OpenCVデバッグ支援プラグイン
マイクロソフトがOpenCVアプリケーション開発向けにおけるデバッグ支援のVisual Studioプラグイン「Image Watch」を配布しています。このプラグインを導入することでVisual Studioによるデバッグ中にMatデータの処理結果をランタイムで確認できます。
3.1 インストール、アンインストール
Image Watchのインストール/アンインストール方法などは、以下のサイトを参照してください。
インストール、アンインストール
チュートリアル
公式ヘルプ
- 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プラグインの使い方を説明します。
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がどのモジュールで提供されているかを調べる例を示します。
4.3 意図通りの動作にならない
OpenCVを用いた実装を行っていて意図通りの動作にならない場合は、以下のサイトで同様の内容の投稿がないかを探すとよいでしょう。
また、公式フォーラムや公式issue trackerに投稿する際には、再現環境に関する情報を記載することが推奨されています。そのため、開発者に再現環境やOpenCVのバージョンなどを正確に伝えるために、第5回で紹介したcv::getBuildInformation関数の出力結果も併せて記載するとよいでしょう。
5. おわりに
今回は、デバッグ機能およびデバッグ支援ツールを紹介しました。次回は、CMakeを使ったカスタマイズビルドの方法を紹介します。
Copyright© Digital Advantage Corp. All Rights Reserved.