MediaPipe টাস্ক সহ একটি হাতে লেখা ডিজিট ক্লাসিফায়ার অ্যান্ড্রয়েড অ্যাপ তৈরি করুন

১. ভূমিকা

মিডিয়াপাইপ কী?

মিডিয়াপাইপ সলিউশনস আপনাকে আপনার অ্যাপে মেশিন-লার্নিং (এমএল) সলিউশন প্রয়োগ করার সুযোগ দেয়। এটি আগে থেকে তৈরি প্রসেসিং পাইপলাইন কনফিগার করার জন্য একটি ফ্রেমওয়ার্ক প্রদান করে, যা ব্যবহারকারীদের কাছে তাৎক্ষণিক, আকর্ষণীয় এবং দরকারি আউটপুট পৌঁছে দেয়। এমনকি আপনি মিডিয়াপাইপ মডেল মেকার ব্যবহার করে ডিফল্ট মডেলগুলো আপডেট করার জন্য এই সলিউশনগুলোকে কাস্টমাইজও করতে পারেন।

ইমেজ ক্লাসিফিকেশন হলো মিডিয়াপাইপ সলিউশনস-এর দেওয়া বিভিন্ন এমএল ভিশন টাস্কগুলোর মধ্যে একটি। মিডিয়াপাইপ টাস্কস অ্যান্ড্রয়েড, আইওএস, পাইথন (রাস্পবেরি পাই সহ!), এবং ওয়েবের জন্য উপলব্ধ।

এই কোডল্যাবে, আপনি এমন একটি অ্যান্ড্রয়েড অ্যাপ দিয়ে শুরু করবেন যা আপনাকে স্ক্রিনে সংখ্যাসূচক অঙ্ক আঁকতে দেবে, তারপর আপনি এমন একটি কার্যকারিতা যোগ করবেন যা সেই আঁকা অঙ্কগুলোকে ০ থেকে ৯ পর্যন্ত একটি একক মান হিসাবে শ্রেণীবদ্ধ করবে।

আপনি যা শিখবেন

  • MediaPipe Tasks ব্যবহার করে কীভাবে একটি অ্যান্ড্রয়েড অ্যাপে ইমেজ ক্লাসিফিকেশন টাস্ক অন্তর্ভুক্ত করা যায়।

আপনার যা যা লাগবে

  • অ্যান্ড্রয়েড স্টুডিও -র একটি ইনস্টল করা সংস্করণ (এই কোডল্যাবটি অ্যান্ড্রয়েড স্টুডিও জিরাফ দিয়ে লেখা ও পরীক্ষা করা হয়েছে)।
  • অ্যাপটি চালানোর জন্য একটি অ্যান্ড্রয়েড ডিভাইস বা এমুলেটর।
  • অ্যান্ড্রয়েড ডেভেলপমেন্ট সম্পর্কে প্রাথমিক জ্ঞান (এটি "হ্যালো ওয়ার্ল্ড" নয়, তবে খুব বেশি দূরেও নয়!)।

২. অ্যান্ড্রয়েড অ্যাপে মিডিয়াপাইপ টাস্ক যোগ করুন

অ্যান্ড্রয়েড স্টার্টার অ্যাপটি ডাউনলোড করুন

এই কোডল্যাবটি একটি পূর্ব-নির্মিত নমুনা দিয়ে শুরু হবে যা আপনাকে স্ক্রিনে আঁকতে দেবে। আপনি সেই শুরুর অ্যাপটি অফিসিয়াল MediaPipe স্যাম্পলস রিপো-তে এখানে খুঁজে পাবেন। রিপোটি ক্লোন করুন অথবা Code > Download ZIP-এ ক্লিক করে জিপফাইলটি ডাউনলোড করুন।

অ্যান্ড্রয়েড স্টুডিওতে অ্যাপটি ইম্পোর্ট করুন

  1. অ্যান্ড্রয়েড স্টুডিও খুলুন।
  2. "Welcome to Android Studio" স্ক্রিন থেকে, উপরের ডান কোণায় থাকা "Open" নির্বাচন করুন।

