有时候我们需要批量载入图片以进行后续处理,而这些图片放置在某个文件夹下,现在来看看如何实现。
浏览文件夹的功能可以参考这篇文章(在MFC中实现“浏览文件夹”功能),本文略去这一步。
作为示例,我们编写Console程序输入文件夹路径,设置扩展名,完整代码(有详细注释)如下
#include <io.h> // _finddata_t结构, _access(), _findfile()等
#include <direct.h> //_chdir()目录操作
#include <iostream>
#include <fstream>
#include <sstream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#pragma comment(lib,"opencv_core249.lib")
#pragma comment(lib,"opencv_highgui249.lib")
using namespace cv;
using namespace std;
// 最多支持50张图片,可根据需要修改(但这个数字不能太大)
#define MAX_COUNT 50
//搜索folder目录下扩展名为ext的文件,将文件完整路径写入fileNames
int TraverseFolder(const string& folder, const string& extension, vector<string>& fileNames);
//载入folder目录下扩展名为ext的图片(数目不超过maxCount),存放在images容器中
int LoadImages(const string& folder, const string& extension, vector<Mat>& images, const int maxCount);
int main(void)
{
system("COLOR FD");
string folder, ext = "*.jpg";
cout << "Folder path:";
getline(cin, folder); //从输入读取一行
try
{
vector<Mat> images;
int count=LoadImages(folder, ext, images, MAX_COUNT);
string windowName = "Display";
namedWindow(windowName, WINDOW_NORMAL);
resizeWindow(windowName, 800, 600);
char text[16] = { 0 };
for (int idx = 0; idx < count; ++idx)
{
//Mat的重载'='只是浅拷贝(引用方式)
Mat currentImage = images[idx];
sprintf_s(text, "%02d/%d", idx + 1, count);
// 显示文字:“当前/总数”
putText(currentImage, string(text), cvPoint(10, 50), CV_FONT_BLACK, 1, cvScalar(255));
imshow(windowName, currentImage);
//等待用户按键,持续2000毫秒
//若在这段时间内按下按键则执行后续步骤(循环++i)
int key = waitKey(2000);
//用户按键ESC(ASCII码值27)则跳出循环
if (key == 27) break;
}
destroyAllWindows();
}
catch (exception except)
{
//捕获到异常,打印异常消息
cout << except.what() << endl;
}
cout << "Press any key to continue...";
cin.get(); // 类似于_getch()
return 0;
}
int TraverseFolder(const string& folder, const string& extension, vector<string>& fileNames)
{
string dir = folder;
if (_access(dir.c_str(), 06) == -1)
{
// 抛出异常 throw(exception)
// 异常(exception)构造函数的重在版本 exception(const char* const)
// 语句(dir + ": Path does not exist!")返回一个string对象
// string::c_str()返回const char*
throw exception((dir + ": Path does not exist!").c_str());
}
if (dir.at(dir.length() - 1) != '\\')
{
// 路径字符串不是以'\'结尾的,补上'\'
dir += '\\';
}
if (_chdir(dir.c_str()) != 0)
{
// 切换到dir路径
throw exception(("Error in _chdir:" + dir).c_str());
}
string ext = extension;
if (ext.substr(0, 2) != "*.")
{
//扩展名不是'*.xx'形式的补全为该形式
ext.insert(0, "*.");
}
//
// 查找文件
_finddata_t findData;
memset(&findData, 0x0, sizeof(findData));
int findHandle = _findfirst(ext.c_str(), &findData);
if (findHandle == -1)
{
throw exception("Failed in function: \'_findfirst\'");
}
int result=0, count = 0;
do
{
fileNames.push_back(dir + findData.name);
++count;
result = _findnext(findHandle, &findData);
} while (result == 0);
_findclose(findHandle);
//查找完成
//
return count;
}
int LoadImages(const string& folder, const string& extension, vector<Mat>& images, const int maxCount = 20)
{
vector<string> fileNames;
int count = TraverseFolder(folder, extension, fileNames);
if (count == 0)
{
throw exception("No image found");
}
count = (count < maxCount) ? count : maxCount;
for (int i = 0; i < count; ++i)
{
images.push_back(imread(fileNames[i], CV_LOAD_IMAGE_UNCHANGED));
cout << fileNames[i] << " ...loaded." << endl;
}
cout << "Loaded "<<count << " image(s)" << endl;
return count;
}
显示图片部分的代码可以从这篇文章(OpenCV学习之例程详解(01):HelloWorld)里找到影子。
当然,还有几种替代方案,其一就是事先生成文件列表然后从列表文件中读取文件路径,效果大同小异,
这里也贴出来吧。先编写bat文件(假设图片存放在F:\Image\test文件夹下)并保存
@cls
@rem @dir F:\Image\test /s /b >F:\Image\test_imgs.csv
@pause
执行上述命令,然后实现LoadImages函数模块
#include <iostream>
#include <fstream>
#include <sstream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#pragma comment(lib,"opencv_core249.lib")
#pragma comment(lib,"opencv_highgui249.lib")
using namespace cv;
using namespace std;
static void LoadImages(const string& filename, vector<Mat>& images)
{
ifstream file(filename, ifstream::in);
assert(file.is_open());
string line;
while (getline(file, line))
{
images.push_back(imread(line, CV_LOAD_IMAGE_UNCHANGED));
}
}
在主函数中类似下述方式载入图片(代码片段):
try
{
string imgListFile;
cout << "请输入列表文件名(*.csv文件):";
cin >> imgListFile; // F:/Image/test_imgs.csv
vector<Mat> images;
LoadImages(imgListFile, images);
assert(images.size() > 1);
//...
}
catch(..)
剩余部分与前文一致。
本文示例的运行结果截图如下