使用运动探测器和训练有素的DNN检测害虫

目录

介绍

检测运动

将运动检测器与DNN模型相结合

下一步


在这里,我们提供并解释了使用OpenCV开发的简单运动检测器的Python代码。然后,我们将经过训练的DNN嵌入到运动检测器中,以检测视频中的驼鹿。

介绍

野蛮的野生生物可能给企业和房主带来痛苦。鹿、驼鹿甚至猫等动物都会对花园、庄稼和财产造成破坏。

在本系列文章中,我们将演示如何在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_wmin_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 rx<0 :
                rx = 0
            if (rx+rw)>w :
                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 ry<0 :
                    ry = 0
                if (ry+rh)>h :
                    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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值