OpenCV 利用Directory类实现文件夹遍历
最近,我在进行新的工作中,需要对多张图片进行测试,一一测试太慢了,于是想到将需要处理的图片放到一个文件夹中,对文件夹中所有图像进行遍历,这样可以省时省力.
参考博客https://blog.csdn.net/watkinsong/article/details/9227439中的内容,我在VS2010中实践了一番:
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
std::string dirPath = "E:\\test"; //文件夹路径
cv::Directory dir; //文件夹类对象
std::vector<std::string> fileNames = dir.GetListFiles(dirPath, "*.bmp", false); //获取文件夹下文件名序列
for(size_t i = 0; i < fileNames.size(); i++)
{
std::string fileName = fileNames[i]; //取出当前文件名
std::string fileFullPath = dirPath + fileName; //获取当前文件完整路径
std::cout << "file name: " << fileName << endl; //打印当前文件名
std::cout << "file paht: " << fileFullPath << endl; //打印当前文件路径
}
system("pause");
return 0;
}
控制台输出如下:
查看一下我测试用的文件夹
结果正确.
虽然得到我想要的结果了,但是觉得有必要大致了解一下用到的类和函数.上述实现主要用到了Directory类的成员函数GetListFiles,
下面简单学习一下,便于以后遇到可以快速使用.
可以看到Directory类存在于opencv中的contrib模块,含有三个成员函数:
(1)static std::vector<std::string> GetListFiles ( const std::string& path, const std::string & exten = "*", bool addPath = true );
(2)static std::vector<std::string> GetListFilesR ( const std::string& path, const std::string & exten = "*", bool addPath = true );
(3)static std::vector<std::string> GetListFolders( const std::string& path, const std::string & exten = "*", bool addPath = true );
可以看到三个成员函数的返回值都是std::vector<std::string>,输入参数都是const std::string& path, const std::string & exten = "*", bool addPath = true.关于返回值和输入参数,需要结合函数实现加以理解.
在D:\opencv\opencv2410\opencv\sources\modules\contrib\src\inputoutput.cpp中可以看到函数实现
首先看一下GetListFiles函数实现
std::vector<std::string> Directory::GetListFiles( const std::string& path, const std::string & exten, bool addPath )
{
std::vector<std::string> list;
list.clear();
std::string path_f = path + "/" + exten;
#ifdef WIN32
#ifdef HAVE_WINRT
WIN32_FIND_DATAW FindFileData;
#else
WIN32_FIND_DATAA FindFileData;
#endif
HANDLE hFind;
#ifdef HAVE_WINRT
wchar_t wpath[MAX_PATH];
size_t copied = mbstowcs(wpath, path_f.c_str(), MAX_PATH);
CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
hFind = FindFirstFileExW(wpath, FindExInfoStandard, &FindFileData, FindExSearchNameMatch, NULL, 0);
#else
hFind = FindFirstFileA((LPCSTR)path_f.c_str(), &FindFileData);
#endif
if (hFind == INVALID_HANDLE_VALUE)
{
return list;
}
else
{
do
{
if (FindFileData.dwFileAttributes == FILE_ATTRIBUTE_NORMAL ||
FindFileData.dwFileAttributes == FILE_ATTRIBUTE_ARCHIVE ||
FindFileData.dwFileAttributes == FILE_ATTRIBUTE_HIDDEN ||
FindFileData.dwFileAttributes == FILE_ATTRIBUTE_SYSTEM ||
FindFileData.dwFileAttributes == FILE_ATTRIBUTE_READONLY)
{
char* fname;
#ifdef HAVE_WINRT
char fname_tmp[MAX_PATH] = {0};
size_t copied = wcstombs(fname_tmp, FindFileData.cFileName, MAX_PATH);
CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
fname = fname_tmp;
#else
fname = FindFileData.cFileName;
#endif
if (addPath)
list.push_back(path + "/" + std::string(fname));
else
list.push_back(std::string(fname));
}
}
#ifdef HAVE_WINRT
while(FindNextFileW(hFind, &FindFileData));
#else
while(FindNextFileA(hFind, &FindFileData));
#endif
FindClose(hFind);
}
#else
(void)addPath;
DIR *dp;
struct dirent *dirp;
if((dp = opendir(path.c_str())) == NULL)
{
return list;
}
while ((dirp = readdir(dp)) != NULL)
{
if (dirp->d_type == DT_REG)
{
if (exten.compare("*") == 0)
list.push_back(static_cast<std::string>(dirp->d_name));
else
if (std::string(dirp->d_name).find(exten) != std::string::npos)
list.push_back(static_cast<std::string>(dirp->d_name));
}
}
closedir(dp);
#endif
return list;
}
虽然我不能完全看懂代码,但是可以大概了解返回值和输入参数代表什么.
std::string path_f = path + "/" + exten;这句话可以说明path是输入待遍历的文件夹路径,extern是待遍历的文件的扩展名
if (addPath)
list.push_back(path + "/" + std::string(fname));
else
list.push_back(std::string(fname));
这句if-else判断是根据addPath的值决定返回的list中存放的是文件名还是文件的完整路径
如果addPath是false,则返回的是文件名
如果addPath是True,则返回的是文件完整路径
这一点也就解释了上述实现中使用false所以得到的只是文件的名,得到完整路径还需要自己连接一下.如果改成true,是否真的可以直接得到路径呢?验证一下吧:
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
std::string dirPath = "E:\\test"; //文件夹路径
cv::Directory dir; //文件夹类对象
std::vector<std::string> fileNames = dir.GetListFiles(dirPath, "*.bmp", true); //获取文件夹下文件名序列
for(size_t i = 0; i < fileNames.size(); i++)
{
std::string fileName = fileNames[i]; //取出当前文件名
//std::string fileFullPath = dirPath + fileName; //获取当前文件完整路径
std::cout << "file name: " << fileName << endl; //打印当前文件名
//std::cout << "file paht: " << fileFullPath << endl; //打印当前文件路径
}
system("pause");
return 0;
}
这个路径有点奇怪,前面是\后面是/,然后自习看函数实现,里面用的都是/,因此我也改成/就可以了,改成
std::string dirPath = "E:/test"; //文件夹路径
在运行一下
这样就可以直接返回正常的路径了.
本来还想把函数GetListFolders和函数GetListFilesR好好研究一下,但是时间有点太晚了,得睡觉了,明天还得上班.简单粘贴一下别人博客中的话:
(1)、GetListFiles:遍历指定文件夹下的所有文件,不包括指定文件夹内的文件夹;
(2)、GetListFolders:遍历指定文件夹下的所有文件夹,不包括指定文件夹下的文件;
(3)、GetListFilesR:遍历指定文件夹下的所有文件,包括指定文件夹内的文件夹。
也就是说GetListFilesR是集合了GetListFiles函数和GetListFolders函数的功能,我看了一眼实现,确实如此.这个实现基本一下就能看懂,先遍历文件夹下所有文件(不包括指定文件夹内的文件夹),然后遍历文件夹下所有文件夹(不包括指定文件夹下的文件),最后遍历上一部得到的文件夹下所有文件,这样返回的就是文件夹下所有的文件(包括指定文件夹内的文件夹).
std::vector<std::string> Directory::GetListFilesR ( const std::string& path, const std::string & exten, bool addPath )
{
std::vector<std::string> list = Directory::GetListFiles(path, exten, addPath);
std::vector<std::string> dirs = Directory::GetListFolders(path, exten, addPath);
std::vector<std::string>::const_iterator it;
for (it = dirs.begin(); it != dirs.end(); ++it)
{
std::vector<std::string> cl = Directory::GetListFiles(*it, exten, addPath);
list.insert(list.end(), cl.begin(), cl.end());
}
return list;
}
}
有时间利用这三个函数完成一下文件夹对比功能的实现,之前在github上下了一个python版的实现,看到这三个函数我觉得也可以用opencv实现一下文件夹比对.
参考博客:
1 https://blog.csdn.net/watkinsong/article/details/9227439
2 https://blog.csdn.net/fengbingchun/article/details/42435901