CPP+OpenCV ( 環境設定/静止画/動画表示 )

最終ターゲットCPUは「RaspbrryPi」ですが開発中ではターゲットボード上では動作が非常に遅く実用に耐えない
ので開発中のみ一般のノートPC(Linux & Windows10)上で開発します。
もちろん開発後の実行形式になればそこそこの動作が期待できるので移植して検証します。
開発ツールは、VisualStudio2019(C++)とします。

【開発環境】
使用PC:Mouse Computer(自宅PC)
使用Soft:Microsoft Visual Studio Community 2019 Version 16.4.2
元ホルダ-:D:\CPP_DATA\
作業ホルダ-:D:\work2
参考資料:詳細OpeCV3

【Visual Studio VC++ 環境設定】
ここでC++でプログラム開発する上で重要な環境設定があります。
この設定なしでプログラムを作りコンパイルを始めるとエラーとなります。その原因はコンパイル &リンクする時
必要なライブラリーの場所が解らずエラーとなるためです。もちろん作業場所の情報も必要となります。
これらの情報をプロジェクトを作成するたびに設定をする必要があります。
それはプロパティー上で行います。 以下は私の環境での例です。

VC++を開けて「ソリューションエクスプローラー」を表示させます。
ここで「プロジェクト名」で右クリックすると下記のような画面が表示されます。


この画面の一番下段にある「プロパティ」を選択します。
以下がプロパティー画面です。

ここでは「C++」「全般」を選択。
右側の窓では、追加インクルードディレクトリー>下側矢印をクリックし「編集」を選択。


最上段窓に「D:\opencv\build\include」ー>「OK」


次は「リンカー」「全般」右側窓で「追加のライブラリーディレクトリ」下側矢印で「編集」を選択


上の窓で「D:\opencv\build\x64\vc15\lib」と記載後、「OK」


同じ「リンカ―」「入力」、右窓で下矢印を押し「編集」を押す。


ここでは、「opencv_world411.lib」と「opencv_world411d.lib」を記載、「OK」


これですべての設定は終了です。

 


参考書籍は「詳解OpenCV3:O'reilly」ですが、この書籍はすべてがコンソール上で実行するプログラムになって
いるので開発環境でデバッグできる様に修正したり簡潔(エラー処理等の省略)化しています。



【Sample1】静止画表示


img1.jpg 画像は、「d:/work2/」フォルダー内に置きます。

画像ファイルの指定方法は、Sample1_1記載以外に「d:\\work2\\」もあります。
日本語キー以外ではバックラッシュを、1個ずつ使用します。

OpenCVの関数は、cv という名前空間にあります。
それぞれの関数呼び出しの前に cv:: をつけてコンパイラに cv という名前空間を使っていることを明示的に知ら
せています。
これが面倒な場合は、次の例の様に using namespace cv; というディレクティブ(指示子)を使用することで
省略することができます。

実行結果(画像名称:img1.png)



【Sample1_1】静止画表示<namespace を使用>


この例は、前の例で書きました名前空間の省略指定を行っている例です。
ここでは使っていませんが、インクルードファイルの指定で OpenCV のライブラリーをインクルードする場合、
#include <opencv2/opencv.hpp>はフルライブラリーをインクルードしていますので時間が掛かるが、この例
の場合は #include "opencv2/highgui/highgui.hpp"と言う様に必要パッケージのみをインクルードすると高速に
コンパイルできます。


<プログラム説明>
int main() ではコンパイル後プログラムは即実行されますが、コンソール画面上で実行する場合は、
int main( int argc, char** argv ) ではコンパイル後、コマンドラインに引数を1つ指定して実行します。
引数は画像名です。
プログラム終了は、何かのキー入力で終了します。

Mat img
img = imread()
画像を読み込みます。imread()関数は抽象度の高いルーチンでファイル名に基づいて読込むファイルの
フォーマットを決定します。また画像データ用クラスに必要なメモリー領域を自動的に確保します。
フォーマットは、BMP, DIB, JPEG, JPE, PNG, PBM, PGM, PPM, SR, RAS, TIFF等があります。

エラー処理は記述していませんが、
if ( img.empty() ) return -1;
これは画像が実際に読み込まれたかチェックします。

nameWindow ("Sample", WINDOW_AUTOSIZE );
画像の保持と表示が可能なウインドウを画面に表示します。
この関数は、HighGUI ライブラリーが提供するもので、ウインドウへの名前付けも行います。

imshow( "Sample", img );
画像を cv::Mat クラスに保持していれば、いつでも cv::imshow() で既存のウインドウに表示する事ができます。

cv::destroyWindow( "Sample" );
画像領域メモリー管理作業の自動開放します。


【Sample2】PC内臓カメラ画像表示
OpenCVを使った動画表示(再生)は、画像を1枚表示するのと同等です。
忘れてはいけないのは、ループを抜け出す処理を忘れない事です。


VideoCapture cap;
cap open(0);
open(0) のゼロは、内臓カメラを示しています。



【Sample3】動画表示


まずは、ビデオキャプチャオブジェクト cv::VideoCapture がインスタンス化され動画ファイルを扱う事が出来る
ようになります。
動画ファイルを開くと、その動画に関するすべての情報がこのオブジェクトに読み込まれます。
また同オブジェクトは、動画の開始位置を示すように初期化されます。
このプログラムでは、cv::Mat frame; が動画フレームを保存します。

