אפשרויות הגדרה

אתם מגדירים כל תרחיש לדוגמה של CameraX כדי לשלוט בהיבטים שונים של השימוש הפעולות של הפנייה.

לדוגמה, בתרחיש לדוגמה של צילום תמונה, אפשר להגדיר יחס גובה-רוחב ליעד ומצב הבהוב. הקוד הבא מציג דוגמה אחת:

Kotlin

val imageCapture = ImageCapture.Builder()
    .setFlashMode(...)
    .setTargetAspectRatio(...)
    .build()

Java

ImageCapture imageCapture =
    new ImageCapture.Builder()
        .setFlashMode(...)
        .setTargetAspectRatio(...)
        .build();

בנוסף לאפשרויות ההגדרה, תרחישים מסוימים לדוגמה חושפים ממשקי API משנים את ההגדרות אחרי שהתרחיש לדוגמה נוצר. מידע על שספציפית לתרחישים לדוגמה ספציפיים, ראו יישום תצוגה מקדימה, ניתוח תמונות, ותמונות תיעוד דיגיטלי.

CameraXConfig

כדי לשמור על פשטות, ל- CameraX יש הגדרות ברירת מחדל, כמו מנהלים פנימיים ו-handlers שמתאימים לרוב תרחישי השימוש. אבל אם לאפליקציה יש דרישות מיוחדות או שהיא מעדיפה להתאים אותן אישית הגדרות אישיות, CameraXConfig הוא הממשק למטרה הזו.

באמצעות CameraXConfig, אפליקציה יכולה לבצע את הפעולות הבאות:

מודל השימוש

התהליך הבא מתאר איך להשתמש ב-CameraXConfig:

  1. יוצרים אובייקט CameraXConfig עם ההגדרות המותאמות אישית.
  2. ליישם את CameraXConfig.Provider ב-Application, החזרת אובייקט CameraXConfig ב- getCameraXConfig().
  3. עליך להוסיף את הכיתה Application לקובץ AndroidManifest.xml, בתור שמתוארים כאן.

לדוגמה, דוגמת הקוד הבאה מגבילה את הרישום של CameraX לשגיאות הודעות בלבד:

Kotlin

class CameraApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
           .setMinimumLoggingLevel(Log.ERROR).build()
   }
}

שמירת עותק מקומי של האובייקט CameraXConfig אם האפליקציה צריכה מכירה את התצורה של CameraX לאחר ההגדרה שלה.

מגביל המצלמה

במהלך ההפעלה הראשונה של ProcessCameraProvider.getInstance() CameraX סופר ושואל את המאפיינים של המצלמות הזמינות במכשיר. מכיוון ש- CameraX צריך לתקשר עם רכיבי חומרה, עשויה להימשך זמן קצר מאוד עבור כל מצלמה, במיוחד במכשירים פשוטים. אם האפליקציה משתמשת רק במצלמות ספציפיות במכשיר, כמו מצלמת ברירת המחדל הקדמית, ניתן להגדיר את CameraX להתעלם ממצלמות אחרות, מה שיכול לקצר את זמן האחזור בהפעלה של המצלמות שבהן האפליקציה משתמשת.

אם הערך של CameraSelector עבר כדי CameraXConfig.Builder.setAvailableCamerasLimiter() מסנן מצלמה, CameraX פועלת כאילו המצלמה לא קיימת. עבור לדוגמה, הקוד הבא מגביל את האפליקציה כך שהיא תשתמש רק מצלמה אחורית המוגדרת כברירת מחדל:

Kotlin

class MainApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
              .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA)
              .build()
   }
}

שרשורים

רבים מממשקי ה-API של הפלטפורמות שבהן נבנה CameraX מחייבים חסימה תקשורת בין תהליכים (IPC) לחומרה שעשויה לקחת לפעמים מאות של אלפיות שנייה כדי להגיב. לכן, CameraX קורא לממשקי ה-API האלה רק מ: שרשורי רקע, כדי שה-thread הראשי לא יהיה חסום וממשק המשתמש נשאר נוזל. CameraX מנהלת באופן פנימי את שרשורי הרקע האלה כדי נראה שקוף. עם זאת, אפליקציות מסוימות דורשות בקרה מחמירה של שרשורים. CameraXConfig מאפשרת לאפליקציה להגדיר את השרשורים ברקע שמשתמשים בהם CameraXConfig.Builder.setCameraExecutor() וגם CameraXConfig.Builder.setSchedulerHandler().

מנהל המצלמה

מנהל המצלמה משמש גם לכל הקריאות הפנימיות ל-API של פלטפורמת המצלמה. וגם הקריאות החוזרות (callback) מממשקי ה-API האלה. CameraX מקצה ומנהל Executor כדי לבצע את המשימות האלה. עם זאת, אם האפליקציה שלכם דורשת שליטה מחמירה יותר על שרשורים, תוכלו להשתמש CameraXConfig.Builder.setCameraExecutor()

