struck(结构化SVM用于视觉跟踪)--源代码详解--main.cpp

struck 利用结构化SVM来实现视觉跟踪,在深度学习流行起来之前,struck是视觉跟踪领域效果最好的方法。深度学习流行之后,利用泛化的卷积特征能够得到很好的效果。struck的优点在于,它可以使用任意的特征来实现跟踪,因此它可以利用卷积神经网络提取的特征,然后结合结构化SVM来实现视觉跟踪,这样的效果说不定更好。

struck的源码是C++实现的,作者写的很好,思路清晰,代码结构清晰,而且与论文中的相符,没有那么多小trick,结果比较可靠。

下面从它的主函数开始,分析这份源码是如何实现的:

main.cpp

/* 
 * Struck: Structured Output Tracking with Kernels
 * 
 * Code to accompany the paper:
 *   Struck: Structured Output Tracking with Kernels
 *   Sam Hare, Amir Saffari, Philip H. S. Torr
 *   International Conference on Computer Vision (ICCV), 2011
 * 
 * Copyright (C) 2011 Sam Hare, Oxford Brookes University, Oxford, UK
 * 
 * This file is part of Struck.
 * 
 * Struck is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Struck is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Struck.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
 
#include "Tracker.h"
#include "Config.h"

#include <iostream>
#include <fstream>

#include <opencv/cv.h>
#include <opencv/highgui.h>

using namespace std;
using namespace cv;

static const int kLiveBoxWidth = 80;
static const int kLiveBoxHeight = 80;

void rectangle(Mat& rMat, const FloatRect& rRect, const Scalar& rColour)
{
	IntRect r(rRect);
	rectangle(rMat, Point(r.XMin(), r.YMin()), Point(r.XMax(), r.YMax()), rColour);
}

int main(int argc, char* argv[])
{

//这几句话没啥作用,我给注释掉
#ifndef WIN32
	string programName = argv[0];
	programName = programName.substr(programName.find_first_of('/'));
	cout << "programName: " << programName << endl;
#endif

	// read config file
	string configPath = "../docs/config.txt";
	Config conf(configPath);//作者定义的类Config 读取了所有的配置信息,并且cout输出
	cout << conf << endl;
	
	if (conf.features.size() == 0)
	{
		cout << "error: no features specified in config" << endl;
		return EXIT_FAILURE;
	}

	if (argc > 1)
	{
		conf.sequenceName = argv[1];
	}

	ofstream outFile;//定义一个输出文件流,输出结果
	if (conf.resultsPath != "")
	{
#ifdef WIN32
		string resultsPath = conf.resultsPath + "/" + conf.sequenceName + "_result.txt";
#else
		string resultsPath = conf.resultsPath + "/" + conf.sequenceName + "_" + programName + "Result.txt";
		
#endif

		outFile.open(resultsPath, ios::out);
		if (!outFile)
		{
			cout << "error: could not open results file: " << conf.resultsPath << endl;
			return EXIT_FAILURE;
		}
	}
	
	// if no sequence specified then use the camera
	bool useCamera = (conf.sequenceName == "");//根据在config.txt中是否给出视频名称,判断是否使用摄像头
	
	VideoCapture cap;
	
	int startFrame = -1;
	int endFrame = -1;
	FloatRect initBB;//这是一个模板类,
	string imgFormat;
	float scaleW = 1.f;
	float scaleH = 1.f;
	
	if (useCamera)//使用摄像头
	{
		if (!cap.open(0))
		{
			cout << "error: could not start camera capture" << endl;
			return EXIT_FAILURE;
		}
		startFrame = 0;
		endFrame = INT_MAX;
		Mat tmp;
		cap >> tmp;//读入一帧视频
		scaleW = (float)conf.frameWidth/tmp.cols;//config中宽/读入视频的宽,比率
		scaleH = (float)conf.frameHeight/tmp.rows;

		/*该函数,创造了一个矩形,左上角在(120,80),80*80的矩形*/
		initBB = IntRect(conf.frameWidth/2-kLiveBoxWidth/2, conf.frameHeight/2-kLiveBoxHeight/2, kLiveBoxWidth, kLiveBoxHeight);
		cout << "press 'i' to initialise tracker" << endl;
	}
	else//使用视频
	{
		// parse frames file
		string framesFilePath = conf.sequenceBasePath+"/"+conf.sequenceName+"/"+"frames.txt";
		ifstream framesFile(framesFilePath.c_str(), ios::in);
		if (!framesFile)
		{
			cout << "error: could not open sequence frames file: " << framesFilePath << endl;
			return EXIT_FAILURE;
		}
		string framesLine;
		getline(framesFile, framesLine);
		printf("%s", framesLine.c_str());
		sscanf(framesLine.c_str(), "%d,%d", &startFrame, &endFrame);
		if (framesFile.fail() || startFrame == -1 || endFrame == -1)
		{
			cout << "error: could not parse sequence frames file" << endl;
			return EXIT_FAILURE;
		}
		
		imgFormat = conf.sequenceBasePath+"/"+conf.sequenceName+"/img/%04d.jpg";//qyy changed
		
		// read first frame to get size
		char imgPath[256];
		sprintf(imgPath, imgFormat.c_str(), startFrame);
		Mat tmp = cv::imread(imgPath, 0);
		scaleW = (float)conf.frameWidth/tmp.cols;
		scaleH = (float)conf.frameHeight/tmp.rows;
		
		// read init box from ground truth file
		string gtFilePath = conf.sequenceBasePath+"/"+conf.sequenceName+"/"+"groundtruth_rect.txt";//qyy changed
		ifstream gtFile(gtFilePath.c_str(), ios::in);
		if (!gtFile)
		{
			cout << "error: could not open sequence gt file: " << gtFilePath << endl;
			return EXIT_FAILURE;
		}
		string gtLine;
		getline(gtFile, gtLine);
		float xmin = -1.f;
		float ymin = -1.f;
		float width = -1.f;
		float height = -1.f;
		sscanf(gtLine.c_str(), "%f,%f,%f,%f", &xmin, &ymin, &width, &height);
		if (gtFile.fail() || xmin < 0.f || ymin < 0.f || width < 0.f || height < 0.f)
		{
			cout << "error: could not parse sequence gt file" << endl;
			return EXIT_FAILURE;
		}
		initBB = FloatRect(xmin*scaleW, ymin*scaleH, width*scaleW, height*scaleH);
	}
	
	Tracker tracker(conf);//使用conf类,初始化Tracker类
	if (!conf.quietMode)//quietMode模式下,不显示结果,只运算
	{
		namedWindow("result");
	}
	
	Mat result(conf.frameHeight, conf.frameWidth, CV_8UC3);
	bool paused = false;
	bool doInitialise = false;
	srand(conf.seed);
	for (int frameInd = startFrame; frameInd <= endFrame; ++frameInd)
	{
		cout << "frame num is: " << frameInd << endl;//qyy
		Mat frame;
		if (useCamera)
		{
			Mat frameOrig;
			cap >> frameOrig;
			resize(frameOrig, frame, Size(conf.frameWidth, conf.frameHeight));
			//imshow("result",frame);//qyy
			//waitKey(0);//qyy
			flip(frame, frame, 1);//作者把视频左右对称翻转了,不知道为什么这么做?
			//imshow("result", frame);//qyy
			//waitKey(0);//qyy
			frame.copyTo(result);
			if (doInitialise)
			{
				if (tracker.IsInitialised())
				{
					tracker.Reset();
				}
				else
				{
					tracker.Initialise(frame, initBB);
				}
				doInitialise = false;
			}
			else if (!tracker.IsInitialised())
			{
				rectangle(result, initBB, CV_RGB(255, 255, 255));//没有初始化,就在result上画白色框框
			}
		}
		else
		{			
			char imgPath[256];
			sprintf(imgPath, imgFormat.c_str(), frameInd);
			Mat frameOrig = cv::imread(imgPath, 0);//第二个参数flag指定读取的颜色类型,=0表示读取为灰度图像
			cout << "frameOrig.channels: " << frameOrig.channels() << endl;//qyy
			if (frameOrig.empty())
			{
				cout << "error: could not read frame: " << imgPath << endl;
				return EXIT_FAILURE;
			}
			resize(frameOrig, frame, Size(conf.frameWidth, conf.frameHeight));
			cvtColor(frame, result, CV_GRAY2RGB);//作者读进来的时候是灰度图像,为了显示转换成3通道都是灰度图
		
			if (frameInd == startFrame)//如果是第一帧,初始化
			{
				tracker.Initialise(frame, initBB);
			}
		}
		
		if (tracker.IsInitialised())//如果初始化了,就开始跟踪
		{
			tracker.Track(frame);//跟踪程序,把tracker当做一个类来对待,很清晰明了啊,赞一个;算法都在这里面实现
			
			if (!conf.quietMode && conf.debugMode)
			{
				tracker.Debug();//debug模式下,可以开启很多额外的窗口显示
			}
			
			rectangle(result, tracker.GetBB(), CV_RGB(0, 255, 0));//使用绿色框,画出跟踪的效果
			
			if (outFile)//这里是得到的矩形框,存储到txt文本中
			{
				const FloatRect& bb = tracker.GetBB();
				outFile << bb.XMin() / scaleW << "," << bb.YMin() / scaleH << "," << bb.Width() / scaleW << "," << bb.Height() / scaleH << flush << endl;
				cout << "cout to file: " << bb.XMin() / scaleW << "," << bb.YMin() / scaleH << "," << bb.Width() / scaleW << "," << bb.Height() / scaleH << endl;
			}
		}
		
		if (!conf.quietMode)//如果使用的是摄像头,作者提供了几个按键来选择是否初始化,我用的是OTB数据集,就不管这个了
		{
			imshow("result", result);
			int key = waitKey(paused ? 0 : 1);
			if (key != -1)
			{
				if (key == 27 || key == 113) // esc q
				{
					break;
				}
				else if (key == 112) // p
				{
					paused = !paused;
				}
				else if (key == 105 && useCamera)//i
				{
					doInitialise = true;
					cout << "initialised !" << endl;//qyy
				}
			}
			if (conf.debugMode && frameInd == endFrame)
			{
				cout << "\n\nend of sequence, press any key to exit" << endl;
				//waitKey();
			}
		}
	}
	
	if (outFile.is_open())
	{
		outFile.close();
	}
	
	return EXIT_SUCCESS;
}


所以,后面我主要关注tracker这个类做了什么,我们看到在main.cpp中调用了tracker.Initialize Debug Track这几个成员函数,所以这几个函数是作者算法实现的关键。







猜你喜欢

转载自blog.csdn.net/sloanqin/article/details/54986380