يمكنك ضبط كل حالة استخدام في CameraX للتحكّم في الجوانب المختلفة لعمليات حالة الاستخدام.
على سبيل المثال، في حالة استخدام التقاط الصور، يمكنك ضبط نسبة العرض إلى الارتفاع المستهدَفة ووضع الفلاش. يوضّح الرمز التالي مثالاً واحدًا:
Kotlin
val imageCapture = ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build()
Java
ImageCapture imageCapture = new ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build();
بالإضافة إلى خيارات الضبط، تعرض بعض حالات الاستخدام واجهات برمجة تطبيقات لتغيير الإعدادات ديناميكيًا بعد إنشاء حالة الاستخدام. للحصول على معلومات حول الإعدادات الخاصة بحالات الاستخدام الفردية، يُرجى الاطّلاع على تنفيذ المعاينة وتحليل الصور والتقاط الصور.
CameraXConfig
For simplicity, CameraX has default configurations such as internal executors
and handlers that are suitable for most usage scenarios. ومع ذلك، إذا كان تطبيقك يتطلّب إعدادات خاصة أو إذا كنت تفضّل تخصيص هذه الإعدادات، يمكنك استخدام واجهة 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() } }
سلاسل المحادثات
تتطلّب العديد من واجهات برمجة التطبيقات الخاصة بالمنصة التي تستند إليها مكتبة CameraX حظر
التواصل البيني للعمليات (IPC) مع الأجهزة التي قد تستغرق أحيانًا مئات
المللي ثانية للاستجابة. لهذا السبب، لا تستدعي CameraX واجهات برمجة التطبيقات هذه إلا من سلاسل التعليمات في الخلفية، حتى لا يتم حظر سلسلة التعليمات الرئيسية وتظل واجهة المستخدم سلسة. تدير واجهة برمجة التطبيقات CameraX سلاسل المهام في الخلفية داخليًا لكي يظهر هذا السلوك بشكل واضح. ومع ذلك، تتطلب بعض التطبيقات تحكمًا صارمًا
في سلاسل المحادثات. تتيح السمة CameraXConfig للتطبيق ضبط سلاسل المحادثات في الخلفية
التي يتم استخدامها من خلال
CameraXConfig.Builder.setCameraExecutor()
و
CameraXConfig.Builder.setSchedulerHandler().
Camera Executor
يُستخدَم منفِّذ الكاميرا لجميع طلبات البيانات من واجهة برمجة التطبيقات الداخلية لمنصة الكاميرا، بالإضافة إلى عمليات الاستدعاء من واجهات برمجة التطبيقات هذه. تخصّص CameraX وتدير Executor داخليًا لتنفيذ هذه المهام.
ومع ذلك، إذا كان تطبيقك يتطلّب تحكّمًا أكثر صرامة في سلاسل المحادثات، استخدِم
CameraXConfig.Builder.setCameraExecutor().
معالج أداة الجدولة
يُستخدَم معالج الجدولة لجدولة المهام الداخلية على فترات زمنية ثابتة،
مثل إعادة محاولة فتح الكاميرا عندما لا تكون متاحة. لا ينفّذ هذا المعالج
المهام، بل يرسلها فقط إلى منفّذ الكاميرا. ويُستخدم أيضًا في بعض الأحيان على منصات واجهات برمجة التطبيقات القديمة التي تتطلب Handler لعمليات الاستدعاء. وفي هذه الحالات، لا يتم إرسال عمليات الاستدعاء إلا إلى منفّذ الكاميرا مباشرةً. تخصّص CameraX
وتدير HandlerThread داخليًا
لتنفيذ هذه المهام،
ولكن يمكنك إلغاؤه باستخدام CameraXConfig.Builder.setSchedulerHandler().
تسجيل
يتيح تسجيل CameraX للتطبيقات فلترة رسائل logcat، لأنّها قد تكون ممارسة جيدة لتجنُّب الرسائل المطوّلة في رمز الإنتاج. تتيح مكتبة CameraX أربعة مستويات من التسجيل، من الأكثر تفصيلاً إلى الأكثر خطورة:
Log.DEBUG(تلقائي)Log.INFOLog.WARNLog.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(). - ImageAnalysis: يتم توفير مخرجات البيانات الوصفية بحيث تكون إحداثيات مخزن الصور المؤقت معروفة بالنسبة إلى إحداثيات العرض.
- 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. بعد تطبيق
مستطيل الاقتصاص والتدوير على المخزن المؤقت للإخراج، تكون الصورة من جميع حالات الاستخدام
هي نفسها، ولكن ربما بدرجات دقة مختلفة. لمزيد من المعلومات حول كيفية تطبيق معلومات التحويل، يُرجى الاطّلاع على تحويل الناتج.
اختيار الكاميرا
تختار واجهة 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 تلقائيًا. يوضح الجدول التالي درجات الدقة:
| حالة الاستخدام | حلّ المشاكل على مستوى السطح الداخلي | دقة بيانات الناتج |
|---|---|---|
| معاينة | نسبة العرض إلى الارتفاع: هي درجة الدقة التي تناسب الهدف على أفضل نحو وفقًا للإعدادات. | دقة السطح الداخلي يتم توفير البيانات الوصفية للسماح لـ View بالاقتصاص وتغيير الحجم والتدوير لنسبة العرض إلى الارتفاع المستهدَفة. |
| درجة الدقة التلقائية: أعلى درجة دقة للمعاينة أو أعلى درجة دقة يفضّلها الجهاز وتتطابق مع نسبة العرض إلى الارتفاع في المعاينة | ||
| الحد الأقصى للدقة: حجم المعاينة، والذي يشير إلى أفضل حجم يتطابق مع دقة شاشة الجهاز، أو إلى 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 في إطار الإحداثيات بعد تدوير الأحجام المتوافقة حسب درجة التدوير المستهدَفة. على سبيل المثال، يمكن لجهاز ذي اتجاه طبيعي عمودي في تدوير مستهدف طبيعي يطلب صورة عمودية تحديد 480 × 640، ويمكن للجهاز نفسه، الذي تم تدويره 90 درجة واستهداف الاتجاه الأفقي، تحديد 640 × 480.
تحاول الدقة المستهدفة وضع حد أدنى لدقة الصورة. دقة الصورة الفعلية هي أقرب دقة متاحة في الحجم الذي لا يقل عن الدقة المستهدفة، على النحو الذي يحدده تنفيذ الكاميرا.
ومع ذلك، إذا لم تتوفّر دقة تساوي الدقة المستهدَفة أو أكبر منها، يتم اختيار أقرب دقة متاحة أصغر من الدقة المستهدَفة. يتم منح درجات الدقة التي لها نسبة العرض إلى الارتفاع نفسها
للقيمة 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().
يكون ذلك مفيدًا إذا أردت عرض نص نسبة التكبير/التصغير بجانب شريط التمرير.
ما عليك سوى اتّباع ZoomState LiveData لتعديل كليهما بدون الحاجة إلى إجراء عملية تحويل.
توفّر السمة ListenableFuture التي تعرضها كلتا واجهتَي برمجة التطبيقات خيارًا للتطبيقات
لتلقّي إشعار عند اكتمال طلب متكرّر بقيمة التكبير/التصغير المحدّدة. بالإضافة إلى ذلك، إذا ضبطت قيمة تكبير/تصغير جديدة بينما لا تزال العملية السابقة قيد التنفيذ، ستفشل عملية التكبير/التصغير السابقة ListenableFuture على الفور.
شعلة
CameraControl.enableTorch(boolean)
تفعيل المصباح أو إيقافه (المعروف أيضًا باسم ضوء الفلاش)
CameraInfo.getTorchState()
يمكن استخدامها للاستعلام عن حالة المصباح الحالية. يمكنك التحقّق من القيمة التي يعرضها
CameraInfo.hasFlashUnit()
لتحديد ما إذا كان المصباح متاحًا. وإذا لم يكن الأمر كذلك، يؤدي استدعاء
CameraControl.enableTorch(boolean) إلى اكتمال ListenableFuture الذي تم عرضه
على الفور بنتيجة فاشلة ويضبط حالة المصباح على
TorchState.OFF.
عند تفعيل المصباح، يظل قيد التشغيل أثناء التقاط الصور والفيديوهات
بغض النظر عن إعداد وضع الفلاش. لا يعمل وضع الفلاش الذي تم ضبطه باستخدام
ImageCapture.setFlashMode()
إلا عند إيقاف المصباح اليدوي.
التركيز والقياس
CameraControl.startFocusAndMetering() triggers
autofocus (AF), auto exposure (AE), and auto white balance (AWB) metering by
setting metering regions based on the given 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:
// Use PreviewView.getMeteringPointFactory if PreviewView is used for preview.
previewView.setOnTouchListener { view, motionEvent ->
if (motionEvent.action == MotionEvent.ACTION_UP) {
val meteringPoint = previewView.meteringPointFactory
.createPoint(motionEvent.x, motionEvent.y)
// Build and start a FocusMeteringAction with meteringPoint.
view.performClick()
}
true
}
// Use DisplayOrientedMeteringPointFactory if SurfaceView / TextureView is used
// for preview. 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 displayFactory = DisplayOrientedMeteringPointFactory(
surfaceView.display,
camera.cameraInfo,
surfaceView.width.toFloat(),
surfaceView.height.toFloat()
)
// Use SurfaceOrientedMeteringPointFactory if the point is specified in
// ImageAnalysis ImageProxy.
val surfaceFactory = SurfaceOrientedMeteringPointFactory(
imageWidth.toFloat(),
imageHeight.toFloat(),
imageAnalysis
)
startFocusAndMetering وFocusMeteringAction
لاستدعاء startFocusAndMetering()،
يجب أن تنشئ التطبيقات FocusMeteringAction،
الذي يتكوّن من عنصر واحد أو أكثر من عناصر MeteringPoint مع مجموعات اختيارية
من أوضاع القياس من FLAG_AF وFLAG_AE
وFLAG_AWB. يوضّح الرمز التالي هذا الاستخدام:
// meteringPointFactory is any MeteringPointFactory from the previous example.
val meteringPoint1 = meteringPointFactory.createPoint(x1, y1)
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 or 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 واحد لمناطق قياس
التركيز التلقائي/التعرّض التلقائي/توازن اللون الأبيض التلقائي وMeteringPoint آخر للتركيز التلقائي والتعرّض التلقائي فقط.
Because not every device supports AF/AE/AWB and multiple regions, CameraX
executes the FocusMeteringAction with best effort. تستخدم واجهة CameraX الحد الأقصى لعدد عناصر MeteringPoint المتوافقة، بالترتيب الذي تمت به إضافة النقاط. ويتم تجاهل جميع النقاط التي تتم إضافتها بعد الحد الأقصى للعدد. على سبيل المثال، إذا تم تزويد FocusMeteringAction بثلاثة عناصر MeteringPoint على نظام أساسي يتيح استخدام عنصرين فقط، سيتم استخدام أول نقطتين فقط. تتجاهل CameraX
MeteringPoint النهائي.
تعويض درجة الإضاءة
يكون تعويض درجة الإضاءة مفيدًا عندما تحتاج التطبيقات إلى ضبط قيم التعريض (EV) بدقة تتجاوز نتيجة مخرجات التعرض التلقائي للضوء (AE). يتم دمج قيم تعويض درجة الإضاءة بالطريقة التالية لتحديد التعرض اللازم لظروف الصورة الحالية:
Exposure = ExposureCompensationIndex * ExposureCompensationStep
توفّر واجهة برمجة التطبيقات CameraX الدالة
CameraControl.setExposureCompensationIndex()
لضبط تعويض درجة الإضاءة كقيمة فهرس.
تؤدي قيم المؤشر الموجبة إلى زيادة سطوع الصورة، بينما تؤدي القيم السالبة إلى تعتيمها. يمكن للتطبيقات الاستعلام عن النطاق المتوافق باستخدام
ExposureState.getExposureCompensationRange().
إذا كانت القيمة متوافقة، تكتمل قيمة ListenableFuture التي تم عرضها عند تفعيل القيمة بنجاح في طلب الالتقاط. وإذا كان الفهرس المحدّد خارج النطاق المتوافق، يؤدي setExposureCompensationIndex() إلى اكتمال قيمة ListenableFuture التي تم عرضها على الفور مع ظهور نتيجة غير ناجحة.
تحتفظ واجهة CameraX بأحدث طلب setExposureCompensationIndex() معلّق فقط. إذا استدعيت الدالة مرة أخرى قبل أن تطبّق CameraX الطلب السابق،
ستلغي CameraX الطلب السابق.
يضبط المقتطف التالي مؤشر تعويض درجة الإضاءة ويسجّل دالة رد اتصال عند تنفيذ طلب تغيير درجة الإضاءة:
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
// Handle the updated exposure compensation index.
}, mainExecutor)
يستردّ
CameraInfo.getExposureState()ExposureStateالحالي، بما في ذلك:- إمكانية دعم عنصر التحكّم في تعويض درجة الإضاءة
- مؤشر تعويض درجة الإضاءة الحالي
- نطاق مؤشر تعويض درجة الإضاءة
- خطوة تعويض درجة الإضاءة المستخدَمة في احتساب قيمة تعويض درجة الإضاءة
على سبيل المثال، يعمل الرمز التالي على إعداد إعدادات التعرض
SeekBar مع قيم ExposureState
الحالية. بما أنّ قيمة مستوى تقدّم SeekBar تبدأ من 0، يعوّض الرمز ذلك بالحدّ الأدنى لنطاق تعويض درجة الإضاءة:
val exposureState = camera.cameraInfo.exposureState
val range = exposureState.exposureCompensationRange
binding.seekBar.apply {
isEnabled = exposureState.isExposureCompensationSupported
max = range.upper - range.lower
progress = exposureState.exposureCompensationIndex - range.lower
setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
camera.cameraControl.setExposureCompensationIndex(progress + range.lower)
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
}
مراجع إضافية
لمزيد من المعلومات حول CameraX، يُرجى الاطّلاع على المراجع الإضافية التالية.
درس تطبيقي حول الترميز
عيّنة تعليمات برمجية