LiteRT ile Android'de Gerçek Zamanlı Hızlandırılmış Görüntü Segmentasyonu

1. Başlamadan önce

Kodu yazarak kas hafızanızı geliştirebilir ve materyali daha iyi anlayabilirsiniz. Kopyalama ve yapıştırma işlemi zaman kazandırsa da bu uygulamaya yatırım yapmak uzun vadede daha fazla verimlilik ve daha güçlü kodlama becerileri sağlayabilir.

Bu codelab'de, Google'ın TensorFlow Lite için yeni çalışma zamanı olan LiteRT'yi kullanarak canlı kamera feed'inde gerçek zamanlı görüntü segmentasyonu gerçekleştiren bir Android uygulaması oluşturmayı öğreneceksiniz. Başlangıç Android uygulamasını alıp buna görüntü segmentasyonu özellikleri ekleyeceksiniz. Ayrıca ön işleme, çıkarım ve son işleme adımlarını da inceleyeceğiz. Bu kurstan sonra:

  • Görüntüleri gerçek zamanlı olarak segmentlere ayıran bir Android uygulaması oluşturun.
  • Önceden eğitilmiş bir LiteRT görüntü segmentasyonu modelini entegre edin.
  • Giriş resmini model için önceden işleyin.
  • CPU ve GPU hızlandırması için LiteRT çalışma zamanını kullanın.
  • Segmentasyon maskesini görüntülemek için modelin çıkışını nasıl işleyeceğinizi öğrenin.
  • Ön kameraya göre nasıl ayarlama yapacağınızı öğrenin.

Sonunda, aşağıdaki resme benzer bir şey oluşturursunuz:

Tamamlanmış Uygulama

Ön koşullar

Bu codelab, makine öğrenimi konusunda deneyim kazanmak isteyen deneyimli mobil geliştiriciler için tasarlanmıştır. Aşağıdaki konular hakkında bilgi sahibi olmanız gerekir:

  • Kotlin ve Android Studio ile Android geliştirme
  • Görüntü işlemenin temel kavramları

Neler öğreneceksiniz?

  • LiteRT çalışma zamanını Android uygulamasına entegre etme ve kullanma
  • Önceden eğitilmiş bir LiteRT modeli kullanarak görüntü segmentasyonu gerçekleştirme
  • Giriş görüntüsünün model için nasıl önceden işleneceği.
  • Model için çıkarım çalıştırma
  • Sonuçları görselleştirmek için segmentasyon modelinin çıkışını işleme
  • Gerçek zamanlı kamera feed'i işleme için CameraX'i kullanma

