├── README.md ├── input └── parking.png ├── park_positions ├── parking_space_counter.py └── parking_space_picker.py /README.md: -------------------------------------------------------------------------------- 1 | # Parking space counter created using OpenCV and Python 2 | 3 | ![parking_space_counter](https://user-images.githubusercontent.com/72137556/164909493-ad13efb6-6997-41de-bc1c-168c11b2532b.png) 4 | 5 | ## Result: 6 | https://youtu.be/LERHWFmSSdM 7 | 8 | ## Video used in the code: 9 | Tom Berrigan 10 | https://www.youtube.com/watch?v=yojapmOkIfg&list=LL&index=10 11 | 12 | Download this video in 1080p, rename it to parking.mp4 and place it in an input folder, and you are good to run the parking_space_counter.py. 13 | 14 | 15 | ## The code is inspired by: 16 | Murtaza's Workshop - Robotics and AI 17 | https://www.youtube.com/watch?v=caKnQlCMIYI 18 | -------------------------------------------------------------------------------- /input/parking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegiovanni/Parking_space_counter/e58db50666341a6683b93bbd024275c934fb9119/input/parking.png -------------------------------------------------------------------------------- /park_positions: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codegiovanni/Parking_space_counter/e58db50666341a6683b93bbd024275c934fb9119/park_positions -------------------------------------------------------------------------------- /parking_space_counter.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import pickle 3 | 4 | cap = cv2.VideoCapture('input/parking.mp4') 5 | 6 | with open('park_positions', 'rb') as f: 7 | park_positions = pickle.load(f) 8 | 9 | font = cv2.FONT_HERSHEY_COMPLEX_SMALL 10 | 11 | # Parking space parameters 12 | width, height = 40, 19 13 | full = width * height 14 | empty = 0.22 15 | 16 | 17 | def parking_space_counter(img_processed): 18 | global counter 19 | 20 | counter = 0 21 | 22 | for position in park_positions: 23 | x, y = position 24 | 25 | img_crop = img_processed[y:y + height, x:x + width] 26 | count = cv2.countNonZero(img_crop) 27 | 28 | ratio = count / full 29 | 30 | if ratio < empty: 31 | color = (0, 255, 0) 32 | counter += 1 33 | else: 34 | color = (0, 0, 255) 35 | 36 | cv2.rectangle(overlay, position, (position[0] + width, position[1] + height), color, -1) 37 | cv2.putText(overlay, "{:.2f}".format(ratio), (x + 4, y + height - 4), font, 0.7, (255, 255, 255), 1, cv2.LINE_AA) 38 | 39 | 40 | while True: 41 | 42 | # Video looping 43 | if cap.get(cv2.CAP_PROP_POS_FRAMES) == cap.get(cv2.CAP_PROP_FRAME_COUNT): 44 | cap.set(cv2.CAP_PROP_POS_FRAMES, 0) 45 | 46 | _, frame = cap.read() 47 | overlay = frame.copy() 48 | 49 | # Frame processing 50 | img_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 51 | img_blur = cv2.GaussianBlur(img_gray, (3, 3), 1) 52 | img_thresh = cv2.adaptiveThreshold(img_blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 25, 16) 53 | 54 | parking_space_counter(img_thresh) 55 | 56 | alpha = 0.7 57 | frame_new = cv2.addWeighted(overlay, alpha, frame, 1 - alpha, 0) 58 | 59 | w, h = 220, 60 60 | cv2.rectangle(frame_new, (0, 0), (w, h), (255, 0, 255), -1) 61 | cv2.putText(frame_new, f"{counter}/{len(park_positions)}", (int(w / 10), int(h * 3 / 4)), font, 2, (255, 255, 255), 2, cv2.LINE_AA) 62 | 63 | cv2.namedWindow('frame', cv2.WINDOW_NORMAL) 64 | cv2.setWindowProperty('frame', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN) 65 | 66 | cv2.imshow('frame', frame_new) 67 | # cv2.imshow('image_blur', img_blur) 68 | # cv2.imshow('image_thresh', img_thresh) 69 | 70 | if cv2.waitKey(1) & 0xFF == 27: 71 | break 72 | 73 | cap.release() 74 | cv2.destroyAllWindows() 75 | -------------------------------------------------------------------------------- /parking_space_picker.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import pickle 3 | from math import sqrt 4 | 5 | width, height = 40, 23 6 | pt1_x, pt1_y, pt2_x, pt2_y = None, None, None, None 7 | line_count = 0 8 | 9 | try: 10 | with open('park_positions', 'rb') as f: 11 | park_positions = pickle.load(f) 12 | except: 13 | park_positions = [] 14 | 15 | 16 | def parking_line_counter(): 17 | global line_count 18 | line_count = int((sqrt((pt2_x - pt1_x) ** 2 + (pt2_y - pt1_y) ** 2)) / height) 19 | return line_count 20 | 21 | 22 | def mouse_events(event, x, y, flag, param): 23 | global pt1_x, pt1_y, pt2_x, pt2_y 24 | 25 | if event == cv2.EVENT_LBUTTONDOWN: 26 | pt1_x, pt1_y = x, y 27 | 28 | elif event == cv2.EVENT_LBUTTONUP: 29 | pt2_x, pt2_y = x, y 30 | parking_spaces = parking_line_counter() 31 | if parking_spaces == 0: 32 | park_positions.append((x, y)) 33 | else: 34 | for i in range(parking_spaces): 35 | park_positions.append((pt1_x, pt1_y + i * height)) 36 | 37 | if event == cv2.EVENT_RBUTTONDOWN: 38 | for i, position in enumerate(park_positions): 39 | x1, y1 = position 40 | if x1 < x < x1 + width and y1 < y < y1 + height: 41 | park_positions.pop(i) 42 | 43 | with open('park_positions', 'wb') as f: 44 | pickle.dump(park_positions, f) 45 | 46 | 47 | while True: 48 | 49 | img = cv2.imread('input/parking.png') 50 | 51 | for position in park_positions: 52 | cv2.rectangle(img, position, (position[0] + width, position[1] + height), (255, 0, 255), 3) 53 | 54 | cv2.namedWindow('image', cv2.WINDOW_NORMAL) 55 | cv2.setWindowProperty('image', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN) 56 | 57 | cv2.imshow('image', img) 58 | cv2.setMouseCallback('image', mouse_events) 59 | 60 | key = cv2.waitKey(30) 61 | if key == 27: 62 | break 63 | 64 | cv2.destroyAllWindows() 65 | --------------------------------------------------------------------------------