使用深度学习检测停车位(Python实现)

使用深度学习检测停车位(Python实现)

近年来,深度学习计算机视觉算法在各种任务已经得到了越来越多的应用。在本教程中,我将向您展示如何使用深度学习构建一个简单的停车位检测系统。让我们直接开始业务,我们将分解为三个主要部分:

  • 检测停车位。
  • 检测汽车。
  • 计算IoU。

使用深度学习检测停车位(Python实现)

系统概述

在输入视频的每一帧上,我们将首先使用Mask-RCNN对象检测模型来检测汽车及其边界框。从Mask-RCNN获得边界框后,我们将在每对边界框和停车位坐标上计算IoU。如果任何停车位的IoU值大于某个阈值,我们将认为该停车位已被占用。

依赖

  • Python 3.6
  • Tensorflow≥1.3.0
  • OpenCV
  • Matplotlib
  • Shapely

停车位检测

车位探测系统的第一步是识别停车位。有一些技巧可以做到这一点。例如,通过在一个地点定位停车线来识别停车位。这可以使用OpenCV提供的边缘检测器来完成。但是如果没有停车线呢?

我们可以使用的另一种方法是假设长时间不移动的汽车停在停车位上。换句话说,有效的停车位就是那些停着不动的车的地方。但是,这似乎也不可靠。它可能会导致假阳性和真阴性。

那么,当自动化系统看起来不可靠时,我们应该怎么做呢?我们可以手动操作。与基于空间的方法需要对每个不同的停车位进行标签和训练不同,我们只需标记一次停车场边界和周围道路区域即可为新的停车位配置我们的系统。

在这里,我们将从停车位的视频流中截取一帧,并标记停车区域。Python库matplotlib 提供了称为PolygonSelector的功能。它提供了选择多边形区域的功能。

我制作了一个简单的python脚本来标记输入视频的初始帧之一上的多边形区域。它以视频路径作为参数,并将选定多边形区域的坐标保存在pickle文件中作为输出。

使用深度学习检测停车位(Python实现)

默认情况下,此Python脚本设置为仅接受四边形。稍加修改,即可用于标记任意数量的面。

import os
import numpy as np
import cv2
import pickle
import argparse
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from matplotlib.widgets import PolygonSelector
from matplotlib.collections import PatchCollection
from shapely.geometry import box
from shapely.geometry import Polygon as shapely_poly

points = []
prev_points = []
patches = []
total_points = []
breaker = False

class SelectFromCollection(object):
 def __init__(self, ax):
 self.canvas = ax.figure.canvas
 self.poly = PolygonSelector(ax, self.onselect)
 self.ind = []

 def onselect(self, verts):
 global points
 points = verts
 self.canvas.draw_idle()

 def disconnect(self):
 self.poly.disconnect_events()
 self.canvas.draw_idle()

def break_loop(event):
 global breaker
 global globSelect
 global savePath
 if event.key == 'b':
 globSelect.disconnect()
 if os.path.exists(savePath):
 os.remove(savePath)

 print("data saved in "+ savePath + " file") 
 with open(savePath, 'wb') as f:
 pickle.dump(total_points, f, protocol=pickle.HIGHEST_PROTOCOL)
 exit()

def onkeypress(event):
 global points, prev_points, total_points
 if event.key == 'n': 
 pts = np.array(points, dtype=np.int32) 
 if points != prev_points and len(set(points)) == 4:
 print("Points : "+str(pts))
 patches.append(Polygon(pts))
 total_points.append(pts)
 prev_points = points

