C++基于OpenCV实现实时监控和运动检测记录
基于OpenCV实现实时监控并通过运动检测记录视频
一、课程介绍
1. 课程来源
课程使用的操作系统为 Ubuntu 14.04
,OpenCV 版本为OpenCV 2.4.13.1
,你可以在这里查看该版本 OpenCV 的文档。官方文档中有两个例子可以帮助你理解此课程,分别是
- OpenCV 3.1.0 版本中背景减除的例子
- OpenCV 2.4.13 版本中通过直方图比较相似度
你可以在我的 Github仓库 上找到 Windows 系统对应的 Visual Studio 工程。全部代码文件也可以在我的仓库中找到。
这里提供了完整的代码 http://labfile.oss.aliyuncs.com/courses/671/monitor-recorder.zip 。
2. 内容简介
- 课程实验使用PC机自带的摄像头作为监视器进行实时监控。
- 对原始图像做一定处理,使监控人员或监控软件更易发现监控中存在的问题。
- 当摄像头捕捉到运动产生时自动记录视频。
3. 课程知识点
本课程项目完成过程中将学习:
- 对摄像头数据的捕获
- 对捕获到的监控帧作背景处理
- 对监控视频做运动检测并记录视频
二、实验环境
本实验需要先在实验平台安装 OpenCV ,需下载依赖的库、源代码并编译安装。安装过程建议按照教程给出的步骤,或者你可以参考官方文档中 Linux 环境下的安装步骤,但 有些选项需要变更。安装过程所需时间会比较长,这期间你可以先阅读接下来的教程,在大致了解代码原理后再亲自编写尝试。
我提供了一个编译好的
2.4.13-binary.tar.gz
包,你可以通过下面的命令下载并安装,节省了编译的时间,通过这个包安装大概需要20~30分钟,视实验楼当前环境运转速度而定。$ sudo apt-get update $ sudo apt-get install build-essential libgtk2.0-dev libjpeg-dev libtiff5-dev libjasper-dev libopenexr-dev cmake python-dev python-numpy python-tk libtbb-dev libeigen2-dev yasm libfaac-dev libopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-dev libxvidcore-dev libx264-dev libqt4-dev libqt4-opengl-dev sphinx-common texlive-latex-extra libv4l-dev libdc1394-22-dev libavcodec-dev libavformat-dev libswscale-dev $ cd ~ $ mkdir OpenCV && cd OpenCV $ wget http://labfile.oss.aliyuncs.com/courses/671/2.4.13-binary.tar.gz $ tar -zxvf 2.4.13-binary.tar.gz $ cd opencv-2.4.13 $ cd build $ sudo make install
如果你想体验编译的整个过程,我也提供了一个一键安装的脚本文件,你可以通过下面的命令尝试。这个过程会非常漫长,约2小时,期间可能还需要你做一定的交互确认工作。
$ cd ~ $ sudo apt-get update $ wget http://labfile.oss.aliyuncs.com/courses/671/opencv.sh $ sudo chmod 777 opencv.sh $ ./opencv.sh
如果你觉得有必要亲自尝试一下安装的每一步,可以按照下面的命令逐条输入执行,在实验楼的环境中大概需要两个小时。
$ sudo apt-get update $ sudo apt-get install build-essential libgtk2.0-dev libjpeg-dev libtiff5-dev libjasper-dev libopenexr-dev cmake python-dev python-numpy python-tk libtbb-dev libeigen2-dev yasm libfaac-dev libopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-dev libxvidcore-dev libx264-dev libqt4-dev libqt4-opengl-dev sphinx-common texlive-latex-extra libv4l-dev libdc1394-22-dev libavcodec-dev libavformat-dev libswscale-dev $ wget https://github.com/Itseez/opencv/archive/2.4.13.zip $ unzip 2.4.13.zip $ cd 2.4.13 $ mkdir release && cd release $ cmake -D WITH_TBB=ON -D BUILD_NEW_PYTHON_SUPPORT=ON -D WITH_V4L=ON -D INSTALL_C_EXAMPLES=ON -D INSTALL_PYTHON_EXAMPLES=ON -D BUILD_EXAMPLES=ON -D WITH_QT=ON -D WITH_GTK=ON -D WITH_OPENGL=ON .. $ sudo make $ sudo make install $ sudo gedit /etc/ld.so.conf.d/opencv.conf $ 输入 /usr/local/lib,按 Ctrl + X 退出,退出时询问是否保存,按 Y 确认。 $ sudo ldconfig -v $ sudo gedit /etc/bash.bashrc $ 在文件末尾加入 $ PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig export PKG_CONFIG_PATH 按 Ctrl + X 退出,按 Y 确认保存。
检验配置是否成功。将 OpenCV 自带的例子(在目录
PATH_TO_OPENCV/samples/C
下)运行检测。如果成功,将显示 lena 的脸部照片,同时圈出其面部。$ cd samples/C $ ./build_all.sh $ ./facedetect --cascade="/usr/local/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml" --scale=1.5 lena.jpg
三、实验原理
实验通过 OpenCV 提供的 API 完成大部分任务,首先捕获摄像头数据,之后对捕获到的每一帧作背景减除处理,得出易于识别的图像,最后利用直方图做实时图像和背景图像的对比,实现运动检测并写入视频文件。
四、实验步骤
通过以下命令可下载项目源码,作为参照对比完成下面详细步骤的学习。
wget http://labfile.oss.aliyuncs.com/courses/671/monitor-recorder.zip
unzip monitor-recorder.zip
1.定义头文件
工程文件由一个头文件 monitor.hpp
和一个入口文件 main.cpp
构成。首先在头文件中定义将使用的库和相关变量。
代码中使用到的 OpenCV 头文件和 C++ 头文件在头文件 monitor.hpp
中声明如下,其中 unistd.h
包含了 Linux 下的 sleep
函数,参数为睡眠的秒数。
#ifndef __MONITOR_H_
#define __MONITOR_H_//opencv
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/video/background_segm.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/imgproc/imgproc.hpp>
//C++
#include <ctime>
#include <iostream>
#include <string>
#include <cstdio>
#include <unistd.h>
#include <sstream>using namespace cv;
using namespace std;// ...#endif __MONITOR_H_
2.设计 processCamera
函数
processCamera
负责完成主要功能,包括监控数据的获取和处理。首先要了解 OpenCV 中提供的几个 API。
CvCapture* cvCaptureFromCAM(int device)
: 此函数捕获指定设备的数据并返回一个cvCapture
类型指针,捕获失败时返回NULL
。cvCreateFileCapture(char * filepath)
: 从本地视频读入。CvVideoWriter * cvCreateVideoWriter(char * filepath, , fps, size, is_color)
: 新建一个视频写入对象,返回其指针,filepath
指定写入视频的路径,fps
指定写入视频的帧速,size
指定写入视频的像素大小,is_color
仅在windows下有效,指定写入是否为彩色。double cvGetCaptureProperty(CvCapture* capture, int property_id)
: 获取一个视频流的某个特性,property_id
指定要获取的特性名称。IplImage* cvQueryFrame(CvCapture* capture)
: 从视频流获取帧。void cvCvtColor(const CvArr* src, CvArr* dst, int code)
: 按code
指定的模式将src
指向的帧转换后写入dst
指向的地址。void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, SparseMat& hist, int dims, const int* histSize, const float** ranges, bool uniform, bool accumulate)
: 为image
指向的帧计算直方图,保存在hist
中。void normalize(const SparseMat& src, SparseMat& dst, double alpha, int normType)
: 按指定模式将src
正常化并写入dst
中。double compareHist(const SparseMat& H1, const SparseMat& H2, int method)
:按method
指定的方式比较两个直方图的相似程度。int cvWriteFrame(CvVideoWriter* writer, const IplImage* image)
: 向视频写入流写入一帧。成功返回 1,否则返回 0。
了解这些API的基本功能后,梳理程序执行的步骤:
- 程序开始执行,启动摄像头并获得数据流
- 进入循环:
- 捕获一帧
- 是否为第一帧?是则记录该帧作为监控区域的背景
- 将该帧做适当的变换,输出到监视器中
- 分析该帧和背景帧的相似程度
- 相似程度是否低于阈值且当前没有在记录视频?低于阈值开始记录。
- 相似程度是否低于阈值且当前已经开始记录视频?低于阈值继续记录,否则停止记录。
- 循环中程序的停止,通过接受外部中断相应。
3.代码实现 processCamera
启动摄像头并获得数据流,调用上面提到的
cvCaptureFromCAM
函数,默认摄像头的device
为 0。void processCamera() { CvCapture *capture = cvCaptureFromCAM(0); if (!capture){cerr << "Unable to open camera " << endl;exit(EXIT_FAILURE); } // TODO... } // end processCamera
进入循环,循环条件中使用到一个
keyboard
变量用于接收外部中断,如果Esc
或者q
键被按下则退出循环。keyboard
通过 OpenCV 提供的waitKey()
函数获得外部按键情况。在下面的循环中,每次先检查视频输入流capture
是否为空,防止访问违例内存。在capture
不为NULL
的情况下,从capture
读取一帧。在退出 while循环后,要通过cvReleaseCapture
释放此前申请的capture
。void processCamera() { // ... Mat frame; // current frame while ((char)keyboard != 'q' && (char)keyboard != 27){if (!capture) {cerr << "Unable to read camera" << endl;cerr << "Exiting..." << endl;exit(EXIT_FAILURE);}frame = cvQueryFrame(capture);// TODO...keyboard = waitKey(30); } // end While cvReleaseCapture(&capture); } // end processCamera
判断当前帧是否为第一帧,通过一个bool型变量
backGroundFlag
来标识。若backGroundFlag
为true
表示当前帧为第一帧,则记录该帧并将backGroundFlag
置为False
。此时代码如下。在当前帧为第一帧的情况下,我们不需要记录该帧的真实数据,只需要记录该帧对应的直方图,这里首先将RGB类型的图像转为HSV格式,之后计算该帧的直方图,保存在base
中。void processCamera() { // ... bool backGroundFlag = true; Mat frame; // current frame Mat HSV; // HSV format MatND base; // histogram while ((char)keyboard != 'q' && (char)keyboard != 27){if (!capture) {cerr << "Unable to read camera" << endl;cerr << "Exiting..." << endl;exit(EXIT_FAILURE);}frame = cvQueryFrame(capture);// set backgroundif (backGroundFlag){cvtColor(frame, HSV, CV_BGR2HSV);calcHist(&HSV, 1, channels, Mat(), base, 2, histSize, ranges, true, false);normalize(base, base, 0, 1, NORM_MINMAX, -1, Mat());backGroundFlag = false;}// TODO...keyboard = waitKey(30); } // end While cvReleaseCapture(&capture); } // end processCamera
对当前帧做适当变换并输出到监视器。此时代码如下。我们要实现的程序可以对原始图像做两种背景减除处理,因此需要用户指定使用哪种方式,这里通过参数传给
processCamera
,method 为 0 代表使用 MOG2 方式减除, method为 1代表使用 MOG1 方式减除, method为0 代表不作任何变换。两种方式均可以突出背景外的变化情况,实际效果将在最终程序执行时展示。在对原始frame
做处理并写入fgMask
后,通过imshow
函数输出到监视器中。imshow
函数的第一个参数为输出的窗口名,这里先假设已经有一个名为Monitor
的窗口等待接收输出,这个窗口将在最终的main
函数中创建。用户应当可以指定这个监控程序是否将处理后的图像输出,因此我们传入一个showWindow
参数表明是否显示实时监控窗口。对每一帧的处理方式和上面对背景帧的处理方式相同。void processCamera(bool showWindow,unsigned int method) { // ... bool backGroundFlag = true; Mat frame; // current frame Mat HSV; // HSV format MatND base; // histogram while ((char)keyboard != 'q' && (char)keyboard != 27){if (!capture) {cerr << "Unable to read camera" << endl;cerr << "Exiting..." << endl;exit(EXIT_FAILURE);}frame = cvQueryFrame(capture);if (method == 0)pMOG2->operator()(frame, fgMask);else if (method == 1)pMOG->operator()(frame, fgMask);else if (method == 2)fgMask = frame;// set backgroundif (backGroundFlag){cvtColor(frame, HSV, CV_BGR2HSV);calcHist(&HSV, 1, channels, Mat(), base, 2, histSize, ranges, true, false);normalize(base, base, 0, 1, NORM_MINMAX, -1, Mat());backGroundFlag = false;}cvtColor(frame, HSV, CV_BGR2HSV);calcHist(&HSV, 1, channels, Mat(), cur, 2, histSize, ranges, true, false);normalize(cur, cur, 0, 1, NORM_MINMAX, -1, Mat());// TODO ...if (showWindow && !fgMask.empty()){imshow("Monitor", fgMask);}keyboard = waitKey(30); } // end While cvReleaseCapture(&capture); } // end processCamera
比较当前帧和背景帧的相似度,当出现异常时开始记录视频。这里直接调用
compareHist
函数,输出一个 0 - 1 范围内的指标,越接近1 表示两个直方图代表的图像越相似。这里我设置的阈值为 0.65,这个阈值应当根据实际监控区域的光线、色彩等因素修正。我们创建了一个recorder
指针用于写入视频,需要指定写入视频的帧速和大小,这里大小通过cvGetCaptureProperty
自动获取,帧速fps
由用户传入参数指定。在这里为了避免监控过于敏感的情况出现,设置了一个UnnormalFrames
参数,该参数记录当前已经持续出现了多少帧与背景不同的画面,也就是运动状态出现了多久。当UnnormalFrames
达到用户指定的阈值unnormal
时,我们认为监控中确实出现了异常,因此开始记录。为了更完整的提供监控信息,一旦确认监控中有运动状态发生,在运动结束后,也就是检测到当前帧和背景重新一致后,程序将继续记录视频信息,继续记录的时长和之前运动状态持续的时长相同。代码中通过recordFlag
标识当前是否应该记录视频,在UnnormalFrames > unnormal
时,recordFlag
被置位,同时UnnormalFrames
随着运动帧被检测持续增加,当运动结束后,UnnormalFrames
将递减,至 0 时停止记录视频。这里unnormal
通过参数传入processCamera
。至此,processCamera
函数编写完成。void processCamera(bool showWindow, unsigned int method, unsigned int unnormal = 10, unsigned int fps = 24) { CvCapture *capture = cvCaptureFromCAM(0); if (!capture){cerr << "Unable to open camera " << endl;exit(EXIT_FAILURE); }bool backGroundFlag = true, recordFlag = false; Mat frame, fgMask; // current frame, fg mask Mat HSV; // HSV format MatND base, cur; // histogram unsigned int UnnormalFrames = 0; int channels[] = { 0, 1 };CvSize size = cvSize((int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH),(int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT));CvVideoWriter * recorder = cvCreateVideoWriter(recordName, CV_FOURCC('D', 'I', 'V', 'X'), 32, size, 1);// ESC or 'q' for quitting while ((char)keyboard != 'q' && (char)keyboard != 27){if (!capture) {cerr << "Unable to read camera" << endl;cerr << "Exiting..." << endl;exit(EXIT_FAILURE);}frame = cvQueryFrame(capture);if (method == 0)pMOG2->operator()(frame, fgMask);else if (method == 1)pMOG->operator()(frame, fgMask);else if (method == 2)fgMask = frame;// set backgroundif (backGroundFlag){cvtColor(frame, HSV, CV_BGR2HSV);calcHist(&HSV, 1, channels, Mat(), base, 2, histSize, ranges, true, false);normalize(base, base, 0, 1, NORM_MINMAX, -1, Mat());backGroundFlag = false;}cvtColor(frame, HSV, CV_BGR2HSV);calcHist(&HSV, 1, channels, Mat(), cur, 2, histSize, ranges, true, false);normalize(cur, cur, 0, 1, NORM_MINMAX, -1, Mat());double comp = compareHist(base, cur, 0);if (comp < 0.65)UnnormalFrames += 1;else if (UnnormalFrames > 0)UnnormalFrames--;if (UnnormalFrames > unnormal)recordFlag = true;else if (UnnormalFrames <= 0){UnnormalFrames = 0;recordFlag = false;}// DO SOMETHING WARNING// Here We Starting Recodingif (recordFlag){cvWriteFrame(recorder, &(IplImage(frame)));}if (showWindow && !fgMask.empty()){imshow("Monitor", fgMask);}keyboard = waitKey(30); } // end While cvReleaseVideoWriter(&recorder); cvReleaseCapture(&capture); } // end processCamera
4.定义外部或全局变量
在processCamera
函数中使用到了一些函数中没有声明过的变量,这些变量有的是配置使用的常量如ranges
等,不需要理解,下面在头文件中声明。pMOG
和pMOG2
对应了对frame做变换的两个方式,他们将在main
函数中被定义。keyboard
用于接收外部键盘输入。其余的均为常量,用于配置 OpenCV 提供的函数。
// ...
extern Ptr<BackgroundSubtractor> pMOG; //MOG Background subtractor
extern Ptr<BackgroundSubtractor> pMOG2; //MOG2 Background subtractor
extern int keyboard;const float h_ranges[] = { 0, 256 };
const float s_ranges[] = { 0, 180 };
const float* ranges[] = { h_ranges, s_ranges };const int h_bins = 50, s_bins = 60;
const int histSize[] = { h_bins, s_bins };extern char recordName[128];// ...
5.编写 main.cpp
下面编写程序入口。
- 首先需要告知用户程序的使用方式,编写
help
函数输出帮助信息。-vis
选项用于指定程序显示实时监控,[MODE]
参数指定使用何种方式显示监控,[FPS]
指定帧速,[THRESHOLD]
指定经过多少异常帧后开始记录,[OUTPUTFILE]
指定输出视频记录位置。
void help(){cout<< "----------------------------------------------------------------------------\n"<< "Usage: \n"<< " ./MonitorRecorder.exe [VIS] [MODE] [FPS] [THRESHOLD] [OUTPUTFILE] \n"<< " [VIS] : use -vis to show the monitor window, or it will run background. \n"<< " [MODE] : -src shows the original frame; \n"<< " -mog1 shows the MOG frame; \n"<< " -mog2 shows the MOG2 frame. \n"<< " [FPS] : set the fps of record file, default is 24. \n"<< " [THRESHOLD] \n"<< " : set the number x that the monitor will start recording after \n"<< " x unnormal frames passed. \n"<< " [OUTPUTFILE] \n"<< " : assign the output recording file. It must be .avi format. \n"<< " designed by Forec \n";<< "----------------------------------------------------------------------------\n";
}
- 编写
main
函数。在main
函数中我们需要用到外部声明的pMOG
、pMOG2
以及recordName
。这里需要在函数外声明。主函数中主要部分为解析用户的命令行参数,其中stoi
函数需要使用 C++ 11 标准编译。我们使用sleep(2)
将processCamera
延时 2 秒执行,这个时间你可以离开电脑,让程序捕获你的背景,之后可以回到电脑前,观察监控程序的显示和记录情况。如果用户指定了-vis
参数,则产生一个Monitor
窗口显示实时监控,这个窗口就是此前processCamera
函数中输出图像的窗口。在main
函数最后,用destroyAllWindows
销毁namedWindow
产生的窗口。
#include "monitor.hpp"Ptr<BackgroundSubtractor> pMOG; //MOG Background subtractor
Ptr<BackgroundSubtractor> pMOG2; //MOG2 Background subtractor
int keyboard;char recordName[128];void help();int main(int argc, char* argv[]){bool showWindow = false;unsigned int method = 0, unnormal = 10, fps = 24;if (argc > 6){cerr << "Invalid Parameters, Exiting..." << endl;exit(EXIT_FAILURE);}if (argc >= 2){if (strcmp(argv[1], "-vis") == 0)showWindow = true;if (strcmp(argv[1], "-h") == 0 ||strcmp(argv[1], "--help") == 0){help();exit(EXIT_SUCCESS);}}if (argc >= 3){if (strcmp(argv[2], "-mog2") == 0)method = 0;if (strcmp(argv[2], "-mog1") == 0)method = 1;if (strcmp(argv[2], "-src") == 0)method = 2;}if (argc >= 4){int param = stoi(argv[3], nullptr, 10);if (param <= 10)fps = 24;elsefps = param;}if (argc >= 5){int param = stoi(argv[4], nullptr, 10);if (param <= 0)unnormal = 10;elseunnormal = param;}if (argc >= 6){strcpy(recordName, argv[5]);}else{// set record video file nametime_t t = time(NULL);sprintf(recordName, "%d.avi", int(t));}cout << "Starts After 2s..." << endl;sleep(2);if (showWindow)namedWindow("Monitor");pMOG = new BackgroundSubtractorMOG(); //MOG approachpMOG2 = new BackgroundSubtractorMOG2(); //MOG2 approachprocessCamera(showWindow, method, unnormal, fps);destroyAllWindows();return EXIT_SUCCESS;
}
6.编译运行
因为实验楼的环境不提供摄像头,因此我们将程序捕获摄像头的部分修改为程序从一个本地的监控视频中读取,模拟读取摄像头的情况。这需要修改monitor.hpp
中的第 38 行,将CvCapture *capture = cvCaptureFromCAM(0);
改为 CvCapture *capture = cvCreateFileCapture("test.mp4");
,假设将要读入的本地视频文件名为test.mp4
。在修改完你的代码后,你可以通过以下命令下载test.mp4
(该视频文件是周杰伦《浪漫手机》的MV),并检验代码。
$ wget http://labfile.oss.aliyuncs.com/courses/671/test.mp4
请确认你已经修改了代码,将读取摄像头改为读取test.mp4
,并且将test.mp4
已经拷贝到代码目录下。在代码目录下输入如下命令编译。请检查自己是否已经按照教程开始的环境配置方案配置成功。编译可能产生两个warning。
g++ -ggdb `pkg-config --cflags opencv` -std=c++11 -fpermissive -o `basename main` main.cpp `pkg-config --libs opencv`
编译成功后目录下将产生一个名为 main
的可执行文件,在终端键入如下命令,将使程序输出MOG1处理后的实时监控画面,且画面连续异常10帧后开始记录视频,视频记录到当前文件夹下的out.avi
中,帧速 24。
sudo vim /etc/ld.so.conf.d/opencv.conf
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
sudo ldconfig -v
./main -vis -mog1 24 10 out.avi
程序按上述命令的运行截图如下。
以下几张分别是程序在不同显示模式下的显示情况,你可以通过切换-mog1
,-mog2
和-src
来自己观察对应的效果。
五、代码获取
你可以在我的 Github仓库 中获取到完整的代码。里面提供了Windows 版本和 Linux版本的配置、运行方案。如果你有建议或想法,欢迎提 PR 沟通。
六、参考资料
- OpenCV在 Ubuntu下链接库配置
C++基于OpenCV实现实时监控和运动检测记录相关推荐
- Python基于OpenCV的实时疲劳检测[源码&演示视频&部署教程]
1.图片演示 2.视频演示 [项目分享]Python基于OpenCV的实时疲劳检测[源码&演示视频&部署教程]_哔哩哔哩_bilibili 3.检测方法 1)方法 与用于计算眨眼的传统 ...
- 基于OpenCV的实时车道线分割&车道保持系统(源码&教程)
1.研究背景 汽车主动安全系统能够实现风险的主动预防和规避,其能有力缓解当前我国汽车交通事故频发的困境,故对其的相关研究得到了国家的大力支持. 车道保持辅助系统(LKAS,Lane Keeping A ...
- 基于android手机实时监控ipcam视频之一:RTSP
我以前做过一个这样的项目,基于android实现手机实时监控ipcam,ipcam厂商提供控件,该控件安装以后,在IE上面输入ipcam的ip地址,就可以实时查看ipcam的图像,这实时视频是通过HT ...
- 基于OpenCV的实时面部识别
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 我们将使用一些简单的代码来实现实时面部识别代码,我们可以对个人的面 ...
- 五分钟搭建基于 Prometheus + Grafana 实时监控系统
文章目录 Prometheus + Grafana 实时监控系统 Prometheus + Grafana 实时监控系统 依赖镜像包 启动 node-exporter check 端口 node_ex ...
- 基于OpenCV的实时睡意检测系统
该系统可以检测一个人在开车时是否困倦,如果有的话,可以通过使用语音消息实时提醒他.该系统使用网络摄像头和电话摄像头进行实时数据传输. 01.研究目的 根据国家公路交通安全管理局的数据,每年均涉及疲劳驾 ...
- dlib疲劳检测_基于OpenCV的实时睡意检测系统
该系统可以检测一个人在开车时是否困倦,如果有的话,可以通过使用语音消息实时提醒他.该系统使用网络摄像头和电话摄像头进行实时数据传输. 研究目的 根据国家公路交通安全管理局的数据,每年均涉及疲劳驾驶事故 ...
- 服务器之间文件同步 go,基于golang文件实时监控并同步远端服务器工具
### SftpSync 基于golang开发的 文件实时同步软件 ### 前言 1.本人是个重度某度云用户,但是云盘的速度不开会员的情况下真是太慢了,而且也不能实时备份,所以想过自己搭建一个NAS. ...
- 基于OpenCV的实时停车地点查找
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 简介 我们常常会在停车场周围四处行驶很多次来寻找一个停车位,如果我 ...
最新文章
- 今年2月的微盟“删库”主角被判 6 年有期徒刑
- 团队项目-北航MOOC系统Android客户端 NABC
- 购买阿里云服务器地域如何选择?
- [BUUCTF-pwn]——ciscn_2019_n_1
- live555 源码分析:简介
- P5200 [USACO19JAN]Sleepy Cow Sorting 牛客假日团队赛6 D	迷路的牛 (贪心)
- C#中常用的几种读取XML文件的方法
- [转载] java中关于用\t格式输出
- 宏定义 是否retina高清屏,判断当前设备
- mysql odb驱动_odb C++访问mysql数据库,从安装到写入
- java 微服务源码_Redkale 1.9.4 Java 分布式微服务框架
- response.sendRedirect()与request.getRequestDispatcher().forward()区别
- 关于 java 上传,下载和导入报java.lang.IllegalStateException异常解决办法
- 分享16款Java小游戏源码Java applet小游戏源码
- 高中信息技术知识点汇总(必修)
- 硬盘是计算机的储存器,硬盘存储器
- 华为路由器与交换机常用命令(20200618)
- 最近微信上很火的小游戏【壹秒】android版——开发分享
- 不用PS,小白也能轻松搞定抠图
- html怎样把图片做成链接,怎么把图片弄成链接