İhtiyacınız olanlar

  • Android Studio'nun son sürümlerinden biri (v2025.1.1'de test edilmiştir).
  • Fiziksel bir Android cihaz. En iyi test Galaxy ve Pixel cihazlarda yapılır.
  • Örnek kod (GitHub'dan).
  • Kotlin ile Android geliştirmeye ilişkin temel bilgiler.

2. Görüntü Segmentasyonu

Görüntü segmentasyonu, bir görüntüyü birden fazla segmente veya bölgeye ayırmayı içeren bir bilgisayar görme görevidir. Nesne algılama, bir nesnenin etrafına sınırlayıcı kutu çizerken görüntü segmentasyonu, görüntüdeki her bir piksele belirli bir sınıf veya etiket atar. Bu sayede, resmin içeriği hakkında çok daha ayrıntılı ve ayrıntılı bir anlayış elde edebilir, her nesnenin tam şeklini ve sınırını öğrenebilirsiniz.

Örneğin, bir kutuda yalnızca "bir kişi" olduğunu bilmek yerine, bu kişiye ait piksellerin hangileri olduğunu tam olarak öğrenebilirsiniz. Bu eğiticide, önceden eğitilmiş bir makine öğrenimi modeli kullanarak Android cihazda gerçek zamanlı görüntü segmentasyonu işleminin nasıl yapılacağı gösterilmektedir.

Segmentasyon örneği

LiteRT: Cihazda makine öğreniminin sınırlarını zorlama

Mobil cihazlarda gerçek zamanlı ve yüksek doğrulukta segmentasyon sağlayan temel teknolojilerden biri LiteRT'dir. TensorFlow Lite için Google'ın yeni nesil, yüksek performanslı çalışma zamanı olan LiteRT, temel donanımdan mutlak en iyi performansı elde etmek üzere tasarlanmıştır.

Bu, GPU (Grafik İşlem Birimi) ve NPU (Nöral İşlem Birimi) gibi donanım hızlandırıcıların akıllı ve optimize edilmiş kullanımıyla sağlanır. LiteRT, segmentasyon modelinin yoğun hesaplama iş yükünü genel amaçlı CPU'dan bu özel işlemcilere aktararak çıkarım süresini önemli ölçüde kısaltır. Bu hızlandırma, karmaşık modellerin canlı kamera feed'inde sorunsuz bir şekilde çalıştırılmasını mümkün kılar ve makine öğrenimiyle doğrudan telefonunuzda yapabileceklerimizin sınırlarını genişletir. Bu performans seviyesi olmadan, gerçek zamanlı segmentasyon iyi bir kullanıcı deneyimi için çok yavaş ve kesintili olurdu.

3. Hazırlanın

Depoyu klonlama

İlk olarak, LiteRT deposunu klonlayın:

git clone https://github.com/google-ai-edge/LiteRT.git

LiteRT/litert/samples/image_segmentation, ihtiyacınız olan tüm kaynakların bulunduğu dizindir. Bu codelab için yalnızca kotlin_cpu_gpu/android_starter projesine ihtiyacınız olacaktır. Takılırsanız bitmiş projeyi inceleyebilirsiniz: kotlin_cpu_gpu/android

Dosya yollarıyla ilgili not

Bu eğitimde, dosya yolları Linux/macOS biçiminde belirtilmiştir. Windows kullanıyorsanız yolları buna göre ayarlamanız gerekir.

Android Studio proje görünümü ile standart dosya sistemi görünümü arasındaki farkı da belirtmek gerekir. Android Studio proje görünümü, projenizin dosyalarının Android geliştirme için düzenlenmiş yapılandırılmış bir gösterimidir. Bu eğitimdeki dosya yolları, Android Studio proje görünümündeki yolları değil, dosya sistemi yollarını ifade eder.

Başlangıç uygulamasını içe aktarma

Başlangıç uygulamasını Android Studio'ya aktararak başlayalım.

  1. Android Studio'yu açın ve Open'ı (Aç) seçin.

Android Studio Açık

  1. kotlin_cpu_gpu/android_starter dizinine gidip dizini açın.

Android Starter

Tüm bağımlılıkların uygulamanızda kullanılabilir olduğundan emin olmak için içe aktarma işlemi tamamlandığında projenizi Gradle dosyalarıyla senkronize etmeniz gerekir.

  1. Android Studio araç çubuğundan Projeyi Gradle Dosyalarıyla Senkronize Et'i seçin.

Menü Senkronizasyonu

  1. Lütfen bu adımı atlamayın. Bu adım işe yaramazsa eğitimin geri kalanı anlaşılmaz.

Başlangıç uygulamasını çalıştırma

Projeyi Android Studio'ya aktardığınıza göre artık uygulamayı ilk kez çalıştırmaya hazırsınız.

Android cihazınızı USB ile bilgisayarınıza bağlayın ve Android Studio araç çubuğunda Çalıştır'ı tıklayın.

Çalıştır düğmesi

Uygulama, cihazınızda başlatılmalıdır. Canlı kamera feed'i görürsünüz ancak henüz segmentasyon yapılmaz. Bu eğitimde yapacağınız tüm dosya düzenlemeleri LiteRT/litert/samples/image_segmentation/kotlin_cpu_gpu/android_starter/app/src/main/java/com/google/aiedge/examples/image_segmentation dizini altında olacak (Android Studio'nun bu dizini neden yeniden yapılandırdığını artık biliyorsunuz 😃).

Proje dizini

Ayrıca TODO yorumlarını ImageSegmentationHelper.kt, MainViewModel.kt ve view/SegmentationOverlay.kt dosyalarında da görürsünüz. Aşağıdaki adımlarda, bu TODO'ları doldurarak görüntü segmentasyonu işlevini uygulayacaksınız.

4. Başlangıç uygulamasını anlama

Başlangıç uygulamasında temel bir kullanıcı arayüzü ve kamera işleme mantığı zaten vardır. Aşağıda, önemli dosyalara hızlı bir genel bakış verilmiştir:

  • app/src/main/java/com/google/aiedge/examples/image_segmentation/MainActivity.kt: Bu, uygulamanın ana giriş noktasıdır. Jetpack Compose kullanarak kullanıcı arayüzünü oluşturur ve kamera izinlerini yönetir.
  • app/src/main/java/com/google/aiedge/examples/image_segmentation/MainViewModel.kt: Bu ViewModel, kullanıcı arayüzü durumunu yönetir ve görüntü segmentasyonu sürecini düzenler.
  • app/src/main/java/com/google/aiedge/examples/image_segmentation/ImageSegmentationHelper.kt: Görüntü segmentasyonu için temel mantığı buraya ekleyeceğiz. Modelin yüklenmesi, kamera karelerinin işlenmesi ve çıkarımın çalıştırılması bu kitaplık tarafından gerçekleştirilir.
  • app/src/main/java/com/google/aiedge/examples/image_segmentation/view/CameraScreen.kt: Bu Composable işlevi, kamera önizlemesini ve vücudu bölme yer paylaşımını gösterir.
  • app/src/main/assets/selfie_multiclass.tflite: Bu, kullanacağımız önceden eğitilmiş TensorFlow Lite görüntü segmentasyonu modelidir.