מטפל במתזמן

ה-handler של המתזמן משמש לתזמון משימות פנימיות במרווחי זמן קבועים, למשל, לנסות לפתוח שוב את המצלמה כשהיא לא זמינה. ה-handler הזה לא מבצע משימות, אלא רק שולח אותן למנהל המצלמה. כמו כן בשימוש בפלטפורמות API מדור קודם המחייבות Handler לקריאה חוזרת (callback). במקרים כאלה, הפרמטר קריאות חוזרות (callback) עדיין נשלחות ישירות אל מפעיל המצלמה. מצלמהX מקצה ומנהלת HandlerThread כדי לבצע את המשימות האלה, אבל אפשר לבטל אותו בעזרת CameraXConfig.Builder.setSchedulerHandler().

רישום

הרישום של CameraX מאפשר לאפליקציות לסנן הודעות Logcat, כי הוא יכול להיות טוב כדי להימנע מהודעות מרובות בקוד ההפקה. CameraX תומך ארבע רמות של רישום ביומן, מהגבוהה ביותר ועד לחמורה ביותר:

  • Log.DEBUG (ברירת מחדל)
  • Log.INFO
  • Log.WARN
  • Log.ERROR

אפשר לעיין במסמכי התיעוד של יומן Android. לקבלת תיאורים מפורטים של רמות היומן האלה. כדאי להשתמש CameraXConfig.Builder.setMinimumLoggingLevel(int) כדי להגדיר את רמת הרישום המתאימה לאפליקציה שלכם.

בחירה אוטומטית

CameraX מספקת באופן אוטומטי פונקציונליות שספציפית למכשיר שהאפליקציה פועלת בו. לדוגמה, CameraX קובע באופן אוטומטי הרזולוציה הטובה ביותר אם לא מציינים רזולוציה, או אם הרזולוציה שאתם מציינים, היא לא נתמכת. כל זה מטופל על ידי הספרייה, וכך צורך בכתיבת קוד ספציפי למכשיר.

המטרה של CameraX היא לאתחל בהצלחה הפעלת מצלמה. כלומר CameraX מתפשרת על רזולוציה ויחס גובה-רוחב בהתאם ליכולת המכשיר. הפריצה יכולה לקרות מהסיבות הבאות:

  • המכשיר לא תומך ברזולוציה המבוקשת.
  • יש בעיות תאימות במכשיר, כמו מכשירים מדור קודם שדורשים כדי שרזולוציות מסוימות יפעלו בצורה תקינה.
  • במכשירים מסוימים, פורמטים מסוימים זמינים רק בהיבט מסוים יחסי גובה-רוחב.
  • למכשיר יש העדפה ל-'mod16 הקרוב ביותר' עבור JPEG או וידאו באמצעות הקידוד. מידע נוסף זמין במאמר הבא: SCALER_STREAM_CONFIGURATION_MAP

למרות ש- CameraX יוצר ומנהל את הסשן, כדאי תמיד לבדוק את החזירו גדלים של תמונות בפלט של התרחיש לדוגמה בקוד שלכם, ולשנות בהתאם.

סיבוב

כברירת מחדל, סיבוב המצלמה מוגדר בהתאם לסיבוב של תצוגת ברירת המחדל במהלך יצירת התרחיש לדוגמה. במקרה ברירת המחדל הזה, CameraX מפיק הפלט שלה מאפשר לאפליקציה להתאים למה שאתם מצפים לראות תצוגה מקדימה. אפשר לשנות את הסיבוב לערך מותאם אישית כדי לתמוך בתצוגה מרובת מסכים מכשירים על ידי מעבר בכיוון המסך הנוכחי בתהליך ההגדרה של תרחיש לדוגמה או באופן דינמי אחרי שהם נוצרו.

האפליקציה שלך יכולה להגדיר את סבב היעד באמצעות הגדרות התצורה. לאחר מכן היא יכולה לעדכן את הגדרות הסבב באמצעות השיטות מממשקי ה-API של התרחיש לדוגמה (כמו ImageAnalysis.setTargetRotation()), גם כשמחזור החיים במצב פעיל. אפשר להשתמש בה כשהאפליקציה נעול בפריסה לאורך, לכן לא מתבצעת הגדרה מחדש אבל בתרחיש לדוגמה של תמונה או ניתוח, עליכם להיות מודעים הסיבוב הנוכחי של המכשיר. לדוגמה, ייתכן שיש צורך במוּדעוּת לסבב כך הפנים מכוונות בצורה נכונה לזיהוי פנים, או שהתמונות מצולמות לרוחב או לאורך.

