Как получить предварительный просмотр Camera2 как Bitmap или OpenCV Mat с Renderscript - PullRequest
1 голос
/ 28 апреля 2019

Я хочу выполнять обработку изображений в реальном времени с помощью OpenCV и использовать Android Camera2 API.Моя проблема заключается в преобразовании фрейма предварительного просмотра в OpenCV Mat (или вначале тоже может помочь растровое изображение).

Я знаю, что вы можете прикрепить ImageReader к камере и конвертировать каждое доступное изображение в растровое изображение.Проблема в том, что подключение ImageReader к камере значительно снижает частоту кадров (не любое преобразование изображения, а просто использование ImageReader без какого-либо дополнительного кода).Поэтому моя идея заключалась в том, чтобы прикрепить поверхность Allocation к камере, передать это Allocation в ScriptIntrinsicYuvToRGB-Renderscript и скопировать выходное Allocation в растровое изображение, как в примере android-hdf-viewfinder .Вот что я пробовал до сих пор:

private fun setupRenderscript(){
        rs = RenderScript.create(context)

        val tb1 = Type.Builder(rs, Element.YUV(rs)).setX(size.width).setY(size.height)
        rsInput = Allocation.createTyped(rs, tb1.create(), Allocation.USAGE_IO_INPUT or Allocation.USAGE_SCRIPT)

        bmOut = Bitmap.createBitmap(size.width, size.height, Bitmap.Config.ARGB_8888)
        val tb2 = Type.Builder(rs, Element.RGBA_8888(rs)).setX(size.width).setY(size.height)
        rsOutput = Allocation.createTyped(rs, tb2.create(), Allocation.USAGE_SCRIPT)

        yuvToRgbIntrinistic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs))
        yuvToRgbIntrinistic.setInput(rsInput)
    }

private fun createCameraPreviewSession() {
        setupRenderscript()

        val texture = cameraTextureView.surfaceTexture //Normal camera preview surface
        texture.setDefaultBufferSize(size.width, size.height)
        val surface = Surface(texture)

        captureRequestBuilder = cameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
        captureRequestBuilder?.addTarget(surface)
        captureRequestBuilder?.addTarget(rsInput.surface) //Attach Allocation Surface to CaptureRequest
        cameraDevice?.createCaptureSession(Arrays.asList(surface, rsInput.surface), cameraCaptureSession, backgroundHandler)

private val surfaceTextureListener = object : SurfaceTextureListener {
        override fun onSurfaceTextureAvailable(texture: SurfaceTexture?, width: Int, height: Int) {
            openCamera(width, height)
        }

        override fun onSurfaceTextureSizeChanged(texture: SurfaceTexture?, width: Int, heigth: Int) {
            configureTransform(width, heigth)
        }

        override fun onSurfaceTextureUpdated(texture: SurfaceTexture?){
            if (::rsOutput.isInitialized){
                log("Image")
                rsInput.ioReceive()
                yuvToRgbIntrinistic.forEach(rsOutput)
                rsOutput.copyTo(bmOut)
            }
        }

        override fun onSurfaceTextureDestroyed(texture: SurfaceTexture?) = true
    }

Предварительный просмотр камеры работает нормально, и теперь выдаются фатальные ошибки, но я не получаю предварительный просмотр в виде растрового изображения.Я получаю следующие сообщения журнала:
Для rsInput.ioReceive():

E/NdkImageReader: acquireImageLocked: Output buffer format: 0x22, ImageReader configured format: 0x1
E/RenderScript: lockNextBuffer: acquire image from reader 0x7427c7d8a0 failed! ret: -10000, img 0x0
E/RenderScript_jni: non fatal RS error, Error receiving IO input buffer.

Для yuvToRgbIntrinistic.forEach(rsOutput) Я получаю одно и то же сообщение несколько раз, вероятно для каждого пикселя:

E/RenderScript: YuvToRGB executed without data, skipping

Так что кажется, что-то не работает с копированием / чтением данных во входное распределение, но я не знаю, что я делаю неправильно.Он должен работать аналогично примеру hdr, указанному выше.

1 Ответ

0 голосов
/ 28 апреля 2019

Проблема в том, что подключение ImageReader к камере значительно снижает частоту кадров (не любое преобразование изображения, а просто использование ImageReader без какого-либо дополнительного кода)

Этого не должно быть. Возможно, вы неправильно настроили выходные объекты сеанса камеры и устройства чтения изображений. Вам также необходимо убедиться, что вы закрываете изображения, отправленные в устройство чтения изображений, как можно быстрее, чтобы можно было перейти к следующему. Если вы действительно заботитесь о производительности, вам следует использовать YUV_420_888 в качестве формата пикселей и, в зависимости от Размер выходной цели, 3-5 кадров в качестве буфера для считывателя изображений. Вот пример кода, который поможет вам начать работу с этого сообщения в блоге :

val bufferSize = 3
val imageReader = ImageReader.newInstance(
    // Pick width and height from supported camera output sizes
    width, height, ImageFormat.YUV_420_888, bufferSize)

// Retrieve surface from image reader
val imReaderSurface = imageReader.surface
val targets: MutableList<Surface> = arrayOf(imReaderSurface).toMutableList()

// Create a capture session using the predefined targets
cameraDevice.createCaptureSession(targets, object: CameraCaptureSession.StateCallback() {
    override fun onConfigured(session: CameraCaptureSession) {
        // Submit capture requests here
    }
    // Omitting for brevity...
    override fun onConfigureFailed(session: CameraCaptureSession) = Unit
}, null)

Что касается ошибки RenderScript, то довольно сложно сказать, что происходит с предоставленными вами деталями. Я бы порекомендовал использовать библиотеку поддержки RenderScript , если вы этого еще не сделали, и протестировать свой код в эмуляторе, чтобы исключить возможные проблемы с реализацией драйвера.

...