출력 변환

CameraX 사용 사례의 출력은 버퍼와 변환 정보 두 가지입니다. 버퍼는 바이트 배열이고 변환 정보는 최종 사용자에게 표시하기 전에 버퍼를 자르고 회전하는 방법입니다. 변환을 적용하는 방법은 버퍼 형식에 따라 달라집니다.

ImageCapture

ImageCapture 사용 사례의 경우 디스크에 저장하기 전에 자르기 직사각형 버퍼가 적용되고 회전은 EXIF 데이터에 저장됩니다. 앱에서 필요한 추가 작업은 없습니다.

미리보기

Preview 사용 사례의 경우 SurfaceRequest.setTransformationInfoListener()를 호출하여 변환 정보를 가져올 수 있습니다. 변환이 업데이트될 때마다 호출자는 새 SurfaceRequest.TransformationInfo 객체를 수신합니다.

변환 정보를 적용하는 방법은 Surface의 소스에 따라 달라지며 일반적으로 그리 간단하지는 않습니다. 단순히 미리보기를 표시하는 것이 목표라면 PreviewView를 사용하세요. PreviewView는 자동으로 변환을 처리하는 맞춤 뷰입니다. OpenGL과 같이 미리보기 스트림을 수정해야 하는 고급 사용의 경우 CameraX 핵심 테스트 앱에서 코드 샘플을 참고하세요.

좌표 변환

또 다른 일반적인 작업은 미리보기에서 감지된 얼굴 주위에 상자를 그리는 것과 같이 버퍼 대신 좌표를 사용하는 것입니다. 이와 같은 경우에는 감지된 얼굴의 좌표를 이미지 분석에서 미리보기로 변환해야 합니다.

다음 코드 스니펫은 이미지 분석 좌표에서 PreviewView 좌표로 매핑되는 행렬을 만듭니다. (x, y) 좌표를 Matrix로 변환하려면 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
}

자바

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;
}