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

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

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

کاتلین

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

جاوا

ImageCapture imageCapture =
    new ImageCapture.Builder()
        .setFlashMode(...)
        .setTargetAspectRatio(...)
        .build();

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

پیکربندی دوربین

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

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

  • با استفاده از setAvailableCameraLimiter() تأخیر راه‌اندازی را بهینه‌سازی کنید.
  • با استفاده از setCameraExecutor() مجری برنامه را در اختیار CameraX قرار دهید.
  • کنترل‌کننده‌ی زمان‌بندی پیش‌فرض را با setSchedulerHandler() جایگزین کنید.
  • سطح ثبت وقایع را با استفاده از setMinimumLoggingLevel() تغییر دهید.

مدل استفاده

روش زیر نحوه استفاده از 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()
   }
}

موضوعات

بسیاری از APIهای پلتفرمی که CameraX بر اساس آنها ساخته شده است، نیاز به مسدود کردن ارتباط بین پردازشی (IPC) با سخت‌افزار دارند که گاهی اوقات می‌تواند صدها میلی‌ثانیه طول بکشد تا پاسخ دهد. به همین دلیل، CameraX فقط این APIها را از نخ‌های پس‌زمینه فراخوانی می‌کند، به طوری که نخ اصلی مسدود نمی‌شود و رابط کاربری روان باقی می‌ماند. CameraX به صورت داخلی این نخ‌های پس‌زمینه را مدیریت می‌کند تا این رفتار شفاف به نظر برسد. با این حال، برخی از برنامه‌ها نیاز به کنترل دقیق نخ‌ها دارند. CameraXConfig به یک برنامه اجازه می‌دهد تا نخ‌های پس‌زمینه‌ای را که از طریق CameraXConfig.Builder.setCameraExecutor() و CameraXConfig.Builder.setSchedulerHandler() استفاده می‌شوند، تنظیم کند.

مجری دوربین

مجری دوربین برای تمام فراخوانی‌های API داخلی پلتفرم دوربین و همچنین برای فراخوانی‌های برگشتی از این APIها استفاده می‌شود. CameraX یک Executor داخلی را برای انجام این وظایف اختصاص داده و مدیریت می‌کند. با این حال، اگر برنامه شما نیاز به کنترل دقیق‌تر نخ‌ها دارد، از CameraXConfig.Builder.setCameraExecutor() استفاده کنید.

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

از کنترل‌کننده‌ی زمان‌بندی برای زمان‌بندی وظایف داخلی در فواصل زمانی ثابت، مانند تلاش مجدد برای باز کردن دوربین در صورت عدم دسترسی، استفاده می‌شود. این کنترل‌کننده وظایف را اجرا نمی‌کند و فقط آنها را به اجراکننده‌ی دوربین ارسال می‌کند. همچنین گاهی اوقات در پلتفرم‌های API قدیمی که برای فراخوانی‌های برگشتی به یک Handler نیاز دارند، استفاده می‌شود. در این موارد، فراخوانی‌های برگشتی هنوز فقط مستقیماً به اجراکننده‌ی دوربین ارسال می‌شوند. CameraX یک HandlerThread داخلی را برای انجام این وظایف اختصاص داده و مدیریت می‌کند، اما می‌توانید آن را با CameraXConfig.Builder.setSchedulerHandler() لغو کنید.

ثبت وقایع

ثبت وقایع CameraX به برنامه‌ها اجازه می‌دهد پیام‌های logcat را فیلتر کنند، زیرا اجتناب از پیام‌های طولانی در کد تولید شما می‌تواند تمرین خوبی باشد. CameraX از چهار سطح ثبت وقایع، از طولانی‌ترین تا شدیدترین، پشتیبانی می‌کند:

  • Log.DEBUG (پیش‌فرض)
  • Log.INFO
  • Log.WARN
  • Log.ERROR

