কনফিগারেশন সংক্রান্ত বিকল্প

ব্যবহারের ক্ষেত্রে অপারেশনের বিভিন্ন দিক নিয়ন্ত্রণ করতে আপনি প্রতিটি CameraX ব্যবহারের ক্ষেত্র কনফিগার করেন।

যেমন, ইমেজ ক্যাপচার ব্যবহারের ক্ষেত্রে, আপনি টার্গেট অ্যাস্পেক্ট রেশিও এবং ফ্ল্যাশ মোড সেট করতে পারেন। নিচের কোডটি একটি উদাহরণ দেখায়:

Kotlin

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

জাভা

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

কনফিগারেশন বিকল্প ছাড়াও, কিছু ব্যবহারের ক্ষেত্রে API প্রকাশ করে যাতে ব্যবহারের ক্ষেত্র তৈরি হওয়ার পরে ডায়নামিকভাবে সেটিংস পরিবর্তন করা যায়। স্বতন্ত্র ব্যবহারের ক্ষেত্রে নির্দিষ্ট কনফিগারেশন সম্পর্কে তথ্যের জন্য, প্রিভিউ প্রয়োগ করুন, ছবি বিশ্লেষণ করুন এবং ছবি ক্যাপচার করুন দেখুন।

CameraXConfig

সহজ করার জন্য, CameraX-এর ডিফল্ট কনফিগারেশন আছে যেমন ইন্টার্নাল এক্সিকিউটর এবং হ্যান্ডলার যা বেশিরভাগ ব্যবহারের ক্ষেত্রে উপযুক্ত। তবে, আপনার অ্যাপ্লিকেশনের যদি বিশেষ প্রয়োজনীয়তা থাকে অথবা সেইসব কনফিগারেশন কাস্টমাইজ করতে চান, তাহলে CameraXConfig ইন্টারফেস ব্যবহার করতে পারেন।

CameraXConfig-এর মাধ্যমে, কোনও অ্যাপ্লিকেশন নিম্নলিখিত কাজ করতে পারে:

  • setAvailableCameraLimiter()-এর মাধ্যমে স্টার্টআপ লেটেন্সি অপ্টিমাইজ করুন।
  • setCameraExecutor()-এর সাথে CameraX-এ অ্যাপ্লিকেশনের এক্সিকিউটর প্রদান করুন।
  • ডিফল্ট শিডিউলার হ্যান্ডলারের পরিবর্তে setSchedulerHandler() ব্যবহার করুন।
  • setMinimumLoggingLevel()-এর মাধ্যমে লগিং লেভেল পরিবর্তন করুন।

ব্যবহারের মডেল

নিম্নলিখিত পদ্ধতিতে CameraXConfig কীভাবে ব্যবহার করতে হয় তা বর্ণনা করা হয়েছে:

  1. আপনার কাস্টমাইজ করা কনফিগারেশন সহ একটি CameraXConfig অবজেক্ট তৈরি করুন।
  2. আপনার Application-এ CameraXConfig.Provider ইন্টারফেস প্রয়োগ করুন এবং getCameraXConfig()-এ আপনার CameraXConfig অবজেক্ট ফিরিয়ে দিন।
  3. AndroidManifest.xml ফাইলে আপনার Application ক্লাস যোগ করুন, এখানে যেমন বলা হয়েছে।

যেমন, নিম্নলিখিত কোড স্যাম্পেল CameraX লগিংকে শুধুমাত্র ত্রুটি মেসেজে সীমাবদ্ধ করে:

Kotlin

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 সেট করতে পারেন, যা আপনার অ্যাপ্লিকেশন ব্যবহার করে এমন ক্যামেরার জন্য স্টার্টআপ লেটেন্সি কমাতে পারে।

