静态背景下运动目标定位算法实现
静态背景下运动目标定位算法说明
本程序开发环境为OpenCV2.41,只要为以上版本应该都能正常运行,测试的PC机基本信息如下:
源程序代码已附着在此文件夹下,基本不存在硬编码情况,使用时只需改变视频流地址(实时视频或者离线视频)即源程序中videoAddress字符串参数
如果需要改变检测的速度和检测的精度可以改变源程序中的变量shrink的值
shrink为图片进行减差时图片缩小的倍数,源程序默认为长宽缩小2倍,即原图像缩小为原来的1/4,缩小倍数越小检测速度相应的越快,但不是无限的提速,当图片大小达到宽150像素,高100像素左右时(每秒检测时间为16ms左右 每秒检测60-70帧),受到读取视频流和显示视频流的耗时影响,已很难进行提速(其中读取视频流和现实视频流耗时大概15ms左右)
因测试视频中有很长一段时间运动物体飞出视频框,需要进行全图检测(因为物体下次飞入视频框不知道从哪个方向,所以无法进行局部检测)平均耗时在16ms,但物体如果一直在视频框内理论上可以接近每秒百帧的检测速度,如果不进行视频显示仅仅检测物体的位置亲测不到3ms,也就是可以达到300帧以上
基本思路:检测到上一帧有运动目标时进行3*3局部搜索减差进行检测,如果上一帧没有目标定位框时进行全图检测
本程序所有的测试数据都已附着在此文件夹里面
#include<opencv2/opencv.hpp> #include<time.h> using namespace cv; using namespace std; //定义全局变量 Rect preDrawRect;//前一帧的定位框 int shrink = 2;//图片帧缩小倍数 在此处改变你所要缩小原图检测的倍数 string videoAddress = "E:\\软件所资料\\录制视频\\test (2).mov"; int frame_num = 1;//记录循环帧数 Rect searchRect;//定义搜索区域 Mat nowFrame; //运动物体检测函数声明 Mat MoveDetect(Mat temp, Mat frame); //求矩形的中心点 Point getCenterPoint(Rect rect); //获取两点间的距离 double getDistance(Point p1, Point p2); void main() { VideoCapture cap; cap.open(videoAddress);//也可以实时获取视频流 if (!cap.isOpened())//如果视频不能正常打开则返回 return; Mat preFrame, resultFrame; Mat temp, frame;//缩略后的图片 cap >> preFrame; searchRect = Rect(Point(0, 0), Point(preFrame.cols / shrink, preFrame.rows / shrink));//刚开始全局搜索 clock_t start, finish; double processTime, totalTime = 0; int frame_num = 0; while (1) { frame_num++; if (frame_num == 1) { //第一帧的话将前一帧进行灰度转换 之后帧就不用可以直接用后一帧的灰度图 resize(preFrame, temp, Size(preFrame.cols / shrink, preFrame.rows / shrink));//temp是缩减后的之前帧图片 //temp = preFrame.clone(); cvtColor(temp, temp, CV_RGB2GRAY); } else { temp = frame.clone(); //如果非首帧 直接将后一帧的灰度图赋给temp } cap >> nowFrame;//等价于cap.read(frame); if (nowFrame.empty()) {//如果某帧为空则退出循环 double avgTime = totalTime / frame_num; break; } resize(nowFrame, frame, Size(nowFrame.cols / shrink, nowFrame.rows / shrink));//frame是缩减后的当前帧的图 //frame = nowFrame.clone(); cvtColor(frame, frame, CV_RGB2GRAY); resultFrame = MoveDetect(temp, frame);//在搜索区域内检测 imshow("result", resultFrame); waitKey(1);//每帧延时1毫秒 preFrame = nowFrame.clone(); } cap.release();//释放资源 } //帧差和目标检测 Mat MoveDetect(Mat temp, Mat frame) { Mat result = nowFrame.clone(); //2.将background和frame做差 Mat diff = Mat::zeros(temp.rows, temp.cols, CV_8UC1); //从搜索区域进行减差 int search_tlx = searchRect.tl().x; int search_tly = searchRect.tl().y; int search_brx = searchRect.br().x; int search_bry = searchRect.br().y; //搜索框飞出原来视频框 if (search_tlx < 0) { search_tlx = 0; } if (search_tly < 0) { search_tly = 0; } if (search_brx < 0) { search_brx = 0; } if (search_bry < 0) { search_bry = 0; } if (search_tlx > temp.cols) { search_tlx = temp.cols - 1; } if (search_tly > temp.rows) { search_tly = temp.rows - 1; } if (search_brx > temp.cols) { search_brx = temp.cols - 1; } if (search_bry > temp.rows) { search_bry = temp.rows - 1; } int t1 = 20, t2 = 200; for (int i = search_tlx; i < search_brx - 1; i++) { for (int j = search_tly; j < search_bry - 1; j++) { if (abs(temp.at<uchar>(j, i) - frame.at<uchar>(j, i)) >= t1 && abs(temp.at<uchar>(j, i) - frame.at<uchar>(j, i)) <= t2) { diff.at<uchar>(j, i) = 255; } } } Mat element = getStructuringElement(MORPH_RECT, Size(30 / shrink, 30 / shrink)); //第一个参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的 //高级形态学处理,形态学闭操作处理 morphologyEx(diff, diff, MORPH_CLOSE, element); //6.查找轮廓并绘制轮廓 vector<vector<Point> > contours; findContours(diff, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //drawContours(result, contours, -1, Scalar(0, 0, 255), 2);//在result上绘制轮廓 //7.查找正外接矩形 vector<Rect> boundRect(contours.size()); Rect maxRect; int maxArea = 0; for (int i = 0; i < boundRect.size(); i++) { boundRect[i] = boundingRect(contours[i]);//边缘矩形赋值给定义的矩形数组元素 if (boundRect[i].area() > maxArea) { maxArea = boundRect[i].area(); maxRect = boundRect[i]; } } int pre_Rect_width = (preDrawRect.br().x - preDrawRect.tl().x);//上一个目标框的宽 int pre_Rect_height = (preDrawRect.br().y - preDrawRect.tl().y);//上一个目标框的高 Rect drawRect = maxRect;//把最大的矩形框赋值给当前定位框 preDrawRect = drawRect; //在上一个矩形标注框的3*3的区域搜索矩形 if (preDrawRect.area() != 0) { int search_lt_x = preDrawRect.tl().x - 1.0 * pre_Rect_width;//左上角点的x值 int search_lt_y = preDrawRect.tl().y - 1.0 * pre_Rect_height;//左上角点的y值 int search_br_x = preDrawRect.br().x + 1.0 * pre_Rect_width;//右下角点的x值 int search_br_y = preDrawRect.br().y + 1.0 * pre_Rect_height;//右下角的y值 Point search_lt = Point(search_lt_x, search_lt_y);//左上角点 Point search_br = Point(search_br_x, search_br_y);//右下角点 searchRect = Rect(search_lt, search_br);//搜索矩形 } else { searchRect = Rect(Point(0, 0), Point(nowFrame.cols / shrink * 4, nowFrame.rows / shrink * 4));//没有运动物体时全图搜索 } //求缩放之后真实的定位框位置 int tlx = drawRect.tl().x*shrink; int tly = drawRect.tl().y*shrink; int brx = drawRect.br().x*shrink; int bry = drawRect.br().y*shrink; Rect realdrawRect = Rect(Point(tlx, tly), Point(brx, bry)); //求缩放之后真实的搜索框位置 search_tlx = searchRect.tl().x*shrink; search_tly = searchRect.tl().y*shrink; search_brx = searchRect.br().x*shrink; search_bry = searchRect.br().y*shrink; Rect realsearchRect = Rect(Point(search_tlx, search_tly), Point(search_brx, search_bry)); rectangle(result, realdrawRect, Scalar(0, 0, 255), 2);//在result上绘制选出的搜索区域内的正外接矩形 //rectangle(result, realsearchRect, Scalar(255, 255, 0), 2); return result;//返回result } //求矩形框的中心点 Point getCenterPoint(Rect rect) { Point cpt; cpt.x = rect.x + cvRound(rect.width / 2.0);//cvRound四舍五入 cpt.y = rect.y + cvRound(rect.height / 2.0); return cpt; } //求两点之间的距离 double getDistance(Point p1, Point p2) { return sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2)); }
本程序使用的测试数据为静态背景下的无人机视频,有需要的可以在此连接下进行下载
链接:https://pan.baidu.com/s/1-bLmqFzXOh0__xYMnGv1Mw
提取码:zp8d