边缘设备上的实时AI人员检测:使用预先训练的SSD模型检测人员

在本系列的上一篇文章中,我们选择了两种SSD模型进行进一步工作,一种基于MobileNet,另一种基于SqueezeNet。在本文中,我们将开发一些Python代码,这些代码使我们能够使用这些模型检测图像中的人物。

选定的DNN被实现为Caffe模型。Caffe模型由两部分组成:模型结构(.prototxt)文件和训练后的模型(.caffemodel)。Caffe模型结构以类似于JSON的格式编写。训练后的模型是CNN内核和其他训练后数据的二进制序列化。在本系列的第一篇文章中,我们提到了将CaffePython OpenCV结合使用。这是什么意思?我们应该同时安装两个框架,OpenCVCaffe吗?幸运的是,没有,只有OpenCV库。该框架包括DNN模块,该模块直接支持使用TensorFlowCaffeTorchDarknet等开发的网络模型。所以——幸运的我们!—— OpenCV框架允许同时使用计算机视觉算法和深度神经网络。这就是我们所需要的。

让我们从两个实用程序类开始我们的Python代码:

import cv2
import numpy as np
import os
 
class CaffeModelLoader:	
    @staticmethod
    def load(proto, model):
    	  net = cv2.dnn.readNetFromCaffe(proto, model)
    	  return net
 
class FrameProcessor:	
    def __init__(self, size, scale, mean):
    	  self.size = size
    	  self.scale = scale
    	  self.mean = mean
	
    def get_blob(self, frame):
    	  img = frame
            (h, w, c) = frame.shape
    	  if w>h :
            dx = int((w-h)/2)
            img = frame[0:h, dx:dx+h]
        	
   	  resized = cv2.resize(img, (self.size, self.size), cv2.INTER_AREA)
        blob = cv2.dnn.blobFromImage(resized, self.scale, (self.size, self.size), self.mean)
        return blob

CaffeModelLoader类具有一个静态方法来加载来自磁盘的Caffe模型。之后的FrameProcessor类旨在将数据从图像转换为DNN的特定格式。该类的构造函数接收三个参数。size参数定义用于DNN处理的输入数据的大小。用于图像处理的卷积网络几乎总是使用正方形图像作为输入,因此我们仅为宽度和高度指定一个值。scalemean参数用于将数据缩放到训练SSD所用的值范围。该类的唯一方法是get_blob,它接收图像并返回blob——神经网络处理的一种特殊结构。为了接收blob,首先将图像调整为指定的正方形。然后,使用OpenCVDNN模块中的blobFromImage方法使用指定的scalesizemean值,从调整大小后的图像创建Blob 

注意get_blob方法开头的代码。该代码实现了一些技巧:我们修剪非正方形图像以仅获取图像的中心正方形部分,如下图所示:

此技巧旨在使图像的纵横比保持恒定。如果宽高比改变,图像将变形,并且物体检测的精度将降低。此技巧的一个缺点是,我们只会在图像的中央正方形部分(上图中以蓝色显示)检测到人员。

现在让我们看一下使用SSD模型进行人检测的主要类别:

class SSD:	
    def __init__(self, frame_proc, ssd_net):
    	  self.proc = frame_proc
 	  self.net = ssd_net
	
    def detect(self, frame):
        blob = self.proc.get_blob(frame)
    	  self.net.setInput(blob)
  	  detections = self.net.forward()
    	  # detected object count
    	  k = detections.shape[2]
    	  obj_data = []
    	  for i in np.arange(0, k):
            obj = detections[0, 0, i, :]
            obj_data.append(obj)
        	
    	  return obj_data
 
    def get_object(self, frame, data):
        confidence = int(data[2]*100.0)
    	  (h, w, c) = frame.shape
    	  r_x = int(data[3]*h)
    	  r_y = int(data[4]*h)
    	  r_w = int((data[5]-data[3])*h)
    	  r_h = int((data[6]-data[4])*h)
    	
    	  if w>h :
            dx = int((w-h)/2)
            r_x = r_x+dx
    	
    	  obj_rect = (r_x, r_y, r_w, r_h)
    	
    	  return (confidence, obj_rect)
    	
    def get_objects(self, frame, obj_data, class_num, min_confidence):
        objects = []
    	  for (i, data) in enumerate(obj_data):
            obj_class = int(data[1])
        	obj_confidence = data[2]
        	if obj_class==class_num and obj_confidence>=min_confidence :
                obj = self.get_object(frame, data)
                objects.append(obj)
            	
        return objects