برای توضیحات دقیق در مورد این سطوح ثبت وقایع، به مستندات ثبت وقایع اندروید مراجعه کنید. از CameraXConfig.Builder.setMinimumLoggingLevel(int) برای تنظیم سطح ثبت وقایع مناسب برای برنامه خود استفاده کنید.

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

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

هدف CameraX راه‌اندازی موفقیت‌آمیز یک جلسه دوربین است. این بدان معناست که CameraX بر اساس قابلیت دستگاه، در مورد وضوح و نسبت ابعاد مصالحه می‌کند. این مصالحه می‌تواند به دلایل زیر اتفاق بیفتد:

  • دستگاه از وضوح درخواستی پشتیبانی نمی‌کند.
  • دستگاه دارای مشکلات سازگاری است، مانند دستگاه‌های قدیمی که برای عملکرد صحیح به وضوح تصویر خاصی نیاز دارند.
  • در برخی دستگاه‌ها، فرمت‌های خاصی فقط با نسبت‌های ابعاد خاصی در دسترس هستند.
  • این دستگاه برای کدگذاری JPEG یا ویدیو، "nearest mod16" را ترجیح می‌دهد. برای اطلاعات بیشتر، به SCALER_STREAM_CONFIGURATION_MAP مراجعه کنید.

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

چرخش

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

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

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

برای نمایش داده‌های پیش‌نمایش با جهت صحیح، می‌توانید از خروجی فراداده از Preview.PreviewOutput() برای ایجاد تبدیل‌ها استفاده کنید.

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

کاتلین

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

جاوا

@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 سفارشی کنید. با گروه‌بندی موارد استفاده و تنظیم viewport، 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 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 را نشان می‌دهد:

کاتلین

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

جاوا

ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();

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

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

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

جاوا

// 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 وضوح‌ها را بر اساس مجموعه موارد استفاده محدود شده با در نظر گرفتن سطح سخت‌افزار پشتیبانی شده دستگاه و با در نظر گرفتن واریانس خاص دستگاه (جایی که یک دستگاه از پیکربندی‌های جریان موجود فراتر می‌رود یا مطابقت ندارد) تعیین می‌کند. هدف این است که برنامه بتواند روی طیف گسترده‌ای از دستگاه‌ها اجرا شود و در عین حال مسیرهای کد خاص دستگاه را به حداقل برساند.

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

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

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

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

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

مورد استفاده وضوح سطح داخلی وضوح داده خروجی
پیش‌نمایش نسبت ابعاد: وضوحی که به بهترین شکل هدف را با تنظیمات تطبیق می‌دهد. وضوح سطح داخلی. فراداده (metadata) برای برش، مقیاس‌بندی و چرخش تصویر (rotate) برای نسبت ابعاد مورد نظر ارائه شده است.
وضوح پیش‌فرض: بالاترین وضوح پیش‌نمایش، یا بالاترین وضوح ترجیحی دستگاه که با نسبت ابعاد پیش‌نمایش مطابقت دارد.
حداکثر وضوح: اندازه پیش‌نمایش، که به بهترین اندازه مطابق با وضوح صفحه نمایش دستگاه یا 1080p (1920x1080)، هر کدام که کوچکتر باشد، اشاره دارد.
تحلیل تصویر نسبت ابعاد: وضوحی که به بهترین شکل هدف را با تنظیمات تطبیق می‌دهد. وضوح سطح داخلی.
وضوح پیش‌فرض: تنظیم وضوح تصویر هدف پیش‌فرض ۶۴۰x۴۸۰ است. تنظیم همزمان وضوح تصویر هدف و نسبت ابعاد مربوطه، منجر به بهترین وضوح پشتیبانی‌شده می‌شود.
حداکثر وضوح: حداکثر وضوح خروجی دستگاه دوربین با فرمت YUV_420_888 که از StreamConfigurationMap.getOutputSizes() بازیابی می‌شود. وضوح هدف به طور پیش‌فرض روی 640x480 تنظیم شده است، بنابراین اگر وضوحی بزرگتر از 640x480 می‌خواهید، باید از setTargetResolution() و setTargetAspectRatio() برای دریافت نزدیکترین وضوح از بین وضوح‌های پشتیبانی شده استفاده کنید.
ضبط تصویر نسبت ابعاد: نسبت ابعادی که به بهترین شکل با تنظیمات مطابقت دارد. وضوح سطح داخلی.
وضوح پیش‌فرض: بالاترین وضوح موجود، یا بالاترین وضوح ترجیحی دستگاه که با نسبت ابعاد ImageCapture مطابقت دارد.
حداکثر وضوح: حداکثر وضوح خروجی دستگاه دوربین در قالب JPEG. برای بازیابی این مقدار از StreamConfigurationMap.getOutputSizes() استفاده کنید.

