🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
OpenCV

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

Java

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

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Q&A

1回答

3079閲覧

Android Studio(java)上のOpenCVを使用して、黒色ピクセルを数える

NieR2B

総合スコア1

OpenCV

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

Java

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

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

0グッド

0クリップ

投稿2020/12/21 03:00

編集2020/12/21 06:14

前提・実現したいこと

Android Studio上でOpenCVを使用してカメラから取得した映像の黒要素とそれ以外に分けて出力
を行い、さらに任意の領域(xy座標を指定するなどして)の黒色ピクセルを数えて
テキストに%表示したいと考えています。

###開発・実行環境
[開発]
Android Studio 4.0.1
MacBook Pro (macOS Mojave ver10.14.6)
-言語- java

[実行]
OPPO Reno A 128GB

発生している問題・エラーメッセージ

黒要素とそれ以外に分けることはできたのですが
「任意の領域の黒色ピクセルを数える」というところが実現できていません。

「領域」というのが何を指すのか自分自身でも決めきれていません。
xy座標を指定するなどして長方形を描き、その長方形内の黒色の割合を求めれたらと思っています。

該当のソースコード

java

1package com.example.blackline; 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.content.Context; 17import android.content.pm.PackageManager; 18import android.graphics.Bitmap; 19import android.os.Bundle; 20import android.util.Log; 21import android.view.Surface; 22import android.widget.ImageView; 23import android.widget.TextView; 24 25import com.google.common.util.concurrent.ListenableFuture; 26 27import org.opencv.android.Utils; 28import org.opencv.core.Core; 29import org.opencv.core.Mat; 30import org.opencv.core.Point; 31import org.opencv.core.Scalar; 32import org.opencv.imgproc.Imgproc; 33import org.opencv.core.Rect; 34 35import java.nio.ByteBuffer; 36import java.util.concurrent.ExecutorService; 37import java.util.concurrent.Executors; 38 39import static org.opencv.core.CvType.CV_8SC3; 40import static org.opencv.core.CvType.CV_8UC1; 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 private TextView textView; 53 /*** For CameraX ***/ 54 private Camera camera = null; 55 private Preview preview = null; 56 private ImageAnalysis imageAnalysis = null; 57 private ExecutorService cameraExecutor = Executors.newSingleThreadExecutor(); 58 int i = 0; 59 int y = 0; 60 int x = 0; 61 int binary; 62 63 static { 64 System.loadLibrary("opencv_java4"); 65 } 66 @Override 67 protected void onCreate(Bundle savedInstanceState) { 68 super.onCreate(savedInstanceState); 69 setContentView(R.layout.activity_main); 70 71 previewView = findViewById(R.id.previewView); 72 imageView = findViewById(R.id.imageView); 73// textView =findViewById(R.id.textView); 74 75 if (checkPermissions()) { 76 startCamera(); 77 } else { 78 ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_FOR_PERMISSIONS); 79 } 80 } 81 82 private void startCamera() { 83 final ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); 84 Context context = this; 85 cameraProviderFuture.addListener(new Runnable() { 86 @Override 87 public void run() { 88 try { 89 ProcessCameraProvider cameraProvider = cameraProviderFuture.get(); 90 preview = new Preview.Builder().build(); 91 imageAnalysis = new ImageAnalysis.Builder().build(); 92 imageAnalysis.setAnalyzer(cameraExecutor, new MyImageAnalyzer()); 93 CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build(); 94 95 cameraProvider.unbindAll(); 96 camera = cameraProvider.bindToLifecycle((LifecycleOwner)context, cameraSelector, preview, imageAnalysis); 97 preview.setSurfaceProvider(previewView.createSurfaceProvider(camera.getCameraInfo())); 98 } catch(Exception e) { 99 Log.e(TAG, "[startCamera] Use case binding failed", e); 100 } 101 } 102 }, ContextCompat.getMainExecutor(this)); 103 } 104 105 private class MyImageAnalyzer implements ImageAnalysis.Analyzer { 106 private Mat matPrevious = null; 107 @Override 108 public void analyze(@NonNull ImageProxy image) { 109 /* Create cv::mat(RGB888) from image(NV21) */ 110 Mat matOrg = getMatFromImage(image); 111 /* Fix image rotation (it looks image in PreviewView is automatically fixed by CameraX???) */ 112 Mat mat = fixMatRotation(matOrg); 113 medianBlur(mat,mat, 5); 114// Mat mat_bin = mat.clone(); 115// Mat mat_hsv = mat.clone(); 116 // ↓ここから黒色とそれ以外に分けています↓ 117 Imgproc.cvtColor(mat,mat, Imgproc.COLOR_RGBA2BGR); 118 Imgproc.cvtColor(mat,mat, Imgproc.COLOR_BGR2HSV); 119 Core.inRange(mat, new Scalar(0,0,0), new Scalar(179,128,100),mat); 120 Imgproc.cvtColor(mat,mat, Imgproc.COLOR_GRAY2BGRA); 121// Imgproc.rectangle(mat,new Point(200,250),new Point(300,500),new Scalar(76,255,0),2); 122 Log.i(TAG, "[analyze] width = " + image.getWidth() + ", height = " + image.getHeight() + "Rotation = " + previewView.getDisplay().getRotation()); 123 Log.i(TAG, "[analyze] mat width = " + matOrg.cols() + ", mat height = " + matOrg.rows()); 124 125 /* Do some image processing */ 126// Mat matOutput = new Mat(mat.rows(), mat.cols(), mat.type()); 127// if (matPrevious == null) matPrevious = mat; 128// Core.absdiff(mat, matPrevious, matOutput); 129// matPrevious = mat; 130 131 /* Draw something for test */ 132 Imgproc.rectangle(mat, new Rect(10, 10, 100, 100), new Scalar(255, 0, 0)); 133// Imgproc.putText(mat, "mat",new Point(100,100), 10, 10, new Scalar(255, 0, 0)); 134 /* Convert cv::mat to bitmap for drawing*/ 135 Bitmap bitmap = Bitmap.createBitmap(mat.cols(),mat.rows(),Bitmap.Config.ARGB_8888); 136 Utils.matToBitmap(mat,bitmap); 137       //↑ここまで黒色とそれ以外に分けています↑ 138 /* Display the result onto ImageView */ 139 runOnUiThread(new Runnable() { 140 @Override 141 public void run() { 142 imageView.setImageBitmap(bitmap); 143 } 144 }); 145 146 /* Close the image otherwise, this function is not called next time */ 147 image.close(); 148 } 149 150 private Mat getMatFromImage(ImageProxy image) { 151 /* https://stackoverflow.com/questions/30510928/convert-android-camera2-api-yuv-420-888-to-rgb */ 152 ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); 153 ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); 154 ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); 155 int ySize = yBuffer.remaining(); 156 int uSize = uBuffer.remaining(); 157 int vSize = vBuffer.remaining(); 158 byte[] nv21 = new byte[ySize + uSize + vSize]; 159 yBuffer.get(nv21, 0, ySize); 160 vBuffer.get(nv21, ySize, vSize); 161 uBuffer.get(nv21, ySize + vSize, uSize); 162 Mat yuv = new Mat(image.getHeight() + image.getHeight() / 2, image.getWidth(), CV_8UC1); 163 yuv.put(0, 0, nv21); 164 Mat mat = new Mat(); 165 Imgproc.cvtColor(yuv, mat, Imgproc.COLOR_YUV2RGB_NV21, 3); 166 return mat; 167 } 168 169 private Mat fixMatRotation(Mat matOrg) { 170 Mat mat; 171 switch (previewView.getDisplay().getRotation()){ 172 default: 173 case Surface.ROTATION_0: 174 mat = new Mat(matOrg.cols(), matOrg.rows(), matOrg.type()); 175 Core.transpose(matOrg, mat); 176 Core.flip(mat, mat, 1); 177 break; 178 case Surface.ROTATION_90: 179 mat = matOrg; 180 break; 181 case Surface.ROTATION_270: 182 mat = matOrg; 183 Core.flip(mat, mat, -1); 184 break; 185 } 186 return mat; 187 } 188 } 189 190 private boolean checkPermissions(){ 191 for(String permission : REQUIRED_PERMISSIONS){ 192 if(ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){ 193 return false; 194 } 195 } 196 return true; 197 } 198 199 @Override 200 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 201// super.onRequestPermissionsResult(requestCode, permissions, grantResults); 202 if(requestCode == REQUEST_CODE_FOR_PERMISSIONS){ 203 if(checkPermissions()){ 204 startCamera(); 205 } else{ 206 Log.i(TAG, "[onRequestPermissionsResult] Failed to get permissions"); 207 this.finish(); 208 } 209 } 210 } 211}

コメントの「↓ここから黒色とそれ以外に分けています↓」 と 「↑ここまで黒色とそれ以外に分けています↑」
の部分がOpenCVを使った画像処理になります。
多分この続きに黒色ピクセルを数えるという処理を書くのが適切なんだろうとは思っています。

上記の部分以外は、カメラからの映像を出力などに使っているので気にしなくても大丈夫だと思います。

これらのプログラムをスマホに書き込むと以下のような映像が取得できます。
イメージ説明

理想は下の白黒映像のどこかに分かりやすく「〇〇ピクセル」のように出力したいと考えています。

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

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

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

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

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

fana

2020/12/21 05:55

> 任意の領域の黒色ピクセルを数える この話で「領域」とは何を指しますか?
NieR2B

2020/12/21 06:17

修正の依頼ありがとうございます。 投稿内容を修正させていただきましたが、 xy座標を指定するなどして長方形を描き、その長方形内の黒色の割合を求めれたらと思っています。 ご期待に沿う回答だったでしょうか? また何かあれば修正していくのでご助力の程よろしくお願いします。
guest

回答1

0

OpenCVにcountNonZeroという関数があります.
「黒orNot」というマスク(黒の箇所の画素値が非0で,他が0な絵)が存在するのであれば,とりあえずこの関数で「黒」側の画素数がわかります.

「領域」の与え方が「矩形領域」ならば,まぁこの関数一発で終了でしょう.

「領域」が複雑な形状の場合は,
例えば,その形状のマスク(領域内255,領域外を0とするような絵)を作って「黒orNot」マスクとANDしてからcountNonZeroを使うとかで良いのではないでしょうか.

投稿2020/12/21 06:36

編集2020/12/21 06:37
fana

総合スコア11990

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

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

NieR2B

2020/12/21 07:18 編集

int pixel = Core.countNonZero(mat); と書くことで映像全体の黒を数えることはできるようになったと思います。 しかし、矩形領域を与える方法と結果をディスプレイに表示する方法が分からないので止まってしまいました。
fana

2020/12/21 07:28 編集

私はその環境を知らないので,入力系や出力系の話は答えようがないです. 表示に関してはOpenCVの世界で画像に putText() で描いてしまうという手も採れなくはないと思いますが,putText()はフォントや描画できる文字種の制約がきついので,その環境で使える何らかの表示手段を用いる方が良いのかもしれません.
NieR2B

2020/12/21 07:37 編集

返信していただきありがとうございます。 なるほどputText()で描くというのと、環境に合わせた(Android StudioならText Viewかな)数値出力を試していきたいと思います。 あ、矩形領域を与えるのは Rect roi = new Rect(10, 10, 100,100); Mat trim = new Mat(mat, roi); int pixel = Core.countNonZero(trim); とすることで矩形領域の白黒判別が出来るようになったんですかね?
fana

2020/12/21 07:34

矩形であればそんな感じで部分画像に対して countNonZero すれば良いかと.
NieR2B

2020/12/22 05:41

範囲をかたどることはできたのですが countNonZeroを入れた時にアプリが強制終了していますようになってしまいました。 多分アプリ立ち上げに時にcountNonZeroを行っているから とかいう理由だとは思うのですがいかがでしょうか? 難しい質問ならもう一度これだけで質問をし直した方がいいのでしょうか?
fana

2020/12/22 05:46

【本件が処理方法(?)の話であり && その件はとりあえずOKで && 現状の強制終了の話というのが処理タイミング関係の話(?)で全く別の話】なのであれば, (現状の強制終了が起こるコードを提示しての)別質問にした方がよいのかも.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問