for() ループの中に入ると、動画ファイルがビデオキャプチャーオブジェクトのデータストリームからフレームごと
に読み込まれます。

次にデータが実際読み込まれたかチェックします。
if ( frame.empty())
読込まれていない場合は終了します。
動画フレームがうまく読み込まれたら、cv::imshow() で表示します。

if ( ( char ) waitKey( 33 ) >= 0 ) break;
フレームを表示すると33ミリ秒待ちます。この間にユーザーがキーを押すと、この読み込みループから抜けます
押されない場合、33ミリ秒後に再びループを実行します。
終了時には、すべての確保されたデータはスコープの外に出たとき自動的に開放されます。



【Sample4】動画表示<場面指定スクロールバー付き>
ここでは、動画内を動き回ってみましょう。<動画表示・各種機能詳解>

 1.トラックバーによる動画移動
 2.Sキーを押すことで動画をステップ再生(コマ送り)
 3.Rキーで通常の再生モードに戻る
 4.トラックバーで移動した場合、ステップ再生モードにして一時停止させる

以上の機能は、HighGUI ツールキットの一部です。
特に役に立つ機能の1つは、トラックバーです。
トラックバーを作成するには、cv::createrTrackbar() を呼び出し、どのウインドウにトラックバーを表示したい
かを指定します。後はコールバックで移動するだけです。


<プログラム解説>
グローバル変数

 int g_slider_position = 0;
 int g_run = 1;
 int g_dontset = 0;

g_slider_position は、トラックバーのスライダー位置の状態を保持するグローバル変数です。
コールバックがビデオキャプチャオブジェクト g_cap にアクセスする必要があるのでグローバル変数にしています
g_run この値が0でない場合に新しいフレームを表示し続けます。また正の数は停止するまでに表示される
フレーム数を表示し、負の数はシステムが連続モードで再生いていることを示してします。
ユーザーがトラックバーをクリックして動画内を新しい場所にジャンプしようとしたら、g_run = 1 と セットし
動画がそこでステップ再生の状態で止まったままにする様にします。
ただ問題は、動画が進むにつれ動画の位置に合わせて表示ウインドウのトラックバーのスライダーの位置が進む
ようにしたいので、main プログラムでは新しい動画のフレームが得られるたびにトラックバーのコールバック関数
を呼び出しスライダーの位置を更新します。
しかし、このようなプログラム的なトラックバーコールバックの呼び出しでは、ステップ再生モードに入りたく
ないので、これを避けるために最後のグローバル変数 g_dontset を用意します。
これを用いることでステップ再生モードに切り替わることなくトラックバー位置を更新できます。

<関数:onTrackbarsSlide 説明>
これは、ユーザーがスライダーを操作したときに使われるコールバックルーチンを定義したものです。
このルーチンには新しいトラックバーの位置を表す 32 Bit 整数 pos が渡されます。
コールバックの中では、渡された新しい位置を使って、g_cap.set() で実際に動画の再生を新しい位置
に進めます。
ここの if () 文は、次の新しいフレームを表示したらステップ再生モードに入るためのものです。
これは、コールバックがユーザーのクリック操作から呼び出された場合にだけ変更され、main 関数から呼び出され
た場合(この場合、main 関数内で g_dontset が 1 にセットされている)は変更されない
g_cap.set() と g_cap.get() はペアで使用されます。これらの関数により cv::VideoCapture オブジェクトの
さまざまなプロパティーを設定(後者の場合は取得)できます。
ここでは引数に cv::CAP_PROP_POS_FRAMES を渡しています。
これは読込み位置をフレーム単位で設定したいことを示しています。

<前例から追加した部分の解説>
動画を開いた以降の最初の相違点は、g_cap.get() を使ってその動画のフレーム数、動画像の幅、高さを調べて
いる部分です。その後、これらの数値をコンソール画面に出力します。動画のフレーム数は、次のステップで
トラックバーを調整するために必要です。

(54行から60行)トラックバー本体の作成
cv::createTrackbar() は、トラックバーにラベル( この場合、position )を与えトラックバーを配置する
ウインドウを指定します。続いてトラックバーにバインドされる変数、トラックバーの最大値(動画
のフレーム数)、スライダーが動かされたときのコールバック(必要ない場合はNULL)を与えます。

(63行から)
for () ループでは動画フレームを読み込んで表示するのに加えて、動画内の位置を取得しトラックバー
のコールバックでステップ再生モードにならない様に g_dontset を設定してからコールバックを呼び出して
ユーザーに表示されるトラックバーの位置を更新します。
ここでグローバル変数 g_run が1減らされます。
これは次に説明するユーザーのキー入力で設定された以前の状態に応じてステップ再生モードにするか 動画再生
する効果を持ちます。

(76行から88行)
for () ループの最後の方で、ユーザーからのキー入力を調べます。
Sキー:ステップ再生モード(g_run = 1)に設定され単一フレームが読み込まれる。
Rキー:再生モード(g_run = -1)に設定され、以降は1ずつ減算さるのでどの様なフレーム数の動画でもこの値は
    負のままになります。
Escキー:プログラム終了。

cv::destroyWindow() で開放する処理は省略していますが本来必要です。