CameraXConfig.Builder.setAvailableCamerasLimiter()-এ পাস করা CameraSelector কোনও ক্যামেরা ফিল্টার করলে, CameraX এমনভাবে কাজ করে যেন সেই ক্যামেরাটি নেই। যেমন, নিম্নলিখিত কোডটি অ্যাপ্লিকেশনটিকে শুধুমাত্র ডিভাইসের ডিফল্ট পিছনের ক্যামেরা ব্যবহার করার জন্য সীমাবদ্ধ করে:

Kotlin

class MainApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
              .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA)
              .build()
   }
}

থ্রেড

CameraX যে প্ল্যাটফর্ম API-এর উপর তৈরি করা হয়েছে তার মধ্যে অনেক API-এর ক্ষেত্রে হার্ডওয়্যারের সাথে ইন্টারপ্রসেস কমিউনিকেশন (IPC) ব্লক করার প্রয়োজন হয়, যার উত্তর দিতে কখনও কখনও কয়েকশো মিলিসেকেন্ড সময় লাগতে পারে। এই কারণে, CameraX শুধুমাত্র ব্যাকগ্রাউন্ড থ্রেড থেকে এইসব API কল করে, যাতে প্রধান থ্রেড ব্লক না হয় এবং UI মসৃণ থাকে। 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

এইসব লগ লেভেলের বিস্তারিত বিবরণের জন্য Android লগ ডকুমেন্টেশন দেখুন। আপনার অ্যাপ্লিকেশনের জন্য উপযুক্ত লগিং লেভেল সেট করতে CameraXConfig.Builder.setMinimumLoggingLevel(int) ব্যবহার করুন।

অটোমেটিক বেছে নেওয়া

CameraX অটোমেটিক এমন কার্যকারিতা প্রদান করে যা আপনার অ্যাপ যে ডিভাইসে চলছে তার জন্য নির্দিষ্ট। যেমন, আপনি কোনও রেজোলিউশন নির্দিষ্ট না করলে বা আপনার নির্দিষ্ট করা রেজোলিউশন কাজ না করলে, CameraX অটোমেটিক সবচেয়ে ভালো রেজোলিউশন নির্ধারণ করে। এইসব কিছু লাইব্রেরি ম্যানেজ করে, তাই আপনাকে ডিভাইস-নির্দিষ্ট কোড লিখতে হয় না।

CameraX-এর লক্ষ্য হল ক্যামেরা সেশন সফলভাবে শুরু করা। এর অর্থ হল CameraX ডিভাইসের ক্ষমতার উপর ভিত্তি করে রেজোলিউশন এবং অ্যাস্পেক্ট রেশিওর সাথে আপস করে। এইসব কারণে পাসওয়ার্ড চুরি হতে পারে:

  • অনুরোধ করা রেজোলিউশন এই ডিভাইসে কাজ করে না।
  • ডিভাইসে কম্প্যাটিবিলিটি সংক্রান্ত সমস্যা আছে, যেমন পুরনো ডিভাইসে সঠিকভাবে কাজ করার জন্য নির্দিষ্ট রেজোলিউশন প্রয়োজন।
  • কিছু ডিভাইসে, নির্দিষ্ট কিছু ফর্ম্যাট শুধুমাত্র নির্দিষ্ট কিছু অ্যাস্পেক্ট রেশিওতে উপলভ্য।
  • JPEG বা ভিডিও এনকোডিংয়ের জন্য ডিভাইসে "নিকটতম mod16" পছন্দ করা হয়। আরও তথ্যের জন্য, SCALER_STREAM_CONFIGURATION_MAP দেখুন।

CameraX সেশন তৈরি ও ম্যানেজ করলেও, আপনার কোডে ইউজ কেস আউটপুটে ফেরত আসা ছবির সাইজ সবসময় চেক করুন এবং সেই অনুযায়ী অ্যাডজাস্ট করুন।

রোটেশন

