OpenCV连续自适应跟踪算法CAMShift实现视频对象跟踪

一、概述

  案例:使用OpenCV的CAMShift算法实现视频中对象跟踪

  算法API介绍:

CamShift( InputArray probImage, CV_IN_OUT Rect& window,
                                   TermCriteria criteria );
probImage:要跟踪对象的直方图反向投影矩阵
window:跟踪的目标区域
criteria:迭代停止条件

  算法实现步骤:

    1.实例化VideoCapture

    2.使用VideoCapture的open方法打开视频

    3.while循环读取视频帧

    4.检测是否是读取的第一帧,如果是第一帧则对第一帧选取roi区域,并生成roi区域的直方图矩阵

    5.否则就使用计算过的直方图数据制作脂肪头的反射投影矩阵

    6.使用CAMShift算法跟踪对象,并输入跟踪对象矩阵

    7.使用跟踪矩阵数据绘制外接圆

    8.循环4~7步骤,直到视频帧读取结束

  

二、代码示例

CAM_Shift_Video_Object_Tracking::CAM_Shift_Video_Object_Tracking(QWidget *parent)
    : MyGraphicsView{parent}
{
    this->setWindowTitle("CAMShift对象跟踪");
    QPushButton *btn = new QPushButton(this);
    btn->setText("选择视频");
    connect(btn,&QPushButton::clicked,[=](){
        choiceVideo();
    });
}

void CAM_Shift_Video_Object_Tracking::choiceVideo(){
    path = QFileDialog::getOpenFileName(this,"请选择视频","/Users/yangwei/Downloads/",tr("Image Files(*.mp4 *.avi)"));
    qDebug()<<"视频路径:"<<path;
    videoObjectTracking(path.toStdString().c_str());
}

void CAM_Shift_Video_Object_Tracking::videoObjectTracking(const char *filePath){
    VideoCapture capture;
    capture.open(filePath);
    if(!capture.isOpened()){
        qDebug()<<"打开视频失败";
        return;
    }
    bool isFirstRead = true;//是否读取的第一帧
    Mat frame;//视频帧图像
    Mat hsv;//hsv色彩空间图像数据
    Rect selection;//选中区域
    Mat mask;//颜色过滤的遮罩
    Mat hue;//h通道数据
    Mat hist;//图像直方图数据
    Mat backprojection;//直方图反向投影的输出
    float hrange[] = { 0, 180 };
    const float* hranges = hrange;
    int bins = 16;
    Mat drawImg = Mat::zeros(300, 300, CV_8UC3);//直方图就绘制在这个图像中
    while(capture.read(frame)){
        if(isFirstRead){//如果是第一帧就需要选取roi区域,并对selection填充数据
            Rect2d rect = selectROI(frame);
            selection.x = rect.x;
            selection.y = rect.y;
            selection.width = rect.width;
            selection.height = rect.height;
            qDebug()<<"第一帧已选取完成";
        }
        qDebug()<<"转HSV";
        //转换hsv色彩空间
        cvtColor(frame,hsv,COLOR_BGR2HSV);
        //使用inRange过滤像素
//        inRange(hsv,Scalar(26,43,46),Scalar(34,255,255),mask);
        inRange(hsv,Scalar(13,30,32),Scalar(50,255,255),mask);
        //使用mixChanngles获取H通道的数据
        int channels[] = { 0, 0 };
        qDebug()<<"过滤H通道";
        hue = Mat(hsv.size(), hsv.depth());
        mixChannels(&hsv, 1, &hue, 1, channels, 1);

        if(isFirstRead){//如果是第一帧,就计算roi区域的直方图
            qDebug()<<"计算并绘制直方图";
            Mat roi(hue,selection);
            Mat maskRoi(mask,selection);
            calcHist(&roi,1,0,maskRoi,hist,1,&bins,&hranges);
            //将直方图数据归一化到0~255
            normalize(hist,hist,0,255,NORM_MINMAX);
            //绘制直方图数据
            int binw = drawImg.cols / bins;
            Mat colorIndex = Mat(1, bins, CV_8UC3);
            for (int i = 0; i < bins; i++) {
                colorIndex.at<Vec3b>(0, i) = Vec3b(saturate_cast<uchar>(i * 180 / bins), 255, 255);
            }
            cvtColor(colorIndex, colorIndex, COLOR_HSV2BGR);
            for (int i = 0; i < bins; i++) {
                int  val = saturate_cast<int>(hist.at<float>(i)*drawImg.rows / 255);
                rectangle(drawImg, Point(i*binw, drawImg.rows), Point((i + 1)*binw, drawImg.rows - val), Scalar(colorIndex.at<Vec3b>(0, i)), -1, 8, 0);
            }

        }

        //对直方图执行反向投影(作为camshift函数会用到),作为输入
        calcBackProject(&hue,1,0,hist,backprojection,&hranges);
        //执行CAMShift跟踪
        backprojection &= mask;//此处是为了让结果更加精准
        RotatedRect trackBox = CamShift(backprojection,selection,TermCriteria((TermCriteria::COUNT | TermCriteria::EPS), 10, 1));
        //绘制roi区域的外接圆
        ellipse(frame,trackBox,Scalar(0,255,255),3);

        if(isFirstRead){
            isFirstRead= false;
        }
        imshow("frame",frame);
        imshow("hist",drawImg);
        int c = waitKey(1);
        if(c==27){
            break;
        }
    }
    capture.release();

}

 

三、图像演示

 

posted on 2022-04-25 19:47  飘杨......  阅读(328)  评论(0编辑  收藏  举报