2024.1.17 2020.5.3 Coskx Lab
OpenCV4.9.0 までは,android-sdkを使って,カメラプレビューするアプリを作ると,縦位置の表示に不具合がありました。
4.1 OCVprePlus2.zipのダウンロード
次のところから,OCVprePlus2.zipをダウンロードします。
OCVprePlus2.zipのダウンロード
zipを展開すると次のファイルが現れます。
・AndroidManifest.xml
・CameraBridgeViewBase49.java
・JavaCamera2ViewPlus.java
・MainActivity.java
・activity_main.xml
5.1 幅・高さの概念と,画像データ
説明の準備として,4つの「幅×高さ」の概念を使います。
デバイススクリーンの幅・高さの概念はデバイスの向きによって変化するはずです。
しかしカメラの幅・高さはカメラに固定した概念になっていて,変化しません。
JavaCamera2View.javaとJavaCamera2ViewPlusの持つ4つのサイズ概念について見てみます。
また,カメラ画像データはpreviewsizeの横長長方形で得られるので,デバイスを横向き(landscape)の時にはそのまま使えますが,
縦向き(portrait)の時には,90度回転した画像データを受け取ることになり,画像データを90度回転させないと正しく表示できません。
5.2 JavaCamera2View.java(OpenCV4.9.0以前)での作業
(OpenCVモジュールに付属,横位置のみで正しく動作)
「5.1」の4つのサイズに関する作業はすべてconnectCamera()で行われています。
connectCameraはconnectCamera(int width, int height)のように呼び出されますが,このwidth,heightがavailablesizeです。
calcPreviewSize()でavailablesizeをもとに,カメラにとって都合の良いpreviewsizeを設定してもらいます。(ここに問題があります)
そして,previewsizeをそのままframesizeにしています。(ここにも問題があります)
その後,framesizeをdisplaysizeにするための倍率mScaleを求めています。
CameraBridgeViewBase中でmScale倍されてdisplaysizeに変換されてスクリーンのユーザー領域に表示されます。
この操作においては,デバイスを横向きにした場合にはデバイスの幅・高さとカメラの幅・高さの概念は共通ですが,
デバイスを縦にしたときは,デバイスでの幅・高さの概念とカメラの幅・高さの概念がずれてしまうので,正しいpreviewsizeを選ぶことができず,
また受け取った画像データも90度回転したものを受け取るため,うまく動作しません。
5.3 JavaCamera2ViewPlus.javaでの作業
(横位置・縦位置で正しく動作するように修正したもの)
「5.1」の4つのサイズに関する修正作業はすべてconnectCamera()内で行われています。
connectCameraはconnectCamera(int width, int height)のように呼び出されますが,このwidth,heightがavailablesizeです。
width<heightの値がえられたら,デバイスが縦位置にあることがわかります。
デバイスが縦位置のときは,calcPreviewSize()に与える幅・高さは幅>高さでなければならないので,availablesizeの幅・高さを
longside,shortsideとしてcalcPreviewSize()に与える準備をします。
そして,longside,shortsideをもとに,カメラにとって都合の良いpreviewsizeをcalcPreviewSize(longside, shortside);で設定してもらいます。
ただし,previewsize(CameraBridgeViewBaseの変数)を設定しているところはここでは見えません。
求められたpreviewsize(CameraBridgeViewBaseの変数)は必ず幅>高さなので,デバイスが横位置のときは,framesizeにはpreviewsizeの値をそのまま使い,
デバイスが縦位置のときは,framesizeにはpreviewsizeを幅・高さを逆にして使います。
その後,availablesizeとframesizeから,framesizeをdisplaysizeにするための倍率mScaleを求めています。
ここはJavaCamera2View.javaと同じ記述になります。
デバイスが縦位置のときは,得られた画像データを90度回転して表示します。(回転すると幅・高さがframesizeに一致します。)
画像の回転は,JavaCamera2ViewPlus.javaのclass JavaCamera2Frame内で行います。
画像がgrayscaleのときは,Mat gray()で,rgbaのときはMat rgba()で画像の回転を行っています。
CameraBridgeViewBase中でmScale倍されてdisplaysizeに変換されてスクリーンのユーザー領域に表示されます。
スクリーンのユーザ領域の縦横比とカメラの制約によるプレビューサイズの縦横比が異なるため,
表示に余白が生じます。画像処理用途では,「画面の余白(余黒?)をなくして,全画面に表示したい」
という要求はないと思いますが,見かけはきれいな方がよいと思う場合もあるでしょう。
その場合は
表示倍率を大きくして,プレビューの一部を捨てると全画面表示にすることができます。
JavaCamera2ViewPlus.javaのメソッドconnectCamera()で,
Math.min(((float)height)/mFrameHeight, ((float)width)/mFrameWidth);
を
Math.max(((float)height)/mFrameHeight, ((float)width)/mFrameWidth);
に変更することで実現できます。表示倍率を大きくして,縦横で長い側に合うようにして表示画像を画面からはみ出させています。
これは表示が変わるだけで,MainActivityのonCameraFrame()で扱っている元のMatの画像情報は変化しません。
デバイスを縦位置にして使っても横位置にして使っても,常に正しい向きで表示させるには,「
デバイスの回転角」,「カメラ光軸の方向(カメラの正面方向)」,「カメラ(イメージセンサ)の設置角
(デバイスに対してどの向きにカメラがついているか)」の3つの値が必要になります。
デバイスの回転角 通常の使い方では,デバイスを縦(portrait)に構えるので,それが初期位置で0度になります。
デバイスを時計回りに倒して横向き(landscape)にしたときが90度,初期位置から反時計回りに倒して横向きにしたときが270度になります。
カメラ光軸の方向 バックカメラの向きを0度としたとき,フロントカメラの向きは180度と考えるのが都合がよいでしょう。
カメラ(イメージセンサ)の設置角 イメージセンサは横向き(landscape)についているので,
横長カメラの上辺がデバイスの3時方向のときカメラの設置角は90度,デバイスの9時方向のとき270度になります。
この3つの情報を得て,画像を回転させる角度を求めているのが,クラスJavaCamera2ViewPlusのメソッドgetRotationIndex()になります。
この関数の返す値は,角度を90で除した値が使われています。
そしてこの「回転させる角度」を使って,rgb()あるいはgray()のメソッド中で,
画像(Mat形式の画像)を正しく回転させて表示に使えるようにしています。
OpenCV4.9.0-sdk以前を利用していた縦位置でも横位置でも正しくカメラプレビューできるアプリを作っていました。 OpenCV4.10.0-sdkの元でも利用できるアプリに修正しました。これは,以前のアプリの保守のための説明です。
非推奨になった記述が,
・CameraBridgeViewBase.java
・JavaCamera2View.java
にあり,それらは
・CameraBridgeViewBase49.java
・JavaCamera2ViewPlus.java
で修正されています。
結局OpenCV4.10.0-sdkの OpenCV > java\src > org > opencv > androidの中で使用しているのは,
・FpsMeter.java
・OpenCVLoader.java
・StaticHelper.java
・Utils.java
のみであり,
・Camera2Renderer.java
・CameraActivity.java
・CameraBridgeViewBase.java
・CameraGLRendererBase.java
・CameraGLSurfaceView.java
・CameraRenderer.java
・JavaCamera2View.java
・JavaCameraView.java
・NativeCameraView.java
は使用していません。