Resultado de la transformación

El resultado de un caso de uso de CameraX es doble: el búfer y la información de transformación. El búfer es un array de bytes y la información de transformación es cómo se debe recortar y rotar el búfer antes de mostrarse a los usuarios finales. Cómo debes aplicar la transformación depende del formato del búfer.

ImageCapture

En el caso de uso de ImageCapture, el búfer de rectángulo de recorte se aplica antes de guardar la imagen en el disco y la rotación se guarda en los datos EXIF. No se requieren acciones adicionales de la app.

Vista previa

Para el caso de uso de Preview, puedes llamar a SurfaceRequest.setTransformationInfoListener() a fin de obtener la información de transformación. Cada vez que se actualiza la transformación, el llamador recibe un objeto SurfaceRequest.TransformationInfo nuevo.

Cómo debes aplicar la información de transformación depende de la fuente de Surface y no suele ser trivial. Si el objetivo es simplemente mostrar la vista previa, usa PreviewView. PreviewView es una vista personalizada que controla la transformación automáticamente. Para usos avanzados, cuando necesitas editar la transmisión de vista previa, como con OpenGL, observa la muestra de código en la app de prueba principal de CameraX.

Transformación de coordenadas

Otra tarea común es trabajar con las coordenadas en lugar del búfer, por ejemplo, dibujar un recuadro alrededor del rostro que se detectó en la vista previa. En estos casos, debes transformar las coordenadas del rostro detectado en el análisis de imágenes para obtener una vista previa.

Con el siguiente fragmento de código, se crea una matriz que realiza un mapeo de coordenadas de análisis de imágenes a coordenadas PreviewView. Para transformar las coordenadas (x, y) con un elemento Matrix, consulta Matrix.mapPoints().

Kotlin

fun getCorrectionMatrix(imageProxy: ImageProxy, previewView: PreviewView) : Matrix {
   val cropRect = imageProxy.cropRect
   val rotationDegrees = imageProxy.imageInfo.rotationDegrees
   val matrix = Matrix()

   // A float array of the source vertices (crop rect) in clockwise order.
   val source = floatArrayOf(
       cropRect.left.toFloat(),
       cropRect.top.toFloat(),
       cropRect.right.toFloat(),
       cropRect.top.toFloat(),
       cropRect.right.toFloat(),
       cropRect.bottom.toFloat(),
       cropRect.left.toFloat(),
       cropRect.bottom.toFloat()
   )

   // A float array of the destination vertices in clockwise order.
   val destination = floatArrayOf(
       0f,
       0f,
       previewView.width.toFloat(),
       0f,
       previewView.width.toFloat(),
       previewView.height.toFloat(),
       0f,
       previewView.height.toFloat()
   )

   // The destination vertexes need to be shifted based on rotation degrees. The
   // rotation degree represents the clockwise rotation needed to correct the image.

   // Each vertex is represented by 2 float numbers in the vertices array.
   val vertexSize = 2
   // The destination needs to be shifted 1 vertex for every 90° rotation.
   val shiftOffset = rotationDegrees / 90 * vertexSize;
   val tempArray = destination.clone()
   for (toIndex in source.indices) {
       val fromIndex = (toIndex + shiftOffset) % source.size
       destination[toIndex] = tempArray[fromIndex]
   }
   matrix.setPolyToPoly(source, 0, destination, 0, 4)
   return matrix
}

Java

Matrix getMappingMatrix(ImageProxy imageProxy, PreviewView previewView) {
   Rect cropRect = imageProxy.getCropRect();
   int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees();
   Matrix matrix = new Matrix();

   // A float array of the source vertices (crop rect) in clockwise order.
   float[] source = {
       cropRect.left,
       cropRect.top,
       cropRect.right,
       cropRect.top,
       cropRect.right,
       cropRect.bottom,
       cropRect.left,
       cropRect.bottom
   };

   // A float array of the destination vertices in clockwise order.
   float[] destination = {
       0f,
       0f,
       previewView.getWidth(),
       0f,
       previewView.getWidth(),
       previewView.getHeight(),
       0f,
       previewView.getHeight()
   };

   // The destination vertexes need to be shifted based on rotation degrees.
   // The rotation degree represents the clockwise rotation needed to correct
   // the image.

   // Each vertex is represented by 2 float numbers in the vertices array.
   int vertexSize = 2;
   // The destination needs to be shifted 1 vertex for every 90° rotation.
   int shiftOffset = rotationDegrees / 90 * vertexSize;
   float[] tempArray = destination.clone();
   for (int toIndex = 0; toIndex < source.length; toIndex++) {
       int fromIndex = (toIndex + shiftOffset) % source.length;
       destination[toIndex] = tempArray[fromIndex];
   }
   matrix.setPolyToPoly(source, 0, destination, 0, 4);
   return matrix;
}