MediaPipe Tasks ile el yazısı bir rakam sınıflandırıcı Android uygulaması geliştirin

1. Giriş

MediaPipe nedir?

MediaPipe Çözümleri, makine öğrenimi (ML) çözümlerini uygulamalarınıza uygulamanıza olanak tanır. Kullanıcılara anında, ilgi çekici ve faydalı çıktılar sağlayan önceden oluşturulmuş işleme işlem hatlarını yapılandırmak için bir çerçeve sunar. Hatta varsayılan modelleri güncellemek için bu çözümleri MediaPipe Model Maker ile özelleştirebilirsiniz.

Görüntü sınıflandırma, MediaPipe Çözümleri'nin sunduğu çeşitli makine öğrenimi görsel görevlerinden biridir. MediaPipe Görevleri; Android, iOS, Python (Raspberry Pi dahil) ve web'de kullanılabilir.

Bu Codelab'de, ekranda sayısal rakamlar çizmenize olanak tanıyan bir Android uygulamasıyla başlayacak, ardından çizilen bu rakamları 0 ile 9 arasında tek bir değer olarak sınıflandıran bir işlev ekleyeceksiniz.

Neler öğreneceksiniz?

İhtiyacınız olanlar

  • Android Studio'nun yüklü bir sürümü (bu codelab, Android Studio Giraffe ile yazılmış ve test edilmiştir).
  • Uygulamayı çalıştırmak için bir Android cihaz veya emülatör.
  • Android geliştirme hakkında temel düzeyde bilgi (Bu, "Hello World" değildir ancak çok da uzak değildir!).

2. Android uygulamasına MediaPipe Görevleri'ni ekleme

Android başlangıç uygulamasını indirin

Bu codelab, ekrana çizim yapmanıza olanak tanıyan önceden hazırlanmış bir örnekle başlayacak. Başlangıç uygulamasını resmi MediaPipe Samples deposunda burada bulabilirsiniz. Depoyu klonlayın veya Kodu > ZIP olarak indir'i tıklayarak ZIP dosyasını indirin.

Uygulamayı Android Studio'ya aktarma

  1. Android Studio'yu açın.
  2. Welcome to Android Studio (Android Studio'ya Hoş Geldiniz) ekranında sağ üst köşedeki Open'ı (Aç) seçin.

a0b5b070b802e4ea.png

  1. Depoyu klonladığınız veya indirdiğiniz yere gidin ve codelabs/digitclassifier/android/start dizinini açın.
  2. Android Studio'nun sağ üst kısmındaki yeşil çalıştır okunu ( 7e15a9c9e1620fe7.png) tıklayarak her şeyin doğru şekilde açıldığını doğrulayın.
  3. Uygulama, üzerine çizim yapabileceğiniz siyah bir ekranla açılır. Ayrıca, bu ekranı sıfırlamak için Temizle düğmesi de görünür. Bu ekranda çizim yapabilirsiniz ancak başka bir işlem yapamazsınız. Bu nedenle, sorunu düzeltmek için çalışmaya başlayacağız.

11a0f6fe021fdc92.jpeg

Model

Uygulamayı ilk kez çalıştırdığınızda mnist.tflite adlı bir dosyanın indirilip uygulamanızın assets dizininde depolandığını fark edebilirsiniz. Basitlik adına, rakamları sınıflandıran bilinen bir model olan MNIST'yi aldık ve projede download_models.gradle komut dosyasını kullanarak uygulamaya ekledik. El yazısıyla yazılmış harfler için olan gibi kendi özel modelinizi eğitmek isterseniz download_models.gradle dosyasını kaldırır, uygulama düzeyindeki build.gradle dosyanızda bu dosyaya yapılan referansı siler ve modelin adını kodda (özellikle DigitClassifierHelper.kt dosyasında) daha sonra değiştirirsiniz.

build.gradle dosyasını güncelleme

MediaPipe Görevleri'ni kullanmaya başlamadan önce kitaplığı içe aktarmanız gerekir.

  1. app modülünüzde bulunan build.gradle dosyasını açın ve dependencies bloğuna gidin.
  2. Bu bloğun en altında // STEP 1 Dependency Import (1. ADIM Bağımlılık İçe Aktarma) yazan bir yorum görmeniz gerekir.
  3. Bu satırı aşağıdaki uygulama ile değiştirin.
implementation("com.google.mediapipe:tasks-vision:latest.release")
  1. Bu bağımlılığı indirmek için Android Studio'nun üst kısmındaki banner'da görünen Şimdi Senkronize Et düğmesini tıklayın.

3. MediaPipe Görevleri ile rakam sınıflandırıcısı yardımcı programı oluşturma

Bir sonraki adımda, makine öğrenimi sınıflandırmanız için zorlu görevleri yerine getirecek bir sınıf dolduracaksınız. DigitClassifierHelper.kt dosyasını açın ve başlayalım.

  1. Sınıfın en üstünde // 2. ADIM Dinleyici oluşturun yazan yorumu bulun.
  2. Bu satırı aşağıdaki kodla değiştirin. Bu işlem, DigitClassifierHelper sınıfından sonuçları bu sonuçları dinleyen yere (bu durumda DigitCanvasFragment sınıfınız olacak ancak bu konuya yakında değineceğiz) geri iletmek için kullanılacak bir dinleyici oluşturur.
// STEP 2 Create listener

interface DigitClassifierListener {
    fun onError(error: String)
    fun onResults(
        results: ImageClassifierResult,
        inferenceTime: Long
    )
}
  1. Ayrıca, sınıf için isteğe bağlı bir parametre olarak DigitClassifierListener'ı kabul etmeniz gerekir:
class DigitClassifierHelper(
    val context: Context,
    val digitClassifierListener: DigitClassifierListener?
) {
  1. // STEP 3 define classifier (3. ADIM: Sınıflandırıcıyı tanımlayın) yazan satıra giderek bu uygulamada kullanılacak ImageClassifier için yer tutucu oluşturmak üzere aşağıdaki satırı ekleyin:

// STEP 3 define classifier

private var digitClassifier: ImageClassifier? = null
  1. Yorumu gördüğünüz yere şu işlevi ekleyin: // 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)
    }
}