5. LiteRT'yi anlama ve bağımlılık ekleme

Şimdi de başlangıç uygulamasına görüntü segmentasyonu işlevini ekleyelim.

1. LiteRT bağımlılığını ekleme

Öncelikle LiteRT kitaplığını projenize eklemeniz gerekir. Bu, Google'ın optimize edilmiş çalışma zamanı ile cihaz üzerinde makine öğrenimini etkinleştirmenin ilk ve en önemli adımıdır.

app/build.gradle.kts dosyasını açın ve aşağıdaki satırı dependencies bloğuna ekleyin:

// LiteRT for on-device ML
implementation(libs.litert)

Bağımlılığı ekledikten sonra Android Studio'nun sağ üst köşesinde görünen Şimdi Senkronize Et düğmesini tıklayarak projenizi Gradle dosyalarıyla senkronize edin.

Şimdi Senkronize Et

2. Key LiteRT API'lerini anlama

ImageSegmentationHelper.kt

Uygulama kodunu yazmadan önce, kullanacağınız LiteRT API'nin temel bileşenlerini anlamanız önemlidir. com.google.ai.edge.litert paketinden içe aktardığınızdan emin olun ve ImageSegmentationHelper.kt dosyasının en üstüne aşağıdaki içe aktarma işlemlerini ekleyin:

import com.google.ai.edge.litert.Accelerator
import com.google.ai.edge.litert.CompiledModel
  • CompiledModel: Bu, TFLite modelinizle etkileşim kurmak için kullanılan merkezi sınıftır. Belirli bir donanım hızlandırıcı (CPU veya GPU gibi) için önceden derlenmiş ve optimize edilmiş bir modeli temsil eder. Bu ön derleme, LiteRT'nin temel bir özelliğidir ve daha hızlı ve verimli çıkarım yapılmasını sağlar.
  • CompiledModel.Options: CompiledModel öğesini yapılandırmak için bu oluşturucu sınıfını kullanırsınız. En önemli ayar, modelinizi çalıştırmak için kullanmak istediğiniz donanım hızlandırıcıyı belirtmektir.
  • Accelerator: Bu enum, çıkarım için donanım seçmenize olanak tanır. Başlangıç projesi, şu seçenekleri işleyecek şekilde yapılandırılmıştır:
    • Accelerator.CPU: Modeli cihazın CPU'sunda çalıştırmak için. Bu, en evrensel uyumluluğa sahip seçenektir.
    • Accelerator.GPU: Modeli cihazın GPU'sunda çalıştırmak için. Bu, genellikle görüntü tabanlı modeller için CPU'dan çok daha hızlıdır.
  • Giriş ve Çıkış Arabellekleri (TensorBuffer): LiteRT, model girişleri ve çıkışları için TensorBuffer kullanır. Bu sayede, bellek üzerinde ayrıntılı kontrol sahibi olabilir ve gereksiz veri kopyalarını önleyebilirsiniz. Bu arabellekleri model.createInputBuffers() ve model.createOutputBuffers() kullanarak doğrudan CompiledModel örneğinizden alırsınız. Ardından giriş verilerinizi bu arabelleklere yazıp sonuçları bu arabelleklerden okursunuz.
  • model.run(): Bu, çıkarımı yürüten işlevdir. Giriş ve çıkış arabelleklerini LiteRT'ye iletirsiniz. LiteRT, modeli seçilen donanım hızlandırıcıda çalıştırma gibi karmaşık görevleri yönetir.

6. İlk ImageSegmentationHelper uygulamasını tamamlama