ডিফল্ট হিসেবে, ব্যবহারের কেস তৈরি করার সময় ডিফল্ট ডিসপ্লের রোটেশনের সাথে ম্যাচ করার জন্য ক্যামেরার রোটেশন সেট করা হয়। এই ডিফল্ট ক্ষেত্রে, CameraX আউটপুট তৈরি করে যাতে অ্যাপটি প্রিভিউতে আপনি যা দেখতে চান তার সাথে মেলে। আপনি মাল্টি-ডিসপ্লে ডিভাইস সাপোর্ট করার জন্য কাস্টম ভ্যালুতে রোটেশন পরিবর্তন করতে পারেন। এটি করার জন্য, ব্যবহারের কেস অবজেক্ট কনফিগার করার সময় বা সেগুলি তৈরি হওয়ার পরে ডাইনামিকভাবে বর্তমান ডিসপ্লে ওরিয়েন্টেশন পাস করুন।

আপনার অ্যাপ কনফিগারেশন সেটিংস ব্যবহার করে টার্গেট রোটেশন সেট করতে পারে। তারপর এটি ব্যবহারের ক্ষেত্রে API (যেমন 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()
}

জাভা

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

জাভা

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

জাভা

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

আগের উদাহরণে, ImageAnalysis এবং ImageCapture থেকে অ্যাপ যা পায় তা PreviewView-এ ব্যবহারকারী যা দেখেন তার সাথে মিলে যায়, ধরে নেওয়া হয় যে PreviewView-এর স্কেল টাইপ ডিফল্ট, FILL_CENTER-এ সেট করা আছে। আউটপুট বাফারে ক্রপ রেক্ট ও রোটেশন প্রয়োগ করার পরে, সব ব্যবহারের ক্ষেত্রে ছবি একই থাকে, যদিও রেজোলিউশন আলাদা হতে পারে। ট্রান্সফর্মেশন সংক্রান্ত তথ্য কীভাবে প্রয়োগ করবেন সেই সম্পর্কে আরও জানতে, ট্রান্সফর্ম আউটপুট দেখুন।

ক্যামেরা বেছে নেওয়া

CameraX আপনার অ্যাপ্লিকেশনের প্রয়োজনীয়তা ও ব্যবহারের ক্ষেত্রে সবচেয়ে উপযুক্ত ক্যামেরা ডিভাইস অটোমেটিক বেছে নেয়। আপনার জন্য বেছে নেওয়া ডিভাইসের পরিবর্তে অন্য কোনও ডিভাইস ব্যবহার করতে চাইলে, আপনার কাছে কয়েকটি বিকল্প আছে:

নিম্নলিখিত কোড স্যাম্পেল থেকে বোঝা যায় যে ডিভাইস বেছে নেওয়ার ক্ষেত্রে প্রভাব ফেলতে কীভাবে 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)

একসাথে একাধিক ক্যামেরা বেছে নিন

CameraX 1.3 থেকে শুরু করে, আপনি একসাথে একাধিক ক্যামেরা বেছে নিতে পারবেন। যেমন, আপনি একই সাথে দুটি দৃষ্টিকোণ থেকে ফটো তুলতে বা ভিডিও রেকর্ড করতে সামনের এবং পিছনের ক্যামেরার সাথে বাইন্ড করতে পারেন।

কনকারেন্ট ক্যামেরা ফিচার ব্যবহার করার সময়, ডিভাইসটি একই সময়ে দুটি ক্যামেরা আলাদা-ফেসিং লেন্সের সাথে অপারেট করতে পারে অথবা একই সময়ে দুটি ব্যাক ক্যামেরা অপারেট করতে পারে। 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]

জাভা

// 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-কে ছবির রেজোলিউশন সেট করতে দিতে পারেন। বিকল্প হিসেবে, আপনি নির্দিষ্ট টার্গেট রেজোলিউশন বা নির্দিষ্ট অ্যাস্পেক্ট রেশিও সেট করতে পারেন, যা সেই কনফিগারেশনকে সাপোর্ট করে।

অটোমেটিক রেজোলিউশন