יכול להיות שהנתונים של התמונות שצולמו יישמרו ללא מידע על הסיבוב. נתוני תצוגת Exif מכיל מידע על הסיבוב כדי שאפליקציות גלריה יוכלו להציג את התמונה בכיוון הנכון אחרי השמירה.

כדי להציג את נתוני התצוגה המקדימה בכיוון הנכון, אפשר להשתמש במטא-נתונים פלט מ- Preview.PreviewOutput() כדי ליצור טרנספורמציות.

דוגמת הקוד הבאה מראה איך להגדיר את הסיבוב באירוע כיוון:

Kotlin

override fun onCreate() {
    val imageCapture = ImageCapture.Builder().build()

    val orientationEventListener = object : OrientationEventListener(this as Context) {
        override fun onOrientationChanged(orientation : Int) {
            // Monitors orientation values to determine the target rotation value
            val rotation : Int = when (orientation) {
                in 45..134 -> Surface.ROTATION_270
                in 135..224 -> Surface.ROTATION_180
                in 225..314 -> Surface.ROTATION_90
                else -> Surface.ROTATION_0
            }

            imageCapture.targetRotation = rotation
        }
    }
    orientationEventListener.enable()
}

Java

@Override
public void onCreate() {
    ImageCapture imageCapture = new ImageCapture.Builder().build();

    OrientationEventListener orientationEventListener = new OrientationEventListener((Context)this) {
       @Override
       public void onOrientationChanged(int orientation) {
           int rotation;

           // Monitors orientation values to determine the target rotation value
           if (orientation >= 45 && orientation < 135) {
               rotation = Surface.ROTATION_270;
           } else if (orientation >= 135 && orientation < 225) {
               rotation = Surface.ROTATION_180;
           } else if (orientation >= 225 && orientation < 315) {
               rotation = Surface.ROTATION_90;
           } else {
               rotation = Surface.ROTATION_0;
           }

           imageCapture.setTargetRotation(rotation);
       }
    };

    orientationEventListener.enable();
}

בהתאם לסיבוב המוגדר, כל תרחיש לדוגמה מבצע סיבוב של נתוני התמונה באופן ישיר, או מספק מטא-נתונים של סיבוב לצרכנים של התמונה שאינה מסובבת .

  • תצוגה מקדימה: פלט המטא-נתונים מסופק כדי לבצע סבב של היעד ידועה באמצעות Preview.getTargetRotation().
  • ImageAnalysis: פלט המטא-נתונים מסופק כך שמאגר הנתונים הזמני של התמונה ידועות ביחס לקואורדינטות של התצוגה.
  • ImageCapture: המטא-נתונים של קובץ ה-XML של התמונה, מאגר הנתונים הזמני או שניהם המטא-נתונים משתנים כדי לשים לב להגדרת הסבב. הערך שהשתנה תלויה בהטמעת HAL.

מלבן חיתוך

כברירת מחדל, מלבן החיתוך הוא מלבן מלא. אפשר להתאים אותו אישית עם ViewPort ו- UseCaseGroup לפי שימוש קיבוץ וההגדרה של אזור התצוגה, CameraX מבטיח אזורי חיתוך התרחישים לדוגמה בקבוצה מצביעים על אותו אזור בחיישן המצלמה.

קטע הקוד הבא מראה איך להשתמש בשתי המחלקות האלה:

Kotlin

val viewPort =  ViewPort.Builder(Rational(width, height), display.rotation).build()
val useCaseGroup = UseCaseGroup.Builder()
    .addUseCase(preview)
    .addUseCase(imageAnalysis)
    .addUseCase(imageCapture)
    .setViewPort(viewPort)
    .build()
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup)

Java

ViewPort viewPort = new ViewPort.Builder(
         new Rational(width, height),
         getDisplay().getRotation()).build();
UseCaseGroup useCaseGroup = new UseCaseGroup.Builder()
    .addUseCase(preview)
    .addUseCase(imageAnalysis)
    .addUseCase(imageCapture)
    .setViewPort(viewPort)
    .build();
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup);

ViewPort מגדיר את שטח מאגר הנתונים הזמני שיהיה גלוי למשתמשי הקצה. לאחר מכן היא מחשבת את CameraX משטח החיתוך הגדול ביותר האפשרי על סמך המאפיינים של אזור התצוגה של תרחישים לדוגמה מצורפים. בדרך כלל, כדי להשיג אפקט WYSIWYG, אפשר להגדיר אזור התצוגה על סמך התרחיש לדוגמה של התצוגה המקדימה. דרך פשוטה לקבל את אזור התצוגה היא כדי להשתמש ב-PreviewView.

קטעי הקוד הבאים מראים איך לקבל את האובייקט ViewPort:

Kotlin

val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort

Java

ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();

