Android'de gizlilik öncelikli OCR: Subly faturaları buluta göndermeden nasıl okuyor
ML Kit Text Recognition ile Android'de cihaz üzerinde OCR için pratik bir 2026 rehberi — kurulum, kod ve kullanıcı verisini sunucularınızdan uzak tutan alan çıkarma teknikleri.
Bir kullanıcı telefonunu bir faturaya doğrulttuğu anda, uygulamanızın bir seçimi olur. O görüntüyü bir bulut OCR servisine gönderip 400 ms içinde temiz metni geri alabilirsiniz — ama bunun bedeli birinin özel finansal belgesini kendi sunucularınız ve bir üçüncü taraf üzerinden geçirmektir. Ya da işi cihazın üzerinde yapabilir, daha dürüst bir gizlilik politikası yazabilir ve bir sonraki veri ihlalinin sizin suçunuz olacağını hiç düşünmek zorunda kalmazsınız.
Ben ikinci yolu Subly için seçtim; Subly, bir Netflix veya Spotify faturasını tarayıp servis adını, tutarı, sıklığı ve bir sonraki ödeme tarihini sizin için doldurmanıza olanak tanıyan bir Android abonelik takip uygulaması. Hiçbir şey telefondan çıkmıyor. Bu yazı, bunun 2026’da nasıl çalıştığını anlatıyor — kurulum, kod ve zor yoldan öğrendiğim alan çıkarma teknikleri.
Bulut bu kadar kolayken neden cihaz üzerinde
Bulut OCR API’leri tek satırlık işler. Google Cloud Vision, AWS Textract, Azure Read — hepsi size net sonuçlar verir ve hepsi kullanıcılarınızın görüntülerini ister. Bir abonelik takip uygulaması için bu görüntüler şunları içerebilir:
- Hesap sahibinin adı, son 4 hane veya tam e-posta adresi
- Basılı bir faturanın altındaki adres blokları
- Ödeme yöntemi bilgileri
- Toplam fiyat, kalem detayları ve tarihler
Bu listedeki hiçbir şey, bir üçüncü tarafın loglarında durmasını istediğim türden değil. Ürün açısından bakıldığında da “faturalarınızı hiçbir yere göndermiyoruz”, “faturalarınızı Google Cloud’a gönderiyor ve 30 gün içinde siliyoruz” ifadesinden çok daha güçlü bir pazarlama cümlesi. Bu yüzden mimari, değeri takip ediyor: görüntüyü cihazda tut, yalnızca çıkarılan alanları sakla ve tarama için hiç internet bağlantısı açma.
En gizli bayt, telefondan hiç çıkmayan bayttır.
ML Kit Text Recognition v2: iş atı
Google’ın ML Kit Text Recognition v2 çözümü, 2026’da Android’de cihaz üzerinde OCR için sıkıcı ama doğru olan yanıttır. Latin modelini uygulamanızın içine gömülü olarak gönderir (yaklaşık 4 MB), Pixel sınıfı bir telefonda onlarca milisaniyede çalışır ve basılı metni fişler, faturalar ve e-posta ekran görüntüleri için yeterince iyi işler.
Bunda hoşuma giden şeyler:
- Ağ izni gerektirmez.
INTERNETiznini kaldırdığınız bir uygulama gönderebilirsiniz ve OCR yine de çalışır. Sadece bu bile entegrasyon süresine değer. - Çevrimdışı çalışır. Uçak modunda tarama bir hata değil, bir özelliktir.
- Uygulama başına model. Modeli gömmek, ilk açılışta sürpriz bir indirme olmayacağı ve kullanıcının Wi-Fi’si dengesiz olduğunda
Tekrar Deneiletişim kutusu çıkmayacağı anlamına gelir. - Ücretsiz. Ölçeklendikçe API maliyeti yok. Indie bir uygulama için bu, yayınlamak ile fazla düşünmek arasındaki farktır.
Ödünleşim, uç durumlardaki doğrulukta: el yazısı belli belirsizdir, yoğun termal basılı fişler yerleşimi karıştırabilir ve Latin dışı yazılar farklı bir model gerektirir. Fiş/fatura/e-posta kullanım senaryosu içinse fazlasıyla yeterli.
2026’da kurulum
Gradle bağımlılığı:
// app/build.gradle.kts
dependencies {
implementation("com.google.mlkit:text-recognition:16.0.1")
implementation("androidx.camera:camera-camera2:1.4.0")
implementation("androidx.camera:camera-lifecycle:1.4.0")
implementation("androidx.camera:camera-view:1.4.0")
}
Ve manifest’inizde tek bir satır, böylece Play modelin gömülü olduğunu ve önceden indirildiğini tanır:
<application>
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="ocr" />
</application>
Kurulumun tamamı bu. API anahtarı yok, servis hesabı JSON’u yok, imzalı istek yok.
Bir CameraX karesinden metin tanıma
İşte çekirdek döngü. CameraX size ImageProxy kareleri besler, birini bir InputImage içine sararsınız ve tanıyıcı size paragraflar, satırlar ve dikdörtgenler içeren bir Text nesnesi verir.
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.text.TextRecognition
import com.google.mlkit.vision.text.latin.TextRecognizerOptions
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
private val recognizer = TextRecognition.getClient(
TextRecognizerOptions.DEFAULT_OPTIONS,
)
@androidx.camera.core.ExperimentalGetImage
fun analyzeFrame(image: ImageProxy, onScanned: (String) -> Unit) {
val media = image.image ?: return image.close()
val input = InputImage.fromMediaImage(media, image.imageInfo.rotationDegrees)
recognizer.process(input)
.addOnSuccessListener { result ->
// Whole page as a single string, line breaks preserved.
onScanned(result.text)
}
.addOnCompleteListener { image.close() }
}
Bilmeye değer birkaç şey:
- Hata durumunda bile
image.close()fonksiyonunu her zaman tam olarak bir kez çağırın. Unutursanız ML Kit’in dinleyici deseni sizi ısırabilir. result.textkolay moddur. Daha iyi çıkarım içinresult.textBlocksisteyeceksiniz; böylece fişte hangi satırların yan yana olduğunu takip edebilirsiniz.- ML Kit dikdörtgenler de döndürür. Bunlar katmanlar çizmek (“bunu algıladık”) ve kullanıcının faturayı gerçekten çerçeveye alıp almadığını doğrulamak için kullanışlıdır.
Alan çıkarma: ham metinden yapılandırılmış veriye
OCR size metin verir. İlginç iş, o metni bir aboneliğe dönüştürmektir. Subly için bu dört alan demektir:
- Servis adı — “Netflix”, “Spotify”, “ChatGPT Plus”
- Tutar — “₺199.00” veya “$9.99”
- Faturalandırma sıklığı — aylık, yıllık
- Bir sonraki ödeme tarihi — gelecekteki tarih
Pratik bir cihaz üzeri işlem hattı şöyle görünür:
data class ExtractedSubscription(
val service: String?,
val amount: Money?,
val cycle: BillingCycle?,
val nextDate: LocalDate?,
)
fun extract(text: String): ExtractedSubscription {
val lines = text.lines().map { it.trim() }.filter { it.isNotEmpty() }
return ExtractedSubscription(
service = findService(lines),
amount = findAmount(lines),
cycle = findCycle(lines),
nextDate = findNextDate(lines),
)
}
Her find* fonksiyonu küçük, test edilebilir bir şeydir. Gerçek uygulamadan birkaç ders:
- Servis adı en zor olanıdır. Yaygın servislerden oluşan bir sözlük (Netflix, Spotify, ChatGPT, YouTube Premium, Apple Music…) durumların %80’ini yakalar. Geri kalanı için fişin ilk belirgin satırı ya da fatura e-postalarındaki
From:/Sent by:başlığı etrafında bir sezgisel yöntem iyi işler. - Tutar en kolayıdır.
(?:[₺$€£])\s?\d{1,3}(?:[.,]\d{3})*(?:[.,]\d{2})?gibi yerel ayara duyarlı bir regex çoğu para birimini kapsar. Ayrıştırmadan önce binlik ayırıcıları her zaman temizleyin. - Sıklık, tutarın etrafındaki dilden gelir: “per month”, “/mo”, “monthly”, “aylık”, “yearly”, “/yr”. Küçük bir Türkçe + İngilizce kelime dağarcığı ihtiyacınız olanı karşılar.
- Bir sonraki tarih iki stratejidir: varsa açık ifadeleri yakala (“next billing on”, “renews on”), yoksa fişteki en son tarihe bir sıklık ekleyerek çıkarım yap.
Bunların hiçbiri gösterişli değil. Hepsi cihazın üzerinde, yani bir başarısızlık sızdırılmış bir fatura anlamına gelmiyor.
Kullanıcıya yapılan işi göster
Ne yaptığını göstermezseniz bir tarayıcı kara kutudur. Subly’nin deseni şu: bir kare eşleşir eşleşmez, kamera önizlemesinin üzerine çıkarılan alanları ve bir Düzenle düğmesini içeren bir kart çiz. OCR’a duyulan güven kırılgandır; bir Düzenle düğmesi dürüst ve ücretsizdir.
@Composable
fun ExtractedCard(s: ExtractedSubscription, onEdit: () -> Unit, onAccept: () -> Unit) {
Card(...) {
Text("AI detected", style = labelMono)
Text("${s.service ?: "—"} ${s.amount?.format() ?: ""}", style = title)
Text("${s.cycle?.label} · next ${s.nextDate?.format()}", style = body)
Row {
TextButton(onClick = onEdit) { Text("Edit") }
FilledButton(onClick = onAccept) { Text("Add") }
}
}
}
Dört alandan ikisi eksikse, otomatik ekleme yapma — kullanıcıyı, elde ettiğiniz verilerle önceden doldurulmuş manuel formun içine bırakın.
Cihaz üzeri OCR’dan elde edemedikleriniz
Birkaç dürüst ödünleşim:
- Diller. Her yazı kendi modelini gerektirir. Latin varsayılan olarak gömülüdür; Çince, Japonca, Korece ve Devanagari ayrıdır.
- Tablo düzenleri. Çok sütunlu bir faturadan tam sütun-satır yeniden yapılandırması gerekiyorsa, beklediğinizden daha fazla yerleşim kodu yazacaksınız. Çoğu abonelik fişi için bu bir sorun değil.
- El yazısı. Söz vermeyin. Bulut OCR’ları bile burada güvenilmezdir.
- Model boyutu. Birden fazla dil modeli eklemek APK’nizi gözle görülür şekilde büyütebilir. Yükleme boyutunu önemsiyorsanız Play’in App Bundle ve özellik teslimatını kullanın.
Pazarlama cümlesi kendini yazıyor
Gizlilik politikamızda söyleyebilmek istediğim bir cümle: “yakaladığınız fatura görüntüsü sunucularımıza yüklenmez ve işlendikten sonra saklanmaz.” Cihaz üzeri ML Kit ile bu cümle, kodun tam anlamıyla nasıl çalıştığıdır. Bu ifadenin pazarlamayla ilgisi yok — bir mimari beyanıdır ve gizlilik politikası, tersine değil, bundan türer.
2026’da cihaz üzeri OCR için gerçek argüman budur. Sadece biraz daha hızlı ya da biraz daha ucuz olması değil. “Verilerinizi asla görmüyoruz” ifadesini, hukuk departmanı muhtemelen sorun yoktur dediği için değil, doğru olduğu için söyleyebilmenizdir.
Abonelik / fiş / kişisel finans alanında bir şey geliştiriyorsanız ve hangi yöne gideceğinizden emin değilseniz, varsayılan olarak cihaz üzeri yolu seçin. Kütüphane ücretsiz, gizlilik hikayesi kurşun geçirmez ve olabilecek en kötü şey, rakiplerinizin yığınlarını yeniden inşa etmeden asla eşleştiremeyeceği bir özellik yayınlamanızdır.
Subly artık Google Play’de yayında — söz verildiği için değil, mimarisi gereği gizlilik öncelikli. Uygulama sayfasında daha fazlası.
// İlgili okumalar
Günlükten dahası
Granyn'i Geliştirmek: banka girişi olmayan bir bütçe takipçisi, üç tabloda
Granyn tek bir banka hesabı bağlamadan, para birimleri arasında harcamaları nasıl takip ediyor ve tekrarlayan faturaları nasıl yakalıyor — altındaki Room şeması ve bunun getirdiği ödünleşimler.
Stocky'yi Geliştirmek: mutfağınızda neyin biteceğini tahmin etmek — hem de cihazın üzerinde
Stocky bir barkod taramasını ve biraz kullanım geçmişini, siz daha bitirmeden önünüze düşen bir alışveriş listesine nasıl dönüştürüyor — tüm matematik ve tüm veriler telefonunuzda kalarak.
2026'da local-first Android: SQLite, Room ve kullanıcı verisini cihazda tutmak
Room ve SQLite ile local-first Android uygulamaları geliştirmenin 2026 rehberi — şema tasarımı, migration'lar, WAL, dışa aktarma ve senkronizasyonu ne zaman eklemeli (ve ne zaman eklememeli).