diff --git a/data/video_1.avi b/data/video_1.avi index 1c7424e..122acac 100644 Binary files a/data/video_1.avi and b/data/video_1.avi differ diff --git a/data/video_2.mp4 b/data/video_2.mp4 new file mode 100644 index 0000000..7e15ad1 Binary files /dev/null and b/data/video_2.mp4 differ diff --git a/main.py b/main.py index fb346b8..b504996 100644 --- a/main.py +++ b/main.py @@ -1,68 +1,199 @@ import cv2 +import numpy as np +import vehicles +import time -input_video = 'data/video_1.avi' -size = 1.0 #wielkosc obrazu - -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 +counter_up=0 #licznik pojazdow jadacych w gore +counter_down=0 #licznik pojazdow jadacych w dol while True: + user_choice = input("Wybierz nagranie:\n1. video_1.mp4\n2. video_2.avi\n") + if user_choice == "1": + input_video = 'data/video_1.avi' + vid_name = 'video_1' + kernelSize = 6 + break + elif user_choice == "2": + input_video = 'data/video_2.mp4' + vid_name = 'video_2' + kernelSize = 3 + break + +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() - if not ret: # jezeli film sie zakonczyl - #frame = cv2.VideoCapture(input_video) - #continue - break - if ret: #przetwarzanie kadru - input_vid = cv2.resize(frame, (0, 0), None, size, size) # rozmiar obrazu - cv2.imshow("bez zmian", input_vid) # wyswietlanie filmu wejsciowego bez zmian - gray = cv2.cvtColor(input_vid, cv2.COLOR_BGR2GRAY) - cv2.imshow("czarnobialy", gray) # wyswietlanie filmu w skali szarosci - fgMask = sub.apply(gray) # uzycie background subtraction - cv2.imshow("fgMask", fgMask) + 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 - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # kernel to apply to the morphology - closing = cv2.morphologyEx(fgMask, cv2.MORPH_CLOSE, kernel) - #cv2.imshow("closing", closing) - 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 + #Binarization + ret,imBinary = cv2.threshold(fgMask,200,255,cv2.THRESH_BINARY) + #cv2.imshow("binarny", imBinary) - minContourSize = 400 - maxContourSize = 50000 + kernelOpening = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernelSize, kernelSize)) # kernels to apply to the morphology + 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 - if hierarchy[0, i, 3] == -1: # uzycie hierarchii zeby uwzgledniac tylko "rodzicow" (najbardziej zewnetrzne kontury) - area = cv2.contourArea(contours[i]) - if minContourSize < area < maxContourSize: # nie obrysowuj zbyt duzych i malych obiektow - # obliczanie centoidu konturu (czyli srodka) za pomoca tzw "momentow" - centroid = contours[i] - moments = cv2.moments(centroid) - cx = int(moments['m10'] / moments['m00']) - cy = int(moments['m01'] / moments['m00']) + #Closing is reverse of Opening, Dilation followed by Erosion. + #It is useful in closing small holes inside the foreground objects, or small black points on the object. + mask = cv2.morphologyEx(mask,cv2.MORPH_CLOSE,kernelClosing) + cv2.imshow("closing", mask) - # 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) - # utworz prostokat wokol konturu - cv2.rectangle(input_vid, (x, y), (x + w, y + h), (255, 0, 0), 2) + # kontury (obrysowywanie) + contours,hierarchy=cv2.findContours(mask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE) # RETR_EXTERNAL - bierz pod uwage najbardziej 'zewnetrzne' kontury - cv2.imshow("wykryte pojazdy", input_vid) + # 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) + cx = int(moments['m10'] / 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 + x, y, w, h = cv2.boundingRect(centroid) + + 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 - key = cv2.waitKey(60) - if key == 27: #Wyjdz po nacisnieciu escape + + + 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) + if key == 27: # Wyjdz po nacisnieciu escape + 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() -cv2.destroyAllWindows() \ No newline at end of file +cv2.destroyAllWindows() + + + + + + + + + diff --git a/traffic logs/dummy.txt b/traffic logs/dummy.txt new file mode 100644 index 0000000..e69de29 diff --git a/vehicles.py b/vehicles.py new file mode 100644 index 0000000..9e4b65b --- /dev/null +++ b/vehicles.py @@ -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: + 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 \ No newline at end of file