Python ve OpenCV Kullanarak Gerçek Zamanlı Nesne Ölçümü

Bu derste OpenCV ve Python kullanarak nesne ölçümünün nasıl yapılacağını öğreneceğiz. Rehberimiz olarak bir A4 kağıt kullanacağız ve bu bölgeye yerleştirilen nesnelerin genişliğini ve yüksekliğini bulacağız.
python gercek zamanlı mesafe ölçümü

Kodlama

Kütüphaneleri Dahil Etme

Kütüphanelerimizi ithal ederek başlayacağız. Şimdi ObjectMeasurement.py adında bir ana komut dosyası ve tüm yardım fonksiyonlarımızı yazmak için utlis.py bir yardımcı program komut dosyası kullanacağız . Opencv ve numpy paketlerini kuracağız ve daha sonra komut dosyalarımıza aktaracağız.

Ana Script

import cv2
import utlis

Araçlar

import cv2
import numpy as np

Görüntü ve Web Kamerası

Öğreticinin çoğu için görüntüye sadık kalacağız ve en sonunda bir web kamerasına geçeceğiz. Bu, süreci biraz daha yönetilebilir hale getirir. Şimdi elimizdeki görüntü büyük, bu yüzden onu görebilmemiz için en sonunda azaltacağız. Gerçekte, hesaplamalar gerçek görüntü boyutu kullanılarak yapılacaktır.

Web kamerası ve görüntü dosyasından mesafe ölçümü arasında seçim yapabilmek amacıyla webcam adında bir değişken tanımladık. Bu değişken False değerinde ise görüntü dosyası True değeri alırsa da WebCam’den mesafe ölçümü yapılır.Kamera varsayılan olarak 0 yani bilgisayarınızın ana kamerasını açar. Farklı bir kamera kullanmak için bu değeri değiştirebiirsiniz.

webcam = False
path = '1.jpg'
cap = cv2.VideoCapture(0)
cap.set(10,160)
cap.set(3,1920)
cap.set(4,1080)
 
while True:
    if webcam:success,img = cap.read()
    else: img = cv2.imread(path)
 
 
    img = cv2.resize(img,(0,0),None,0.5,0.5)
    cv2.imshow('Original',img)
    cv2.waitKey(1)

Contours Fonksiyonu

Şimdi önce A4 kağıdını bulmamıza ve daha sonra içindeki nesneyi bulmamıza izin veren bir contours fonksiyonu yaratacağız. Bu nedenle, her iki görevi yerine getirmek için aynı işlevi kullanacağız. Bu işlevselliği önceki birçok öğreticide kullandık (Şekil Algılama Uygulamasına Bakabilirsiniz) , ancak bunun için gerçekten genel bir işlev oluşturmadık. Bu yüzden, birden fazla projede kullanabileceğimiz bir fonksiyon yarattık.

Burada fikir basitçe renkli görüntüler gönderiyoruz ve bize tüm nesne bilgilerini içeren bir liste döndürüyor. Bu yüzden önce renkli imajımıza bazı önermeler uygulayacağız. Gri skalaya dönüştüreceğiz, sonra biraz bulanıklık ekleyeceğiz, sonra Canny kenar dedektörünü kullanarak kenarları bulacağız ve daha sonra biraz genişleme ve erozyon uygulayacağız. Canny işlevi 2 eşik aldığından, kullanıcının değiştirmek istediği takdirde bunu kontur fonksiyonlarımız için bir argüman olarak koyacağız. Ayrıca, canny görüntüsünü görüntülemek için bir değişken ekleyeceğiz.

def getContours(img,cThr=[100,100],showCanny=False,minArea=1000,filter=0,draw =False):
    imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    imgBlur = cv2.GaussianBlur(imgGray,(5,5),1)
    imgCanny = cv2.Canny(imgBlur,cThr[0],cThr[1])
    kernel = np.ones((5,5))
    imgDial = cv2.dilate(imgCanny,kernel,iterations=3)
    imgThre = cv2.erode(imgDial,kernel,iterations=2)
    if showCanny:cv2.imshow('Canny',imgThre)

