Android cámara2 jpeg framerate
Estoy tratando de guardar secuencias de imágenes con frames fijos (preferiblemente hasta 30) en un dispositivo Android con capacidad COMPLETA para camera2 (Galaxy S7), pero no soy capaz de a) obtener un framerate estable, b) alcanzar incluso 20fps (con jpeg Codificación). Ya he incluido las sugerencias de la captura de la cámara Android2 ráfaga es demasiado lento .
La duración mínima de la trama para JPEG es de 33,33 milisegundos (para resoluciones inferiores a 1920×1080) según
- Problemas al escalar una imagen YUV utilizando la biblioteca libyuv
- Rotar una matriz de bytes YUV en Android
- Convertir de YUV a RGB en c + + (android-ndk)
- El proceso de alojamiento del servicio de cámara ha muerto inesperadamente
- Convertir la cámara Android api YUV_420_888 a RGB
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputMinFrameDuration(ImageFormat.JPEG, size);
Y el stallduration es 0ms para cada tamaño (similar para YUV_420_888).
Mi constructor de captura se ve así:
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_OFF); captureBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, _exp_time); captureBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true); captureBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, _iso_value); captureBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, _foc_dist); captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CONTROL_AF_MODE_OFF); captureBuilder.set(CaptureRequest.CONTROL_AWB_MODE, _wb_value); // https://stackoverflow.com/questions/29265126/android-camera2-capture-burst-is-too-slow captureBuilder.set(CaptureRequest.EDGE_MODE,CaptureRequest.EDGE_MODE_OFF); captureBuilder.set(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE, CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE_OFF); captureBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_OFF); captureBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); // Orientation int rotation = getWindowManager().getDefaultDisplay().getRotation(); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION,ORIENTATIONS.get(rotation));
La distancia de enfoque se establece en 0.0 (inf), iso se fija en 100, tiempo de exposición 5ms. La balanza de blancos se puede ajustar en OFF / AUTO / ANY VALUE, no afecta los tiempos que se indican a continuación.
Comienzo la sesión de captura con el siguiente comando:
session.setRepeatingRequest(_capReq.build(), captureListener, mBackgroundHandler);
Nota: No hace ninguna diferencia si solicito RepeatingRequest o RepeatingBurst.
En la vista previa (sólo superficie de textura adjunta), todo está a 30fps. Sin embargo, tan pronto como adjunto un lector de imágenes (oyente que se ejecuta en HandlerThread) que instancio como sigue (sin guardar, sólo medir el tiempo entre fotogramas):
reader = ImageReader.newInstance(_img_width, _img_height, ImageFormat.JPEG, 2); reader.setOnImageAvailableListener(readerListener, mBackgroundHandler);
Con código de medición del tiempo:
ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader myreader) { Image image = null; image = myreader.acquireNextImage(); if (image == null) { return; } long curr = image.getTimestamp(); Log.d("curr- _last_ts", "" + ((curr - last_ts) / 1000000) + " ms"); last_ts = curr; image.close(); } }
Recibo periódicamente repitiendo diferencias de tiempo como esta:
99 ms – 66 ms – 66 ms – 99 ms – 66 ms – 66 ms …
¿No entiendo porqué éstos toman el doble o el triple el tiempo que el mapa de la configuración de la corriente anunció para el JPEG? El tiempo de exposición está muy por debajo de la duración de la trama de 33ms. ¿Hay algún otro proceso interno que no conozco?
He intentado lo mismo para el formato YUV_420_888, lo que dio lugar a constantes diferencias de tiempo de 33ms. El problema que tengo aquí es que el teléfono celular carece del ancho de banda para almacenar las imágenes lo suficientemente rápido (probé el método descrito en Cómo guardar una imagen YUV_420_888? ). Si usted sabe de cualquier método para comprimir o codificar estas imágenes lo suficientemente rápido, por favor hágamelo saber.
Edit: De la documentación de getOutputStallDuration: "En otras palabras, usar una repetición de solicitud YUV daría lugar a una velocidad de cuadros fija (digamos que es 30 FPS) .Si una sola solicitud JPEG se presenta periódicamente, la velocidad de fotogramas se mantendrá en 30 FPS (Siempre y cuando esperemos a que el JPEG anterior regrese cada vez) Si intentamos enviar una solicitud YUV + JPEG repetitiva, la velocidad de fotogramas caerá de 30 FPS ". ¿Esto implica que necesito solicitar periódicamente una sola captura ()?
Edit2: From https://developer.android.com/reference/android/hardware/camera2/CaptureRequest.html : "La información necesaria para la aplicación, dado el modelo anterior, se proporciona a través del campo android.scaler.streamConfigurationMap utilizando getOutputMinFrameDuration (Int, Size), que se utilizan para determinar la velocidad máxima de trama / duración mínima de trama que es posible para una configuración de flujo dada.
Específicamente, la aplicación puede utilizar las siguientes reglas para determinar la duración de trama mínima que puede solicitar desde el dispositivo de cámara:
Deje que el conjunto de corrientes de entrada / salida actualmente configuradas sea llamado S. Encuentre las duraciones de marco mínimas para cada flujo en S, buscando en android.scaler.streamConfigurationMap usando getOutputMinFrameDuration (int, Size) (con su tamaño / formato respectivo) . Deje que este conjunto de duraciones de trama sea llamado F. Para cualquier petición dada R, la duración mínima de trama permitida para R es el máximo de todos los valores en F. Deje que los flujos usados en R se llamen S_r. Si ninguna de las secuencias de S_r tiene un tiempo de parada (enumerado en getOutputStallDuration (int, Size) usando su tamaño / formato respectivo), entonces la duración de la trama en F determina la velocidad de cuadro estacionario que obtendrá la aplicación si usa R como Una petición repetida ".
- OnPreviewFrame data image to imageView
- Cómo convertir RGB565 a YUV420SP más rápido en Android?
- Convierta la matriz de bytes NV21 en formato legible de mapa de bits
- Decodificación de YUV a RGB en C / C ++ con NDK
- Cómo mostrar la imagen de YUV en Android
- Camera.PreviewCallback equivalente en Camera2 API
- Imágenes verdes al hacer una codificación JPEG desde YUV_420_888 utilizando la nueva cámara Android2 api
- ¿Por qué el decodificador de MediaCodec no emite un formato YUV unificado (como YUV420P)?
La salida JPEG no es, por lo tanto, la forma más rápida de recuperar los fotogramas. Puede lograr esto mucho más rápido dibujando los marcos directamente en un Quad usando OpenGL.
Para la captura de ráfaga, una solución más rápida sería capturar las imágenes a la RAM sin codificarlas, luego codificarlas y guardarlas de forma asíncrona.
En este sitio web se puede encontrar un montón de excelente código relacionado con la multimedia android en general.
Este programa específico utiliza OpenGL para obtener los datos de píxeles de un vídeo MPEG. No es difícil utilizar la cámara como entrada en lugar de un video. Básicamente puede utilizar la textura utilizada en la clase CodecOutputSurface
del programa mencionado como textura de salida para su solicitud de captura.
Una posible solución que encontré consiste en usar y descargar YUV sin codificarlo como JPEG en combinación con una micro tarjeta Sd que es capaz de ahorrar hasta 95 Mb por segundo. (Tuve la idea errónea de que las imágenes YUV serían más grandes, por lo que con un teléfono celular que tiene pleno soporte para la cámara 2-pipeline, la velocidad de escritura debe ser el factor limitante.
Con esta configuración, pude lograr las siguientes tasas estables:
- 1920×1080, 15fps (aproximadamente 4Mb * 15 == 60Mb / s)
- 960 x 720, 30 fps. (Aproximadamente 1,5Mb * 30 = 45Mb / s)
Entonces codifico las imágenes fuera de línea de YUV a PNG usando una escritura del python.