使用Python寻找免费的停车位

图片

我住在一个好的城市。 但是,与其他许多方面一样,寻找停车位始终成为一种考验。 自由空间很快就会占据,即使您有自己的空间,朋友也很难打电话给您,因为他们无处可泊。

因此,我决定将相机对准窗户并进行深度学习,以便计算机可以告诉我何时有空间可用:

图片

听起来可能很复杂,但是实际上编写具有深度学习的工作原型是快速简便的。 所有必需的组件已经存在-您只需要知道在哪里找到它们以及如何将它们组合在一起即可。

因此,让我们玩得开心,使用Python和深度学习编写一个准确的免费停车通知系统

分解任务


当我们要使用机器学习解决一个困难的任务时,第一步是将其分解为一系列简单的任务。 然后,我们可以使用各种工具来解决每个问题。 通过将几个简单的解决方案组合在一起,我们得到了一个功能复杂的系统。

这是我完成任务的方式:

图片

从网络摄像头定向到窗口的视频流进入传送带输入:

图片

通过管道,我们将一次传输视频的每一帧。

第一步是识别车架中所有可能的停车位。 显然,在我们可以搜索空闲位置之前,我们需要了解图像的哪些部分有停车位。

然后,在每个框架上需要找到所有的汽车。 这将使我们能够跟踪每台机器在机架之间的运动。

第三步是确定哪些地方被机器占用,哪些没有。 为此,请合并前两个步骤的结果。

最后,当停车位空闲时,程序应发送警报。 这将取决于视频帧之间机器位置的变化。

这些步骤中的每一个都可以使用不同的技术以不同的方式完成。 组成该输送机没有单一的对与错方法;不同的方法会有其优点和缺点。 让我们更详细地处理每个步骤。

我们认识到停车位


这是我们的相机看到的内容:

图片

我们需要以某种方式扫描此图像并获取要停放的地方的列表:

图片

“在额头上”的解决方案将是简单地手动对所有停车位的位置进行硬编码,而不是自动识别它们。 但是在这种情况下,如果我们移动摄像机或想在另一条街道上寻找停车位,则必须再次执行整个过程。 听起来不错,所以让我们寻找一种识别停车位的自动方法。

或者,您可以在图像中搜索停车收费表,并假设它们旁边都有一个停车位:

图片

但是,使用这种方法,并非一切都那么顺利。 首先,并不是每个停车位都设有停车收费表,并且的确,我们对寻找无需付费的停车位更感兴趣。 其次,停车收费表的位置不会告诉我们有关停车位的任何信息,而只是让我们作一个假设。

另一个想法是创建一个对象识别模型,该模型寻找道路上绘制的停车位标记:

图片

但是这种方法是马马虎虎。 首先,在我市,所有这些标记都很小,很难在远处看到,因此将很难使用计算机检测到它们。 其次,这条街上到处都是各种各样的其他线条和标记。 将停车标记与行车道分隔线和人行横道分开很难。

当您遇到乍一看似乎很困难的问题时,请花几分钟时间找到另一种解决问题的方法,这将有助于避免某些技术问题。 有什么停车位? 这只是长时间停放汽车的地方。 也许我们根本不需要识别停车位。 我们为什么不仅仅识别停滞不前的汽车,而不假设它们停在停车位?

换句话说,停车位位于汽车长时间站立的位置:

图片

因此,如果我们能够识别出汽车并找出其中哪一辆在框架之间不移动,我们就可以猜出停车位在哪里。 就这么简单-进行机器识别!

识别汽车


在视频帧上识别汽车是经典的对象识别任务。 我们可以使用许多机器学习方法进行识别。 从“老派”到“新派”,以下是其中的一些:

  • 您可以基于HOG(定向梯度直方图,方向梯度直方图)训练检测器,并在整个图像中遍历以找到所有汽车。 这种不使用深度学习的旧方法工作相对较快,但是对于以不同方式放置的机器则无法很好地应对。
  • 您可以训练基于CNN的检测器(卷积神经网络,一个卷积神经网络)并遍历整个图像,直到找到所有机器。 由于我们需要使用CNN扫描几次图像以找到所有机器,因此该方法确实有效,但效率不高。 尽管可以找到位于不同位置的机器,但与HOG检测器相比,我们需要更多的训练数据。
  • 您可以使用诸如Mask R-CNN,Faster R-CNN或YOLO之类的深度学习新方法,它将CNN的准确性和一系列技术技巧相结合,从而大大提高识别速度。 如果我们有很多数据可以训练模型,那么此类模型将在GPU上相对较快地运行。

