يمكنك ضبط كل حالة استخدام من حالات استخدام CameraX للتحكّم في جوانب مختلفة من عمليات حالة الاستخدام.
على سبيل المثال، في حالة استخدام "التقاط الصور"، يمكنك ضبط نسبة عرض إلى ارتفاع مستهدفة ووضع فلاش. يعرض الرمز البرمجي التالي مثالاً واحدًا:
Kotlin
val imageCapture = ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build()
Java
ImageCapture imageCapture = new ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build();
بالإضافة إلى خيارات الضبط، توفّر بعض حالات الاستخدام واجهات برمجة تطبيقات لتغيير الإعدادات ديناميكيًا بعد إنشاء حالة الاستخدام. للحصول على معلومات عن الإعدادات الخاصة بحالات الاستخدام الفردية، يُرجى الاطّلاع على تنفيذ معاينة وتحليل الصور والتقاط الصور.
CameraXConfig
للتبسيط، تتضمّن CameraX إعدادات تلقائية، مثل مشغّلات داخلية
ومعالجات مناسبة لمعظم سيناريوهات الاستخدام. ومع ذلك، إذا كان
تطبيقك يتضمّن متطلبات خاصة أو كان يفضّل تخصيص تلك الإعدادات،
CameraXConfig
هو الواجهة المخصّصة لهذا الغرض.
باستخدام CameraXConfig
، يمكن للتطبيق تنفيذ ما يلي:
- يمكنك تحسين وقت الاستجابة في بدء التشغيل باستخدام
setAvailableCameraLimiter()
. - قدِّم مشغّل التطبيق إلى CameraX باستخدام
setCameraExecutor()
. - استبدِل معالِج جدولة المهام التلقائي برمز
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() } }
Threads
تتطلّب العديد من واجهات برمجة التطبيقات الخاصة بالنظام الأساسي التي تم إنشاء CameraX استنادًا إليها حظر
التواصل بين العمليات (IPC) مع الأجهزة التي يمكن أن تستغرق أحيانًا مئات
المللي ثانية للردّ. لهذا السبب، لا تستدعي CameraX واجهات برمجة التطبيقات هذه إلا من سلاسل التعليمات التي تعمل في الخلفية، وذلك حتى لا يتم حظر السلسلة الرئيسية وتبقى واجهة المستخدم płynna. تدير CameraX هذه سلاسل المهام في الخلفية داخليًا لكي يبدو هذا
السلوك شفافيًا. ومع ذلك، تتطلّب بعض التطبيقات التحكّم بشكل صارم في سلاسل المحادثات. يتيح CameraXConfig
للتطبيق ضبط سلاسل المهام التي تعمل في الخلفية
التي يتم استخدامها من خلال
CameraXConfig.Builder.setCameraExecutor()
و
CameraXConfig.Builder.setSchedulerHandler()
.
منفّذ الكاميرا
يتم استخدام أداة تنفيذ الكاميرا لجميع طلبات البيانات الداخلية من Camera platform API، بالإضافة
إلى طلبات البيانات المرسَلة من هذه واجهات برمجة التطبيقات. تخصص CameraX Executor
داخليًا وتُديره لتنفيذ هذه المهام.
ومع ذلك، إذا كان تطبيقك يتطلّب التحكّم بشكل أكثر صرامة في سلاسل المحادثات، استخدِم
CameraXConfig.Builder.setCameraExecutor()
.
معالج المخطِّط
يُستخدَم معالِج المخطِّط لتحديد موعد المهام الداخلية على فترات ثابتة،
مثل إعادة محاولة فتح الكاميرا عندما لا تكون متاحة. لا يؤدي معالِج الأحداث هذا
إلى تنفيذ المهام، بل يرسلها فقط إلى مشغّل الكاميرا. ويُستخدَم أيضًا
في بعض الأحيان على منصات واجهات برمجة التطبيقات القديمة التي تتطلّب استخدام Handler
لطلبات إعادة الاتصال. في هذه الحالات، لا يتم إرسال callbacks إلا إلى مشغّل الكاميرا مباشرةً. تحدِّد CameraX
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 نتائج للسماح للتطبيق بمطابقة ما تتوقّع أن تراه في المعاينة. يمكنك تغيير التدوير إلى قيمة مخصّصة لتتوافق مع الأجهزة التي تتضمّن عدّة شاشة، وذلك من خلال ضبط اتجاه الشاشة الحالي عند ضبط عناصر حالة الاستخدام أو بشكل ديناميكي بعد إنشائها.
يمكن لتطبيقك ضبط سرعة الدوران المستهدَفة باستخدام إعدادات الضبط. ويمكنه بعد ذلك تعديل إعدادات التدوير باستخدام الطرق الواردة في واجهات برمجة تطبيقات حالات الاستخدام (مثل
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()
. - تحليل الصور: يتم توفير مخرجات البيانات الوصفية لكي تكون إحداثيات ملف التخزين المؤقت للصورة معلومة بالنسبة إلى إحداثيات الشاشة.
- ImageCapture: يتم تغيير البيانات الوصفية Exif للصورة أو المخزن المؤقت أو كليهما لتسجيل إعدادات التدوير. تعتمد القيمة التي تم تغييرها على طريقة تنفيذ 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
. بعد تطبيق
مستطيل الاقتصاص والدوران على ذاكرة التخزين المؤقت للإخراج، تكون الصورة من جميع حالات الاستخدام
نفسها، إلا أنّه من المحتمل أن تكون بدرجات دقة مختلفة. لمزيد من
المعلومات عن كيفية تطبيق معلومات التحويل، اطّلِع على transform
output.
اختيار الكاميرا
يختار 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
، وكيفية الحصول على كلا عنصرَي Camera من عنصر
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)، أيهما أصغر. | ||
تحليل الصور | نسبة العرض إلى الارتفاع: هي درجة الدقة التي تناسب الهدف بشكلٍ أفضل في الإعداد. | درجة دقة السطح الداخلي |
درجة الدقة التلقائية: الإعداد التلقائي لدرجة الدقة المستهدَفة هو 640×480. يؤدي تعديل كلّ من درجة الدقة المستهدَفة ونسبة العرض إلى الارتفاع المقابلة إلى الحصول على أفضل درجة دقة متوافقة. | ||
الحد الأقصى للدقة: الحد الأقصى لدقة الإخراج لجهاز الكاميرا بتنسيق
YUV_420_888 الذي يتم استرجاعه من
StreamConfigurationMap.getOutputSizes() .
يتم ضبط درجة الدقة المستهدَفة على 640×480 تلقائيًا، لذا إذا أردت درجة دقة أكبر من 640×480، عليك استخدام
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
في إطار
الإحداثيات بعد تدوير الأحجام المتوافقة حسب درجة التدوير المستهدَفة. على سبيل المثال، يمكن لجهاز
بالاتجاه العمودي الطبيعي في الاتجاه الطبيعي المستهدَف الذي يطلب
صورة عمودية تحديد 480×640، ويمكن للجهاز نفسه الذي تم تدويره 90 درجة
والذي يستهدف الاتجاه الأفقي تحديد 640×480.
تحاول درجة الدقة المستهدَفة وضع حدّ أدنى لدرجة دقة الصورة. دقة الصورة الفعلية هي أقرب درجة دقة متاحة من حيث الحجم ليست أصغر من درجة الدقة المستهدَفة، وذلك وفقًا لما تحدّده طريقة تنفيذ الكاميرا.
ومع ذلك، إذا لم تتوفّر درجة دقة مساوية أو
أكبر من درجة الدقة المستهدَفة، يتم اختيار أقرب درجة دقة متاحة أصغر من
درجة الدقة المستهدَفة. تُعطى الأولوية الأعلى لدرجات الدقة التي تتوافق مع نسبة العرض إلى الارتفاع نفسها في
Size
المقدَّمة مقارنةً بدرجات الدقة التي تتوافق مع نسب عرض إلى ارتفاع مختلفة.
تطبِّق CameraX أفضل درجة دقة مناسبة استنادًا إلى الطلبات. إذا كانت
الحاجة الأساسية هي استيفاء نسبة العرض إلى الارتفاع، حدِّد setTargetAspectRatio
فقط،
وحدِّد CameraX درجة دقة معيّنة مناسبة استنادًا إلى الجهاز.
إذا كانت الحاجة الأساسية للتطبيق هي تحديد درجة دقة لجعل معالجة
الصور أكثر فعالية (على سبيل المثال، صورة صغيرة أو متوسطة الحجم استنادًا إلى
قدرة الجهاز على المعالجة)، استخدِم setTargetResolution(Size resolution)
.
إذا كان تطبيقك يتطلّب درجة دقة معيّنة، اطّلِع على الجدول ضمن
createCaptureSession()
لتحديد الحد الأقصى لدرجات الدقة المتوافقة مع كل مستوى من مستويات الأجهزة. للاطّلاع على درجات الدقة المحدّدة التي يتوافق معها الجهاز الحالي، يُرجى الاطّلاع على StreamConfigurationMap.getOutputSizes(int)
.
إذا كان تطبيقك يعمل بنظام التشغيل Android 10 أو إصدار أحدث، يمكنك استخدام
isSessionConfigurationSupported()
لإثبات ملكية SessionConfiguration
معيّن.
التحكّم في إخراج الكاميرا
بالإضافة إلى السماح لك بضبط إخراج الكاميرا حسب الحاجة لكل حالة استخدام فردیة، تطبِّق CameraX أيضًا الواجهات التالية لتفعيل عمليات الكاميرا الشائعة لجميع حالات الاستخدام المرتبطة:
- يتيح لك تطبيق
CameraControl
ضبط ميزات الكاميرا الشائعة. - يتيح لك
CameraInfo
طلب حالات ميزات الكاميرا الشائعة هذه.
في ما يلي ميزات الكاميرا المتوافقة مع CameraControl:
- Zoom
- ضوء
- التركيز والقياس (النقر للتركيز)
- تعويض درجة الإضاءة
الحصول على نُسخ من 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
تعذُّرًا.
Zoom
يوفّر 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()
.
يكون هذا مفيدًا إذا كنت تريد عرض نص نسبة التكبير/التصغير بجانب شريط تمرير.
ما عليك سوى مراقبة ZoomState
LiveData
لتعديل كليهما بدون الحاجة إلى إجراء
تحويل.
توفّر السمة ListenableFuture
التي تعرضها كلتا واجهات برمجة التطبيقات خيارًا للتطبيقات
لتلقّي إشعار عند اكتمال طلب متكرّر بالقيمة المحدّدة للتكبير/التصغير. بالإضافة إلى ذلك، في حال ضبط قيمة تكبير جديدة أثناء تنفيذ العملية السابقة
، سيتعذّر ListenableFuture
لعملية التكبير السابقة
على الفور.
ضوء
CameraControl.enableTorch(boolean)
تفعيل ضوء الفلاش أو إيقافه
يمكن استخدام CameraInfo.getTorchState()
للاستعلام عن حالة مصباح يدوي الحالي. يمكنك التحقّق من القيمة المعروضة
بواسطة
CameraInfo.hasFlashUnit()
لتحديد ما إذا كان مصباح يدوي متوفّرًا. وإذا لم يكن الأمر كذلك، يؤدي استدعاء
CameraControl.enableTorch(boolean)
إلى إكمالListenableFuture
المعروض على الشاشة
على الفور بنتيجة تشير إلى تعذُّر إكمال العملية وضبط حالة مصباح يدوي على
TorchState.OFF
.
عند تفعيل ضوء الفلاش، يظل مُفعَّلاً أثناء التقاط الصور والفيديوهات
بغض النظر عن إعداد flashMode. لا يعمل الرمز
flashMode
في
ImageCapture
إلا عندما يكون مصباح يدوي غير مفعَّل.
التركيز وقياس الإضاءة
CameraControl.startFocusAndMetering()
يؤدي إلى بدء التركيز التلقائي وقياس التعريض من خلال ضبط مناطق قياس التركيز التلقائي/التعريض/التوازن التلقائي للأبيض والأسود
استنادًا إلى FocusMeteringAction المحدَّد. ويُستخدَم هذا الإجراء غالبًا لتنفيذ ميزة "النقر
للتركيز" في العديد من تطبيقات الكاميرا.
MeteringPoint
للبدء، أنشئ
MeteringPoint
باستخدام
MeteringPointFactory.createPoint(float x, float y, float
size)
.
يمثّل الرمز MeteringPoint
نقطة واحدة على الكاميرا
Surface
. ويتم تخزينه في تنسيق موحّد
حتى يمكن تحويله بسهولة إلى إحداثيات أداة الاستشعار لتحديد مناطق
ضبط التركيز التلقائي/ضبط الإضاءة التلقائية/ضبط التوازن الأبيض التلقائي.
يتراوح حجم 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
واحد لمناطق قياس الإضاءة لميزة "ضبط التركيز التلقائي/ضبط الإضاءة التلقائي/ضبط توازن اللون الأبيض تلقائيًا" ونقطة قياس أخرى لميزة "ضبط التركيز التلقائي" وميزة "ضبط الإضاءة التلقائي" فقط.
وداخليًا، تحوّل CameraX هذا الطلب إلى Camera2
MeteringRectangles
وتضبط المَعلمات المقابلة
CONTROL_AF_REGIONS
/
CONTROL_AE_REGIONS
/
CONTROL_AWB_REGIONS
في طلب الالتقاط.
بما أنّ بعض الأجهزة لا تتيح استخدام ميزة ضبط التركيز التلقائي/التوازن التلقائي للأبيض والأسود/ضبط التوازن التلقائي للألوان ومناطق متعددة، تنفِّذ CameraXFocusMeteringAction
بأفضل الجهود. يستخدم CameraX الحد الأقصى
لعدد نقاط القياس المتاحة، وذلك بالترتيب الذي تمت فيه إضافة النقاط. يتم تجاهل كل
MeteringPoints التي تمت إضافتها بعد الحد الأقصى لعدد النقاط. على سبيل المثال، إذا تم تزويد
FocusMeteringAction
بـ 3 نقاط قياس على منصة تتيح استخدام
نقطتَين فقط، سيتم استخدام أول نقطتَي قياس فقط. يتم تجاهل 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، يمكنك الرجوع إلى المراجع الإضافية التالية.
الدرس التطبيقي حول الترميز
نموذج رمز