يمكنك ضبط كل حالة استخدام من حالات استخدام 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
لطلبات إعادة الاتصال. في هذه الحالات، لا يزال يتم إرسال
طلبات معاودة الاتصال مباشرةً إلى مسؤول تنفيذ الكاميرا. تحدِّد 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
، وكيفية جلب كائنَي "الكاميرا" من كائن "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()
معلّق فقط، ويطلب الوظيفة عدة مرات قبل أن يتم تنفيذ الطلب السابق
لتنفيذ نتائج أثناء إلغائه.
يضبط المقتطف التالي فهرس تعويض عدد المشاهدين ويسجِّل استدعاء عند تنفيذ طلب تغيير عدد المشاهدين:
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، يمكنك الرجوع إلى المراجع الإضافية التالية.
درس تطبيقي حول الترميز
نموذج رمز