گزینه‌های پیکربندی

هر مورد استفاده CameraX را برای کنترل جنبه‌های مختلف عملیات مورد استفاده پیکربندی می‌کنید.

برای مثال، با مورد استفاده ضبط تصویر، می‌توانید نسبت ابعادی هدف و حالت فلاش را تنظیم کنید. کد زیر یک نمونه را نشان می‌دهد:

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

علاوه‌بر گزینه‌های پیکربندی، برخی‌از موارد استفاده میاناهای برنامه‌سازی کاربردی را برای تغییر دادن پویای تنظیمات پس‌از ایجاد مورد استفاده نشان می‌دهند. برای اطلاعات درباره پیکربندی مختص موارد استفاده فردی، به پیاده‌سازی پیش‌نما، تحلیل تصاویر، و ضبط تصویر مراجعه کنید.

CameraXConfig

برای سادگی، CameraX پیکربندی‌های پیش‌فرضی مانند مجریان داخلی دارد و کنترل‌کننده‌هایی که برای اکثر سناریوهای استفاده مناسب هستند. بااین‌حال، اگر برنامه شما الزامات خاصی دارد یا ترجیح می‌دهد آن پیکربندی‌ها را سفارشی‌سازی کند، CameraXConfig میانای مناسب برای این هدف است.

با CameraXConfig، برنامه می‌تواند کارهای زیر را انجام دهد:

مدل استفاده

روش زیر نحوه استفاده از CameraXConfig را شرح می‌دهد:

  1. یک شیء CameraXConfig با پیکربندی‌های سفارشی‌شده خود ایجاد کنید.
  2. میانای CameraXConfig.Provider را در Application پیاده‌سازی کنید و شیء CameraXConfig را در getCameraXConfig() برگردانید.
  3. کلاس Application را به فایل AndroidManifest.xml اضافه کنید، همان‌طور که اینجا شرح داده شده است.

برای مثال، نمونه کد زیر ثبت CameraX را فقط به پیام‌های خطا محدود می‌کند:

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

اگر برنامه شما باید پیکربندی CameraX را پس‌از تنظیم آن بداند، یک نسخه محلی از شیء CameraXConfig نگه دارید.

محدودکننده دوربین

درطول اولین فراخوانی ProcessCameraProvider.getInstance()، CameraX ویژگی‌های دوربین‌های موجود در دستگاه را فهرست و پرسمان می‌کند. چون CameraX باید با مؤلفه‌های سخت‌افزاری ارتباط برقرار کند، این فرایند می‌تواند برای هر دوربین زمان قابل‌توجهی طول بکشد، به‌ویژه در دستگاه‌های رده‌پایین. اگر برنامه شما فقط از دوربین‌های خاصی در دستگاه استفاده می‌کند، مثلاً دوربین جلوی پیش‌فرض، می‌توانید CameraX را طوری تنظیم کنید که دوربین‌های دیگر را نادیده بگیرد، که می‌تواند تأخیر راه‌اندازی را برای دوربین‌هایی که برنامه‌تان استفاده می‌کند کاهش دهد.

اگر CameraSelector ارسال‌شده به CameraXConfig.Builder.setAvailableCamerasLimiter() دوربینی را فیلتر کند، CameraX طوری رفتار می‌کند که گویی آن دوربین وجود ندارد. برای مثال، کد زیر برنامه را محدود می‌کند تا فقط از دوربین پشت پیش‌فرض دستگاه استفاده کند:

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() استفاده کنید.

کنترل‌کننده زمان‌بندی

