无论是商业农场还是家庭花园,动物如鹿、驼鹿甚至猫都可能对作物和财产造成损害。在本系列文章中,将展示如何在树莓派上实时(或近实时)检测害虫(如驼鹿),然后采取措施将其驱赶。由于不想造成伤害,将专注于通过播放响亮的噪音来吓走害虫。
欢迎下载项目的源代码。假设熟悉Python并且对神经网络的工作原理有基本的了解。
在本系列的前一篇文章中,比较了两种用于检测害虫的深度神经网络(DNN)类型:检测器和分类器。检测器胜出。在本文中,将开发使用预训练检测DNN的Python代码来检测害虫。
有几种常见的对象检测网络架构,如Faster-RCNN、单次检测器(SSD)和You Only Look Once (YOLO)。由于网络需要在具有有限内存和CPU的边缘设备上运行,将使用MobileNet单次检测器(SSD)架构。MobileNet SSD是一种轻量级的对象检测网络,它在移动和边缘设备上表现良好。它在Pascal VOC 2012数据集上进行了训练,其中包含一些可能代表害虫的类别,如猫、牛、狗、马和羊。
将使用与之前文章系列中用于人类检测的算法相同的算法来检测视频中的害虫。
首先,需要修改MobileNet代码以使其检测害虫。让从创建一些实用程序类开始,以使这项任务更简单:
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, False, False)
return blob
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 + "\n" + 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)
CaffeModelLoader类使用提供的原型和模型文件路径从磁盘加载Caffe模型。下一个实用程序类FrameProcessor,将帧转换为blob(作为CNN输入的特别结构化数据)。最后,Utils类在帧中检测到的任何对象周围绘制边界矩形。实用程序类使用的大多数方法都来自Python版本的OpenCV库。让详细看看这些。
实用程序类到此为止。接下来,将编写实际检测害虫的代码。
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()
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
类中的关键方法是detect和get_objects。detect方法将加载的DNN模型应用于每一帧,以检测所有可能类别的对象。get_objects方法查看检测到的对象,并仅选择那些既属于指定类别又具有高正确检测概率(置信度)的对象。
然后将VideoSSD类,它在整个视频剪辑上运行害虫检测:
class VideoSSD:
def __init__(self, ssd):
self.ssd = ssd
def detect(self, video, class_num, min_confidence, class_name):
detection_num = 0
capture = cv2.VideoCapture(video)
img = None
dname = 'Pest detections'
cv2.namedWindow(dname, cv2.WINDOW_NORMAL)
cv2.resizeWindow(dname, 1280, 960)
while True:
(ret, frame) = capture.read()
if frame is None:
break
obj_data = self.ssd.detect(frame)
class_objects = self.ssd.get_objects(frame, obj_data, class_num, min_confidence)
p_count = len(class_objects)
detection_num += p_count
if len(class_objects) > 0:
Utils.draw_objects(class_objects, class_name, (0, 0, 255), frame)
cv2.imshow(dname, frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
capture.release()
cv2.destroyAllWindows()
return detection_num
类中唯一的方法是detect。它处理从视频文件中提取的所有帧。在每一帧中,它检测由class_num参数指定类别的所有对象,然后在检测到的对象周围的帧上显示边界矩形。
让启动代码,看看它如何处理视频文件。以下代码加载视频文件并尝试检测狗:
proto_file = r"C:\PI_PEST\net\mobilenet.prototxt"
model_file = r"C:\PI_PEST\net\mobilenet.caffemodel"
ssd_net = CaffeModelLoader.load(proto_file, model_file)
mobile_proc_frame_size = 300
ssd_proc = FrameProcessor(mobile_proc_frame_size, 1.0/127.5, 127.5)
pest_class = 12
pest_name = "DOG"
ssd = SSD(ssd_proc, ssd_net)
video_file = r"C:\PI_PEST\video\dog_1.mp4"
video_ssd = VideoSSD(ssd)
detections = video_ssd.detect(video_file, pest_class, 0.2, pest_name)
将pest_class的值设置为12,因为在MobileNet SSD模型中,“狗”是第12类。这是在运行上述代码时捕获的视频。
正如所见,当在PC上运行SSD检测器时,它成功地在视频中检测到了狗。那么在边缘设备上呢?检测器是否能够快速处理视频流以实时检测对象?可以通过测试帧速率(以每秒帧数FPS为单位)来找出答案。