مشخص کردن رزولوشن

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

کاتلین

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

جاوا

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

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

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

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

با این حال، اگر هیچ وضوحی وجود نداشته باشد که برابر یا بزرگتر از وضوح هدف باشد، نزدیکترین وضوح موجود کوچکتر از وضوح هدف انتخاب می‌شود. وضوح‌هایی با نسبت ابعاد یکسان با Size ارائه شده، اولویت بالاتری نسبت به وضوح‌هایی با نسبت ابعاد متفاوت دارند.

CameraX بر اساس درخواست‌ها، بهترین رزولوشن مناسب را اعمال می‌کند. اگر نیاز اصلی برآورده کردن نسبت ابعاد است، فقط setTargetAspectRatio را مشخص کنید و CameraX یک رزولوشن خاص مناسب بر اساس دستگاه تعیین می‌کند. اگر نیاز اصلی برنامه تعیین رزولوشنی برای کارآمدتر کردن پردازش تصویر است (برای مثال، یک تصویر کوچک یا متوسط ​​بر اساس قابلیت پردازش دستگاه)، از setTargetResolution(Size resolution) استفاده کنید.

اگر برنامه شما به یک رزولوشن دقیق نیاز دارد، برای تعیین حداکثر رزولوشن‌های پشتیبانی شده توسط هر سطح سخت‌افزاری، به جدول درون createCaptureSession() مراجعه کنید. برای بررسی رزولوشن‌های خاص پشتیبانی شده توسط دستگاه فعلی، به StreamConfigurationMap.getOutputSizes(int) مراجعه کنید.

اگر برنامه شما روی اندروید ۱۰ یا بالاتر اجرا می‌شود، می‌توانید از 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

جاوا

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() ارسال کنید. پس از متوقف کردن یا از بین بردن activity مورد استفاده برای اتصال نمونه دوربین، 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 تنظیم کند.

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

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

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

نقطه اندازه‌گیری

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