از گرداننده زمان‌بندی برای زمان‌بندی کارهای داخلی در فواصل زمانی ثابت استفاده می‌شود، مثلاً تلاش مجدد برای باز کردن دوربین وقتی دردسترس نیست. این گرداننده کارها را اجرا نمی‌کند و فقط آن‌ها را به اجراکننده دوربین ارسال می‌کند. گاهی اوقات نیز در پلاتفرم‌های قدیمی API که برای فراخوانی‌های برگشتی به 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 بروندادهایی تولید می‌کند تا برنامه با آنچه انتظار دارید در پیش‌نمایش ببینید مطابقت داشته باشد. می‌توانید چرخش را به مقدار سفارشی تغییر دهید تا از دستگاه‌های چندنمایشگری پشتیبانی کنید. برای این کار، جهت نمایش فعلی را هنگام پیکربندی اشیای مورد استفاده یا به‌صورت پویا پس‌از ایجاد آن‌ها ارسال کنید.

برنامه شما می‌تواند چرخش هدف را بااستفاده از تنظیمات پیکربندی تنظیم کند. سپس می‌تواند تنظیمات چرخش را بااستفاده از روش‌های APIهای مورد استفاده (مانند ImageAnalysis.setTargetRotation()) به‌روز کند، حتی زمانی که چرخه حیات در حالت اجرا است. ممکن است زمانی از این استفاده کنید که برنامه در حالت عمودی قفل شده باشد—و بنابراین هیچ پیکربندی مجددی در چرخش رخ ندهد—اما مورد استفاده عکس یا تحلیل باید از چرخش فعلی دستگاه مطلع باشد. برای مثال، ممکن است به آگاهی از چرخش نیاز باشد تا چهره‌ها به‌درستی برای تشخیص چهره جهت‌یابی شوند، یا عکس‌ها روی افقی یا عمودی تنظیم شوند.

داده‌های تصاویر ثبت‌شده ممکن است بدون اطلاعات چرخش ذخیره شود. داده‌های Exif حاوی اطلاعات چرخش است تا برنامه‌های گالری بتوانند تصویر را پس‌از ذخیره کردن در جهت درست نشان دهند.

نمونه کد زیر نحوه تنظیم چرخش در رویداد جهت‌یابی را نشان می‌دهد:

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()
}

براساس چرخش تنظیم‌شده، هر مورد استفاده یا داده‌های تصویر را مستقیماً می‌چرخاند یا فراداده چرخش را به مصرف‌کنندگان داده‌های تصویر نچرخیده ارائه می‌دهد.

  • پیش‌نمایش: خروجی فراداده ارائه می‌شود تا چرخش وضوح هدف بااستفاده از Preview.getTargetRotation() مشخص شود. برای نمایش داده‌های پیش‌نمایش با جهت صحیح، می‌توانید از خروجی فراداده از SurfaceRequest.TransformationInfo برای ایجاد تبدیل‌ها استفاده کنید.
  • ImageAnalysis: خروجی فراداده ارائه می‌شود تا مختصات میان‌گیر تصویر نسبت به مختصات نمایشگر مشخص باشد.
  • ImageCapture: فراداده Exif تصویر، میان‌گیر، یا هم میان‌گیر و هم فراداده برای توجه به تنظیم چرخش تغییر داده می‌شوند. مقدار تغییریافته به پیاده‌سازی HAL بستگی دارد.

مستطیل برش

به‌طور پیش‌فرض، مستطیل برش همان مستطیل میان‌گیر کامل است. می‌توانید آن را با ViewPort و UseCaseGroup سفارشی‌سازی کنید. با گروه‌بندی موارد استفاده و تنظیم کردن نمای هدف، CameraX تضمین می‌کند که مستطیل‌های برش همه موارد استفاده در گروه به همان ناحیه در حسگر دوربین اشاره می‌کنند.

تکه‌کد زیر نحوه استفاده از این دو کلاس را نشان می‌دهد:

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)

ViewPort مستطیل میان‌گیر قابل‌مشاهده برای کاربران نهایی را تعریف می‌کند. سپس CameraX بزرگ‌ترین مستطیل برش ممکن را براساس ویژگی‌های نمای هدف و موارد استفاده پیوست‌شده محاسبه می‌کند. معمولاً برای دستیابی به جلوه WYSIWYG، می‌توانید ناحیه نمایش را براساس مورد استفاده پیش‌نمایش پیکربندی کنید. یک راه ساده برای دریافت ناحیه نمایش استفاده از PreviewView است.

