#実現したいこと
Android Studio上でOpenCVを使用して
カメラからの映像を2値化(黒とそれ以外)表示して、ある矩形領域における変動する黒色の割合(ピクセル数でも可)を表示したい
#開発・実行環境
[開発]
Android Studio 4.0.1
MacBook Pro (macOS Mojave ver10.14.6)
-言語- java
[実行]
OPPO Reno A 128GB
#発生している問題・エラーメッセージ
・黒要素とそれ以外に分けて表示する
・矩形領域を指定する
以上のことは出来たのですが
OpenCVのcountNonZero
を使用して黒のピクセル数を数えるという所で
countNonZero
関数を書き込んだ時点でアプリ起動直後にアプリ強制終了が起きて
アプリを起動することが出来ません。
エラーメッセージは特になく、書き込めはするけど起動は出来ないと言った感じです。
#該当のソースコード
java
1package com.example.line; 2 3import androidx.annotation.NonNull; 4import androidx.appcompat.app.AppCompatActivity; 5import androidx.camera.core.Camera; 6import androidx.camera.core.CameraSelector; 7import androidx.camera.core.ImageAnalysis; 8import androidx.camera.core.ImageProxy; 9import androidx.camera.core.Preview; 10import androidx.camera.lifecycle.ProcessCameraProvider; 11import androidx.camera.view.PreviewView; 12import androidx.core.app.ActivityCompat; 13import androidx.core.content.ContextCompat; 14import androidx.lifecycle.LifecycleOwner; 15 16import android.Manifest; 17import android.app.Activity; 18import android.content.Context; 19import android.content.pm.PackageManager; 20import android.graphics.Bitmap; 21import android.os.Bundle; 22import android.util.Log; 23import android.view.Surface; 24import android.widget.ImageView; 25 26import com.google.common.util.concurrent.ListenableFuture; 27 28import org.opencv.android.Utils; 29import org.opencv.core.Core; 30import org.opencv.core.CvType; 31import org.opencv.core.Mat; 32import org.opencv.core.Point; 33import org.opencv.core.Rect; 34import org.opencv.core.Scalar; 35import org.opencv.imgproc.Imgproc; 36 37import java.nio.ByteBuffer; 38import java.util.concurrent.ExecutorService; 39import java.util.concurrent.Executors; 40 41import static org.opencv.imgproc.Imgproc.medianBlur; 42 43public class MainActivity extends AppCompatActivity { 44 /*** Fixed values ***/ 45 private static final String TAG = "MyApp"; 46 private int REQUEST_CODE_FOR_PERMISSIONS = 1234;; 47 private final String[] REQUIRED_PERMISSIONS = new String[]{"android.permission.CAMERA", "android.permission.WRITE_EXTERNAL_STORAGE"}; 48 49 /*** Views ***/ 50 private PreviewView previewView; 51 private ImageView imageView; 52 /*** For CameraX ***/ 53 private Camera camera = null; 54 private Preview preview = null; 55 private ImageAnalysis imageAnalysis = null; 56 private ExecutorService cameraExecutor = Executors.newSingleThreadExecutor(); 57 58 int x; 59 int y; 60 61 static { 62 System.loadLibrary("opencv_java4"); 63 } 64 65 @Override 66 protected void onCreate(Bundle savedInstanceState) { 67 super.onCreate(savedInstanceState); 68 setContentView(R.layout.activity_main); 69 70 previewView = findViewById(R.id.previewView); 71 imageView = findViewById(R.id.imageView); 72 73 if (checkPermissions()) { 74 startCamera(); 75 } else { 76 ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_FOR_PERMISSIONS); 77 } 78 } 79 80 private void startCamera() { 81 final ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); 82 Context context = this; 83 cameraProviderFuture.addListener(new Runnable() { 84 @Override 85 public void run() { 86 try { 87 ProcessCameraProvider cameraProvider = cameraProviderFuture.get(); 88 preview = new Preview.Builder().build(); 89 imageAnalysis = new ImageAnalysis.Builder().build(); 90 imageAnalysis.setAnalyzer(cameraExecutor, new MyImageAnalyzer()); 91 CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build(); 92 93 cameraProvider.unbindAll(); 94 camera = cameraProvider.bindToLifecycle((LifecycleOwner)context, cameraSelector, preview, imageAnalysis); 95 preview.setSurfaceProvider(previewView.createSurfaceProvider(camera.getCameraInfo())); 96 } catch(Exception e) { 97 Log.e(TAG, "[startCamera] Use case binding failed", e); 98 } 99 } 100 }, ContextCompat.getMainExecutor(this)); 101 } 102 103 private class MyImageAnalyzer implements ImageAnalysis.Analyzer { 104 private Mat matPrevious = null; 105 106 @Override 107 public void analyze(@NonNull ImageProxy image) { 108 /* Create cv::mat(RGB888) from image(NV21) */ 109 Mat matOrg = getMatFromImage(image); 110 /* Fix image rotation (it looks image in PreviewView is automatically fixed by CameraX???) */ 111 Mat mat = fixMatRotation(matOrg); 112 medianBlur(mat,mat, 5); 113 //黒とそれ以外に分ける 114 Imgproc.cvtColor(mat,mat, Imgproc.COLOR_RGBA2BGR); 115 Imgproc.cvtColor(mat,mat, Imgproc.COLOR_BGR2HSV); 116 Core.inRange(mat, new Scalar(0,0,0), new Scalar(179,128,100),mat); 117 Imgproc.cvtColor(mat,mat, Imgproc.COLOR_GRAY2BGRA); 118 //任意の領域をトリミングする(黒要素を数えるため) 119 Rect roi = new Rect(10, 10, 100,100); 120 Mat trim = new Mat(mat, roi); 121 //トリミングした範囲でcountNonZeroを行い、int型のxに代入する 122 x = Core.countNonZero(trim); 123 //xを文字列に変換させる(putTextでは文字のみを扱っているため) 124 //他に数値を表示させる方法があれば使わなくても良い 125 String y = String.valueOf(x); 126 //一応ログ出しとく 127 Log.i(TAG, "[analyze] width = " + image.getWidth() + ", height = " + image.getHeight() + "Rotation = " + previewView.getDisplay().getRotation()); 128 Log.i(TAG, "[analyze] mat width = " + matOrg.cols() + ", mat height = " + matOrg.rows()); 129 //どこをトリミングしているか画面を見れば分かるように同じ範囲に線を引く 130 Imgproc.rectangle(mat, new Rect(10, 10, 100, 100), new Scalar(255, 0, 0)); 131 //countNonZeroで数えたxを文字列に変えたyを代入 132 Imgproc.putText(mat, y , new Point(10, 10), 1, 1, new Scalar(255, 0, 0)); 133 /* Convert cv::mat to bitmap for drawing */ 134 Bitmap bitmap = Bitmap.createBitmap(mat.cols(), mat.rows(),Bitmap.Config.ARGB_8888); 135 Utils.matToBitmap(mat, bitmap); 136 137 /* Display the result onto ImageView */ 138 runOnUiThread(new Runnable() { 139 @Override 140 public void run() { 141 imageView.setImageBitmap(bitmap); 142 } 143 }); 144 145 /* Close the image otherwise, this function is not called next time */ 146 image.close(); 147 } 148 private Mat getMatFromImage(ImageProxy image) { 149 /* https://stackoverflow.com/questions/30510928/convert-android-camera2-api-yuv-420-888-to-rgb */ 150 ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); 151 ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); 152 ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); 153 int ySize = yBuffer.remaining(); 154 int uSize = uBuffer.remaining(); 155 int vSize = vBuffer.remaining(); 156 byte[] nv21 = new byte[ySize + uSize + vSize]; 157 yBuffer.get(nv21, 0, ySize); 158 vBuffer.get(nv21, ySize, vSize); 159 uBuffer.get(nv21, ySize + vSize, uSize); 160 Mat yuv = new Mat(image.getHeight() + image.getHeight() / 2, image.getWidth(), CvType.CV_8UC1); 161 yuv.put(0, 0, nv21); 162 Mat mat = new Mat(); 163 Imgproc.cvtColor(yuv, mat, Imgproc.COLOR_YUV2RGB_NV21, 3); 164 return mat; 165 } 166 167 private Mat fixMatRotation(Mat matOrg) { 168 Mat mat; 169 switch (previewView.getDisplay().getRotation()){ 170 default: 171 case Surface.ROTATION_0: 172 mat = new Mat(matOrg.cols(), matOrg.rows(), matOrg.type()); 173 Core.transpose(matOrg, mat); 174 Core.flip(mat, mat, 1); 175 break; 176 case Surface.ROTATION_90: 177 mat = matOrg; 178 break; 179 case Surface.ROTATION_270: 180 mat = matOrg; 181 Core.flip(mat, mat, -1); 182 break; 183 } 184 return mat; 185 } 186 } 187 188 private boolean checkPermissions(){ 189 for(String permission : REQUIRED_PERMISSIONS){ 190 if(ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){ 191 return false; 192 } 193 } 194 return true; 195 } 196 197 @Override 198 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 199// super.onRequestPermissionsResult(requestCode, permissions, grantResults); 200 if(requestCode == REQUEST_CODE_FOR_PERMISSIONS){ 201 if(checkPermissions()){ 202 startCamera(); 203 } else{ 204 Log.i(TAG, "[onRequestPermissionsResult] Failed to get permissions"); 205 this.finish(); 206 } 207 } 208 } 209}
日本語でコメントを書いているところが現在作成中の範囲になります。
それ以外は映像をスマホに表示したり、映像方向を変換したりするために使っているので
基本的には関係ないとおもっています。
これらのプログラムから
countNonZero
部分を排除したものを実行すると以下のような映像が取得できます。
したの赤い長方形の上部分に0
と表示されていますがこれは
はじめにint x = 0;としているためです。
ここに、黒色の割合もしくは黒ピクセル数を表示したいと考えています。
#考えられる問題点
countNonZero
はonCreateの中のstartCamera()に記述しているので
Activity生成時にピクセル数えるのは問題があるということでしょうか?
その場合、どのように記述すれば良いのかを示していただけると大変助かります。
回答1件
あなたの回答
tips
プレビュー