בדוגמה שלמעלה, מה האפליקציה מקבלת מ-ImageAnalysis וגם ImageCapture תואם למה שמשתמש הקצה רואה ב-PreviewView, בהנחה סוג קנה המידה של PreviewView מוגדר לברירת המחדל, FILL_CENTER. אחרי ההחלה את מלב החיתוך והסיבוב למאגר הנתונים הזמני, התמונה מכל התרחישים לדוגמה זהה, אם כי ייתכן שגם ברזולוציות שונות. לקבלת מידע נוסף מידע על האופן שבו מחילים את פרטי הטרנספורמציה, ראו טרנספורמציה הפלט.

בחירת מצלמה

CameraX בוחר באופן אוטומטי את מכשיר המצלמה הטוב ביותר עבור האפליקציה שלך בדרישות ובתרחישים לדוגמה. אם אתם רוצים להשתמש במכשיר אחר יש כמה אפשרויות:

דוגמת הקוד הבאה ממחישה איך ליצור CameraSelector השפעה על בחירת המכשירים:

Kotlin

fun selectExternalOrBestCamera(provider: ProcessCameraProvider):CameraSelector? {
   val cam2Infos = provider.availableCameraInfos.map {
       Camera2CameraInfo.from(it)
   }.sortedByDescending {
       // HARDWARE_LEVEL is Int type, with the order of:
       // LEGACY < LIMITED < FULL < LEVEL_3 < EXTERNAL
       it.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
   }

   return when {
       cam2Infos.isNotEmpty() -> {
           CameraSelector.Builder()
               .addCameraFilter {
                   it.filter { camInfo ->
                       // cam2Infos[0] is either EXTERNAL or best built-in camera
                       val thisCamId = Camera2CameraInfo.from(camInfo).cameraId
                       thisCamId == cam2Infos[0].cameraId
                   }
               }.build()
       }
       else -> null
    }
}

// create a CameraSelector for the USB camera (or highest level internal camera)
val selector = selectExternalOrBestCamera(processCameraProvider)
processCameraProvider.bindToLifecycle(this, selector, preview, analysis)

בחירה של כמה מצלמות בו-זמנית

החל מגרסה 1.3 של CameraX, ניתן גם לבחור מספר מצלמות בו-זמנית. למשל, אפשר לקשר למצלמה קדמית ולמצלמה אחורית כדי לצלם תמונות או להקליט סרטונים משתי נקודות המבט בו-זמנית.

כשמשתמשים בתכונה 'מצלמה בו-זמנית', המכשיר יכול להפעיל שתי מצלמות משתמשים בעדשות שונות בו-זמנית, או מפעילים שתי מצלמות אחוריות כמעט באותה עת. בלוק הקוד הבא מראה איך להגדיר שתי מצלמות קוראים לפונקציה bindToLifecycle ואיך לקבל את שני האובייקטים של המצלמה מהמוחזר אובייקט ConcurrentCamera.

Kotlin

// Build ConcurrentCameraConfig
val primary = ConcurrentCamera.SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
)

val secondary = ConcurrentCamera.SingleCameraConfig(
    secondaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
)

val concurrentCamera = cameraProvider.bindToLifecycle(
    listOf(primary, secondary)
)

val primaryCamera = concurrentCamera.cameras[0]
val secondaryCamera = concurrentCamera.cameras[1]

Java

// Build ConcurrentCameraConfig
SingleCameraConfig primary = new SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
);

SingleCameraConfig secondary = new SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
);

ConcurrentCamera concurrentCamera =  
    mCameraProvider.bindToLifecycle(Arrays.asList(primary, secondary));

Camera primaryCamera = concurrentCamera.getCameras().get(0);
Camera secondaryCamera = concurrentCamera.getCameras().get(1);

רזולוציית המצלמה

ניתן לבחור לאפשר ל- CameraX להגדיר את רזולוציית התמונה על סמך שילוב יכולות המכשיר, נתוני החומרה שנתמכים במכשיר רמה, ויחס הגובה-רוחב שסופקו. לחלופין, אפשר להגדיר רזולוציית יעד או יחס גובה-רוחב ספציפי, במקרים שתומכים הגדרה אישית.

רזולוציה אוטומטית

CameraX יכול לקבוע באופן אוטומטי את הגדרות הרזולוציה הטובות ביותר על סמך התרחישים לדוגמה שצוינו ב-cameraProcessProvider.bindToLifecycle(). בכל זמן לציין את כל התרחישים לדוגמה שנדרשים כדי להריץ במקביל סשן בשיחת bindToLifecycle() אחת. CameraX קובעת רזולוציות בהתאם לקבוצת התרחישים לדוגמה, על סמך אפשרויות התמיכה במכשיר ברמת החומרה והתחשבות בשונות הספציפית למכשיר (כאשר מכשיר חורג או לא מתאים להגדרות השידור ). הכוונה היא לאפשר לאפליקציה לפעול במגוון רחב של מכשירים בזמן צמצום נתיבי קוד ספציפיים למכשיר.

