OpenCV ile Portakallar ve Elmalar
Bu yazıda işleyeceğim kodların orijinal halini yine PyImageSearch sitesinde bulabilirsiniz. “ball tracking pyimagesearch” sözcüklerini Google’da aratırsanız, konuyla ilgili sayfalara kolayca ulaşırsınız.
Orijinal kodlarda izlediğimiz yuvarlağın (renkli topun) merkez noktasına ait koordinatlardaki değişimler görsel olarak izlenebiliyor. Benim aşağıda vereceğim kodlarda ise merkez noktası yerine, belirlediğimiz renk aralığının grileştirilerek maskelenmiş izdüşümünü ikinci çerçeveden izleyeceğiz.
(Not: Bir sonraki yazımda merkez noktalarının izlenmesi konusunu da ele alacağım. Ama dilerseniz bu kodları geliştirmeyi şimdiden bir ödev olarak üstlenebilirsiniz.)
OpenCV ile nesneleri izlemede kullanılan en basit yöntem, HSV renk sistemine uygun bir renk aralığını kullanmaktır. Çünkü HSV renk sistemi bu tür bir çalışmaya RGB veya BGR renk sistemlerine kıyasla çok daha yatkındır. Eğer renk sistemleriyle ilgili daha çok ayrıntıya ulaşmak isterseniz, internetteki pek çok kaynak size yardımcı olacaktır.
HSV terimi (Hue Saturation Value) sözcüklerinin ilk harflerini birleştirerek oluşturulmuştur. Hue renk tonlarını, saturation doygunluk oranını ve value parlaklık düzeyini ifade eder. OpenCV kütüphanesinde hue değerleri 0-179, diğerleri 0-255 arası değerler alır.
Şimdi kodlarımızı incelemeye başlayalım:
1 2 |
import cv2 import imutils |
Örnek kodumuz için sadece cv2 ve imutils kütüphanelerini kullanacağız.
4 5 6 7 8 9 10 11 12 13 |
video_file = '' # ''ab03.mp4' # if given frames are read from file WIDTH = 600 # width of the windows ONLY_MAX=False # if True only the max circle is drawn GREEN_RANGE = ((29,86,6),(64,255,255)) RED_RANGE = ((139,0,0),(255,160,122)) BLUE_RANGE = ((110,50,50),(130,255,255)) ORANGE_RANGE = ((160,100,47),(179,255,255)) YELLOW_RANGE = ((10,100,100),(40,255,255)) colorLower,colorUpper = YELLOW_RANGE # select color range |
Web kamera görüntülerini kullanmak istiyorsak video_file string değeri boş olmalıdır. Eğer buraya bir video dosyasının erişim adresini yazarsak, web kamera yerine o videonun görüntüleri kullanılır.
WIDTH sabiti, pencerelerimizin genişliğini belirler. İstediğimiz gibi değiştirebiliriz.
ONLY_MAX sabiti False olarak tanımlanırsa, bulunan tüm sınır çizgileri (kontur) dikkate alınır. Bu değer True olarak verilirse, sadece alanı en büyük olan nesnenin çevre çizgisi çizilir.
Renk aralıkları için farklı opsiyonlar tanımladım. Kodu çalıştırmadan önce istediğiniz aralığı seçebilirsiniz. Örneğin YELLOW_RANGE aralığını kullanırsanız alt HSV değerler (10,100,100), üst değerler ise (40,255,255) olur.
15 16 17 18 19 20 21 22 23 |
if len(video_file) == 0: kamera = cv2.VideoCapture(0) # default web camera=0 else: kamera = cv2.VideoCapture(video_file) # read from file cv2.namedWindow('frame') cv2.moveWindow('frame',10,10) # 'frame' window position cv2.namedWindow('mask') cv2.moveWindow('mask',WIDTH+50,10) # 'mask' window position |
video_file değişkenimizin uzunluğu sıfıra eşitse, içinde bir dosya adı yok demektir. Bu durumda web kamerayı etkinleştiriyoruz. İlk web kameranın karşılığı “0”dır. İkinci bir kamera tanımlarsanız, değeri “1” olur.
Örneğimizde yanyana 2 farklı pencere açacağız: frame ve mask. “mask” penceresi, “frame” penceresinin tam sağında yer alacak. Eğer bu yerleştirmeyi yapmazsak, pencereler üst üste açılacak; ikisini birden izleyebilmek için fare ile yerleşimi düzenlemek zorunda kalacaktık. (Not: cv2.moveWindow() parametreleri için kendi bilgisayarınızda biraz farklı değerler vermeniz gerekebilir.)
24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
while True: (ok, frame) = kamera.read() # if filename is given but frames cannot be read then exit if len(video_file)>0 and not ok: break frame = imutils.resize(frame,WIDTH) hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) mask = cv2.inRange(hsv, colorLower, colorUpper) mask = cv2.erode(mask, None, iterations=3) mask = cv2.dilate(mask, None, iterations=3) mask_copy = mask.copy() |
Ön tanımlarımızı yaptıktan ve pencere yerleşimlerini belirledikten sonra asıl işlemlerin içinde yer aldığı sonsuz döngüyü başlatıyoruz.
kamera.read() komutu, web kamera veya video dosyasından hangisini belirlediysek oradan bir çerçeveyi okuyor. Eğer ok değeri False ise ve dosya adı tanımlanmış ise okunacak çerçeve kalmadı demektir. Bu yüzden break komutuyla döngüyü sonlandırırız. Aksi halde işlenmesi gereken bir çerçeve okundu demektir.
Çerçeve boyutlarımızı WIDTH sabitine göre yeniden düzenledikten sonra frame isimli çerçevemizi HSV renk sistemine göre dönüştürüp, hsv isimli değişkene atarız. “frame” isimli çerçevemizin renk sistemi BGR’dir.
hsv isimli çerçeveden colorLower ve colorUpper renk değerlerine göre filtrelenen yeni çerçeveyi bu kez mask değişkenine atarız. Bu çerçeveye önce erode(), sonra da dilate() metodlarını uyguladıktan sonra bir de kopyasını alıp, çerçeve içinde yer alan nesne kenar çizgilerini (konturları) belirlemede kullanmak üzere mask_copy değişkenine atarız. (Not: erode() ve dilate() metodlarını kullanmadan önce mask çerçevemize GaussianBlur() metodu uygulayarak görüntüyü yumuşatmak daha güzel bir görsel etki yaratabilir. Bir sonraki yazımda bu metodu da örnekleyeceğim.)
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
contours = cv2.findContours(mask_copy, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2] if len(contours)>0: for ctr in contours: if ONLY_MAX: cmax = max(contours, key=cv2.contourArea) (x,y), radius = cv2.minEnclosingCircle(cmax) else: (x, y), radius = cv2.minEnclosingCircle(ctr) if radius >= 40: # draw circle if radius>40 px cv2.circle(frame, (int(x), int(y)), int(radius), (0, 255, 255), 4) cv2.imshow("frame", frame) cv2.imshow("mask", mask) key = cv2.waitKey(4) & 0xFF if key==ord('q') or key==27: break |
contours listesi çerçevemizde yer alan tüm konturları içerir.
Eğer kontur listesi boş değilse, hepsini tek tek işleme sokarız.
ONLY_MAX sabitimiz True olarak tanımlandıysa her çerçevede sadece bir tek (en büyük) kontur belirlenir. Aksi halde yarıçapı 40 pikselden büyük olan tüm çemberleri kullanacağız demektir.
Konturlarımızı circle() metoduyla frame isimli çerçevemize işledikten sonra hem orijinal frame isimli çerçevemizi, hem de mask çerçevemizi görüntüleriz.
waitKey() metodumuz 4 milisaniyelik bir süre içinde basılacak tuşları dinler. Eğer ‘q’ veya ‘Esc’ tuşlarından birine basılmışsa, döngünün kırılmasını sağlar.
60 61 62 |
# release all objects and free memory kamera.release() cv2.destroyAllWindows() |
Döngü kırıldığında kamera nesnesini serbest bırakır ve açılmış tüm pencereleri kapatarak belleği temizleriz.
Örnek kodumuzun tamamı aşağıda:
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
import cv2 import imutils video_file = '' # ''ab03.mp4' # if given frames are read from file WIDTH = 600 # width of the windows ONLY_MAX=False # if True only the max circle is drawn GREEN_RANGE = ((29,86,6),(64,255,255)) RED_RANGE = ((139,0,0),(255,160,122)) BLUE_RANGE = ((110,50,50),(130,255,255)) ORANGE_RANGE = ((160,100,47),(179,255,255)) YELLOW_RANGE = ((10,100,100),(40,255,255)) colorLower,colorUpper = YELLOW_RANGE # select color range if len(video_file) == 0: kamera = cv2.VideoCapture(0) # default web camera=0 else: kamera = cv2.VideoCapture(video_file) # read from file cv2.namedWindow('frame') cv2.moveWindow('frame',10,10) # 'frame' window position cv2.namedWindow('mask') cv2.moveWindow('mask',WIDTH+50,10) # 'mask' window position while True: (ok, frame) = kamera.read() # if filename is given but frames cannot be read then exit if len(video_file)>0 and not ok: break frame = imutils.resize(frame,WIDTH) hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) mask = cv2.inRange(hsv, colorLower, colorUpper) mask = cv2.erode(mask, None, iterations=3) mask = cv2.dilate(mask, None, iterations=3) mask_copy = mask.copy() contours = cv2.findContours(mask_copy, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2] if len(contours)>0: for ctr in contours: if ONLY_MAX: cmax = max(contours, key=cv2.contourArea) (x,y), radius = cv2.minEnclosingCircle(cmax) else: (x, y), radius = cv2.minEnclosingCircle(ctr) if radius >= 40: # draw circle if radius>40 px cv2.circle(frame, (int(x), int(y)), int(radius), (0, 255, 255), 4) cv2.imshow("frame", frame) cv2.imshow("mask", mask) key = cv2.waitKey(4) & 0xFF if key==ord('q') or key==27: break # release all objects and free memory kamera.release() cv2.destroyAllWindows() |
Videoda kodların nasıl çalıştığı örnekleniyor. Tenis topu yerine elma ve portakal kullandım. Siz de isterseniz sarı renkler içeren başka nesneler kullanabilirsiniz.
mask çerçevesini görselleştirmek yerine sadece asıl çerçevedeki alanın veya alanların çember ile işaretlenmesini kullanabilir; ya da yakaladığımız nesnenin merkez koordinatlarındaki değişimi izlenebilir hale getirebiliriz.
Bu işlem için kendiniz doğrudan uğraşabilir ya da PyImageSearch sitesindeki örnekten veya benzer başka örneklerden yararlanabilirsiniz. (Bir sonraki yazımda bu konuyu ele alacağım.)
Yeni yazılarımdan anında haberdar olmak isterseniz sitemize abone olun.
Ahmet Aksoy