Saída de transformação

A saída de um caso de uso do CameraX é ambivalente: o buffer e as informações de transformação. O buffer é uma matriz de bytes, e as informações de transformação são o modo como o buffer precisa ser cortado e girado antes de ser mostrado aos usuários finais. A maneira de aplicar a transformação depende do formato do buffer.

ImageCapture

Para o caso de uso de ImageCapture, o buffer de retângulo cortado é aplicado antes de salvar no disco, e a rotação é salva nos dados EXIF. Não é necessário fazer mais nada no app.

Visualização

Para o caso de uso de Preview, é possível conseguir as informações de transformação chamando SurfaceRequest.setTransformationInfoListener(). Toda vez que a transformação é atualizada, o autor da chamada recebe um novo objeto SurfaceRequest.TransformationInfo.

A maneira de aplicar as informações de transformação depende da origem de Surface e normalmente não é trivial. Use PreviewView se o objetivo for simplesmente exibir a visualização. PreviewView é uma visualização personalizada que processa automaticamente a transformação. Para usos avançados, quando você precisar editar o stream de visualização, como com o OpenGL, veja o exemplo de código no app de teste principal do CameraX.

Coordenadas de transformação

Outra tarefa comum é trabalhar com as coordenadas em vez do buffer, como desenhar uma caixa em torno do rosto detectado na visualização. Nesses casos, é preciso transformar as coordenadas do rosto detectado da análise de imagem para a visualização.

O snippet de código a seguir cria uma matriz que mapeia de coordenadas de análise de imagem para coordenadas PreviewView. Para transformar as coordenadas (x, y) com uma Matrix, consulte Matrix.mapPoints().

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
}