Tengo una aplicación de procesamiento de video en tiempo real en Android que captura desde la cámara y muestra la vista previa en un TextureView.

El problema es que quiero capturar la vista previa de la cámara a 2k, pero el tamaño de la pantalla es 1080, por lo que el tamaño de TextureView es un máximo de 1080.

Quiero poder obtener TextureView.getBitmap() y obtener una imagen de 2k, pero debido a que TextureView está maximizado al tamaño de la pantalla, la imagen es solo de 1080 px.

Descubrí que podía sobrescribir el método TextureView onMeasure() para forzar el tamaño de TextureView a 2k. Esto funciona, sin embargo, debido a que la pantalla es 1080, el teléfono solo muestra la mitad de la imagen de 2k, ¿cómo mostrar la imagen completa?

Mi segundo intento, hice el tamaño de diseño de TextureView 0 para que estuviera oculto, y agregué otro ImageView al diseño, luego, desde el oyente onImageAvailable (), convierto la imagen en un mapa de bits y lo configuro en ImageView. Esto funciona, pero es muy lento, obtiene una imagen retrasada en lugar de una vista previa de la cámara en vivo.

Creo que necesito poder alejar el TextureView, o quizás tener 2 vistas de textura y copiar de una a la otra. Pero no puedo hacer que funcione como se desea.

Por favor, cualquier ayuda o sugerencia.

protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); ancho = CameraConnectionFragment.MINIMUM_PREVIEW_SIZE; altura = CameraConnectionFragment.MINIMUM_PREVIEW_SIZE; setMeasuredDimension(ancho, alto); }

El código es similar a https://github.com/miyosuda/TensorFlowAndroidDemo/tree/master/app/src/main/java/org/tensorflow/demo

El código de la cámara es,

private void openCamera(final int width, final int height) {
        setUpCameraOutputs(width, height);
        configureTransform(width, height);
        final Activity activity = getActivity();
        final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
        manager.openCamera(cameraId, stateCallback, backgroundHandler);
    }

El código de vista previa de la cámara es,

private void createCameraPreviewSession() {
        try {
            final SurfaceTexture texture = textureView.getSurfaceTexture();
            assert texture != null;

            // We configure the size of default buffer to be the size of camera preview we want.
            texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());

            // This is the output Surface we need to start preview.
            final Surface surface = new Surface(texture);

            // We set up a CaptureRequest.Builder with the output Surface.
            previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            previewRequestBuilder.addTarget(surface);

            // Create the reader for the preview frames.
            previewReader =
                ImageReader.newInstance(
                    previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 2);

            previewReader.setOnImageAvailableListener(imageListener, backgroundHandler);
            previewRequestBuilder.addTarget(previewReader.getSurface());
            zoom(zoom, activity, false);
            // Here, we create a CameraCaptureSession for camera preview.
            cameraDevice.createCaptureSession(
                Arrays.asList(surface, previewReader.getSurface()),
                new CameraCaptureSession.StateCallback() {
                    @Override
                    public void onConfigured(final CameraCaptureSession cameraCaptureSession) {
                        // The camera is already closed
                        if (null == cameraDevice) {
                            return;
                        }

                        // When the session is ready, we start displaying the preview.
                        captureSession = cameraCaptureSession;
                        try {
                            // Auto focus should be continuous for camera preview.
                            previewRequestBuilder.set(
                                CaptureRequest.CONTROL_AF_MODE,
                                CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                            // Flash is automatically enabled when necessary.

                            previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON);
                            if (isTorchOn) {
                                previewRequestBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
                            } else {
                                previewRequestBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF);
                            }
                            // Finally, we start displaying the camera preview.
                            previewRequest = previewRequestBuilder.build();
                            captureSession.setRepeatingRequest(previewRequest, captureCallback, backgroundHandler);
                        } catch (final CameraAccessException exception) {
                            LOGGER.e(exception, "Exception!");
                        } catch (Exception exception) {
                            Log.wtf("Camera preview", exception);
                        }
                    }
                },
                null);
        } catch (final Exception e) {
            LOGGER.e(e, "Exception!");
        }
respuesta

Estás haciendo cosas raras. Por lo general, no tiene control sobre el tamaño máximo y mínimo de la vista previa. Método

CameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId)
val supportedSizes: Size[] = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
                              .getOutputSizes(SurfaceTexture::class.java)

devolverá la lista completa de todos los tamaños que puede usar. Es una limitación de implementación: no puede superarla para una implementación específica de TextureView de un dispositivo específico. Todo lo que puede hacer es elegir la relación de aspecto correcta.

Los tamaños de captura serán diferentes y, por lo general, mucho más grandes, y la lista también será más grande; el problema es que no podrá mostrar eso en la vista previa; los datos capturados solo serán visibles en el archivo/secuencia de salida.

¡Aunque! Hay varios enfoques que puede usar aquí para lograr lo que desea.

  1. Modifique la transmisión de vista previa como desee: interpole (hay mucha información sobre eso en línea) para hacerla más grande tanto en tamaño como en calidad o simplemente mejorar la pérdida de calidad. (todavía se mostrará en el TextureView que está limitado por su implementación)
  2. Implemente su propia versión de vista de textura que hace toda esta magia con OpenGL dentro de TextureView: será mucho más eficaz pero relativamente más difícil de implementar.
  3. Intente utilizar el flujo de captura de salida (o incluso una vista previa de uno) como fuente para algún TextureView que ya sepa cómo mostrar imágenes de calidad, la de algún reproductor de video.
  4. Tal vez algo más.

Personalmente, implementé los dos primeros enfoques hace mucho tiempo y puedo decir que son posibles y relativamente fáciles de implementar. No proporcionaré código o técnicas porque es una gran cantidad de código y no recuerdo todos los métodos lo suficientemente bien.

El tercer enfoque también es posible y lo he visto en el trabajo, pero nunca lo implementé (solo corrigí errores) (en mi caso, se implementó en el lado nativo, por lo que tal vez necesite C para eso).

Sé que la respuesta puede no ser tan directa como querías, pero la tarea tampoco es tan trivial. De cualquier manera, espero que ayude.

Una posible solución que puedo pensar es extender su costumbre TextureViewy anular el onMeasure()método como sugirió en su pregunta para forzar que el tamaño de TextureView sea 2k como:

@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    setMeasuredDimension(2000, 2000);
}

y haga que se TextureViewpueda desplazar en ambas direcciones (vertical y horizontalmente) algo así como:

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

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fillViewport="true">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <mypackage.name.MyTextureView
                    android:id="@+id/textureView"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />

            </LinearLayout>
        </ScrollView>
    </LinearLayout>
</HorizontalScrollView>

Y luego recupere el mapa de bits con una textureView.getBitmap();resolución de 2k.