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

1. Giriş

MediaPipe nedir?

MediaPipe Solutions, makine öğrenimi (ML) çözümlerini uygulamalarınıza uygulamanıza olanak tanır. Kullanıcılara anında, ilgi çekici ve yararlı sonuçlar sunan önceden oluşturulmuş işleme ardışık düzenlerini yapılandırmak için bir çerçeve sağlar. Varsayılan modelleri güncellemek için MediaPipe Model Maker ile bu çözümleri özelleştirebilirsiniz.

Görüntü sınıflandırma, MediaPipe Solutions'un sunduğu çeşitli makine öğrenimi görüntüleme görevlerinden biridir. MediaPipe Tasks; 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şlarsınız. Ardından, çizilen bu rakamları 0 ile 9 arasında tek bir değer olarak sınıflandıran işlevler eklersiniz.

Neler öğreneceksiniz?

İhtiyacınız olanlar

  • Android Studio'nun yüklü bir sürümü (bu kod laboratuvarı, 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 "Merhaba Dünya" değildir ancak çok da uzak değildir.)

2. Android uygulamasına MediaPipe Tasks ekleme

Android başlangıç uygulamasını indirin

Bu codelab, ekranda çizim yapmanıza olanak tanıyan önceden hazırlanmış bir örnekle başlar. Bu başlangıç uygulamasını resmi MediaPipe Samples deposunda burada bulabilirsiniz. Depoyu klonlayın veya Kod > ZIP İndir'i tıklayarak ZIP dosyasını indirin.

Uygulamayı Android Studio'ya aktarma

  1. Android Studio'yu açın.
  2. Android Studio'ya hoş geldiniz ekranında, sağ üst köşedeki 'ı 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, üzerinde çizebileceğiniz siyah bir ekranla açılır. Bu ekranı sıfırlamak için Temizle düğmesi de gösterilir. Bu ekranda çizim yapabilirsiniz ancak başka bir şey yapamazsınız. Bu sorunu düzeltmeye hemen başlayacağız.

11a0f6fe021fdc92.jpeg

Model

Uygulamayı ilk kez çalıştırdığınızda mnist.tflite adlı bir dosyanın indirildiğini ve uygulamanızın assets dizininde depolandığını fark edebilirsiniz. Basitlik açısından, sayıları sınıflandıran bilinen bir model olan MNIST'i aldık ve projedeki download_models.gradle komut dosyasını kullanarak uygulamaya ekledik. El yazısı harfleri için özel bir model eğitmeye karar verirseniz download_models.gradle dosyasını kaldırır, uygulama düzeyindeki build.gradle dosyanızdaki referansını siler ve modelin adını kodda daha sonra (özellikle DigitClassifierHelper.kt dosyasında) değiştirirsiniz.

build.gradle dosyasını güncelleme

MediaPipe Görevler'i 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 alt kısmında // 1. ADIM Bağımlılık İç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 sayı sınıflandırıcı yardımcısı oluşturma

Sonraki adımda, makine öğrenimi sınıflandırmanız için ağır işleri yapacak bir sınıf dolduracaksınız. DigitClassifierHelper.kt dosyasını açıp başlayalım.

  1. Sınıfın en üstündeki // 2. ADIM Dinleyici oluştur ifadesini içeren yorumu bulun.
  2. Bu satırı aşağıdaki kodla değiştirin. Bu işlem, DigitClassifierHelper sınıfındaki sonuçları, bu sonuçları dinleyen yere (bu durumda DigitCanvasFragment sınıfınız olacak, ancak yakında oraya geleceğiz) 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. Sınıf için isteğe bağlı parametre olarak bir DigitClassifierListener da 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 giderek bu uygulama için kullanılacak ImageClassifier için bir yer tutucu oluşturmak üzere aşağıdaki satırı ekleyin:

// STEP 3 define classifier

private var digitClassifier: ImageClassifier? = null
  1. // STEP 4 set up classifier (4. ADIM sınıflandırıcıyı ayarlayın) yorumunu gördüğünüz yere aşağıdaki işlevi ekleyin:
