meanshift算法学习(三):自己实现meanshift

2024-02-07 14:50
文章标签 算法 实现 学习 meanshift

本文主要是介绍meanshift算法学习(三):自己实现meanshift,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

       有关meanShift的原理和数学推导,网上有大神提供了很详细的讲解文档,这里我推荐下面三篇个人认为比较好的文章。

https://wenku.baidu.com/view/5862334827d3240c8447ef40.html meanShift算法简介

http://blog.csdn.net/jinshengtao/article/details/30258833 基于meanShift的目标跟踪算法及实现

http://blog.csdn.net/anymake_ren/article/details/25484059 meanShift知识整理

       原理部分就不再重复造轮子了,这里直接把自己写的代码贴出来。代码实现的是读取一个视频序列,按下按键“p”后视频暂停,通过鼠标左键进行跟踪区域的选取,选取结束后按下按键“p”后视频继续播放同时开始跟踪。

#include "core/core.hpp"    
#include "highgui/highgui.hpp"    
#include "imgproc/imgproc.hpp"
#include "video/tracking.hpp"
#include<iostream>    
#include <numeric>
using namespace cv;    
using namespace std;    Mat image;
bool leftButtonDown = false;// 鼠标左键是否按下
bool videoPauseFlag = false;// 是否暂停视频
bool trackingFlag = false;// 是否开始meanShift跟踪
Point pt1, pt2;// 记录选择区域的左上点/右下点
Rect rect;// 跟踪区域
vector<float> dstRegionDensity;// 目标跟踪区域的核函数估计密度向量
vector<float> testRegionDensity;// 候选区域的核函数估计密度向量
vector<float> w;// meanShift公式中的权值计算
int densityNum = 4096;// 对于每一帧图像的R/G/B三通道,每个通道按照值的大小分为16个区间,所以密度向量为16*16*16=4096维
void onMouse(int event,int x,int y,int flags ,void* ustc); //鼠标回调函数  
void calcKernelDensity(Mat imageSrc, vector<float>&density, int densityNum); // 计算图像的核函数估计密度向量
void meanShiftTracking(Mat& imageSrc, int iteration, double eps, Rect& rect);// meanShift算法跟踪int main()
{// 打开视频文件VideoCapture cap("768X576.avi");if(!cap.isOpened()){cout<<"cannot open avi file"<<endl;return -1;}// 全局链表分配空间dstRegionDensity.resize(densityNum);testRegionDensity.resize(densityNum);w.resize(densityNum);double fps = cap.get(CV_CAP_PROP_FPS);// 获取图像帧率int pauseTime = (int)(1000.f/fps);namedWindow("video");setMouseCallback("video", onMouse);while(1){if(waitKey(pauseTime) == 'p'){videoPauseFlag = !videoPauseFlag;}if(videoPauseFlag)// 暂停视频时不会更新图像帧continue;else{cap>>image;if(trackingFlag){meanShiftTracking(image, 30, 0.2, rect);// meanShift跟踪int num = 0;/*for(int n = 0; n < densityNum; n++){dstRegionDensity[n] = testRegionDensity[n];}*/rectangle(image, rect, Scalar(0, 0, 0));// 绘制出计算得到的跟踪位置}}imshow("video", image);}}void onMouse(int event,int x,int y,int flags ,void* ustc)
{Mat imageROI;// 鼠标左键按下获取区域起始点if(event == CV_EVENT_LBUTTONDOWN&&!trackingFlag){leftButtonDown = true;pt1 = Point(x,y);}// 拖动选取区域并使用黑色线框显示选择区域else if(event == CV_EVENT_MOUSEMOVE && leftButtonDown){Mat image_tmp;image.copyTo(image_tmp);pt2 = Point(x,y);rectangle(image_tmp, pt1, pt2, Scalar(0, 0, 0));imshow("video", image_tmp);}// 左键松开获取区域结束点,确定目标跟踪区域else if(event == CV_EVENT_LBUTTONUP && leftButtonDown){leftButtonDown = false;pt2 = Point(x,y);image(Rect(pt1, pt2)).copyTo(imageROI);rect.x = std::min(pt1.x, pt2.x);rect.y = std::min(pt1.y, pt2.y);rect.width = pt1.x > pt2.x ? pt1.x - pt2.x : pt2.x - pt1.x;rect.height = pt1.y > pt2.y ? pt1.y - pt2.y : pt2.y - pt1.y;namedWindow("imageROI");imshow("imageROI", imageROI);// 计算目标跟踪区域的核函数估计密度向量calcKernelDensity(imageROI, dstRegionDensity, densityNum);waitKey(2000);destroyWindow("imageROI");trackingFlag = true;}}void calcKernelDensity(Mat imageSrc, vector<float>&density, int densityNum)
{/* 选取的核函数轮廓函数为k(x) = 1-x^2,其中x为图像中像素到图像中心位置的归一化距离*/int rows = imageSrc.rows;int cols = imageSrc.cols;float h = 0.25 * (rows*rows + cols*cols);// 带宽float k_sum = 0;for(int i = 0; i < densityNum; i++){density[i] = 0;}for(int i = 0; i < rows; i++){for(int j = 0; j < cols; j++){int b,g,r,index;b = imageSrc.at<cv::Vec3b>(i,j)[0];g = imageSrc.at<cv::Vec3b>(i,j)[1];r = imageSrc.at<cv::Vec3b>(i,j)[2];index = b/16*256 + g/16*16 + r/16;// 获取像素点的索引值,0-4095float dis = ((i- rows/2)*(i- rows/2) + (j- cols/2)*(j- cols/2))/h;// x = sqrt(dis)float k = 1-dis;// k(x) = 1 - x^2; density[index] += k;k_sum += k;}}for(int i = 0; i < densityNum; i++){density[i] /= k_sum;// 密度归一化}
}void meanShiftTracking(Mat& imageSrc, int iteration, double eps, Rect& rect)
{Mat imageROI;int num = 0;while(1){imageSrc(rect).copyTo(imageROI);// 获取感兴趣区域Point2f pt_d;pt_d.x = pt_d.y = 0;float weightSum = 0;float h = 0.25 * (imageROI.rows*imageROI.rows + imageROI.cols*imageROI.cols);// 带宽calcKernelDensity(imageROI, testRegionDensity, densityNum);// 计算候选区域的核函数估计密度向量for(int i = 0; i < densityNum; i++){if(testRegionDensity[i] != 0)w[i] = sqrt(dstRegionDensity[i] / testRegionDensity[i]);elsew[i] = 0;// 计算迭代公式中的权值}for(int i = 0; i < imageROI.rows; i++){for(int j = 0; j < imageROI.cols; j++){int b,g,r, index;b = imageROI.at<cv::Vec3b>(i,j)[0];g = imageROI.at<cv::Vec3b>(i,j)[1];r = imageROI.at<cv::Vec3b>(i,j)[2];index = b/16*256 + g/16*16 + r/16;float dis = ((i- imageROI.rows/2)*(i- imageROI.rows/2)+ (j- imageROI.cols/2)*(j- imageROI.cols/2))/h;float weight_g = 2*sqrt(dis);pt_d.x += w[index]*weight_g*(j - imageROI.cols/2);pt_d.y += w[index]*weight_g*(i- imageROI.rows/2);weightSum += w[index]*weight_g;}}pt_d.x/=weightSum;pt_d.y/=weightSum;// 计算meanShift增量rect.x += pt_d.x;rect.y += pt_d.y;// 更新跟踪区域rect = rect&Rect(0, 0, imageSrc.cols, imageSrc.rows);// 保证跟踪区域位于图像内,这里的处理不一定合适float e = (pt_d.x*pt_d.x + pt_d.y * pt_d.y);if(e < eps)break;// 阈值判断num++;if(num > iteration)// 迭代次数判断break;}	
}

       跟踪效果如下图所示。



       代码和视频文件可以在点击打开链接下载。

这篇关于meanshift算法学习(三):自己实现meanshift的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/688063

相关文章

Spring Boot 实现 IP 限流的原理、实践与利弊解析

《SpringBoot实现IP限流的原理、实践与利弊解析》在SpringBoot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文给大家介绍SpringBoot实现IP限... 目录一、引言二、IP 限流原理2.1 令牌桶算法2.2 漏桶算法三、使用场景3.1 防止恶意攻击3.2 控制资源

springboot下载接口限速功能实现

《springboot下载接口限速功能实现》通过Redis统计并发数动态调整每个用户带宽,核心逻辑为每秒读取并发送限定数据量,防止单用户占用过多资源,确保整体下载均衡且高效,本文给大家介绍spring... 目录 一、整体目标 二、涉及的主要类/方法✅ 三、核心流程图解(简化) 四、关键代码详解1️⃣ 设置

Nginx 配置跨域的实现及常见问题解决

《Nginx配置跨域的实现及常见问题解决》本文主要介绍了Nginx配置跨域的实现及常见问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来... 目录1. 跨域1.1 同源策略1.2 跨域资源共享(CORS)2. Nginx 配置跨域的场景2.1

Python中提取文件名扩展名的多种方法实现

《Python中提取文件名扩展名的多种方法实现》在Python编程中,经常会遇到需要从文件名中提取扩展名的场景,Python提供了多种方法来实现这一功能,不同方法适用于不同的场景和需求,包括os.pa... 目录技术背景实现步骤方法一:使用os.path.splitext方法二:使用pathlib模块方法三

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4