OpenCV中MeanShift算法视频移动对象分析

  1. 云栖社区>
  2. OpenCV学堂>
  3. 博客>
  4. 正文

OpenCV中MeanShift算法视频移动对象分析

技术小能手 2018-12-19 15:29:59 浏览2517

MeanShift算法
Mean Shift是一种聚类算法,在数据挖掘,图像提取,视频对象跟踪中都有应用。OpenCV在图像处理模块中使用均值迁移可以实现去噪、边缘保留滤波等操作。在视频分析模块中使用均值迁移算法结合直方图反向投影算法实现对移动对象分析,是一种非常稳定的视频移动对象跟踪算法。其核心的思想是对反向投影之后的图像做均值迁移(meanshift)从而发现密度最高的区域,也是对象分布最大的区域,均值迁移的原理可以通过下面这张图来解释:

image
会从初始化的中心位置,通过计算生成新的中心位置坐标,dx与dy就是均值迁移每次移动的步长,移动到新的中心之后,会基于新的分布进行中心位置计算,如此不断迭代,直到中心位置处于最大分布为止。

MeanShift移动对象分析,首先会读取视频第一帧,选择ROI区域,生成直方图。然后对视频中的每一帧执行如下操作:

1.直方图反向投影该帧
2.基于前一帧的窗口位置,使用means shift寻找新的最大分布密度,生成新位置窗口
3.更新窗口直至最后一帧
image
image
OpenCV中meanshift的API函数如下:

int cv::meanShift(
    InputArray probImage,
    Rect &  window,
    TermCriteria criteria 
)

参数解释

probImage 输入图像,是直方图反向投影的结果
window 搜索窗口,ROI对象区域,每帧会自动更新窗口
criteria 均值迁移停止条件

代码演示
代码实现分为如下几个部分

通过VidoeCapture读取视频文件

VideoCapture cap("D:/images/video/balltest.mp4");
  1. 对第一帧进行ROI选择,绘制直方图
// Object has been selected by user, set up CAMShift search properties once
Mat roi(hue, selection), maskroi(mask, selection);
calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);
normalize(hist, hist, 0, 255, NORM_MINMAX);

trackWindow = selection;
trackObject = 1; // Don't set up again, unless user selects new ROI

histimg = Scalar::all(0);
int binW = histimg.cols / hsize;
Mat buf(1, hsize, CV_8UC3);
for (int i = 0; i < hsize; i++)
    buf.at<Vec3b>(i) = Vec3b(saturate_cast<uchar>(i*180. / hsize), 255, 255);
cvtColor(buf, buf, COLOR_HSV2BGR);

for (int i = 0; i < hsize; i++)
{
    int val = saturate_cast<int>(hist.at<float>(i)*histimg.rows / 255);
    rectangle(histimg, Point(i*binW, histimg.rows),
        Point((i + 1)*binW, histimg.rows - val),
        Scalar(buf.at<Vec3b>(i)), -1, 8);
}

执行反向投影

calcBackProject(&hue, 1, 0, hist, backproj, &phranges);

执行MeanShift均值迁移分析,得到每帧移动位置信息, 并完成绘制

meanShift(backproj, trackWindow, TermCriteria(TermCriteria::EPS | TermCriteria::COUNT, 10, 1));
rectangle(image, trackWindow, Scalar(0, 0, 255), 3, LINE_AA);

完整的演示代码如下

#include <opencv2/opencv.hpp>"
#include <iostream>
#include <ctype.h>

using namespace cv;
using namespace std;

Mat image;
bool selectObject = false;
int trackObject = 0;
bool showHist = true;
Point origin;
Rect selection;
int vmin = 10, vmax = 256, smin = 30;

int main(int argc, const char** argv)
{
    // VideoCapture cap(0);
    VideoCapture cap("D:/images/video/balltest.mp4");
    Rect trackWindow;
    int hsize = 16;
    float hranges[] = { 0,180 };
    const float* phranges = hranges;

    if (!cap.isOpened())
    {
        printf("could not open camera...\n");
        return -1;
    }

    namedWindow("Histogram", WINDOW_AUTOSIZE);
    namedWindow("CamShift Demo", WINDOW_AUTOSIZE);

    Mat frame, hsv, hue, mask, hist, histimg = Mat::zeros(200, 320, CV_8UC3), backproj;
    bool paused = false;
    cap.read(frame);
    Rect selection = selectROI("CamShift Demo", frame, true, false);

    while(true)
    {
        bool ret = cap.read(frame);
        if (!ret) break;
        frame.copyTo(image);

        cvtColor(image, hsv, COLOR_BGR2HSV);

        int _vmin = vmin, _vmax = vmax;
        inRange(hsv, Scalar(26, 43, 46), Scalar(34, 255, 255), mask);
        int ch[] = { 0, 0 };
        hue.create(hsv.size(), hsv.depth());
        mixChannels(&hsv, 1, &hue, 1, ch, 1);

        if (trackObject <= 0)
        {
            // Object has been selected by user, set up CAMShift search properties once
            Mat roi(hue, selection), maskroi(mask, selection);
            calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);
            normalize(hist, hist, 0, 255, NORM_MINMAX);

            trackWindow = selection;
            trackObject = 1; // Don't set up again, unless user selects new ROI

            histimg = Scalar::all(0);
            int binW = histimg.cols / hsize;
            Mat buf(1, hsize, CV_8UC3);
            for (int i = 0; i < hsize; i++)
                buf.at<Vec3b>(i) = Vec3b(saturate_cast<uchar>(i*180. / hsize), 255, 255);
            cvtColor(buf, buf, COLOR_HSV2BGR);

            for (int i = 0; i < hsize; i++)
            {
                int val = saturate_cast<int>(hist.at<float>(i)*histimg.rows / 255);
                rectangle(histimg, Point(i*binW, histimg.rows),
                    Point((i + 1)*binW, histimg.rows - val),
                    Scalar(buf.at<Vec3b>(i)), -1, 8);
            }
        }

        // Perform meanShift
        calcBackProject(&hue, 1, 0, hist, backproj, &phranges);
        backproj &= mask;
        meanShift(backproj, trackWindow, TermCriteria(TermCriteria::EPS | TermCriteria::COUNT, 10, 1));
        rectangle(image, trackWindow, Scalar(0, 0, 255), 3, LINE_AA);

        imshow("CamShift Demo", image);
        imshow("Histogram", histimg);
        char c = (char)waitKey(50);
        if (c == 27)
            break;
    }

    return 0;
}

显示效果如下:
image
image

原文发布时间为:2018-12-19
本文作者:gloomyfish
本文来自云栖社区合作伙伴“ OpenCV学堂”,了解相关信息可以关注“CVSCHOOL”微信公众号