Şimdi biraz kod yazma zamanı. ImageSegmentationHelper.kt'nın ilk uygulamasını tamamlayacaksınız. Bu işlem, LiteRT modelini tutmak için Segmenter özel sınıfının ayarlanmasını ve düzgün şekilde yayınlamak için cleanup() işlevinin uygulanmasını içerir.

  1. Segmenter sınıfını ve cleanup() işlevini tamamlama: ImageSegmentationHelper.kt dosyasında, Segmenter adlı özel bir sınıfın ve cleanup() adlı bir işlevin iskeletini bulacaksınız. İlk olarak, Segmenter sınıfını tamamlayın. Bunun için sınıfın oluşturucusunu modeli tutacak şekilde tanımlayın, giriş/çıkış arabellekleri için özellikler oluşturun ve modeli serbest bırakacak bir close() yöntemi ekleyin. Ardından, bu yeni close() yöntemini çağırmak için cleanup() işlevini uygulayın. Mevcut Segmenter sınıfını ve cleanup() işlevini aşağıdakilerle değiştirin: (~83. satır)
    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()
        }
    }
    
  2. toAccelerator yöntemini tanımlayın: Bu yöntem, tanımlanan hızlandırıcı numaralandırmalarını hızlandırıcı menüsünden içe aktarılan LiteRT modüllerine özgü hızlandırıcı numaralandırmalarıyla eşler (~225. satır):
    fun toAccelerator(acceleratorEnum: AcceleratorEnum): Accelerator {
      return when (acceleratorEnum) {
        AcceleratorEnum.CPU -> Accelerator.CPU
        AcceleratorEnum.GPU -> Accelerator.GPU
      }
    }
    
  3. CompiledModel işlevini başlatın: Şimdi initSegmenter işlevini bulun. CompiledModel örneğini burada oluşturacak ve artık tanımlanmış olan Segmenter sınıfınızı örneklendirmek için kullanacaksınız. Bu kod, modeli belirtilen hızlandırıcıyla (CPU veya GPU) kurar ve çıkarıma hazırlar. initSegmenter içindeki TODO öğesini aşağıdaki uygulamayla değiştirin (Cmd/Ctrl+f "initSegmenter" veya ~62. satır):
    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. Segmentasyon ve Ön İşleme İşlemini Başlatma

Artık bir modelimiz olduğuna göre, segmentasyon sürecini tetiklememiz ve giriş verilerini model için hazırlamamız gerekiyor.

Tetikleyici Segmentasyonu

Segmentasyon süreci, kameradan kareler alan MainViewModel.kt içinde başlar.

MainViewModel.kt

  1. Kamera Çerçevelerinden Segmentasyonu Tetikleme: MainViewModel içindeki segment işlevleri, segmentasyon görevimizin giriş noktasıdır. Kameradan yeni bir resim geldiğinde veya galeriden bir resim seçildiğinde çağrılırlar. Bu işlevler daha sonra ImageSegmentationHelper içindeki segment yöntemini çağırır. Her iki segment işlevindeki TODO'ları aşağıdakiyle değiştirin (satır ~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)
            }
    }
    

Resmi önceden işleme

Şimdi de resim ön işlemesini yapmak için ImageSegmentationHelper.kt'ye geri dönelim.

ImageSegmentationHelper.kt

  1. Public segment işlevini uygulama: Bu işlev, Segmenter sınıfındaki özel segment işlevini çağıran bir sarmalayıcı görevi görür. TODO yerine şunu ekleyin (~line 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)
    }
    
  2. Ön İşleme Uygulama: Segmenter sınıfındaki özel segment işlevi, giriş görüntüsünde modeli hazırlamak için gerekli dönüşümleri gerçekleştireceğimiz yerdir. Görüntüyü ölçeklendirme, döndürme ve normalleştirme bu kapsamdadır. Bu işlev daha sonra çıkarım gerçekleştirmek için başka bir özel segment işlevini çağırır. segment(bitmap: Bitmap, ...) işlevindeki TODO yerine şunu girin (~121. satır):
    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 ile birincil çıkarım

Giriş verileri önceden işlendiğinden artık LiteRT kullanarak temel çıkarımı çalıştırabiliriz.

ImageSegmentationHelper.kt

  1. Model Yürütmeyi Uygulama: Özel segment(inputFloatArray: FloatArray) işlevi, LiteRT run() yöntemiyle doğrudan etkileşim kurduğumuz yerdir. Önceden işlenmiş verilerimizi giriş arabelleğine yazar, modeli çalıştırır ve sonuçları çıkış arabelleğinden okuruz. Bu işlevdeki TODO yerine şunu yazın (~188. satır):
    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. İşleme sonrası ve yer paylaşımını görüntüleme

Çıkarım çalıştırdıktan sonra modelden ham bir çıkış elde ederiz. Görsel segmentasyon maskesi oluşturmak için bu çıkışı işlememiz ve ardından ekranda göstermemiz gerekir.