if __name__ == '__main__':
 parser = argparse.ArgumentParser()
 parser.add_argument('video_path', help="Path of video file")
 parser.add_argument('--out_file', help="Name of the output file", default="regions.p")
 args = parser.parse_args()

 global globSelect
 global savePath
 savePath = args.out_file if args.out_file.endswith(".p") else args.out_file+".p"

 print("\n> Select a region in the figure by enclosing them within a quadrilateral.")
 print("> Press the 'f' key to go full screen.")
 print("> Press the 'esc' key to discard current quadrilateral.")
 print("> Try holding the 'shift' key to move all of the vertices.")
 print("> Try holding the 'ctrl' key to move a single vertex.")
 print("> After marking a quadrilateral press 'n' to save current quadrilateral and then press 'q' to start marking a new quadrilateral")
 print("> When you are done press 'b' to Exit the program\n")

 video_capture = cv2.VideoCapture(args.video_path)
 cnt=0
 rgb_image = None
 while video_capture.isOpened():
 success, frame = video_capture.read()
 if not success:
 break
 if cnt == 5:
 rgb_image = frame[:, :, ::-1]
 cnt += 1
 video_capture.release()

 while True:
 fig, ax = plt.subplots()
 image = rgb_image
 ax.imshow(image)

 p = PatchCollection(patches, alpha=0.7)
 p.set_array(10*np.ones(len(patches)))
 ax.add_collection(p)

 globSelect = SelectFromCollection(ax)
 bbox = plt.connect('key_press_event', onkeypress)
 break_event = plt.connect('key_press_event', break_loop)
 plt.show()
 globSelect.disconnect()

使用深度学习检测停车位(Python实现)

使用深度学习检测停车位(Python实现)

在视频中检测汽车

如前所述,要检测视频中的汽车,我们将使用Mask-RCNN。它是一个卷积神经网络,对来自几个数据集(包括COCO数据集)的数百万个图像和视频进行了训练,以检测各种对象及其边界。 Mask-RCNN建立在Faster-RCNN对象检测模型的基础上。

除了每个检测到的对象的类标签和边界框坐标外,Mask R-CNN还将返回图像中每个检测到的对象的像pixel-wise mask。这种pixel-wise masking称为“ 实例分割”。我们在计算机视觉领域所看到的一些最新进展,包括自动驾驶汽车、机器人等,都是由实例分割技术推动的。

M-RCNN将用于视频的每一帧,它将返回一个字典,其中包含边界框坐标、检测对象的masks、每个预测的置信度和检测对象的class_id。现在使用class_ids过滤掉汽车,卡车和公共汽车的边界框。然后,我们将在下一步中使用这些框来计算IoU。

使用深度学习检测停车位(Python实现)

M-RCNN的输出

计算IoU

使用深度学习检测停车位(Python实现)

如前所述,我们将为每对停车位坐标和汽车边界框计算IoU。如果一对的IoU高于某个阈值,我们将认为该停车位已被占用。

为了计算IoU,我们将使用一个名为Shapely的python库。它带有一个易于使用的API,我们将使用它来计算两个多边形的相交面积和并集面积。

检测器代码

现在让我们以python脚本的形式将所有片段放在一起。

import git
import os

if not os.path.exists("Mask_RCNN"):
 print("Cloning M-RCNN repository...")
 git.Git("./").clone("https://github.com/matterport/Mask_RCNN.git")

import numpy as np
import cv2
import Mask_RCNN.mrcnn.config
import Mask_RCNN.mrcnn.utils
from Mask_RCNN.mrcnn.model import MaskRCNN
from pathlib import Path
import pickle
import argparse

from shapely.geometry import box
from shapely.geometry import Polygon as shapely_poly
import io
import base64

class Config(Mask_RCNN.mrcnn.config.Config):
 NAME = "model_config"
 IMAGES_PER_GPU = 1
 GPU_COUNT = 1
 NUM_CLASSES = 81

config = Config()
config.display()

ROOT_DIR = os.getcwd()
MODEL_DIR = os.path.join(ROOT_DIR, "logs")
COCO_MODEL_PATH = os.path.join(ROOT_DIR, "mask_rcnn_coco.h5")

print(COCO_MODEL_PATH)
if not os.path.exists(COCO_MODEL_PATH):
 Mask_RCNN.mrcnn.utils.download_trained_weights(COCO_MODEL_PATH)

model = MaskRCNN(mode="inference", model_dir=MODEL_DIR, config=Config())
model.load_weights(COCO_MODEL_PATH, by_name=True)

