忍者ブログ

Fグループ電子工作講座

秋月電子SH7125ボードで始めるマイコン開発

ラズパイのOpenCV処理を速くする

関連記事
Raspbianのインストール
RaspberryPiへOpenCVを導入オフラインで導入
ラズパイ専用カメラをOpenCVで使う

ラズベリーパイへOpenCVを自分でビルドしてインストールするとOpenCV標準の
サンプルプログラムを利用できます。
インストールフォルダを/home/pi/opencv/とした場合、Python用であればサンプルプログラムは
/home/pi/opencv/samples/python
の中にあります。

USBカメラや専用カメラを接続してOpenCV定番の顔検出facedetect.pyを実行すれば

この様にリアルタイムで顔検出ができます。

ただ、サンプルプログラムだと少々処理に遅延があり、時間経過と共に遅延が大きくなって
いきます。
今回はこの遅延を解消して使い勝手を良くします。


遅延の原因

遅延の原因は大きく2種類に分ける事が出来ます。
1)CPUの性能に対して処理が重すぎる(処理する映像の解像度が高すぎる)
2)時間経過で映像のバッファが溜まる

1)CPUの性能に対して処理が重すぎる

USBカメラや専用カメラを接続してサンプルプログラムを実行すると640×480(所謂VGA画質)
で処理が実行されます。
画像処理プログラムではこの640×480=約30万個(30万画素、0.3メガピクセル)の点に対して
処理を行っています。処理の内容にもよりますが、ラズパイでリアルタイム実行させるには
画素数が多いので画素数を減らすことで処理を軽くします。

2)時間経過で映像のバッファが溜まる

ラズパイでサンプルプログラムを実行すると30フレーム毎秒(30fps、処理間隔33.33ms)で
カメラから画像を取得してラズパイ内のバッファに一時保存されます。画像を読み出す際
にはこのバッファの画像が使用されます。
基本的にバッファ内の画像は古い順に使用されます。
 
カメラから画像が取得されてから実際に使用されるまでに時間差があるため、これが
遅延に繋がります。

処理が終わった時点で画像を取得できれば良いのですが、処理させる内容によるものの
ガッツリ処理を行うと処理時間が33.33msを超えてしまい、画像取得周期に追いつけません。
時間経過につれて使いきれない画像がバッファに溜まって遅延が大きくなっていきます。
 

画像処理自体は滑らかに動いている様に見えるのに遅延が発生するのはこれが原因です。

リアルタイム処理を目的とする場合、多少コマ落ちしても最新の画像を処理したいので
バッファを使い切るように設定変更して遅延を解消します。


画像取得機能

さて、設定変更をするためにサンプルプログラムを見ると画像取得の個所でなにやら色々
書かれています。これはカメラを接続していない場合に自動的にjpgファイルを開いたり、
ターミナルからjpg画像をしていする機能を実現するためです。

よく見ると、

 cam = create_capture(※中身省略)

でカメラを使用している様ですが、細かい内容は見当たりません。
結局のところ、ビデオ取得関連は別のプログラムに記載されています。

facedetect.py内でcreate_captureを検索するとプログラムの冒頭で

 from video import create_capture

との記述が見つかります。これは
videoというプログラムからcreate_captureという関数を利用可能にする」
という意味です。
という事で詳細を確認するにはvideo.pyを見る必要があります。

facedetect.pyと同じフォルダにあるvideo.py内でcreate_captureを検索すると

 def create_capture(※中身省略):

が見つかり、これが問題の個所です。
色々書いてありますが、returnするのがcapなのでcap関連のみ設定できれば動かせます。

 cap = cv.VideoCapture(source)

をベースに色々設定します。

ただ、video.pyfacedetect.py以外にもいろいろなプログラムで参照されているため、
編集すると他のプログラムが動かなくなる可能性があります。
そこで、facedetect_lite2.py(名前は適当)を新規に作成して各種機能を移植します。

最終的に出来上がったサンプルプログラム

facedetect_lite2.py

以下はこのプログラムの解説となります。


解像度を変更する

カメラから画像を取得する際の解像度(画素数)を変更する際は

 cap.set(cv.CAP_PROP_FRAME_WIDTH, 320)
 cap.set(cv.CAP_PROP_FRAME_HEIGHT, 240)

の様に設定します。
指定する数字は何でも良い訳ではなく、カメラが対応している解像度を指定する必要が
あります。画面表示する必要がない場合は取得する時点で解像度を下げて良いでしょう。

取得して表示する解像度はそのままで、処理を行う画像のみ解像度を下げる事もできます。

サンプルだと
 ret, img = cam.read()
で取得したカラー画像を
 gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
でBGRカラー画像からグレースケールへ変換後
 gray = cv.equalizeHist(gray)
にてヒストグラム均一化した後に
 rects = detect(gray, cascade)
にて検出を行っています。

検出を実行する前に

 gray = cv.resize(gary,(320,240))

等サイズ変更すれば処理を軽くする事ができます。
ただし、顔を検出した際に出力される長方形のスケールが異なるので、長方形を描画する
際にはスケールを変更する必要があります。


バッファ数を変更する

最新の画像を取得するために
 img = cam.read()
を数回実行してバッファから画像を減らしたり
 cap.set(CV,CAP_PROP_FPS, 15)
で取得周期を変えることもできますが、
今回はバッファが設定されている事自体が問題なので、バッファ数を変更します。
参考:GitHub門馬様

バッファ数は

 cap.set(CV,CAP_PROP_BUFFERSIZE, 1)

にて変更可能です。
この機能が実装された当初はIEEE1394接続のカメラのみ利用可能な機能だったそうですが、
OpenCV3.4.0以降はUSBカメラでもラズパイ専用カメラでも設定変更可能な様です。


ちなみにWindows環境であればビデオの取得にDirextXを使う方法もあります。
参考:EWCLIB(DirectXでビデオキャプチャするためのライブラリ)
Windows+C言語環境でOpenCVを使用する際はEWCLIBを利用させていただいています。
PR

コメント

プロフィール

HN:
ぼんどF博士
性別:
男性

最新コメント

[02/26 とし]
[02/25 ぼんどF博士]
[02/25 とし]
[10/25 ぼんどF博士]
[10/18 ss400]