تکه‌کدهای زیر نحوه دریافت شیء ViewPort را نشان می‌دهد:

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

در مثال قبلی، آنچه برنامه از ImageAnalysis و ImageCapture دریافت می‌کند با آنچه کاربر نهایی در PreviewView می‌بیند مطابقت دارد، با فرض اینکه نوع مقیاس PreviewView روی پیش‌فرض، FILL_CENTER، تنظیم شده باشد. پس‌از اعمال مستطیل برش و چرخش به میان‌گیر برونداد، تصویر همه موارد استفاده یکسان است، هرچند ممکن است وضوح متفاوتی داشته باشد. برای اطلاعات بیشتر درباره نحوه اعمال اطلاعات تبدیل، به خروجی تبدیل مراجعه کنید.

انتخاب دوربین

CameraX به‌طور خودکار بهترین دستگاه دوربین را برای الزامات و موارد استفاده برنامه‌تان انتخاب می‌کند. اگر می‌خواهید از دستگاهی غیراز دستگاهی که برایتان انتخاب شده است استفاده کنید، چند گزینه وجود دارد:

نمونه کد زیر نحوه ایجاد CameraSelector را برای تأثیرگذاری بر انتخاب دستگاه نشان می‌دهد:

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) ?: return
processCameraProvider.bindToLifecycle(this, selector, preview, analysis)

انتخاب چند دوربین به‌طور هم‌زمان

با شروع CameraX 1.3، می‌توانید چندین دوربین را به‌طور هم‌زمان انتخاب کنید. برای مثال، می‌توانید به دوربین جلو و پشت متصل شوید تا به‌طور هم‌زمان از هر دو زاویه عکس بگیرید یا ویدیو ضبط کنید.

هنگام استفاده از ویژگی «دوربین هم‌زمان»، دستگاه می‌تواند دو دوربین با لنزهای روبه‌روی متفاوت را به‌طور هم‌زمان به‌کار بیندازد، یا دو دوربین پشت را به‌طور هم‌زمان به‌کار بیندازد. بلوک کد زیر نحوه تنظیم دو دوربین را هنگام فراخوانی bindToLifecycle و نحوه دریافت هر دو شیء «دوربین» از شیء ConcurrentCamera برگردانده‌شده نشان می‌دهد.

// 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]

وضوح دوربین

می‌توانید انتخاب کنید که CameraX وضوح تصویر را براساس ترکیبی از قابلیت‌های دستگاه، سطح سخت‌افزار پشتیبانی‌شده دستگاه، مورد استفاده، و نسبت ابعادی ارائه‌شده تنظیم کند. یا می‌توانید در موارد استفاده‌ای که از آن پیکربندی پشتیبانی می‌کنند، وضوح هدف خاص یا نسبت ابعادی خاصی را تنظیم کنید.

وضوح خودکار

CameraX می‌تواند به‌طور خودکار بهترین تنظیمات وضوح را براساس موارد استفاده مشخص‌شده در cameraProcessProvider.bindToLifecycle() تعیین کند. هرزمان ممکن بود، همه موارد استفاده‌ای را که لازم است به‌طور هم‌زمان در یک جلسه اجرا شوند در یک تماس bindToLifecycle() مشخص کنید. CameraX وضوح را براساس مجموعه موارد استفاده محدودشده با درنظر گرفتن سطح سخت‌افزار پشتیبانی‌شده دستگاه و با درنظر گرفتن تفاوت‌های خاص دستگاه (جایی که دستگاه از پیکربندی‌های جریان موجود فراتر می‌رود یا آن‌ها را برآورده نمی‌کند) تعیین می‌کند. هدف این است که برنامه بتواند در طیف گسترده‌ای از دستگاه‌ها اجرا شود و درعین‌حال مسیرهای کد مختص دستگاه به حداقل برسد.

