OpenCV ile Yüz Tanıma Bölüm 3-1
Bu yazımda 3 Yeşilçam emektarının fotoğraflarını içeren küçük bir veri setini oluşturmak için gerekli işlemleri ele alacağım. Eğitim ve sınıflandırma işlemleri ise bir sonraki yazımın konusu olacak.
Orijinal kodları yine PyimageSearch web sitesinden edinmek mümkün. Adrian, anime karakterleri kullanmış. Ben ise Yeşilçam karakterlerini tercih ettim. Ayrıca kodları biraz daha basitleştirdim ve gerekli yerlerini Türkçeleştirdim.
Veri seti için Google üzerinden ulaşabildiğim fotoğrafların sayısı hiç ummadığım kadar düşük çıktı. Oysa çok fazla fotoğrafa ulaşabileceğimi umuyordum. Bu proje için 3 sanatçımızın toplam 147 farklı fotoğrafına ulaşabildim. Sayının azlığına rağmen sınıflandırma değerleri fena değil.
Projemizde 4 ayrı betiğimiz var:
1- Dosyadaki linklerin okunup, ilgili imaj dosyalarının internetten indirilip kaydedilmesi
2- Kaydedilen dosyaların denetlenerek, içeriği aynı olan dosyaların teke düşürülmesi
3- Veri setinin eğitilmesi
4- Eğitilmiş veri seti bilgilerini kullanarak örnek dosyaların sınıflandırılması
VERİ SETİMİZİ OLUŞTURMAK
Sanatçılarımız: Türkan Şoray, Sadri Alışık ve Cüneyt Arkın. Amacım sadece çalışma yöntemini örneklemek olduğu için sayıyı daha fazla arttırmadım. Belki ileride, bu veri setini genişletip çok daha zengin ve işlevsel bir projeye dönüştürmek mümkün olabilir.
1. ADIM – VERİ SETİNİN OLUŞTURULMASI
İnternet üzerinden ulaştığım görsel linklerini, “linkler.txt” isimli bir metin dosyasına kaydettim. Bu dosyayı linkler.txt adresinden indirebilir veya tamamen kendiniz oluşturabilirsiniz.
Dosya yapısı şöyle:
sanatçı_kodu,link1
sanatçı_kodu,link2
…
Sanatçı kodları aynı zamanda data klasörünün içindeki klasörlerin de adlarıdır. O nedenle kodlarda Türkçe karakter kullanmadım. Kodlar ve klasör isimleri şu şekildedir:
c_arkin
s_alisik
t_soray
Betik kodlarını kısa tutmak için data klasörünü ve onun altındaki klasörleri kendim oluşturdum.
Çalışma klasörümüzün adı “yesilcam”. Alt dizin yapısı da şu şekilde:
yesilcam
|–data
| |–c_arkin
| |–s_alisik
| |–t_soray
|–kontrol
|–__pycache__
Önce ilk betiğimizi (resim_indir.py) inceleyelim:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
import cv2 from skimage import io filename='linkler.txt' ok=0 hata=0 kac={} with open(filename) as f: for sat in f.readlines(): sat = sat.strip() if len(sat)>3: if sat[0]=='#':continue ss=sat.split(',') kim=ss[0] url=ss[1] if len(ss)>2:url+=ss[2] try: print(kim,url) if kim not in kac.keys(): kac[kim]=1 image=io.imread(url) image=cv2.cvtColor(image,cv2.COLOR_RGB2BGR) cv2.imshow('image',image) if cv2.waitKey(1) & 0xff == 27: break ok+=1 # kaydet while True: if kac[kim]>100:break try: dosya=f"data/{kim}/{kac[kim]:04}.jpg" cv2.imwrite(dosya,image) kac[kim]+=1 break except Exception as e: kac[kim]+=1 print(kac[kim]) except: hata+=1 print("hata: ",hata) cv2.destroyAllWindows() |
skimage kütüphanesi, imaj dosyalarımızı doğrudan internet üzerinden okuyabilmek için gerekli. Ancak bu kütüphane RGB renk sistemini kullanıyor. Bu yüzden 22 nolu satır ile renk sistemini OpenCV’nin kullandığı BGR sistemine çeviriyoruz.
Linkleri tutan dosyamızın adı “linkler.txt”.
Sorunsuz okunan dosya sayısını tutmak için ok, sorunluları saymak içinse hata değişkenlerini kullanıyoruz.
kac sözlük değişkeni, her sanatçı için dosya isimlerini ayrı ayrı sayılaştırmamızı sağlıyor.
Satırları tek tek okuyoruz. # ile başlayan veya boyu 3 karakterden kısa olan satırları atlıyoruz.
İşlenecek satırları “,” ile parçalıyoruz. İlk parçada sanatçı kodu bulunuyor. Normalde ikinci parçanın link adresini tutması lazım. Ancak, sayıları az olsa da, bazı link adreslerinde “,” işaretinin kullanıldığına rastlayabiliriz. Bu nedenle -eğer varsa- 3. parçayı 2. parçaya ekleyerek doğru adresi elde ediyoruz.
17. satırda başlattığımız try bloğu, hatalı okumalardan kaçınmamızı sağlıyor.
kim değişkeni sanatçı kodunu, url ise imaj adresini saklıyor.
kac anahtarları içinde kim stringi yoksa, ilk kez tanımlanacak demektir. Başlangıç değeri olarak 1 atıyoruz.
21. satırda dosyamızı internet üzerinden okuyor; 2. satırda renk sistemini BGR haline çeviriyor ve 23. satırda okuduğumuz imajı görselleştiriyoruz.
24. satırdaki tuş kontrolü, gerektiğinde, Esc tuşuna basarak döngüyü kırabilmemizi sağlıyor. Bu satır aynı zamanda görselleştirme işlemleri için de gerekli.
27-36 satırlarını kullanarak dosyayı data klasörü altındaki sanatçı klasörüne (kim) kaydediyoruz. Dosya isimlendirmesinde bir çakışma olması ihtimalini dikkate alıyor, ama bu sayının 100’den fazla olmasına izin vermiyoruz. (Dikkat! Eğer kendi veri setinizi oluşturacaksanız, kullanacağınız link sayısını dikkate almak için bu sınırı değiştirmeniz gerekebilir.)
41. satırda son işlem olarak bilgisayar belleğini temizliyoruz.
İmajlar farklı boyutlarda olduğu için işlenmeleri farklı süreler gerektirebiliyor.
(Not: Çalışmalarımı Pycharm üzerinde yapıyor ve çalıştırıyorum. Ancak bu betiği terminal üzerinden çalıştırmak zorunda kaldım. Aynı durum sizin başınıza da gelebilir. Betiği çalıştırmak için Pycharm’ın terminal modunu kullanabilirsiniz.)
2. ADIM – İMAJ TEKRARLARININ ÖNLENMESİ
Bu betiğimizde doğrudan md5 hesaplamak yerine imagehash kütüphanesindeki dhash() metodunu uyguluyoruz. Bu metod hash hesabını imajları belli bir boyuta indirip gri renge çevirdikten sonra yapıyor. Yeniden boyutlandırma biraz zaman alıyor olsa da sonuçlar daha güvenilir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
""" imaj_dup_check.py aynı klasördeki aşırı benzer dosyalar temizleniyor """ import os from PIL import Image import imagehash def hash(f): image = Image.open(f) return str(imagehash.dhash(image)) def tekrar_sil(path): if not os.path.isdir(path): print('Belirtilen klasör mevcut değil!') else: hash_dict = {} for root, dirs, files in os.walk(path): for f in files: if not hash(os.path.join(root, f)) in hash_dict: hash_dict.update({hash(os.path.join(root, f)): [os.path.join(root, f)]}) else: hash_dict[hash(os.path.join(root, f))].append(os.path.join(root, f)) for key in hash_dict: while len(hash_dict[key]) > 1: for item in hash_dict[key]: print(item," siliniyor...") os.remove(item) hash_dict[key].remove(item) print('Tamam!') if __name__ == '__main__': tekrar_sil("data") |
İkinci betiğimizde os, PIL ve imagehash kütüphanelerini kullanıyoruz.
hash() fonksiyonumuzda okuma işlemini PIL.Image modülü ile gerçekleştiriyor ve elde edilen imaja imagehash.dhash() işlemini uyguluyoruz. (Doğrudan md5 hesabı yaptığımızda birbirine çok benzer bazı imajlar gözden kaçabiliyor.)
Tekrarlanan imaj dosyaları tekrar_sil() fonksiyonunun içinde temizleniyor.
19. satırda path değişkenimizle aktarılan klasör yolu bir döngü yapısı içinde kök, klasör ve dosya bileşenlerine ayrıştırılıyor ve her bir dosya için hash() değeri hesaplanıp, hash_dict sözlük değişkeninde saklanıyor. Eğer eşdeğer hash değeri (key) bulunursa, aynı anahtarın karşılığına mükerrer dosyanın adı ekleniyor. Böylece len(hash_dict[k]) değeri birden büyük olan kayıtlar, mükerrer dosyaları tutuyor.
25-30 satırları mükerrer dosyaların silinmesini sağlıyor.
34. satırda data klasörünü işleme sokuyoruz. Böylece onun altındaki tüm klasör ve dosyaları taramış oluyoruz.
Sizi daha fazla yormamak ve veri setini hazırlamanız zaman tanımak için bu yazıyı burada kesiyorum. Bir sonraki yazımda, oluşturduğumuz veri setini önce eğiteceğiz, sonra da eğitim setinde yer almayan kontrol görsellerinin kime ait olduğunu sorgulayacağız.
Beni izlemeye devam edin.
Ahmet Aksoy
Referanslar: