imlementacja sledzenia pojazdow i kierunku ich ruchu. Zliczanie przejezdzajacych pojazdow.

This commit is contained in:
Krzysztof Strzelecki 2020-02-14 23:28:48 +01:00
parent c7e7bd6c70
commit 50a90d23d6
5 changed files with 268 additions and 54 deletions

Binary file not shown.

BIN
data/video_2.mp4 Normal file

Binary file not shown.

223
main.py
View File

@ -1,68 +1,199 @@
import cv2 import cv2
import numpy as np
import vehicles
import time
input_video = 'data/video_1.avi' counter_up=0 #licznik pojazdow jadacych w gore
size = 1.0 #wielkosc obrazu counter_down=0 #licznik pojazdow jadacych w dol
cap = cv2.VideoCapture(input_video)
height, width, frames_count, fps = cap.get(cv2.CAP_PROP_FRAME_HEIGHT), cap.get(
cv2.CAP_PROP_FRAME_WIDTH), cap.get(cv2.CAP_PROP_FRAME_COUNT), cap.get(cv2.CAP_PROP_FPS),
height = int(height)
width = int(width)
print(height, width, frames_count, fps)
sub = cv2.createBackgroundSubtractorMOG2() # utworz background subtractor
while True: while True:
ret, frame = cap.read() user_choice = input("Wybierz nagranie:\n1. video_1.mp4\n2. video_2.avi\n")
if not ret: # jezeli film sie zakonczyl if user_choice == "1":
#frame = cv2.VideoCapture(input_video) input_video = 'data/video_1.avi'
#continue vid_name = 'video_1'
kernelSize = 6
break break
if ret: #przetwarzanie kadru elif user_choice == "2":
input_vid = cv2.resize(frame, (0, 0), None, size, size) # rozmiar obrazu input_video = 'data/video_2.mp4'
cv2.imshow("bez zmian", input_vid) # wyswietlanie filmu wejsciowego bez zmian vid_name = 'video_2'
gray = cv2.cvtColor(input_vid, cv2.COLOR_BGR2GRAY) kernelSize = 3
cv2.imshow("czarnobialy", gray) # wyswietlanie filmu w skali szarosci break
fgMask = sub.apply(gray) # uzycie background subtraction
cv2.imshow("fgMask", fgMask) cap = cv2.VideoCapture(input_video)
height, width, frames_count, fps = cap.get(cv2.CAP_PROP_FRAME_HEIGHT), cap.get(
cv2.CAP_PROP_FRAME_WIDTH), cap.get(cv2.CAP_PROP_FRAME_COUNT), cap.get(cv2.CAP_PROP_FPS),
height = int(height)
width = int(width)
frameArea = height*width
print("Wysokosc", height, "szerokosc", width, "liczba klatek", frames_count, "fps",fps)
font = cv2.FONT_HERSHEY_SIMPLEX
"""
#Polozenie linii "mety" (na srodku kadru)
lineUpper = int(4*(height/10))
lineLower = int(6*(height/10))
upLimit = int(2*(height/10))
downLimit = int(8*(height/10))
"""
#Polozenie linii "mety" (w dolnej czesci kadru)
lineUpper = int(7*(height/10))
lineLower = int(8*(height/10))
upLimit = int(4*(height/10))
downLimit = int(10*(height/10))
print("Zielona linia y:",str(lineLower))
print("Czerwona linia y:",str(lineUpper))
lineLower_color = (0,255,0)
lineUpper_color = (0,0,255)
point1 = [0, lineLower]
point2 = [width, lineLower]
points_lineLower = np.array([point1,point2], np.int32)
# Przekształcenie tablicy do 1x2, polecane w dokumentacji OpenCV ale chyba zbedne w tym przypadku
#points_lineLower = points_lineLower.reshape((-1,1,2))
point3 = [0, lineUpper]
point4 = [width, lineUpper]
points_LineUpper = np.array([point3,point4], np.int32)
#points_LineUpper = points_LineUpper.reshape((-1,1,2))
point5 = [0, upLimit]
point6 = [width, upLimit]
points_upLimit = np.array([point5,point6], np.int32)
#points_upLimit = points_upLimit.reshape((-1,1,2))
point7 = [0, downLimit]
point8 = [width, downLimit]
points_downLimit= np.array([point7,point8], np.int32)
#points_downLimit = points_downLimit.reshape((-1,1,2))
sub=cv2.createBackgroundSubtractorMOG2() # utworz background subtractor
size = 1.0 #wielkosc obrazu
cars = []
maxAllowedAge = 3 #przez ile klatek "zgubiony" pojazd bedzie pozostawal na liscie do "sledzenia"
id = 1
while(cap.isOpened()):
ret, frame = cap.read()
for i in cars:
i.age_one()
if ret == True: #przetwarzanie kadru
#frame = cv2.resize(frame, (0, 0), None, size, size) # rozmiar obrazu. Bez zmian
#cv2.imshow("bez zmian", frame) # wyswietlanie filmu wejsciowego bez zmian
#gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
#cv2.imshow("czarnobialy", gray) # wyswietlanie filmu w skali szarosci
fgMask = sub.apply(frame) # uzycie background subtraction
fgMask2 = sub.apply(frame)
#cv2.imshow("fgMask", fgMask)
# operacje morfologiczne. Wg tutoriala na docs.opencv.org. Opening, closing - usuwanie szumów # operacje morfologiczne. Wg tutoriala na docs.opencv.org. Opening, closing - usuwanie szumów
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # kernel to apply to the morphology #Binarization
closing = cv2.morphologyEx(fgMask, cv2.MORPH_CLOSE, kernel) ret,imBinary = cv2.threshold(fgMask,200,255,cv2.THRESH_BINARY)
#cv2.imshow("closing", closing) #cv2.imshow("binarny", imBinary)
opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel)
#cv2.imshow("opening", opening)
dilation = cv2.dilate(opening, kernel)
#cv2.imshow("dilation", dilation)
retvalbin, bins = cv2.threshold(dilation, 220, 255, cv2.THRESH_BINARY) # Thresholding/binaryzacja obrazu usuwanie cieni
#cv2.imshow('bins',bins)
# obrysowywanie
contours, hierarchy = cv2.findContours(bins, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # RETR_EXTERNAL - bierz pod uwage najbardziej 'zewnetrzne' kontury
minContourSize = 400 kernelOpening = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) # kernels to apply to the morphology
maxContourSize = 50000 kernelClosing = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11))
#Opening is just another name of erosion followed by dilation.
#It is useful in removing noise
mask = cv2.morphologyEx(imBinary,cv2.MORPH_OPEN,kernelOpening)
#cv2.imshow("opening", mask)
for i in range(len(contours)): # przejdz wszystkie kontury w kadrze #Closing is reverse of Opening, Dilation followed by Erosion.
if hierarchy[0, i, 3] == -1: # uzycie hierarchii zeby uwzgledniac tylko "rodzicow" (najbardziej zewnetrzne kontury) #It is useful in closing small holes inside the foreground objects, or small black points on the object.
area = cv2.contourArea(contours[i]) mask = cv2.morphologyEx(mask,cv2.MORPH_CLOSE,kernelClosing)
if minContourSize < area < maxContourSize: # nie obrysowuj zbyt duzych i malych obiektow cv2.imshow("closing", mask)
# obliczanie centoidu konturu (czyli srodka) za pomoca tzw "momentow"
centroid = contours[i] # kontury (obrysowywanie)
contours,hierarchy=cv2.findContours(mask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE) # RETR_EXTERNAL - bierz pod uwage najbardziej 'zewnetrzne' kontury
# kontury zajmujace zbyt duza lub mala czesc kadru beda ignorowane
maxContourSize = frameArea / 4
minContourSize = frameArea / 400
for j in range(len(contours)): # przejdz wszystkie kontury w kadrze
area = cv2.contourArea(contours[j])
#print("wielkosc konturu:",area)
if minContourSize < area < maxContourSize: # zbyt duze i male obiekty sa ignorowane
#### Sledzenie konturow ######
centroid = contours[j]
moments = cv2.moments(centroid) moments = cv2.moments(centroid)
cx = int(moments['m10'] / moments['m00']) cx = int(moments['m10'] / moments['m00'])
cy = int(moments['m01'] / moments['m00']) cy = int(moments['m01'] / moments['m00'])
# punkty graniczne (bounding points) konturu. x,y to wspolrzedne lewego gornego rogu, w,h to szerokosc i wysokosc prostokata # punkty graniczne (bounding points) konturu. x,y to wspolrzedne lewego gornego rogu, w,h to szerokosc i wysokosc prostokata
x, y, w, h = cv2.boundingRect(centroid) x, y, w, h = cv2.boundingRect(centroid)
# utworz prostokat wokol konturu
cv2.rectangle(input_vid, (x, y), (x + w, y + h), (255, 0, 0), 2)
cv2.imshow("wykryte pojazdy", input_vid) new = True #przyjmij ze kontur to nowy pojazd
if cy in range(upLimit,downLimit): # sprawdz czy jest wart sledzenia (czyli miedzy szarymi liniami limitujacymi)
for i in cars:
# jesli odnajdziesz sledzony kontur o bardzo zblizonych koordynatach,
#przymij ze to ten sam i zaktualizuj jego polozenie
if abs(x - i.getX()) <= w and abs(y - i.getY()) <= h: #abs function calculates an absolute value of each matrix element.
new = False
i.updateCoords(cx, cy)
#sprawdz czy prekroczyl mete
if i.going_UP(lineLower,lineUpper) == True:
counter_up+=1
print("Pojazd o ID:",i.getId(),'przekroczyl linie mety w gore o czasie:', time.strftime("%c"))
elif i.going_DOWN(lineLower,lineUpper) == True:
counter_down+=1
print("Pojazd o ID:", i.getId(), 'przekroczyl linie mety w dol o czasie:', time.strftime("%c"))
break
#jesli kierunek ruchu jest znany i linia mety zostala przekroczona, przestan sledzic i usun z listy
if i.getState() == '1':
if i.getDir() == 'down'and i.getY() > downLimit:
i.setDone()
elif i.getDir() == 'up'and i.getY() < upLimit:
i.setDone()
if i.timedOut():
index=cars.index(i)
cars.pop(index)
del i
if new == True: #Jesli kontur nie byl dotychczas sledzony, dodaj nowy samochod do listy
newCar=vehicles.Car(id,cx,cy,maxAllowedAge)
cars.append(newCar)
id+=1
cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
#for i in cars:
#cv2.putText(frame, str(i.getId()), (i.getX(), i.getY()), font, 0.3, i.getRGB(), 1, cv2.LINE_AA) #wypisywanie id
str_up='W GORE: '+str(counter_up)
str_down='W DOL: '+str(counter_down)
frame = cv2.polylines(frame,[points_lineLower],False,lineLower_color,thickness=2)
frame = cv2.polylines(frame,[points_LineUpper],False,lineUpper_color,thickness=2)
frame = cv2.polylines(frame,[points_upLimit],False,(255,255,255),thickness=1)
frame = cv2.polylines(frame,[points_downLimit],False,(255,255,255),thickness=1)
cv2.putText(frame, str_up, (10, 40), font, 0.5, (255, 255, 255), 2, cv2.LINE_AA) #biale tlo zeby licznik byl wyrazniejszy
cv2.putText(frame, str_up, (10, 40), font, 0.5, lineUpper_color, 1, cv2.LINE_AA)
cv2.putText(frame, str_down, (10, 90), font, 0.5, (255, 255, 255), 2, cv2.LINE_AA) #biale tlo zeby licznik byl wyrazniejszy
cv2.putText(frame, str_down, (10, 90), font, 0.5, lineLower_color, 1, cv2.LINE_AA)
cv2.imshow('Frame',frame)
key = cv2.waitKey(60) key = cv2.waitKey(60)
if key == 27: #Wyjdz po nacisnieciu escape if key == 27: # Wyjdz po nacisnieciu escape
break break
else:
break
timestr = time.strftime("%Y_%m_%d-%H_%M_%S")
f = open("traffic logs/"+vid_name+" "+timestr+".txt","w+")
f.write("Natężenie ruchu:\r\nliczba samochodów jadących w górę: %d\rliczba samochodów jadących w dół: %d" %(counter_up, counter_down))
cap.release() cap.release()
cv2.destroyAllWindows() cv2.destroyAllWindows()

