הפלט של תרחיש לדוגמה ב-CameraX הוא דו-שכבתי: המאגר והמידע על הטרנספורמציה. המאגר הוא מערך בייטים, והמידע על הטרנספורמציה הוא אופן החיתוך והסיבוב של המאגר לפני שהוא מוצג למשתמשי הקצה. אופן החלת הטרנספורמציה תלוי בפורמט של המאגר.
ImageCapture
בתרחיש לדוגמה ImageCapture
, מאגר ה-rect של החיתוך מיושם לפני השמירה בדיסק, והסיבוב נשמר בנתוני ה-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 }
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; }