我们来看步骤(1)创建图片读取器中完成了哪些工作。
// (1)创建图片读取器,设置正样本vec文件路径,从中读取正样本图片;负样本图片的文件名列表;样本图像的size,宽高像素值
if( !imgReader.create( _posFilename, _negFilename, _cascadeParams.winSize ) )
调用PosReader和NegReader的create函数
bool CvCascadeImageReader::create( conststring _posFilename, const string _negFilename, Size _winSize )
{
return posReader.create(_posFilename) &&negReader.create(_negFilename, _winSize);
}
正样本
先来看一下正样本长什么样,这在前面的原理部分已经讲过。我们会收集大量的人脸图片,然后转成0-255的灰度图片,缩放到同样的尺寸比如24*24。opencv中提供工具可以把这些小图像以vec文件的格式存到一个大文件中,以加快读取的速度并方便训练程序处理。
例如下面是已经用工具制作好的24*24大小的vec文件,里面包含了大量上述人脸小图像。
在来看代码如何处理该文件获得正样本。
PosReader::create函数,打开vec文件,读取样本个数count,图片的像素数目vecSize。
boolCvCascadeImageReader::PosReader::create( const string _filename )
{
if ( file )
fclose( file );
//file 文件指针,打开文件
file = fopen( _filename.c_str(), "rb" );
if( !file )
return false;
short tmp = 0;
//读取vec文件头
//int count -- vec中样本的个数
//int vecSize -- 图片像素数目 w*h ,样本图像保存为short类型
//short tmp[2] -- 两个short,没用到
if( fread( &count, sizeof( count ), 1, file ) != 1 ||
fread( &vecSize, sizeof( vecSize ), 1, file ) != 1 ||
fread( &tmp, sizeof( tmp ), 1, file ) != 1 ||
fread( &tmp, sizeof( tmp ), 1, file ) != 1 )
CV_Error_( CV_StsParseError, ("wrong file format for %s\n",_filename.c_str()) );
// base为实际图片数据开始的位置,字节数
base= sizeof( count ) + sizeof( vecSize ) + 2*sizeof( tmp );
if( feof( file ) )
return false;
last = 0;
//short vec[vecSize] 用于缓存一个样本的图像数据
vec = (short*) cvAlloc( sizeof( *vec ) * vecSize );
CV_Assert( vec );
return true;
}
负样本
负样本只要不包含人脸就可以,因此没必要像正样本那样精挑细选,收集大量不包含人脸的图片即可。
我们需要告诉训练程序使用哪些文件作为负样本,因此会制作一个文件名列表,作为负样本数据输入给程序,示例如下:
neg.list
neg0\535.png
neg0\536.png
neg0\537.png
neg0\538.png
neg0\539.png
neg0\54.png
neg0\540.png
neg0\541.png
neg0\542.png
neg0\543.png
neg0\544.png
neg0\545.png
neg0\546.png
neg0\547.png
neg0\548.png
neg0\549.png
neg0\55.png
neg0\550.png
neg0\551.png
neg0\552.png
neg0\553.png
负样本列表文件每行存放一个文件名,create函数打开该文件,读取所有文件路径到内部变量imgFilenames中。
bool CvCascadeImageReader::NegReader::create( const string _filename, Size _winSize)
{
string dirname, str;
std::ifstream file(_filename.c_str());
if ( !file.is_open() )
return false;
size_t pos = _filename.rfind('\\');
char dlmrt = '\\';
if (pos == string::npos)
{
pos = _filename.rfind('/');
dlmrt = '/';
}
dirname = pos == string::npos ? "" : _filename.substr(0, pos)+ dlmrt;
//负样本列表文件,每行记录一个图片文件路径
//#开头的行为注释,忽略
//读取所有文件名到列表imgFilenames 中
while( !file.eof() )
{
std::getline(file, str);
if (str.empty()) break;
if (str.at(0) == '#' ) continue; /* comment */
imgFilenames.push_back(dirname + str);
}
file.close();
//保存窗口尺寸
winSize = _winSize;
last = round = 0;
return true;
}