// 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ç işlem gerçekleşiyor. 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. BaseOptions altında uygulamanızda depolanan model (mnist.tflite) ve ImageClassifierOptions altındaki RunningMode (bu durumda IMAGE, ancak VIDEO ve LIVE_STREAM da kullanılabilir seçeneklerdir) buna dahildir. Diğer kullanılabilir parametreler arasında, modeli maksimum sayıda sonuç döndürecek şekilde sınırlayan MaxResults ve modelin bir sonucu döndürmeden önce bu sonuç için sahip olması gereken minimum güveni ayarlayan 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çenekler göndererek yeni ImageClassifier'ınızı oluşturabilirsiniz. Bu başlatma işleminde bir sorun oluşursa DigitClassifierListener aracılığıyla bir hata döndürülür.

  1. ImageClassifier sınıfını kullanılmadan önce başlatmak istediğimizden, setupDigitClassifier() işlevini çağırmak için bir init bloğu ekleyebilirsiniz.
init {
    setupDigitClassifier()
}
  1. Son olarak, // STEP 5 create classify function (5. ADIM sınıflandırma işlevi oluştur) yazan yoruma gidip aşağıdaki kodu ekleyin. Bu işlev, bir Bitmap (bu durumda çizilen rakam) kabul eder, bunu bir MediaPipe Image nesnesine (MPImage) dönüştürür, ardından ImageClassifier'ı kullanarak bu resmi sınıflandırır ve çıkarım işleminin ne kadar sürdüğünü kaydeder. Ardından bu sonuçları DigitClassifierListener üzerinden döndürür.
// 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ı dosyayla ilgili açıklamalarımız bu kadar. Sonraki bölümde, çekilen numaralarınızı sınıflandırmaya başlamak için son adımları tamamlayacaksınız.

4. MediaPipe Tasks ile çıkarım yapma

Bu bölümü, Android Studio'da DigitCanvasFragment sınıfını açarak başlatabilirsiniz. Tüm işlemlerin yapılacağı yer burasıdır.

  1. Bu dosyanın en altında // 6. ADIM Dinleyiciyi ayarlayın yazan bir yorum göreceksiniz. Dinleyiciyle ilişkili onResults() ve onError() işlevlerini buraya eklersiniz.
// 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örüntüleyeceğinden özellikle önemlidir. Bu geri çağırma, arka plan iş parçacığında tetiklendiğinden 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 üst kısmına uygulama beyanını da eklemeniz gerekir.
class DigitCanvasFragment : Fragment(), DigitClassifierHelper.DigitClassifierListener
  1. Sınıfın üst kısmında // ADIM 7a Sınıflandırıcıyı başlat yazan bir yorum görürsünüz. DigitClassifierHelper için beyanı buraya yerleştireceksiniz.
// STEP 7a Initialize classifier.
private lateinit var digitClassifierHelper: DigitClassifierHelper
  1. // ADIM 7b Sınıflandırıcıyı başlat bölümüne ilerleyin. digitClassifierHelper'ı onViewCreated() işlevi içinde 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 // 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 bitmap ayıklayıp onResults() arayüz işlevinde sonuçları almak için sınıflandırma yapmak ü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 bunları tamamladıktan sonra, ekranda çizilen rakamları sınıflandırabilen çalışan bir uygulamanız olur. Uygulamayı test etmek için bir Android emülatörüne 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 pad'ine herhangi bir rakam çizin ve uygulamanın bunu tanıyıp tanıyamayacağını öğrenin. Hem modelin çizildiğini düşündüğü rakamı hem de bu rakamın tahmin edilmesinin ne kadar sürdüğünü gösterir.

7f37187f8f919638.gif

6. Tebrikler!

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

Sonraki adımlar

  • Rakamları sınıflandırabildiğinize göre, çizilmiş harfleri, hayvanları veya sonsuz sayıda başka öğeyi sınıflandırmak için kendi modelinizi eğitebilirsiniz. MediaPipe Model Maker ile yeni bir resim sınıflandırma modeli eğitmeyle ilgili dokümanları 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.

Yapacağınız harika içerikleri görmek için sabırsızlanıyoruz.