计算机视觉
图像处理

利用TinyXML读取VOC2012数据集的XML标注文件裁剪出所有人体目标保存为文件

PASCAL VOC目标检测数据集(The PASCAL Visual Object Classes)

http://pascallin.ecs.soton.ac.uk/challenges/VOC/

图片中的目标用XML文件标注,格式为:

VOC20122007_000346.jpg
		The VOC2007 Database
		PASCAL VOC2007
		flickr
	
	
		500
		375
		3
	
	1
			bottle		Unspecified		0		0					124			107			230			343			 		person		Unspecified		0		0					137			78			497			375			 		person		Unspecified		1		0					89			202			129			247			 		person		Frontal		1		0					72			209			111			259			

对应的图片为:


所以如果想用这个数据集做某种目标识别的训练集的话,需要先从中裁出需要的目标。

下面这个程序就是这个目的,其中用到了TinyXML这个简单易用的XML解析器

#include 
#include 
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/ml/ml.hpp>

#include 

using namespace std;
using namespace cv;

int CropImageCount=0;//裁剪出来的人体图片个数

/**
* 通过根节点和节点名查找所有指定节点,结果放到节点数组NodeVector中
* @param pRootEle xml文件的根节点
* @param strNodeName 要查询的节点名
* @param NodeVector 查询到的节点指针数组
* @return 找到至少一个相应节点,返回true;否则false
*/
bool GetAllNodePointerByName(TiXmlElement* pRootEle, string strNodeName, vector<TiXmlElement*> &NodeVector)
{
	//如果NodeName等于根节点名,加入NodeVector数组
	if(strNodeName == pRootEle->Value())
	{
		NodeVector.push_back(pRootEle);//添加到数组末尾
		//这里根据VOC Annotation的XML文件格式,认为相同节点名的节点不会有父子关系,所以所有相同节点名的节点都在同一级别上
		//只要找到第一个,剩下的肯定在它的兄弟节点里面
		for(TiXmlElement * pElement = pRootEle->NextSiblingElement(); pElement; pElement = pElement->NextSiblingElement())
			if(strNodeName == pElement->Value())
				NodeVector.push_back(pElement);
		return true;
	}
	TiXmlElement * pEle = pRootEle;
	for(pEle = pRootEle->FirstChildElement(); pEle; pEle = pEle->NextSiblingElement())
	{
		//递归处理子节点,获取节点指针
		if(GetAllNodePointerByName(pEle,strNodeName,NodeVector))
			return true;
	}
	return false;//没找到
}

/**
* 根据目标名过滤目标节点数组,删除所有目标名不是objectName的元素
* @param NodeVector 要操作的TiXmlElement元素指针数组
* @param objectName 指定的目标名,删除所有目标名不是objectName的元素
* @return 过滤后目标数组为空,返回false;否则返回true
*/
bool FiltObject(vector<TiXmlElement*> &NodeVector, string objectName)
{
	TiXmlElement * pEle = NULL;
	vector::iterator iter = NodeVector.begin();//数组的迭代器
	for(; iter != NodeVector.end();)
	{
		pEle = * iter;//第i个元素
		//若目标名不是objectName,删除此节点
		if( objectName != pEle->FirstChildElement()->GetText() )
		{
			//cout<<"删除的目标节点:"<FirstChildElement()->GetText() <<endl;
			iter = NodeVector.erase(iter);//删除目标名不是objectName的,返回下一个元素的指针
		}
		else
			iter++;
	}
	if( 0 == NodeVector.size())//过滤后目标数组为空,说明不包含指定目标
		return false;
	else 
		return true;
}

