计算机视觉
图像处理

关于训练样本的真值标定

机器学习算法总 是离不开训练样本的,通常情况下,你找到的图片并非仅仅含有正样本的,而应该是同时含有正样本和负样本的图片;例如,你打算利用机器学习的算法来进行人脸 检测,需要找到人脸的正样本(人脸图片)和负样本(非人脸图片),这个时候,正样本和负样本往往不是那么容易找到的(当然,人脸的训练样本目前在网上能找 到很多公开的训练样本库,但如果你要找车辆的训练样本呢?表情的训练样本呢?猫的训练样本?飞机的训练样本?。。。);这个时候,就需要拍摄或者下载很多 包含正样本(如,人脸)的图片;当然,这个图片当中,某些区域是人脸(正样本),其它区域是非人脸(负样本);显然,用画图工具之类的软件把一张一张的图 片中的正样本区域人工扣去下来,作为正样本, 剩下的区域,作为负样本,这是一个可行的办法;你可以这么做,但作为程序员,似乎写个程序,遍历文件夹中所有的图片,依次显示图片,由用户通过鼠标点击几 下得到正样本区域和负样本区域,这样要更高效一些吧。前者是纯人工的办法,后者是半人工的办法;当你扣取了足够的正负样本,训练得到分类器之后,利用机器 (计算机)就能自动的把图片中的正样本区域(人脸)给扣取出来,这就是全自动的办法了;机器学习的目的,就是让机器来代替人工高效的完成重复性的工作嘛; 当然了,在没有得到训练样本之前,你还是得利用纯人工或者半人工的方法来解决训练样本的问题;笔者给出一个半人工的程序,方便朋友们以后在样本制作过程中 使用;

参考代码:

#include "stdafx.h"
#include "windows.h"
#include 
#include 
#include "opencv.hpp"
#include "iostream"
#include "fstream"
using namespace std;
typedef std::vector file_lists;

static int str_compare(const void *arg1, const void *arg2)
{
	return strcmp((*(std::string*)arg1).c_str(), (*(std::string*)arg2).c_str());//比较字符串arg1 and arg2
}

file_lists ScanDirectory(const std::string &path, const std::string &extension)
{
    WIN32_FIND_DATA wfd;//WIN32_FIND_DATA:Contains information about the file that is found by the 
        //FindFirstFile, FindFirstFileEx, or FindNextFile function
     HANDLE hHandle;
     string searchPath, searchFile;
     file_lists vFilenames;
     int nbFiles = 0;
    
     searchPath = path + "/*" + extension;
     hHandle = FindFirstFile(searchPath.c_str(), &wfd);//Searches a directory for a file or subdirectory
              //with a name that matches a specific name
     if (INVALID_HANDLE_VALUE == hHandle)
    {
         fprintf(stderr, "ERROR(%s, %d): Cannot find (*.%s)files in directory %s/n",
              __FILE__, __LINE__, extension.c_str(), path.c_str());
         exit(0);
    }
    do
    {
         //. or ..
          if (wfd.cFileName[0] == '.')
         {
              continue;
          }
          // if exists sub-directory
          if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)//dwFileAttributes:The file attributes of a file
        {             

          //FILE_ATTRIBUTE_DIRECTORY:The handle identifies a directory
             continue;
         }
        else//if file
        {
            searchFile = path + "/" + wfd.cFileName;
            vFilenames.push_back(searchFile);
            nbFiles++;
         }
    }while (FindNextFile(hHandle, &wfd));//Call this member function to continue a file search begun 
          //with a call to CGopherFileFind::FindFile

    FindClose(hHandle);//Closes a file search handle opened by the FindFirstFile, FindFirstFileEx,
       //or FindFirstStreamW function

 // sort the filenames
    qsort((void *)&(vFilenames[0]), (size_t)nbFiles, sizeof(string), str_compare);//Performs a quick sort

    return vFilenames;
}

bool rBtnDown = false;
const int ptsSize = 4;
CvPoint pts[ptsSize];
int ptsCount = 0;

void mouseOn(int e,int x,int y,int flags,void* param)
{
	if (e == CV_EVENT_LBUTTONDOWN)
	{
		pts[ptsCount++] = cvPoint(x,y);
	}
	else if (e == CV_EVENT_RBUTTONDOWN)
	{
		rBtnDown = true;
	}
}