cameraProcessProvider.bindToLifecycle()-এ উল্লেখ করা ব্যবহারের কেসের উপর ভিত্তি করে CameraX অটোমেটিক সেরা রেজোলিউশন সেটিংস নির্ধারণ করতে পারে। সম্ভব হলে, একটি bindToLifecycle() কলে একটি সেশনে একসাথে রান করার জন্য প্রয়োজনীয় সব ব্যবহারের উদাহরণ নির্দিষ্ট করুন। CameraX ডিভাইসের সমর্থিত হার্ডওয়্যার লেভেল বিবেচনা করে এবং ডিভাইস-নির্দিষ্ট বৈচিত্র্যের (যেখানে একটি ডিভাইস উপলভ্য স্ট্রিম কনফিগারেশন অতিক্রম করে বা পূরণ করে না) জন্য অ্যাকাউন্টিং করে আবদ্ধ ব্যবহারের ক্ষেত্রে সেটের উপর ভিত্তি করে রেজোলিউশন নির্ধারণ করে। ডিভাইস-নির্দিষ্ট কোড পাথ কম করার সাথে সাথে বিভিন্ন ধরনের ডিভাইসে অ্যাপ্লিকেশন চালানোর অনুমতি দেওয়াই হল এর উদ্দেশ্য।

ইমেজ ক্যাপচার ও ইমেজ বিশ্লেষণ ব্যবহারের ক্ষেত্রে ডিফল্ট অ্যাস্পেক্ট রেশিও হল ৪:৩।

ব্যবহারের ক্ষেত্রে কনফিগার করা যায় এমন অ্যাস্পেক্ট রেশিও থাকে যাতে অ্যাপ্লিকেশন UI ডিজাইনের উপর ভিত্তি করে পছন্দসই অ্যাস্পেক্ট রেশিও নির্দিষ্ট করতে পারে। ডিভাইস যতটা সাপোর্ট করে তার সাথে যতটা সম্ভব কাছাকাছি অনুরোধ করা অ্যাস্পেক্ট রেশিওর সাথে ম্যাচ করার জন্য CameraX আউটপুট তৈরি করা হয়। যদি কোনও সম্পূর্ণ ম্যাচ করা রেজোলিউশন কাজ না করে, তাহলে যে রেজোলিউশনটি সবচেয়ে বেশি শর্ত পূরণ করে সেটি বেছে নেওয়া হয়। তাই, অ্যাপে ক্যামেরা কীভাবে দেখা যাবে তা অ্যাপ্লিকেশন নির্ধারণ করে এবং বিভিন্ন ডিভাইসে সেই অনুযায়ী সবচেয়ে ভালো ক্যামেরা রেজোলিউশন সেটিংস CameraX নির্ধারণ করে।

যেমন, কোনও অ্যাপ নিম্নলিখিত যেকোনও কাজ করতে পারে:

  • কোনও ব্যবহারের ক্ষেত্রে ৪:৩ বা ১৬:৯-এর টার্গেট রেজোলিউশন নির্দিষ্ট করুন
  • একটি কাস্টম রেজোলিউশন নির্দিষ্ট করুন, যার সাথে CameraX সবচেয়ে কাছাকাছি মিল খুঁজে বের করার চেষ্টা করে
  • ImageCapture-এর জন্য ক্রপিং অ্যাস্পেক্ট রেশিও উল্লেখ করুন

CameraX অটোমেটিক ইন্টার্নাল Camera2 সারফেস রেজোলিউশন বেছে নেয়। নিচের সারণীতে রেজোলিউশন দেখানো হয়েছে:

ব্যবহারের উদাহরণ ইন্টার্নাল সারফেস রেজোলিউশন আউটপুট ডেটা রেজোলিউশন
প্রিভিউ অ্যাস্পেক্ট রেশিও: যে রেজোলিউশন টার্গেটের সাথে সবচেয়ে ভালোভাবে ফিট করে সেটিং। ইন্টার্নাল সারফেস রেজোলিউশন। টার্গেট অ্যাস্পেক্ট রেশিওর জন্য ভিউ ক্রপ, স্কেল ও রোটেট করতে মেটাডেটা প্রদান করা হয়।
ডিফল্ট রেজোলিউশন: প্রিভিউয়ের সর্বোচ্চ রেজোলিউশন অথবা প্রিভিউয়ের অ্যাস্পেক্ট রেশিওর সাথে মেলে এমন ডিভাইসের জন্য উপযুক্ত সর্বোচ্চ রেজোলিউশন।
সর্বাধিক রেজোলিউশন: প্রিভিউ সাইজ, যা ডিভাইসের স্ক্রিন রেজোলিউশনের সাথে সবচেয়ে ভালো সাইজ ম্যাচ করে বা 1080p (1920x1080), যেটি ছোট।
ছবি বিশ্লেষণ অ্যাস্পেক্ট রেশিও: যে রেজোলিউশন টার্গেটের সাথে সবচেয়ে ভালোভাবে ফিট করে সেটিং। ইন্টার্নাল সারফেস রেজোলিউশন।
ডিফল্ট রেজোলিউশন: ডিফল্ট টার্গেট রেজোলিউশন সেটিং হল ৬৪০x৪৮০। টার্গেট রেজোলিউশন ও সংশ্লিষ্ট অ্যাস্পেক্ট রেশিও অ্যাডজাস্ট করলে সবচেয়ে ভালো রেজোলিউশন পাওয়া যায়।
সর্বাধিক রেজোলিউশন: ক্যামেরা ডিভাইসের সর্বাধিক আউটপুট রেজোলিউশন YUV_420_888 ফর্ম্যাট যা StreamConfigurationMap.getOutputSizes() থেকে পাওয়া যায়। টার্গেট রেজোলিউশন ডিফল্ট হিসেবে ৬৪০x৪৮০ সেট করা থাকে, তাই আপনি যদি ৬৪০x৪৮০-এর চেয়ে বেশি রেজোলিউশন চান, তাহলে আপনাকে অবশ্যই setTargetResolution() এবং setTargetAspectRatio() ব্যবহার করতে হবে যাতে কাজ করে এমন রেজোলিউশনের মধ্যে সবচেয়ে কাছাকাছি রেজোলিউশন পাওয়া যায়।
ইমেজ ক্যাপচার অ্যাস্পেক্ট রেশিও: সেটিংয়ের সাথে সবচেয়ে মানানসই অ্যাস্পেক্ট রেশিও। ইন্টার্নাল সারফেস রেজোলিউশন।
ডিফল্ট রেজোলিউশন: উপলভ্য সর্বোচ্চ রেজোলিউশন বা ডিভাইসের জন্য সবচেয়ে উপযুক্ত সর্বোচ্চ রেজোলিউশন যা ImageCapture-এর অ্যাস্পেক্ট রেশিওর সাথে মেলে।
সর্বাধিক রেজোলিউশন: JPEG ফর্ম্যাটে ক্যামেরা ডিভাইসের সর্বাধিক আউটপুট রেজোলিউশন। এটি ফিরিয়ে আনতে StreamConfigurationMap.getOutputSizes() ব্যবহার করুন।

রেজোলিউশন উল্লেখ করুন

setTargetResolution(Size resolution) পদ্ধতি ব্যবহার করে ব্যবহারের কেস তৈরি করার সময় আপনি নির্দিষ্ট রেজোলিউশন সেট করতে পারেন, যেমনটি নিম্নলিখিত কোড স্যাম্পেলে দেখানো হয়েছে:

Kotlin

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) দেখুন।

আপনার অ্যাপ Android 10 বা তার পরবর্তী যেকোনও ভার্সনে রান করলে, আপনি isSessionConfigurationSupported() ব্যবহার করে নির্দিষ্ট SessionConfiguration যাচাই করতে পারবেন।

ক্যামেরা আউটপুট কন্ট্রোল করুন

