Python, OpenCV ile Cascade kullanarak Yüz Tanıma

Image by Microsoft {link to https://blogs.microsoft.com/on-the-issues/2018/12/06/facial-recognition-its-time-for-action/}


Python, OpenCV ile cascade kullanarak araba tanıma uygulamalarından sonra sadece kendi yüzümü tanıyan ve aynı zamanda çevredeki diğer insanların yüzlerini de algılayıp 'unknown' olarak gösteren bir uygulamanın nasıl yapılacağını anlatıp, yaparken karşılaştığım sorunlara değineceğim.

Face Detection vs Face Recognition

Öncelikle, ilk deneyimlediğim 'object detection' uygulamalarından (Haar Cascade ile Araba Tanıma) yararlanarak bu uygulamayı gerçekleştirmeye başladım. Fakat araştırma yaptıktan sonra iki uygulamanın farklı uygulama alanları olduğunu gördüm. 'object detection' genelinde 'face detection' uygulamaları sadece genel bir yüz tanımı için  'cascade' dosyalarını kullanarak insan yüzünün genel özelliklerinden faydalanıp (örneğin: göz ve burunun birbirine göre konumları vs.) verilen bir frame'de bulduğu yüzün konumunun koordinat bilgilerini verirken 'face recognition' uygulamaları yüz tanıma işlemini sizin tanıttığınız ve tespit etmek istediğiniz yüzü birden fazla yüzle birlikte karşılaştırıp hangi yüzün sizin aradığınız yüzle aynı oluğunu 'embedding vector'ler kullanarak belirliyor. Ayrıca, OpenCV'nin 'face recognition' için geliştirilmiş farklı tool'ları da var.

Kısacası, frame'de herhangi bir yüz olduğunu tespit etmek için 'face detection', benim yapmak istediğim uygulama gibi spesifik bir yüzü (kendi yüzünüz) tespit etmek için 'face recognition' kullanılmalı. Burada, 'detection' konsepti kullanarak uygulamayı nasıl gerçekleştirebilirim ve bu uygulamanın başarısı ne olur düşünceleriyle yine aynı araba tespit etme uygulamasında olduğu gibi cascade dosyamı 'Cascade Trainer GUI' programıyla oluşturup kendi yüzümü tanıyan ve eş zamanlı diğer yüzleri de tanıyıp 'unknown' olarak etiketleyen bir uygulama yaptım. Bir sonraki aşama olarak aynı uygulamayı 'face recognition' konseptini kullanarak yapmayı planlıyorum.

Positive Image'ları Elde Etme

İlk olarak, positive image'ları yani yüzümüzü içeren fotoğrafları elde etmemiz gerekiyor. 200'e yakın positive image bulurken zaman kaybı olmaması için de webcam'den kayıt alan ve alırken her 30 frame'den bir tanesini kaydeden bir program yazdım.

Bu programı yazarken başarısız olduğum ilk nokta OpenCV kütüphanesinin sağladığı cap.set(cv2.CAP_PROP_FPS, fps = 10)metodunu kullanarak kameranın fps ayarlarını değiştirmek oldu. Fakat bunu kullanmak herhangi bir değişiklik göstermedi. Bunun nedenini araştırdığımda çoğu bilgisayar kamerasının default frame rate'inin(30) değiştirilmesine izin vermediğini gördüm. Daha detaylı bilgi için buraya bakabilirsiniz. Ayrıca bu linkte çözüm olarak sunulan: 'time' kütüphanesi kullanarak fps'yi saniyede 1 kareyle sınırlamak, webcam frame toplamak için çalıştığında kesik kesik görüntü vereceğinden işe yarar bulmadım.(Benim istediğim kamera normal akışındayken saniyede 1 kare toplamasıydı). Bunun için yazdığım kodda, döngünün içinde frame'leri olduğu gibi alıp(kamera default fps-30- ayarındayken), 30 frame'den sadece birisini kaydediyor. Böylece hem kamera yavaşlamıyor, hem de saniyede 1 kare toplama amacına ulaşıyorum.

Bu şekilde 200 renkli positive image elde etmiş oldum. Sonrasında, daha önce bahsettiğim, bir klasördeki renkli dosyaları gri tona çevirip kaydeden kodu kullanarak positive image'ları train için hazır hale getirdim (öyle sandım).

Burada başarısız olduğum nokta webcam'den çekilen image'ların sadece benim yüzümü değil odayı da içermesi. Dolayısıyla yüz tanıma için oluşturulan cascade dosyasını denediğimde benim yüzümü çerçeve içine almak yerine benim yüzümle birlikte odanın büyük çoğunluğunu da çerçeve içine alıyor, oda değiştirdiğimde ya da kameraya yaklaştığımda neredeyse hiç tanımıyordu. Bu yüzden gri fotoğrafların hepsinde sadece kendi yüzümün olduğu kısımları teker teker kare formatında kestim. Bu sefer uygulama başarılı şekilde çalıştı ve kendi yüzümü tanıyabildi.

Fotoğrafların hepsinde yüzümün olduğu kısmı teker teker kesip kaydetmek çok sıkıcı ve aşırı zaman alıcı bir işlem. Bunu nasıl otomatik hale getirebilirim diye düşününce zaten yüz tanıma(face detection) konusunda çok başarılı cascade dosyalar halihazırda var. (haarcascade_frontalface_default.xml) Bu cascade'i kullanarak yüzümü içeren 200 fotoğraflık bir klasördeki her fotoğrafı tek tek alıp içinde bulduğu yüzlerin koordinatlarını kullanarak yüzleri kesip başka bir klasöre kaydeden program fikri aklıma geldi. Büyük bir zahmetten kurtaracak. Daha gerçekleştiremedim ama yapıp bitirince burayı güncelleyeceğim.

Aslında son dediğim otomatik olarak fotoğrafları kırpma güncellemesini de eklersek buraya kadar bir çok şeyi otomatikleştirmiş olduk:
1. Tanınacak yüzün webcam aracılığıyla fotoğraflarını elde etme. (getFrames.py)
2. Fotoğrafları gri tona çevirme. (convertGray.py)
3. Elde ettiğimiz gri tonlu fotoğraflardan yüzleri kırpma.
Bunlardan sonra positive image'lar training aşaması için hazır hale geliyor.

Negative Image'lar Nasıl Seçilmeli?

Negative image seçimine gelince bu fotoğrafların nasıl seçilmesi gerektiği konusunda bulduğum tek görüş negative image'ların tanımak istediğimiz objeyi içermemesi. Araştırma yaptıktan sonra bu görüşten başka bir şey bulamadım. Negative image'ların seçimi cascade'in başarısını etkiliyor mu? Verilen bir positive image'dan negative image'lar üretilebilir mi? Negative image'lar neye göre seçilmeli? Kendi yüzümü tanıyan bir uygulama için sadece kendi yüzümün olmadığı fotoğraflar seçmem yeterli mi yoksa negative image için başka insan yüzleri kullanmak tanımadaki başarı oranını arttırır mı? (Araba tanıma uygulamalarında boş yol fotoğrafları daha uygun bir seçim midir?) Bu soruların cevaplarını hala bulamadım.Stack Overflow'da sorduğumda da uygun cevabı alamadım.

Sonuçta bir şey bulamadığım için kendim test etmek istedim. Negative image olarak önceden araba tanıma uygulaması için kullandığım negative image'ları ve ünlülerin sadece yüzlerini içeren bir dataset kullandım. Bunları kullanarak 3 farklı 1000 fotoğraflık negative image klasörü oluşturdum: 1000 random, 1000 yüz, 500 random-500 yüz. Üçünü de denediğimde hepsi doğru çalışmasına rağmen gözle görülür bir fark yoktu.

Uygulama

Positive-Negative image'ları topladıktan sonra cascade dosyasını oluşturabiliriz. Sadece yeni oluşturulan cascade dosyasını kullanarak araba tanıma uygulamasındaki kodun aynısını uygulayıp kendi yüzümüzü tanıyan bir uygulama yapmış oluyoruz.

Başka insan yüzlerini de tanıma kısmına gelince, sadece yüz algılaması(face detection) yeterli olacağından yukarıda linkini verdiğim 'face detection için cascade' dosyasını kullandım. Fakat frame'de bulduğu yüzlerin koordinatlarını döndüren iki farklı 'for' döngüsü(biri benim yüzüm, diğeri unknown'lar için) kullanınca; unknown'lar için kullanacağım cascade, frame'deki tüm yüzleri tanıdığı için benim yüzümde 'arda' ve 'unknown' etiketiyle iki çerçeve oluştu.

Bu yüzden kodu değiştirerek: frame'de bulduğu yüzlerin koordinatlarını döndürürken eğer hala benim yüzüme denk gelmemişse, diğer döngüye girip frame içindeki 'arda'nın koordinatlarını alarak ('arda' yoksa zaten döngüye girmiyor) iki koordinat bilgisini(faces ve ardas'dan aldığı) findProp methodunda parametre olarak kullanıyor. Bu methodun algoritması ise: verilen koordinatlar iki dikdörtgen çiziyor. Eğer faces'in döndürdüğü yüz benim yüzümse, iki dikdörtgenin kesişimi minimum yüzde 80 olacaktır (kesişim alanının, iki dikdörtgenin alanlarının ortalamasına oranını test ederken benim yüzümü tanıdığında bu oranının genelde yüzde 85-95 olduğunu tespit ettim.) Eğer faces'de döndürülen benim yüzüm değilse zaten kesişimlerinin alanı ya 0 olacak ya da yüzde 80'den daha az bir kesişim alanına sahip olacaktır. Benim yüzümü bulduktan sonra da ardas döngüsüne bir daha girmeyecek. Böylece kendi yüzümle diğer yüzleri ayırmış oldum. (Kod için: OwnFaceDetector.py)




Yorumlar