远程视频监控与运动目标检测

本来是一次网络编程课的大作业,在做的过程中发现了蛮多问题,之后也一一调试完成,总的来说并不复杂,但也学到蛮多东西的,所以打算放上来

目的

随着图像处理技术和无线通信技术的发展,人们对视频监控的效果提出了更高的要求,智能监控以其实时性和智能性展现了其广阔的应用前景,越来越受到人们的重视。运动目标的检测是视频智能处理与分析的关键,也是其他后续处理的基础,本实验使用了背景减除法作为检测算法,设计了一个智能监控系统,利用OpenCv技术实现了系统各个模块的功能。新增的运动物体检测算法,给原来功能单一的监控系统带来了新的应用价值。如若推广到实际工作与生活中,会大大提高监控系统的安防性能,给用户带来最大限度的可靠性。

实现方法

python
平台:Jupyter Notebook
第三方库:cv2、numpy、socket等

目标检测模块BackgroundSubtractor

运动目标检测——OpenCV基于KNN的背景减除算法
背景减除(Background Subtraction)是许多基于计算机视觉的任务中的主要预处理步骤。对于图像某个位置的新像素值:与该像素值历史信息(包括前几帧的像素值和像素点是前景还是背景的判断)比较,如果像素值之间的差别在指定阈值内,则认为新像素值与该历史信息是匹配的,是“潜在的”一类;所有历史信息比较完毕后,如果与历史信息匹配的次数超过了设定阈值,那么:(1)新像素点被归为“潜在背景点”(2)如果被匹配的历史信息中属于背景的点个数超过设定阈值,那么新的像素点就被归为背景点将新像素点根据一定规则保存到历史信息中。通过这一方法我们能够从视频中分离出运动的物体前景,从而达到目标检测的目的。

OpenCV中的的BackgroundSubtractor类提供了这些现代的背景分割算法,使得检测效果相比传统方法更为准确与灵活。将得到的图像逐帧进行处理,进行灰度化以及高斯模糊化。遍历每一帧并计算与历史帧的区别,进行二值化和膨胀等操作,查找轮廓并进行轮廓筛选,最后判定该区域是否为运动物体所在区域,若是在原图像上画出轮廓。

通信模块Socket

Socket支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。

代码

服务器端
 import socket
 import struct
 import threading
 import cv2
 import numpy
 import pygame
   

    class Server:
    def __init__(self):
        # 设置tcp服务端的socket
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置重复使用
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        # 绑定地址和端口
        self.server.bind(('127.0.0.1', 11000))

        self.server.listen(128)

    def run(self):
        while True:
            print('等待客户端连接')
            # 等待客户端连接
            client, addr = self.server.accept()
            ProcessClient(client).start()


    class ProcessClient(threading.Thread):

    def __init__(self, client):
        super().__init__()
        self.client = client
        # 创建一个窗口
        #self.screen = pygame.display.set_mode((640, 480), 0, 32)

    def run(self):       
        fourcc = cv2.VideoWriter_fourcc(*'XVID')
        out = cv2.VideoWriter('E:\output.avi',fourcc,30,(640,480))
        while True:
            data = self.client.recv(8)
            if not data:
                break
            # 图片的长度 图片的宽高
            length, width, height = struct.unpack('ihh', data)

            imgg = b''  # 存放最终的图片数据
            while length:
                # 接收图片
                temp_size = self.client.recv(length)
                length -= len(temp_size)  # 每次减去收到的数据大小
                imgg += temp_size  # 每次收到的数据存到img里
            # 把二进制数据还原
            data = numpy.fromstring(imgg, dtype='uint8')
            # 还原成矩阵数据
            image = cv2.imdecode(data, cv2.IMREAD_UNCHANGED)
            #cv2.imshow('win1', image) 
            out.write(image)
            key = cv2.waitKey(60) & 0xff
            if key == 27:   # 按下ESC时,退出
                break

        out.release()
       cv2.destroyAllWindows()
    if __name__ == '__main__':
    server = Server()
    server.run()

