大家好哦,小编来讲程序啦,好好看哦,慢慢体会。。。一、说明实验介绍本次实验将使用利用 OpenCV 来实现对视频中感兴趣的动态物体的追踪。
首先我贴出我的水下追踪效果图吧


在图中我们可以看到鱼的追踪轨迹,以及鱼的个数
实验涉及的知识点

C++ 语言基础
g++ 的使用
图像基础
OpenCV 在图像及视频中的应用
Meanshift 和 Camshift 算法

二、环境搭建进行本节的实验时,您需要先完成了解一些linux 基本指令和图像处理知识才能进行下面的相关学习。
创建视频文件视频可以通过水下摄像头获取,当然了我们同样可以实时性处理,前面打开摄像头实验已经介绍过了,此实验只不过添加了meanshift和camshift.前面扯过的东西不知道大家看了没有,我再扯一遍吧,看看那图像的基本知识。。。。。
图像基础OpenCV 是一个开源的跨平台计算机视觉库,与 OpenGL 所提供的图像绘制不同,OpenCV 实现了图像处理和计算机视觉方面的很多通用的算法。在学习 OpenCV 之前,我们需要先了解一下图像、视频在计算机中的一些基本概念。
首先,我们要理解图片在计算机中的表示方式:图像在显示生活中以连续变化的形式而存在,但在计算机中,有两种常见的存储图片的方式:一种是矢量图,一种则是像素图。
矢量图,也称为面向对象的图像或绘图图像,在数学上定义为一系列由线连接的点。矢量文件中的图形元素称为对象。每个对象都是一个自成一体的实体,它具有颜色、形状、轮廓、大小和屏幕位置等属性。
而更常见的则是像素图,比如常说的一张图片的尺寸为1024*768,这就意味着这张图片水平方向上有1024个像素,垂直方向上有768个像素。
像素就是表示像素图的基本单位,通常情况下一个像素由三原色(红绿蓝)混合而成。由于计算机的本质是对数字的识别,一般情况下我们把一种原色按亮度的不同从 0~255 进行表示,换句话说,对于原色红来说,0表示最暗,呈现黑色,255表示最亮,呈现纯红色。
这样,一个像素就可以表示为一个三元组(B,G,R),比如白色可以表示为(255,255,255),黑色则为(0,0,0),这时我们也称这幅图像是 RGB 颜色空间中的一副图像,R、G、B 分别成为这幅图像的三个通道,除了 RGB 颜色空间外,还有很多其他的颜色空间,如 HSV、YCrCb 等等。
像素是表示像素图的基本单位,而图像则是表示视频的基本单位。一个视频由一系列图像组成,在视频中我们称其中的图像为帧。而通常我们所说的视频帧率,意味着这个视频每秒钟包含多少帧图像。比如帧率为 25,那么这个视频每秒钟就会播放25帧图像(人眼认为少于25帧差不多就不流畅了,所以一般你得设置为25以上)。
1秒钟共有1000毫秒,因此如果帧率为 rate 那么每一帧图像之间的时间间隔为 1000/rate。
图像颜色直方图颜色直方图是描述图像的一种工具,它和普通的直方图类似,只是颜色直方图需是根据某一幅图片计算而来。
如果一副图片是 RGB 颜色空间,那么我们可以统计 R 通道中,颜色值为 0~255 这 256中颜色出现的次数,这边能获得一个长度为 256 的数组(颜色概率查找表),我们再将所有的值同时除以这幅图像中像素的总数,将这之后所得的数列转化为直方图,其结果就是颜色直方图。
直方图反向投影人们已经证明,在 RGB 颜色空间中,对光照亮度变化较为敏感,为了减少此变化对跟踪效果的影响,就需要对直方图进行反向投影。这一共分为三个步骤:

首先将图像从RGB空间转换到HSV空间。
然后对其中的 H 颜色通道的直方图。
将图像中每个像素的值用颜色概率查找表中对应的概率进行替换,就得到了颜色概率分布图。

