質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

1回答

3166閲覧

カメラの画像(Mat)をトリミングしてからOpenCVで処理を実行したい

m-mega

総合スコア56

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2022/03/18 10:24

実現したいこと

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)に戻すとエラーが解消されるので、サイズが原因であることは確かかと考えています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2022/03/20 18:32

最終的に Utils.matToBitmap を使って OpenCV の Mat を Android の Bitmap に変換して表示か何かしてるんだと思いますが、その際に Bitmap を変換元の Mat と同じサイズ (width, height) で作成する必要があると思います。
m-mega

2022/03/21 06:48

ご回答いただきありがとうございます。 なるほど、つまり処理自体はトリミングした状態で行うとして、最終的に表示する際には元のサイズに戻しておく必要があるということですね。 おっしゃる通り、現在のコードではカメラの映像を画面上にプレビューしておりますので、その際にエラーが出ているのかもしれません。 修正して検証してみます。
hoshi-takanori

2022/03/21 06:59

> 最終的に表示する際には元のサイズに戻しておく必要がある それでもいいですが、サイズ変更した Mat を表示したいなら、Mat と同じサイズの Bitmap を作れば良いってことです。具体的には、 val newBitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888) Utils.matToBitmap(mat, newBitmap) みたいな。
m-mega

2022/03/21 12:02

なるほど。ご丁寧にありがとうございます! 明日早速試してみたいと思います。
guest

回答1

0

自己解決

hoshi-takanori様にご指摘いただいた方法で修正を行うことで、無事にトリミングしたMatで処理を行うことができました。
また、トリミング後のMatをBitmapに変換することで、トリミング後の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, 200, 200) // トリミングサイズ指定(x始点,y始点,横幅,縦幅) 11 mMatTrim = Mat(mMatRed, roi) // トリミング 12 13 // 赤色の輪郭面積に応じた処理を実行 14 detectRed(mMatTrim!!) 15 16 val newBitmap = Bitmap.createBitmap(mMatTrim!!.width(), mMatTrim!!.height(), Bitmap.Config.ARGB_8888) 17 Utils.matToBitmap(mMatTrim, newBitmap) // プレビュー表示用にトリミングしたmatをbitmapに変換 18 return mMatTrim 19 }

投稿2022/03/22 00:49

m-mega

総合スコア56

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問