1. আপনি শুরু করার আগে
কোড টাইপ করা পেশী মেমরি তৈরি করার এবং উপাদান সম্পর্কে আপনার বোঝার গভীরতর করার একটি দুর্দান্ত উপায়। যদিও কপি-পেস্ট করা একটি সময় বাঁচাতে পারে, এই অনুশীলনে বিনিয়োগ দীর্ঘমেয়াদে আরও বেশি দক্ষতা এবং শক্তিশালী কোডিং দক্ষতার দিকে নিয়ে যেতে পারে।
এই কোডল্যাবে, আপনি শিখবেন কীভাবে একটি অ্যান্ড্রয়েড অ্যাপ্লিকেশন তৈরি করতে হয় যা টেনসরফ্লো লাইট, LiteRT-এর জন্য Google-এর নতুন রানটাইম ব্যবহার করে লাইভ ক্যামেরা ফিডে রিয়েল-টাইম ইমেজ সেগমেন্টেশন সম্পাদন করে। আপনি একটি স্টার্টার অ্যান্ড্রয়েড অ্যাপ্লিকেশন নেবেন এবং এতে ইমেজ সেগমেন্টেশন ক্ষমতা যুক্ত করবেন। এছাড়াও আমরা প্রি-প্রসেসিং, ইনফারেন্স, এবং পোস্টপ্রসেসিং ধাপগুলিও অতিক্রম করব। আপনি করবেন:
- একটি অ্যান্ড্রয়েড অ্যাপ তৈরি করুন যা রিয়েল-টাইমে ছবিগুলিকে ভাগ করে।
- একটি প্রাক-প্রশিক্ষিত LiteRT ইমেজ সেগমেন্টেশন মডেল একীভূত করুন।
- মডেলের জন্য ইনপুট ইমেজ প্রিপ্রসেস করুন।
- CPU এবং GPU ত্বরণের জন্য LiteRT রানটাইম ব্যবহার করুন।
- সেগমেন্টেশন মাস্ক প্রদর্শন করতে মডেলের আউটপুট কিভাবে প্রক্রিয়া করতে হয় তা বুঝুন।
- সামনের ক্যামেরার জন্য কীভাবে সামঞ্জস্য করা যায় তা বুঝুন।
শেষ পর্যন্ত, আপনি নীচের চিত্রের অনুরূপ কিছু তৈরি করবেন:
পূর্বশর্ত
এই কোডল্যাবটি অভিজ্ঞ মোবাইল ডেভেলপারদের জন্য ডিজাইন করা হয়েছে যারা মেশিন লার্নিংয়ের অভিজ্ঞতা অর্জন করতে চান। আপনার সাথে পরিচিত হওয়া উচিত:
- কোটলিন এবং অ্যান্ড্রয়েড স্টুডিও ব্যবহার করে অ্যান্ড্রয়েড বিকাশ
- ইমেজ প্রসেসিং এর মৌলিক ধারণা
আপনি কি শিখবেন
- কিভাবে একটি অ্যান্ড্রয়েড অ্যাপ্লিকেশনে LiteRT রানটাইম সংহত এবং ব্যবহার করবেন।
- কিভাবে একটি প্রাক-প্রশিক্ষিত LiteRT মডেল ব্যবহার করে ইমেজ সেগমেন্টেশন করতে হয়।
- মডেলের জন্য ইনপুট ইমেজ কিভাবে প্রিপ্রসেস করবেন।
- মডেলের জন্য অনুমান চালানো কিভাবে.
- ফলাফলগুলি কল্পনা করার জন্য একটি বিভাজন মডেলের আউটপুট কীভাবে প্রক্রিয়া করা যায়।
- রিয়েল-টাইম ক্যামেরা ফিড প্রক্রিয়াকরণের জন্য ক্যামেরাএক্স কীভাবে ব্যবহার করবেন।
আপনি কি প্রয়োজন হবে
- অ্যান্ড্রয়েড স্টুডিওর একটি সাম্প্রতিক সংস্করণ (v2025.1.1 এ পরীক্ষা করা হয়েছে)।
- একটি শারীরিক অ্যান্ড্রয়েড ডিভাইস। এটি গ্যালাক্সি এবং পিক্সেল ডিভাইসে সেরা পরীক্ষা করা হয়।
- নমুনা কোড (GitHub থেকে)।
- কোটলিনে অ্যান্ড্রয়েড বিকাশের প্রাথমিক জ্ঞান।
2. ইমেজ সেগমেন্টেশন
ইমেজ সেগমেন্টেশন হল একটি কম্পিউটার ভিশন টাস্ক যার মধ্যে একটি ইমেজকে একাধিক সেগমেন্ট বা অঞ্চলে ভাগ করা জড়িত। বস্তু সনাক্তকরণের বিপরীতে, যা একটি বস্তুর চারপাশে একটি বাউন্ডিং বাক্স আঁকে, চিত্র বিভাজন চিত্রের প্রতিটি একক পিক্সেলের জন্য একটি নির্দিষ্ট শ্রেণী বা লেবেল বরাদ্দ করে। এটি চিত্রের বিষয়বস্তুগুলির আরও বিশদ এবং দানাদার বোঝার প্রদান করে, আপনাকে প্রতিটি বস্তুর সঠিক আকৃতি এবং সীমানা জানার অনুমতি দেয়।
উদাহরণস্বরূপ, একটি 'ব্যক্তি' একটি বাক্সে আছে তা জানার পরিবর্তে, আপনি ঠিক কোন পিক্সেল সেই ব্যক্তির অন্তর্গত তা জানতে পারবেন। এই টিউটোরিয়ালটি দেখায় কিভাবে একটি পূর্ব-প্রশিক্ষিত মেশিন লার্নিং মডেল ব্যবহার করে একটি অ্যান্ড্রয়েড ডিভাইসে রিয়েল-টাইম ইমেজ সেগমেন্টেশন করতে হয়।
LiteRT: অন-ডিভাইস ML-এর প্রান্ত ঠেলে দেওয়া
মোবাইল ডিভাইসে রিয়েল-টাইম, হাই-ফিডেলিটি সেগমেন্টেশন সক্ষম করে এমন একটি মূল প্রযুক্তি হল LiteRT । Google-এর পরবর্তী প্রজন্ম, TensorFlow Lite-এর জন্য উচ্চ-পারফরম্যান্স রানটাইম হিসাবে, LiteRT অন্তর্নিহিত হার্ডওয়্যার থেকে নিখুঁত সেরা পারফরম্যান্স পাওয়ার জন্য প্রকৌশলী।
এটি জিপিইউ (গ্রাফিক্স প্রসেসিং ইউনিট) এবং এনপিইউ (নিউরাল প্রসেসিং ইউনিট) এর মতো হার্ডওয়্যার এক্সিলারেটরের বুদ্ধিমান এবং অপ্টিমাইজড ব্যবহারের মাধ্যমে এটি অর্জন করে। সাধারণ-উদ্দেশ্য সিপিইউ থেকে এই বিশেষায়িত প্রসেসরগুলিতে সেগমেন্টেশন মডেলের তীব্র গণনামূলক কাজের চাপ অফলোড করার মাধ্যমে, LiteRT নাটকীয়ভাবে অনুমান সময় হ্রাস করে। এই ত্বরণই একটি লাইভ ক্যামেরা ফিডে জটিল মডেলগুলিকে মসৃণভাবে চালানো সম্ভব করে তোলে, যা আমরা সরাসরি আপনার ফোনে মেশিন লার্নিং দিয়ে অর্জন করতে পারি তার প্রান্তকে প্রসারিত করে। পারফরম্যান্সের এই স্তরটি ছাড়া, একটি ভাল ব্যবহারকারীর অভিজ্ঞতার জন্য রিয়েল-টাইম সেগমেন্টেশন খুব ধীর এবং বিচ্ছিন্ন হবে।
3. সেট আপ করুন
সংগ্রহস্থল ক্লোন করুন
প্রথমে, LiteRT এর জন্য সংগ্রহস্থল ক্লোন করুন:
git clone https://github.com/google-ai-edge/LiteRT.git
LiteRT/litert/samples/image_segmentation
হল আপনার প্রয়োজনীয় সমস্ত সংস্থান সহ ডিরেক্টরি। এই কোডল্যাবের জন্য, আপনার শুধুমাত্র kotlin_cpu_gpu/android_starter
প্রকল্পের প্রয়োজন হবে। আপনি যদি আটকে যান তাহলে আপনি সমাপ্ত প্রকল্প পর্যালোচনা করতে চাইতে পারেন: kotlin_cpu_gpu/android
ফাইল পাথ একটি নোট
এই টিউটোরিয়ালটি Linux/macOS ফরম্যাটে ফাইল পাথ নির্দিষ্ট করে। আপনি যদি উইন্ডোজে থাকেন তবে আপনাকে সেই অনুযায়ী পাথগুলি সামঞ্জস্য করতে হবে।
অ্যান্ড্রয়েড স্টুডিও প্রজেক্ট ভিউ এবং একটি স্ট্যান্ডার্ড ফাইল সিস্টেম ভিউয়ের মধ্যে পার্থক্য লক্ষ্য করাও গুরুত্বপূর্ণ। অ্যান্ড্রয়েড স্টুডিও প্রজেক্ট ভিউ হল আপনার প্রোজেক্টের ফাইলগুলির একটি কাঠামোগত উপস্থাপনা, যা Android ডেভেলপমেন্টের জন্য সংগঠিত। এই টিউটোরিয়ালের ফাইল পাথগুলি ফাইল সিস্টেম পাথগুলিকে নির্দেশ করে, Android স্টুডিও প্রকল্পের দৃশ্যের পাথগুলি নয়৷
স্টার্টার অ্যাপ আমদানি করুন
স্টার্টার অ্যাপটি অ্যান্ড্রয়েড স্টুডিওতে আমদানি করে শুরু করা যাক।
- অ্যান্ড্রয়েড স্টুডিও খুলুন এবং খুলুন নির্বাচন করুন।
-
kotlin_cpu_gpu/android_starter
ডিরেক্টরিতে নেভিগেট করুন এবং এটি খুলুন।
আপনার অ্যাপে সমস্ত নির্ভরতা উপলব্ধ রয়েছে তা নিশ্চিত করতে, আমদানি প্রক্রিয়া শেষ হয়ে গেলে আপনার গ্রেডল ফাইলগুলির সাথে আপনার প্রকল্প সিঙ্ক করা উচিত।
- অ্যান্ড্রয়েড স্টুডিও টুলবার থেকে গ্রেডল ফাইলগুলির সাথে সিঙ্ক প্রকল্প নির্বাচন করুন।
- অনুগ্রহ করে এই ধাপটি এড়িয়ে যাবেন না - যদি এটি কাজ না করে তবে বাকি টিউটোরিয়ালটি অর্থহীন হবে।
স্টার্টার অ্যাপটি চালান
এখন আপনি অ্যান্ড্রয়েড স্টুডিওতে প্রকল্পটি আমদানি করেছেন, আপনি প্রথমবারের জন্য অ্যাপটি চালানোর জন্য প্রস্তুত৷
আপনার কম্পিউটারে USB এর মাধ্যমে আপনার Android ডিভাইসটি সংযুক্ত করুন এবং Android Studio টুলবারে Run এ ক্লিক করুন।
অ্যাপটি আপনার ডিভাইসে চালু করা উচিত। আপনি একটি লাইভ ক্যামেরা ফিড দেখতে পাবেন, কিন্তু কোনো বিভাজন এখনও ঘটবে না। এই টিউটোরিয়ালে আপনি যে সমস্ত ফাইল সম্পাদনা করবেন LiteRT/litert/samples/image_segmentation/kotlin_cpu_gpu/android_starter/app/src/main/java/com/google/aiedge/examples/image_segmentation
ডিরেক্টরির অধীনে থাকবে (এখন আপনি জানেন কেন Android স্টুডিও এটি পুনর্গঠন করে) 😃
এছাড়াও আপনি ImageSegmentationHelper.kt
, MainViewModel.kt
, এবং view/SegmentationOverlay.kt
ফাইলগুলিতে TODO
মন্তব্যগুলি দেখতে পাবেন৷ নিম্নলিখিত ধাপে, আপনি এই TODO
গুলি পূরণ করে ইমেজ সেগমেন্টেশন কার্যকারিতা বাস্তবায়ন করবেন।
4. স্টার্টার অ্যাপ বুঝুন
স্টার্টার অ্যাপটিতে ইতিমধ্যে একটি মৌলিক UI এবং ক্যামেরা হ্যান্ডলিং লজিক রয়েছে। এখানে মূল ফাইলগুলির একটি দ্রুত ওভারভিউ:
-
app/src/main/java/com/google/aiedge/examples/image_segmentation/MainActivity.kt
: এটি অ্যাপ্লিকেশনটির প্রধান প্রবেশ বিন্দু। এটি জেটপ্যাক কম্পোজ ব্যবহার করে UI সেট আপ করে এবং ক্যামেরা অনুমতিগুলি পরিচালনা করে। -
app/src/main/java/com/google/aiedge/examples/image_segmentation/MainViewModel.kt
: এই ভিউমডেলটি UI অবস্থা পরিচালনা করে এবং চিত্র বিভাজন প্রক্রিয়াকে অর্কেস্ট্রেট করে। -
app/src/main/java/com/google/aiedge/examples/image_segmentation/ImageSegmentationHelper.kt
: এখানে আমরা ইমেজ সেগমেন্টেশনের জন্য মূল যুক্তি যোগ করব। এটি মডেল লোড করা, ক্যামেরা ফ্রেম প্রক্রিয়াকরণ এবং অনুমান চালানো পরিচালনা করবে। -
app/src/main/java/com/google/aiedge/examples/image_segmentation/view/CameraScreen.kt
: এই কম্পোজেবল ফাংশন ক্যামেরা প্রিভিউ এবং সেগমেন্টেশন ওভারলে প্রদর্শন করে। -
app/src/main/assets/selfie_multiclass.tflite
: এটি হল প্রাক-প্রশিক্ষিত টেনসরফ্লো লাইট ইমেজ সেগমেন্টেশন মডেল যা আমরা ব্যবহার করব।
5. LiteRT বোঝা এবং নির্ভরতা যোগ করা
এখন, স্টার্টার অ্যাপে ইমেজ সেগমেন্টেশন কার্যকারিতা যোগ করা যাক।
1. LiteRT নির্ভরতা যোগ করুন
প্রথমে, আপনাকে অবশ্যই আপনার প্রকল্পে LiteRT লাইব্রেরি যোগ করতে হবে। Google-এর অপ্টিমাইজ করা রানটাইম দিয়ে অন-ডিভাইস মেশিন লার্নিং সক্ষম করার জন্য এটি গুরুত্বপূর্ণ প্রথম ধাপ।
app/build.gradle.kts
ফাইলটি খুলুন এবং dependencies
ব্লকে নিম্নলিখিত লাইন যোগ করুন:
// LiteRT for on-device ML
implementation(libs.litert)
নির্ভরতা যোগ করার পরে, অ্যান্ড্রয়েড স্টুডিওর উপরের-ডান কোণায় প্রদর্শিত সিঙ্ক নাও বোতামটি ক্লিক করে গ্রেডল ফাইলগুলির সাথে আপনার প্রকল্পটি সিঙ্ক করুন৷
2. কী LiteRT APIs বুঝুন
ImageSegmentationHelper.kt
খুলুন
বাস্তবায়ন কোড লেখার আগে, আপনি যে LiteRT API-এর মূল উপাদানগুলি ব্যবহার করবেন তা বোঝা গুরুত্বপূর্ণ। নিশ্চিত করুন যে আপনি com.google.ai.edge.litert
প্যাকেজ থেকে আমদানি করছেন, ImageSegmentationHelper.kt
এর শীর্ষে নিম্নলিখিত আমদানি যোগ করুন:
import com.google.ai.edge.litert.Accelerator
import com.google.ai.edge.litert.CompiledModel
-
CompiledModel
: এটি আপনার TFLite মডেলের সাথে ইন্টারঅ্যাক্ট করার জন্য কেন্দ্রীয় শ্রেণী। এটি এমন একটি মডেলের প্রতিনিধিত্ব করে যা একটি নির্দিষ্ট হার্ডওয়্যার অ্যাক্সিলারেটরের (যেমন CPU বা GPU) জন্য পূর্ব-সংকলিত এবং অপ্টিমাইজ করা হয়েছে। এই প্রাক-সংকলন হল LiteRT-এর একটি মূল বৈশিষ্ট্য যা দ্রুত এবং আরও দক্ষ অনুমানের দিকে নিয়ে যায়। -
CompiledModel.Options
: আপনিCompiledModel
কনফিগার করতে এই বিল্ডার ক্লাস ব্যবহার করেন। আপনার মডেল চালানোর জন্য আপনি যে হার্ডওয়্যার অ্যাক্সিলারেটর ব্যবহার করতে চান তা উল্লেখ করা সবচেয়ে গুরুত্বপূর্ণ সেটিং। -
Accelerator
: এই এনাম আপনাকে অনুমানের জন্য হার্ডওয়্যার নির্বাচন করতে দেয়। স্টার্টার প্রকল্পটি ইতিমধ্যে এই বিকল্পগুলি পরিচালনা করার জন্য কনফিগার করা হয়েছে:-
Accelerator.CPU
: ডিভাইসের CPU-তে মডেল চালানোর জন্য। এটি সর্বজনীনভাবে সামঞ্জস্যপূর্ণ বিকল্প। -
Accelerator.GPU
: ডিভাইসের GPU-তে মডেল চালানোর জন্য। এটি প্রায়শই চিত্র-ভিত্তিক মডেলগুলির জন্য CPU-এর তুলনায় উল্লেখযোগ্যভাবে দ্রুত হয়।
-
- ইনপুট এবং আউটপুট বাফার (
TensorBuffer
) : LiteRT মডেল ইনপুট এবং আউটপুটগুলির জন্যTensorBuffer
ব্যবহার করে। এটি আপনাকে মেমরির উপর সূক্ষ্ম নিয়ন্ত্রণ দেয় এবং অপ্রয়োজনীয় ডেটা কপি এড়ায়। আপনিmodel.createInputBuffers()
এবংmodel.createOutputBuffers()
ব্যবহার করে আপনারCompiledModel
উদাহরণ থেকে সরাসরি এই বাফারগুলি পাবেন এবং তারপরে আপনার ইনপুট ডেটা তাদের কাছে লিখুন এবং তাদের থেকে ফলাফলগুলি পড়ুন। -
model.run()
: এটি এমন ফাংশন যা অনুমান নির্বাহ করে। আপনি এতে ইনপুট এবং আউটপুট বাফারগুলি পাস করেন এবং LiteRT নির্বাচিত হার্ডওয়্যার অ্যাক্সিলারেটরে মডেল চালানোর জটিল কাজ পরিচালনা করে।
6. প্রাথমিক ইমেজ সেগমেন্টেশন হেল্পার ইমপ্লিমেন্টেশন শেষ করুন
এখন কিছু কোড লেখার পালা। আপনি ImageSegmentationHelper.kt
এর প্রাথমিক বাস্তবায়ন সম্পন্ন করবেন। এটি LiteRT মডেল ধরে রাখার জন্য Segmenter
প্রাইভেট ক্লাস সেট আপ করা এবং এটিকে সঠিকভাবে প্রকাশ করার জন্য cleanup()
ফাংশন বাস্তবায়ন করা জড়িত।
-
Segmenter
ক্লাস এবংcleanup()
ফাংশন শেষ করুন :ImageSegmentationHelper.kt
ফাইলে, আপনিSegmenter
নামে একটি প্রাইভেট ক্লাসের জন্য একটি কঙ্কাল এবংcleanup()
নামে একটি ফাংশন পাবেন। প্রথমে, মডেল ধরে রাখার জন্য তার কন্সট্রাক্টরকে সংজ্ঞায়িত করে, ইনপুট/আউটপুট বাফারগুলির জন্য বৈশিষ্ট্য তৈরি করে এবং মডেলটি প্রকাশ করার জন্য একটিclose()
পদ্ধতি যোগ করেSegmenter
ক্লাসটি সম্পূর্ণ করুন। তারপর, এই নতুনclose()
পদ্ধতিতে কল করার জন্যcleanup()
ফাংশনটি প্রয়োগ করুন৷ বিদ্যমানSegmenter
ক্লাস এবংcleanup()
ফাংশনটি নিম্নলিখিতগুলির সাথে প্রতিস্থাপন করুন: (~ লাইন 83)private class Segmenter( // Add this argument private val model: CompiledModel, private val coloredLabels: List<ColoredLabel>, ) { // Add these private vals private val inputBuffers = model.createInputBuffers() private val outputBuffers = model.createOutputBuffers() fun cleanup() { // cleanup buffers inputBuffers.forEach { it.close() } outputBuffers.forEach { it.close() } // cleanup model model.close() } }
- toAccelerator পদ্ধতি সংজ্ঞায়িত করুন : এই পদ্ধতিটি এক্সিলারেটর মেনু থেকে আমদানি করা LiteRT মডিউলগুলির জন্য নির্দিষ্ট এক্সিলারেটর এনামগুলিতে সংজ্ঞায়িত এক্সিলারেটর এনামগুলিকে ম্যাপ করে (~ লাইন 225):
fun toAccelerator(acceleratorEnum: AcceleratorEnum): Accelerator { return when (acceleratorEnum) { AcceleratorEnum.CPU -> Accelerator.CPU AcceleratorEnum.GPU -> Accelerator.GPU } }
CompiledModel
শুরু করুন : এখনinitSegmenter
ফাংশন খুঁজুন। এখানেই আপনিCompiledModel
ইনস্ট্যান্স তৈরি করবেন এবং আপনার এখন-সংজ্ঞায়িতSegmenter
ক্লাসকে ইনস্ট্যান্ট করতে এটি ব্যবহার করবেন। এই কোডটি নির্দিষ্ট অ্যাক্সিলারেটর (CPU বা GPU) দিয়ে মডেল সেট আপ করে এবং অনুমানের জন্য প্রস্তুত করে।initSegmenter
এTODO
প্রতিস্থাপন করুন নিম্নলিখিত বাস্তবায়নের সাথে (Cmd/Ctrl+f 'initSegmenter` বা ~ লাইন 62):cleanup() try { withContext(singleThreadDispatcher) { val model = CompiledModel.create( context.assets, "selfie_multiclass.tflite", CompiledModel.Options(toAccelerator(acceleratorEnum)), null, ) segmenter = Segmenter(model, coloredLabels) Log.d(TAG, "Created an image segmenter") } } catch (e: Exception) { Log.i(TAG, "Create LiteRT from selfie_multiclass is failed: ${e.message}") _error.emit(e) }
7. সেগমেন্টেশন এবং প্রিপ্রসেসিং শুরু করুন
এখন আমাদের কাছে একটি মডেল আছে, আমাদের সেগমেন্টেশন প্রক্রিয়াটি ট্রিগার করতে হবে এবং মডেলের জন্য ইনপুট ডেটা প্রস্তুত করতে হবে।
ট্রিগার সেগমেন্টেশন
বিভাজন প্রক্রিয়া MainViewModel.kt
এ শুরু হয়, যা ক্যামেরা থেকে ফ্রেম গ্রহণ করে।
MainViewModel.kt
খুলুন
- ক্যামেরা ফ্রেম থেকে সেগমেন্টেশন ট্রিগার করুন :
MainViewModel
এsegment
ফাংশনগুলি আমাদের সেগমেন্টেশন টাস্কের এন্ট্রি পয়েন্ট। যখনই ক্যামেরা থেকে একটি নতুন ছবি পাওয়া যায় বা গ্যালারি থেকে নির্বাচিত হয় তখনই তাদের বলা হয়। এই ফাংশনগুলি তখন আমাদেরImageSegmentationHelper
এsegment
পদ্ধতিকে কল করে। উভয়segment
ফাংশনেTODO
গুলিকে নিম্নলিখিত (লাইন ~107) দিয়ে প্রতিস্থাপন করুন:// For ImageProxy (from CameraX) fun segment(imageProxy: ImageProxy) { segmentJob = viewModelScope.launch { imageSegmentationHelper.segment(imageProxy.toBitmap(), imageProxy.imageInfo.rotationDegrees) imageProxy.close() } } // For Bitmaps (from gallery) fun segment(bitmap: Bitmap, rotationDegrees: Int) { segmentJob = viewModelScope.launch { val argbBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true) imageSegmentationHelper.segment(argbBitmap, rotationDegrees) } }
ইমেজ প্রিপ্রসেস করুন
এখন ছবি প্রিপ্রসেসিং পরিচালনা করতে ImageSegmentationHelper.kt
এ ফিরে যাই।
ImageSegmentationHelper.kt
খুলুন
- পাবলিক
segment
ফাংশন বাস্তবায়ন করুন: এই ফাংশনটি একটি মোড়ক হিসাবে কাজ করে যাSegmenter
ক্লাসের মধ্যে প্রাইভেটsegment
ফাংশনকে কল করে।TODO
এর সাথে প্রতিস্থাপন করুন (~ লাইন 95):try { withContext(singleThreadDispatcher) { segmenter?.segment(bitmap, rotationDegrees)?.let { if (isActive) _segmentation.emit(it) } } } catch (e: Exception) { Log.i(TAG, "Image segment error occurred: ${e.message}") _error.emit(e) }
- প্রি-প্রসেসিং ইমপ্লিমেন্ট করুন :
Segmenter
ক্লাসের অভ্যন্তরে প্রাইভেটsegment
ফাংশন হল যেখানে আমরা মডেলের জন্য প্রস্তুত করার জন্য ইনপুট ইমেজে প্রয়োজনীয় রূপান্তরগুলি সম্পাদন করব। এর মধ্যে রয়েছে স্কেলিং, ঘূর্ণন এবং চিত্রকে স্বাভাবিক করা। এই ফাংশনটি তখন অনুমান সঞ্চালনের জন্য অন্য একটি ব্যক্তিগতsegment
ফাংশনকে কল করবে।segment(bitmap: Bitmap, ...)
ফাংশনেTODO
প্রতিস্থাপন করুন (~ লাইন 121):val totalStartTime = SystemClock.uptimeMillis() val rotation = -rotationDegrees / 90 val (h, w) = Pair(256, 256) // Preprocessing val preprocessStartTime = SystemClock.uptimeMillis() var image = bitmap.scale(w, h, true) image = rot90Clockwise(image, rotation) val inputFloatArray = normalize(image, 127.5f, 127.5f) Log.d(TAG, "Preprocessing time: ${SystemClock.uptimeMillis() - preprocessStartTime} ms") // Inference val inferenceStartTime = SystemClock.uptimeMillis() val segmentResult = segment(inputFloatArray) Log.d(TAG, "Inference time: ${SystemClock.uptimeMillis() - inferenceStartTime} ms") Log.d(TAG, "Total segmentation time: ${SystemClock.uptimeMillis() - totalStartTime} ms") return SegmentationResult(segmentResult, SystemClock.uptimeMillis() - inferenceStartTime)
8. LiteRT দিয়ে প্রাথমিক অনুমান
ইনপুট ডেটা প্রি-প্রসেস করার সাথে, আমরা এখন LiteRT ব্যবহার করে মূল অনুমান চালাতে পারি।
ImageSegmentationHelper.kt
খুলুন
- মডেল এক্সিকিউশন বাস্তবায়ন করুন : প্রাইভেট
segment(inputFloatArray: FloatArray)
ফাংশন হল যেখানে আমরা সরাসরি LiteRTrun()
পদ্ধতির সাথে যোগাযোগ করি। আমরা ইনপুট বাফারে আমাদের প্রাক-প্রসেসড ডেটা লিখি, মডেলটি চালাই এবং আউটপুট বাফার থেকে ফলাফল পড়ি। এই ফাংশনেTODO
প্রতিস্থাপন করুন (~ লাইন 188):val (h, w, c) = Triple(256, 256, 6) // MODEL EXECUTION PHASE val modelExecStartTime = SystemClock.uptimeMillis() // Write input data - measure time val bufferWriteStartTime = SystemClock.uptimeMillis() inputBuffers[0].writeFloat(inputFloatArray) val bufferWriteTime = SystemClock.uptimeMillis() - bufferWriteStartTime Log.d(TAG, "Buffer write time: $bufferWriteTime ms") // Optional tensor inspection logTensorStats("Input tensor", inputFloatArray) // Run model inference - measure time val modelRunStartTime = SystemClock.uptimeMillis() model.run(inputBuffers, outputBuffers) val modelRunTime = SystemClock.uptimeMillis() - modelRunStartTime Log.d(TAG, "Model.run() time: $modelRunTime ms") // Read output data - measure time val bufferReadStartTime = SystemClock.uptimeMillis() val outputFloatArray = outputBuffers[0].readFloat() val outputBuffer = FloatBuffer.wrap(outputFloatArray) val bufferReadTime = SystemClock.uptimeMillis() - bufferReadStartTime Log.d(TAG, "Buffer read time: $bufferReadTime ms") val modelExecTime = SystemClock.uptimeMillis() - modelExecStartTime Log.d(TAG, "Total model execution time: $modelExecTime ms") // Optional tensor inspection logTensorStats("Output tensor", outputFloatArray) // POSTPROCESSING PHASE val postprocessStartTime = SystemClock.uptimeMillis() // Process mask from model output val inferenceData = InferenceData(width = w, height = h, channels = c, buffer = outputBuffer) val mask = processImage(inferenceData) val postprocessTime = SystemClock.uptimeMillis() - postprocessStartTime Log.d(TAG, "Postprocessing time (mask creation): $postprocessTime ms") return Segmentation( listOf(Mask(mask, inferenceData.width, inferenceData.height)), coloredLabels, )
9. পোস্ট-প্রসেসিং এবং ওভারলে প্রদর্শন করা
অনুমান চালানোর পরে, আমরা মডেল থেকে একটি কাঁচা আউটপুট পাই। একটি ভিজ্যুয়াল সেগমেন্টেশন মাস্ক তৈরি করতে আমাদের এই আউটপুটটি প্রক্রিয়া করতে হবে এবং তারপর এটি স্ক্রিনে প্রদর্শন করতে হবে।
ImageSegmentationHelper.kt
খুলুন
- আউটপুট প্রসেসিং বাস্তবায়ন করুন :
processImage
ফাংশন মডেল থেকে কাঁচা ফ্লোটিং-পয়েন্ট আউটপুটকেByteBuffer
রূপান্তর করে যা সেগমেন্টেশন মাস্ককে প্রতিনিধিত্ব করে। এটি প্রতিটি পিক্সেলের জন্য সর্বোচ্চ সম্ভাবনা সহ ক্লাস খুঁজে বের করে এটি করে। এরTODO
এর সাথে প্রতিস্থাপন করুন (~ লাইন 238):val mask = ByteBuffer.allocateDirect(inferenceData.width * inferenceData.height) for (i in 0 until inferenceData.height) { for (j in 0 until inferenceData.width) { val offset = inferenceData.channels * (i * inferenceData.width + j) var maxIndex = 0 var maxValue = inferenceData.buffer.get(offset) for (index in 1 until inferenceData.channels) { if (inferenceData.buffer.get(offset + index) > maxValue) { maxValue = inferenceData.buffer.get(offset + index) maxIndex = index } } mask.put(i * inferenceData.width + j, maxIndex.toByte()) } } return mask
MainViewModel.kt
খুলুন
- সেগমেন্টেশন ফলাফল সংগ্রহ করুন এবং প্রক্রিয়া করুন : এখন আমরা
ImageSegmentationHelper
থেকে বিভাজন ফলাফল প্রক্রিয়া করতেMainViewModel
এ ফিরে যাই।segmentationUiShareFlow
SegmentationResult
সংগ্রহ করে, মুখোশটিকে একটি রঙিনBitmap
রূপান্তর করে এবং এটি UI-তে প্রদান করে।segmentationUiShareFlow
প্রপার্টিতেTODO
প্রতিস্থাপন করুন (~ লাইন 63) - কোডটি আগে থেকেই প্রতিস্থাপন করবেন না, শুধু বডিটি পূরণ করুন:viewModelScope.launch { imageSegmentationHelper.segmentation .filter { it.segmentation.masks.isNotEmpty() } .map { val segmentation = it.segmentation val mask = segmentation.masks[0] val maskArray = mask.data val width = mask.width val height = mask.height val pixelSize = width * height val pixels = IntArray(pixelSize) val colorLabels = segmentation.coloredLabels.mapIndexed { index, coloredLabel -> ColorLabel(index, coloredLabel.label, coloredLabel.argb) } // Set color for pixels for (i in 0 until pixelSize) { val colorLabel = colorLabels[maskArray[i].toInt()] val color = colorLabel.getColor() pixels[i] = color } // Get image info val overlayInfo = OverlayInfo(pixels = pixels, width = width, height = height) val inferenceTime = it.inferenceTime Pair(overlayInfo, inferenceTime) } .collect { flow.emit(it) } }
view/SegmentationOverlay.kt
খুলুন
ব্যবহারকারী যখন সামনের দিকের ক্যামেরায় ফ্লিপ করেন তখন চূড়ান্ত অংশটি হল সেগমেন্টেশন ওভারলেকে সঠিকভাবে অভিমুখী করা। ক্যামেরা ফিড স্বাভাবিকভাবেই সামনের ক্যামেরার জন্য মিরর করা হয়, তাই আমাদের ওভারলে Bitmap
একই অনুভূমিক ফ্লিপ প্রয়োগ করতে হবে যাতে এটি ক্যামেরার পূর্বরূপের সাথে সঠিকভাবে সারিবদ্ধ হয়।
- ওভারলে ওরিয়েন্টেশন পরিচালনা করুন :
SegmentationOverlay.kt
ফাইলেTODO
খুঁজুন এবং নিম্নলিখিত কোড দিয়ে প্রতিস্থাপন করুন। এই কোডটি সামনের দিকের ক্যামেরা সক্রিয় কিনা তা পরীক্ষা করে এবং যদি তাই হয়,Canvas
আঁকার আগে ওভারলেBitmap
একটি অনুভূমিক ফ্লিপ প্রয়োগ করে৷ (~ লাইন 42):val orientedBitmap = if (lensFacing == CameraSelector.LENS_FACING_FRONT) { // Create a matrix for horizontal flipping val matrix = Matrix().apply { preScale(-1f, 1f) } Bitmap.createBitmap(image, 0, 0, image.width, image.height, matrix, false).also { image.recycle() } } else { image }
10. চালান এবং চূড়ান্ত অ্যাপ ব্যবহার করুন
আপনি এখন সমস্ত প্রয়োজনীয় কোড পরিবর্তন সম্পন্ন করেছেন। এটি অ্যাপটি চালানোর এবং আপনার কাজকে কর্মে দেখার সময়!
- অ্যাপটি চালান : আপনার অ্যান্ড্রয়েড ডিভাইস কানেক্ট করুন এবং অ্যান্ড্রয়েড স্টুডিও টুলবারে রান ক্লিক করুন।
- বৈশিষ্ট্যগুলি পরীক্ষা করুন : একবার অ্যাপটি চালু হলে, আপনি একটি রঙিন সেগমেন্টেশন ওভারলে সহ লাইভ ক্যামেরা ফিড দেখতে পাবেন।
- ক্যামেরা স্যুইচ করুন : সামনে এবং পিছনের ক্যামেরাগুলির মধ্যে স্যুইচ করতে উপরের দিকে ক্যামেরা ফ্লিপ আইকনে আলতো চাপুন। লক্ষ্য করুন কিভাবে ওভারলে সঠিকভাবে নিজেকে ওরিয়েন্ট করে।
- অ্যাক্সিলারেটর পরিবর্তন করুন : হার্ডওয়্যার অ্যাক্সিলারেটর স্যুইচ করতে নীচে "CPU" বা "GPU" বোতামে আলতো চাপুন৷ স্ক্রিনের নীচে প্রদর্শিত অনুমান সময়ের পরিবর্তনটি পর্যবেক্ষণ করুন। GPU উল্লেখযোগ্যভাবে দ্রুত হওয়া উচিত।
- একটি গ্যালারী চিত্র ব্যবহার করুন : আপনার ডিভাইসের ফটো গ্যালারি থেকে একটি চিত্র নির্বাচন করতে শীর্ষে "গ্যালারী" ট্যাবে আলতো চাপুন৷ অ্যাপটি নির্বাচিত স্ট্যাটিক ইমেজে সেগমেন্টেশন চালাবে।
আপনার কাছে এখন LiteRT দ্বারা চালিত একটি সম্পূর্ণ কার্যকরী, রিয়েল-টাইম ইমেজ সেগমেন্টেশন অ্যাপ রয়েছে!
11. উন্নত (ঐচ্ছিক): NPU ব্যবহার করে
এই সংগ্রহস্থলটিতে অ্যাপটির একটি সংস্করণও রয়েছে যা নিউরাল প্রসেসিং ইউনিট (NPUs) এর জন্য অপ্টিমাইজ করা হয়েছে। এনপিইউ সংস্করণটি একটি সামঞ্জস্যপূর্ণ এনপিইউ আছে এমন ডিভাইসগুলিতে একটি উল্লেখযোগ্য কর্মক্ষমতা বৃদ্ধি করতে পারে।
NPU সংস্করণ চেষ্টা করতে, Android স্টুডিওতে kotlin_npu/android
প্রকল্পটি খুলুন। কোডটি CPU/GPU সংস্করণের অনুরূপ এবং NPU প্রতিনিধি ব্যবহার করার জন্য কনফিগার করা হয়েছে।
NPU প্রতিনিধি ব্যবহার করার জন্য, আপনাকে প্রাথমিক অ্যাক্সেস প্রোগ্রামে নথিভুক্ত করতে হবে।
12. অভিনন্দন!
আপনি সফলভাবে একটি Android অ্যাপ তৈরি করেছেন যা LiteRT ব্যবহার করে রিয়েল-টাইম ইমেজ সেগমেন্টেশন সম্পাদন করে। আপনি শিখেছেন কিভাবে:
- একটি Android অ্যাপে LiteRT রানটাইম সংহত করুন।
- একটি TFLite ইমেজ সেগমেন্টেশন মডেল লোড করুন এবং চালান।
- মডেলের ইনপুট প্রিপ্রসেস করুন।
- একটি সেগমেন্টেশন মাস্ক তৈরি করতে মডেলের আউটপুট প্রক্রিয়া করুন।
- একটি রিয়েল-টাইম ক্যামেরা অ্যাপের জন্য CameraX ব্যবহার করুন।
পরবর্তী পদক্ষেপ
- একটি ভিন্ন চিত্র বিভাজন মডেল চেষ্টা করুন.
- বিভিন্ন LiteRT প্রতিনিধিদের (CPU, GPU, NPU) সাথে পরীক্ষা করুন।