Görüntüyü hazır hale getirdikten sonra artık cv2 findCountours işlevine gönderebiliriz. Burada Dış yöntemi kullanacağız, bu nedenle basit kontur yaklaşımıyla birlikte dış konturları bulacağız. Şimdi tespit edilen tüm konturlar arasında dolaşabilir ve alanlarını bulabiliriz. Bu, Alan Filtresi eklememize izin verecektir. Bunu fonksiyonumuza girdi olarak da koyacağız. Minimum alan filtresi gürültüyü önlememizi sağlar. Daha sonra her bir konturun sahip olduğu köşe sayısına yaklaşacağız ve üzerine bir filtre uygulayacağız. Bu, örneğin yalnızca kare nesneler veya üçgen nesneler yerine hepsinden ziyade belirli bir şekil istememiz durumunda eklenir. Son olarak, tüm bu bilgileri bir listeye koyacağız ve en büyük konturun önce gelmesi için yeniden sıralayacağız.

contours,hiearchy = cv2.findContours(imgThre,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    finalCountours = []
    for i in contours:
        area = cv2.contourArea(i)
        if area > minArea:
            peri = cv2.arcLength(i,True)
            approx = cv2.approxPolyDP(i,0.02*peri,True)
            bbox = cv2.boundingRect(approx)
            if filter > 0:
                if len(approx) == filter:
                    finalCountours.append([len(approx),area,approx,bbox,i])
            else:
                finalCountours.append([len(approx),area,approx,bbox,i])
    finalCountours = sorted(finalCountours,key = lambda x:x[1] ,reverse= True)

Geliştirme süreci için yöntemimizi değerlendirmek üzere bir görüntüleme işlevselliğine sahip olmak esastır. Bu yüzden, giriş görüntüsünde son konturları çizecek bir ekran bayrağı da ekleyeceğiz.

if draw:
    for con in finalCountours:
        cv2.drawContours(img,con[4],-1,(0,0,255),3)
return img, finalCountours

Artık A4 kağıt bulmak için ana komut dosyamızda bu işlevi çağırabiliriz. Kağıdın dört köşesi olduğundan 4 filtre değerini ve 50.000 min alan büyüklüğünü ekleyeceğiz. Kontür listesine geri döndüğümüzde, ilk öğeyi basitçe azalan düzende çıkararak çıkarabiliriz. Bazen konturlar bulunmaz ve listemizden elementi çıkarmaya çalışırsak hata verir. Bundan kaçınmak için önce listenin boş olmadığını kontrol edip devam edeceğiz.

imgContours , conts = utlis.getContours(img,minArea=50000,filter=4)
if len(conts) != 0:
    biggest = conts[0][2]
    #print(biggest)

ReOrder fonksiyonu

Şimdi A4 kağıdımızın köşe noktalarına sahip olduğumuza göre, bükülmemiz gerekiyor, böylece yöndeki herhangi bir eğim sabitleşiyor. Ancak görüntüyü çarpıtmak için, köşe noktalarını her elde ettiğimizde emin olmalıyız. Bundan emin olmak için köşe noktalarını alan bir yeniden sıralama işlevi oluşturacağız ve bunları yeniden sıralayacağız.

def reorder(myPoints):
    #print(myPoints.shape)
    myPointsNew = np.zeros_like(myPoints)
    myPoints = myPoints.reshape((4,2))
    add = myPoints.sum(1)
    myPointsNew[0] = myPoints[np.argmin(add)]
    myPointsNew[3] = myPoints[np.argmax(add)]
    diff = np.diff(myPoints,axis=1)
    myPointsNew[1]= myPoints[np.argmin(diff)]
    myPointsNew[2] = myPoints[np.argmax(diff)]
    return myPointsNew

Şimdi yeniden sıralanan noktaları kullanarak yeni çarpık görüntülerimizi oluşturabiliriz. Noktaları hazırlayacağız ve dönüşüm matrisini yaratacağız. Sonra bunu Çözgü Perspektifi fonksiyonuna gireceğiz. Sonuç 100 kağıt olmadığı için kenarlara biraz dolgu ekleyebiliriz.

def warpImg (img,points,w,h,pad=20):
    # print(points)
    points =reorder(points)
    pts1 = np.float32(points)
    pts2 = np.float32([[0,0],[w,0],[0,h],[w,h]])
    matrix = cv2.getPerspectiveTransform(pts1,pts2)
    imgWarp = cv2.warpPerspective(img,matrix,(w,h))
    imgWarp = imgWarp[pad:imgWarp.shape[0]-pad,pad:imgWarp.shape[1]-pad]
    return imgWarp

Şimdi bu işlevi çağırabiliriz

imgWarp = utlis.warpImg(img, biggest, wP,hP)

Nesne Ölçümü

Çarpık görüntüyü kullanarak nesnelerimizin konturlarını bulacağız.

imgContours2, conts2 = utlis.getContours(imgWarp,
                                                 minArea=2000, filter=4,
                                                 cThr=[50,50],draw = False)

Kontürlerin ve sınırlayıcı kutularının bir listesine sahip olduğumuz göz önüne alındığında, şimdi konturlarımızın genişliğini ve yüksekliğini bulabiliriz. Ancak buradaki sorun, sınırlayıcı kutumuzun genişliğini ve yüksekliğini alabilmemiz için nesnenin yatırılabilmesidir. Büyüklüğü 2 nokta ve biraz matematik kullanarak bulabiliriz.

Python ve OpenCV Kullanarak Gerçek Zamanlı Nesne Ölçümü 2 Python ve OpenCV Kullanarak Gerçek Zamanlı Nesne Ölçümü 3

def findDis(pts1,pts2):
    return ((pts2[0]-pts1[0])**2 + (pts2[1]-pts1[1])**2)**0.5
    if len(conts) != 0:
      for obj in conts2:
          cv2.polylines(imgContours2,[obj[2]],True,(0,255,0),2)
          nPoints = utlis.reorder(obj[2])
          nW = round((utlis.findDis(nPoints[0][0]//scale,nPoints[1][0]//scale)/10),1)
          nH = round((utlis.findDis(nPoints[0][0]//scale,nPoints[2][0]//scale)/10),1)

Görüntüleme

Son olarak, nesne ölçümlerimizi bazı oklarla birlikte görüntüleyebiliriz.

cv2.arrowedLine(imgContours2, (nPoints[0][0][0], nPoints[0][0][1]), (nPoints[1][0][0], nPoints[1][0][1]),
                                (255, 0, 255), 3, 8, 0, 0.05)
cv2.arrowedLine(imgContours2, (nPoints[0][0][0], nPoints[0][0][1]), (nPoints[2][0][0], nPoints[2][0][1]),
                                (255, 0, 255), 3, 8, 0, 0.05)
x, y, w, h = obj[3]
cv2.putText(imgContours2, '{}cm'.format(nW), (x + 30, y - 10), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,(255, 0, 255), 2)
cv2.putText(imgContours2, '{}cm'.format(nH), (x - 70, y + h // 2), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,(255, 0, 255), 2)
cv2.imshow('A4', imgContours2)

Komple Kod

Nesne Ölçümü.py

import cv2
import utlis
 
###################################
webcam = True
path = '1.jpg'
cap = cv2.VideoCapture(0)
cap.set(10,160)
cap.set(3,1920)
cap.set(4,1080)
scale = 3
wP = 210 *scale
hP= 297 *scale
###################################
 
while True:
    if webcam:success,img = cap.read()
    else: img = cv2.imread(path)
 
    imgContours , conts = utlis.getContours(img,minArea=50000,filter=4)
    if len(conts) != 0:
        biggest = conts[0][2]
        #print(biggest)
        imgWarp = utlis.warpImg(img, biggest, wP,hP)
        imgContours2, conts2 = utlis.getContours(imgWarp,
                                                 minArea=2000, filter=4,
                                                 cThr=[50,50],draw = False)
        if len(conts) != 0:
            for obj in conts2:
                cv2.polylines(imgContours2,[obj[2]],True,(0,255,0),2)
                nPoints = utlis.reorder(obj[2])
                nW = round((utlis.findDis(nPoints[0][0]//scale,nPoints[1][0]//scale)/10),1)
                nH = round((utlis.findDis(nPoints[0][0]//scale,nPoints[2][0]//scale)/10),1)
                cv2.arrowedLine(imgContours2, (nPoints[0][0][0], nPoints[0][0][1]), (nPoints[1][0][0], nPoints[1][0][1]),
                                (255, 0, 255), 3, 8, 0, 0.05)
                cv2.arrowedLine(imgContours2, (nPoints[0][0][0], nPoints[0][0][1]), (nPoints[2][0][0], nPoints[2][0][1]),
                                (255, 0, 255), 3, 8, 0, 0.05)
                x, y, w, h = obj[3]
                cv2.putText(imgContours2, '{}cm'.format(nW), (x + 30, y - 10), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,
                            (255, 0, 255), 2)
                cv2.putText(imgContours2, '{}cm'.format(nH), (x - 70, y + h // 2), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,
                            (255, 0, 255), 2)
        cv2.imshow('A4', imgContours2)
 
    img = cv2.resize(img,(0,0),None,0.5,0.5)
    cv2.imshow('Original',img)
    cv2.waitKey(1)

Utlis.py

import cv2
import numpy as np
 
def getContours(img,cThr=[100,100],showCanny=False,minArea=1000,filter=0,draw =False):
    imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    imgBlur = cv2.GaussianBlur(imgGray,(5,5),1)
    imgCanny = cv2.Canny(imgBlur,cThr[0],cThr[1])
    kernel = np.ones((5,5))
    imgDial = cv2.dilate(imgCanny,kernel,iterations=3)
    imgThre = cv2.erode(imgDial,kernel,iterations=2)
    if showCanny:cv2.imshow('Canny',imgThre)
    contours,hiearchy = cv2.findContours(imgThre,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    finalCountours = []
    for i in contours:
        area = cv2.contourArea(i)
        if area > minArea:
            peri = cv2.arcLength(i,True)
            approx = cv2.approxPolyDP(i,0.02*peri,True)
            bbox = cv2.boundingRect(approx)
            if filter > 0:
                if len(approx) == filter:
                    finalCountours.append([len(approx),area,approx,bbox,i])
            else:
                finalCountours.append([len(approx),area,approx,bbox,i])
    finalCountours = sorted(finalCountours,key = lambda x:x[1] ,reverse= True)
    if draw:
        for con in finalCountours:
            cv2.drawContours(img,con[4],-1,(0,0,255),3)
    return img, finalCountours
 
def reorder(myPoints):
    #print(myPoints.shape)
    myPointsNew = np.zeros_like(myPoints)
    myPoints = myPoints.reshape((4,2))
    add = myPoints.sum(1)
    myPointsNew[0] = myPoints[np.argmin(add)]
    myPointsNew[3] = myPoints[np.argmax(add)]
    diff = np.diff(myPoints,axis=1)
    myPointsNew[1]= myPoints[np.argmin(diff)]
    myPointsNew[2] = myPoints[np.argmax(diff)]
    return myPointsNew
 
def warpImg (img,points,w,h,pad=20):
    # print(points)
    points =reorder(points)
    pts1 = np.float32(points)
    pts2 = np.float32([[0,0],[w,0],[0,h],[w,h]])
    matrix = cv2.getPerspectiveTransform(pts1,pts2)
    imgWarp = cv2.warpPerspective(img,matrix,(w,h))
    imgWarp = imgWarp[pad:imgWarp.shape[0]-pad,pad:imgWarp.shape[1]-pad]
    return imgWarp
 
def findDis(pts1,pts2):
    return ((pts2[0]-pts1[0])**2 + (pts2[1]-pts1[1])**2)**0.5

Video öğretici

Son Sözler

Sitemizde OpenCV ile ilgili birçok örnek uygulamaya erişebilirsiniz. Bu yazının orijinal haline murtazahassan.com sitesinden erişebilirsiniz.

Yorumlarınızı bekliyorum.