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

1. Giriş

MediaPipe nedir?

MediaPipe Çözümleri, uygulamalarınıza makine öğrenimi (ML) çözümleri uygulamanıza olanak tanır. Kullanıcılara anında, ilgi çekici ve faydalı çıkışlar sunan, önceden oluşturulmuş işleme ardışık düzenlerini 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 Solutions'ın sunduğu çeşitli makine öğrenimi vizyon görevlerinden biridir. MediaPipe Tasks; Android, iOS, Python (Raspberry Pi dahil!) ve web'de kullanılabilir.

Bu Codelab'de, ekranda sayısal rakamları çıkarmanıza olanak tanıyan bir Android uygulamasıyla başlayacaksınız. Ardından, çizdiğiniz rakamları 0 ile 9 arasında tek bir değer olarak sınıflandıran bir işlev ekleyeceksiniz.

Neler öğreneceksiniz?

  • Bir Android uygulamasına MediaPipe Tasks ile görüntü sınıflandırma görevi ekleme.

Gerekenler

  • 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 kullanılan bir Android cihaz veya emülatör.
  • Android geliştirmeyle ilgili temel düzeyde bilgi (Bu, "Hello World" değil, ancak çok da uzak değildir!).

2. Android uygulamasına MediaPipe Görevleri ekleme

Android başlangıç uygulamasını indirin

Bu codelab, ekranda çizim yapmanızı sağlayan önceden hazırlanmış bir örnekle başlar. Başlangıç uygulamasını buradaki resmi MediaPipe Samples deposunda bulabilirsiniz. Depoyu klonlayın veya Kod > seçeneğini tıklayarak zip dosyasını indirin ZIP dosyasını indir.

Uygulamayı Android Studio'ya aktarın

  1. Android Studio'yu açın.
  2. Android Studio'ya hoş geldiniz ekranında sağ üst köşedeki 'ı seçin.

a0b5b070b802e4ea.png

  1. Kod deposunu klonladığınız veya indirdiğiniz yere gidin ve codelabs/digitalclassifier/android/start dizinini açın.
  2. Android Studio'nun sağ üst tarafındaki yeşil çalıştır okunu ( 7e15a9c9e1620fe7.png) tıklayarak her şeyin düzgün şekilde açıldığını doğrulayın.
  3. Uygulamanın, üzerine çizim yapabileceğiniz siyah bir ekranla açıldığını ve ekranı sıfırlayan Temizle düğmesini görürsünüz. Bu ekranda çizim yapabilirsiniz, ancak başka işlevin farkı yoktur, bu yüzden şimdi düzeltmeye 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 dizinine depolandığını fark edebilirsiniz. Kolaylık sağlaması açısından, rakamları sınıflandıran bilinen bir model olan MNIST'yi daha önce alıp projedeki download_models.gradle komut dosyasını kullanarak bu modeli uygulamaya ekledik. Kendi özel modelinizi (örneğin, elle yazılmış harfler için bir model) eğitmeye karar verirseniz download_models.gradle dosyasını kaldırır, uygulama düzeyindeki build.gradle dosyanızdan bu referansı siler ve modelin adını kodun ilerleyen kısımlarında (özellikle DigitClassifierHelper.kt dosyasında) değiştirirsiniz.

build.gradle dosyasını güncelleyin

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

  1. app modülünüzde bulunan build.gradle dosyasını açın, ardından aşağı kaydırarak dependencies bloğuna gidin.
  2. Bu bloğun en altında // STEP 1 Dependency Import (1. ADIM Bağımlılığı İçe Aktarma) yazan bir yorum görürsünüz.
  3. Bu satırı aşağıdaki uygulamayla 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 Tasks rakam sınıflandırıcı yardımcısı oluşturma

Sonraki adımda makine öğrenimi sınıflandırmanızın zor kısmını üstlenecek bir sınıf dolduracaksınız. DigitClassifierHelper.kt dosyasını açın ve başlayalım.

  1. Sınıfın üst tarafında // 2. ADIM (İşleyici oluştur) yazan yorumu bulun.
  2. Bu satırı aşağıdaki kodla değiştirin. Bu komut, DigitClassifierHelper sınıfındaki sonuçları, bu sonuçları dinlediği her yere geri aktarmak için kullanılacak bir işleyici oluşturur (bu örnekte bu, sizin DigitCanvasFragment sınıfınız olacak, ancak yakında bu konuya değineceğiz).