a0b5b070b802e4ea.png

  1. আপনি যেখানে রিপোজিটরিটি ক্লোন বা ডাউনলোড করেছেন সেখানে যান এবং codelabs/digitclassifier/android/start ডিরেক্টরিটি খুলুন।
  2. সবুজ রান অ্যারোতে (Run arrow) ক্লিক করে যাচাই করুন যে সবকিছু সঠিকভাবে খুলেছে কিনা। 7e15a9c9e1620fe7.png অ্যান্ড্রয়েড স্টুডিওর উপরের ডানদিকে )
  3. আপনি দেখবেন অ্যাপটি একটি কালো স্ক্রিনসহ খুলবে, যেখানে আপনি আঁকতে পারবেন এবং স্ক্রিনটি রিসেট করার জন্য একটি 'ক্লিয়ার' বাটনও থাকবে। যদিও আপনি ওই স্ক্রিনে আঁকতে পারবেন, এটি আর তেমন কিছু করে না, তাই আমরা এখন থেকেই এটি ঠিক করা শুরু করব।

11a0f6fe021fdc92.jpeg

মডেল

আপনি যখন প্রথমবার অ্যাপটি চালাবেন, তখন হয়তো লক্ষ্য করবেন যে mnist.tflite নামের একটি ফাইল ডাউনলোড হয়ে আপনার অ্যাপের অ্যাসেটস ডিরেক্টরিতে জমা হয়েছে। সরলতার জন্য, আমরা ইতোমধ্যেই MNIST নামক একটি পরিচিত মডেল, যা সংখ্যা শ্রেণীবদ্ধ করে, নিয়ে এসেছি এবং প্রজেক্টের download_models.gradle স্ক্রিপ্ট ব্যবহার করে এটিকে অ্যাপে যুক্ত করেছি। আপনি যদি নিজের কাস্টম মডেল, যেমন হাতে লেখা অক্ষরের জন্য কোনো মডেল, তৈরি করার সিদ্ধান্ত নেন, তাহলে আপনাকে download_models.gradle ফাইলটি সরিয়ে ফেলতে হবে, আপনার অ্যাপ লেভেলের build.gradle ফাইল থেকে এর রেফারেন্সটি মুছে ফেলতে হবে এবং কোডের পরবর্তী অংশে (বিশেষ করে DigitClassifierHelper.kt ফাইলে) মডেলটির নাম পরিবর্তন করতে হবে।

build.gradle আপডেট করুন

MediaPipe Tasks ব্যবহার শুরু করার আগে, আপনাকে লাইব্রেরিটি ইম্পোর্ট করতে হবে।

  1. আপনার অ্যাপ মডিউলে অবস্থিত build.gradle ফাইলটি খুলুন, তারপর নিচে স্ক্রল করে dependencies ব্লক পর্যন্ত যান।
  2. আপনি ওই ব্লকটির একদম নিচে একটি কমেন্ট দেখতে পাবেন, যেখানে লেখা থাকবে // ধাপ ১ ডিপেন্ডেন্সি ইম্পোর্ট
  3. ঐ লাইনটি নিম্নলিখিত বাস্তবায়ন দ্বারা প্রতিস্থাপন করুন।
implementation("com.google.mediapipe:tasks-vision:latest.release")
  1. এই ডিপেন্ডেন্সিটি ডাউনলোড করতে অ্যান্ড্রয়েড স্টুডিও-র উপরের ব্যানারে প্রদর্শিত ' সিঙ্ক নাও' বোতামটিতে ক্লিক করুন।

৩. একটি MediaPipe Tasks ডিজিট ক্লাসিফায়ার হেল্পার তৈরি করুন

পরবর্তী ধাপে আপনাকে এমন একটি ক্লাস পূরণ করতে হবে যা আপনার মেশিন লার্নিং ক্লাসিফিকেশনের মূল কাজটি করবে। DigitClassifierHelper.kt ফাইলটি খুলুন এবং চলুন শুরু করা যাক!

  1. ক্লাসের শীর্ষে থাকা সেই মন্তব্যটি খুঁজুন যেখানে লেখা আছে // ধাপ ২ লিসেনার তৈরি করুন
  2. ঐ লাইনটি নিচের কোড দিয়ে প্রতিস্থাপন করুন। এটি একটি লিসেনার তৈরি করবে যা DigitClassifierHelper ক্লাস থেকে ফলাফল যেখানে শোনা হচ্ছে সেখানে ফেরত পাঠাতে ব্যবহৃত হবে (এই ক্ষেত্রে এটি হবে আপনার DigitCanvasFragment ক্লাস, কিন্তু আমরা শীঘ্রই সেখানে পৌঁছাব)।
// STEP 2 Create listener

interface DigitClassifierListener {
    fun onError(error: String)
    fun onResults(
        results: ImageClassifierResult,
        inferenceTime: Long
    )
}
  1. এছাড়াও আপনাকে ক্লাসের জন্য একটি ঐচ্ছিক প্যারামিটার হিসেবে একটি DigitClassifierListener গ্রহণ করতে হবে:
class DigitClassifierHelper(
    val context: Context,
    val digitClassifierListener: DigitClassifierListener?
) {
  1. // STEP 3 define classifier লেখা লাইনটিতে গিয়ে, এই অ্যাপের জন্য ব্যবহৃত ImageClassifier-এর একটি প্লেসহোল্ডার তৈরি করতে নিম্নলিখিত লাইনটি যোগ করুন:

// ধাপ ৩ ক্লাসিফায়ার নির্ধারণ করুন

private var digitClassifier: ImageClassifier? = null
  1. যেখানে // STEP 4 set up classifier : এই কমেন্টটি দেখবেন, সেখানে নিম্নলিখিত ফাংশনটি যোগ করুন।
// STEP 4 set up classifier
private fun setupDigitClassifier() {

    val baseOptionsBuilder = BaseOptions.builder()
        .setModelAssetPath("mnist.tflite")

    // Describe additional options
    val optionsBuilder = ImageClassifierOptions.builder()
        .setRunningMode(RunningMode.IMAGE)
        .setBaseOptions(baseOptionsBuilder.build())

    try {
        digitClassifier =
            ImageClassifier.createFromOptions(
                context,
                optionsBuilder.build()
            )
    } catch (e: IllegalStateException) {
        digitClassifierListener?.onError(
            "Image classifier failed to initialize. See error logs for " +
                    "details"
        )
        Log.e(TAG, "MediaPipe failed to load model with error: " + e.message)
    }
}

উপরের অংশে কয়েকটি বিষয় ঘটছে, তাই আসলে কী হচ্ছে তা ভালোভাবে বোঝার জন্য চলুন এর ছোট ছোট অংশগুলো দেখি।

val baseOptionsBuilder = BaseOptions.builder()
    .setModelAssetPath("mnist.tflite")

// Describe additional options
val optionsBuilder = ImageClassifierOptions.builder()
    .setRunningMode(RunningMode.IMAGE)
    .setBaseOptions(baseOptionsBuilder.build())

এই ব্লকটি ImageClassifier দ্বারা ব্যবহৃত প্যারামিটারগুলো নির্ধারণ করবে। এর মধ্যে রয়েছে আপনার অ্যাপের ( mnist.tflite ) BaseOptions-এর অধীনে থাকা মডেল এবং ImageClassifierOptions-এর অধীনে থাকা RunningMode, যা এই ক্ষেত্রে IMAGE, তবে VIDEO এবং LIVE_STREAM হলো অতিরিক্ত উপলব্ধ বিকল্প। অন্যান্য উপলব্ধ প্যারামিটারগুলো হলো MaxResults, যা মডেলকে সর্বোচ্চ সংখ্যক ফলাফল ফেরত দেওয়ার ক্ষেত্রে সীমাবদ্ধ করে, এবং ScoreThreshold, যা কোনো ফলাফল ফেরত দেওয়ার আগে সেটির ওপর মডেলের ন্যূনতম আস্থার মাত্রা নির্ধারণ করে।

try {
    digitClassifier =
        ImageClassifier.createFromOptions(
            context,
            optionsBuilder.build()
        )
} catch (e: IllegalStateException) {
    digitClassifierListener?.onError(
        "Image classifier failed to initialize. See error logs for " +
                "details"
    )
    Log.e(TAG, "MediaPipe failed to load model with error: " + e.message)
}

আপনার কনফিগারেশন অপশনগুলো তৈরি করার পর, আপনি একটি কনটেক্সট এবং অপশনগুলো পাস করে আপনার নতুন ImageClassifier তৈরি করতে পারেন। এই ইনিশিয়ালাইজেশন প্রক্রিয়ায় কোনো সমস্যা হলে, আপনার DigitClassifierListener-এর মাধ্যমে একটি এরর রিটার্ন করা হবে।

  1. যেহেতু আমরা ImageClassifier-কে ব্যবহারের আগে ইনিশিয়ালাইজ করতে চাইব, তাই আপনি setupDigitClassifier() কল করার জন্য একটি init ব্লক যোগ করতে পারেন।
init {
    setupDigitClassifier()
}
  1. অবশেষে, // STEP 5 create classify function লেখা কমেন্টটি পর্যন্ত স্ক্রল করে নিচে যান এবং নিচের কোডটি যোগ করুন। এই ফাংশনটি একটি Bitmap গ্রহণ করবে, যা এই ক্ষেত্রে আঁকা সংখ্যাটি, সেটিকে একটি MediaPipe Image অবজেক্টে (MPImage) রূপান্তর করবে এবং তারপর ImageClassifier ব্যবহার করে সেই ছবিটিকে ক্লাসিফাই করবে। এছাড়াও, ইনফারেন্স হতে কত সময় লাগছে তা রেকর্ড করার পর, DigitClassifierListener-এর মাধ্যমে সেই ফলাফলগুলো ফেরত দেবে।
// STEP 5 create classify function
fun classify(image: Bitmap) {
    if (digitClassifier == null) {
        setupDigitClassifier()
    }

    // Convert the input Bitmap object to an MPImage object to run inference.
    // Rotating shouldn't be necessary because the text is being extracted from
    // a view that should always be correctly positioned.
    val mpImage = BitmapImageBuilder(image).build()

    // Inference time is the difference between the system time at the start and finish of the
    // process
    val startTime = SystemClock.uptimeMillis()

    // Run image classification using MediaPipe Image Classifier API
    digitClassifier?.classify(mpImage)?.also { classificationResults ->
        val inferenceTimeMs = SystemClock.uptimeMillis() - startTime
        digitClassifierListener?.onResults(classificationResults, inferenceTimeMs)
    }
}

এবং হেল্পার ফাইলের জন্য এটুকুই! পরবর্তী বিভাগে আপনি আপনার তোলা নম্বরগুলোকে শ্রেণীবদ্ধ করা শুরু করার জন্য চূড়ান্ত ধাপগুলো পূরণ করবেন।

৪. মিডিয়াপাইপ টাস্ক ব্যবহার করে ইনফারেন্স চালান

আপনি অ্যান্ড্রয়েড স্টুডিওতে DigitCanvasFragment ক্লাসটি খোলার মাধ্যমে এই অংশটি শুরু করতে পারেন, যেখানে সমস্ত কাজ সম্পন্ন হবে।

  1. এই ফাইলের একদম শেষে আপনি // STEP 6 Set up listener লেখা একটি কমেন্ট দেখতে পাবেন। এখানেই আপনি লিসেনারের সাথে যুক্ত onResults() এবং onError() ফাংশনগুলো যোগ করবেন।
// STEP 6 Set up listener
override fun onError(error: String) {
    activity?.runOnUiThread {
        Toast.makeText(requireActivity(), error, Toast.LENGTH_SHORT).show()
        fragmentDigitCanvasBinding.tvResults.text = ""
    }
}

override fun onResults(
    results: ImageClassifierResult,
    inferenceTime: Long
) {
    activity?.runOnUiThread {
        fragmentDigitCanvasBinding.tvResults.text = results
            .classificationResult()
            .classifications().get(0)
            .categories().get(0)
            .categoryName()

        fragmentDigitCanvasBinding.tvInferenceTime.text = requireActivity()
            .getString(R.string.inference_time, inferenceTime.toString())
    }
}

onResults() বিশেষভাবে গুরুত্বপূর্ণ, কারণ এটি ImageClassifier থেকে প্রাপ্ত ফলাফল প্রদর্শন করবে। যেহেতু এই কলব্যাকটি একটি ব্যাকগ্রাউন্ড থ্রেড থেকে ট্রিগার হয়, তাই আপনাকে আপনার UI আপডেটগুলোও অ্যান্ড্রয়েডের UI থ্রেডে চালাতে হবে।

  1. যেহেতু আপনি উপরের ধাপে একটি ইন্টারফেস থেকে নতুন ফাংশন যোগ করছেন, তাই আপনাকে ক্লাসের শীর্ষে ইমপ্লিমেন্টেশন ডিক্লারেশনটিও যোগ করতে হবে।
class DigitCanvasFragment : Fragment(), DigitClassifierHelper.DigitClassifierListener
  1. ক্লাসের উপরের দিকে আপনি // STEP 7a Initialize classifier লেখা একটি কমেন্ট দেখতে পাবেন। এখানেই আপনি DigitClassifierHelper-এর ডিক্লারেশনটি রাখবেন।
// STEP 7a Initialize classifier.
private lateinit var digitClassifierHelper: DigitClassifierHelper
  1. এবার আসা যাক ধাপ ৭খ: ক্লাসিফায়ার ইনিশিয়ালাইজ করা-তে, আপনি onViewCreated() ফাংশনের ভেতরে digitClassifierHelper-কে ইনিশিয়ালাইজ করতে পারেন।
// STEP 7b Initialize classifier
// Initialize the digit classifier helper, which does all of the
// ML work. This uses the default values for the classifier.
digitClassifierHelper = DigitClassifierHelper(
    context = requireContext(), digitClassifierListener = this
)
  1. শেষ ধাপগুলোর জন্য, // STEP 8a *: classify* এই কমেন্টটি খুঁজুন এবং কিছুক্ষণ পরেই যোগ করা একটি নতুন ফাংশনকে কল করার জন্য নিম্নলিখিত কোডটি যোগ করুন। এই কোড ব্লকটি অ্যাপের ড্রয়িং এরিয়া থেকে আপনার আঙুল তুলে নিলেই ক্লাসিফিকেশন প্রক্রিয়াটি চালু করবে।
// STEP 8a: classify
classifyDrawing()
  1. অবশেষে, নতুন classifyDrawing() ফাংশনটি যোগ করতে // STEP 8b classify কমেন্টটি খুঁজুন। এটি ক্যানভাস থেকে একটি বিটম্যাপ বের করে, তারপর ক্লাসিফিকেশন করার জন্য সেটিকে DigitClassifierHelper-এর কাছে পাঠাবে এবং onResults() ইন্টারফেস ফাংশনে ফলাফল গ্রহণ করবে।
// STEP 8b classify
private fun classifyDrawing() {
    val bitmap = fragmentDigitCanvasBinding.digitCanvas.getBitmap()
    digitClassifierHelper.classify(bitmap)
}

৫. অ্যাপটি স্থাপন ও পরীক্ষা করুন

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

  1. রান-এ ক্লিক করুন ( 7e15a9c9e1620fe7.png অ্যাপটি চালানোর জন্য অ্যান্ড্রয়েড স্টুডিও টুলবারে থাকা আইকনটি চাপুন।
  2. ড্রয়িং প্যাডে যেকোনো একটি সংখ্যা আঁকুন এবং দেখুন অ্যাপটি সেটি চিনতে পারে কি না। মডেলটির মতে আঁকা সংখ্যাটির পাশাপাশি, সেই সংখ্যাটি অনুমান করতে কত সময় লেগেছে, তাও অ্যাপটিতে দেখানো উচিত।

7f37187f8f919638.gif

৬. অভিনন্দন!

আপনি পেরেছেন! এই কোডল্যাবে আপনি শিখেছেন কীভাবে একটি অ্যান্ড্রয়েড অ্যাপে ইমেজ ক্লাসিফিকেশন যুক্ত করতে হয়, এবং বিশেষভাবে MNIST মডেল ব্যবহার করে কীভাবে হাতে আঁকা সংখ্যাগুলোকে শ্রেণীবদ্ধ করতে হয়।

পরবর্তী পদক্ষেপ

  • এখন যেহেতু আপনি সংখ্যা শ্রেণীবদ্ধ করতে পারেন, আপনি হয়তো আঁকা অক্ষর, বা প্রাণী, বা আরও অগণিত অন্যান্য জিনিস শ্রেণীবদ্ধ করার জন্য আপনার নিজস্ব মডেলকে প্রশিক্ষণ দিতে চাইতে পারেন। MediaPipe Model Maker ব্যবহার করে একটি নতুন ইমেজ ক্লাসিফিকেশন মডেল প্রশিক্ষণের ডকুমেন্টেশন আপনি developers.google.com/mediapipe পেজে খুঁজে পেতে পারেন।
  • অ্যান্ড্রয়েডের জন্য উপলব্ধ অন্যান্য মিডিয়াপাইপ টাস্কগুলো সম্পর্কে জানুন, যার মধ্যে রয়েছে ফেস ল্যান্ডমার্ক ডিটেকশন, জেসচার রিকগনিশন এবং অডিও ক্লাসিফিকেশন।

তোমার তৈরি করা সব চমৎকার জিনিসগুলো দেখার জন্য আমরা অধীর আগ্রহে অপেক্ষা করছি!