移动图像监测

移动图像监测,利用摄像头定点监测某个区域,当有移动物体经过时,摄像头便自动抓拍(要监测多大物体、按拍照速率都是可调的),并把拍到的图像存储在指定目录,实现无人监控功能。
一、linux下的移动图像监测:Motion 能够控制云台功能,内置 Http 服务器,在网络浏览器中进行远程监控,支持 MySQL 和 PostgreSQL 数据库。
二、基于OpenCV移动图像监测:选取背景差法和 帧间差法 来检测运动目标,在经过图像二值化将运动目标提取出来,通过面积阀值分割出目标的运动区域。

实现步骤:
(1)开辟静态内存,对图像进行初始化准备采集;
(2)采集图像,定义参数k,作为图像序列计数;
采集第1幅图像,判断是否等于1,是,则存储到内存,作为静态模板;如果k不等于1则把接下来的3幅图像放到开辟的动态内存;
(3)每一幅图像和静态模板图像做差;
(4)进行图像预处理;
(4.1)转换为灰度图像 – cvtColor()
(4.2)对差图像做二值化 – cvThreshold()
(4.3)中值滤波 - cvSmooth()
(4.4)下采样 - cvPyrDown()
(4.5)膨胀操作 - cvDilate()
(4.6)上采样 - cvPyrUp()
(4.7)在二值图像中提取轮廓 - cvFindContours()
(5)计算差值图像中目标物体大小,记录此时动态图像和静态图像相减后图像的像素数:
(5.1)如果面积小于1500像素,那么证明没有异物侵入监视场景,此次判断结束,继续下面图像的判断;
(5.2)如果面积大于1500像素,可能是因为光线突然变化或者有异物侵入,把此图像放入开辟的动态内存。继续采集第2幅图像,并把它和背景图像进行做差运算,如果差图像像素数小于1500,那么证明是瞬间的微弱的光线变化,不做任何响应;如果此差值大于1500,则把它放入另一个的动态内存,以便进一步判断。然后判断与背景作差之后的第2幅图像和第1幅图像是否为连续图像,如果是连续图像,那么把第2幅图像和背景图像做差后的像素数记录下来,否则两个图象不连续则是有异物入侵要报警。同样判断与背景作差之后的第3幅图像是否是和前两幅连续的,如果是连续图像,那么把第3幅图像和背景图像做差后的像素数记录下来,否则就报警。
(5.3)接下来根据连续帧之间的差值做判断,把第2幅图像和第1幅图像,第3幅和第2幅图像分别做差并分别记录做差后像素数。如果这两个数值都大于阈值1500,那么就报警:否则就把这变化了的3幅连续图形的第1幅图像作为模板图像,来代替静态内存中原来的背景图像,从而实现了运动目标检测和背景更新。
该方法把背景差法的“准确监测物体”和帧间差法的“对光线具有较强的适应性”结合起来,加之多帧的联合判断,进一步提高了监视系统的稳定性。

源码
#include "cv.h"
#include "highgui.h"
#include <time.h>
#include <stdio.h>

const double MHI_DURATION = 0.5;//最大跟踪时间,灵敏度调节
const int N = 2;//用于运动检测的循环帧数,与机器速度及FPS设置有关
const int CONTOUR_MAX_AERA = 500;//矩形面积
IplImage **buf = 0;//圈出图像缓冲
IplImage *mhi = 0; //运动历史图像 
int last = 0;
int flag;

