• OpenCV之GOTURN目标追踪


    • 💂 个人主页:风间琉璃
    • 🤟 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主
    • 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)订阅专栏

    目录

    前言

    一、goturn简介

    二、预处理

    三、模型加载

    四、执行推理

    五、解析输出


    前言

    GOTURN(Generic Object Tracking Using Regression Networks)是一种用于目标跟踪的计算机视觉算法,它使用回归神经网络来实现实时目标跟踪。GOTURN的目标是通过检测并跟踪特定物体,使其能够在视频序列中保持物体的连续性,即使物体发生尺寸变化、遮挡或平移。达到了Tracking中效果上的state-of-the-art,尤其在检测速度上达到了100FPS(第一个达到100FPS的深度学习方法)。

    一、goturn简介

    goturn整个算法的框架其实非常简单:输入当前帧和前一帧进入网络,输出当前帧bounding-box的位置。

    以前一帧的目标区域为中心扩展,并crop出来。也就是说:在第t−1帧,Tracker预测的bounding-box位置为c=(cx,xy),宽和高分别为w,h,crop出来的框大小为k1*w,k1*h,k1决定接受多少背景信息。

    对于当前帧,也就是第t帧,基于上一帧的位置,找到待搜寻目标的区域,即search region,网络的目的就是要回归目标在当前search region中的location。这里设置search region的中心坐标为c'=(cx',xy')=c,和前一帧框出来的区域是一样的,search region的大小为k2*w,k2*ℎ,w,ℎ是第t−1帧bounding-box的大小,设置k1=k2=2,对于快速移动的目标,k1,k2就需要增大了。

    网络结构:

    在当前帧和前一帧分别crop出region之后,送入网络进行feature extraction(CaffeNet的卷积层),将这些feature级联并输入fully-connected层,fc层的目的是为了比较object的特征和当前帧的特征,以找到object被移动到了哪里。fc层学习到的是一个复杂的特征比较函数,输出目标的相对运动。

    随后fc层的输出被连接到一个4节点的层(分别代表bounding-box两个角的坐标),以输出object的位置。

    二、预处理

    图像预处理:对目标区域和搜索区域进行了预处理,包括调整大小和减去均值。这里因为是对视频流操作,因此需要对每一帧图像都进行预处理。

    1. //图像预处理:调整尺寸和减去均值
    2. resize(targetPatch, targetPatch, Size(width, height));
    3. resize(searchPatch, searchPatch, Size(width, height));
    4. targetPatch = targetPatch - meanval;
    5. searchPatch = searchPatch - meanval;
    6. //转换为浮点数
    7. targetPatch.convertTo(targetPatch, CV_32F);
    8. searchPatch.convertTo(searchPatch, CV_32F);
    9. Mat targetBlob = blobFromImage(targetPatch);
    10. Mat searchBlob = blobFromImage(searchPatch);

    三、模型加载

    数据预处理之后,就可以加载模型进行预测了。

    1. //加载网络模型
    2. net = readNetFromCaffe(goturn_config, goturn_model);

    四、执行推理

    这里使用了cuda进行加速处理,如果没有cuda,注释即可。

    1. net.setInput(targetBlob, "data1");
    2. net.setInput(searchBlob, "data2");
    3. //使用cuda加速
    4. net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
    5. net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);
    6. Mat res = net.forward("scale");
    7. Mat resMat = res.reshape(1, 1);

    五、解析输出

    网络预测结果存储在res中,我们需要对其进行分析获取我们想要的数据,我们得到的是bounding-box两个对角的坐标。

    1. curBB.x = targetPatchRect.x + (resMat.at<float>(0) * targetPatchRect.width / width) - targetPatchRect.width;
    2. curBB.y = targetPatchRect.y + (resMat.at<float>(1) * targetPatchRect.height / height) - targetPatchRect.height;
    3. curBB.width = (resMat.at<float>(2) - resMat.at<float>(0)) * targetPatchRect.width / width;
    4. curBB.height = (resMat.at<float>(3) - resMat.at<float>(1)) * targetPatchRect.height / height;
    5. if (curBB.width > 300)
    6. curBB.width = curBB.width / 4;
    7. if (curBB.height > 300)
    8. curBB.height = curBB.height / 4;
    9. //Predicted BB
    10. Rect boundingBox = curBB;

    最后的判断是由于不知道为什么在使用cuda进行加速时,到最后预测框会越来越大,帧率越来越低,只能强制限制范围,追踪效果改善了不少。如果觉得多余也可以注释。

    运行结果:在视频开始前需要用鼠标在视频中确定追踪的目标,然后按下空格或者enter进行追踪。

    1694681842016

    源码:下载:OpenCVGoturn目标追踪资源-CSDN文库

    1. // Goturn_Object_Track.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    2. //
    3. #include
    4. #include
    5. #include
    6. #include
    7. using namespace cv;
    8. using namespace cv::dnn;
    9. using namespace std;
    10. String goturn_model = "F:/data/CQU/VS/Goturn_Object_Track/goturn.caffemodel";
    11. String goturn_config = "F:/data/CQU/VS/Goturn_Object_Track/goturn.prototxt";
    12. Net net;
    13. //当前帧和前一帧
    14. Mat frame, prevFrame;
    15. //前一帧的bounding-box的坐标
    16. Rect prevBB;
    17. //追踪物体矩形框
    18. Rect trackObjects(Mat& frame, Mat& prevFrame)
    19. {
    20. Rect rect;
    21. int width = 227; //输入图像宽
    22. int height = 227; //输入图像高
    23. int meanval = 128;
    24. //思路:使用前一帧和前一帧的前一帧的bounding-box的坐标以及当前帧计算预测当前帧的bounding-box坐标
    25. Mat curFrame = frame.clone();
    26. //要预测输出的当前一帧的前一帧的bounding-box的坐标
    27. Rect2d curBB;
    28. float padTargetPatch = 2.0;
    29. Rect2f searchPatchRect, targetPatchRect;
    30. Point2f currCenter, prevCenter;
    31. Mat prevFramePadded, curFramePadded;
    32. Mat searchPatch, targetPatch;
    33. //前一帧bounding-box中心坐标
    34. prevCenter.x = (float)(prevBB.x + prevBB.width / 2);
    35. prevCenter.y = (float)(prevBB.y + prevBB.height / 2);
    36. targetPatchRect.width = (float)(prevBB.width * padTargetPatch);
    37. targetPatchRect.height = (float)(prevBB.height * padTargetPatch);
    38. targetPatchRect.x = (float)(prevCenter.x - prevBB.width * padTargetPatch / 2.0 + targetPatchRect.width);
    39. targetPatchRect.y = (float)(prevCenter.y - prevBB.height * padTargetPatch / 2.0 + targetPatchRect.height);
    40. copyMakeBorder(prevFrame, prevFramePadded, (int)targetPatchRect.height, (int)targetPatchRect.height, (int)targetPatchRect.width, (int)targetPatchRect.width, BORDER_REPLICATE);
    41. targetPatch = prevFramePadded(targetPatchRect).clone();
    42. copyMakeBorder(curFrame, curFramePadded, (int)targetPatchRect.height, (int)targetPatchRect.height, (int)targetPatchRect.width, (int)targetPatchRect.width, BORDER_REPLICATE);
    43. searchPatch = curFramePadded(targetPatchRect).clone();
    44. //图像预处理:调整尺寸和减去均值
    45. resize(targetPatch, targetPatch, Size(width, height));
    46. resize(searchPatch, searchPatch, Size(width, height));
    47. targetPatch = targetPatch - meanval;
    48. searchPatch = searchPatch - meanval;
    49. //转换为浮点数
    50. targetPatch.convertTo(targetPatch, CV_32F);
    51. searchPatch.convertTo(searchPatch, CV_32F);
    52. Mat targetBlob = blobFromImage(targetPatch);
    53. Mat searchBlob = blobFromImage(searchPatch);
    54. net.setInput(targetBlob, "data1");
    55. net.setInput(searchBlob, "data2");
    56. //使用cuda加速
    57. net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
    58. net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);
    59. Mat res = net.forward("scale");
    60. Mat resMat = res.reshape(1, 1);
    61. //printf("width : %d, height : %d\n", (resMat.at(2) - resMat.at(0)), (resMat.at(3) - resMat.at(1)));
    62. curBB.x = targetPatchRect.x + (resMat.at<float>(0) * targetPatchRect.width / width) - targetPatchRect.width;
    63. curBB.y = targetPatchRect.y + (resMat.at<float>(1) * targetPatchRect.height / height) - targetPatchRect.height;
    64. curBB.width = (resMat.at<float>(2) - resMat.at<float>(0)) * targetPatchRect.width / width;
    65. curBB.height = (resMat.at<float>(3) - resMat.at<float>(1)) * targetPatchRect.height / height;
    66. if (curBB.width > 300)
    67. curBB.width = curBB.width / 4;
    68. if (curBB.height > 300)
    69. curBB.height = curBB.height / 4;
    70. //Predicted BB
    71. Rect boundingBox = curBB;
    72. return boundingBox;
    73. }
    74. int main()
    75. {
    76. //打开视频
    77. VideoCapture capture("cap.mp4");
    78. capture.read(frame);
    79. //加载网络模型
    80. net = readNetFromCaffe(goturn_config, goturn_model);
    81. frame.copyTo(prevFrame);
    82. //选取感兴趣区域
    83. prevBB = selectROI(frame, true, true);
    84. namedWindow("frame", CV_WINDOW_AUTOSIZE);
    85. //读取视频每一帧,并预测
    86. while(capture.read(frame))
    87. {
    88. //获得当前系统的计时间周期数,求FPS
    89. double t = (double)getTickCount();
    90. //预测
    91. Rect currentBB = trackObjects(frame, prevFrame);
    92. rectangle(frame, currentBB, Scalar(0, 255, 0), 2, 8, 0);
    93. // 准备下一帧
    94. frame.copyTo(prevFrame);
    95. prevBB.x = currentBB.x;
    96. prevBB.y = currentBB.y;
    97. prevBB.width = currentBB.width;
    98. prevBB.height = currentBB.height;
    99. //FPS计算
    100. t = ((double)getTickCount() - t) / getTickFrequency();//求输入帧后经过的周期数/每秒系统计的周期数=一帧用时多少秒
    101. int fps = 1.0 / t;//求倒数得到每秒经过多少帧,即帧率
    102. string text = "FPS:" + to_string(fps);
    103. cv::putText(frame, text, Point(10, 50), FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 0), 2, 8, 0);
    104. imshow("frame", frame);
    105. char c = waitKey(5);
    106. if(c == 27)
    107. {
    108. break;
    109. }
    110. }
    111. return 0;
    112. }

    结束语
    感谢你观看我的文章呐~本次航班到这里就结束啦 🛬

    希望本篇文章有对你带来帮助 🎉,有学习到一点知识~

    躲起来的星星🍥也在努力发光,你也要努力加油(让我们一起努力叭)。

    最后,博主要一下你们的三连呀(点赞、评论、收藏),不要钱的还是可以搞一搞的嘛~

    不知道评论啥的,即使扣个666也是对博主的鼓舞吖 💞 感谢 💐

  • 相关阅读:
    理论第七课——sort
    地址解析协议ARP
    乔布斯时代的“老人”,一个个都离开苹果了
    6.1、Flink数据写入到文件
    基于AI智能分析网关的智慧视频监控系统一站式解决方案
    开发实战经验分享:互联网医院系统源码与在线问诊APP搭建
    【Paddle】稀疏计算的使用指南 & 稀疏ResNet的学习心得 (2) + Paddle3D应用实例稀疏 ResNet代码解读 (1.6w字超详细)
    kotlin修饰符const的含义
    用户运营,如何多方面拉新?
    RabbitMQ
  • 原文地址:https://blog.csdn.net/qq_53144843/article/details/132881730