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.

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.

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.

görüntü işlemeopenCVopencv görüntü işlemeopencv örnekleriprogramlamapython
Comments (10)
Add Comment
  • fatih

    hocam çok kral çalışmalar yapmışsınız ellerinize saglik cok güzel calisiyor program

  • halil

    boy, en ve ağırlık nasıl belirlenir
    bir kavunun en boy ve ağırlığı bilinirse onun daha sonra diğer kavunlar için ağırlık tahmini nasıl yaparız
    derin öğrenme cnn ile kastediyorum
    hiç bir yerde bununla ilgili bir kod bulamadım
    yardımcı olabilir misiniz

    • Halil İbrahim K.

      Onunla ilgili başka kaynaklara bakmalısınız. Derin öğrenme ile olur ancak örnek kod olarak benzer çalışmalar varsa bakılabilir.

  • hüseyin emre çınar

    Merhabalar kodları çalıştırıyorum ancak ölçüm yapılan ekran açılmıyor orjinali görüyorum sadece. sebei konusunda bilginiz var mı acaba? şimdiden teşekkür ederim.

  • Halil İbrahim K.

    Videoda 1.34 süresine bakarsan önce ölçelicek alanın resmini path değişkenine atama yapıyorsun.
    Kodun 6. satırında path =”1.jpg” var. Sizde ölçüm yapılacak alanı ve ölçülecek nesneyi belirleyip girişini yapmalısınız.

  • Hüseyin Emre ÇINAR

    Hocam merhaba. *** *** ** ** numaraya yazma imkanınız var mı acaba? Birkaç sorum olacak. Gerekirse ücretli yardım veriyorsanız ona da hazırım. Yazarsanız çok sevinirim.

    • Halil İbrahim K.

      Sorunuzu buraya yazarsanız daha iyi olur. Kısaca anlatırsanız yapıp yapamayacağım konusunda size bilgi veririm.

  • Hüseyin Emre ÇINAR

    Hocam dediğiniz kısımları kontrol ettim ancak halen program çalışmıyor. Ana kamera açılmasına rağmen uzunluk ölçümü yaptığı ekran açılmıyor sebebini bir türlü bulamadım.

    • Halil İbrahim K.

      Ben bugün veya yarın tekrar deneyeyim. Sonra buraya yazarım açıklamalı bir şekilde. Veya size mesaj atarım telefondan. Büyük ihtimalle yarın denerim.

  • Halil İbrahim K.

    Şimdi tekrar denedim. Kodlar gayet güzel çalışıyor. Öncelikle boş bir a4 kağıdının fotoğrafını çekip 1.jpg ismiyle kodlarla aynı klasörün içine atın. Kamerayı mümkün olduğunca uzaktan tutun. Burada benim yaptığım çalışmalar var.
    https://imgyukle.com/i/EUjOGP
    https://imgyukle.com/i/EUjH0e
    https://imgyukle.com/i/EUjN5N
    https://imgyukle.com/i/EUjjbq
    https://imgyukle.com/i/EUjcrY
    https://imgyukle.com/i/EUjgg0
    https://imgyukle.com/i/EUjh8v

    Bunlara bakarsanız çalıştığını görürsünüz.