def get_cars(boxes, class_ids):
 cars = []
 for i, box in enumerate(boxes):
 if class_ids[i] in [3, 8, 6]:
 cars.append(box)
 return np.array(cars)

def compute_overlaps(parked_car_boxes, car_boxes):
 new_car_boxes = []
 for box in car_boxes:
 y1 = box[0]
 x1 = box[1]
 y2 = box[2]
 x2 = box[3]

 p1 = (x1, y1)
 p2 = (x2, y1)
 p3 = (x2, y2)
 p4 = (x1, y2)
 new_car_boxes.append([p1, p2, p3, p4])

 overlaps = np.zeros((len(parked_car_boxes), len(new_car_boxes)))
 for i in range(len(parked_car_boxes)):
 for j in range(car_boxes.shape[0]):
 pol1_xy = parked_car_boxes[i]
 pol2_xy = new_car_boxes[j]
 polygon1_shape = shapely_poly(pol1_xy)
 polygon2_shape = shapely_poly(pol2_xy)

 polygon_intersection = polygon1_shape.intersection(polygon2_shape).area
 polygon_union = polygon1_shape.union(polygon2_shape).area
 IOU = polygon_intersection / polygon_union
 overlaps[i][j] = IOU
 return overlaps

if __name__ == "__main__":
 parser = argparse.ArgumentParser()
 parser.add_argument('video_path', help="Video file")
 parser.add_argument('regions_path', help="Regions file", default="regions.p")
 args = parser.parse_args()

 regions = args.regions_path
 with open(regions, 'rb') as f:
 parked_car_boxes = pickle.load(f)

 VIDEO_SOURCE = args.video_path
 alpha = 0.6
 video_capture = cv2.VideoCapture(VIDEO_SOURCE)
 video_FourCC = cv2.VideoWriter_fourcc('M','J','P','G')
 video_fps = video_capture.get(cv2.CAP_PROP_FPS)
 video_size = (int(video_capture.get(cv2.CAP_PROP_FRAME_WIDTH)),
 int(video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
 out = cv2.VideoWriter("out.avi", video_FourCC, video_fps, video_size)

 while video_capture.isOpened():
 success, frame = video_capture.read()
 overlay = frame.copy()
 if not success:
 break

 rgb_image = frame[:, :, ::-1]
 results = model.detect([rgb_image], verbose=0)

 cars = get_cars(results[0]['rois'], results[0]['class_ids'])
 overlaps = compute_overlaps(parked_car_boxes, cars)

 for parking_area, overlap_areas in zip(parked_car_boxes, overlaps):
 max_IoU_overlap = np.max(overlap_areas)
 if max_IoU_overlap < 0.15:
 cv2.fillPoly(overlay, [np.array(parking_area)], (71, 27, 92))
 free_space = True 
 cv2.addWeighted(overlay, alpha, frame, 1 - alpha, 0, frame)

 cv2.imshow('output', frame)
 out.write(frame)
 if cv2.waitKey(1) & 0xFF == ord('q'):
 break

 video_capture.release()
 out.release()
 cv2.destroyAllWindows()
 print("output saved as out.avi")

使用深度学习检测停车位(Python实现)

使用深度学习检测停车位(Python实现)

最后

在本教程中,我们探讨了如何使用Mask-RCNN制作简单的停车位检测系统。之所以在本教程中使用Mask-RCNN是因为其具有易于使用的API和更高的准确性。为了获得更好的帧频,您可以选择YOLO对象检测模型。YOLO比M-RCNN快得多,但与M-RCNN相比,准确性较差。

为了获得更好的结果,我们计算IoU,而不是使用边界框。在计算中使用masks将产生更准确的IoU值。

可以在此代码之上添加各种功能,以使其在实际场景中可用和可扩展。例如,可以在此代码的顶部添加一个Web界面来监视停车位,或者我们可以添加一项功能来发送推送通知,以提醒用户有关空位的信息等。