在一般情况下,我们需要最简单的解决方案,该解决方案将按预期方式工作,并且需要最少的培训数据。 不需要是最新最快的算法。 但是,特别是在我们的情况下,尽管Mask R-CNN相当新且快速,但却是一个合理的选择。

Mask R-CNN架构的设计方式是,它可以识别整个图像中的对象,有效地消耗资源,并且不使用滑动窗口方法。 换句话说,它的工作速度非常快。 使用现代的GPU,我们将能够以每秒几帧的速度识别高分辨率的视频对象。 对于我们的项目,这应该足够了。

此外,Mask R-CNN还提供有关每个已识别对象的大量信息。 大多数识别算法只为每个对象返回一个边界框。 但是,Mask R-CNN不仅会给我们每个对象的位置,还会给我们它的轮廓(遮罩):

图片

要训​​练Mask R-CNN,我们需要很多要识别的物体图像。 我们可以到外面去,给汽车拍照,然后在照片上做标记,这需要几天的工作。 幸运的是,汽车是人们经常想要识别的那些对象之一,因此已经存在一些带有汽车图像的公共数据集。

其中之一是流行的SOCO 数据集 (Context中的Common Objects的缩写),其中包含带有对象蒙版注释的图像。 该数据集包含超过12,000张带有已标记机器的图像。 这是来自数据集的示例图像:

图片

这样的数据对于训练基于Mask R-CNN的模型非常有用。

但是牵着马,有更好的消息! 我们并不是第一个想要使用COCO数据集训练模型的人-许多人已经在我们之前完成了这项工作并分享了他们的结果。 因此,我们无需训练模型,而可以选择现成的可以识别汽车的模型。 对于我们的项目,我们将使用Matterport的开源模型。

如果我们将摄像机的图像提供给该模型的输入,这就是我们已经“开箱即用”的结果:

图片

该模型不仅识别汽车,还识别交通信号灯和人等物体。 她把树当做室内植物,真是可笑。

对于每个识别的对象,Mask R-CNN模型返回4件事:

  • 检测到的对象类型(整数)。 预先训练的COCO模型可以识别80种不同的常见物体,例如汽车和卡车。 可以在此处找到它们的完整列表
  • 识别结果的置信度。 数字越高,模型对物体识别的信心就越大。
  • 图像中像素的XY坐标形式的对象的边界框。
  • 一个“遮罩”,显示边界框中的哪些像素是对象的一部分。 使用遮罩数据,您可以找到对象的轮廓。

以下是使用预训练的Mask R-CNN和OpenCV模型检测机器边界框的Python代码:

import numpy as np import cv2 import mrcnn.config import mrcnn.utils from mrcnn.model import MaskRCNN from pathlib import Path # ,     Mask-RCNN. class MaskRCNNConfig(mrcnn.config.Config): NAME = "coco_pretrained_model_config" IMAGES_PER_GPU = 1 GPU_COUNT = 1 NUM_CLASSES = 1 + 80 #   COCO  80  + 1  . DETECTION_MIN_CONFIDENCE = 0.6 #    ,    . def get_car_boxes(boxes, class_ids): car_boxes = [] for i, box in enumerate(boxes): #     ,   . if class_ids[i] in [3, 8, 6]: car_boxes.append(box) return np.array(car_boxes) #   . ROOT_DIR = Path(".") #       . MODEL_DIR = ROOT_DIR / "logs" #       . COCO_MODEL_PATH = ROOT_DIR / "mask_rcnn_coco.h5" #   COCO  . if not COCO_MODEL_PATH.exists(): mrcnn.utils.download_trained_weights(COCO_MODEL_PATH) #     . IMAGE_DIR = ROOT_DIR / "images" #      —   0,    ,   . VIDEO_SOURCE = "test_images/parking.mp4" #   Mask-RCNN   . model = MaskRCNN(mode="inference", model_dir=MODEL_DIR, config=MaskRCNNConfig()) #   . model.load_weights(COCO_MODEL_PATH, by_name=True) #   . parked_car_boxes = None #  ,     . video_capture = cv2.VideoCapture(VIDEO_SOURCE) #      . while video_capture.isOpened(): success, frame = video_capture.read() if not success: break #      BGR ( OpenCV)  RGB. rgb_image = frame[:, :, ::-1] #    Mask R-CNN   . results = model.detect([rgb_image], verbose=0) # Mask R-CNN ,       . #     ,     . r = results[0] #  r    : # - r['rois'] —      ; # - r['class_ids'] —  () ; # - r['scores'] —  ; # - r['masks'] —   (    ). #      . car_boxes = get_car_boxes(r['rois'], r['class_ids']) print("Cars found in frame of video:") #     . for box in car_boxes: print("Car:", box) y1, x1, y2, x2 = box #  . cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 1) #    . cv2.imshow('Video', frame) #  'q',  . if cv2.waitKey(1) & 0xFF == ord('q'): break #    . video_capture.release() cv2.destroyAllWindows() 