اندازه MeteringPoint از ۰ تا ۱ متغیر است و اندازه پیش‌فرض آن ۰.۱۵f است. هنگام فراخوانی 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) ->  {
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 است. کد زیر این کاربرد را نشان می‌دهد:

کاتلین

val meteringPoint1 = meteringPointFactory.createPoint(x1, x1)
val meteringPoint2 = meteringPointFactory.createPoint(x2, y2)
val action = FocusMeteringAction.Builder(meteringPoint1) // default AF|AE|AWB
      // Optionally add meteringPoint2 for AF/AE.
      .addPoint(meteringPoint2, FLAG_AF | FLAG_AE)
      // The action is canceled in 3 seconds (if not set, default is 5s).
      .setAutoCancelDuration(3, TimeUnit.SECONDS)
      .build()

val result = cameraControl.startFocusAndMetering(action)
// Adds listener to the ListenableFuture if you need to know the focusMetering result.
result.addListener({
   // result.get().isFocusSuccessful returns if the auto focus is successful or not.
}, ContextCompat.getMainExecutor(this)

همانطور که در کد قبلی نشان داده شده است، startFocusAndMetering() یک FocusMeteringAction شامل یک MeteringPoint برای نواحی نورسنجی AF/AE/AWB و یک MeteringPoint دیگر فقط برای AF و AE می‌گیرد.

به صورت داخلی، CameraX آن را به Camera2 MeteringRectangles تبدیل می‌کند و پارامترهای CONTROL_AF_REGIONS / CONTROL_AE_REGIONS / CONTROL_AWB_REGIONS مربوطه را برای درخواست ضبط تنظیم می‌کند.

از آنجایی که هر دستگاهی از AF/AE/AWB و چندین ناحیه پشتیبانی نمی‌کند، CameraX با تمام توان FocusMeteringAction اجرا می‌کند. CameraX از حداکثر تعداد نقاط اندازه‌گیری پشتیبانی‌شده، به ترتیبی که نقاط اضافه شده‌اند، استفاده می‌کند. تمام نقاط اندازه‌گیری اضافه‌شده پس از حداکثر تعداد نادیده گرفته می‌شوند. به عنوان مثال، اگر یک FocusMeteringAction با ۳ نقطه اندازه‌گیری در پلتفرمی که فقط ۲ نقطه اندازه‌گیری را پشتیبانی می‌کند، ارائه شود، فقط از ۲ نقطه اندازه‌گیری اول استفاده می‌شود. MeteringPoint نهایی توسط CameraX نادیده گرفته می‌شود.

جبران نوردهی

جبران نوردهی زمانی مفید است که برنامه‌ها نیاز به تنظیم دقیق مقادیر نوردهی (EV) فراتر از نتیجه خروجی نوردهی خودکار (AE) داشته باشند. مقادیر جبران نوردهی به روش زیر ترکیب می‌شوند تا نوردهی لازم برای شرایط تصویر فعلی تعیین شود:

Exposure = ExposureCompensationIndex * ExposureCompensationStep

CameraX تابع Camera.CameraControl.setExposureCompensationIndex() را برای تنظیم جبران نوردهی به عنوان یک مقدار شاخص ارائه می‌دهد.

مقادیر شاخص مثبت، تصویر را روشن‌تر می‌کنند، در حالی که مقادیر منفی، تصویر را کم‌نورتر می‌کنند. برنامه‌ها می‌توانند محدوده پشتیبانی‌شده را توسط CameraInfo.ExposureState.exposureCompensationRange() که در بخش بعدی توضیح داده شده است، جستجو کنند. اگر مقدار پشتیبانی شود، ListenableFuture برگردانده شده زمانی کامل می‌شود که مقدار با موفقیت در درخواست ضبط فعال شود؛ اگر شاخص مشخص شده خارج از محدوده پشتیبانی‌شده باشد، setExposureCompensationIndex() باعث می‌شود ListenableFuture برگردانده شده بلافاصله با یک نتیجه ناموفق کامل شود.

CameraX فقط آخرین درخواست setExposureCompensationIndex() را که هنوز اجرا نشده نگه می‌دارد و فراخوانی چندین باره تابع قبل از اجرای درخواست قبلی منجر به لغو آن می‌شود.

قطعه کد زیر یک شاخص جبران نوردهی تنظیم می‌کند و یک فراخوانی برای زمانی که درخواست تغییر نوردهی اجرا شده است، ثبت می‌کند:

کاتلین

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 مقداردهی اولیه می‌کند:

کاتلین

val exposureState = camera.cameraInfo.exposureState
binding.seekBar.apply {
   isEnabled = exposureState.isExposureCompensationSupported
   max = exposureState.exposureCompensationRange.upper
   min = exposureState.exposureCompensationRange.lower
   progress = exposureState.exposureCompensationIndex
}

منابع اضافی

برای کسب اطلاعات بیشتر در مورد CameraX، به منابع اضافی زیر مراجعه کنید.

کدلب

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

  • نمونه‌هایی از اپلیکیشن‌های CameraX
  • جامعه توسعه‌دهندگان

    Android CameraX Discussion Group