יחס הגובה-רוחב שמוגדר כברירת מחדל לצילום תמונות ולניתוח תמונות הוא 4:3.

לתרחישים לדוגמה יש יחס גובה-רוחב שניתן להגדיר, כדי שהאפליקציה תוכל לציין ביחס הגובה-רוחב הרצוי בהתאם לעיצוב של ממשק המשתמש. פלט CameraX מופק אל להתאים ליחסי הגובה-רוחב המבוקשים בדיוק כפי שהמכשיר תומך בהם. אם יש אין תמיכה ברזולוציה של התאמה מדויקת, זו שעונה על הכי הרבה תנאים מסומנת. לכן, האפליקציה מכתיבת איך המצלמה תופיע ו- CameraX קובע את הגדרות הרזולוציה הטובות ביותר כדי לספק במכשירים שונים.

לדוגמה, אפליקציה יכולה לבצע כל אחת מהפעולות הבאות:

  • צריך לציין רזולוציית יעד של 4:3 או 16:9 לתרחיש לדוגמה
  • לציין רזולוציה מותאמת אישית, ש- CameraX תנסה למצוא את התמונה הקרובה ביותר התאמה ל
  • ציון יחס גובה-רוחב לחיתוך של ImageCapture

CameraX בוחרת באופן אוטומטי את רזולוציות המשטח הפנימיות של Camera2. הטבלה הבאה מציגה את הרזולוציות:

תרחיש לדוגמה רזולוציית משטח פנימית רזולוציית נתוני הפלט
תצוגה מקדימה יחס גובה-רוחב: הרזולוציה המתאימה ביותר ליעד הגדרה אישית. רזולוציית משטח פנימית. מטא-נתונים מסופקים כדי לאפשר חיתוך של תצוגה, ומסובבים כדי להציג את יחס הגובה-רוחב של היעד.
רזולוציית ברירת המחדל: הרזולוציה הגבוהה ביותר של התצוגה המקדימה, או הגבוהה ביותר רזולוציה מועדפת למכשיר שתואמת ליחס הגובה-רוחב של התצוגה המקדימה.
רזולוציה מקסימלית: גודל התצוגה המקדימה, כלומר הגודל הטוב ביותר לרזולוציית המסך של המכשיר, או לרזולוציה של 1080p (1920x1080), הקטנה מביניהם.
ניתוח תמונות יחס גובה-רוחב: הרזולוציה שהכי מתאימה ליעד הגדרה אישית. רזולוציית משטח פנימית.
רזולוציית ברירת המחדל: הגדרת ברירת המחדל של רזולוציית היעד היא 640x480. התאמה של רזולוציית היעד ויחס הגובה-רוחב התואם יובילו לרזולוציה הטובה ביותר.
רזולוציה מקסימלית: רזולוציית הפלט המקסימלית של מכשיר המצלמה פורמט YUV_420_888 שמאוחזר מ StreamConfigurationMap.getOutputSizes(). רזולוציית היעד מוגדרת כברירת מחדל כ-640x480, כך שאם ברצונך ברזולוציה גבוהה מ-640x480, עליך להשתמש setTargetResolution() וגם setTargetAspectRatio() כדי לקבל את הרזולוציה הכי קרובה לרזולוציות הנתמכות.
צילום תמונה יחס גובה-רוחב: יחס הגובה-רוחב המתאים ביותר להגדרה. רזולוציית משטח פנימית.
רזולוציית ברירת המחדל: הרזולוציה הגבוהה ביותר שזמינה, או הגבוהה ביותר רזולוציה מועדפת למכשיר שתואמת ליחס הגובה-רוחב של ImageCapture.
רזולוציה מקסימלית: רזולוציית הפלט המקסימלית של מכשיר המצלמה בפורמט JPEG. כדאי להשתמש StreamConfigurationMap.getOutputSizes() כדי לאחזר את זה.

ציון רזולוציה

אפשר להגדיר רזולוציות ספציפיות כשיוצרים תרחישים לדוגמה באמצעות שיטת setTargetResolution(Size resolution), כפי שמוצג בקוד הבא דוגמה:

Kotlin

val imageAnalysis = ImageAnalysis.Builder()
    .setTargetResolution(Size(1280, 720))
    .build()

Java

ImageAnalysis imageAnalysis =
  new ImageAnalysis.Builder()
    .setTargetResolution(new Size(1280, 720))
    .build();

אי אפשר להגדיר גם יחס גובה-רוחב של היעד וגם רזולוציית יעד באותו שימוש מותאמת אישית. הפעולה הזו גורמת להטלת IllegalArgumentException במהלך פיתוח התצורה. לאובייקט.