运行此脚本后,屏幕上将出现一幅图像,每张检测到的机器周围都有一个框:

图片

同样,每台机器的坐标将显示在控制台中:

 Cars found in frame of video: Car: [492 871 551 961] Car: [450 819 509 913] Car: [411 774 470 856] 

因此,我们学会了识别图像中的汽车。

我们认识到空的停车位


我们知道每台机器的像素坐标。 通过连续几个帧查看,我们可以轻松确定哪些车没有移动,并假设有停车位。 但是如何理解汽车离开了停车场呢?

问题是机器的框架彼此部分重叠:

图片

因此,如果您想象每个框架代表一个停车位,则可能是机器将其部分占用,而实际上却是空的。 我们需要找到一种方法来测量两个对象的相交程度,以便仅搜索“最空白”的帧。

我们将使用一种称为“交集相交”(交集面积与总面积之比)或IoU的度量。 可以通过计算两个对象相交的像素数并除以这些对象所占据的像素数来找到IoU:

图片

因此,我们可以了解汽车的边界框架如何与停车位框架相交。 这样可以轻松确定是否免费停车。 如果IoU值很低,例如0.15,则汽车仅占停车位的一小部分。 如果它很高,例如0.6,则意味着汽车占据了大部分空间,您将无法停在那里。

由于IoU在计算机视觉中经常使用,因此相应的库很可能实现此措施。 在我们的库Mask R-CNN中,它作为函数mrcnn.utils.compute_overlaps()实现。

如果我们有停车位边界框的列表,则可以通过添加整行或两行代码来检查该框架中是否存在汽车:

  #      . car_boxes = get_car_boxes(r['rois'], r['class_ids']) # ,        . overlaps = mrcnn.utils.compute_overlaps(car_boxes, parking_areas) print(overlaps) 

结果应如下所示:

 [ [1. 0.07040032 0. 0.] [0.07040032 1. 0.07673165 0.] [0. 0. 0.02332112 0.] ] 

在此二维阵列中,每一行反映一帧停车位。 每列表示每个位置与检测到的机器之一相交的强度。 结果为1.0表示整个位置完全被汽车占据,而0.02这样的低值表示汽车已经爬入了一点位置,但是您仍然可以在上面停放。

要查找未占用的地方,您只需检查此数组中的每一行。 如果所有数字都接近零,那么该地方很可能是免费的!

但是,请记住,对象识别并不总是与实时视频完美配合。 尽管基于Mask R-CNN的模型非常准确,但有时可能会错过一帧或一帧视频中的两辆汽车。 因此,在断言该场所是免费的之前,您需要确保对视频的下5-10个下一帧保持该位置。 这样,我们可以避免系统由于视频一帧中的故障而错误地将某个地方标记为空的情况。 只要我们确保该地方在几帧内保持空闲状态,您就可以发送消息!

发送短信


当出现免费停车位时,传送带的最后一部分将发送SMS通知。

如果使用Twilio,从Python发送消息非常容易。 Twilio是一种流行的API,使用它,您只需几行代码就可以从几乎任何编程语言发送SMS。 当然,如果您喜欢其他服务,则可以使用它。 我与Twilio无关,这只是想到的第一件事。

要使用Twilio,请注册一个试用帐户 ,创建一个Twilio电话号码,并获取您的帐户身份验证信息。 然后安装客户端库:

 $ pip3 install twilio 

之后,使用以下代码发送消息:

 from twilio.rest import Client #   Twilio. twilio_account_sid = ' Twilio SID' twilio_auth_token = '   Twilio' twilio_source_phone_number = '   Twilio' #    Twilio. client = Client(twilio_account_sid, twilio_auth_token) #  SMS. message = client.messages.create( body=" ", from_=twilio_source_phone_number, to=" ,   " ) 

要添加向我们的脚本发送消息的功能,只需在此处复制此代码即可。 但是,您需要确保不会在可以看到可用空间的每一帧上发送消息。 因此,我们将有一个标志,在安装状态下,将不允许发送消息一段时间或直到腾出另一个地方。

