[英]C++, OpenCV: Fastest way to read a file containing non-ASCII characters on windows
我正在使用OpenCV编写程序,该程序可以在Windows上运行,也可以在Linux上运行。 现在OpenCV的问题是,它的cv::imread
函数无法处理Windows上包含非ASCII字符的文件路径。 解决方法是首先使用其他库(例如std-libraries或Qt)将文件读入缓冲区,然后使用cv::imdecode
函数从该缓冲区中读取文件。 这就是我目前所做的。 但是,它不是非常快,而且比使用cv::imread
慢得多。 我的TIF图像大小约为1GB。 用cv::imread
读它需要大约。 1s,用缓冲方法读取它需要大约14s。 我假设imread
只是读取显示图像所需的TIF部分(没有层等)。 无论是这个,还是我将文件读入缓冲区的代码都不好。
现在我的问题是,是否有更好的方法来做到这一点。 要么是关于OpenCV的更好方法,要么是关于将文件读入缓冲区的更好方法。
我尝试了两种不同的缓冲方法,一种使用std库,一种使用Qt(实际上它们都使用QT进行某些操作)。 它们都同样慢:
std::shared_ptr<std::vector<char>> readFileIntoBuffer(QString const& path) {
#ifdef Q_OS_WIN
std::ifstream file(path.toStdWString(), std::iostream::binary);
#else
std::ifstream file(path.toStdString(), std::iostream::binary);
#endif
if (!file.good()) {
return std::shared_ptr<std::vector<char>>(new std::vector<char>());
}
file.exceptions(std::ifstream::badbit | std::ifstream::failbit | std::ifstream::eofbit);
file.seekg(0, std::ios::end);
std::streampos length(file.tellg());
std::shared_ptr<std::vector<char>> buffer(new std::vector<char>(static_cast<std::size_t>(length)));
if (static_cast<std::size_t>(length) == 0) {
return std::shared_ptr<std::vector<char>>(new std::vector<char>());
}
file.seekg(0, std::ios::beg);
try {
file.read(buffer->data(), static_cast<std::size_t>(length));
} catch (...) {
return std::shared_ptr<std::vector<char>>(new std::vector<char>());
}
file.close();
return buffer;
}
然后从缓冲区读取图像:
std::shared_ptr<std::vector<char>> buffer = utility::readFileIntoBuffer(path);
cv::Mat image = cv::imdecode(*buffer, cv::IMREAD_UNCHANGED);
QByteArray readFileIntoBuffer(QString const & path) {
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
return QByteArray();
}
return file.readAll();
}
并用于解码图像:
QByteArray buffer = utility::readFileIntoBuffer(path);
cv::Mat matBuffer(1, buffer.size(), CV_8U, buffer.data());
cv::Mat image = cv::imdecode(matBuffer, cv::IMREAD_UNCHANGED);
此方法使用QFileDevice::map
将文件映射到内存中,然后使用cv::imdecode
。
QFile file(path);
file.open(QIODevice::ReadOnly);
unsigned char * fileContent = file.map(0, file.size(), QFileDevice::MapPrivateOption);
cv::Mat matBuffer(1, file.size(), CV_8U, fileContent);
cv::Mat image = cv::imdecode(matBuffer, cv::IMREAD_UNCHANGED);
然而,这种方法也没有比其他两种方法更短的时间。 我还做了一些时间测量,发现在内存中读取文件或将其映射到内存实际上并不是瓶颈。 占用大部分时间的操作是cv::imdecode
。 我不知道为什么会这样,因为使用相同图像的cv::imread
只需要一小部分时间。
我尝试使用以下代码在Windows上获取包含非ascii字符的文件的8.3路径名:
QString getShortPathname(QString const & path) {
#ifndef Q_OS_WIN
return QString();
#else
long length = 0;
WCHAR* buffer = nullptr;
length = GetShortPathNameW(path.toStdWString().c_str(), nullptr, 0);
if (length == 0) return QString();
buffer = new WCHAR[length];
length = GetShortPathNameW(path.toStdWString().c_str(), buffer, length);
if (length == 0) {
delete[] buffer;
return QString();
}
QString result = QString::fromWCharArray(buffer);
delete[] buffer;
return result;
#endif
}
但是,我必须发现在我的机器上禁用了8.3路径名生成,所以它也可能在其他机器上。 所以我还没能测试它,它似乎没有提供可靠的解决方法。 我还有一个问题,该函数没有告诉我8.3路径名生成被禁用。
OpenCV GitHub上有一张开放票: https : //github.com/opencv/opencv/issues/4292
其中一条评论提出了一种解决方法,即使用内存映射文件(在Boost的帮助下)不将整个文件读取到内存中:
mapped_file map(path(L"filename"), ios::in);
Mat file(1, numeric_cast<int>(map.size()), CV_8S, const_cast<char*>(map.const_data()), CV_AUTOSTEP);
Mat image(imdecode(file, 1));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.