প্রতিটি স্বতন্ত্র ব্যবহারের ক্ষেত্রে প্রয়োজন অনুযায়ী ক্যামেরা আউটপুট কনফিগার করার পাশাপাশি, CameraX সমস্ত বাউন্ড ব্যবহারের ক্ষেত্রে সাধারণ ক্যামেরা অপারেশনগুলিকে সমর্থন করার জন্য নিম্নলিখিত ইন্টারফেসগুলিও প্রয়োগ করে:

  • CameraControl আপনাকে সাধারণ ক্যামেরা ফিচার কনফিগার করতে দেয়।
  • CameraInfo আপনাকে সেইসব সাধারণ ক্যামেরা ফিচারের স্ট্যাটাস সম্পর্কে কোয়েরি করার সুবিধা দেয়।

CameraControl-এর সাথে কাজ করে এমন ক্যামেরা ফিচারগুলি হল:

  • জুম
  • টর্চ, যার মধ্যে কাজ করে এমন ডিভাইসে টর্চের আলো অন্তর্ভুক্ত
  • কাজ করে এমন ডিভাইসে লো-লাইট বুস্ট
  • ফোকাস ও মিটারিং (ফোকাস করতে ট্যাপ করুন)
  • এক্সপোজার নিয়ন্ত্রণ

CameraControl ও CameraInfo-এর ইনস্ট্যান্স পান

ProcessCameraProvider.bindToLifecycle()-এর মাধ্যমে ফেরত পাঠানো Camera অবজেক্ট ব্যবহার করে CameraControl ও CameraInfo-এর ইনস্ট্যান্স ফিরিয়ে আনুন। নিচের কোডটি একটি উদাহরণ দেখায়:

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

জাভা

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 দেখুন।

দুটি API-এর মাধ্যমে রিটার্ন করা ListenableFuture, নির্দিষ্ট জুম ভ্যালু সহ রিপিট করা অনুরোধ সম্পূর্ণ হলে অ্যাপ্লিকেশনকে বিজ্ঞপ্তি পাঠানোর বিকল্প অফার করে। এছাড়াও, আগের অপারেশন চলাকালীন আপনি নতুন জুম ভ্যালু সেট করলে, আগের জুম অপারেশনের ListenableFuture অবিলম্বে ব্যর্থ হয়।

টর্চ

CameraControl.enableTorch(boolean) টর্চ (ফ্ল্যাশলাইট নামেও পরিচিত) চালু বা বন্ধ করে।

CameraInfo.getTorchState() ব্যবহার করে টর্চের বর্তমান অবস্থা সম্পর্কে কোয়েরি করা যেতে পারে। টর্চ উপলভ্য আছে কিনা তা নির্ধারণ করতে আপনি CameraInfo.hasFlashUnit() দ্বারা ফেরত দেওয়া ভ্যালু চেক করতে পারেন। তা না হলে, CameraControl.enableTorch(boolean) কল করলে, রিটার্ন করা ListenableFuture ব্যর্থ ফলাফল সহ অবিলম্বে সম্পূর্ণ হয় এবং টর্চের স্ট্যাটাস TorchState.OFF-এ সেট করে।

টর্চ চালু করা থাকলে, ফ্ল্যাশ মোড সেটিং যাই হোক না কেন, ফটো ও ভিডিও ক্যাপচার করার সময় এটি চালু থাকে। ImageCapture.setFlashMode() দিয়ে সেট করা ফ্ল্যাশ মোড শুধুমাত্র টর্চ বন্ধ থাকলে কাজ করে।

ফোকাস ও মিটারিং

CameraControl.startFocusAndMetering() প্রদত্ত FocusMeteringAction-এর উপর ভিত্তি করে মিটারিং অঞ্চল সেট করার মাধ্যমে অটোফোকাস (AF), অটো এক্সপোজার (AE) এবং অটো হোয়াইট ব্যালেন্স (AWB) মিটারিং ট্রিগার করে। ক্যামেরা অ্যাপে ট্যাপ-টু-ফোকাস ফিচার প্রয়োগ করতে আপনি এই পদ্ধতি ব্যবহার করতে পারেন।

MeteringPoint