/**
* 根据每个目标的BoundingBox,剪裁图像,保存为文件
* @param img 图像
* @param NodeVector 目标节点数组
*/
void CropImage(Mat img, vector<TiXmlElement*> NodeVector)
{
	int xmin,ymin,xmax,ymax;//从目标节点中读出的包围盒参数
	char fileName[256];//剪裁后的图片和其水平翻转图片的文件名

	//遍历目标数组
	vector::iterator iter = NodeVector.begin();//数组的迭代器
	for(; iter != NodeVector.end(); iter++)
	{
		//遍历每个目标的子节点
		TiXmlElement *pEle = (*iter)->FirstChildElement();//第i个元素的第一个孩子
		for(; pEle; pEle = pEle->NextSiblingElement())
		{
			//找到包围盒"bndbox"节点
			if(string("bndbox") == pEle->Value())
			{
				TiXmlElement * pCoord= pEle->FirstChildElement();//包围盒的第一个坐标值
				//依次遍历包围盒的4个坐标值,放入整型变量中
				for(; pCoord; pCoord = pCoord->NextSiblingElement())
				{
					if(string("xmin") == pCoord->Value())
						xmin = atoi(pCoord->GetText());//xmin
					if(string("ymin") == pCoord->Value())
						ymin = atoi(pCoord->GetText());//ymin
					if(string("xmax") == pCoord->Value())
						xmax = atoi(pCoord->GetText());//xmax
					if(string("ymax") == pCoord->Value())
						ymax = atoi(pCoord->GetText());//ymax
				}
				//cout<<"xmin:"<<xmin<<","<<"ymin:"<<ymin<<","<<"xmax:"<<xmax<<","<<"ymax:"<<ymax<<endl;; //根据读取的包围盒坐标设置图像ROI Mat imgROI = img(Rect(xmin,ymin,xmax-xmin,ymax-ymin)); resize(imgROI,imgROI,Size(64,128));//缩放为64*128大小 sprintf(fileName,"person%06d.jpg",++CropImageCount);//生成剪裁图片的文件名 imwrite(fileName,imgROI);//保存文件 flip(imgROI,imgROI,1);//水平翻转 memset(fileName,0x00,sizeof(fileName)); sprintf(fileName,"person%06d.jpg",++CropImageCount);//生成剪裁图片的水平翻转图片的文件名 imwrite(fileName,imgROI);//保存文件 } } } } /** * 根据XML文件,从图像中剪裁出objectName目标 * @param XMLFile XML文件名 * @param img 对应的图像 * @param objectName 目标名 * @return 若图像中包含objectName目标,返回true;否则返回false */ bool CropImageAccordingToXML(string XMLFile, Mat img, string objectName) { TiXmlDocument * pDoc = new TiXmlDocument();//创建XML文档 pDoc->LoadFile(XMLFile.c_str());//装载XML文件
	vector<TiXmlElement*> nodeVector;//节点数组

	//查找所有节点名是object的节点,即目标节点,结果放到节点数组nodeVector中
	if( false == GetAllNodePointerByName(pDoc->RootElement(), "object", nodeVector) )//未找到指定目标
		return false;
	//cout<<"所有目标个数:"<<nodeVector.size()<<endl;

	//过滤节点数组,删除所有节点名不是objectName的节点
	if( false == FiltObject(nodeVector,objectName) )//目标数组中没有指定目标
		return false;
	//cout<<"过滤后的目标个数:"<<nodeVector.size()<<endl;

	//根据每个目标的BoundingBox,剪裁图像,保存为文件
	CropImage(img,nodeVector);
}


int main()
{
	int fileCount=0;//文件个数
	Mat src;
	string XMLName,ImgName;//XML文件名和对应的图片文件名
	//ifstream fin("VOC2012AnnotationsXMLList.txt");//打开XML文件列表
	ifstream fin("subset.txt");
	//ifstream fin("test.txt");

	//读取XML文件列表
	while(getline(fin,XMLName))
	{
		cout<<"处理:"<<XMLName<<endl;
		ImgName = "D:\\DataSet\\VOCtrainval_11-May-2012\\VOCdevkit\\VOC2012\\JPEGImages\\" + XMLName + ".jpg";
		XMLName = "D:\\DataSet\\VOCtrainval_11-May-2012\\VOCdevkit\\VOC2012\\Annotations\\" + XMLName + ".xml";
		src = imread(ImgName);
		CropImageAccordingToXML(XMLName,src,"person");//根据XML标注文档,从图像src中剪裁出所有person目标,保存为文件
	}

	system("pause");
}

源码下载,环境为VS2010 + OpenCV2.4.4 + TinyXML2.6.2:

编译好的TinyXML2.6.2:

!!温馨提示: 此处需要评论本文后才能查看.

转载注明来源:CV视觉网 » 利用TinyXML读取VOC2012数据集的XML标注文件裁剪出所有人体目标保存为文件

分享到:更多 ()
扫描二维码,给作者 打赏
pay_weixinpay_weixin

请选择你看完该文章的感受:

1不错 1超赞 0无聊 0扯淡 0不解 0路过

评论 2

评论前必须登录!

 

  1. #2

    目前正在使用该功能,非常感谢

    wolfworld610个月前 (07-05)
  2. #1

    很好

    laing11个月前 (05-19)