使用nodejs和python构建一个远程监控系统1.视频采集模块

使用nodejs和python构建一个远程监控系统2.主控程序编写

使用nodejs和python构建一个远程监控系统3.web模块的编写

如何使用python和nodejs构建一个远程监控系统,不说废话。

1.平台和环境

  1. python3.6,opencv3.0以上(或者cv2版本某些功能无法实现)
  2. nodejs8.9.4依赖如下
{
  "name": "video",
  "version": "1.0.0",
  "description": "简单的视频直播程序",
  "main": "config.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "keywords": [
    "直播"
  ],
  "author": "fengziyu",
  "license": "ISC",
  "dependencies": {
    "ejs": "^2.6.1",
    "express": "^4.16.3",
    "socket.io": "^2.1.1"
  }
}

2.主要思路

   使用python的opencv框架采集视频,将每一帧图像压缩为jpg格式在通过udp协议发送给nodejs编写的服务器程序,服务器程序通过websocket协议转发给用浏览器访问的客户

3.具体实现

   1.工具类编写(对于图像压缩,转码,文件操作,日志操作等等方法的封装)

#encoding=utf-8
import time
import numpy
import base64
import os
import logging
import sys
from settings import *
from PIL import Image
from io import BytesIO

#工具类
class IOUtil(object):
    #流操作工具类
    @staticmethod
    def array_to_bytes(pic,formatter="jpeg",quality=70):
        '''
        静态方法,将numpy数组转化二进制流
        :param pic: numpy数组
        :param format: 图片格式
        :param quality:压缩比,压缩比越高,产生的二进制数据越短
        :return: 
        '''
        stream = BytesIO()
        picture = Image.fromarray(pic)
        picture.save(stream,format=formatter,quality=quality)
        jepg = stream.getvalue()
        stream.close()
        return jepg
    @staticmethod
    def bytes_to_base64(byte):
        '''
        静态方法,bytes转base64编码
        :param byte: 
        :return: 
        '''
        return base64.b64encode(byte)
    @staticmethod
    def transport_rgb(frame):
        '''
        将bgr图像转化为rgb图像,或者将rgb图像转化为bgr图像
        '''
        return frame[...,::-1]
    @staticmethod
    def byte_to_package(bytes,cmd,var=1):
        '''
        将每一帧的图片流的二进制数据进行分包
        :param byte: 二进制文件
        :param cmd:命令
        :return: 
        '''
        head = [ver,len(byte),cmd]
        headPack = struct.pack("!3I", *head)
        senddata = headPack+byte
        return senddata
    @staticmethod
    def mkdir(filePath):
        '''
        创建文件夹
        '''
        if not os.path.exists(filePath):
            os.mkdir(filePath)
    @staticmethod
    def countCenter(box):
        '''
        计算一个矩形的中心
        '''
        return (int(abs(box[0][0] - box[1][0])*0.5) + box[0][0],int(abs(box[0][1] - box[1][1])*0.5) +box[0][1])
    @staticmethod
    def countBox(center):
        '''
        根据两个点计算出,x,y,c,r
        '''
        return (center[0][0],center[0][1],center[1][0]-center[0][0],center[1][1]-center[0][1])
    @staticmethod
    def getImageFileName():
        return time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime())+'.png'

#构造日志
logger = logging.getLogger(LOG_NAME)
formatter = logging.Formatter(LOG_FORMATTER)
IOUtil.mkdir(LOG_DIR);
file_handler = logging.FileHandler(LOG_DIR + LOG_FILE,encoding='utf-8')
file_handler.setFormatter(formatter)
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.addHandler(console_handler)
logger.setLevel(logging.INFO)

   这部分需要注意的是提供了一个方法用于将图像在rgb-bgr颜色模式之间转换,因为opencv使用的图像模式为bgr,而我们习惯的颜色模式是rgb

  2.配置文件

#照片保存文件夹
SCREENSHOT_DIR = "screenshots/"
#视频保存文件夹
VIDEO_DIR = "videos/"
#预警图片文件夹
WARN_DIR = "warn/"
#日志文件夹
LOG_DIR = "log/"
#日志文件
LOG_FILE = "camera.log"
#图像发送IP
IMAGE_IP = "127.0.0.1"
#图像发送端口
IMAGE_PORT = 9999
#日志输出格式
LOG_FORMATTER = "%(asctime)s %(levelname)-8s: %(message)s"
#日志输出名称
LOG_NAME = 'cameraLogger'
#是否打开本地窗口
IS_WINDOW_ON = True

   3.视频采集类(负责采集摄像头信息,拍照,录制视频等等)

#encoding=utf-8
import cv2
import numpy
import socket
import json
import threading
import os
import time
import copy
from settings import *
from threading import Thread
from utils import IOUtil,logger

'''
管理者模块,负责控制信息获取,命令获取
'''
class CameraManager(object):
    #相机管理类,负责控制信息获取
    def __init__(self,capture):
        '''
        :param capture: 摄像头对象
        :param windowManager: 钩子类,窗口管理,按键
        '''
        #从配置文件中读取截图目录和录像目录创建文件夹
        self.screenshot_dir = SCREENSHOT_DIR
        self.video_dir = VIDEO_DIR
        self._capture = capture
        #当前画面
        self._frame = None
        #录像编码
        self._videoEncoding = None
        #视频写入工具
        self._videoWriter = None
        #是否开启显示
        self._isShow = False
        #是否工作
        self._isWorking = True
        #fps
        self._fps = 0
        #是否正在写入视频
        self._videoFilename = None
        IOUtil.mkdir(self.screenshot_dir)
        IOUtil.mkdir(self.video_dir)
        logger.info("视频采集器初始化完毕!")
    def getFrame(self):
        #返回当前的帧
        return copy.copy(self._frame)
    def getFps(self):
        #获得当前fps
        return self._fps
    def isWritingVideo(self):
        #是否正在写入视频
        return self._videoFilename is not None
    def start(self):
        #开始工作
        logger.info("开启视频采集")
        frameThread = Thread(target = self._update,args=())
        frameThread.start()
        return self
    def stop(self):
        #停止工作
        self._isWorking = False
        self._videoFilename = None
        logger.info("关闭视频采集")
    def _update(self):
        #更新摄像头画面
        logger.info("视频采集线程启动...")
        while self._isWorking:
            startTime = time.time()
            if self._capture is not None:
                _,self._frame = self._capture.read()
            try:
                self._fps = 1/(time.time() - startTime)
            except:
                self._fps = 30
        logger.info("视频采集线程关闭...")
    def getImageFileName(self,filename=None,imageformat=".png"):
        #获得图片文件名
        if not filename:
            filename = time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime())
        filename += imageformat
        return os.path.join(self.screenshot_dir,filename)
    def writeImage(self):
        #写入图片文件
        logger.info("开始写入一张图片")
        imageFileName = self.getImageFileName()
        try:
            cv2.imwrite(imageFileName,self._frame)
        except Exception as e:
            logger.error("写入图片失败:"+str(e))
        logger.info("写入图片成功"+imageFileName)
    def getVideoFileName(self,filename=None,videoformat=".avi"):
        #获得视频文件名
        if not filename:
            filename = time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime())
        filename += videoformat
        return os.path.join(self.video_dir,filename)
    def startWritingVideo(self,filename=None,encoding=cv2.VideoWriter_fourcc("I","4","2","0"),videoformat=".avi"):
        #初始化写入视频文件
        logger.info("开启视频录制")
        self._videoFilename = self.getVideoFileName()
        self._videoEncoding = encoding
        Thread(target = self._writingVideo,args=()).start()
    def stopWritingVideo(self):
        #关闭视频写入
        self._videoFilename = None
        self._videoWriter = None
        self._videoEncoding = None
    def _writingVideo(self):
        #写入视频文件
        logger.info("视频采集线程启动,目标文件为:"+self._videoFilename)
        if self._videoWriter is None:
                size = (int(self._capture.get(cv2.CAP_PROP_FRAME_WIDTH)),int(self._capture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
                self._videoWriter = cv2.VideoWriter(self._videoFilename,self._videoEncoding,self._fps,size)
        while self._videoFilename is not None:
            self._videoWriter.write(self._frame)
        logger.info("写入视频完毕,文件名为:"+self._videoFilename)
    def isWorking(self):
        #工作控制
        return self._isWorking
if __name__ == "__main__":
    video = cv2.VideoCapture(0)
    camera = CameraManager(video)
    camera.start()
    while True:
        frame = camera.getFrame()
        if frame is not None:
            cv2.imshow("test",frame)
            k = cv2.waitKey(1) & 0xff
            if k == 27:
                camera.stop()
                break
            elif k == 9:
            #tab键开启录像
              if not camera.isWritingVideo():
                  camera.startWritingVideo()
              else:
                  camera.stopWritingVideo()
            elif k == 32:
                #空格键截图
                camera.writeImage()

直接运行脚本,出现test窗口,按空格会截图到配置文件指定的目录中

使用nodejs和python构建一个远程监控系统1.视频采集模块_第1张图片

 

 

你可能感兴趣的:(项目)