void GetTrainSample()
{
	string folderIn, fileExt, folderOut;

	ifstream fileIn;
	fileIn.open("config.ini", ios::in);
	if (!fileIn)
	{
		cout<<"config.ini open error"<<endl; system("pause"); exit(-1); } char str[512]; memset(str, '\0', 512*sizeof(char)); fileIn>>str;
	folderIn = str;
	memset(str, '\0', 512*sizeof(char));
	fileIn>>str;
	fileExt = str;
	memset(str, '\0', 512*sizeof(char));
	fileIn>>str;
	folderOut = str;

	file_lists files = ScanDirectory(folderIn, fileExt);
	int size = files.size();
	cout<<"文件夹中的图片总数:"<<size<<endl;

	string fileName;
	string path;
	string ptsName;
	int len;

	cvNamedWindow("img", 0);
	cvSetMouseCallback("img", mouseOn);
	for (int i=0; i<size; i++)
	{
		cout<<i+1<<"/"<<size<<endl;

		int idx = files[i].find_last_of('\/');
		fileName.clear();
		fileName = files[i].substr(idx+1, files[i].length()-idx);
		path = folderOut + "/"+ fileName;

		ptsName = path;
		len = ptsName.length();
		if (ptsName[len-4] = '.')
		{
			ptsName[len-1] = 't';
			ptsName[len-2] = 'x';
			ptsName[len-3] = 't';
		}
		else
		{
			ptsName[len-1] = '\0';
			ptsName[len-2] = 't';
			ptsName[len-3] = 'x';
			ptsName[len-4] = 't';
		}
		

		ofstream fileOut;
		fileOut.open(ptsName.c_str(), ios::out);

		IplImage* pImg = cvLoadImage(files[i].c_str());
		if (!pImg)
		{
			cout<<"img load error, fileName: "<<files[i].c_str();
			continue;
		}
		cvSaveImage(path.c_str(), pImg);

		while(!rBtnDown)
		{
			cvShowImage("img", pImg);
			cvWaitKey(1);
			if (ptsCount == ptsSize)
			{
				int minX,minY,maxX,maxY;
				minX = maxX = pts[0].x;
				minY = maxY = pts[0].y;
				for (int j=1; j<ptsSize; j++)
				{
					minX = minX<pts[j].x ? minX:pts[j].x;
					minY = minY<pts[j].y ? minY:pts[j].y; maxX = maxX>pts[j].x ? maxX:pts[j].x;
					maxY = maxY>pts[j].y ? maxY:pts[j].y;
				}
				fileOut<<minX<<" "<<minY<<" "<<maxX<<" "<<maxY<<endl;

				ptsCount = 0;
				cvRectangle(pImg, cvPoint(minX,minY), cvPoint(maxX,maxY), CV_RGB(255,0,0));
				cvShowImage("img", pImg);
				cvWaitKey(1);
			}
		} 
		rBtnDown = false;
		ptsCount = 0;

		cvReleaseImage(&pImg);
		fileOut.close();
	}
}

void Usage()
{
	cout<<"config.ini说明"<<endl;
	cout<<"程序从config.ini文件中依次读取三行信息:\n输入图片文件夹路径 \n输入图片后缀 \n输出路径;\n\nconfig.ini中的内容举例:\nc:\\inputDir \n.bmp \nD:\\result"<<endl;
	cout<<endl;
	cout<<"本程序使用说明:"<<endl;
	cout<<"用户鼠标左键单击4次即可选定图片中的一个矩形区域,选定若干个矩形区域之后,单击鼠标右键即可切换到下一张图片"<<endl;
	cout<<"//////////////////////////////////////////////////////////////////////"<<endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	Usage();
	GetTrainSample();

	system("pause");
	return 0;
}

说明:

(1)以上代码需要opencv,请自行配置相关的lib和dll;

(2)以上代码在编译过程中,请勿选择“Unicode”字符集,如,VS2008,VS2010中,请如下设置,项目–>属性–>常规–>字符集–>未设置;

(3)可执行文件所在路径请创建一个config.ini文件,该文件包含三行:

输入图片路径

图片后缀名

输出路径;

config.ini参考设置:

E:\Images
.bmp
E:\result

(4)config.ini中的输入和输出路径请尽量不要包含中文路径,可能会出错;

转载注明来源:CV视觉网 » 关于训练样本的真值标定

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

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

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

评论 2

评论前必须登录!