imlementacja sledzenia pojazdow i kierunku ich ruchu. Zliczanie przejezdzajacych pojazdow.
This commit is contained in:
parent
c7e7bd6c70
commit
50a90d23d6
BIN
data/video_1.avi
BIN
data/video_1.avi
Binary file not shown.
BIN
data/video_2.mp4
Normal file
BIN
data/video_2.mp4
Normal file
Binary file not shown.
223
main.py
223
main.py
@ -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
0
traffic logs/dummy.txt
Normal file
83
vehicles.py
Normal file
83
vehicles.py
Normal 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
|
Loading…
Reference in New Issue
Block a user