Kotlin / Anko / OpenCV / CameraView не может создать обработчик, потому что поток не вызвал Looper.prepare () - PullRequest
0 голосов
/ 09 октября 2018

Итак, в настоящее время я пытаюсь преобразовать снимок, сделанный с помощью CameraView , в шкалу серого с использованием OpenCV.После того, как снимок сделан, я использую Anko , чтобы обработать его асинхронно.После завершения обработки я записываю изображение в MediaStore.В Activity есть CameraView и кнопка для съемки.

Однако, когда я нажимаю кнопку, появляется следующее исключение:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
   at android.os.Handler.<init>(Handler.java:200)
   at android.os.Handler.<init>(Handler.java:114)
   at com.otaliastudios.cameraview.CameraUtils.decodeBitmap(CameraUtils.java:101)
   at com.otaliastudios.cameraview.CameraUtils.decodeBitmap(CameraUtils.java:83)
   at com.example.joao.waterprototype.MainActivity$onCreate$2$onPictureTaken$1.invoke(MainActivity.kt:55)
   at com.example.joao.waterprototype.MainActivity$onCreate$2$onPictureTaken$1.invoke(MainActivity.kt:46)
   at org.jetbrains.anko.AsyncKt$doAsync$1.invoke(Async.kt:143)
   at org.jetbrains.anko.AsyncKt$doAsync$1.invoke(Async.kt)
   at org.jetbrains.anko.AsyncKt$sam$java_util_concurrent_Callable$0.call(Async.kt)
   at java.util.concurrent.FutureTask.run(FutureTask.java:237)
   at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
   at java.lang.Thread.run(Thread.java:761)

место, где происходит указанная проблема, описывается вследующий фрагмент:

camera.addCameraListener(object : CameraListener() {

        override fun onPictureTaken(jpeg: ByteArray?) {

            Toast.makeText(applicationContext, "Image Taken", Toast.LENGTH_SHORT).show()

            doAsync() {

                lateinit var result : Bitmap
                CameraUtils.decodeBitmap(jpeg) {

                    val mat = Mat()
                    val bmp32 = it.copy(Bitmap.Config.ARGB_8888, true)
                    Utils.bitmapToMat(bmp32, mat)

                    val greyMat = Mat()
                    cvtColor(mat, greyMat, COLOR_BGRA2GRAY)

                    result = Bitmap.createBitmap(greyMat.cols(), greyMat.rows(), Bitmap.Config.ARGB_8888)
                    Utils.matToBitmap(greyMat, result)

                }

                uiThread {

                    val title = "${UUID.randomUUID()}.jpg"
                    val savedImageURL = MediaStore.Images.Media.insertImage(
                            contentResolver,
                            result,
                            title,
                            "Image of $title"
                    )

                    val uri = Uri.parse(savedImageURL)
                    Toast.makeText(applicationContext, "Image Created at ${uri.path}", Toast.LENGTH_SHORT).show()

                }
            }
        }

    })

Общая тема для этих исключений - использование Toasts при использовании контекстов Async, но я дважды проверил это, и я стараюсь не использовать их там.Фактически указанная ошибка возникает при вызове decodeBitmap.Что странно, поскольку документация CameraView настаивает на том, что указанная функция вызывается в рабочем потоке.Может ли кто-нибудь из вас объяснить мне, что происходит и как это исправить?

Чтобы упростить воспроизведение, вот полный файл MainActivity.kt:

package com.example.joao.waterprototype

import android.graphics.Bitmap
import android.net.Uri
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.WindowManager
import android.widget.Toast
import com.otaliastudios.cameraview.CameraListener
import com.otaliastudios.cameraview.CameraUtils
import com.otaliastudios.cameraview.Gesture
import com.otaliastudios.cameraview.GestureAction
import kotlinx.android.synthetic.main.activity_main.*
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.uiThread
import org.opencv.android.OpenCVLoader
import org.opencv.android.Utils
import org.opencv.core.Mat
import org.opencv.imgproc.Imgproc.COLOR_BGRA2GRAY
import org.opencv.imgproc.Imgproc.cvtColor
import java.util.*

class MainActivity : AppCompatActivity(){


    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        camera.setLifecycleOwner(this)

        take_photo.setOnClickListener {
            camera.capturePicture()
        }

        camera.mapGesture(Gesture.PINCH, GestureAction.ZOOM); // Pinch to zoom!
        camera.mapGesture(Gesture.TAP, GestureAction.FOCUS_WITH_MARKER); // Tap to focus!
        camera.mapGesture(Gesture.LONG_TAP, GestureAction.CAPTURE); // Long tap to shoot!

        camera.addCameraListener(object : CameraListener() {

            override fun onPictureTaken(jpeg: ByteArray?) {

                Toast.makeText(applicationContext, "Image Taken", Toast.LENGTH_SHORT).show()

                doAsync() {

                    lateinit var result : Bitmap
                    CameraUtils.decodeBitmap(jpeg) {

                        val mat = Mat()
                        val bmp32 = it.copy(Bitmap.Config.ARGB_8888, true)
                        Utils.bitmapToMat(bmp32, mat)

                        val greyMat = Mat()
                        cvtColor(mat, greyMat, COLOR_BGRA2GRAY)

                        result = Bitmap.createBitmap(greyMat.cols(), greyMat.rows(), Bitmap.Config.ARGB_8888)
                        Utils.matToBitmap(greyMat, result)

                    }

                    uiThread {

                        val title = "${UUID.randomUUID()}.jpg"
                        val savedImageURL = MediaStore.Images.Media.insertImage(
                                contentResolver,
                                result,
                                title,
                                "Image of $title"
                        )

                        val uri = Uri.parse(savedImageURL)
                        Toast.makeText(applicationContext, "Image Created at ${uri.path}", Toast.LENGTH_SHORT).show()

                    }
                }
            }

        })

    }


    companion object {

        // Used to load the 'native-lib' library on application startup.
        init {

            if (!OpenCVLoader.initDebug()) {
                Log.e("OpenCV", "Cannot connect to OpenCV Manager")
            } else {
                Log.e("OpenCV", "Connected Successfully")
            }


        }
    }
}

И вотActivity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.otaliastudios.cameraview.CameraView
        android:id="@+id/camera"
        android:keepScreenOn="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/take_photo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Take Photo"
        android:textAlignment="center"/>

</LinearLayout>

1 Ответ

0 голосов
/ 10 октября 2018

Я полагаю, что существует предположение, что decodeBitmmap инициируется из потока пользовательского интерфейса, даже если фактическая работа выполняется в рабочем потоке.Это код, о котором идет речь ... выполняется в потоке вызывающих абонентов (строка 101):

final Handler ui = new Handler();

Существует небольшая дискуссия по аналогичной проблеме здесь

...