Преобразование выходных данных

Выходные данные варианта использования CameraX состоят из двух частей: буфера и информации о преобразовании. Буфер представляет собой массив байтов, а информация о преобразовании указывает, как буфер должен быть обрезан и повернут перед показом конечным пользователям. Способ применения преобразования зависит от формата буфера.

ImageCapture

В случае с ImageCapture буфер кадрирования применяется перед сохранением на диск, а поворот сохраняется в данных EXIF. Дополнительных действий от приложения не требуется.

Предпросмотр

Для варианта использования Preview вы можете получить информацию о преобразовании, вызвав SurfaceRequest.setTransformationInfoListener(). При каждом обновлении преобразования вызывающий объект получает новый объект SurfaceRequest.TransformationInfo.

Способ применения информации о преобразовании зависит от источника Surface и обычно нетривиален. Если цель – просто показать предварительную версию, используйте PreviewView. PreviewView – это специальное представление, которое автоматически обрабатывает преобразование. Если вам нужно отредактировать поток предварительного просмотра, например с помощью OpenGL, посмотрите пример кода в основном тестовом приложении CameraX.

Преобразование координат

Другая распространенная задача – работа с координатами вместо буфера, например рисование рамки вокруг обнаруженного лица в режиме предварительного просмотра. В таких случаях вам необходимо преобразовать координаты обнаруженного лица из анализа изображения в предварительный просмотр.

В следующем фрагменте кода создается матрица, которая сопоставляет координаты анализа изображений с координатами PreviewView. Чтобы преобразовать координаты (x, y) с помощью Matrix, ознакомьтесь с разделом 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
}