ImageSegmentationHelper.kt

  1. Çıkış İşlemeyi Uygulama: processImage işlevi, modelden gelen ham kayan nokta çıkışını, segmentasyon maskesini temsil eden bir ByteBuffer değerine dönüştürür. Bunu, her piksel için en yüksek olasılıklı sınıfı bularak yapar. TODO yerine şunu ekleyin (~line 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

  1. Segmentasyon Sonuçlarını Toplama ve İşleme: Şimdi MainViewModel'ye geri dönerek ImageSegmentationHelper'den gelen segmentasyon sonuçlarını işliyoruz. segmentationUiShareFlow, SegmentationResult toplar, maskeyi renkli bir Bitmap haline getirir ve kullanıcı arayüzüne sunar. segmentationUiShareFlow özelliğindeki TODO yerine (~63. satır) ifadesini ekleyin. Burada zaten bulunan kodu değiştirmeyin, yalnızca gövdeyi doldurun:
    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

Son olarak, kullanıcı ön kameraya geçtiğinde segmentasyon yer paylaşımını doğru şekilde yönlendirmek gerekir. Kamera feed'i, ön kamera için doğal olarak yansıtılır. Bu nedenle, yer paylaşımımızın Bitmap kamera önizlemesiyle doğru şekilde hizalanmasını sağlamak için aynı yatay çevirme işlemini uygulamamız gerekir.

  1. Handle Overlay Orientation: SegmentationOverlay.kt dosyasında TODO ifadesini bulun ve aşağıdaki kodla değiştirin. Bu kod, ön kameranın etkin olup olmadığını kontrol eder. Etkinse Canvas üzerine çizilmeden önce yer paylaşımlı resme Bitmap yatay çevirme uygular. (~line 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. Son Uygulamayı Çalıştırma ve Kullanma

Gerekli tüm kod değişikliklerini tamamladınız. Uygulamayı çalıştırmanın ve çalışmanızı görmenin zamanı geldi.

  1. Uygulamayı çalıştırma: Android cihazınızı bağlayın ve Android Studio araç çubuğunda Run'ı (Çalıştır) tıklayın.

Çalıştır düğmesi

  1. Özellikleri Test Etme: Uygulama başlatıldıktan sonra, renkli bir segmentasyon yer paylaşımıyla birlikte canlı kamera feed'ini görmeniz gerekir.
    • Kameralar Arasında Geçiş Yapma: Ön ve arka kameralar arasında geçiş yapmak için üst kısımdaki kamera çevirme simgesine dokunun. Yerleşimin nasıl doğru şekilde yönlendirildiğine dikkat edin.
    • Hızlandırıcıyı değiştirme: Donanım hızlandırıcıyı değiştirmek için alttaki "CPU" veya "GPU" düğmesine dokunun. Ekranın alt kısmında gösterilen Çıkarım Süresi'ndeki değişikliği gözlemleyin. GPU önemli ölçüde daha hızlı olmalıdır.
    • Galeri resmini kullanma: Cihazınızın fotoğraf galerisinden bir resim seçmek için üst kısımdaki "Galeri" sekmesine dokunun. Uygulama, seçilen statik görüntüde segmentasyon gerçekleştirir.

Diğer kullanıcı arayüzü

Artık LiteRT tarafından desteklenen, tam işlevli ve gerçek zamanlı bir görüntü segmentasyonu uygulamanız var.

11. Gelişmiş (isteğe bağlı): NPU'yu kullanma

Bu depoda, uygulama sürümünün sinirsel işlem birimleri (NPU'lar) için optimize edilmiş bir versiyonu da bulunur. NPU sürümü, uyumlu NPU'ya sahip cihazlarda önemli bir performans artışı sağlayabilir.

NPU sürümünü denemek için kotlin_npu/android projesini Android Studio'da açın. Kod, CPU/GPU sürümüne çok benzer ve NPU temsilcisini kullanacak şekilde yapılandırılmıştır.

NPU temsilcisini kullanmak için erken erişim programına kaydolmanız gerekir.

12. Tebrikler!

LiteRT kullanarak gerçek zamanlı görüntü segmentasyonu gerçekleştiren bir Android uygulamasını başarıyla oluşturdunuz. Öğrendikleriniz:

  • LiteRT çalışma zamanını bir Android uygulamasına entegre edin.
  • Bir TFLite görüntü segmentasyonu modelini yükleyip çalıştırma
  • Modelin girişini önceden işleyin.
  • Segmentasyon maskesi oluşturmak için modelin çıkışını işleyin.
  • Anlık kamera uygulaması için CameraX'i kullanın.

Sonraki Adımlar

  • Farklı bir görüntü segmentasyonu modeli deneyin.
  • Farklı LiteRT temsilcileriyle (CPU, GPU, NPU) denemeler yapın.

Daha Fazla Bilgi