├── LICENSE.md ├── README.md ├── main.py ├── mask_1920_1080.png ├── mask_crop.png ├── requirements.txt └── util.py /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 computervisiondeveloper 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # parking-space-counter 2 | 3 | In this video I show you how to build a parking space detector and counter with computer vision ! 4 | 5 | [![Watch the video](https://img.youtube.com/vi/F-884J2mnOY/0.jpg)](https://www.youtube.com/watch?v=F-884J2mnOY) 6 | 7 | ## data 8 | 9 | You can download the data and model I used in this tutorial [here](https://drive.google.com/drive/folders/1CjEFWihRqTLNUnYRwHXxGAVwSXF2k8QC?usp=sharing). 10 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | 5 | from util import get_parking_spots_bboxes, empty_or_not 6 | 7 | 8 | def calc_diff(im1, im2): 9 | return np.abs(np.mean(im1) - np.mean(im2)) 10 | 11 | 12 | mask = './mask_1920_1080.png' 13 | video_path = './samples/parking_1920_1080_loop.mp4' 14 | 15 | 16 | mask = cv2.imread(mask, 0) 17 | 18 | cap = cv2.VideoCapture(video_path) 19 | 20 | connected_components = cv2.connectedComponentsWithStats(mask, 4, cv2.CV_32S) 21 | 22 | spots = get_parking_spots_bboxes(connected_components) 23 | 24 | spots_status = [None for j in spots] 25 | diffs = [None for j in spots] 26 | 27 | previous_frame = None 28 | 29 | frame_nmr = 0 30 | ret = True 31 | step = 30 32 | while ret: 33 | ret, frame = cap.read() 34 | 35 | if frame_nmr % step == 0 and previous_frame is not None: 36 | for spot_indx, spot in enumerate(spots): 37 | x1, y1, w, h = spot 38 | 39 | spot_crop = frame[y1:y1 + h, x1:x1 + w, :] 40 | 41 | diffs[spot_indx] = calc_diff(spot_crop, previous_frame[y1:y1 + h, x1:x1 + w, :]) 42 | 43 | print([diffs[j] for j in np.argsort(diffs)][::-1]) 44 | 45 | if frame_nmr % step == 0: 46 | if previous_frame is None: 47 | arr_ = range(len(spots)) 48 | else: 49 | arr_ = [j for j in np.argsort(diffs) if diffs[j] / np.amax(diffs) > 0.4] 50 | for spot_indx in arr_: 51 | spot = spots[spot_indx] 52 | x1, y1, w, h = spot 53 | 54 | spot_crop = frame[y1:y1 + h, x1:x1 + w, :] 55 | 56 | spot_status = empty_or_not(spot_crop) 57 | 58 | spots_status[spot_indx] = spot_status 59 | 60 | if frame_nmr % step == 0: 61 | previous_frame = frame.copy() 62 | 63 | for spot_indx, spot in enumerate(spots): 64 | spot_status = spots_status[spot_indx] 65 | x1, y1, w, h = spots[spot_indx] 66 | 67 | if spot_status: 68 | frame = cv2.rectangle(frame, (x1, y1), (x1 + w, y1 + h), (0, 255, 0), 2) 69 | else: 70 | frame = cv2.rectangle(frame, (x1, y1), (x1 + w, y1 + h), (0, 0, 255), 2) 71 | 72 | cv2.rectangle(frame, (80, 20), (550, 80), (0, 0, 0), -1) 73 | cv2.putText(frame, 'Available spots: {} / {}'.format(str(sum(spots_status)), str(len(spots_status))), (100, 60), 74 | cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2) 75 | 76 | cv2.namedWindow('frame', cv2.WINDOW_NORMAL) 77 | cv2.imshow('frame', frame) 78 | if cv2.waitKey(25) & 0xFF == ord('q'): 79 | break 80 | 81 | frame_nmr += 1 82 | 83 | cap.release() 84 | cv2.destroyAllWindows() 85 | -------------------------------------------------------------------------------- /mask_1920_1080.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/computervisioneng/parking-space-counter/f232fc9c9b0cb29406883ca768a7750f788e0d7e/mask_1920_1080.png -------------------------------------------------------------------------------- /mask_crop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/computervisioneng/parking-space-counter/f232fc9c9b0cb29406883ca768a7750f788e0d7e/mask_crop.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | opencv-python==4.6.0.66 2 | scikit-learn==1.1.3 3 | pandas==1.5.1 4 | Pillow==9.3.0 5 | scikit-image==0.17.2 6 | matplotlib==3.6.2 7 | -------------------------------------------------------------------------------- /util.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | 3 | from skimage.transform import resize 4 | import numpy as np 5 | import cv2 6 | 7 | 8 | EMPTY = True 9 | NOT_EMPTY = False 10 | 11 | MODEL = pickle.load(open("model.p", "rb")) 12 | 13 | 14 | def empty_or_not(spot_bgr): 15 | 16 | flat_data = [] 17 | 18 | img_resized = resize(spot_bgr, (15, 15, 3)) 19 | flat_data.append(img_resized.flatten()) 20 | flat_data = np.array(flat_data) 21 | 22 | y_output = MODEL.predict(flat_data) 23 | 24 | if y_output == 0: 25 | return EMPTY 26 | else: 27 | return NOT_EMPTY 28 | 29 | 30 | def get_parking_spots_bboxes(connected_components): 31 | (totalLabels, label_ids, values, centroid) = connected_components 32 | 33 | slots = [] 34 | coef = 1 35 | for i in range(1, totalLabels): 36 | 37 | # Now extract the coordinate points 38 | x1 = int(values[i, cv2.CC_STAT_LEFT] * coef) 39 | y1 = int(values[i, cv2.CC_STAT_TOP] * coef) 40 | w = int(values[i, cv2.CC_STAT_WIDTH] * coef) 41 | h = int(values[i, cv2.CC_STAT_HEIGHT] * coef) 42 | 43 | slots.append([x1, y1, w, h]) 44 | 45 | return slots 46 | 47 | --------------------------------------------------------------------------------