מציינים את הפתרון Size בקואורדינטה אחרי סיבוב הגדלים הנתמכים על ידי סבב היעד. לדוגמה, במכשיר עם כיוון טבעי לאורך בסיבוב טבעי, שמבקש תמונה לאורך יכולה לציין 480x640, ואותו מכשיר מסובב ב-90 מעלות כיוון מיקוד לרוחב יכול לציין 640x480.

רזולוציית היעד מנסה לקבוע סף מינימלי לתמונה ורזולוציה. רזולוציית התמונה בפועל היא הרזולוציה הקרובה ביותר שזמינה שאינו קטן מרזולוציית היעד, כפי שנקבע לפי הטמעת מצלמה.

עם זאת, אם אין פתרון ששווה ל- או גדול מרזולוציית היעד, הרזולוציה הזמינה הקרובה ביותר קטנה מ- נבחר היעד. רזולוציות עם יחס גובה-רוחב זהה של ניתנת עדיפות גבוהה יותר לרזולוציות של Size שצוינו יחסי גובה-רוחב.

CameraX מחילה את הרזולוציה המתאימה ביותר על סמך הבקשות. אם הצורך העיקרי הוא לספק יחס גובה-רוחב, לציין רק setTargetAspectRatio, ו- CameraX קובע רזולוציה ספציפית שמתאימה למכשיר. אם הצורך העיקרי של האפליקציה הוא לציין רזולוציה כדי ליצור תמונה יעיל יותר לעיבוד (לדוגמה, תמונה קטנה או בגודל בינוני המבוססת על יכולת עיבוד מכשיר), יש להשתמש ב-setTargetResolution(Size resolution).

אם לאפליקציה שלך דרושה רזולוציה מדויקת, כדאי לעיין בטבלה הבאה createCaptureSession() כדי לקבוע אילו רזולוציות מקסימליות נתמכות בכל רמת חומרה. שפת תרגום לראות את הרזולוציות הספציפיות שנתמכות על ידי המכשיר הנוכחי, StreamConfigurationMap.getOutputSizes(int)

אם האפליקציה פועלת ב-Android מגרסה 10 ואילך, אפשר להשתמש isSessionConfigurationSupported() כדי לאמת SessionConfiguration ספציפי.

שליטה בפלט המצלמה

בנוסף לאפשרות להגדיר את פלט המצלמה לפי הצורך עבור כל אחד מהם אישית, מצלמת CameraX מיישמת גם את הממשקים הבאים כדי לתמוך פעולות מצלמה הנפוצות לכל התרחישים לדוגמה:

  • CameraControl מאפשר לך להגדיר תכונות נפוצות של המצלמה.
  • CameraInfo מאפשר לשלוח שאילתה במצב של אותן תכונות מצלמה נפוצות.

אלה התכונות הנתמכות של המצלמה עם CameraControl:

  • שינוי מרחק התצוגה
  • לפיד
  • פוקוס ומדידה (הקשה למיקוד)
  • פיצוי חשיפה

אחזור מופעים של CameraControl ו- CameraInfo

מאחזרים את המופעים של CameraControl ו-CameraInfo באמצעות הפקודה אובייקט Camera שהוחזר על ידי ProcessCameraProvider.bindToLifecycle(). הקוד הבא מציג דוגמה:

Kotlin

val camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// For performing operations that affect all outputs.
val cameraControl = camera.cameraControl
// For querying information and states.
val cameraInfo = camera.cameraInfo

Java

Camera camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// For performing operations that affect all outputs.
CameraControl cameraControl = camera.getCameraControl()
// For querying information and states.
CameraInfo cameraInfo = camera.getCameraInfo()

לדוגמה, אפשר לשלוח זום ופעולות אחרות של CameraControl אחרי בהתקשרות אל bindToLifecycle(). אחרי שמפסיקים או משמידים את הפעילות שמשמשת לקישור במופע של המצלמה, CameraControl לא יכול יותר לבצע פעולות מחזירה ListenableFuture שנכשלה.

שינוי מרחק התצוגה

CameraControl מציע שתי שיטות לשינוי רמת הזום:

  • setZoomRatio() מגדירה את מרחק התצוגה לפי יחס הזום.

    היחס חייב להיות בטווח של CameraInfo.getZoomState().getValue().getMinZoomRatio() והקבוצה CameraInfo.getZoomState().getValue().getMaxZoomRatio(). אחרת, הפונקציה מחזירה את הערך של ListenableFuture שנכשל.

  • setLinearZoom() מגדירה את מרחק התצוגה הנוכחי עם ערך זום ליניארי שנע בין 0 ל-1.0.

    היתרון של זום לינארי הוא שהופך את שדה הראייה (FOV) שינוי מרחק התצוגה. זה הופך אותו למקום אידיאלי לשימוש עם תצוגה Slider.