这个过程就叫反向投影,颜色概率分布图是一个灰度图像。
OpenCV 初步使用首先安装 OpenCV:

    sudo apt-get install libopencv-dev(前面已经讲过了,这里再执行一遍,如果安装过会自动忽略)我们已经熟悉了 C++的基本语法,几乎在写过的每一个程序中都有使用到 #include <iostream> 和 using namespace std; 或者 std::cout。OpenCV 也有它自己的命名空间。

使用 OpenCV,只需要包含这一个头文件:

#include <opencv2/opencv.hpp> // OpenCV 头文件
int main() {// 创建一个视频捕获对象// OpenCV 提供了一个 VideoCapture 对象,它屏蔽了// 从文件读取视频流和从摄像头读取摄像头的差异,当构造// 函数参数为文件路径时,会从文件读取视频流;当构造函// 数参数为设备编号时(第几个摄像头, 通常只有一个摄像// 头时为0),会从摄像头处读取视频流。cv::VideoCapture video("xiaorun.avi"); // 读取文件// cv::VideoCapture video(0);        // 使用摄像头// 捕获画面的容器,OpenCV 中的 Mat 对象// OpenCV 中最关键的 Mat 类,Mat 是 Matrix(矩阵)// 的缩写,OpenCV 中延续了像素图的概念,用矩阵来描述// 由像素构成的图像。cv::Mat frame;while(true) {// 将 video 中的内容写入到 frame 中,// 这里 >> 运算符是经过 OpenCV 重载的video >> frame;// 当没有帧可继续读取时,退出循环if(frame.empty()) break;// 显示当前帧cv::imshow("test", frame);// 录制视频帧率为 15, 等待 1000/15 保证视频播放流畅。// waitKey(int delay) 是 OpenCV 提供的一个等待函数,// 当运行到这个函数时会阻塞 delay 毫秒的时间来等待键盘输入int key = cv::waitKey(1000/15);// 当按键为 ESC 时,退出循环if (key == 27) break;}// 释放申请的相关内存cv::destroyAllWindows();video.release();return 0;
}

在shell里敲入 g++ main.cpp pkg-config opencv --libs --cflags opencv -o main
运行 ./main 可以看到成功播放视频:
理解了 Camshift 算法的基本思想之后我们就可分析实现这个代码主要分为几个步骤了:
设置选择追踪目标的鼠标回调事件;
从视频流中读取图像;
实现 Camshift 过程;

下面我们继续修改 main.cpp 中的代码:
第一步:选择追踪目标区域的鼠标回调函数与 OpenGL 不同,在 OpenCV 中,对鼠标的回调函数指定了五个参数,其中前三个是我们最需要的:通过 event 的值我们可以获取这次回调鼠标发生的具体事件,如左键被按下(CV_EVENT_LBUTTONDOWN)、左键被抬起(CV_EVENT_LBUTTONUP) 等。

    bool selectObject = false; // 用于标记是否有选取目标int trackObject = 0;       // 1 表示有追踪对象 0 表示无追踪对象 -1 表示追踪对象尚未计算 Camshift 所需的属性cv::Rect selection;        // 保存鼠标选择的区域cv::Mat image;             // 用于缓存读取到的视频帧// OpenCV 对所注册的鼠标回调函数定义为:// void onMouse(int event, int x, int y, int flag, void *param)// 其中第四个参数 flag 为 event 下的附加状态,param 是用户传入的参数,我们都不需要使用// 故不填写其参数名void onMouse( int event, int x, int y, int, void* ) {static cv:Point origin;if(selectObject) {// 确定鼠标选定区域的左上角坐标以及区域的长和宽selection.x = MIN(x, origin.x);selection.y = MIN(y, origin.y);selection.width = std::abs(x - origin.x);selection.height = std::abs(y - origin.y);// & 运算符被 cv::Rect 重载// 表示两个区域取交集, 主要目的是为了处理当鼠标在选择区域时移除画面外selection &= cv::Rect(0, 0, image.cols, image.rows);}switch(event) {// 处理鼠标左键被按下case CV_EVENT_LBUTTONDOWN:origin = cv:Point(x, y);selection = cv::Rect(x, y, 0, 0);selectObject = true;break;// 处理鼠标左键被抬起case CV_EVENT_LBUTTONUP:selectObject = false;if( selection.width > 0 && selection.height > 0 )trackObject = -1; // 追踪的目标还未计算 Camshift 所需要的属性break;}}

