Python'da OpenCV ile Yüz Tanıma

Kayıt Tarihi:

Son Güncelleme:

Özet:

Bu yazımda yapay zeka uygulamalarında sıkça karşılaştığımız yüz tanıma işlemine basit bir örnek vereceğim. Bu yazıda Python ile meşhur OpenCV (Open Source Computer Vision) kütüphanesi kullanımına giriş yapmış olacaksınız. Bu kütüphanenin içerdiği hazır makine öğrenmesi modellerini kullanarak resim ve videolardaki yüz ve gözlerin nasıl tespit edildiğini öğreneceksiniz.

Anahtar Kelimeler: eye detection · face detection · face recognition · göz tespiti · Haar cascade classifier · LBPH · makine öğrenmesi · numpy · OpenCV · python · yapay zeka · yüz tanıma · yüz tespiti

Bu yazımda size Python ile bir makine öğrenmesi modeli örneği vereceğim. Günümüz teknolojisinde resim işleme ve analiz etme problemi oldukça önemli bir yere sahiptir, bilgisayarlar verilen resimleri analiz ederek bazı sorulara cevap verebiliyor. Mesela araç resimlerinden plaka numaralarını okuyabiliyor, resimlerdeki insan yüzlerini tespit edebiliyor hatta kime ait olduklarını bile tahmin edebiliyor. Yazılımların bunları nasıl yapabildiğini açıklamak çok uzun sürer ama mantığını kısaca özetleyeyim.

Klasik yazılım yapısı şöyledir; siz bir algoritma tasarlarsınız ve bunu bir programlama dilinde kodlarsınız, daha sonra programınıza girdi olarak bir veri girersiniz ve program algoritmanızı takip ederek size istediğiniz cevabı verir. Yapay zeka uygulamalarında ise mantık şöyledir; programa bol miktarda veri ve bunlara ait cevapları girersiniz, program da bu eşleşmeleri analiz ederek algoritmayı kestirmeye çalışır. Bu kestirimden sonra artık girdiğiniz yeni verilere ilişkin cevapları tahmin eder. Yani makine sorun çözmeyi öğrenmiş olur, bu öğrenme işi oldukça tekniktir (training aşaması denir) ve çeşitli yöntemleri vardır. Şimdi bu konuya örnek olarak yüz tespit etme ve tanıma olayını açıklayayım.

İlk olarak Intel firması tarafından başlatılan, daha sonra başka firmaların katılımıyla geliştirilen ve hala aktif olarak geliştirilmeye devam eden OpenCV (Open Source Computer Vision) kütüphanesi günümüzde bilgisayar grafikleri üzerinde gerçek zamanlı analiz yapmak için yaygın olarak kullanılan bir kütüphanedir ve Python da dahil olmak üzere çeşitli dillerde çalışmaktadır. Bu kütüphane ile Python programlarımızın resimler üzerindeki nesneleri tespit etmesini ve tanımasını sağlayabiliriz, örneğin insan resimlerindeki yüzleri ve gözleri. Bu yazımı hazırlarken OpenCV'nin 4.1.2 versiyonunu kullandım, başka versiyonlarda söz dizimi farklı olabilir.

P. Viola ve M. Jones adındaki araştırmacılar 2001 yılında yayınladıkları bir araştırmalarında resimlerdeki yüzleri tespit etmek için oldukça verimli bir yöntem bildirmişler, bu yöntem günümüzde de yoğun olarak kullanılıyor. Haar cascade classifier olarak bilinen bu yöntem ile ilgili detaylı bilgi şu sayfada bulunabilir, aşağıda vereceğim örnekte kullandığım her komutu detaylı olarak açıklamayacağım. Kodlar üzerine yeterli açıklama düşeceğim ama teknik detaylar için OpenCV dokümantasyonlarını araştırabilirsiniz. OpenCV kütüphanesinde bu modele ilişkin önceden eğitilmiş (pretrained) veriler geliyor ve kendiniz training yapmadan doğrudan bunları kullanarak yüz tespiti yapabiliyorsunuz.

Örneğe başlamadan önce yapacağımız hazırlıklar var, Python 3 ile çalışacağız ve python3 komutu Python3 konsolunu çalıştıracak şekilde anlatıyorum. Öncelikle sisteminizden izole bir Python ortamınızın olması faydalı olacaktır, bunun için terminalde çalışacağınız klasöre gidip python3 -m venv faceproject yazıp bir Python3 virtual environment oluşturun (virtualenv kütüphanesinin yüklü olduğunu var sayıyroum, yoksa pip3 install virtualenv.) Sonra bu izole ortamı aktifleştirmek için source faceproject/bin/activate komutunu girin, terminalde satırların başında parantez içinde ortam isminin görünüyor olması gerekiyor. Daha sonra pip install --upgrade pip yazarak pip yazılımını güncelleyin. Şimdi ihtiyacımız olan kütüphaneleri yükleyin, OpenCV yüklemek için pip install opencv-contrib-python komutunu girin, sisteminizde yüklü değilse NumPy paketini de yükleyin (pip install numpy).