检测物体

import cv2
import numpy as np
 
knn = cv2.createBackgroundSubtractorKNN(detectShadows = True) #创建KNN接口
es = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (20,12))
camera = cv2.VideoCapture("E:\output.avi")
 
def drawCnt(fn, cnt):
    if cv2.contourArea(cnt) > 1400:
        (x, y, w, h) = cv2.boundingRect(cnt)
        cv2.rectangle(fn, (x, y), (x + w, y + h), (255, 255, 0), 2)
while True:
    ret, frame = camera.read()
    if not ret:
        break
    fg = knn.apply(frame.copy()) #计算了前景掩码
    fg_bgr = cv2.cvtColor(fg, cv2.COLOR_GRAY2BGR)  #进行色彩转换
    bw_and = cv2.bitwise_and(fg_bgr, frame)
    draw = cv2.cvtColor(bw_and, cv2.COLOR_BGR2GRAY)
    draw = cv2.GaussianBlur(draw, (21, 21), 0)
    draw = cv2.threshold(draw, 20, 255, cv2.THRESH_BINARY)[1]  #二值化操作
    draw = cv2.dilate(draw, es, iterations = 2)
    contours, hierarchy = cv2.findContours(draw.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    for c in contours:
        drawCnt(frame, c)
    cv2.imshow("motion detection", frame)
    if cv2.waitKey(int(1000 / 12)) & 0xff == ord("q"):
        break
camera.release()
cv2.destroyAllWindows()

客户端
测试的时候只在自己的电脑上进行,所以用了回环接口127.0.0.1,如果有多台设备就把ip那里改掉

import socket
import struct
import time
import traceback
import cv2
import numpy

class Client(object):
    def __init__(self, addr_port=('127.0.0.1', 11000)):
        # 连接的服务器的地址
        # 连接的服务器的端口
        self.addr_port = addr_port
        # 创建套接字
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 地址端口可以复用
        self.client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 分辨率
        self.resolution = (640, 480)
    def connect(self):
        """链接服务器"""
        try:
            self.client.connect(self.addr_port)
            return True
        except Exception as e:
            traceback.print_exc()  
            print('连接失败')
            return False
            
    def send2server(self):
        """读摄像头数据 发送给服务器"""
        #camera = cv2.VideoCapture('E:\\cut.avi')  # 读取本地视频
        camera = cv2.VideoCapture(0)  # 摄像头对象
        print('isOpened:', camera.isOpened())
        while camera.isOpened():
            try:
                # 获取摄像头数据
                ret, frame = camera.read()
                # 对每一帧图片做大小处理 和大小的压缩
                frame = cv2.resize(frame, self.resolution)
                # 参1图片后缀名 参2 原图片的数据 参3图片质量 0-100 越大越清晰
                ret, img = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 100])
                # img 是被压缩后的数据 无法正常显示
                #print(img)
                #print('-------------')
                #print(img.tostring())
                # 转换为numpy格式数据
                img_code = numpy.array(img)
                # 转为二进制数据
                img = img_code.tostring()
                # 获取数据长度
                length = len(img)
                # 发送的数据  大小 宽 高 图片数据
                # 数据打包变为二进制
                # pack方法参数1 指定打包数据的数据大小  i 4字节 h代表2字节
                all_data = struct.pack('ihh', length, self.resolution[0], self.resolution[1]) + img
                self.client.send(all_data)
                time.sleep(0.01)
            except:
                camera.release()  # 释放摄像头
                traceback.print_exc()
                return

if __name__ == '__main__':
    client = Client()
    if client.connect():
        client.send2server()


拿了个监控视频来测试,效果还是可以的拿了个监控视频来测试,效果还是可以的

总结

cv2中的函数还是非常好用的,但是在传输数据的格式那里,当时写代码的时候出现一些问题,尤其是numpy格式的数据,导致视频图像一直显示不出来,以后会在这部分多加注意。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