簡體   English   中英

C ++,OpenCV:在Windows上讀取包含非ASCII字符的文件的最快方法

[英]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進行某些操作)。 它們都同樣慢:

方法1

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);

方法2

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);

UPDATE

方法3

此方法使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM