Sizlerle daha önce ML & DL & AI ile araç şerit takip ile plaka tanıma yazılım ve bunların nasıl yazılabileceğini açıklayan yazılar paylaşmıştım.
Yaklaşık 10 günden fazla bir süre nesnelerin tanınması ve araç şerit takip yazılımlarının mobil cihazlarda özellikle performans konusunda nasıl bir sonuç vereceğini merak ettiğimden bunlar üzerine çalıştım.. Eve kapandım yani. Hani bazıları eline cihazı alıp bu cihaz şunu yapar bunu yapar der ya bende acaba bir yerlerden kodları tam çalışan örneği ile bulup hiçbir iş yapmadan çalıştırıp onunla ilgili bir yazı veya video mu yapsam diyor duruyorum kendime.
Daha önce yazdığım ve sizinle deneyimlerimi paylaştığım şerit takip ve plaka tanıma yazılımları desktop dediğimiz türden yazılımlardı ve bilgisayarın gücüne bağlı olarak gayet tatminkar sayılırdı. Bu arada sizlere Haar cascade LBP ve CNN dediğimiz türden DL ve AI yı içinde algoritmik olarak barındıran yaklaşımlardan da bahsetmiştim.
Şimdi bakalım mobil cihazlarda performans nasıl oluyor. Ben android tabanlı bir cihazda denemeleri yaptım. Elimde aynı cihazın 3 değişik modeli vardı ancak 2 sini kullandım.
Tespit ettiğim en önemli konular şunlar:
1-) Eğer sadece android java (hatta kotlin vb) yazarsanız işiniz çok zor. Hatta IOS da Swift veya Objective C de dahil. Tek bir konuda yani sadece nesne tanıma için yeterli olur ancak iş daha karmaşık örneğin şerit tanıma takip de işin içine girerse yandınız.
2-) Mutlaka C++ kullanmalısınız. İşi yapacak hesapları görecek katmanı C++ ile yazıp SO dediğimiz Shared Object dosyasına derleyerek çevirip android projelerde kullanmalısınız. İsterseniz static olan .a ları da kullanmanız mümkün. Ben SO tercih ederim.
3-) Çoğu kişi (yeni başlayan ya da inat edenler) android studio içinde yaptıkları proje de CPP yani C++ kodlarını yazarlar ve orada bir dizine koyup cmake ve/veya ndk-build ile derleme yaparlar. Bence hiç yapmayın hem işiniz uzar hem de anlamsız hata mesajlarıyla uğraşırsınız. Pratiği anlatacağım aşağıda.
4-) Obje tanıma da örnek incelenecek proje bence Google ın tensorflow demo yazılımı. İndirip bakın. Hızlı çalışmasının sebepleri var. Bir kere camera nın onpreviewframe event metodunu kullanıp gelen byte array frame i resme çevirip içinde obje arayıp bulup işaretleyip tekrar camera buffer ına koyup bize göstermiyor. (Böylece bazı uygulamaların nasıl yapıldığını da anladınız). Peki ne yapıyor :
OnImageAvailableListener, Camera.PreviewCallback interface lerini kullanıp kameranın onpreviewframe eventini pasifize edip kameradan direkt evet direkt Imagereader ile android.media.image tipte (kendisi abstract dır aslında) bunu alıp şunları yapıyor. Resmi belirli bir büyüklüğe getirip yani crop ediyor ve resim tanıma ya sokuyor. Gelen sonuçları değerlendirip android ekranda tanımlanan OverlayView üzerinde canvas ile dikdörtgen çiziyor. Aslında kandırıkçı diyebiliriz. Kamera görüntüsü üzerine şeffaf bir pencere açtığınızı bunun da Overlay olduğunuz düşünün gelen resmi inceledi ve cisimleri algıladı o zaman bunun üzerine bir tuval yani canvas oluşturup bunun üzerinde canvas a çiziyor ve overlay e render edip ekranda sizin gördüğünüz görüntü üzerinde cisimlerin dikdörtgenle çizildiğini görüyorsunuz ! ne kadar kolay değil mi? Değil kodları inceleyin sonunda anlarsınız.
Dolayısı ile oldukça hızlı çalışıyor çünkü kamera görüntüsü ile manipulasyon yapılmıyor o görüntü hep engelsiz tampersiz çalışıyor.
Siz ikinci bir çözüm yazmak istediniz. Yani şerit takip sistemini de buna monte edeceksiniz. O zaman yazdıklarıma dikkat edin ve başka yollara sapmayın. Aksi halde mobil cihazınızda çok yavaş çalışır bu entegre yazılımınız. Aynı tekniği ikinci çözümünüzü buna entegre yapmak için kullanabilirsiniz.
Eğer klasik yöntemlerle yaparsanız çok yavaşlar cihazınız.
5-) Şerit tanıma yani ikinci yazılımınızı Haar, LBP vb lerle yaparsanız ki yanlış yola sapmış olursunuz yapmanız gereken öyle java v bile yazmak değil. Oturup C++ da yazacaksınız. Masaüstü olsa python , C# artık neyle yazarsanız yazın. Ama masaüstünde belediye yazılımı yapıp gelen kamera ve radar cihaz ortak bilgisinden 20 den fazla aracın plakasını ve hızını okuyacaksanız yine C++ la yazmanız tavsiye olunur. Android tarafta C++ ile bu uygulamayı yazın. Android taraf dediysem illa android studio da değil. Bir yerde yazın. Yazdıkatn sonra PC nizde android sdk altında NDK yüklediyseniz NDK-BUILD dizininde derleyici var. Onunla derleyin.
Yukarıda yazdığım gibi android studio içinde yapmayın uzman tavsiyesidir.
Bu arada tam burada CV yani Computer Vision dan bahsedelim. Eksik kaldı. Computer Vision bir görüntü işleme DEĞİLDİR. Nesnelerin vb tanınması işidir. OPENCV ise; görüntü işlemede dahil Computer Vision da yani AI konusunda da bize yardımcı olan bir kütüphanedir. Bunu da burada kullanacağız.
O zaman OPENCV nin 2.4.9 veya ben 3.2.0 versionunu indirmiştim. Bir SDK ları var ki ZIP olarak indiriliyor, bir de tam Kaynak kodları var o da EXE olarak indiriliyor. SDK ve kaynak kodları incelerseniz ve biraz da çalışmaya başlarsanız bir şeylerin eksik olduğunu göreceksiniz. Size lazım olacak bazı SO dosyaları yok. Libopencv_ ile başlayan bazıları var. (2.x lilerde libopencv_java.so ve 3.x lilerde libopencv_java3.so geçerli.) Örneğin objdetect nesnesinde bir metod kullanacaksınız ama 2.4.9 da var bunda yok ya da var ama kullansanız da yavaş kalıyor vb.
Bu durumda kaynak kodlarında modules dizinlerinde C++ kaynak kodlar var. Onları uygun şekilde derleyecek ve SO dosyasını oluşturacaksınız.
Ben bana lazım olan bazı metodları bu kaynak kütüphaneden C++ olarak saatlerce araştırarak AŞIRDIM. Kendi C++ yazılımımın içine koydum. Sanıyorum 3-5 tane metod işime yaradı. Sonra yazdığım 6-7 tane C++ CPP ve HPP kodunu komut satırından ndk-build ile derledim. Bakın şöyle yaptım:
Öncelikle File-New-import Module ü seçin gelen ekranda OpenCV yi install ettiğiniz dizinin içinde sk altında Java dizini vardır. Onu seçin. Gerekli işleri Android Studio yapacaktır. Projeniz de şimdi bunu göreceksiniz. Bu yetmez bir iş daha yapmanız gerekir.
Android Projeniz de File dan project Structure a gelip oradan gelen ekranda solda app nizi seçerseniz yukarıda dependency sekmesi var orada sağdaki artı + ya dokunup module dependency yi seçip gelen ekranda zaten OpenCV320 vb olacaktır seçin o kadar.
D veya C sürücüsünde bir dizin açın. O dizinin içinde Jni uzantılı bir dizin daha açın. Oraya C++ kaynak dosyalarınızı koyun. Sonra Android.mk ve Application.mk yı yazın. Bunlar derleyici tarafından okunup ona göre derleme yapacağı için lazım. Internette örnekleri var bulabilirsiniz. Belki ben size yazıp satır satır anlatırım içindekileri. Application.mk zaten en fazla 4 satır. Android.mk biraz dikkat gerektiriyor. Evet bunlar tamam. O zaman oluşturduğunuz dizinden (altındaki jni den değil) ndk-build.cmd yazıp enter e basın. (ndk-build tanınmadı derse o zaman ya ndk yüklü değil ya da path de belirtmediniz yerini bunu yapın)
Bir baktınız libs ve obj isimli dizinler oluşturdu ve Application.mk da abi_filter da belirttiğinize göre arm dizinlerini de oluşturup hem sizin so file ınızı hem de diğer gerekli so file ları oraya getirdi. Süper değil mi?
Şimdi bunları android projenizin altında JniLibs içindeki arm (armeabi-v7a, arme64-v8a vb) dizinlere kopyalayın. Proje içinde de System.Loadlibrary komutu ile yükleyin güle güle kullanın MI dersiniz? Bir kaç şey daha yapmanız lazım. Oradan çağıracağınız yani C++ ile yaptığınız ve SO olan dosyadan çağıracağınız metodunuzu public native — ile reference olarak belirtmeniz lazım tek satırla yani. Hepsi tamam bir çalıştırdınız ve LogCat penceresinde hatayı gördünüz (Runtime I kastediyorum) diyor ki UnsatisfiedLink (diğerleri fasa fiso) işte o zaman bakın ne hata yaptınız. Ki başınıza daha önce gelmiştir. Bir yerden aşırdığınız bir SO file ile bu yüzden çalışamadınız değil mi.?
Şöyle : C++ kodlarınız JNI interface I aracılığı ile Android Studio ile çalışır derlenir. Yani JAVA ile C++ JNI destekli bir yapı varsa birbirleri ile çalışır. O zaman C++ kodlarınızın başında belirttiğiniz JNI tanımları BURAYA DİKKAT (bir yerden hatırladınız mı) sizin android projenizdeki proje isminiz vary a hani package name olarak geçer işte onunla uyumlu ve aynı olmalı. Sadece android studio da ornegin org.selcukcelik.lanedetection ise C++ kodlarınızda örneğin :
JNIEXPORT void JNICALL Java_org_selcukcelik_lanedetection_LaneDetector_mainDelegate
OLMALI. Yani dikkat edin alt çizgili C++ tarafında. İşte bu uyumlu olursa UnsatisfiedLink hatası GELMEZ.
Çalıştırdınız ve C++ tarafta hata varsa hemen o tarafta değişiklik yapıp tekrar derleyip SO dosyasını tekrar projeye copy-paste yapın.
Çalıştırdığınızda performansı açık şekilde göreceksiniz.
Bunun dışında genelde bir Android Studio projesi yapıldığında basit bir native-lib.cpp açar ve siz orada kodları ki zaten yazılmış basit bir parça var onu derlediğinizde bir SO file elde edersiniz. Benim anlattığım daha ileri ve Pratik bir yaklaşım.
SymLink denilen bir olay daha var. OPENCV deki Jni icindeki library ler ki armeabi-v7a gibi dizinler içinde olur onları otomatik projenize gerekirse illa kopyalamanız gerekmez. Komut satırından projenizin app/src/main dizinine gidin ve komut satırından mklink komutu ile sanal bir dizin oluşturun. Öğrenin nasıl yapıldığını. Proje de bir bakarsınız jniLibs oraya gelmiş otomatikman.
Kısaca bu işlere dalacaksanız sadece IOS veya Android de kod yazmayı değil bir çok konuyu çok iyi bilmeniz faydalı olacaktır. Hız ve performansı düşünmeniz gerekir.
Çok fazla hata mesajı ile karşılaşabilirsiniz bazıları çok garip hatalar ve sizi yanıltabilir.
Şimdi temiz bir havada araç ile dışarı çıkıp tam bir test yapmanın zamanı. Sonraki aşama önümüze çıkan engelleri araç sürerken bize bildirmesi (yavaşlama) ve öndeki araç ile mesafe koruma vs vs.
Eğlence devam ediyor. Sağlıcakla kalın.
Selcuk Celik