OpenCV视频分析与对象跟踪C++(三)CAMShift对象跟踪

MeanShift算法(均值漂移)
假设有一堆数据,不均匀的在平面空间分布,首先通过均值计算,评估中心点在哪里,然后中心向密度的地方平移最后找到密度最高的地方。
MeanShift算法使用的直方图比较与模板匹配类似,这两个算法只能匹配Windows固定的roi区域。
如果视频中选择的roi区域的移动忽远忽近(甚至变形),这两种算法就不能再准确的跟踪到roi区域这时候用CAMShift
:窗口尺寸自动变化
:适合变形目标检测

步骤:
是第一帧的话则:

  • 读取第一帧
  • 选择ROI区域
  • HSV空间H通道直方图
  • 直方图反向映射
  • CAMShift位置跟踪
  • 绘制位置更新显示

不是第一帧的话则:

  • 直方图反向映射
  • CAMShift位置跟踪
  • 绘制位置更新显示

相关API:

mixChannels( // 将输入数组的指定通道复制到输出数组的指定通道
const Mat* src, //输入数组或向量矩阵,所有矩阵的大小和深度必须相同。
size_t nsrcs, //矩阵的数量
Mat* dst, //输出数组或矩阵向量,大小和深度必须与src相同
size_t ndsts,//矩阵的数量
const int* fromTo,//指定被复制通道与要复制到的位置组成的索引对
size_t npairs //fromTo中索引对的数目
);


calcHist(// 计算直方图
const Mat* images,//输入图像指针
int images,// 图像数目
const int* channels,// 要计算的通道数的下标,可以传一个数组 {0, 1} 表示计算第0通道与第1通道的直方图,此数组长度要与histsize ranges 数组长度一致
InputArray mask,// 输入mask,可选,如有,则表示只计算mask元素值为255的位置的直方图
OutputArray hist,//输出的直方图数据
int dims,// 维数
const int* histsize,// 直方图级数, 对应 bins
const float** ranges,// 值域范围
bool uniform,// true by default   是否归一化到 0-1 之间
bool accumulate// false by defaut 多通道时true
)

calcBackProject ( // 反向投影
const Mat * images, // 输入图像,图像深度必须位CV_8U,CV_16U或CV_32F中的一种,尺寸相同,每一幅图像都可以有任意的通道数
int nimages, // 输入图像的数量
const int * channels, // 用于计算反向投影的通道列表,通道数必须与直方图维度相匹配,第一个数组的通道是从0到image[0].channels()-1,
第二个数组通道从图像image[0].channels()到image[0].channels()+image[1].channels()-1计数
InputArray hist, // 输入的直方图,直方图的bin可以是密集(dense)或稀疏(sparse)
OutputArray backProject, // 目标反向投影输出图像,是一个单通道图像,与原图像有相同的尺寸和深度
const float ** ranges, // 直方图中每个维度bin的取值范围
double scale = 1, // 可选输出反向投影的比例因子
bool uniform = true // 直方图是否均匀分布(uniform)的标识符,默认值true
)

RotatedRect CamShift( // CAMShift跟踪算法
InputArray probImage, // 反向投影图像
CV_IN_OUT Rect& window, // 输入和输出的搜索窗口/目标窗口,window的尺寸在跟踪过程中会随着跟踪目标的大小变化自动调整
TermCriteria criteria // 迭代收敛终止条件
);
返回值为跟踪到的目标所在的旋转了的矩形框
CAMShift跟踪算法对于跟踪的目标,即使发生远近变换,形变,都能准确跟踪到,甚至跟踪目标离开了屏幕再回来也能继续跟踪到,
但是离开的期间也会产生 selection,所以这个时候的selection是不对的,当跟踪目标回到屏幕后也不是立即就察觉到,需要一定时间
so CAMShift无法判断目标是否离开屏幕,这段期间产生的误差如何解决?
同时CAMShift跟踪算法适合比较简单的图像,如果图像颜色数据很复杂,有很多大量与跟踪目标颜色重复的像素的话(也就是干扰很强),CAMShift效果就不太好了

代码:

#include 
#include 
#include
#include
#include
#include  
#include 

using namespace cv::face;
using namespace cv;
using namespace std;
using namespace cv::xfeatures2d;

 int smin = 15; // 二值化时,s 最小值的阈值
 int vmin = 40; // 二值化时,v 最小值的阈值
 int vmax = 256; // 二值化时,s v 最大值的阈值
 int bins = 16; // 直方图bins

 int smin = 15; // 二值化时,s 最小值的阈值
 int vmin = 40; // 二值化时,v 最小值的阈值
 int vmax = 256; // 二值化时,s v 最大值的阈值
 int bins = 16; // 直方图bins