نسبت ابعادی پیش‌فرض برای موارد استفاده ضبط تصویر و تحلیل تصویر ۴:۳ است.

موارد استفاده دارای نسبت ابعادی قابل‌پیکربندی هستند تا برنامه بتواند نسبت ابعادی موردنظر را براساس طراحی واسط کاربر مشخص کند. خروجی CameraX به‌گونه‌ای تولید می‌شود که تا حد امکان با نسبت‌های ابعادی درخواست‌شده مطابقت داشته باشد، به‌شرطی که دستگاه از آن پشتیبانی کند. اگر وضوح منطبق دقیقی پشتیبانی نشود، وضوحی که بیشترین شرایط را برآورده کند انتخاب می‌شود. بنابراین، برنامه تعیین می‌کند که دوربین چگونه در برنامه ظاهر شود و CameraX بهترین تنظیمات وضوح دوربین را برای برآورده کردن این نیاز در دستگاه‌های مختلف تعیین می‌کند.

برای مثال، برنامه می‌تواند هرکدام از کارهای زیر را انجام دهد:

  • وضوح هدف ۴:۳ یا ۱۶:۹ را برای مورد استفاده مشخص کنید
  • وضوح سفارشی را مشخص کنید، که CameraX تلاش می‌کند نزدیک‌ترین مطابقت را برای آن پیدا کند
  • نسبت ابعادی برش را برای ImageCapture مشخص کنید

CameraX وضوح سطح Camera2 داخلی را به‌طور خودکار انتخاب می‌کند. جدول زیر وضوح‌ها را نشان می‌دهد:

مورد استفاده وضوح سطح داخلی وضوح داده‌های خروجی
پیش‌نمایش نسبت ابعادی: وضوحی که بهترین تناسب را با هدف در تنظیمات دارد. وضوح سطح داخلی. فراداده ارائه می‌شود تا View بتواند برای نسبت ابعادی هدف، برش، مقیاس‌بندی، و چرخش انجام دهد.
وضوح پیش‌فرض: بالاترین وضوح پیش‌نمایش، یا بالاترین وضوح ترجیحی دستگاه که با نسبت ابعادی «پیش‌نمایش» مطابقت دارد.
حداکثر وضوح: اندازه پیش‌نمایش، که به بهترین اندازه مطابق با وضوح صفحه‌نمایش دستگاه، یا به ۱۰۸۰ پیکسل (۱۹۲۰×۱۰۸۰)، هرکدام که کوچک‌تر باشد، اشاره دارد.
تحلیل تصویر نسبت ابعادی: وضوحی که هدف را به بهترین شکل با تنظیمات مطابقت می‌دهد. وضوح سطح داخلی.
وضوح پیش‌فرض: تنظیم وضوح هدف پیش‌فرض ۶۴۰×۴۸۰ است. تنظیم کردن وضوح هدف و نسبت ابعادی مربوطه منجر به بهترین وضوح پشتیبانی‌شده می‌شود.
حداکثر وضوح: حداکثر وضوح خروجی دستگاه دوربین در قالب YUV_420_888 که از StreamConfigurationMap.getOutputSizes() بازیابی شده است. وضوح هدف به‌طور پیش‌فرض روی ۶۴۰x۴۸۰ تنظیم شده است، بنابراین اگر وضوحی بزرگ‌تر از ۶۴۰x۴۸۰ می‌خواهید، باید از setTargetResolution() و setTargetAspectRatio() استفاده کنید تا نزدیک‌ترین وضوح را از بین وضوح‌های پشتیبانی‌شده دریافت کنید.
ضبط کردن تصویر نسبت ابعادی: نسبت ابعادی که بهترین تناسب را با تنظیمات دارد. وضوح سطح داخلی.
وضوح پیش‌فرض: بالاترین وضوح موجود، یا بالاترین وضوح ترجیحی دستگاه که با نسبت ابعادی ImageCapture مطابقت دارد.
حداکثر وضوح: حداکثر وضوح خروجی دستگاه دوربین در قالب JPEG. برای بازیابی این مورد، از StreamConfigurationMap.getOutputSizes() استفاده کنید.