全部放在一起


 import numpy as np import cv2 import mrcnn.config import mrcnn.utils from mrcnn.model import MaskRCNN from pathlib import Path from twilio.rest import Client # ,     Mask-RCNN. class MaskRCNNConfig(mrcnn.config.Config): NAME = "coco_pretrained_model_config" IMAGES_PER_GPU = 1 GPU_COUNT = 1 NUM_CLASSES = 1 + 80 #   COCO  80  + 1  . DETECTION_MIN_CONFIDENCE = 0.6 #    ,    . def get_car_boxes(boxes, class_ids): car_boxes = [] for i, box in enumerate(boxes): #     ,   . if class_ids[i] in [3, 8, 6]: car_boxes.append(box) return np.array(car_boxes) #  Twilio. twilio_account_sid = ' Twilio SID' twilio_auth_token = '   Twilio' twilio_phone_number = '   Twilio' destination_phone_number = ',   ' client = Client(twilio_account_sid, twilio_auth_token) #   . ROOT_DIR = Path(".") #       . MODEL_DIR = ROOT_DIR / "logs" #       . COCO_MODEL_PATH = ROOT_DIR / "mask_rcnn_coco.h5" #   COCO  . if not COCO_MODEL_PATH.exists(): mrcnn.utils.download_trained_weights(COCO_MODEL_PATH) #     . IMAGE_DIR = ROOT_DIR / "images" #      —   0,   ,   . VIDEO_SOURCE = "test_images/parking.mp4" #   Mask-RCNN   . model = MaskRCNN(mode="inference", model_dir=MODEL_DIR, config=MaskRCNNConfig()) #   . model.load_weights(COCO_MODEL_PATH, by_name=True) #   . parked_car_boxes = None #  ,     . video_capture = cv2.VideoCapture(VIDEO_SOURCE) #         . free_space_frames = 0 #    SMS? sms_sent = False #      . while video_capture.isOpened(): success, frame = video_capture.read() if not success: break #      BGR  RGB. rgb_image = frame[:, :, ::-1] #    Mask R-CNN   . results = model.detect([rgb_image], verbose=0) # Mask R-CNN ,       . #     ,     . r = results[0] #  r    : # - r['rois'] —      ; # - r['class_ids'] —  () ; # - r['scores'] —  ; # - r['masks'] —   (    ). if parked_car_boxes is None: #     — ,       . #            . parked_car_boxes = get_car_boxes(r['rois'], r['class_ids']) else: #   ,  . ,   . #     . car_boxes = get_car_boxes(r['rois'], r['class_ids']) # ,         . overlaps = mrcnn.utils.compute_overlaps(parked_car_boxes, car_boxes) # ,    ,      . free_space = False #        . for parking_area, overlap_areas in zip(parked_car_boxes, overlaps): #        #    (, ). max_IoU_overlap = np.max(overlap_areas) #         . y1, x1, y2, x2 = parking_area # ,   ,   IoU. if max_IoU_overlap < 0.15: #  !     . cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 3) # ,        . free_space = True else: #     —   . cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 1) #   IoU  . font = cv2.FONT_HERSHEY_DUPLEX cv2.putText(frame, f"{max_IoU_overlap:0.2}", (x1 + 6, y2 - 6), font, 0.3, (255, 255, 255)) #       ,   . #   ,  ,     #      . if free_space: free_space_frames += 1 else: #   ,  . free_space_frames = 0 #       ,  ,   . if free_space_frames > 10: #   SPACE AVAILABLE!!  . font = cv2.FONT_HERSHEY_DUPLEX cv2.putText(frame, f"SPACE AVAILABLE!", (10, 150), font, 3.0, (0, 255, 0), 2, cv2.FILLED) #  ,     . if not sms_sent: print("SENDING SMS!!!") message = client.messages.create( body="Parking space open - go go go!", from_=twilio_phone_number, to=destination_phone_number ) sms_sent = True #    . cv2.imshow('Video', frame) #  'q',  . if cv2.waitKey(1) & 0xFF == ord('q'): break #  'q',  . video_capture.release() cv2.destroyAllWindows() 

要运行该代码,首先需要安装Python 3.6 +, Matterport Mask R-CNNOpenCV

我专门编写了尽可能简单的代码。 例如,如果他在第一帧看到一辆汽车,他就得出结论,他们都已停放。 尝试进行试验,看看是否可以提高其可靠性。

只需更改模型正在寻找的对象的标识符,就可以将代码转变为完全不同的东西。 例如,假设您正在滑雪胜地工作。 进行了几处更改后,您可以将此脚本转换为一个系统,该系统可自动识别从坡道上跳下来的滑雪板并记录带有跳跃动作的视频。 或者,如果您在自然保护区工作,则可以创建一个计算斑马纹的系统。 您仅受您的想象力限制。

可以在电报频道Neuron (@neurondata)中阅读更多此类文章。

备用翻译链接: tproger.ru/translations/parking-searching/

所有的知识。 实验!

Source: https://habr.com/ru/post/zh-CN451164/


All Articles