Mean Shift算法(2)在OpenCV上的实现目标跟踪——直方图反向投影
直方图反向投影
直方图反向投影的结果是一个概率分布图,表示一个指定图像片段出现在特定位置的概率。假设我们已经知道图像中某个物体的大致位置,就可以用概率分布图找到物体的准确位置。最可能出现的位置就是窗口中概率最大的位置。如果从一个可能的初始位置开始,在该位置周围反复移动,就可能找到物体所在的准确位置,这个实现方法称为均值漂移算法Mean Shift。MeanShift算法在视频跟踪领域应用广泛,最早由D.Comaniciu和P.Meer于2002年在P.A.M.I上首次提出:Mean Shift: A robust approachtoward feature space analysis[J], IEEE Trans. on Pattern Analysis and MachineIntellligence, 2002,5(24).
函数 cv::meanShift 在给定反向投影和初始搜索窗口位置的情况下,用迭代方法寻找目标中心。当搜索窗口中心的移动小于某个给定值时或者函数已经达到最大迭代次数时停止迭代。 函数返回迭代次数。
int meanShift( InputArray probImage, CV_OUT CV_IN_OUT Rect& window, TermCriteria criteria );
参数说明:
probImage:概率分布图像,可以是目标直方图的反向投影(见 cvCalcBackProject)
Window:初始搜索窗口,可以是使用Rect定义ROI,定义目标区域,求其颜色直方图
Criteria:确定窗口搜索停止的准则,OpenCV实现该算法时定义了两个停止条件:迭代次数达到设置的最大值;窗口中心的漂移值小于某个设定的限值。
程序实现步骤:
- 读入参考图像
- 设置感兴趣区域(ROI)
- 获取感兴趣区域的色调直方图
- 读取新图像计算色调直方图的反向投影:新建直方图反向投影类实例,将色调直方图输入该实例
- 使用meanShift算法在反向投影图中定位物体:设置初始搜索区域,设置迭代终止条件
让我们一步一步来吧!首先要了解什么是直方图反向投影?
反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的特征。反向投影在某一位置的值就是原图对应位置像素值在原图像中的总数目。例如,有一张含有人脸图像,我们选中人脸的区域,求其特征,我们求该特征在这幅图像上的反向投影,得到一张投影图,使用统计学的语言,该反向投影图中存储的数值代表了原始图像中该像素属于人脸区域的概率。亮的区域是人脸区域的可能性更大,而暗的区域则表示更低的可能性。
举个简单的例子来帮助理解这段话的意思。例如灰度图像的像素值如下
对图像进行直方图统计(bin指定的区间为[0,2],[3,5],[6,8],[9,11])如下所示: Histogram=5 4 6 1
根据上述的直方图进行反向投影,得到反向投影图像像素值如下:
例如位置(0,0)上的像素值为1,对应的bin为[0,2),所以反向直方图在该位置上的值这个bin的值5,而在位置(3,3)上的像素为9,其在直方图中的统计为1,故其反向投影图像中的像素为1。
关于反向投影想了解更多的话可参见:https://blog.csdn.net/keith_bb/article/details/70154219
通过求出反向投影后,我们就得到的meanshift函数的第一个参量“InputArray probImage”。
int meanShift( InputArray probImage, CV_OUT CV_IN_OUT Rect& window, TermCriteria criteria );
反向投影在OpenCV中如何实现?主要依赖两个函数:
calcHist()用于计算指定图像区域的直方图
函数原型:
void calcHist(const Mat* arrays, int narrays, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate= false )
参数:
- arrays输入的图的指针,也就是说可以同时输入多个图
- narrays输入的图的个数
- channels每个图的通道数
- mask掩码,其中值为1的点对应的点将被用于计算
- hist计算出的直方图
- dims计算出的直方图的维度,一般为1
- histSize,计算出的直方图的维度上的直方图条数
- ranges用来进行统计的范围
calcBackProject()计算反向投影
函数原型:void cv::calcBackProject( const Mat * images, int nimages, const int * channels, InputArray hist, OutputArray backProject, const float ** ranges, double scale = 1, bool uniform=true )
参数:
- images: 输入图像,图像深度必须位CV_8U,CV_16U或CV_32F中的一种,尺寸相同,每一幅图像都可以有任意的通道数
- nimages: 输入图像的数量
- channels: 用于计算反向投影的通道列表,通道数必须与直方图维度相匹配,第一个数组的通道是从0到image[0].channels()-1,第二个数组通道从图像image[0].channels()到image[0].channels()+image[1].channels()-1计数
- hist: 输入的直方图,直方图的bin可以是密集(dense)或稀疏(sparse)
- backProject: 目标反向投影输出图像,是一个单通道图像,与原图像有相同的尺寸和深度
- ranges**: 直方图中每个维度bin的取值范围
- double scale=1: 可选输出反向投影的比例因子
OpenCV实现:
#include<iostream>
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<ctype.h>
using namespace std;
using namespace cv;Mat image; //当前帧图像
Mat imageCopy; //用于拷贝的当前帧图像
Mat rectImage; //子图像
Point beginPoint; //矩形框起点
Point endPoint; //矩形框终点
bool leftButtonDownFlag = false; //左键单击后视频暂停播放的标志位
int frameCount = 0; //帧数统计
int trackCount = 0; //等于1时初始化直方图
void onMouse(int event, int x, int y, int flags, void* ustc); //鼠标回调函数 int main(int argc,char* argv[]) {VideoCapture capture("C:\\Users\\14527\\Desktop\\Video\\emmm.AVI");//VideoCapture capture(0);int capture_fps = capture.get(CV_CAP_PROP_FPS); //获取视频帧率 int capture_count = capture.get(CV_CAP_PROP_FRAME_COUNT);int capture_width = capture.get(CV_CAP_PROP_FRAME_WIDTH);int capture_height = capture.get(CV_CAP_PROP_FRAME_HEIGHT);cout << "视频帧率:" << capture_fps << endl;cout << "视频帧数:" << capture_count << endl;cout << "视频宽度:" << capture_width << endl;cout << "视频高度:" << capture_height << endl;int pauseTime = 1000 / capture_fps; //两幅画面中间间隔 VideoWriter writer("C:\\Users\\14527\\Desktop\\Video\\out.avi", CV_FOURCC('X', 'V', 'I', 'D'), capture_fps, Size(capture_width, capture_height));namedWindow("Video");setMouseCallback("Video", onMouse);int vmin = 10, vmax = 256,smin = 30;//设置HSV中V和S的值int hbinNum = 16;//灰度分级16float hranges[] = { 40,250 };const float* phranges = hranges;bool backprojectMode = false;namedWindow("Histogram", 0);namedWindow("Video", 0);createTrackbar("Vmin", "Video", &vmin, 256, 0);//createTrackbar函数的功能是在对应的窗口创建滑动条,滑动条Vmin,vmin表示滑动条的值,最大为256 createTrackbar("Vmax", "Video", &vmax, 256, 0);//最后一个参数为0代表没有调用滑动拖动的响应函数 createTrackbar("Smin", "Video", &smin, 256, 0);//vmin,vmax,smin初始值分别为10,256,30 Mat hsvImg;//HSV图像capture >> image;Mat hue, mask, hist, histImg = Mat::zeros(image.size(), image.type()),backproj;Rect trackWindow;while (true) {if (!leftButtonDownFlag) //鼠标左键按下绘制矩形时,视频暂停播放 {capture >> image;frameCount++; //帧数 }if (!image.data || waitKey(pauseTime + 30) == 27) //图像为空或Esc键按下退出播放 {break;}if (trackCount>0) {cvtColor(image, hsvImg, CV_BGR2HSV);inRange(hsvImg, Scalar(0, smin, min(vmin, vmax)), Scalar(180, 256, max(vmin, vmax)), mask);int ch[] = { 0,0 };hue.create(hsvImg.size(), hsvImg.depth());//hue初始化为与hsv大小深度一样的矩阵 mixChannels(&hsvImg, 1, &hue, 1, ch, 1);//将hsv第一个通道(也就是色调)的数复制到hue中 if (trackCount == 1) {histImg = Scalar::all(0);Mat roi(hue, Rect(beginPoint, endPoint)), maskroi(mask, Rect(beginPoint, endPoint));calcHist(&roi, 1, 0, maskroi, hist, 1, &hbinNum, &phranges);normalize(hist, hist, 0, 255, CV_MINMAX);trackCount++;trackWindow = Rect(beginPoint, endPoint);}calcBackProject(&hue, 1, 0, hist, backproj, &phranges);backproj &= mask;meanShift(backproj, trackWindow, TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1));if (backprojectMode) {cvtColor(backproj, image, CV_GRAY2BGR);}rectangle(image, Point(trackWindow.x, trackWindow.y), Point(trackWindow.x + trackWindow.width, trackWindow.y + trackWindow.height), Scalar(0, 0, 255), 1, CV_AA);trackCount++;// writer << image;}imshow("Video", image);}waitKey(0);return 0;
}//鼠标回调函数
void onMouse(int event, int x, int y, int flags, void *ustc)
{if (event == CV_EVENT_LBUTTONDOWN){leftButtonDownFlag = true; //标志位 beginPoint = Point(x, y); //设置左键按下点的矩形起点 endPoint = beginPoint;}if (event == CV_EVENT_MOUSEMOVE && leftButtonDownFlag){imageCopy = image.clone();endPoint = Point(x, y);if (beginPoint != endPoint){//在复制的图像上绘制矩形 rectangle(imageCopy, beginPoint, endPoint, Scalar(0, 0, 255), 2);}imshow("Video", imageCopy);}if (event == CV_EVENT_LBUTTONUP){leftButtonDownFlag = false;Mat subImage = image(Rect(beginPoint, endPoint)); //子图像 rectImage = subImage.clone();trackCount = 1;//imshow("Sub Image", rectImage);}
}
https://blog.csdn.net/weixin_38312031/article/details/79632521
https://blog.csdn.net/iracer/article/details/48955151
Mean Shift算法(2)在OpenCV上的实现目标跟踪——直方图反向投影相关推荐
- OpenCV中直方图反向投影算法详解与实现
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自:opencv学堂 一:直方图交叉 OpenCV中直方图反向 ...
- opencv python 直方图反向投影_python OpenCV学习笔记直方图反向投影的实现
本文介绍了python OpenCV学习笔记直方图反向投影的实现,分享给大家,具体如下: 它用于图像分割或寻找图像中感兴趣的对象.简单地说,它创建一个与我们的输入图像相同大小(但单通道)的图像,其中每 ...
- opencv进阶学习笔记7:直方图,直方图均衡化,直方图比较,直方图反向投影
基础版传送门: python3+opencv学习笔记汇总目录(适合基础入门学习) 进阶版笔记目录链接: python+opencv进阶版学习笔记目录(适合有一定基础) 直方图基础讲解: opencv学 ...
- opencv 直方图反向投影
转载至:http://www.cnblogs.com/zsb517/archive/2012/06/20/2556508.html 直方图反向投影式通过给定的直方图信息,在图像找到相应的像素分布区域, ...
- opencv 直方图_OpenCV之图像直方图反向投影
python代码: import cv2 as cv import numpy as np from matplotlib import pyplot as pltdef back_projectio ...
- Python+OpenCV:直方图反向投影(Histogram Backprojection)
Python+OpenCV:直方图反向投影(Histogram Backprojection) Algorithm in Numpy 1. First we need to calculate the ...
- OpenCV + CPP 系列(十九)直方图比较 与 直方图反向投影,投影分割
文章目录 一.直方图比较 计算公式 效果演示 二.直方图反向投影 三.投影分割 一.直方图比较 对输入的两张图像计算得到直方图H1与H2,归一化到相同的尺度空间,然后可以通过计算H1与H2的之间的距离 ...
- OpenCV 3 Tracking API目标跟踪学习笔记——定义、物体跟踪常用算法、demo
今天开始接触目标跟踪 本文翻译自https://www.learnopencv.com/object-tracking-using-opencv-cpp-python/#opencv-tracking ...
- OpenCV3学习(12.5) opencv实现粒子滤波目标跟踪
OpenCV高版本已经把粒子滤波的CV方面的condensation算法给去掉了,以前学的condensation算法不能用C++开发还是只能用C版本,(OpenCV3学习(12.4) 粒子滤波Con ...
最新文章
- 推送通知服务【WP7学习札记之十三】
- 《系统集成项目管理》第六章 项目整体管理
- LeetCode-38 报数
- vue 调用mutation方法_Vuex白话教程第三讲:Vuex旗下的Mutation
- python小游戏——21点
- 2017蓝桥杯省赛---java---C---7 Excel地址)
- C语言中#define中的一些特殊用法
- 千头万绪:从一道面试题看数据库性能和安全的方方面面
- 启航龙图计算机网络,2020年哈尔滨工业大学854计算机基础考研大纲
- jquery中的each不能结束外层的function
- C语言 求平方 求开方
- sql2000 数据库置疑解决办法
- Progressive Layered Extraction (PLE)
- php orc 验证码,百度图片识别orc实现普通验证码识别
- 产品分析报告——“京东到家”
- 全新的Uber App设计
- Redis高级项目实战!mysql和java的管理系统源码
- 2016.9.8面试水笔
- qt程序打开PDF格式文件
- 面试官问职业规划我们要如何回答?
热门文章
- 最详细的FPN论文笔记
- Kali Linux学习入门-更换源及强制更新
- Android SDK实例之Snake游戏深入解析(一)
- android layout 层次感,FrameLayout的层次问题
- mysql中credit,CREDIT项目:我国类风湿关节炎数据库建设开启
- java基础经典面试题10道
- 链接(跳转)router-link 和 路由实例Router
- docker 在window 10 专业版的安装 .net core 在docker的部署
- C#代码规范化(代码风格化)的几个函数
- 《C++ Primer》第五版课后习题解答_第二章(1)(01-08)