Yukarıdaki bölümde birkaç şey oluyor. Bu nedenle, neler olduğunu gerçekten anlamak için daha küçük parçalara bakalım.

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

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

Bu blok, ImageClassifier tarafından kullanılan parametreleri tanımlar. Buna, BaseOptions altındaki uygulamanızda depolanan model (mnist.tflite) ve ImageClassifierOptions altındaki RunningMode dahildir. Bu durumda RunningMode, IMAGE'dir ancak VIDEO ve LIVE_STREAM de kullanılabilir. Diğer kullanılabilir parametreler arasında, modelin döndüreceği sonuç sayısını sınırlayan MaxResults ve döndürmeden önce modelin bir sonuçta sahip olması gereken minimum güveni belirleyen ScoreThreshold yer alır.

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

Yapılandırma seçeneklerinizi oluşturduktan sonra bir bağlam ve seçenekleri ileterek yeni ImageClassifier'ınızı oluşturabilirsiniz. Bu başlatma sürecinde bir hata oluşursa DigitClassifierListener'ınız üzerinden bir hata döndürülür.

  1. ImageClassifier'ı kullanılmadan önce başlatmak istediğimiz için setupDigitClassifier() işlevini çağırmak üzere bir init bloğu ekleyebilirsiniz.
init {
    setupDigitClassifier()
}
  1. Son olarak, // STEP 5 create classify function yazan yoruma gidin ve aşağıdaki kodu ekleyin. Bu işlev, bu durumda çizilen rakam olan bir bit eşlem'i kabul eder, bunu bir MediaPipe Image nesnesine (MPImage) dönüştürür ve ardından bu görüntüyü ImageClassifier kullanarak sınıflandırır. Ayrıca, bu sonuçları DigitClassifierListener üzerinden döndürmeden önce çıkarımın ne kadar sürdüğünü kaydeder.
// 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)
    }
}

Yardımcı dosya oluşturma işlemi tamamlandı. Bir sonraki bölümde, çizdiğiniz sayıları sınıflandırmaya başlamak için son adımları tamamlayacaksınız.

4. MediaPipe Görevleri ile çıkarım çalıştırma

