0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Flutter(Android)から MethodChannel で OpenCV を呼び出す

0
Last updated at Posted at 2026-02-21

Flutter から Android の OpenCV を使う方法を調べましたが、なかなか、MethodChannelで使う方法の日本語情報が見つかりませんでした。

今回ひとまず動作確認が取れたので、備忘録としてまとめておきます。


動作確認環境

  • Flutter 3.38.9
  • OpenCV SDK 4.12.0

1. Android 用 OpenCV SDK をダウンロード

OpenCV 公式リリースページから Android SDK を取得します。

👉 https://opencv.org/releases/

今回は 4.12.0 を使用しました。


2. Flutter プロジェクト作成

flutter create opencv_sample

プロジェクト名は任意です


3. OpenCV SDK をプロジェクトへコピー

ダウンロードした OpenCV SDK 内の

opencv-4.12.0-android-sdk/OpenCV-android-sdk/sdk

フォルダを、Flutter プロジェクトの

android/

直下にコピーします。

フォルダ名を sdk から openCVLibrary に変更します。
(※後の Gradle 設定と一致させてください)


4. android/settings.gradle.kts を編集

include(":app")
+ include(":openCVLibrary")
+ project(":openCVLibrary").projectDir = File(rootProject.projectDir, "./openCVLibrary")

5. android/app/build.gradle.kts を編集

Java のバージョン不一致でエラーが出たため、21 に統一しました。

    compileOptions {
-        sourceCompatibility = JavaVersion.VERSION_17
-        targetCompatibility = JavaVersion.VERSION_17
+        sourceCompatibility = JavaVersion.VERSION_21
+        targetCompatibility = JavaVersion.VERSION_21
    }

    kotlinOptions {
-        jvmTarget = JavaVersion.VERSION_17.toString()
+        jvmTarget = JavaVersion.VERSION_21.toString()
    }


+ dependencies {
+     implementation(project(":openCVLibrary"))
+     implementation("androidx.exifinterface:exifinterface:1.4.2")
+ }

6. android/openCVLibrary/build.gradle を編集


    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_17
-        targetCompatibility JavaVersion.VERSION_17
+        sourceCompatibility JavaVersion.VERSION_21
+        targetCompatibility JavaVersion.VERSION_21
    }


7. Android 側に OpenCV 処理を書く

android/app/src/main/kotlin/.../MainActivity.kt

OpenCV を初期化し、MethodChannel で Flutter から画像を受け取ってグレースケール変換するサンプルです。

package com.example.opencv_sample

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Log
import android.graphics.Matrix
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import org.opencv.android.OpenCVLoader
import org.opencv.android.Utils
import org.opencv.core.Mat
import org.opencv.imgproc.Imgproc
import org.opencv.core.MatOfByte
import org.opencv.imgcodecs.Imgcodecs
import java.io.ByteArrayOutputStream
import java.io.ByteArrayInputStream
import androidx.exifinterface.media.ExifInterface // build.gradleのdependenciesにandroidx.exifinterface:exifinterface追加しておくこと


class MainActivity : FlutterActivity() {
    private val CHANNEL = "opencv"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        // OpenCV 初期化
        if (!OpenCVLoader.initDebug()) {
            Log.e("OpenCV", "OpenCV initialization failed")
        } else {
            Log.i("OpenCV", "OpenCV initialized successfully")
        }

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                when (call.method) {
                    "processImage" -> {
                        val bytes = call.argument<ByteArray>("bytes")
                        if (bytes != null) {
                            try {
                                val exif = ExifInterface(ByteArrayInputStream(bytes))
                                val orientation = exif.getAttributeInt(
                                    ExifInterface.TAG_ORIENTATION,
                                    ExifInterface.ORIENTATION_NORMAL
                                )

                                val inputBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)

                                val rotatedBitmap = when (orientation) {
                                    ExifInterface.ORIENTATION_ROTATE_90 -> rotateBitmap(inputBitmap, 90f)
                                    ExifInterface.ORIENTATION_ROTATE_180 -> rotateBitmap(inputBitmap, 180f)
                                    ExifInterface.ORIENTATION_ROTATE_270 -> rotateBitmap(inputBitmap, 270f)
                                    else -> inputBitmap
                                }

                                // 回転済みのデータを渡す
                                val output = processWithOpenCV(rotatedBitmap)
                                // メモリ開放
                                inputBitmap.recycle()
                                rotatedBitmap.recycle()

                                result.success(output)
                            } catch (e: Exception) {
                                result.error("PROCESSING_ERROR", e.message, null)
                            }
                        } else {
                            result.error("INVALID_ARGUMENT", "Bytes are null", null)
                        }
                    }
                    else -> result.notImplemented()
                }
            }
    }

    private fun processWithOpenCV(inputBitmap: Bitmap): ByteArray {

        // Bitmap → Mat
        val mat = Mat()
        Utils.bitmapToMat(inputBitmap, mat)

        // グレースケール変換
        val grayMat = Mat()
        Imgproc.cvtColor(mat, grayMat, Imgproc.COLOR_RGBA2GRAY)

        // Matを直接PNG形式のバイナリにエンコード
        val matOfByte = MatOfByte()
        Imgcodecs.imencode(".png", grayMat, matOfByte)

        val retByteArray = matOfByte.toArray()

        // メモリ解放(matOfByteも忘れずに)
        mat.release()
        grayMat.release()
        matOfByte.release()
        
        return retByteArray
    }

    fun rotateBitmap(bitmap: Bitmap, angle: Float): Bitmap {
        val matrix = Matrix()
        matrix.postRotate(angle)
        return Bitmap.createBitmap(
            bitmap, 0, 0,
            bitmap.width, bitmap.height,
            matrix, true
        )
    }
}

8. Flutter 側のブリッジクラス

import 'package:flutter/services.dart';

class OpenCvBridge {
  static const platform = MethodChannel('opencv');

  static Future<Uint8List> processImage(Uint8List bytes) async {
    final result = await platform.invokeMethod('processImage', {
      'bytes': bytes,
    });
    return result;
  }
}

どこかで呼び出し

OpenCvBridge.processImage(Uint8List(0)); // 実際は画像データを渡す

9. 実行

flutter run -d emulator-xxxx

Android で実行してください。


まとめ

  • Flutter から OpenCV を使うには Platform Channel を使用する方法がある
  • OpenCV SDK を Android Module として追加する
  • Java/Kotlin のバージョン不一致に注意

ひとまず、動作しました。

今後は Canny や輪郭抽出なども試してみる予定です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?