目录
介绍
检测运动
将运动检测器与DNN模型相结合
下一步
在这里,我们提供并解释了使用OpenCV开发的简单运动检测器的Python代码。然后,我们将经过训练的DNN嵌入到运动检测器中,以检测视频中的驼鹿。
- 下载源34.8 KB
- 下载模型-26 MB(外部链接)
野蛮的野生生物可能给企业和房主带来痛苦。鹿、驼鹿甚至猫等动物都会对花园、庄稼和财产造成破坏。
在本系列文章中,我们将演示如何在Raspberry Pi上实时(或近实时)检测有害生物(例如驼鹿),然后采取措施消除有害生物。由于我们不想造成任何伤害,我们将通过播放巨大的噪音来吓跑害虫。
欢迎您下载该项目的源代码。我们假设您熟悉Python并且对神经网络的工作原理有基本的了解。
在上一篇文章中,我们开发并训练了一个简单的分类器DNN模型,以预测视频帧中的驼鹿外观。测试的模型准确性为97%,对于我们的目的而言似乎足够好。在本文中,我们将说明如何开发运动检测器以定位视频流中感兴趣的片段,以及如何将该检测器与分类器结合起来以捕获驼鹿。
检测运动使用OpenCV库创建基本的运动检测器不是很复杂。在大多数情况下,我们可以使用背景减法器来实现运动检测。背景减除算法将帧中的所有像素分为两个子集:背景(静态场景像素)和前景(当对象出现在帧中时动态变化的像素)。这是一个简单的运动检测器在代码中的样子:
class MD:
def __init__(self, min_w, min_h):
self.proc_width = 320
self.proc_height = 240
self.min_width = min_w
self.min_height = min_h
self.motion_objects = []
self.subtractor = cv2.createBackgroundSubtractorMOG2(history=300)
self.subtractor.setBackgroundRatio(0.005)
self.fg_mask = None
self.frame_count = 0
def process(self, frame):
p_frame = cv2.resize(frame, (self.proc_width, self.proc_height), cv2.INTER_AREA)
bg_rate = 0.001
if self.frame_count==0 :
bg_rate = 1.0
self.fg_mask = self.subtractor.apply(p_frame, bg_rate)
if bg_rate>=1.0 :
self.fg_mask[:] = 0
self.motion_objects = {}
else :
self.fg_mask = self.cleanup(self.fg_mask)
self.motion_objects = self.extract_objects(self.fg_mask, self.min_width, self.min_height)
self.frame_count = self.frame_count+1
def objects(self):
return self.motion_objects
def foreground(self):
return self.fg_mask
def cleanup(self, mask):
(ret,mask) = cv2.threshold(mask,127,255,cv2.THRESH_BINARY)
mask = cv2.medianBlur(mask, 5)
(ret,mask) = cv2.threshold(mask,127,255,cv2.THRESH_BINARY)
return mask
def extract_objects(self, mask, min_w, min_h):
(contimg, contours, hierarchy) = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
objects = []
(h, w) = mask.shape
for (i, contour) in enumerate(contours):
(rx, ry, rw, rh) = cv2.boundingRect(contour)
rx = rx/w
ry = ry/h
rw = rw/w
rh = rh/h
if (rw>=min_w) and (rh>=min_h) :
rect = (rx, ry, rw, rh)
objects.append(rect)
return objects
在类初始化时,我们设置了要检测的最小对象:min_w和min_h。这将帮助我们拒绝因风、日光变化等而出现的假“物体”。
初始化例程指定320 x 240像素的处理大小。将调整所有已处理帧的大小以适合这些尺寸,以减少要处理的像素数量,从而提高处理速度。在这里,我们还创建了MOG2背景减法器,并使用一些参数值对其进行了初始化。根据您正在处理的视频的分辨率,可能需要更改这些值。
该类的核心方法,process,检测框架中的移动物体。它首先将帧调整为处理大小,然后使用减法器的apply方法获得前景蒙版,最后提取运动段(前景像素)的边界矩形。
请注意,对于第一个处理的帧,我们将背景更新率参数bg_rate分配为1.0 ,然后将其更改为0.001。此低值是专门为测试视频场景选择的。这会导致背景更新缓慢,因此检测器有足够的时间聚焦在移动的物体上。
该实用程序方法cleanup清除了由前景遮罩引起的所有噪声。该extract_objects方法在相对坐标中评估运动对象的边界框。这是必需的,因为视频帧的大小与处理帧的大小不同。
我们需要一个包装器类来在视频文件上运行运动检测器:
class VideoMD:
def __init__(self, md):
self.md = md
def play(self, file_path):
capture = cv2.VideoCapture(file_path)
fgd_name = 'Foreground'
cv2.namedWindow(fgd_name, cv2.WINDOW_NORMAL)
cv2.resizeWindow(fgd_name, 640, 480)
md_name = 'Motion objects'
cv2.namedWindow(md_name, cv2.WINDOW_NORMAL)
cv2.resizeWindow(md_name, 640, 480)
while(True):
(ret, frame) = capture.read()
if frame is None:
break
self.md.process(frame)
objects = self.md.objects()
if len(objects)>0:
Utils.draw_objects(objects, "OBJECT", (255, 0, 0), frame)
# Display foreground
fgd = self.md.foreground()
cv2.imshow(fgd_name, fgd)
# Display the resulting frame with object rects
cv2.imshow(md_name, frame)
time.sleep(0.040)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
capture.release()
cv2.destroyAllWindows()
放置好包装后,我们可以按以下方式启动检测器:
video_file = r"C:\PI_PEST\video\moose_1.mp4"
md = MD(0.05, 0.1)
v_md = VideoMD(md)
v_md.play(video_file)
对于我们的视频文件,我们得到以下结果:
https://youtube.com/embed/l2MwPj_LH-E
将运动检测器与DNN模型相结合接下来,我们需要将运动检测算法与训练后的DNN模型结合起来。让我们从创建PestDetector类开始:
class PestDetector:
def __init__(self, proto, model, size):
self.net = cv2.dnn.readNetFromCaffe(proto, model)
self.size = size
def get_blob(self, frame, obj):
(h, w, c) = frame.shape
(rx, ry, rw, rh) = obj
rx = int(w*rx)
ry = int(h*ry)
rw = int(w*rw)
rh = int(h*rh)
if rh>rw :
dx = int((rh-rw)/2)
rx = rx-dx
rw = rh
if rxw :
vx = w-(rx+rw)
rw = rw - vx
rh = rh - vx
else :
if rw>rh :
dy = int((rw-rh)/2)
ry = ry-dy
rh = rw
if ryh :
vy = h-(ry+rh)
rh = rh - vy
rw = rw - vy
img = frame[ry:ry+rh, rx:rx+rw]
roi = (rx/w, ry/h, rw/w, rh/h)
resized = cv2.resize(img, (self.size, self.size), cv2.INTER_AREA)
blob = cv2.dnn.blobFromImage(resized, 1.0, (self.size, self.size), None, False, False)
return (roi, blob)
def detect(self, frame, obj):
(roi, blob) = self.get_blob(frame, obj)
self.net.setInput(blob)
detection = self.net.forward()
classes = detection.argmax(axis=1)
class_num = classes[0]
class_conf = detection[0][class_num]
return (roi, (class_num, class_conf))
该类类似于我们在本系列第二篇文章中讨论的SSD类。初始化时,它将基于指定的DNN模型创建一个神经网络。该detect方法接收框架和对象(如果是框架,则为移动对象周围的矩形),并确定对象的类别。
请注意,该类的get_blob方法与SSD类的相应方法有何不同。此方法将矩形段重塑为正方形,以满足DNN分类器的输入要求。
接下来,我们将略微修改VideoMD类以使其适用于运动检测和DNN分类:
class VideoPD:
def __init__(self, md, pd, thresh):
self.md = md
self.pd = pd
self.thresh = thresh
def play(self, file_path):
capture = cv2.VideoCapture(file_path)
md_name = 'Motion objects'
cv2.namedWindow(md_name, cv2.WINDOW_NORMAL)
cv2.resizeWindow(md_name, 640, 480)
while(True):
(ret, frame) = capture.read()
if frame is None:
break
self.md.process(frame)
objects = self.md.objects()
l = len(objects)
if l>0:
Utils.draw_objects(objects, "OBJECT", (255, 0, 0), frame)
pests = []
if l>0 :
for (i, obj) in enumerate(objects) :
(roi, (class_num, class_conf)) = self.pd.detect(frame, obj)
if (class_num>0) and (class_conf>=self.thresh) :
pests.append(roi)
k = len(pests)
if k>0:
Utils.draw_objects(pests, "PEST", (0, 0, 255), frame)
# Display the resulting frame with object rects
cv2.imshow(md_name, frame)
time.sleep(0.040)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
capture.release()
cv2.destroyAllWindows()
最后,我们可以使用以下代码启动驼鹿检测器:
video_file = r"C:\PI_PEST\video\moose_1.mp4"
md = MD(0.05, 0.1)
proto = r"C:\PI_PEST\net\moose.prototxt"
model = r"C:\PI_PEST\net\moose.caffemodel"
pd = PestDetector(proto, model, 128)
v_pd = VideoPD(md, pd, 0.99)
v_pd.play(video_file)
这是生成的视频:
https://youtube.com/embed/bW3U8X60q-E
正如我们所看到的,我们开发的解决方案几乎每次都可以检测到驼鹿。
下一步在接下来的文章中,我们将在Raspberry Pi 3设备上测试我们的检测算法,并通过播放一个响亮的sound.Eliminator来创建害虫消除器的“吓跑害虫”部分。
https://www.codeproject.com/Articles/5289753/Detecting-Pests-with-a-Motion-Detector-and-Trained