CameraInfo.getZoomState() מחזירה LiveData של מצב הזום הנוכחי. הערך משתנה כשהמצלמה מאותחל או אם רמת הזום מוגדרת באמצעות setZoomRatio() או setLinearZoom(). קריאה לכל אחת מהשיטות קובעת את גיבוי הערכים ZoomState.getZoomRatio() וגם ZoomState.getLinearZoom() האפשרות הזו שימושית אם רוצים להציג טקסט של יחס הזום לצד פס הזזה. פשוט לצפות בLiveData של ZoomState כדי לעדכן את שניהם בלי שצריך להמרה.

השדה ListenableFuture שמוחזר על ידי שני ממשקי ה-API מאפשר לאפליקציות תישלח הודעה כשבקשה חוזרת עם ערך הזום שצוין הושלמו. בנוסף, אם הגדרת ערך חדש של מרחק מתצוגה בזמן הפעולה הקודמת הפעולה עדיין מתבצעת, ListenableFuture בפעולת הזום הקודמת נכשלה באופן מיידי.

לפיד

CameraControl.enableTorch(boolean) מפעילה או משביתה את הפנס (שנקרא גם פנס).

CameraInfo.getTorchState() יכול לשמש לשאילתה על מצב הלפיד הנוכחי. אפשר לבדוק את הערך שהוחזר לפי CameraInfo.hasFlashUnit() כדי לקבוע אם לפיד זמין. אם לא, מתבצעת התקשרות CameraControl.enableTorch(boolean) גורם לListenableFuture שהוחזרו ישלים באופן מיידי עם תוצאה שנכשלה ומגדיר את מצב הלפיד ל- TorchState.OFF.

כשהפנס מופעל, הוא ממשיך לפעול במהלך צילום תמונה ווידאו ללא קשר להגדרת flashMode. flashMode אינץ' ImageCapture פועל רק כשהלפיד מושבת.

מיקוד ומדידה

CameraControl.startFocusAndMetering() מפעיל מיקוד אוטומטי ומדידת חשיפה על ידי הגדרת אזורים למדידת AF/AE/AWB על סמך ה-FocusMeteringAction הנתון. בדרך כלל משתמשים באפשרות הזו כדי ליישם את הפקודה להתמקד" בהרבה יישומי מצלמה.

MeteringPoint

כדי להתחיל, יוצרים MeteringPoint בשימוש MeteringPointFactory.createPoint(float x, float y, float size) MeteringPoint מייצג נקודה אחת במצלמה Surface. הם מאוחסנים בצורה מנורמלת כך שניתן יהיה להמיר אותה בקלות לקואורדינטות של חיישן אזורי AF/AE/AWB.

הגודל של השדה MeteringPoint נע בין 0 ל-1, כאשר גודל ברירת המחדל הוא 0.15f כשמבצעים קריאה אל MeteringPointFactory.createPoint(float x, float y, float size), אפליקציית CameraX יוצרת אזור של מלבן שממורכז ב-(x, y) עבור size.

הקוד הבא מדגים איך יוצרים MeteringPoint:

Kotlin

