繁体   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