Şimdi bir resim verildiğinde bu resimdeki yüzleri ve gözleri (varsa) tespit edip işaretleyen bir program yazalım. Aşağıdaki kodları inceleyin, üzerinde bazı açıklamalar var.


import cv2, sys

#Haar cascade classifier yukle
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')

img_path = sys.argv[1] #dosya yolunu al
img = cv2.imread(img_path) #dosyayi oku
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #siyah-beyaz yap

#yuzleri bul
faces = face_cascade.detectMultiScale(gray, 1.3, 5, minSize = (130,130))

#yuzleri isaretle
for (x,y,w,h) in faces:
    img = cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
    roi_gray = gray[y:y+h, x:x+w]
    roi_color = img[y:y+h, x:x+w]
    #gozleri bul
    eyes = eye_cascade.detectMultiScale(roi_gray, minSize=(50,50))
    #gozleri isaretle
    for (x1,y1,w1,h1) in eyes:
        cv2.rectangle(roi_color, (x1,y1), (x1+w1,y1+h1), (255,0,0),2)

cv2.imshow('img',img) #ekranda
cv2.imwrite("output_detected.jpg", img) #kaydet
cv2.waitKey(0) #tusa basinca cik
cv2.destroyAllWindows()

Burada önce gerekli kütüphaneleri import ettikten sonra Haar cascade classifier dosyalarının yerini belirtiyoruz. Sonra programa verilen resim dosyasını OpenCV ile okuyup siyah-beyaz yapıyoruz, yüz tanıma işlemi renkli grafiklerle değil siyah-beyaz grafiklerle çalışır. Daha sonra da detectMultiscale fonksiyonu ile resimdeki yüzlerin yerlerini tespit ediyoruz, minSize parametresi ile yüze benzeyen çok küçük bölgeleri görmezden geliyoruz. Bu fonksiyon yüzlerin bulunduğu koordinatları içeren bir array nesnesi döndürür. Sonra bir döngü içinde bulunan yüzler işaretleniyor ve her bir yüz içinde gözler de tespit edilip işaretleniyor, dikdörtgen çizme işlemi OpenCV modülünün rectangle fonksiyonu ile yapılır. Son olarak çıktı resmi ekranda gösteriliyor ve programın sonlanması için kullanıcının herhangi bir tuşa basması bekleniyor. Bu programı face_detect_img.py olarak kaydedin, daha sonra argüman olarak aynı klasördeki bir resim dosyasının ismini vererek çalıştırın. Mesela ben python3 face_detect_img.py hababam.jpg olarak çalıştırdım ve aşağıdaki sonucu aldım (resim göründükten sonra klavyeden herhangi bir tuşa basarak programdan çıkabilirsiniz).

Benzer işlemi video dosyalarında da yapabilirsiniz, bu durumda videodaki her bir frame için yukarıdaki işlem tekrarlanır ve sonuçta video üzerindeki işlemlerle geri döndürülür. Aşağıdaki kodlarda bu işlem açıklanıyor.


import cv2, sys

#Haar cascade classifier yukle
faceCascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
vid_path = sys.argv[1] #dosya yolunu al
video_capture = cv2.VideoCapture(vid_path) #dosyayi okumak icin

while True:
    ret,frame = video_capture.read() #video frameleri oku
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #frameleri siyah-beyaz yap
    faces = faceCascade.detectMultiScale(gray, 1.1, 5, minSize=(100,100)) #yuzleri bul
    for (x,y,w,h) in faces: #yuzleri isaretle
        cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0) ,2)

    #esc basinca cik
    cv2.imshow('Video', frame)
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
	
video_capture.release()
cv2.destroyAllWindows()

Bunu da face_detect_vid.py olarak kaydedin ve aynı klasörde yer alan bir video ile deneyin, aşağıdaki gibi bir sonuç alırsnız. Video oynatıldıktan sonra ESC tuşuna basarak programdan çıkabilirsiniz.

Aynı işlem kayıtlı bir video yerine kameranızdan gelen anlık görüntü üzerinde de yapılabilir, aşağıdaki kodları inceleyin.


import cv2

#Haar cascade classifier yukle
faceCascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

#kamera okumak icin
video_capture = cv2.VideoCapture(0)

while True:
	ret,frame = video_capture.read() #frame oku
	gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #siyah-beyaz yap
	faces = faceCascade.detectMultiScale(gray, 1.1, 5, minSize=(100,100)) #yuzleri bul
	for (x,y,w,h) in faces: #yuzleri isaretle
		cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0) ,2)
		cv2.putText(frame, "insan", (x,y+h+20), cv2.FONT_HERSHEY_DUPLEX, .5, (0,255,0))
	
	#esc ile cik
	cv2.imshow('Video', frame)
	k = cv2.waitKey(1) & 0xFF
	if k == 27:
		break
	
video_capture.release()
cv2.destroyAllWindows()
Bunu face_detect_cam.py olarak kaydedin ve argümansız olarak çalıştırın. Ekrana kamera görüntünüz üzerinde yüzünüz işaretli olarak görünecektir. Dikkat ederseniz bu örnekte putText fonksiyonu ile ekrana dikdörtgen dışında yazı da yazdık.