//
// img – 输入视频帧
// dst – 检测结果
void update_mhi(IplImage* img, IplImage* dst, int diff_threshold)
{
	double timestamp = clock() / 100; //获取当前时间戳 
	CvSize size = cvSize(img->width, img->height); //获取参数img的图像宽高得到当前帧的尺寸 
	int i, idx1, idx2;
	IplImage* silh;
	IplImage* pyr = cvCreateImage(cvSize((size.width & -2) / 2, (size.height & -2) / 2), 8, 1); //创建pyr的图像指针,创建头并分配数据 
	CvMemStorage *stor;
	CvSeq *cont;
	// 开始时为图像分配内存 or 帧尺寸改变时重新分配内存  
	if (!mhi || mhi->width != size.width || mhi->height != size.height)
	{
		if (buf == 0)//若尚没有初始化则分配内存给他 
		{
			buf = (IplImage**)malloc(N*sizeof(buf[0]));//动态内存分配
			memset(buf, 0, N*sizeof(buf[0]));//内存空间初始化
		}

		for (i = 0; i < N; i++)
		{
			cvReleaseImage(&buf[i]);//系统将释放刚才载入图像的内存空间
			buf[i] = cvCreateImage(size, IPL_DEPTH_8U, 1);//创建首地址并分配存储空间
			cvZero(buf[i]);//将数组中的所有通道的元素的值都设置为0
		}
		cvReleaseImage(&mhi);
		mhi = cvCreateImage(size, IPL_DEPTH_32F, 1);
		cvZero(mhi); 
	} 
	//将当前要处理的帧转化为灰度放到buffer的最后一帧中
	cvCvtColor(img, buf[last], CV_BGR2GRAY); //RGB帧图像格式转换为gray
	//设定帧的序号
/*
last---->idx1
^
|
|
idx2<-----(last+1)%3
*/ 
	idx1 = last;
	idx2 = (last + 1) % N; 
	last = idx2;
	
	silh = buf[idx2];//做帧差,差值的指向idx2:|idx2-idx1|-->idx2(<-silh) 
	cvAbsDiff(buf[idx1], buf[idx2], silh);//相邻两帧的差 
	//对图像做二值化 值大越清晰 
	cvThreshold(silh, silh, 30, 255, CV_THRESH_BINARY); //src(x,y)>threshold ,dst(x,y) = max_value; 否则,dst(x,y)=0;二值化
	cvUpdateMotionHistory(silh, mhi, timestamp, MHI_DURATION); //更新像素点的运动历史&去掉影像(silh) 以更新运动历史图像

    // cvCvtScale的第四个参数 shift = (MHI_DURATION - timestamp)*255./MHI_DURATION  
    // 控制帧差的消失速率  
	cvCvtScale(mhi, dst, 255. / MHI_DURATION,
		(MHI_DURATION - timestamp)*255. / MHI_DURATION);//timestamp是时间戳;MHI_DURATION获得的是当前时间
	cvCvtScale(mhi, dst, 255. / MHI_DURATION, 0);

	cvSmooth(dst, dst, CV_MEDIAN, 3, 0, 0, 0);// 中值滤波,消除小的噪声
	cvPyrDown(dst, pyr,  CV_GAUSSIAN_5x5 );// 向下采样,去掉噪声,图像是原图像的四分之一 
	cvDilate(pyr, pyr, 0, 1); // 做膨胀操作,消除目标的不连续空洞,它具有填充物体内细小空洞,连接邻近物体和平滑边界的作用
	cvPyrUp(pyr, dst,  CV_GAUSSIAN_5x5 );// 向上采样,恢复图像,图像是原图像的四倍 

	// 下面的程序段用来找到轮廓
	stor = cvCreateMemStorage(0); //创建一个内存存储器,管理各种动态对象的内存
	cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), stor);//对于一个点的序列,元素类型 CV_SEQ_ELTYPE_POINT应当被指定,参数elem_size必须等同于sizeof(CvPoint)。Storage为指向前面定义的内存存储器.
	// 找到所有轮廓 
	// 二值图像中提取轮廓,并且返回提取轮廓的数目
	cvFindContours(dst, stor, &cont, sizeof(CvContour),
		CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));//提取模式:提取所有轮廓,并且放置在 list 中;逼近方法 (对所有节点)压缩水平、垂直和对角分割,即函数只保留末端的象素点; 
	// 直接使用CONTOUR中的矩形来画轮廓
	for (; cont; cont = cont->h_next)
	{
		flag = 0;
		CvRect r = ((CvContour*)cont)->rect;
		if (r.height * r.width > CONTOUR_MAX_AERA)// 面积小于CONTOUR_MAX_AERA面积的方形抛弃掉 
		{
			Sleep(20);
			cvRectangle(img, cvPoint(r.x, r.y),
				cvPoint(r.x + r.width, r.y + r.height),
				CV_RGB(255, 0, 0), 1, CV_AA, 0); 
			flag = 1;
		}
		else
		{
			flag = 0;
		}
	}
	cvReleaseMemStorage(&stor);
	cvReleaseImage(&pyr);
}

//报警函数
void warning()
{
	//PlaySound("C:\\111.MP3",NULL,SND_FILENAME || SND_ASYNC);
	printf("qqqqqqqqqqq\n");
	flag = 0;
}

int main(int argc, char** argv)
{
	IplImage* motion = 0;
	CvCapture* capture = 0; //视频获取结构 
	int i = 0;
#if 0
	//从摄像头或者视频文件中抓取帧
	if (argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0])))
		//原型:extern int isdigit(char c);
		//用法:#include <ctype.h>   功能:判断字符c是否为数字    说明:当c为数字0-9时,返回非零值,否则返回零。
		capture = cvCaptureFromCAM(argc == 2 ? argv[1][0] - '0' : 0);
	else if (argc == 2)
	capture = cvCaptureFromAVI(argv[1]);
#endif 
	capture = cvCaptureFromAVI("person1.avi");

	if (capture)
	{
		cvNamedWindow("Motion", 1);
		for (i = 0; i < 1000; i++)
		{
			CvFont font;
			cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX, 1.0, 1.0, 0, 2, 8);
			IplImage* image;
			if (!cvGrabFrame(capture)) //从摄像头或者视频文件中抓取帧
				break;
			image = cvRetrieveFrame(capture); //取回由函数cvGrabFrame抓取的图像,返回由函数cvGrabFrame 抓取的图像的指针
			if (image)
			{
				if (!motion)
				{
					motion = cvCreateImage(cvSize(image->width, image->height), 8, 1);//创建motion帧,八位,一通道 
					cvZero(motion); //清空图像缓存,用零填充motion 
					motion->origin = image->origin; //内存存储的顺序和取出的帧相同 /* 0 - 顶—左结构, 1 - 底—左结构 (Windows bitmaps 风格) */
				}
			}
			update_mhi(image, motion, 4);//移动监测图像
			cvShowImage("Motion", image);//显示处理过的图像 

#if 0
			//保存图片
			int j = 0;
			char image_name[13];
			sprintf(image_name, "%s%d%s", "image", ++j, ".jpg");
			cvSaveImage(image_name, image);
#endif
			if (flag){
				warning();
				flag = 0;
			}
			flag = 0;
			if (cvWaitKey(10) >= 0)
				break;
		}
		cvReleaseCapture(&capture);//释放设备 
		cvDestroyWindow("Motion");//销毁窗口 
	}
	return 0;
}
运行结果

下载

摄像头实时监控与报警系统(Opencv)
https://download.csdn.net/download/u010872301/10514275


  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值