وضوح را مشخص کنید

هنگام ساختن موارد استفاده بااستفاده از روش setTargetResolution(Size resolution)، می‌توانید وضوح‌های خاصی را تنظیم کنید، همان‌طور که در نمونه کد زیر نشان داده شده است:

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

نمی‌توانید هم نسبت ابعادی هدف و هم وضوح هدف را در یک مورد استفاده تنظیم کنید. انجام این کار باعث ایجاد IllegalArgumentException هنگام ساختن شیء پیکربندی می‌شود.

وضوح Size را در قاب مختصات پس‌از چرخاندن اندازه‌های پشتیبانی‌شده با چرخش هدف بیان کنید. برای مثال، دستگاهی با جهت طبیعی عمودی در چرخش هدف طبیعی که تصویر عمودی درخواست می‌کند می‌تواند ۴۸۰×۶۴۰ را مشخص کند، و همان دستگاه، با چرخش ۹۰ درجه و هدف‌گذاری جهت افقی می‌تواند ۶۴۰×۴۸۰ را مشخص کند.

وضوح هدف تلاش می‌کند حداقل حد را برای وضوح تصویر تعیین کند. وضوح تصویر واقعی نزدیک‌ترین وضوح موجود ازنظر اندازه است که از وضوح هدف کوچک‌تر نیست، همان‌طور که توسط پیاده‌سازی «دوربین» تعیین شده است.

بااین‌حال، اگر وضوحی برابر یا بزرگ‌تر از وضوح هدف وجود نداشته باشد، نزدیک‌ترین وضوح دردسترس که کوچک‌تر از وضوح هدف است انتخاب می‌شود. وضوح‌هایی که نسبت ابعادی یکسانی با 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() برگردانده شده است بازیابی کنید. کد زیر نمونه‌ای را نشان می‌دهد:

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

برای مثال، می‌توانید پس‌از فراخوانی bindToLifecycle()، بزرگ‌نمایی و سایر عملیات CameraControl را ارسال کنید. پس‌از توقف یا ازبین بردن فعالیت استفاده‌شده برای اتصال نمونه دوربین، CameraControl دیگر نمی‌تواند عملیات را اجرا کند و ListenableFuture ناموفق برمی‌گرداند.

بزرگ‌نمایی

‫CameraControl دو روش برای تغییر سطح بزرگ‌نمایی ارائه می‌دهد:

  • setZoomRatio() بزرگ‌نمایی را براساس نسبت بزرگ‌نمایی تنظیم می‌کند.

    نسبت باید در محدوده CameraInfo.getZoomState().getValue().getMinZoomRatio() و CameraInfo.getZoomState().getValue().getMaxZoomRatio() باشد. درغیراین‌صورت، تابع ListenableFuture ناموفق برمی‌گرداند.

  • setLinearZoom() بزرگ‌نمایی فعلی را با مقدار بزرگ‌نمایی خطی از ۰ تا ۱٫۰ تنظیم می‌کند.

    مزیت بزرگ‌نمایی خطی این است که میدان دید (FOV) را با تغییرات بزرگ‌نمایی مقیاس‌بندی می‌کند. این ویژگی آن را برای استفاده با نمای Slider ایده‌آل می‌کند.

