opencv imread() on Windows for non-ASCII file names
Asked Answered
D

4

13

We have an OpenCV problem of opening (and writing) file paths that contain non-ASCII characters on Windows. Affected functions are: cv::imread(), cv::imwrite(), ...

As far as I saw in the OpenCV source code, it uses fopen even on Windows (instead of _wfopen) and AFAIK fopen does not handle non-ASCII characters on Windows.


I saw questions OpenCV imread with foreign characters and imread(openCV),QString unicodes but still didn't understand a proper way of solving the problem. From the questions above I saw that there could be some trick using QStrings, but if it works what does it exactly do? How does it transform a unicode string to a character array that will be accepted by Windows' fopen()?

P.S. We don't use QT

Delatorre answered 15/7, 2014 at 23:7 Comment(0)
Q
14

The way to do this without hacking the OpenCV source code is to use _wfopen (as Remy suggested) to read the whole file into a memory buffer. Then use OpenCV's function imdecode to create a cv::Mat from that buffer.

You can do the reverse too, if necessary - i.e. use imencode to write an image to a memory buffer, then use _wfopen to open a file with a UNICODE name and write the buffer to it (alternatively, you could just imwrite to a temporary file and then move/rename it using the appropriate API function).

Quip answered 16/7, 2014 at 4:28 Comment(2)
I have the same problem and I'd like to try your solution. I am able to get a FILE* on my file with wfopen but then I don't know how to properly use imdecode to get a cv::Mat from that. Apart from that: does OpenCV still not support non-ASCII filenames natively, also in version 3.0? PS: is there maybe an easier but still efficient way if I do use Qt (I mean without reading QImages and converting afterwards)?Balaklava
Note: the equivalent for Python is cv.imdecode(np.fromfile(fname, np.uint8), flags=cv.IMREAD_COLOR)Tourism
N
5

Try this:

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;
}
Nonce answered 9/12, 2016 at 16:2 Comment(0)
R
3

Microsoft's version of fopen() in Visual Studio supports a non-standard css mode flag for enabling reading/writing of Unicode data, but it does not support Unicode filenames. You have to use _wfopen() for that, so you will have to tweak OpenCV's source code so you can pass in a Unicode filename and open it with _wfopen() instead of fopen().

Roadhouse answered 15/7, 2014 at 23:15 Comment(0)
B
2

Here my solution using 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;

Or a bit shorter using 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;
Balaklava answered 27/9, 2015 at 13:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.