実現したいこと
Androidスマホのカメラから取得した画像をトリミングして、デフォルトでは画像全体になっているOpenCVの検出範囲を、トリミングした限定的な範囲に変更したいと考えています。
言語は「kotlin」を使用しています。
理想とする処理としては下記のようなものになります。
Python+OpenCVで範囲指定して画像処理を行う方法
画像の一部を切り抜いて保存する
OpenCV with Java 処理事例
できていること
現在、下記のコードでカメラの画像からオレンジ色を検出して輪郭を表示し、認識されたオレンジ色の面積に応じてアクティビティの背景色を変更する処理を実現しています。
kotlin
1package jp.gr.java_conf.coskx.targetinggame 2 3import android.Manifest 4import android.annotation.TargetApi 5import android.content.ContentValues.TAG 6import android.content.Intent 7import android.content.pm.PackageManager 8import android.graphics.Color 9import android.os.Build 10import android.os.Bundle 11import android.util.Log 12import android.view.View 13import android.widget.Button 14import androidx.appcompat.app.AppCompatActivity 15import org.opencv.android.CameraBridgeViewBase 16import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2 17import org.opencv.core.* 18import org.opencv.imgproc.Imgproc 19 20 21class MainActivity : AppCompatActivity(), CvCameraViewListener2 { 22 private var mOpenCvCameraView: CameraBridgeViewBase? = null 23 override fun onStart() { 24 super.onStart() 25 var havePermission = true 26 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 27 if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { 28 requestPermissions( 29 arrayOf(Manifest.permission.CAMERA), 30 GamePlayActivity.CAMERA_PERMISSION_REQUEST_CODE 31 ) 32 havePermission = false 33 } 34 } 35 if (havePermission) { 36 mOpenCvCameraView!!.setCameraPermissionGranted() 37 } 38 } 39 40 @TargetApi(Build.VERSION_CODES.M) 41 override fun onRequestPermissionsResult( 42 requestCode: Int, 43 permissions: Array<String>, 44 grantResults: IntArray 45 ) { 46 if (requestCode == GamePlayActivity.CAMERA_PERMISSION_REQUEST_CODE && grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 47 mOpenCvCameraView!!.setCameraPermissionGranted() 48 } 49 super.onRequestPermissionsResult(requestCode, permissions, grantResults) 50 } 51 52 override fun onCreate(savedInstanceState: Bundle?) { 53 super.onCreate(savedInstanceState) 54 setContentView(R.layout.activity_main) 55 56 // ボタンを押したらゲーム画面へ 57 val btnStart :Button = findViewById(R.id.btnStart) // Viewの取得 58 btnStart.setOnClickListener { 59 val intent = Intent(this,GamePlayActivity::class.java) // 遷移元と遷移先の設定 60 startActivity(intent) // 遷移の実行 61 overridePendingTransition(0, 0) // 遷移時のアニメーションを消す 62 } 63 64 // OpenCVの設定 65 System.loadLibrary("opencv_java4") 66 mOpenCvCameraView = findViewById<View>(R.id.camera_view) as CameraBridgeViewBase 67 mOpenCvCameraView!!.setCvCameraViewListener(this) 68 // 画像回転作業負荷を低減するためpreviewの解像度を設定 69 mOpenCvCameraView!!.setMaxFrameSize(960, 720) 70 } 71 72 public override fun onResume() { 73 super.onResume() 74 mOpenCvCameraView!!.enableView() 75 } 76 77 public override fun onDestroy() { 78 super.onDestroy() 79 if (mOpenCvCameraView != null) mOpenCvCameraView!!.disableView() 80 } 81 82 override fun onCameraViewStarted(width: Int, height: Int) { 83 mMatRed = Mat(height, width, CvType.CV_8UC4) 84 } 85 override fun onCameraViewStopped() { 86 mMatRed?.release() 87 } 88 89 private var mMatRed: Mat? = null 90 91 private fun detectRed(img:Mat):Boolean { 92 // 赤色を抽出 <Core.inRange(元Mat, 取得HSVの下限, 取得HSVの上限, 変換後Mat)> 93 Core.inRange(img, Scalar(5.0, 100.0, 100.0), Scalar(15.0, 255.0, 255.0), img) 94 // 赤色の輪郭の面積に応じた処理を実行 95 return compareContourRed(detectContour(img)) 96 } 97 98 private fun detectContour(img:Mat): Int { // 輪郭の情報を取得 99 // ガウシアンフィルタで画像をぼかす 100 Imgproc.GaussianBlur(img, img, Size(5.0, 5.0), 5.0) 101 // 輪郭を取得 102 Imgproc.Canny(img, img, 10.0, 360.0) 103 // 輪郭の情報を取得 104 val contours: List<MatOfPoint> = ArrayList() 105 val hierarchy = Mat.zeros(Size(5.0, 5.0), CvType.CV_8UC1) 106 Imgproc.findContours(img, 107 contours, // 輪郭情報を格納 108 hierarchy, // 階層構造を格納 109 Imgproc.RETR_EXTERNAL, // 輪郭抽出モードを指定 110 Imgproc.CHAIN_APPROX_SIMPLE // 輪郭の表示方法を指定 111 ) 112 return contours.size 113 } 114 115 private fun compareContourRed(size:Int):Boolean { // 取得した赤色の輪郭サイズを比較 116 Log.i(TAG, "=================赤色比較:${size} ====================") 117 var maxarea = 20.0 118 val viewStart : View = findViewById(R.id.viewStart) // スタート画面の背景の取得 119 if(size > maxarea){ 120 // スタート画面の背景色を赤色に変更 121 viewStart?.setBackgroundColor(Color.RED) 122 return true 123 } 124 else { 125 viewStart?.setBackgroundColor(Color.WHITE) 126 return false 127 } 128 } 129 130 131 override fun onCameraFrame(inputFrame: CameraBridgeViewBase.CvCameraViewFrame): Mat? { 132 // カメラプレビューのフレームをフルカラーで取得 133 mMatRed = inputFrame.rgba() 134 135 // 画像をHSVに変換 136 Imgproc.cvtColor(mMatRed, mMatRed, Imgproc.COLOR_RGBA2BGR) // MatをRGBAからBGRに変換 137 Imgproc.cvtColor(mMatRed, mMatRed, Imgproc.COLOR_BGR2HSV) // MatをBGRからHSVに変換 138 139 // Matを指定範囲でトリミングしてOpenCVの検出範囲を限定的にする 140// val roi = Rect(0,0, mMatRed!!.width(), mMatRed!!.height()) 141// mMatRed = Mat(mMatRed, roi) 142 143 // 赤色の輪郭面積に応じた処理を実行 144 detectRed(mMatRed!!) 145 return mMatRed 146 } 147}
やったこと
前述したサイトを参考に、画像(Mat)をトリミングしようと下記のようにコードを作成しました。
(該当箇所はコメントアウトしている部分になります。)
kotlin
1override fun onCameraFrame(inputFrame: CameraBridgeViewBase.CvCameraViewFrame): Mat? { 2 // カメラプレビューのフレームをフルカラーで取得 3 mMatRed = inputFrame.rgba() 4 5 // 画像をHSVに変換 6 Imgproc.cvtColor(mMatRed, mMatRed, Imgproc.COLOR_RGBA2BGR) // MatをRGBAからBGRに変換 7 Imgproc.cvtColor(mMatRed, mMatRed, Imgproc.COLOR_BGR2HSV) // MatをBGRからHSVに変換 8 9 // Matを指定範囲でトリミングしてOpenCVの検出範囲を限定的にする 10// val roi = Rect(0,0, mMatRed!!.width(), mMatRed!!.height()) 11// mMatRed = Mat(mMatRed, roi) 12 13 // 赤色の輪郭面積に応じた処理を実行 14 detectRed(mMatRed!!) 15 return mMatRed 16 }
Rectの範囲を元となる画像(Mat)と同じサイズに設定しています。
そのため、結果としては何もトリミングされないままカメラのプレビューが全画面表示され、検出した色に応じて処理が実行されます。
わからないこと
前述したサイトのように範囲を小さくしてトリミングを行おうとするとエラーが発生してしまい、アプリが強制終了してしまうのですが、何が原因なのでしょうか。
エラー内容
Rectのサイズを小さくすると下記のエラーが発生します。
E/cv::error(): OpenCV(4.5.5) Error: Assertion failed (src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols) in Java_org_opencv_android_Utils_nMatToBitmap2, file /build/master_pack-android/opencv/modules/java/generator/src/cpp/utils.cpp, line 101 E/org.opencv.android.Utils: nMatToBitmap caught cv::Exception: OpenCV(4.5.5) /build/master_pack-android/opencv/modules/java/generator/src/cpp/utils.cpp:101: error: (-215:Assertion failed) src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols in function 'Java_org_opencv_android_Utils_nMatToBitmap2' E/CameraBridge: Mat type: Mat [ 100*100*CV_8UC3, isCont=false, isSubmat=true, nativeObj=0x739f63fa80, dataAddr=0x7384e0d000 ] E/CameraBridge: Bitmap type: 480*864 E/CameraBridge: Utils.matToBitmap() throws an exception: OpenCV(4.5.5) /build/master_pack-android/opencv/modules/java/generator/src/cpp/utils.cpp:101: error: (-215:Assertion failed) src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols in function 'Java_org_opencv_android_Utils_nMatToBitmap2' E/cv::error(): OpenCV(4.5.5) Error: Assertion failed (src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols) in Java_org_opencv_android_Utils_nMatToBitmap2, file /build/master_pack-android/opencv/modules/java/generator/src/cpp/utils.cpp, line 101 A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4000 in tid 3074 (OpenCVCameraBac), pid 2978 (x.targetinggame)
要約すると、下記のように記載されているように見受けられます。
nMatToBitmapが例外をキャッチした。 「Java_org_opencv_android_Utils_nMatToBitmap2」関数内では 「 src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols」という条件を満たす必要がある。 BitMapのサイズは480*864にする。
実際、Rectのサイズを元の画像のサイズ(480*864)に戻すとエラーが解消されるので、サイズが原因であることは確かかと考えています。
回答1件
あなたの回答
tips
プレビュー