শুরু করতে, MeteringPointFactory.createPoint(float x, float y, float size) ব্যবহার করে MeteringPoint তৈরি করুন। MeteringPoint হল ক্যামেরার একটি একক পয়েন্ট Surface। এটি একটি নরমালাইজড ফর্মে স্টোর করা হয় যাতে এটি AF/AE/AWB অঞ্চল নির্দিষ্ট করার জন্য সেন্সর কোঅর্ডিনেটে কনভার্ট করা যায়।

MeteringPoint-এর সাইজ ০ থেকে ১-এর মধ্যে হয়, ডিফল্ট সাইজ হল 0.15f. MeteringPointFactory.createPoint(float x, float y, float size) কল করার সময়, CameraX প্রদত্ত size-এর জন্য (x, y)-এ কেন্দ্র করে একটি আয়তক্ষেত্রাকার অঞ্চল তৈরি করে।

নিচের কোড থেকে জানা যাবে কীভাবে 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 নেয় যার মধ্যে AF/AE/AWB মিটারিং অঞ্চলের জন্য একটি MeteringPoint এবং শুধুমাত্র AF ও AE-এর জন্য আরেকটি MeteringPoint থাকে।

যেহেতু প্রতিটি ডিভাইস AF/AE/AWB এবং একাধিক অঞ্চল সাপোর্ট করে না, তাই CameraX FocusMeteringAction-কে যথাসাধ্য কার্যকর করে। CameraX, পয়েন্ট যোগ করার ক্রম অনুযায়ী, সমর্থিত MeteringPoint অবজেক্টের সর্বাধিক সংখ্যা ব্যবহার করে। সর্বাধিক সংখ্যার পরে যোগ করা সমস্ত পয়েন্ট উপেক্ষা করা হয়। যেমন, কোনও প্ল্যাটফর্মে FocusMeteringAction-এর সাথে তিনটি MeteringPoint অবজেক্ট দেওয়া হলে, প্ল্যাটফর্মটি শুধু দুটিকে সাপোর্ট করে, তাই শুধু প্রথম দুটি পয়েন্ট ব্যবহার করা হয়। CameraX, ফাইনাল MeteringPoint উপেক্ষা করে।

এক্সপোজার নিয়ন্ত্রণ

অটো এক্সপোজার (AE) আউটপুট ফলাফলের বাইরে অ্যাপ্লিকেশনকে এক্সপোজার ভ্যালু (EV) ফাইন-টিউন করতে হলে এক্সপোজার নিয়ন্ত্রণ উপযোগী হয়। বর্তমান ছবির অবস্থা অনুযায়ী প্রয়োজনীয় এক্সপোজার নির্ধারণ করতে এক্সপোজার নিয়ন্ত্রণ ভ্যালু নিম্নলিখিত উপায়ে একত্রিত করা হয়:

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 পুনরুদ্ধার করে, যার মধ্যে রয়েছে:

    • এক্সপোজার নিয়ন্ত্রণ কন্ট্রোলের সাপোর্টযোগ্যতা।
    • বর্তমান এক্সপোজার নিয়ন্ত্রণ ইন্ডেক্স।
    • এক্সপোজার নিয়ন্ত্রণ ইনডেক্স রেঞ্জ।
    • এক্সপোজার নিয়ন্ত্রণ ভ্যালু গণনা করার জন্য এক্সপোজার নিয়ন্ত্রণ স্টেপ ব্যবহার করা হয়।

যেমন, নিম্নলিখিত কোডটি বর্তমান ExposureState ভ্যালু সহ SeekBar এক্সপোজারের জন্য সেটিংস শুরু করে। যেহেতু 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 সম্পর্কে আরও জানতে, নিম্নলিখিত অতিরিক্ত রিসোর্স দেখুন।

কোডল্যাব

  • CameraX ব্যবহার শুরু করা
  • কোডের স্যাম্পেল

  • CameraX স্যাম্পেল অ্যাপ
  • ডেভেলপার কমিউনিটি

    Android CameraX আলোচনা গ্রুপ