CameraInfo.getZoomState() یک LiveData از وضعیت بزرگ‌نمایی فعلی برمی‌گرداند. وقتی دوربین مقداردهی اولیه می‌شود یا اگر سطح بزرگ‌نمایی بااستفاده از setZoomRatio() یا setLinearZoom() تنظیم شود، مقدار تغییر می‌کند. فراخوانی هریک از این روش‌ها مقادیر پشتیبان ZoomState.getZoomRatio() و ZoomState.getLinearZoom() را تنظیم می‌کند. این ویژگی زمانی مفید است که بخواهید نوشتار نسبت بزرگ‌نمایی را در کنار لغزنده نمایش دهید. به‌سادگی ZoomState LiveData را مشاهده کنید تا هر دو را بدون نیاز به انجام تبدیل به‌روز کنید.

ListenableFuture که توسط هر دو API برگردانده می‌شود، گزینه‌ای را برای برنامه‌ها ارائه می‌دهد تا وقتی درخواست تکراری با مقدار بزرگ‌نمایی مشخص‌شده تکمیل می‌شود، مطلع شوند. علاوه‌براین، اگر مقدار بزرگ‌نمایی جدیدی را درحالی تنظیم کنید که عملیات قبلی هنوز درحال اجرا است، ListenableFuture عملیات بزرگ‌نمایی قبلی فوراً ناموفق می‌شود.

چراغ

CameraControl.enableTorch(boolean) چراغ‌قوه را فعال یا غیرفعال می‌کند.

CameraInfo.getTorchState() می‌تواند برای استعلام وضعیت فعلی چراغ‌قوه استفاده شود. می‌توانید مقدار برگردانده‌شده توسط CameraInfo.hasFlashUnit() را بررسی کنید تا ببینید آیا چراغ‌قوه دردسترس است یا نه. درغیراین‌صورت، فراخوانی CameraControl.enableTorch(boolean) باعث می‌شود ListenableFuture برگردانده‌شده فوراً با نتیجه ناموفق تکمیل شود و وضعیت چراغ‌قوه را روی TorchState.OFF تنظیم می‌کند.

وقتی چراغ‌قوه فعال باشد، درطول عکس‌برداری و فیلم‌برداری، صرف‌نظر از تنظیم حالت فلاش، روشن می‌ماند. حالت فلاش تنظیم‌شده با ImageCapture.setFlashMode() فقط زمانی کار می‌کند که چراغ‌قوه غیرفعال باشد.

فوکوس و نورسنجی

CameraControl.startFocusAndMetering() با تنظیم کردن مناطق نورسنجی براساس FocusMeteringAction ارائه‌شده، نورسنجی فوکوس خودکار (AF)، نوردهی خودکار (AE)، و تراز سفیدی خودکار (AWB) را راه‌اندازی می‌کند. می‌توانید از این روش برای پیاده‌سازی ویژگی «ضربه زدن برای فوکوس» در برنامه‌های دوربین استفاده کنید.

MeteringPoint

برای شروع، MeteringPoint را بااستفاده از MeteringPointFactory.createPoint(float x, float y, float size) ایجاد کنید. MeteringPoint نشان‌دهنده یک نقطه در دوربین Surface است. این اطلاعات در قالبی نرمال‌شده ذخیره می‌شود تا بتوان آن را به مختصات حسگر برای مشخص کردن مناطق AF/AE/AWB تبدیل کرد.

اندازه MeteringPoint از ۰ تا ۱ است و اندازه پیش‌فرض آن 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 برای مناطق اندازه‌گیری AF/AE/AWB و یک MeteringPoint دیگر فقط برای AF و AE می‌گیرد.

چون همه دستگاه‌ها از AF/AE/AWB و چند منطقه پشتیبانی نمی‌کنند، CameraX FocusMeteringAction را با بهترین تلاش اجرا می‌کند. 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 از ۰ شروع می‌شود، کد آن را با حد پایین محدوده جبران نوردهی جابه‌جا می‌کند:

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، به منابع تکمیلی زیر مراجعه کنید.

Codelab

  • شروع به کار کردن با CameraX
  • نمونه کد

  • برنامه‌های نمونه CameraX
  • انجمن توسعه‌دهندگان

    گروه بحث Android CameraX