静态背景下运动目标定位算法实现

静态背景下运动目标定位算法说明

本程序开发环境为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

posted @ 2019-01-25 16:02  henu小白  阅读(788)  评论(0编辑  收藏  举报