// 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 DigitClassifierListener parametresi olarak kabul etmeniz gerekir:
class DigitClassifierHelper(
    val context: Context,
    val digitClassifierListener: DigitClassifierListener?
) {
  1. // 3. ADIM sınıflandırıcıyı tanımla yazan satıra indiğinizde, bu uygulama için kullanılacak ImageClassifier'a yönelik bir yer tutucu oluşturmak için aşağıdaki satırı ekleyin:

// 3. ADIM sınıflandırıcıyı tanımlayın

private var digitClassifier: ImageClassifier? = null
  1. Şu işlevi, yorumu gördüğünüz yere ekleyin // 4. ADIM: sınıflandırıcıyı:
// 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 yaşanmaktadır. Şimdi, neler olduğunu tam olarak anlamak için 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, uygulamanızda (mnist.tflite) BaseOptions altında ve ImageClassifierOptions altındaki RunningMode (bu örnekte IMAGE) olan RunningMode seçeneği de dahildir. Ancak, VIDEO ve LIVE_STREAM ek seçenekler de kullanılabilir. Diğer kullanılabilir parametreler, modelin maksimum sayıda sonuç döndürmesini sınırlandıran MaxResults ve modelin bir sonuç döndürmeden önce sahip olması gereken minimum güveni belirleyen ScoreThreshold'dur.

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, bağlam ve seçenekleri ileterek yeni ImageClassifier'ınızı oluşturabilirsiniz. Bu başlatma sürecinde bir sorun oluşursa DigitifierListener öğeniz aracılığıyla bir hata döndürülür.

  1. Kullanılmadan önce ImageClassifier'ı başlatmak isteyeceğimiz için, setupDigitClassifier() komutunu çağırmak için bir init bloğu ekleyebilirsiniz.
init {
    setupDigitClassifier()
}
  1. Son olarak, // 5. ADIM sınıflandırma işlevi oluşturun yazan yoruma gidin ve aşağıdaki kodu ekleyin. Bu işlev, Bitmap'i (bu örnekte çizilen basamak) kabul eder, MediaPipe Image nesnesine (MPImage) dönüştürür, ardından ImageClassifier'ı kullanarak o görüntüyü sınıflandırır ve bu sonuçları DigitifierListener ü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 bu kadar! Sonraki bölümde, çizdiğiniz sayıları sınıflandırmaya başlamak için son adımları dolduracaksınız.

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

Bu bölüme, tüm işin gerçekleşeceği Android Studio'da DigitCanvasFragment sınıfını açarak başlayabilirsiniz.

  1. Bu dosyanın en altında // 6. ADIM: İşleyiciyi ayarlayın ifadesini içeren bir yorum görürsünüz. İş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ği için özellikle önemlidir. Bu geri çağırma, bir arka plan ileti dizisinden 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 eklerken 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ısmına doğru, // ADIM 7a Sınıflandırıcıyı başlat yazan bir yorum göreceksiniz. DigitClassifierHelper beyanını buraya yerleştireceksiniz.
// STEP 7a Initialize classifier.
private lateinit var digitClassifierHelper: DigitClassifierHelper
  1. // ADIM 7b Sınıflandırıcıyı başlat'a giderek onViewCreated() işlevi içindeki digitalClassifierHelper öğesini başlatabilirsiniz.
// 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 yorumu bulun // ADIM 8a*: class* ve aşağıdaki kodu ekleyerek hemen ekleyeceğiniz yeni bir işlevi çağırın. 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 categoryDrawing() işlevini eklemek için // ADIM 8b classify yorumunu bulun. Bu işlem tuvalden bir bit eşlem çıkaracak ve ardından onResults() arayüz işlevindeki sonuçları almak için sınıflandırmayı gerçekleştirmek üzere bu bit eşlemi 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 bunların ardından, ekranda çizilmiş rakamları sınıflandırabilen çalışan bir uygulamanız olmalı. Devam edin ve test etmek için uygulamayı bir Android Emülatöre veya fiziksel bir Android cihaza dağıtın.

  1. Uygulamayı çalıştırmak için Android Studio araç çubuğunda Çalıştır'ı ( 7e15a9c9e1620fe7.png) tıklayın.
  2. Çizim tahtasına bir rakam çizin ve uygulamanın bu rakamı tanıyıp tanıyamadığını kontrol edin. Hem modelin çizildiğine inandığı rakamı hem de bu basamağı tahmin etmesinin ne kadar sürdüğünü göstermelidir.

7f37187f8f919638.gif

6. Tebrikler!

Başardınız. Bu codelab'de Android uygulamalarına nasıl görüntü sınıflandırmaları ekleyeceğinizi ve özellikle MNIST modeli kullanarak elle çizilmiş rakamları nasıl sınıflandıracağınızı öğrendiniz.

Sonraki adımlar

  • Artık rakamları sınıflandırabildiğinize göre kendi modelinizi çizilen harfleri sınıflandırmak, hayvanları veya sonsuz sayıda başka öğeyi sınıflandırmak için eğitmek isteyebilirsiniz. MediaPipe Model Maker ile yeni bir resim sınıflandırma modeli eğitmek için gereken dokümanları developers.google.com/mediapipe sayfasında bulabilirsiniz.
  • Yüzde Önemli Nokta Algılama, Hareket Tanıma ve Ses Sınıflandırma dahil olmak üzere Android'de kullanılabilen diğer MediaPipe Görevler hakkında bilgi edinin.

Yapacağınız tüm harika içerikleri sabırsızlıkla bekliyoruz!