আপনি প্রতিটি 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 ব্যবহার করার উপায় বর্ণনা করা হয়েছে:
- আপনার নিজস্ব কনফিগারেশন দিয়ে একটি
CameraXConfigঅবজেক্ট তৈরি করুন। - আপনার
ApplicationCameraXConfig.Providerইন্টারফেসটি ইমপ্লিমেন্ট করুন এবংgetCameraXConfig()ফাংশনে আপনারCameraXConfigঅবজেক্টটি রিটার্ন করুন। - এখানে বর্ণিত পদ্ধতি অনুযায়ী আপনার
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-কে অন্যান্য ক্যামেরা উপেক্ষা করার জন্য সেট করতে পারেন, যা আপনার অ্যাপ্লিকেশন দ্বারা ব্যবহৃত ক্যামেরাগুলোর স্টার্টআপ ল্যাটেন্সি কমাতে পারে।
CameraXConfig.Builder.setAvailableCamerasLimiter() -এ পাস করা CameraSelector যদি কোনো ক্যামেরাকে ফিল্টার করে বাদ দেয়, তাহলে CameraX এমনভাবে আচরণ করে যেন সেই ক্যামেরাটির কোনো অস্তিত্বই নেই। উদাহরণস্বরূপ, নিম্নলিখিত কোডটি অ্যাপ্লিকেশনটিকে শুধুমাত্র ডিভাইসের ডিফল্ট ব্যাক ক্যামেরা ব্যবহার করার জন্য সীমাবদ্ধ করে:
কোটলিন
class MainApplication : Application(), CameraXConfig.Provider { override fun getCameraXConfig(): CameraXConfig { return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig()) .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA) .build() } }
থ্রেড
CameraX যে প্ল্যাটফর্ম API-গুলোর উপর ভিত্তি করে তৈরি, সেগুলোর অনেকগুলোর জন্যই হার্ডওয়্যারের সাথে ব্লকিং ইন্টারপ্রসেস কমিউনিকেশন (IPC) প্রয়োজন হয়, যা সাড়া দিতে কখনও কখনও কয়েকশো মিলিসেকেন্ড সময় নিতে পারে। এই কারণে, CameraX শুধুমাত্র ব্যাকগ্রাউন্ড থ্রেড থেকে এই API-গুলোকে কল করে, যাতে মূল থ্রেড ব্লক না হয় এবং UI সাবলীল থাকে। CameraX অভ্যন্তরীণভাবে এই ব্যাকগ্রাউন্ড থ্রেডগুলোকে এমনভাবে পরিচালনা করে যে এই আচরণটি স্বচ্ছ বলে মনে হয়। তবে, কিছু অ্যাপ্লিকেশনের জন্য থ্রেডের উপর কঠোর নিয়ন্ত্রণ প্রয়োজন হয়। CameraXConfig একটি অ্যাপ্লিকেশনকে CameraXConfig.Builder.setCameraExecutor() এবং CameraXConfig.Builder.setSchedulerHandler() এর মাধ্যমে ব্যবহৃত ব্যাকগ্রাউন্ড থ্রেডগুলো সেট করার সুযোগ দেয়।
ক্যামেরা নির্বাহক
ক্যামেরা এক্সিকিউটরটি ক্যামেরা প্ল্যাটফর্মের সমস্ত অভ্যন্তরীণ এপিআই কল এবং সেই এপিআইগুলো থেকে আসা কলব্যাকগুলোর জন্য ব্যবহৃত হয়। CameraX এই কাজগুলো করার জন্য একটি অভ্যন্তরীণ Executor বরাদ্দ ও পরিচালনা করে। তবে, যদি আপনার অ্যাপ্লিকেশনের থ্রেডগুলোর উপর আরও কঠোর নিয়ন্ত্রণের প্রয়োজন হয়, তাহলে CameraXConfig.Builder.setCameraExecutor() ব্যবহার করুন।
শিডিউলার হ্যান্ডলার
শিডিউলার হ্যান্ডলারটি নির্দিষ্ট বিরতিতে অভ্যন্তরীণ কাজগুলো নির্ধারণ করতে ব্যবহৃত হয়, যেমন ক্যামেরা উপলব্ধ না থাকলে সেটি পুনরায় খোলার চেষ্টা করা। এই হ্যান্ডলারটি কোনো কাজ সম্পাদন করে না, এবং শুধুমাত্র সেগুলোকে ক্যামেরা এক্সিকিউটরের কাছে প্রেরণ করে। এটি কখনও কখনও সেইসব লিগ্যাসি এপিআই প্ল্যাটফর্মেও ব্যবহৃত হয় যেখানে কলব্যাকের জন্য একটি 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 এমন আউটপুট তৈরি করে যা অ্যাপটিকে প্রিভিউতে আপনার প্রত্যাশিত দৃশ্যের সাথে মেলাতে সাহায্য করে। একাধিক ডিসপ্লে ডিভাইস সমর্থন করার জন্য, আপনি ইউজ কেস অবজেক্ট কনফিগার করার সময় বর্তমান ডিসপ্লে ওরিয়েন্টেশন উল্লেখ করে অথবা সেগুলি তৈরি হওয়ার পরে ডায়নামিকভাবে রোটেশন পরিবর্তন করে একটি কাস্টম মান সেট করতে পারেন।
আপনার অ্যাপ কনফিগারেশন সেটিংস ব্যবহার করে টার্গেট রোটেশন সেট করতে পারে। এরপর এটি ইউজ কেস এপিআই (use case APIs)-এর মেথডগুলো (যেমন 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()ব্যবহার করে টার্গেট রেজোলিউশনের ঘূর্ণন জানা যায়। - ইমেজ অ্যানালাইসিস : মেটাডেটা আউটপুট প্রদান করা হয়, যাতে ডিসপ্লে কোঅর্ডিনেটের সাপেক্ষে ইমেজ বাফার কোঅর্ডিনেট জানা যায়।
- ইমেজক্যাপচার : রোটেশন সেটিংটি উল্লেখ করার জন্য ইমেজের এক্সিফ মেটাডেটা, বাফার, অথবা বাফার ও মেটাডেটা উভয়ই পরিবর্তন করা হয়। পরিবর্তিত মানটি এইচএএল (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 ব্যবহারকারীদের কাছে দৃশ্যমান বাফার রেক্ট নির্ধারণ করে। এরপর ক্যামেরাএক্স ভিউপোর্টের বৈশিষ্ট্য এবং সংযুক্ত ইউজ কেসের উপর ভিত্তি করে সম্ভাব্য বৃহত্তম ক্রপ রেক্ট গণনা করে। সাধারণত, একটি 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.DEFAULT_FRONT_CAMERAব্যবহার করে ডিফল্ট ফ্রন্ট ফেসিং ক্যামেরাটি অনুরোধ করুন। -
CameraSelector.DEFAULT_BACK_CAMERAব্যবহার করে ডিফল্ট পেছনের ক্যামেরাটি অনুরোধ করুন। -
CameraSelector.Builder.addCameraFilter()ব্যবহার করে উপলব্ধ ডিভাইসগুলির তালিকা তাদেরCameraCharacteristicsঅনুযায়ী ফিল্টার করুন।
নিম্নলিখিত কোড নমুনাটি দেখায় কিভাবে ডিভাইস নির্বাচনকে প্রভাবিত করার জন্য একটি 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 অবজেক্ট থেকে কিভাবে উভয় `Camera` অবজেক্ট পাওয়া যায়।
কোটলিন
// 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() -এ নির্দিষ্ট করা ব্যবহারের ক্ষেত্রগুলোর (use cases) উপর ভিত্তি করে স্বয়ংক্রিয়ভাবে সেরা রেজোলিউশন সেটিংস নির্ধারণ করতে পারে। যখনই সম্ভব, একটি একক সেশনে একযোগে চালানোর জন্য প্রয়োজনীয় সমস্ত ব্যবহারের ক্ষেত্র একটিমাত্র bindToLifecycle() কলে নির্দিষ্ট করুন। CameraX, ডিভাইসের সমর্থিত হার্ডওয়্যার স্তর বিবেচনা করে এবং ডিভাইস-নির্দিষ্ট ভিন্নতা (যেখানে একটি ডিভাইস উপলব্ধ স্ট্রিম কনফিগারেশনকে অতিক্রম করে বা পূরণ করে না) হিসাব করে, আবদ্ধ ব্যবহারের ক্ষেত্রগুলোর সেটের উপর ভিত্তি করে রেজোলিউশন নির্ধারণ করে। এর উদ্দেশ্য হলো ডিভাইস-নির্দিষ্ট কোড পাথগুলো কমিয়ে এনে অ্যাপ্লিকেশনটিকে বিভিন্ন ধরণের ডিভাইসে চালানোর সুযোগ দেওয়া।
ছবি তোলা এবং ছবি বিশ্লেষণের ক্ষেত্রে ডিফল্ট অ্যাস্পেক্ট রেশিও হলো ৪:৩।
ইউজ কেসগুলোতে একটি কনফিগারযোগ্য অ্যাস্পেক্ট রেশিও থাকে, যা অ্যাপ্লিকেশনটিকে UI ডিজাইনের উপর ভিত্তি করে কাঙ্ক্ষিত অ্যাস্পেক্ট রেশিও নির্দিষ্ট করার সুযোগ দেয়। CameraX-এর আউটপুট এমনভাবে তৈরি করা হয়, যাতে ডিভাইসটি যতটা সম্ভব অনুরোধ করা অ্যাস্পেক্ট রেশিওর সাথে হুবহু মিলে যায়। যদি হুবহু মিলে যায় এমন কোনো রেজোলিউশন সমর্থিত না থাকে, তবে যেটি সর্বাধিক শর্ত পূরণ করে, সেটিই নির্বাচিত হয়। এইভাবে, অ্যাপে ক্যামেরাটি কেমন দেখাবে তা অ্যাপ্লিকেশনটিই নির্ধারণ করে, এবং CameraX বিভিন্ন ডিভাইসে সেই চাহিদা পূরণের জন্য সেরা ক্যামেরা রেজোলিউশন সেটিংস নির্ধারণ করে।
উদাহরণস্বরূপ, একটি অ্যাপ নিম্নলিখিত যেকোনো কাজ করতে পারে:
- ব্যবহারের ক্ষেত্র অনুযায়ী 4:3 বা 16:9 এর একটি লক্ষ্য রেজোলিউশন নির্দিষ্ট করুন।
- একটি কাস্টম রেজোলিউশন নির্দিষ্ট করুন, যার সবচেয়ে কাছাকাছি মিল খুঁজে বের করার চেষ্টা করে CameraX।
-
ImageCaptureজন্য একটি ক্রপিং অ্যাস্পেক্ট রেশিও নির্দিষ্ট করুন।
CameraX স্বয়ংক্রিয়ভাবে Camera2-এর অভ্যন্তরীণ পৃষ্ঠের রেজোলিউশনগুলো নির্বাচন করে। নিচের সারণিতে রেজোলিউশনগুলো দেখানো হলো:
| ব্যবহারের ক্ষেত্র | অভ্যন্তরীণ পৃষ্ঠের রেজোলিউশন | আউটপুট ডেটা রেজোলিউশন |
|---|---|---|
| প্রিভিউ | অ্যাসপেক্ট রেশিও: যে রেজোলিউশনটি সেটিংয়ের সাথে লক্ষ্যবস্তুকে সবচেয়ে ভালোভাবে মানানসই করে। | অভ্যন্তরীণ পৃষ্ঠের রেজোলিউশন। কাঙ্ক্ষিত অ্যাস্পেক্ট রেশিও অনুযায়ী একটি ভিউকে ক্রপ, স্কেল এবং রোটেট করার জন্য মেটাডেটা সরবরাহ করা হয়। |
| ডিফল্ট রেজোলিউশন: সর্বোচ্চ প্রিভিউ রেজোলিউশন, অথবা ডিভাইসের পছন্দের সর্বোচ্চ রেজোলিউশন যা প্রিভিউ-এর অ্যাসপেক্ট রেশিওর সাথে মেলে। | ||
| সর্বোচ্চ রেজোলিউশন: প্রিভিউ সাইজ, যা ডিভাইসের স্ক্রিন রেজোলিউশন বা 1080p (1920x1080)-এর সাথে সবচেয়ে ভালোভাবে মেলে এমন আকারকে বোঝায়, দুটির মধ্যে যেটি ছোট। | ||
| চিত্র বিশ্লেষণ | অ্যাসপেক্ট রেশিও: যে রেজোলিউশনটি সেটিংয়ের সাথে লক্ষ্যবস্তুকে সবচেয়ে ভালোভাবে খাপ খাওয়ায়। | অভ্যন্তরীণ পৃষ্ঠের রেজোলিউশন। |
| ডিফল্ট রেজোলিউশন: ডিফল্ট টার্গেট রেজোলিউশন সেটিং হলো 640x480। টার্গেট রেজোলিউশন এবং সংশ্লিষ্ট অ্যাসপেক্ট রেশিও উভয়ই অ্যাডজাস্ট করলে সেরা সমর্থিত রেজোলিউশনটি পাওয়া যায়। | ||
সর্বোচ্চ রেজোলিউশন: ক্যামেরা ডিভাইসটির YUV_420_888 ফরম্যাটের সর্বোচ্চ আউটপুট রেজোলিউশন, যা StreamConfigurationMap.getOutputSizes() থেকে পাওয়া যায়। ডিফল্টভাবে টার্গেট রেজোলিউশন 640x480 সেট করা থাকে, তাই আপনি যদি 640x480-এর চেয়ে বড় কোনো রেজোলিউশন চান, তবে সমর্থিত রেজোলিউশনগুলো থেকে সবচেয়ে কাছেরটি পাওয়ার জন্য আপনাকে অবশ্যই setTargetResolution() এবং setTargetAspectRatio() ব্যবহার করতে হবে। | ||
| ছবি তোলা | আস্পেক্ট রেশিও: যে আস্পেক্ট রেশিওটি সেটিংয়ের জন্য সবচেয়ে উপযুক্ত। | অভ্যন্তরীণ পৃষ্ঠের রেজোলিউশন। |
| ডিফল্ট রেজোলিউশন: উপলব্ধ সর্বোচ্চ রেজোলিউশন, অথবা ডিভাইসের পছন্দের সর্বোচ্চ রেজোলিউশন যা ইমেজক্যাপচারের অ্যাস্পেক্ট রেশিওর সাথে মেলে। | ||
সর্বোচ্চ রেজোলিউশন: 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 প্রকাশ করুন। উদাহরণস্বরূপ, স্বাভাবিক টার্গেট রোটেশনে পোর্ট্রেট ন্যাচারাল ওরিয়েন্টেশনে থাকা একটি ডিভাইস যদি পোর্ট্রেট ইমেজ চায়, তবে সেটি 480x640 নির্দিষ্ট করতে পারে, এবং একই ডিভাইসটি 90 ডিগ্রি ঘুরিয়ে ল্যান্ডস্কেপ ওরিয়েন্টেশন টার্গেট করলে 640x480 নির্দিষ্ট করতে পারে।
টার্গেট রেজোলিউশন ছবির রেজোলিউশনের জন্য একটি সর্বনিম্ন সীমা নির্ধারণের চেষ্টা করে। ক্যামেরার বাস্তবায়ন দ্বারা নির্ধারিত, প্রকৃত ছবির রেজোলিউশন হলো আকারের দিক থেকে টার্গেট রেজোলিউশনের চেয়ে ছোট নয় এমন নিকটতম উপলব্ধ রেজোলিউশন।
তবে, যদি লক্ষ্য রেজোলিউশনের সমান বা তার চেয়ে বড় কোনো রেজোলিউশন না থাকে, তাহলে লক্ষ্য রেজোলিউশনের চেয়ে ছোট নিকটতম উপলব্ধ রেজোলিউশনটি বেছে নেওয়া হয়। প্রদত্ত Size সাথে একই অ্যাসপেক্ট রেশিওযুক্ত রেজোলিউশনগুলোকে ভিন্ন অ্যাসপেক্ট রেশিওযুক্ত রেজোলিউশনগুলোর চেয়ে বেশি অগ্রাধিকার দেওয়া হয়।
CameraX অনুরোধের উপর ভিত্তি করে সবচেয়ে উপযুক্ত রেজোলিউশন প্রয়োগ করে। যদি মূল প্রয়োজন অ্যাস্পেক্ট রেশিও ঠিক রাখা হয়, তবে শুধু setTargetAspectRatio উল্লেখ করুন, এবং CameraX ডিভাইসের উপর ভিত্তি করে একটি নির্দিষ্ট উপযুক্ত রেজোলিউশন নির্ধারণ করবে। যদি অ্যাপটির মূল প্রয়োজন হয় ইমেজ প্রসেসিংকে আরও কার্যকর করার জন্য একটি রেজোলিউশন নির্দিষ্ট করা (উদাহরণস্বরূপ ডিভাইসের প্রসেসিং ক্ষমতার উপর ভিত্তি করে একটি ছোট বা মাঝারি আকারের ছবি), তবে setTargetResolution(Size resolution) ব্যবহার করুন।
আপনার অ্যাপের যদি একটি নির্দিষ্ট রেজোলিউশনের প্রয়োজন হয়, তাহলে প্রতিটি হার্ডওয়্যার লেভেল দ্বারা সমর্থিত সর্বোচ্চ রেজোলিউশনগুলো নির্ধারণ করতে createCaptureSession() এর ভেতরের টেবিলটি দেখুন। বর্তমান ডিভাইস দ্বারা সমর্থিত নির্দিষ্ট রেজোলিউশনগুলো পরীক্ষা করতে, StreamConfigurationMap.getOutputSizes(int) দেখুন।
আপনার অ্যাপটি অ্যান্ড্রয়েড ১০ বা তার উচ্চতর সংস্করণে চললে, আপনি একটি নির্দিষ্ট SessionConfiguration যাচাই করার জন্য isSessionConfigurationSupported() ব্যবহার করতে পারেন।
ক্যামেরার আউটপুট নিয়ন্ত্রণ করুন
প্রতিটি স্বতন্ত্র ব্যবহারের ক্ষেত্রে প্রয়োজন অনুযায়ী ক্যামেরার আউটপুট কনফিগার করার সুবিধা দেওয়ার পাশাপাশি, CameraX সকল বাউন্ড ব্যবহারের ক্ষেত্রে সাধারণ ক্যামেরা অপারেশনগুলোকে সমর্থন করার জন্য নিম্নলিখিত ইন্টারফেসগুলোও প্রয়োগ করে:
-
CameraControlআপনাকে ক্যামেরার সাধারণ বৈশিষ্ট্যগুলো কনফিগার করতে দেয়। -
CameraInfoআপনাকে ক্যামেরার সাধারণ ফিচারগুলোর অবস্থা জানতে সাহায্য করে।
ক্যামেরা কন্ট্রোল দ্বারা সমর্থিত ক্যামেরার বৈশিষ্ট্যগুলো হলো:
- জুম
- মশাল
- ফোকাস এবং মিটারিং (ট্যাপ-টু-ফোকাস)
- এক্সপোজার ক্ষতিপূরণ
CameraControl এবং CameraInfo-এর ইনস্ট্যান্সগুলো পান
ProcessCameraProvider.bindToLifecycle() দ্বারা ফেরত আসা Camera অবজেক্টটি ব্যবহার করে CameraControl এবং CameraInfo এর ইনস্ট্যান্সগুলো পুনরুদ্ধার করুন। নিম্নলিখিত কোডটিতে একটি উদাহরণ দেখানো হয়েছে:
কোটলিন
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 রিটার্ন করে।
জুম
ক্যামেরা কন্ট্রোল জুম লেভেল পরিবর্তন করার জন্য দুটি পদ্ধতি প্রদান করে:
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 টি অ্যাপ্লিকেশনগুলিকে নির্দিষ্ট জুম মান সহ একটি পুনরাবৃত্তিমূলক অনুরোধ সম্পন্ন হলে অবহিত করার বিকল্প প্রদান করে। এছাড়াও, পূর্ববর্তী অপারেশনটি চলমান থাকা অবস্থায় যদি আপনি একটি নতুন জুম মান সেট করেন, তাহলে পূর্ববর্তী জুম অপারেশনের ListenableFuture অবিলম্বে ব্যর্থ হয়ে যায়।
মশাল
CameraControl.enableTorch(boolean) টর্চ (যা ফ্ল্যাশলাইট নামেও পরিচিত) চালু বা বন্ধ করে।
বর্তমান টর্চের অবস্থা জানতে CameraInfo.getTorchState() ব্যবহার করা যেতে পারে। টর্চ উপলব্ধ আছে কিনা তা জানতে আপনি CameraInfo.hasFlashUnit() দ্বারা ফেরত আসা মানটি পরীক্ষা করতে পারেন। যদি উপলব্ধ না থাকে, তাহলে CameraControl.enableTorch(boolean) কল করলে ফেরত আসা ListenableFuture অবিলম্বে একটি ব্যর্থ ফলাফল সহ সম্পূর্ণ হয়ে যায় এবং টর্চের অবস্থা TorchState.OFF এ সেট হয়ে যায়।
টর্চ চালু থাকলে, ফ্ল্যাশমোড সেটিং নির্বিশেষে ছবি ও ভিডিও তোলার সময় তা জ্বলে থাকে। ImageCapture এর flashMode শুধুমাত্র টর্চ বন্ধ থাকলেই কাজ করে।
ফোকাস এবং মিটারিং
CameraControl.startFocusAndMetering() প্রদত্ত FocusMeteringAction-এর উপর ভিত্তি করে AF/AE/AWB মিটারিং অঞ্চল নির্ধারণের মাধ্যমে অটোফোকাস এবং এক্সপোজার মিটারিং চালু করে। অনেক ক্যামেরা অ্যাপ্লিকেশনে “ট্যাপ টু ফোকাস” বৈশিষ্ট্যটি বাস্তবায়নের জন্য এটি প্রায়শই ব্যবহৃত হয়।
মিটারিংপয়েন্ট
শুরু করার জন্য, 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 প্রদত্ত size জন্য (x, y) বিন্দুতে কেন্দ্র করে একটি আয়তক্ষেত্রাকার অঞ্চল তৈরি করে।
নিম্নলিখিত কোডটি দেখায় কিভাবে একটি 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 গ্রহণ করে, যা AF/AE/AWB মিটারিং অঞ্চলের জন্য একটি MeteringPoint এবং শুধুমাত্র AF ও AE-এর জন্য আরেকটি MeteringPoint নিয়ে গঠিত।
অভ্যন্তরীণভাবে, CameraX এটিকে Camera2 MeteringRectangles এ রূপান্তর করে এবং ক্যাপচার অনুরোধে সংশ্লিষ্ট CONTROL_AF_REGIONS / CONTROL_AE_REGIONS / CONTROL_AWB_REGIONS প্যারামিটারগুলো সেট করে।
যেহেতু সব ডিভাইস AF/AE/AWB এবং একাধিক রিজিয়ন সমর্থন করে না, তাই CameraX সর্বোচ্চ চেষ্টার সাথে FocusMeteringAction টি সম্পাদন করে। CameraX সমর্থিত সর্বোচ্চ সংখ্যক মিটারিংপয়েন্ট ব্যবহার করে, যে ক্রমে পয়েন্টগুলো যোগ করা হয়েছিল সেই ক্রমে। সর্বোচ্চ সংখ্যার পরে যোগ করা সমস্ত মিটারিংপয়েন্ট উপেক্ষা করা হয়। উদাহরণস্বরূপ, যদি একটি FocusMeteringAction ৩টি মিটারিংপয়েন্ট থাকে এমন একটি প্ল্যাটফর্মে যা কেবল ২টি সমর্থন করে, তবে শুধুমাত্র প্রথম ২টি মিটারিংপয়েন্ট ব্যবহার করা হবে। শেষ MeteringPoint CameraX দ্বারা উপেক্ষা করা হয়।
এক্সপোজার ক্ষতিপূরণ
যখন অ্যাপ্লিকেশনগুলিকে অটো এক্সপোজার (AE) আউটপুট ফলাফলের বাইরে এক্সপোজার মান (EV) সূক্ষ্মভাবে সমন্বয় করার প্রয়োজন হয়, তখন এক্সপোজার কম্পেনসেশন কার্যকর হয়। বর্তমান ছবির অবস্থার জন্য প্রয়োজনীয় এক্সপোজার নির্ধারণ করতে এক্সপোজার কম্পেনসেশন মানগুলিকে নিম্নলিখিত উপায়ে একত্রিত করা হয়:
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পুনরুদ্ধার করে, যার মধ্যে অন্তর্ভুক্ত থাকে:- এক্সপোজার ক্ষতিপূরণ নিয়ন্ত্রণের সমর্থনযোগ্যতা।
- বর্তমান এক্সপোজার ক্ষতিপূরণ সূচক।
- এক্সপোজার ক্ষতিপূরণ সূচকের পরিসর।
- এক্সপোজার ক্ষতিপূরণ মান গণনায় ব্যবহৃত এক্সপোজার ক্ষতিপূরণ ধাপ।
উদাহরণস্বরূপ, নিম্নলিখিত কোডটি বর্তমান ExposureState মানগুলির সাহায্যে একটি এক্সপোজার SeekBar এর সেটিংস শুরু করে:
কোটলিন
val exposureState = camera.cameraInfo.exposureState binding.seekBar.apply { isEnabled = exposureState.isExposureCompensationSupported max = exposureState.exposureCompensationRange.upper min = exposureState.exposureCompensationRange.lower progress = exposureState.exposureCompensationIndex }
অতিরিক্ত সম্পদ
CameraX সম্পর্কে আরও জানতে, নিম্নলিখিত অতিরিক্ত উৎসগুলো দেখুন।
কোডল্যাব
কোডের নমুনা
ডেভেলপার কমিউনিটি
অ্যান্ড্রয়েড ক্যামেরাএক্স আলোচনা গোষ্ঠী