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

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

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

کاتلین

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

جاوا

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

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

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

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

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

کاتلین

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

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

جاوا

ImageAnalysis imageAnalysis =
  new ImageAnalysis.Builder()
    .setTargetResolution(new 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

جاوا

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

برای مثال، می‌توانید پس‌از فراخوانی 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 از ۰ تا ۱ است و اندازه پیش‌فرض آن ۰٫۱۵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 ->
    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