<h1 style="text-wrap: wrap; font-size: 32px; border-bottom: 2px solid rgb(204, 204, 204); padding: 0px 4px 0px 0px; margin: 0px 0px 10px;">目标查找与跟踪 - Meanshift与CamShift</h1><p style="text-wrap: wrap;"><span style=" color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif;">当前系列所有demo下载地址:</span></p><p style="margin-top: 0px; margin-bottom: 10px; text-wrap: wrap; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; color: rgb(51, 51, 51); box-sizing: border-box;"><a href="https://github.com/GaoRenBao/OpenCv4-Demo" target="_blank" style="margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(0, 102, 0); transition-duration: 0.2s; transition-property: opacity; outline: none; opacity: 0.8;">https://github.com/GaoRenBao/OpenCv4-Demo</a></p><p style="margin-top: 0px; margin-bottom: 10px; text-wrap: wrap; box-sizing: border-box; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; color: rgb(51, 51, 51); "><a target="_blank" href="https://pan.baidu.com/s/1T9xl4KOBvU1g7AyDfPWlpw" style="box-sizing: border-box; color: rgb(0, 102, 0); transition-duration: 0.2s; transition-property: opacity; background-color: transparent; outline: none;"></a></p><p style="text-wrap: wrap;"><br/></p><p style="margin-top: 0px; margin-bottom: 10px; padding: 0px; list-style: none; border: 0px; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; text-wrap: wrap; box-sizing: border-box; color: rgb(51, 51, 51);">源码参考原文:</p><p style="margin-top: 0px; margin-bottom: 10px; padding: 0px; list-style: none; border: 0px; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; text-wrap: wrap; box-sizing: border-box; color: rgb(51, 51, 51);"><a href="/api/system/download?file=OpenCV-Python-Tutorial-%E4%B8%AD%E6%96%87%E7%89%88.pdf" target="_blank" title="OpenCV-Python-Tutorial-中文版.pdf (P222)">OpenCV-Python-Tutorial-中文版.pdf (P222)</a><span style="margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgba(0, 0, 0, 0.85);"> </span></p><p style="margin-top: 0px; margin-bottom: 10px; padding: 0px; list-style: none; border: 0px; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; text-wrap: wrap; box-sizing: border-box; color: rgb(51, 51, 51);"><a href="https://docs.opencv.org/3.2.0/db/df8/tutorial_py_meanshift.html" target="_blank">https://docs.opencv.org/3.2.0/db/df8/tutorial_py_meanshift.html</a></p><p style="margin-top: 0px; margin-bottom: 10px; text-wrap: wrap; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; box-sizing: border-box; color: rgb(51, 51, 51);">不同编程语言对应的OpenCv版本以及<span style="">开发环境</span>信息如下:<span style="font-family: Calibri; font-size: 14px;"> </span></p><table border="1" style="border-right: none; border-bottom: none; border-image: initial; border-left: 1px solid rgb(102, 102, 102); border-top: 1px solid rgb(102, 102, 102);"><tbody><tr class="firstRow"><td width="81" valign="top" style="border-color: windowtext rgb(102, 102, 102) rgb(102, 102, 102) windowtext; border-bottom-width: 1px; border-bottom-style: solid; border-right-width: 1px; border-right-style: solid; padding: 5px;"><p style="text-align: center;"><strong><span style="font-family: 宋体; font-size: 14px;">语言</span></strong></p></td><td width="223" valign="top" style="border-color: windowtext rgb(102, 102, 102) rgb(102, 102, 102) windowtext; border-bottom-width: 1px; border-bottom-style: solid; border-right-width: 1px; border-right-style: solid; padding: 5px;"><p style="text-align: center;"><strong><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">OpenCv</span>版本</span></strong></p></td><td width="242" valign="top" style="border-color: windowtext rgb(102, 102, 102) rgb(102, 102, 102) windowtext; border-bottom-width: 1px; border-bottom-style: solid; border-right-width: 1px; border-right-style: solid; padding: 5px;"><p style="text-align: center;"><strong><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">IDE</span></span></strong></p></td></tr><tr><td width="81" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">C#</span></span></p></td><td width="223" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: Calibri; letter-spacing: 0px;">OpenCvSharp4.4.8.0.20230708</span></p></td><td width="242" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">Visual Studio 2022</span></span></p></td></tr><tr><td width="81" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">C++</span></span></p></td><td width="223" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; letter-spacing: 0px;"><span style="font-family: Calibri;">O</span></span><span style="font-family: Calibri; letter-spacing: 0px;">pen</span><span style="font-family: 宋体; letter-spacing: 0px;"><span style="font-family: Calibri;">C</span></span><span style="font-family: Calibri; letter-spacing: 0px;">v-4.5.5-vc14_vc15</span></p></td><td width="242" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">Visual Studio 2022</span></span></p></td></tr><tr><td width="81" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">Python</span></span></p></td><td width="223" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">OpenCv-Python (4.6.0.66)</span></span></p></td><td width="242" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">PyCharm Community Edition 2022.1.3</span></span></p></td></tr></tbody></table><p><br/></p><p> 要在OpenCV中使用Meanshift算法首先我们要对目标对象进行设置,计算目标对象的直方图,这样在执行meanshift算法时我们就可以将目标对象反向投影到每一帧中去 了。另外我们还需要提供窗口的起始位置。在这里我们值计算H ( Hue )通道的直方图,同样为了避免低亮度造成的影响,我们使用函数cv2.inRange()将低亮度的值忽略掉。</p><p><br/></p><p>问题:</p><p> 我们的窗口的大小是固定的。而汽车由远及近,在视觉上是一个渐变大的过程,固定的窗口是不合适的。所以我们需要根据目标的大小和角度来对窗口的大小和角度进行修订</p><p><br/></p><p><span style="font-size: 16px; font-style: italic; font-weight: bold; color: rgb(51, 153, 204); line-height: 18px;">Camshift的优点与缺点</span></p><p> 原文:<a href="https://blog.csdn.net/guozhihao12345/article/details/50310253" target="_blank">https://blog.csdn.net/guozhihao12345/article/details/50310253</a></p><p> 之前做的一个项目:在手机上实现波波球、人脸的跟踪。采用的是opencv的Camshift跟踪算法,其结果是对人脸的跟踪效果较好,对小球的跟踪效果较差。通过分析之后发现根本原因是小球移动过快,而人脸的移动较慢,所以Camshift跟踪的上人脸但跟综不上小球,任何一个跟踪算法都有其跟踪速度的极限。还有另外的一个原因是:人脸在视频中占有的区域较大 ,所以背景较简单(相对于人脸),相对于小球在视频中占用的区域较小,所以背景叫复杂(相对于小球而言),为了更好地解释这一原因这面就描述一下Camshift算法的优缺点。</p><p> Camshift的优点:简单,计算量较少,因为Camshift的本质就局部检测,在局部里检测“密度”最大的位置。</p><p> Camshift的缺点:Camshift的优点有时候也正是其缺点,因为其简单,所以对于复杂背景或者纹理丰富的物体跟踪效果较差。因为Camshift是对直方图反投影所形成的二值图像进行处理的,如果背景较为复杂或者物体的纹理较为丰富,那么此二值图像的噪声就很多(具体原因可参考直方图反投影的原理),这将直接干扰Camshift对物体位置的判断。</p><p> 所以对Camshift的总结为:Camshift适用于物体表面颜色较为单一,且和背景颜色差距较大</p><p> 为了实现手机下的小球跟踪,现进行了如下改进:对直方图反投影图像进行滤波处理,采用的是中值滤波,但滤波处理的计算量较大,这将影响跟踪的速度,所以这里将图像的分辨率减小至原来的三分之一,使整个工程的计算量跟之前的差不多,滤波之后就没有了噪声的影响,但是小球移动过快的情况下依然跟踪失败,这里就将Camshift的局部检测改为全局下的检测,因为没有了噪声(实际效果的确如此),所以这样做是可行的,从而实现了对小球的跟踪</p><p><br/></p><p><span style="text-wrap: wrap; ">测试视频</span><span style="color: rgba(0, 0, 0, 0.85); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; text-wrap: wrap; ">:</span><a href="/api/system/download?file=Meanshift_CamShift.mp4" target="_blank" title="Meanshift_CamShift.mp4">Meanshift_CamShift.mp4</a></p><p><br/></p><p>C#的效果会比python的效果好很多,下面这个是C#的效果展示:</p><p><img src="/upload/image/6383463116889145628893413.gif" title="Camshift (1).gif" alt="Camshift (1).gif"/></p><p><br/></p><p style="text-wrap: wrap;"><strong style="margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "><span style="font-style: italic; color: rgb(51, 153, 204); line-height: 18px;">C#版本代码如下:</span></strong></p><p>参考博客:<a href="https://blog.csdn.net/jimtien/article/details/118574551" target="_blank">https://blog.csdn.net/jimtien/article/details/118574551</a></p><pre class="brush:c#;toolbar:false">using OpenCvSharp;
using System;
namespace demo
{
internal class Program
{
static void Main(string[] args)
{
var capture = new VideoCapture("../../../images/Meanshift_CamShift.mp4");
if (!capture.IsOpened())
{
Console.WriteLine("Open video failed!");
return;
}
// take first frame of the video
Mat frame = new Mat();
capture.Read(frame);
//setup initial location of window
Rect track_window = new Rect(65, 275, 105, 105); // simply hardcoded the values
//set up the ROI for tracking
Mat roi = frame[track_window];
//Cv2.ImShow("frame", frame);
//Cv2.ImShow("roi", roi);
//Cv2.WaitKey(0);
Mat hsv_roi = new Mat();
Cv2.CvtColor(roi, hsv_roi, ColorConversionCodes.BGR2HSV);
//将低亮度的值忽略掉
Mat mask = new Mat();
Cv2.InRange(hsv_roi, new Scalar(0, 100, 0), new Scalar(100, 255, 255), mask);
Mat roi_hist = new Mat();
Cv2.CalcHist(new Mat[] { hsv_roi }, new int[] { 0 }, mask,
roi_hist, 1, new int[] { 180 }, new float[][] { new float[] { 0, 180 } });
//归一化
Cv2.Normalize(roi_hist, roi_hist, 0, 255, NormTypes.MinMax);
//Setup the termination criteria, either 10 iteration or move by atleast 1 pt
TermCriteria term_crit = new TermCriteria(CriteriaTypes.Eps | CriteriaTypes.Count, 10, 1);
Mat hsv = new Mat();
Mat dst = new Mat();
Mat img1 = new Mat();
Mat img2 = new Mat();
Rect track_window1 = track_window;
Rect track_window2 = track_window;
while (true)
{
capture.Read(frame);
if (frame.Empty())
break;
frame.CopyTo(img1);
frame.CopyTo(img2);
Cv2.CvtColor(frame, hsv, ColorConversionCodes.BGR2HSV);
Cv2.CalcBackProject(new Mat[] { hsv }, new int[] { 0 },
roi_hist, dst, new Rangef[] { new Rangef(0, 180) }, true);
// meanShift 效果
Cv2.MeanShift(dst, ref track_window1, term_crit);
Cv2.PutText(img1, "MeanShift", new Point(10, 30), HersheyFonts.HersheyComplex, 1, new Scalar(0, 0, 255), 2);
Cv2.Rectangle(img1, track_window1, 255, 2);
Cv2.ImShow("img1", img1);
// CamShift 效果
RotatedRect ret = Cv2.CamShift(dst, ref track_window2, term_crit);
Point2f[] line = ret.Points();
Cv2.PutText(img2, "CamShift", new Point(10, 30), HersheyFonts.HersheyComplex, 1, new Scalar(0, 0, 255), 2);
Cv2.Line(img2, (Point)line[0], (Point)line[1], new Scalar(0, 0, 255), 2, LineTypes.Link8);
Cv2.Line(img2, (Point)line[1], (Point)line[2], new Scalar(0, 0, 255), 2, LineTypes.Link8);
Cv2.Line(img2, (Point)line[2], (Point)line[3], new Scalar(0, 0, 255), 2, LineTypes.Link8);
Cv2.Line(img2, (Point)line[3], (Point)line[0], new Scalar(0, 0, 255), 2, LineTypes.Link8);
Cv2.ImShow("img2", img2);
// 按ESC退出
if (Cv2.WaitKey(30) == 27)
break;
}
}
}
}</pre><p style="text-wrap: wrap;"><strong style="margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "></strong><br/></p><p style="text-wrap: wrap;"><strong style="margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "><strong style="margin: 0px; padding: 0px; list-style: none; border: 0px;"><span style="font-style: italic; color: rgb(51, 153, 204); line-height: 18px;">C++版本代码如下:</span></strong></strong></p><p>C++效果不是很好,感觉是calcBackProject方法用错了,暂时没搞定,不过可以参考这个博客:<a href="https://blog.csdn.net/akadiao/article/details/78991095" target="_blank">https://blog.csdn.net/akadiao/article/details/78991095</a></p><pre class="brush:cpp;toolbar:false">#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <video/tracking.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
//保存目标轨迹
std::vector<Point> pt;
VideoCapture capture("../images/Meanshift_CamShift.mp4");
if (!capture.isOpened())
{
cout << "Open video failed!" << endl;
return 0;
}
// take first frame of the video
Mat frame;
capture.read(frame);
//setup initial location of window
Rect track_window(65, 275, 105, 105); // simply hardcoded the values
//set up the ROI for tracking
Mat roi = frame(track_window);
//imshow("frame", frame);
//imshow("roi", roi);
//waitKey(0);
Mat hsv_roi;
cvtColor(roi, hsv_roi, COLOR_BGR2HSV);
// 将低亮度的值忽略掉
Mat mask;
inRange(hsv_roi, Scalar(0, 100, 0), Scalar(100, 255, 255), mask);
MatND roi_hist;
int channels[] = { 0 };
int histSize[] = { 180 };
float range[] = { 0, 180 };
const float* ranges[] = { range };
// 归一化
calcHist(&hsv_roi, 1, channels, Mat(), roi_hist, 1, histSize, ranges);
normalize(roi_hist, roi_hist, 0, 255, NORM_MINMAX);
//Setup the termination criteria, either 10 iteration or move by atleast 1 pt
TermCriteria term_crit(TermCriteria::EPS | TermCriteria::COUNT, 10, 1);
Mat hsv;
Mat dst;
Mat img1;
Mat img2;
Rect track_window1 = track_window;
Rect track_window2 = track_window;
while (true)
{
capture.read(frame);
if (frame.empty())
break;
frame.copyTo(img1);
frame.copyTo(img2);
cvtColor(frame, hsv, COLOR_BGR2HSV);
float hrange[] = { 0, 180 };
const float* hranges = hrange;
std::vector<cv::Mat> hue;
cv::split(hsv, hue);
calcBackProject(&hue[1], 1, 0, roi_hist, dst, &hranges);
//calcBackProject(vector<Mat>{hue[1]}, vector<int>{0}, roi_hist, dst, vector<float>{ 0, 180 }, 1);
// meanShift 效果
meanShift(dst, track_window1, term_crit);
putText(img1, "MeanShift", Point(10, 30), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
rectangle(img1, track_window1, 255, 2);
imshow("img1", img1);
// CamShift 效果
cv::Point2f lines[4];
RotatedRect ret = CamShift(dst, track_window2, term_crit);
ret.points(lines);
putText(img2, "CamShift", Point(10, 30), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2);
line(img2, lines[0], lines[1], Scalar(0, 0, 255), 2, LINE_8);
line(img2, lines[1], lines[2], Scalar(0, 0, 255), 2, LINE_8);
line(img2, lines[2], lines[3], Scalar(0, 0, 255), 2, LINE_8);
line(img2, lines[3], lines[0], Scalar(0, 0, 255), 2, LINE_8);
// 绘制路径,原文链接:https://blog.csdn.net/akadiao/article/details/78991095
Rect rect;
rect.x = ret.center.x - ret.size.width / 2.0;
rect.y = ret.center.y - ret.size.height / 2.0;
rect.width = ret.size.width;
rect.height = ret.size.height;
pt.push_back(Point(rect.x + rect.width / 2, rect.y + rect.height / 2));
for (int i = 0; i < pt.size() - 1; i++)
{
line(img2, pt[i], pt[i + 1], Scalar(0, 255, 0), 2);
}
imshow("img2", img2);
// 按ESC退出
if (waitKey(30) == 27)
break;
}
return 0;
}</pre><p style="text-wrap: wrap;"><strong style="margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "><strong style="margin: 0px; padding: 0px; list-style: none; border: 0px;"></strong></strong><br/></p><p style="text-wrap: wrap;"><strong style="margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "><strong style="margin: 0px; padding: 0px; list-style: none; border: 0px;"><span style="font-style: italic; color: rgb(51, 153, 204); line-height: 18px;">Python版本代码如下:</span></strong></strong></p><pre class="brush:python;toolbar:false">import numpy as np
import cv2
cap = cv2.VideoCapture('../images/Meanshift_CamShift.mp4')
# take first frame of the video
ret, frame = cap.read()
# setup initial location of window
c, r, w, h = 65, 275, 105, 105
track_window = (c, r, w, h)
# set up the ROI for tracking
roi = frame[r:r + h, c:c + w]
# cv2.imshow('frame', frame)
# cv2.imshow('roi', roi)
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
# 将低亮度的值忽略掉
mask = cv2.inRange(hsv_roi, np.array((0, 100, 0)), np.array((100, 255, 255)))
# 这个方法貌似也行
# roi2 = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
# ret, mask = cv2.threshold(roi2, 100, 255, cv2.THRESH_BINARY)
# mask = cv2.bitwise_not(mask)
# cv2.imshow('hsv_roi', hsv_roi)
# cv2.imshow('mask', mask)
# cv2.waitKey(0)
roi_hist = cv2.calcHist([hsv_roi], [0], mask, [180], [0, 180])
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
# Setup the termination criteria, either 10 iteration or move by atleast 1 pt
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
track_window1 = track_window
track_window2 = track_window
while True:
ret, frame = cap.read()
if ret is True:
img1 = np.copy(frame)
img2 = np.copy(frame)
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
dst = cv2.calcBackProject([hsv], [0], roi_hist, [0, 180], 1)
# meanShift 效果
ret, track_window = cv2.meanShift(dst, track_window1, term_crit)
x, y, w, h = track_window
img1 = cv2.rectangle(img1, (x, y), (x + w, y + h), (0, 0, 255), 2)
img1 = cv2.putText(img1, "MeanShift", (10, 30), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 2)
cv2.imshow('img1', img1)
# CamShift 效果
ret, track_window = cv2.CamShift(dst, track_window2, term_crit)
pts = cv2.boxPoints(ret)
pts = np.int0(pts)
print('len pts:', len(pts), pts)
img2 = cv2.polylines(img2, [pts], True, (0, 255, 0), 2)
img2 = cv2.putText(img2, "CamShift", (10, 30), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2)
cv2.imshow('img2', img2)
# 按ESC退出
if cv2.waitKey(30) == 27:
break
else:
break
cv2.destroyAllWindows()
cap.release()</pre><p style="text-wrap: wrap;"><br/></p>
目标查找与跟踪 - Meanshift与CamShift 当前系列所有demo下载地址:
https://github.com/GaoRenBao/OpenCv4-Demo
源码参考原文:
OpenCV-Python-Tutorial-中文版.pdf (P222)
https://docs.opencv.org/3.2.0/db/df8/tutorial_py_meanshift.html
不同编程语言对应的OpenCv版本以及开发环境 信息如下:
语言
OpenCv 版本
IDE
C#
OpenCvSharp4.4.8.0.20230708
Visual Studio 2022
C++
O pen C v-4.5.5-vc14_vc15
Visual Studio 2022
Python
OpenCv-Python (4.6.0.66)
PyCharm Community Edition 2022.1.3
要在OpenCV中使用Meanshift算法首先我们要对目标对象进行设置,计算目标对象的直方图,这样在执行meanshift算法时我们就可以将目标对象反向投影到每一帧中去 了。另外我们还需要提供窗口的起始位置。在这里我们值计算H ( Hue )通道的直方图,同样为了避免低亮度造成的影响,我们使用函数cv2.inRange()将低亮度的值忽略掉。
问题:
我们的窗口的大小是固定的。而汽车由远及近,在视觉上是一个渐变大的过程,固定的窗口是不合适的。所以我们需要根据目标的大小和角度来对窗口的大小和角度进行修订
Camshift的优点与缺点
原文:https://blog.csdn.net/guozhihao12345/article/details/50310253
之前做的一个项目:在手机上实现波波球、人脸的跟踪。采用的是opencv的Camshift跟踪算法,其结果是对人脸的跟踪效果较好,对小球的跟踪效果较差。通过分析之后发现根本原因是小球移动过快,而人脸的移动较慢,所以Camshift跟踪的上人脸但跟综不上小球,任何一个跟踪算法都有其跟踪速度的极限。还有另外的一个原因是:人脸在视频中占有的区域较大 ,所以背景较简单(相对于人脸),相对于小球在视频中占用的区域较小,所以背景叫复杂(相对于小球而言),为了更好地解释这一原因这面就描述一下Camshift算法的优缺点。
Camshift的优点:简单,计算量较少,因为Camshift的本质就局部检测,在局部里检测“密度”最大的位置。
Camshift的缺点:Camshift的优点有时候也正是其缺点,因为其简单,所以对于复杂背景或者纹理丰富的物体跟踪效果较差。因为Camshift是对直方图反投影所形成的二值图像进行处理的,如果背景较为复杂或者物体的纹理较为丰富,那么此二值图像的噪声就很多(具体原因可参考直方图反投影的原理),这将直接干扰Camshift对物体位置的判断。
所以对Camshift的总结为:Camshift适用于物体表面颜色较为单一,且和背景颜色差距较大
为了实现手机下的小球跟踪,现进行了如下改进:对直方图反投影图像进行滤波处理,采用的是中值滤波,但滤波处理的计算量较大,这将影响跟踪的速度,所以这里将图像的分辨率减小至原来的三分之一,使整个工程的计算量跟之前的差不多,滤波之后就没有了噪声的影响,但是小球移动过快的情况下依然跟踪失败,这里就将Camshift的局部检测改为全局下的检测,因为没有了噪声(实际效果的确如此),所以这样做是可行的,从而实现了对小球的跟踪
测试视频 : Meanshift_CamShift.mp4
C#的效果会比python的效果好很多,下面这个是C#的效果展示:
C#版本代码如下:
参考博客:https://blog.csdn.net/jimtien/article/details/118574551
using OpenCvSharp;
using System;
namespace demo
{
internal class Program
{
static void Main(string[] args)
{
var capture = new VideoCapture("../../../images/Meanshift_CamShift.mp4");
if (!capture.IsOpened())
{
Console.WriteLine("Open video failed!");
return;
}
// take first frame of the video
Mat frame = new Mat();
capture.Read(frame);
//setup initial location of window
Rect track_window = new Rect(65, 275, 105, 105); // simply hardcoded the values
//set up the ROI for tracking
Mat roi = frame[track_window];
//Cv2.ImShow("frame", frame);
//Cv2.ImShow("roi", roi);
//Cv2.WaitKey(0);
Mat hsv_roi = new Mat();
Cv2.CvtColor(roi, hsv_roi, ColorConversionCodes.BGR2HSV);
//将低亮度的值忽略掉
Mat mask = new Mat();
Cv2.InRange(hsv_roi, new Scalar(0, 100, 0), new Scalar(100, 255, 255), mask);
Mat roi_hist = new Mat();
Cv2.CalcHist(new Mat[] { hsv_roi }, new int[] { 0 }, mask,
roi_hist, 1, new int[] { 180 }, new float[][] { new float[] { 0, 180 } });
//归一化
Cv2.Normalize(roi_hist, roi_hist, 0, 255, NormTypes.MinMax);
//Setup the termination criteria, either 10 iteration or move by atleast 1 pt
TermCriteria term_crit = new TermCriteria(CriteriaTypes.Eps | CriteriaTypes.Count, 10, 1);
Mat hsv = new Mat();
Mat dst = new Mat();
Mat img1 = new Mat();
Mat img2 = new Mat();
Rect track_window1 = track_window;
Rect track_window2 = track_window;
while (true)
{
capture.Read(frame);
if (frame.Empty())
break;
frame.CopyTo(img1);
frame.CopyTo(img2);
Cv2.CvtColor(frame, hsv, ColorConversionCodes.BGR2HSV);
Cv2.CalcBackProject(new Mat[] { hsv }, new int[] { 0 },
roi_hist, dst, new Rangef[] { new Rangef(0, 180) }, true);
// meanShift 效果
Cv2.MeanShift(dst, ref track_window1, term_crit);
Cv2.PutText(img1, "MeanShift", new Point(10, 30), HersheyFonts.HersheyComplex, 1, new Scalar(0, 0, 255), 2);
Cv2.Rectangle(img1, track_window1, 255, 2);
Cv2.ImShow("img1", img1);
// CamShift 效果
RotatedRect ret = Cv2.CamShift(dst, ref track_window2, term_crit);
Point2f[] line = ret.Points();
Cv2.PutText(img2, "CamShift", new Point(10, 30), HersheyFonts.HersheyComplex, 1, new Scalar(0, 0, 255), 2);
Cv2.Line(img2, (Point)line[0], (Point)line[1], new Scalar(0, 0, 255), 2, LineTypes.Link8);
Cv2.Line(img2, (Point)line[1], (Point)line[2], new Scalar(0, 0, 255), 2, LineTypes.Link8);
Cv2.Line(img2, (Point)line[2], (Point)line[3], new Scalar(0, 0, 255), 2, LineTypes.Link8);
Cv2.Line(img2, (Point)line[3], (Point)line[0], new Scalar(0, 0, 255), 2, LineTypes.Link8);
Cv2.ImShow("img2", img2);
// 按ESC退出
if (Cv2.WaitKey(30) == 27)
break;
}
}
}
}
C++版本代码如下:
C++效果不是很好,感觉是calcBackProject方法用错了,暂时没搞定,不过可以参考这个博客:https://blog.csdn.net/akadiao/article/details/78991095
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <video/tracking.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
//保存目标轨迹
std::vector<Point> pt;
VideoCapture capture("../images/Meanshift_CamShift.mp4");
if (!capture.isOpened())
{
cout << "Open video failed!" << endl;
return 0;
}
// take first frame of the video
Mat frame;
capture.read(frame);
//setup initial location of window
Rect track_window(65, 275, 105, 105); // simply hardcoded the values
//set up the ROI for tracking
Mat roi = frame(track_window);
//imshow("frame", frame);
//imshow("roi", roi);
//waitKey(0);
Mat hsv_roi;
cvtColor(roi, hsv_roi, COLOR_BGR2HSV);
// 将低亮度的值忽略掉
Mat mask;
inRange(hsv_roi, Scalar(0, 100, 0), Scalar(100, 255, 255), mask);
MatND roi_hist;
int channels[] = { 0 };
int histSize[] = { 180 };
float range[] = { 0, 180 };
const float* ranges[] = { range };
// 归一化
calcHist(&hsv_roi, 1, channels, Mat(), roi_hist, 1, histSize, ranges);
normalize(roi_hist, roi_hist, 0, 255, NORM_MINMAX);
//Setup the termination criteria, either 10 iteration or move by atleast 1 pt
TermCriteria term_crit(TermCriteria::EPS | TermCriteria::COUNT, 10, 1);
Mat hsv;
Mat dst;
Mat img1;
Mat img2;
Rect track_window1 = track_window;
Rect track_window2 = track_window;
while (true)
{
capture.read(frame);
if (frame.empty())
break;
frame.copyTo(img1);
frame.copyTo(img2);
cvtColor(frame, hsv, COLOR_BGR2HSV);
float hrange[] = { 0, 180 };
const float* hranges = hrange;
std::vector<cv::Mat> hue;
cv::split(hsv, hue);
calcBackProject(&hue[1], 1, 0, roi_hist, dst, &hranges);
//calcBackProject(vector<Mat>{hue[1]}, vector<int>{0}, roi_hist, dst, vector<float>{ 0, 180 }, 1);
// meanShift 效果
meanShift(dst, track_window1, term_crit);
putText(img1, "MeanShift", Point(10, 30), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
rectangle(img1, track_window1, 255, 2);
imshow("img1", img1);
// CamShift 效果
cv::Point2f lines[4];
RotatedRect ret = CamShift(dst, track_window2, term_crit);
ret.points(lines);
putText(img2, "CamShift", Point(10, 30), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2);
line(img2, lines[0], lines[1], Scalar(0, 0, 255), 2, LINE_8);
line(img2, lines[1], lines[2], Scalar(0, 0, 255), 2, LINE_8);
line(img2, lines[2], lines[3], Scalar(0, 0, 255), 2, LINE_8);
line(img2, lines[3], lines[0], Scalar(0, 0, 255), 2, LINE_8);
// 绘制路径,原文链接:https://blog.csdn.net/akadiao/article/details/78991095
Rect rect;
rect.x = ret.center.x - ret.size.width / 2.0;
rect.y = ret.center.y - ret.size.height / 2.0;
rect.width = ret.size.width;
rect.height = ret.size.height;
pt.push_back(Point(rect.x + rect.width / 2, rect.y + rect.height / 2));
for (int i = 0; i < pt.size() - 1; i++)
{
line(img2, pt[i], pt[i + 1], Scalar(0, 255, 0), 2);
}
imshow("img2", img2);
// 按ESC退出
if (waitKey(30) == 27)
break;
}
return 0;
}
Python版本代码如下:
import numpy as np
import cv2
cap = cv2.VideoCapture('../images/Meanshift_CamShift.mp4')
# take first frame of the video
ret, frame = cap.read()
# setup initial location of window
c, r, w, h = 65, 275, 105, 105
track_window = (c, r, w, h)
# set up the ROI for tracking
roi = frame[r:r + h, c:c + w]
# cv2.imshow('frame', frame)
# cv2.imshow('roi', roi)
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
# 将低亮度的值忽略掉
mask = cv2.inRange(hsv_roi, np.array((0, 100, 0)), np.array((100, 255, 255)))
# 这个方法貌似也行
# roi2 = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
# ret, mask = cv2.threshold(roi2, 100, 255, cv2.THRESH_BINARY)
# mask = cv2.bitwise_not(mask)
# cv2.imshow('hsv_roi', hsv_roi)
# cv2.imshow('mask', mask)
# cv2.waitKey(0)
roi_hist = cv2.calcHist([hsv_roi], [0], mask, [180], [0, 180])
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
# Setup the termination criteria, either 10 iteration or move by atleast 1 pt
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
track_window1 = track_window
track_window2 = track_window
while True:
ret, frame = cap.read()
if ret is True:
img1 = np.copy(frame)
img2 = np.copy(frame)
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
dst = cv2.calcBackProject([hsv], [0], roi_hist, [0, 180], 1)
# meanShift 效果
ret, track_window = cv2.meanShift(dst, track_window1, term_crit)
x, y, w, h = track_window
img1 = cv2.rectangle(img1, (x, y), (x + w, y + h), (0, 0, 255), 2)
img1 = cv2.putText(img1, "MeanShift", (10, 30), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 2)
cv2.imshow('img1', img1)
# CamShift 效果
ret, track_window = cv2.CamShift(dst, track_window2, term_crit)
pts = cv2.boxPoints(ret)
pts = np.int0(pts)
print('len pts:', len(pts), pts)
img2 = cv2.polylines(img2, [pts], True, (0, 255, 0), 2)
img2 = cv2.putText(img2, "CamShift", (10, 30), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2)
cv2.imshow('img2', img2)
# 按ESC退出
if cv2.waitKey(30) == 27:
break
else:
break
cv2.destroyAllWindows()
cap.release()