[英]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.