2024.1.20 2020.6.6 Coskx Lab
OpenCVを使って,横位置でも縦位置でも正しく表示するデモを作成しましたが,イメージデータの回転が結構負荷が重く,回転を必要とするときには極端にFPSが低下します。
いくらネイティブのメソッドが行っているといってもCPUの限界が見えてしまいます。
そこでGPUを使って回転表示させると速くなることから,OpenGLを用いるアイディアがネット上で散見されました。
ここでは横位置でも縦位置でも正しく表示するデモにおいて回転・表示部にOpenGLを用いることにしました。
OpenGLのスクリーンで矩形にカメラ画像をテクスチャとして貼り付けたものを表示しています。
(OPenCVのライブラリ中にCameraGLRendererBase,CameraGLSurfaceViewがあるので,これらが利用できれば,簡単だと思いますが,検討中です。)
(OPenCLを使用したもう少しましな方法があるようです。)
まとめのところに書きましたが,最近のハードウエアの進歩のため,OpenGLの援用によるご利益は限定的です。
(1)AndroidManifast.xmlにカメラpermission関連の3行を加えます(ダウンロードされたAndroidManifast.xml参照)。
(2)activity_main.xmlを置き換えます。
(3)MainActivity.javaをダウンロードしたファイルで置き換えます。パッケージ名は自動的に揃えられます。
(4)残りのjavaファイルをすべてMainActivity.javaと同じところにペーストします。パッケージ名は自動的に揃えられます。
(5)activity_main.xml内の「JavaCamera2ViewGL」のパッケージ名をこのプロジェクトのパッケージ名に変更します。
カメラのフレームサイズやBitmapの大きさ,拡大率を適正なものにするための変更が
JavaCamera2ViewGL.java
で行われています。
CameraBridgeViewBaseGLでは,次のように流れを変更しています。
カメラが取得したイメージデータがdeliverAndDrawFrame()まで届けられるところまでは,CameraBridgeViewBaseと同じです。
その後の作業はこのdeliverAndDrawFrame()で行われます。
(1)MainActivityのonCameraFrame()に送って,その戻り値としてMat画像を受け取ります。
(デバイスの向きによって画像回転が必要な場合でも,まだ回転していないため,
onCameraFrame()でのMatを作成するところでは向きがおかしなままの作業になります)
(2)受け取ったMatからBitmapを作った後,BitmapをGLRendererに送ります。
(Canvasに貼り付ければそのままOpenCVのビューに表示されてしまいますが,そうしません。)
(3)GLRendererでは,受け取ったBitmapを,四角形に貼り付けて表示します。
ゲームなどの用途では,表示メソッドのonDrawFrame()はOpenGLのタイミングで繰り返し実行されますが,ここでは
CameraBridgeViewBaseGLがBitmapを送ったときにonDrawFrame()が起動するよう
に,requestRender()で要求しています。
また,CameraBridgeViewBaseGLとGLRenderer間でBitmapを送るタイミングの調整をしています。
GLRenderer.javaの最初に,int texturemodeを決めるところがあります。
最近のデバイスではtexturemode=0で問題ないと思います。
イメージサイズが正方形で2のべき乗でなければ動かない古いデバイスでは,texturemode=1にします。
texturemode=1は,カメラ画像を正方形に変形させたイメージを作り,正方形のテクスチャに変換し,正方形をもとのイメージ比率に戻して表示しする動作モードです。
補足
CameraBridgeViewBaseGLはSurfaceViewでなくGLSurfaceViewを継承しています。そのため,
surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3)
surfaceCreated(SurfaceHolder holder)
surfaceDestroyed(SurfaceHolder holder)
の3つのメソッドでは,継承元のGLSurfaceViewの3つのメソッドをそれぞれ呼び出す必要があります。この3つのメソッドを呼び出すことででGLRendererの
onSurfaceCreated(GL10 gl10,EGLConfig eglConfig)
onSurfaceChanged(GL10 gl10,int w,int h)
onDrawFrame(GL10 glUnused)
が呼び出されます。
デバイスを横向きにした場合は,OpenCVでそのまま表示したのと,OpenGLで表示させたものとでは,FPSはあまり変わりません。
デバイスを縦向きにしたとき,OpenCVでそのまま表示すると,表示速度(FPSで比較)が約半分に落ちたのですが,
OpenGLを使って表示するとGPUの働きのため,横向きのときと表示速度は変わりませんでした。
例えばOpenGLを使うと,Nexus5xでは横向き縦向きともに30FPS,P10Liteでは横向き縦向きともに25FPSでした。
デバイス Nexus5x ランドスケープ | |
「pure OpenCV」27FPS @1280x768 | 「OpenCV+OpenGL」30FPS @1280x768 |
デバイス Nexus5x ポートレート | |
「pure OpenCV」14FPS @1080x1440 | 「OpenCV+OpenGL」30FPS @1080x1440 |
デバイス p10lite ランドスケープ |
|
「pure OpenCV」25FPS @1280x720 | 「OpenCV+OpenGL」25FPS @1280x720 |
デバイス p10lite ポートレート |
|
「pure OpenCV」15FPS @1080x1440 | 「OpenCV+OpenGL」25FPS @1080x1440 |
Pixel4aを入手したので,これもテストしてみました。
デバイス Pixel5a | |||
方向 | 画像サイズ ( W x H ) | 方式 | FPS |
ランドスケープ | 1280 x 720 | 「pure OpenCV」 | 30 |
ランドスケープ | 1280 x 720 | 「OpenCV+OpenGL」 | 30 |
ポートレート | 1080 x 1440 | 「pure OpenCV」 | 30 | ポートレート | 1080 x 1440 | 「OpenCV+OpenGL」 | 30 |
ランドスケープ | 1920 x 960 | 「pure OpenCV」 | 30 |
ランドスケープ | 1920 x 960 | 「OpenCV+OpenGL」 | 30 |
ポートレート | 1080 x 1920 | 「pure OpenCV」 | 26 | ポートレート | 1080 x 1920 | 「OpenCV+OpenGL」 | 30 |
というわけで,ハードウエアの進歩のため,最大解像度の縦位置を除けば,OpenGLの援用は意味がないようでした。
OpenCVカメラプレビューで,スクリーン表示部分をOpenGLにまかせるようにしたデモを作成しました。
非力なデバイスでは,デバイスを縦向きで使ってもOpenGLの援用によって表示速度が低下しないようになりました。
デバイスを縦向きで使うときは,どうしてもイメージを90度回転しなければならないため,この作業をCPUが行うのかGPUが行うのかで差が出てきた結果と思われます。
最近のハードウエアの進歩のため,OpenGLの援用は必要ではなくなったようです。