第二步:从视频流中读取图像我们已经在之前实现了读取视频流的基本结构,下面我们进一步细化:

    int main() {cv::VideoCapture video("video.ogv");cv::namedWindow("CamShift at Shiyanlou");// 1. 注册鼠标事件的回调函数, 第三个参数是用户提供给回调函数的,也就是回调函数中最后的 param 参数cv::setMouseCallback("CamShift at Shiyanlou", onMouse, NULL);cv::Mat frame; // 接收来自 video 视频流中的图像帧// 2. 从视频流中读取图像while(true) {video >> frame;if(frame.empty()) break;// 将frame 中的图像写入全局变量 image 作为进行 Camshift 的缓存frame.copyTo(image);// 如果正在选择追踪目标,则画出选择框if( selectObject && selection.width > 0 && selection.height > 0 ) {cv::Mat roi(image, selection);bitwise_not(roi, roi);  // 对选择的区域图像反色}imshow("CamShift at Shiyanlou", image);int key = cv::waitKey(1000/15.0);if(key == 27) break;}// 释放申请的相关内存cv::destroyAllWindows();video.release();return 0;}
提示
ROI(Region of Interest),在图像处理中,被处理的图像以方框、圆、椭圆、不规则多边形等方式勾勒出需要处理的区域,称为感兴趣区域,ROI。

第三步:实现 Camshift 过程计算追踪目标的反向投影直方图为需要先使用 cvtColor 函数,这个函数可以将 RGB 颜色空间的原始图像转换到 HSV 颜色空间。计算直方图必须在选择初始目标之后,因此:

 int main() {cv::VideoCapture video("video.ogv");cv::namedWindow("CamShift at Shiyanlou");cv::setMouseCallback("CamShift at Shiyanlou", onMouse, NULL);cv::Mat frame;cv::Mat hsv, hue, mask, hist, backproj;cv::Rect trackWindow;             // 追踪到的窗口int hsize = 16;                   // 计算直方图所必备的内容float hranges[] = {0,180};        // 计算直方图所必备的内容const float* phranges = hranges;  // 计算直方图所必备的内容while(true) {video >> frame;if(frame.empty()) break;frame.copyTo(image);// 转换到 HSV 空间cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);// 当有目标时开始处理if(trackObject) {// 只处理像素值为H:0~180,S:30~256,V:10~256之间的部分,过滤掉其他的部分并复制给 maskcv::inRange(hsv, cv::Scalar(0, 30, 10), cv::Scalar(180, 256, 10), mask);// 下面三句将 hsv 图像中的 H 通道分离出来int ch[] = {0, 0};hue.create(hsv.size(), hsv.depth());cv::mixChannels(&hsv, 1, &hue, 1, ch, 1);// 如果需要追踪的物体还没有进行属性提取,则对选择的目标中的图像属性提取if( trackObject < 0 ) {// 设置 H 通道和 mask 图像的 ROIcv::Mat roi(hue, selection), maskroi(mask, selection);// 计算 ROI所在区域的直方图calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);// 将直方图归一normalize(hist, hist, 0, 255, CV_MINMAX);// 设置追踪的窗口trackWindow = selection;// 标记追踪的目标已经计算过直方图属性trackObject = 1;}// 将直方图进行反向投影calcBackProject(&hue, 1, 0, hist, backproj, &phranges);// 取公共部分backproj &= mask;// 调用 Camshift 算法的接口cv::RotatedRect trackBox = CamShift(backproj, trackWindow, cv::TermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ));// 处理追踪面积过小的情况if( trackWindow.area() <= 1 ) {int cols = backproj.cols, rows = backproj.rows, r = (MIN(cols, rows) + 5)/6;trackWindow = cv::Rect(trackWindow.x - r, trackWindow.y - r,trackWindow.x + r, trackWindow.y + r) & cv::Rect(0, 0, cols, rows);}// 绘制追踪区域ellipse( image, trackBox, cv::Scalar(0,0,255), 3, CV_AA );}if( selectObject && selection.width > 0 && selection.height > 0 ) {cv::Mat roi(image, selection);bitwise_not(roi, roi);}imshow("CamShift at Shiyanlou", image);int key = cv::waitKey(1000/15.0);if(key == 27) break;}cv::destroyAllWindows();video.release();return 0;}

四、总结本节实现的代码下面的代码实现仅供参考:

    #include <opencv2/opencv.hpp>bool selectObject = false;int trackObject = 0;cv::Rect selection;cv::Mat image;void onMouse( int event, int x, int y, int, void* ) {static cv:Point origin;if(selectObject) {selection.x = MIN(x, origin.x);selection.y = MIN(y, origin.y);selection.width = std::abs(x - origin.x);selection.height = std::abs(y - origin.y);selection &= cv::Rect(0, 0, image.cols, image.rows);}switch(event) {case CV_EVENT_LBUTTONDOWN:origin = cv:Point(x, y);selection = cv::Rect(x, y, 0, 0);selectObject = true;break;case CV_EVENT_LBUTTONUP:selectObject = false;if( selection.width > 0 && selection.height > 0 )trackObject = -1;break;}}int main( int argc, const char** argv ){cv::VideoCapture video(0);cv::namedWindow( "CamShift at Shiyanlou" );cv::setMouseCallback( "CamShift at Shiyanlou", onMouse, 0 );cv::Mat frame, hsv, hue, mask, hist, backproj;cv::Rect trackWindow;int hsize = 16;float hranges[] = {0,180};const float* phranges = hranges;while(true) {video >> frame;if( frame.empty() )break;frame.copyTo(image);cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);if( trackObject ) {cv::inRange(hsv, cv::Scalar(0, 30, 10), cv::Scalar(180, 256, 256), mask);int ch[] = {0, 0};hue.create(hsv.size(), hsv.depth());cv::mixChannels(&hsv, 1, &hue, 1, ch, 1);if( trackObject < 0 ) {cv::Mat roi(hue, selection), maskroi(mask, selection);calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);normalize(hist, hist, 0, 255, CV_MINMAX);trackWindow = selection;trackObject = 1;}calcBackProject(&hue, 1, 0, hist, backproj, &phranges);backproj &= mask;cv::RotatedRect trackBox = CamShift(backproj, trackWindow, cv::TermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ));if( trackWindow.area() <= 1 ) {int cols = backproj.cols, rows = backproj.rows, r = (MIN(cols, rows) + 5)/6;trackWindow = cv::Rect(trackWindow.x - r, trackWindow.y - r,trackWindow.x + r, trackWindow.y + r) &cv::Rect(0, 0, cols, rows);}ellipse( image, trackBox, cv::Scalar(0,0,255), 3, CV_AA );}if( selectObject && selection.width > 0 && selection.height > 0 ) {cv::Mat roi(image, selection);bitwise_not(roi, roi);}imshow( "CamShift at Shiyanlou", image );char c = (char)cv::waitKey(1000/15.0);if( c == 27 )break;}cv::destroyAllWindows();video.release();return 0;}

五、结果重新编译 main.cpp:

g++ main.cpp `pkg-config opencv --libs --cflags opencv` -o  main

运行:

    ./main

在视频中用鼠标拖拽选择一个要追踪的物体,即可看到追踪的效果大家仔细揣摩,不要心急,好好理解这算法的内涵。。。。

opencv实现camshift算法,以及代码详解相关推荐

  1. SLAM-Visual Navigation学习之SIFT算法与代码详解

    ** SIFT算法 ** 文章目录 SIFT算法 一.特征点,关键点,角点? 二.前置知识 1.尺度 2.卷积 3.高斯函数 4.高斯卷积(模糊) 三.SIFT算法的引入 Harris算法缺陷: 1. ...

  2. 特征点检测 FAST算法及代码详解

    本文着重介绍了用于图像特征点检测的算法,FAST算法,以及使用matlab的实现. FAST算法是一种拐点检测算法,其主要应用于提取图像中的特征点,在动态成像的一系列图像中追踪定位对象.众所周知,我们 ...

  3. 《机器学习实战》第二章学习笔记:K-近邻算法(代码详解)

    <机器学习实战>数据资料以及总代码可以去GitHub中下载: GitHub代码地址:https://github.com/yangshangqi/Machine-Learning-in-A ...

  4. 鲸鱼算法matlab代码详解(一)

    主函数 clear all  clc SearchAgents_no=30; %此处为搜索代理的数量,也就是种群的数量 Function_name='F1'; %此处为调用目标函数的信息编号 Max_ ...

  5. MeanTeacher文章解读+算法流程+核心代码详解

    MeanTeacher 本博客仅做算法流程疏导,具体细节请参见原文 原文 原文链接点这里 Github 代码 Github代码点这里 解读 论文解读点这里 算法流程 代码详解 train_transf ...

  6. c语言实现sha1算法注解,【密码学】SHA1算法实现及详解

    1 SHA1算法简介 安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digit ...

  7. 【OpenCV/C++】KNN算法识别数字的实现原理与代码详解

    KNN算法识别数字 一.KNN原理 1.1 KNN原理介绍 1.2 KNN的关键参数 二.KNN算法识别手写数字 2.1 训练过程代码详解 2.2 预测分类的实现过程 三.KNN算法识别印刷数字 2. ...

  8. 干货 | OpenCV中KLT光流跟踪原理详解与代码演示

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转自:opencv学堂 稀疏光流跟踪(KLT)详解 在视频移动 ...

  9. kmeans python interation flag_机器学习经典算法-logistic回归代码详解

    一.算法简要 我们希望有这么一种函数:接受输入然后预测出类别,这样用于分类.这里,用到了数学中的sigmoid函数,sigmoid函数的具体表达式和函数图象如下: 可以较为清楚的看到,当输入的x小于0 ...

  10. 标准oc算法的推导与99行代码详解

    文章目录 标准oc算法的推导与代码详解 问题描述 OC算法的数学描述 结果展示 OC算法的matlab代码及注释 参考文献 标准oc算法的推导与代码详解 对于变密度的参数化方法,设计变量x为材料相对密 ...

最新文章

  1. mySQL(关系型数据库管理系统)编辑
  2. Ubuntu系统如何安装nodejs及npm
  3. Java的反射API
  4. 知道一个数组某个index对应的值 不知道下标的情况下删除该值
  5. Netty工作笔记0049---阶段内容梳理
  6. java split 实现_PL/SQL实现JAVA中的split()方法的例子
  7. oracle 64位数据源,64位Windows 7下32位 Oracle(含XE)ODBC 数据源的配置
  8. RBF神经网络-高斯核函数
  9. 家用千兆路由器排行榜前十名_路由器哪个牌子好?千兆路由器2019排行
  10. 文档服务器 件排名,服务器十大品牌排名
  11. java http 401_java HttpClient模拟登陆一直401
  12. 非容器化jenkins 连接k8s 集群
  13. 显卡的指标有哪些方面_显卡的几个主要的性能指标有()
  14. 深圳物流 inurl php id=,免费快递在线下单接口对接文档-(PHP)
  15. 网络 - 电脑代理 - 学习/实践
  16. python提取excel前十行生成图_Python读取Excel数据生成图表 v2.0
  17. 南卡NANK Runner CC3 耳机评测:骨传导耳机入门级别最强款
  18. 01-使用Camera Raw滤镜调色
  19. Leetcode学习之贪心算法
  20. 2020 2019年 计算机 408 考研 真题 详细 解答

热门文章

  1. python复数类型转换_Python程序设计——复数运算(包括极坐标转换)
  2. MySQL- cluster集群----实现高可用
  3. ftp 发生意外错误 0x8ffe2740
  4. 0x00007FFE9071C408 (ucrtbase.dll) (xxx.exe 中)处有未经处理的异常: 将一个无效参数传递给了将无效参数视为严重错误的函数。
  5. 学计算机20天培训心得体会学生,信息技术2.0培训心得总结3篇
  6. bde oracle 商友的流程_BDE动态连接Oracle数据库
  7. RT-thread应用讲解——FLASH分区
  8. JRebel激活与配置
  9. 沃达丰高管:澳大利亚将于2020年迎来5G
  10. 计算机网络的三种交换方式