在Windows上使用OpenCV的imread()函数处理非ASCII文件名

12
我们在Windows上遇到了一个OpenCV的问题,即打开(和写入)包含非ASCII字符的文件路径。受影响的函数有:cv::imread()cv::imwrite()等。
据我所见,在OpenCV源代码中,它甚至在Windows上使用fopen(而不是_wfopen),而且据我所知,fopen在Windows上无法处理非ASCII字符。
我看到了关于使用OpenCV读取带有外文字符的问题OpenCV imread with foreign charactersimread(openCV),QString unicodes,但仍然不明白解决这个问题的正确方法。从上面的问题中,我看到可能有一些使用QString的技巧,但如果它起作用,它到底是如何将Unicode字符串转换为Windows的fopen()可以接受的字符数组的呢?
附言:我们不使用QT。
4个回答

13

不需要修改OpenCV源代码的方法是使用_wfopen(如Remy所建议的)将整个文件读入内存缓冲区中,然后使用OpenCV的函数imdecode从该缓冲区创建一个cv::Mat

如果必要,您还可以反过来做 -即使用imencode将图像写入内存缓冲区,然后使用_wfopen打开具有UNICODE名称的文件并将缓冲区写入文件中(或者,您可以使用适当的API函数imwrite将其写入临时文件,然后移动/重命名它)。


1
谢谢!我可以确认这个解决方案对我们有效。 - Vyacheslav
我有同样的问题,我想尝试你的解决方案。我能够使用wfopen在我的文件上获得一个FILE*,但是我不知道如何正确使用imdecode从中获取一个cv :: Mat。 此外:OpenCV在3.0版本中是否仍然不支持本地非ASCII文件名?PS:如果我使用Qt,也许有一种更简单但仍然有效的方法吗(我的意思是不读取QImage并在之后转换)? - bweber
请注意:Python中的等效写法cv.imdecode(np.fromfile(fname, np.uint8), flags=cv.IMREAD_COLOR) - Christoph Rackwitz

5

试试这个:

cv::Mat ReadImage(const wchar_t* filename)
{
    FILE* fp = _wfopen(filename, L"rb");
    if (!fp)
    {
        return Mat::zeros(1, 1, CV_8U);
    }
    fseek(fp, 0, SEEK_END);
    long sz = ftell(fp);
    char* buf = new char[sz];
    fseek(fp, 0, SEEK_SET);
    long n = fread(buf, 1, sz, fp);
    _InputArray arr(buf, sz);
    Mat img = imdecode(arr, CV_LOAD_IMAGE_COLOR);
    delete[] buf;
    fclose(fp);
    return img;
}

3

在Visual Studio中,Microsoft的fopen()版本支持非标准的css模式标志以启用Unicode数据的读取/写入,但它不支持Unicode文件名。因此,您必须使用_wfopen(),这样您就可以调整OpenCV的源代码,以便可以传递Unicode文件名并使用_wfopen()而不是fopen()来打开它。


2

这里是使用std::ifstream的解决方案:

std::ifstream file(path.toStdWString(), std::iostream::binary);
if (!file.good()) {
    return cv::Mat();
}
file.exceptions(std::ifstream::badbit | std::ifstream::failbit | std::ifstream::eofbit);
file.seekg(0, std::ios::end);
std::streampos length(file.tellg());
std::vector<char> buffer(static_cast<std::size_t>(length));
if (static_cast<std::size_t>(length) == 0) {
    return cv::Mat();
}
file.seekg(0, std::ios::beg);
try {
    file.read(buffer.data(), static_cast<std::size_t>(length));
} catch (...) {
    return cv::Mat();
}
file.close();
cv::Mat image = cv::imdecode(buffer, CV_LOAD_IMAGE_COLOR);
return image;

使用Qt可以使代码更加简短:

QFile file(path);
std::vector<char> buffer;
buffer.resize(file.size());
if (!file.open(QIODevice::ReadOnly)) {
    return cv::Mat();
}
file.read(buffer.data(), file.size());
file.close();
cv::Mat image = cv::imdecode(buffer, CV_LOAD_IMAGE_COLOR);
return image;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接