上面的类的构造函数有两个参数:frame_proc用于将图像转换为blobssd_net用于检测对象。主要方法detect接收帧(图像)作为输入,并使用指定的帧处理器从该帧获取blob。该blob用作网络的输入,我们使用该forward方法获得检测结果。这些检测结果显示为4级数组(张量)。我们不会分析整个张量。我们只需要数组的第二维。我们将从检测结果中提取出来并返回结果——对象数据列表。

对象数据是一个实数组。这是一个例子:

[array([ 0.	    , 15.    	,  0.90723044,  0.56916684,  0.6017439 ,
     	0.68543154,  0.93739873], dtype=float32)]

该数组包含七个数字:

  1. 检测到的物体数
  2. 类数量
  3. 对象属于给定类的置信度
  4. 对象ROI的四个左上角和右下角坐标(这些坐标相对于Blob大小)

该类的第二种方法将检测数据转换为更简单的格式,以备将来使用。它将相对置信度转换为百分比值,并将相对ROI坐标转换为整数数据——原始图像中的像素坐标。该方法考虑了从原始帧的中心正方形提取blob数据的事实。

最后, get_objects方法仅从检测数据中提取具有指定类别和足够置信度的对象。由于DNN模型可以检测到二十个不同类别 person对象,因此我们仅对该类别的检测进行过滤,以确保检测到的对象确实是人类,因此我们指定了高置信度阈值。

另一个实用程序类——用于将检测到的对象绘制到图像中以可视化结果:

class Utils:	
    @staticmethod
    def draw_object(obj, label, color, frame):
        (confidence, (x1, y1, w, h)) =  obj
        x2 = x1+w
    	  y2 = y1+h
    	  cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
    	  y3 = y1-12
    	  text = label + " " + str(confidence)+"%"
        cv2.putText(frame, text, (x1, y3), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 1, cv2.LINE_AA)
    	
    @staticmethod
    def draw_objects(objects, label, color, frame):
        for (i, obj) in enumerate(objects):
            Utils.draw_object(obj, label, color, frame)

现在我们可以编写代码来启动人检测算法:

proto_file = r"C:\PI_RPD\mobilenet.prototxt"
model_file = r"C:\PI_RPD\mobilenet.caffemodel"
ssd_net = CaffeModelLoader.load(proto_file, model_file)
print("Caffe model loaded from: "+model_file)
 
proc_frame_size = 300
# frame processor for MobileNet
ssd_proc = FrameProcessor(proc_frame_size, 1.0/127.5, 127.5)
person_class = 15
 
ssd = SSD(ssd_proc, ssd_net)
 
im_dir = r"C:\PI_RPD\test_images"
im_name = "woman_640x480_01.png"
im_path = os.path.join(im_dir, im_name)
image = cv2.imread(im_path)
print("Image read from: "+im_path)
 
obj_data = ssd.detect(image)
persons = ssd.get_objects(image, obj_data, person_class, 0.5)
person_count = len(persons)
print("Person count on the image: "+str(person_count))
Utils.draw_objects(persons, "PERSON", (0, 0, 255), image)
 
res_dir = r"C:\PI_RPD\results"
res_path = os.path.join(res_dir, im_name)
cv2.imwrite(res_path, image)
print("Result written to: "+res_path)

该代码实现了一个size= 300的帧处理器,因为我们使用的模型适用于300 x 300像素的图像。scalemean参数都被用于MobileNet模型训练,同样的值。这些值必须始终分配给模型的训练值,否则模型的精度会降低。person_class的值是15,因为人体是模型上下文中的第15个类。

在示例图像上运行代码会产生以下结果:

我们使用非常简单的案例来检测人员。目的只是检查我们的代码是否正确工作,并且DNN模型可以预测人在图像中的存在。而且有效!

下一步

下一步是在Raspberry Pi设备上启动我们的代码。在接下来的文章中,我们将看看如何在设备上安装PythonOpenCV的和运行代码。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值