Bu işlemi geliştirmek sizin elinizde; örneğin dikdörtgenlerin boyutlarını kıyaslama aracı olarak kullanıp kişilerin kameraya olan uzaklıklarını veya gözler arasındaki mesafeyi kullanarak kişilerin boylarını tahmin etmeye çalışabilirsiniz. Daha önemli bir geliştirme yüzlerin kime ait olduklarını tahmin etmek olabilir. Bu işlem oldukça karmaşık gelebilir ama OpenCV içinde bunun için de basit fonksiyonlar var, sadece training işlemini kendimiz yapacağız. OpenCV içinde bunu yapan farklı fonksiyonlar var, biz LBPHFaceRecognizer_create fonksiyonunu kullanacağız. Bunu yapmak için bir yüz veritabanı oluşturalım, aynı klasöre yeni bir face_db klasörü açıp içine her kişi için birer klasör oluşturun. Mesela ben Dikiştutmaz Sabri ve Arnavut için birer klasör oluşturdum, klasör isimleri kişi isimleri olmalı. Sonra da her birinin için ilgili kişilerin bazı resimlerini koyun.

Buradaki resimleri programa train ettireceğiz ve girdideki yüzler ile bunlarda bulunan yüzlerin karşılaştırılmasını isteyeceğiz, sonuç olarak tahminler ekrana yazdırılacak. Aşağıdaki kodları inceleyin.


import cv2, sys, os
import numpy as np
from math import ceil

#Haar cascade classifier yukle
faceCascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
vid_path = sys.argv[1] #dosya yolunu al
video_capture = cv2.VideoCapture(vid_path) #dosyayi okumak icin
recognizer = cv2.face.LBPHFaceRecognizer_create()
min_confidence = 10

#yuz veri setini oku
def get_data():
    images = []
    image_label = []
    names = []
    cd = os.getcwd()
    dataset_dir = os.path.join(cd, 'face_db')
    folders = os.listdir(dataset_dir)
	
    for i in range(len(folders)):
        names.append(folders[i])
        wd = os.path.join(dataset_dir,folders[i])
        folder_imgs = os.listdir(wd)
        for j in folder_imgs:
            im = cv2.imread(os.path.join(wd,j), 0)
            faces = faceCascade.detectMultiScale(im, 1.1, 5, minSize=(50,50))
            for (x,y,w,h) in faces:
                im_arr = np.array(im[x:x+w,y:y+h],'uint8')
                images.append(im_arr)
                image_label.append(i)
	
    cv2.destroyAllWindows()
    return images, image_label, names

#training
image_data, labels, names = get_data()
recognizer.train(image_data, np.array(labels))

while True:
    ret,frame = video_capture.read() #video frameleri oku
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #frameleri siyah-beyaz yap
    faces = faceCascade.detectMultiScale(gray, 1.1, 5, minSize=(100,100)) #yuzleri bul
    for (x,y,w,h) in faces: #yuzleri isaretle
        cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0) ,2)
        test = gray[x:x+w,y:y+h]
        test_img = np.array(test,'uint8')
        if(test_img.any()):#frame icinde yuz varsa tahmin et
            index, confidence = recognizer.predict(test_img)
        if(confidence>=min_confidence):#tahmini yaz
            cv2.putText(frame,names[index],(x,y+h+20),cv2.FONT_HERSHEY_DUPLEX,.5,(0,255,0))
            cv2.putText(frame,str(ceil(confidence))+"%",(x,y-20),cv2.FONT_HERSHEY_DUPLEX,.5,(0,255,0))

    #esc basinca cik
    cv2.imshow('Video', frame)
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
	
video_capture.release()
cv2.destroyAllWindows()
Açıklamalardan da anlaşılacağı gibi önce basit bir fonksiyon tanımlayarak klasördeki resimleri ve karşılık gelen isimleri (label) alıyorum. Daha sonra training aşamasını OpenCV içinde gelen ilgili fonksiyonla yapıyoruz. Daha sonra döngü içinde her bir yüz için predict fonksiyonunu kullanarak tahminde bulunup ekrana yazdırıyoruz. Bunu face_recog_vid.py olarak kaydedip video yolu argümanıyla çalıştırıp çıktısını görebilirsiniz.

Burada kullandığımız LBPH modeli çok basit bir model ve çoğu zaman yeterince keskin sonuç vermez, örneğin aşağıdaki resimleri inceleyin.

Veri tabanımızı geliştirmek biraz daha iyi sonuçlar almamızı sağlayacaktır elbette. Ama daha keskin sonuçlar için başka modeller kullanmanız gerekebilir, bunlar OpenCV dışında modeller olabilir. Elbette yüksek performanslı modeller biraz daha fazla kaynak isteyecektir, üst düzey işlemci donanımınız yoksa özellikle gerçek zamanlı analizler için biraz yavaş kalabilir. Yüz tanıma konusunda daha başarılı bulduğum modeller hakkında daha sonra ayrı bir yazı yazmayı planlıyorum.

Önceki Blog Yazısı:
Raspberry Pi Kurulumu
Sonraki Blog Yazısı:
Bodoslama JavaScript