Bu bölüme başlamak için Android Studio'da DigitCanvasFragment sınıfını açın. Tüm işlemler burada gerçekleşir.

  1. Bu dosyanın en altında // STEP 6 Set up listener (6. ADIM: Dinleyiciyi ayarlayın) yorumunu görmeniz gerekir. İşleyiciyle ilişkili onResults() ve onError() işlevlerini buraya ekleyeceksiniz.
// 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'dan alınan sonuçları göstereceğinden özellikle önemlidir. Bu geri çağırma işlemi bir arka plan iş parçacığından tetiklendiği için kullanıcı arayüzü güncellemelerinizi Android'in kullanıcı arayüzü iş parçacığında da çalıştırmanız gerekir.

  1. Yukarıdaki adımda bir arayüzden yeni işlevler eklediğiniz için sınıfın en üstüne uygulama beyanını da eklemeniz gerekir.
class DigitCanvasFragment : Fragment(), DigitClassifierHelper.DigitClassifierListener
  1. Sınıfın üst kısımlarında // STEP 7a Initialize classifier (Sınıflandırıcıyı başlat) yorumunu görmeniz gerekir. DigitClassifierHelper için bildirimi buraya yerleştirirsiniz.
// STEP 7a Initialize classifier.
private lateinit var digitClassifierHelper: DigitClassifierHelper
  1. // STEP 7b Sınıflandırıcıyı ilk kullanıma hazırlama bölümüne geçerek onViewCreated() işlevi içinde digitClassifierHelper'ı ilk kullanıma hazırlayabilirsiniz.
// 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. Son adımlar için // STEP 8a*: classify* yorumunu bulun ve birazdan ekleyeceğiniz yeni bir işlevi çağırmak için aşağıdaki kodu ekleyin. Bu kod bloğu, parmağınızı uygulamadaki çizim alanından kaldırdığınızda sınıflandırmayı tetikler.
// STEP 8a: classify
classifyDrawing()
  1. Son olarak, yeni classifyDrawing() işlevini eklemek için // STEP 8b classify yorumunu bulun. Bu işlem, tuvalden bir bit eşlem çıkarır ve ardından sonuçları onResults() arayüz işlevinde almak için sınıflandırma işlemini gerçekleştirmek üzere DigitClassifierHelper'a iletir.
// STEP 8b classify
private fun classifyDrawing() {
    val bitmap = fragmentDigitCanvasBinding.digitCanvas.getBitmap()
    digitClassifierHelper.classify(bitmap)
}

5. Uygulamayı dağıtma ve test etme

Tüm bu işlemlerin ardından, ekranda çizilen rakamları sınıflandırabilen çalışan bir uygulamanız olur. Uygulamayı test etmek için Android emülatörüne veya fiziksel bir Android cihaza dağıtabilirsiniz.

  1. Uygulamayı çalıştırmak için Android Studio araç çubuğunda Çalıştır'ı ( 7e15a9c9e1620fe7.png) tıklayın.
  2. Çizim alanına herhangi bir rakam çizin ve uygulamanın bunu tanıyıp tanımadığını görün. Modelin çizildiğini düşündüğü rakamı ve bu rakamı tahmin etmenin ne kadar sürdüğünü göstermelidir.

7f37187f8f919638.gif

6. Tebrikler!

Başardınız! Bu codelab'de, Android uygulamasına görüntü sınıflandırma özelliğini eklemeyi ve özellikle MNIST modelini kullanarak elle çizilmiş rakamları sınıflandırmayı öğrendiniz.

Sonraki adımlar

  • Artık rakamları sınıflandırabildiğinize göre, çizilmiş harfleri, hayvanları veya sayısız başka öğeyi sınıflandırmak için kendi modelinizi eğitmek isteyebilirsiniz. MediaPipe Model Maker ile yeni bir görüntü sınıflandırma modeli eğitme dokümanlarını developers.google.com/mediapipe sayfasında bulabilirsiniz.
  • Yüz İşaret Noktası Algılama, Hareket Tanıma ve Ses Sınıflandırma gibi Android'de kullanılabilen diğer MediaPipe Görevleri hakkında bilgi edinin.

Üreteceğiniz tüm harika içerikleri görmek için sabırsızlanıyoruz.