// Use PreviewView.getMeteringPointFactory if PreviewView is used for preview.
previewView.setOnTouchListener((view, motionEvent) ->  {
val meteringPoint = previewView.meteringPointFactory
    .createPoint(motionEvent.x, motionEvent.y)
…
}

// Use DisplayOrientedMeteringPointFactory if SurfaceView / TextureView is used for
// preview. Please note that if the preview is scaled or cropped in the View,
// it’s the application's responsibility to transform the coordinates properly
// so that the width and height of this factory represents the full Preview FOV.
// And the (x,y) passed to create MeteringPoint might need to be adjusted with
// the offsets.
val meteringPointFactory = DisplayOrientedMeteringPointFactory(
     surfaceView.display,
     camera.cameraInfo,
     surfaceView.width,
     surfaceView.height
)

// Use SurfaceOrientedMeteringPointFactory if the point is specified in
// ImageAnalysis ImageProxy.
val meteringPointFactory = SurfaceOrientedMeteringPointFactory(
     imageWidth,
     imageHeight,
     imageAnalysis)

startFocusAndMetering ו-FocusMeteringAction

להפעלה startFocusAndMetering() חייבים ליצור FocusMeteringAction, שמורכב מ-MeteringPoints אחד או יותר עם מצב מדידה אופציונלי שילובים מ- FLAG_AF, FLAG_AE, FLAG_AWB. קוד המעקב מדגים את השימוש הזה:

Kotlin

val meteringPoint1 = meteringPointFactory.createPoint(x1, x1)
val meteringPoint2 = meteringPointFactory.createPoint(x2, y2)
val action = FocusMeteringAction.Builder(meteringPoint1) // default AF|AE|AWB
      // Optionally add meteringPoint2 for AF/AE.
      .addPoint(meteringPoint2, FLAG_AF | FLAG_AE)
      // The action is canceled in 3 seconds (if not set, default is 5s).
      .setAutoCancelDuration(3, TimeUnit.SECONDS)
      .build()

val result = cameraControl.startFocusAndMetering(action)
// Adds listener to the ListenableFuture if you need to know the focusMetering result.
result.addListener({
   // result.get().isFocusSuccessful returns if the auto focus is successful or not.
}, ContextCompat.getMainExecutor(this)

כמו שמוצג בקוד הקודם, startFocusAndMetering() לוקח טווח FocusMeteringAction שכולל MeteringPoint אחד עבור AF/AE/AWB אזורי מדידה ו-MeteringPoint נוסף עבור AF ו-AE בלבד.

באופן פנימי, CameraX ממיר אותו ל- Camera2 MeteringRectangles ומגדירה את הערכים המתאימים CONTROL_AF_REGIONS /intl/iw/ CONTROL_AE_REGIONS /intl/iw/ CONTROL_AWB_REGIONS פרמטרים לבקשת הלכידה.

מכיוון שלא כל מכשיר תומך ב-AF/AE/AWB ובמספר אזורים, CameraX מפעילה את FocusMeteringAction עם כל המאמץ. CameraX משתמש במספר המקסימלי של MeteringPoints נתמכים, לפי הסדר שבו נוספו הנקודות. הכול נקודות מטרייה שנוספו אחרי הספירה המקסימלית שהמערכת מתעלמת מהן. לדוגמה, אם FocusMeteringAction מסופק עם 3 MeteringPoints בפלטפורמה שתומכת 2 בלבד, רק ב-2 MeteringPoints הראשונים משתמשים. MeteringPoint הסופי הוא CameraX התעלמה.

פיצוי חשיפה

פיצוי חשיפה שימושי כאשר אפליקציות צריכות לכוונן את החשיפה (EV) מעבר לתוצאת הפלט של החשיפה האוטומטית (AE). פיצוי חשיפה משולבים באופן הבא כדי לקבוע את החשיפה הנדרשת תנאי התמונה הנוכחיים:

Exposure = ExposureCompensationIndex * ExposureCompensationStep

CameraX מספקת Camera.CameraControl.setExposureCompensationIndex() להגדרת פיצוי החשיפה כערך אינדקס.

ערכי אינדקס חיוביים הופכים את התמונה לבהירה יותר, בעוד שערכים שליליים מעמעמים את תמונה. האפליקציות יכולות לשלוח שאילתות על הטווח הנתמך, CameraInfo.ExposureState.exposureCompensationRange() בקטע הבא. אם הערך נתמך, הפונקציה תחזיר ListenableFuture מסתיים כשהערך מופעל בהצלחה בקשה לתיעוד; אם האינדקס שצוין נמצא מחוץ לטווח הנתמך, setExposureCompensationIndex() גורם ל-ListenableFuture שהוחזרו יושלם באופן מיידי עם תוצאה שנכשלה.

CameraX שומר רק את setExposureCompensationIndex() הבולטים האחרונים קריאה לפונקציה מספר פעמים לפני הבקשה הקודמת אז הפעולה גורמת לביטול ההפעלה.

קטע הקוד הבא מגדיר מדד של פיצוי חשיפה ורושם קריאה חוזרת (callback) למקרים שבהם בוצעה הבקשה לשינוי החשיפה:

Kotlin

camera.cameraControl.setExposureCompensationIndex(exposureCompensationIndex)
   .addListener({
      // Get the current exposure compensation index, it might be
      // different from the asked value in case this request was
      // canceled by a newer setting request.
      val currentExposureIndex = camera.cameraInfo.exposureState.exposureCompensationIndex
      …
   }, mainExecutor)
  • Camera.CameraInfo.getExposureState() מאחזר את הנוכחי ExposureState כולל:

    • היכולת לתמוך בבקרה על פיצוי החשיפה.
    • המדד הנוכחי של פיצוי החשיפה.
    • טווח המדד של פיצוי החשיפה.
    • השלב של פיצוי החשיפה המשמש בערך פיצוי החשיפה באמצעות חישוב.

לדוגמה, הקוד הבא מאתחל את ההגדרות של חשיפה SeekBar עם ExposureState הנוכחי ערכים:

Kotlin

val exposureState = camera.cameraInfo.exposureState
binding.seekBar.apply {
   isEnabled = exposureState.isExposureCompensationSupported
   max = exposureState.exposureCompensationRange.upper
   min = exposureState.exposureCompensationRange.lower
   progress = exposureState.exposureCompensationIndex
}

מקורות מידע נוספים

למידע נוסף על CameraX, ניתן לעיין במשאבים הנוספים הבאים.

Codelab

  • תחילת העבודה עם CameraX
  • דוגמת קוד

  • אפליקציות לדוגמה של CameraX
  • קהילת המפתחים

    קבוצת דיון של Android CameraX