אתם מגדירים כל תרחיש לדוגמה של CameraX כדי לשלוט בהיבטים שונים של השימוש הפעולות של הפנייה.
לדוגמה, בתרחיש לדוגמה של צילום תמונה, אפשר להגדיר יחס גובה-רוחב ליעד ומצב הבהוב. הקוד הבא מציג דוגמה אחת:
Kotlin
val imageCapture = ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build()
Java
ImageCapture imageCapture = new ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build();
בנוסף לאפשרויות ההגדרה, תרחישים מסוימים לדוגמה חושפים ממשקי API משנים את ההגדרות אחרי שהתרחיש לדוגמה נוצר. מידע על שספציפית לתרחישים לדוגמה ספציפיים, ראו יישום תצוגה מקדימה, ניתוח תמונות, ותמונות תיעוד דיגיטלי.
CameraXConfig
כדי לשמור על פשטות, ל- CameraX יש הגדרות ברירת מחדל, כמו מנהלים פנימיים
ו-handlers שמתאימים לרוב תרחישי השימוש. אבל אם
לאפליקציה יש דרישות מיוחדות או שהיא מעדיפה להתאים אותן אישית
הגדרות אישיות, CameraXConfig
הוא הממשק למטרה הזו.
באמצעות CameraXConfig
, אפליקציה יכולה לבצע את הפעולות הבאות:
- ביצוע אופטימיזציה של זמן האחזור של ההפעלה באמצעות
setAvailableCameraLimiter()
- ספק העורך של האפליקציה ל- CameraX באמצעות
setCameraExecutor()
- החלפת ה-handler שמוגדר כברירת מחדל לתזמון ב-
setSchedulerHandler()
- שינוי רמת הרישום ביומן באמצעות
setMinimumLoggingLevel()
מודל השימוש
התהליך הבא מתאר איך להשתמש ב-CameraXConfig
:
- יוצרים אובייקט
CameraXConfig
עם ההגדרות המותאמות אישית. - ליישם את
CameraXConfig.Provider
ב-Application
, החזרת אובייקטCameraXConfig
ב-getCameraXConfig()
. - עליך להוסיף את הכיתה
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.DEFAULT_FRONT_CAMERA
- בקשת המצלמה האחורית המוגדרת כברירת מחדל באמצעות
CameraSelector.DEFAULT_BACK_CAMERA
- סינון רשימת המכשירים הזמינים לפי
CameraCharacteristics
עםCameraSelector.Builder.addCameraFilter()
דוגמת הקוד הבאה ממחישה איך ליצור 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
דוגמת קוד