int main()
{
    VideoCapture capture;
    capture.open("C:/Users/Administrator/Desktop/pic/3.avi");

    bool firstRead = true;
    float hrange[] = { 0, 180 };
    const float* hranges = hrange;
    Rect selection; // roi区域
    Mat frame, hsv, hue, mask, hist, backprojection;
    Mat drawImg = Mat::zeros(300, 300, CV_8UC3); // 直方图
    char roiName[] = "CAMShift Tracking";
    while (capture.read(frame))
    {
        if (firstRead)
        {
            cout << "frame.size=" << frame.size() << endl;
            // 从frame中选择roi区域,程序会阻断,等待选择。返回值为选择的roi区域,参数windowName要与后面的imshow的name一致
            Rect2d first = selectROI(roiName, frame); // roi可以选择整个黄色小球区域,也可以只选择其内部一部分区域
            selection.x = first.x;
            selection.y = first.y;
            selection.width = first.width;
            selection.height = first.height;
            printf("ROI.x= %d, ROI.y= %d, width = %d, height= %d\n", selection.x, selection.y, selection.width, selection.height);
        }
        // convert to HSV
        cvtColor(frame, hsv, COLOR_BGR2HSV);
        inRange(hsv, Scalar(0, smin, vmin), Scalar(180, vmax, vmax), mask); // 二值化,为CAMShift跟踪算法去掉一定的干扰
        hue = Mat(hsv.size(), hsv.depth());
        int channels[] = { 0, 0 }; // hsv 的第0通道,输出到hue的第0通道

        mixChannels(&hsv, 1, &hue, 1, channels, 1); // 将输入数组的指定通道复制到输出数组的指定通道

        if (firstRead)
        {
            // ROI 直方图计算
            Mat roi(hue, selection); // 抠出hue中roi区域
            Mat maskroi(mask, selection); // 抠出mask中roi区域

            calcHist(&roi, 1, 0, maskroi, hist, 1, &bins, &hranges); // 这里直方图只计算绘制一次第一帧上选择的roi区域
                                                                     // hist size=[1 x 16], depth=5, type=5    CV_32F
            cout << "hist size=" << hist.size() << ", depth=" << hist.depth() << ", type=" << hist.type() << endl;
            normalize(hist, hist, 0, 255, NORM_MINMAX); // 归一化,排除光强干扰进行比较

                                                        // show histogram
            int binw = drawImg.cols / bins;
            Mat colorIndex = Mat(1, bins, CV_8UC3);
            for (int i = 0; i < bins; i++)
            {
                colorIndex.at(0, i) = Vec3b(saturate_cast(i * 180 / bins), 255, 255); // 按照HSV赋值,下面再转换为BGR颜色空间
            }
            cvtColor(colorIndex, colorIndex, COLOR_HSV2BGR); // 不转换为BGR也是可以的,只是直方图颜色不一样而已
            for (int i = 0; i < bins; i++)
            {
                // 如果roi选择的是黄色小球区域或其内部一部分区域,hist的值基本属于两值分化的状态,0 或 255
                int  val = saturate_cast<int>(hist.at<float>(i)*drawImg.rows / 255); // val 等于 0-drawImg.rows 之间
                rectangle(drawImg, Point(i*binw, drawImg.rows), Point((i + 1)*binw, drawImg.rows - val), Scalar(colorIndex.at(0, i)), -1, 8, 0); // 绘制直方图
            }
        }

        calcBackProject(&hue, 1, 0, hist, backprojection, &hranges); // 反响映射
        imshow("backprojection", backprojection);
        imshow("mask", mask);
        // CAMShift tracking
        backprojection &= mask; // &=运算符重载,backprojection与mask必须同size,返回结果为它们相同位置的像素值都为255的像素保留,否则为0
        imshow("back&mask", backprojection);

        // 参数 TermCriteria((TermCriteria::COUNT | TermCriteria::EPS), 10, 1) 表示MeanShift寻找中心点的算法时,最多迭代10次,当中心点的距离插值小于1,立即结束
        RotatedRect trackBox = CamShift(backprojection, selection, TermCriteria((TermCriteria::COUNT | TermCriteria::EPS), 10, 1)); // 算法速度很快,满足实时性
        cout << "selection.size=" << selection.size() << ", ";

        // draw location on frame;
        ellipse(frame, trackBox, Scalar(0, 0, 255), 3, 8); // 在跟踪的roi区域绘制椭圆

        if (firstRead) firstRead = false;
        imshow(roiName, frame);
        imshow("ROI Histogram", drawImg);
    }
    capture.release();

    waitKey(0);
}

结果:
OpenCV视频分析与对象跟踪C++(三)CAMShift对象跟踪_第1张图片
OpenCV视频分析与对象跟踪C++(三)CAMShift对象跟踪_第2张图片

你可能感兴趣的:(OpenCV视频分析与对象跟踪C++(三)CAMShift对象跟踪)