0
traffic logs/dummy.txt Normal file
View File

83
vehicles.py Normal file
View File

@ -0,0 +1,83 @@
from random import randint
import time
class Car:
tracks=[]
def __init__(self,id,xi,yi,max_age):
self.id = id
self.x = xi
self.y = yi
self.tracks = []
self.R = randint(0,255)
self.G = randint(0,255)
self.B = randint(0,255)
self.done = False
self.state = '0' # 0 - wymaga dalszego sledzenia, 1 - przekroczyl mete, zostal policzony i mozna go bedzie usunac
self.age = 0 # o
self.max_age = max_age
self.dir = None
def getRGB(self):
return (self.R,self.G,self.B)
def getId(self):
return self.id
def getX(self): # szerokosc
return self.x
def getY(self): # wysokosc
return self.y
def getState(self):
return self.state
def getDir(self):
return self.dir
#stare wspolrzedne dodaj do listy "tropu" pojazdu, a nastepnie aktualizuj
def updateCoords(self, xNew, yNew):
self.age = 0
self.tracks.append([self.x, self.y])
self.x = xNew
self.y = yNew
def setDone(self):
self.done = True
def timedOut(self):
return self.done
def going_UP(self, mid_start, mid_end):
if len(self.tracks)>=2:
if self.state=='0':
if self.tracks[-1][1]<mid_end and self.tracks[-2][1]>=mid_end:
self.state='1'
self.dir='up'
return True
else:
return False
else:
return False
else:
return False
def going_DOWN(self,mid_start,mid_end):
if len(self.tracks)>=2:
if self.state=='0':
if self.tracks[-1][1]>mid_start and self.tracks[-2][1]<=mid_start:
self.state='1'
self.dir='down'
return True
else:
return False
else:
return False
else:
return False
def age_one(self):
self.age+=1
if self.age>self.max_age:
self.done=True
return True