12

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

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
Vyacheslav
  • 1,186
  • 2
  • 15
  • 29

4 Answers4

13

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

Roger Rowland
  • 25,885
  • 11
  • 72
  • 113
  • 1
    Thanks! I can confirm that this solution works for us – Vyacheslav Jul 16 '14 at 19:33
  • 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 `QImage`s and converting afterwards)? – bweber Sep 26 '15 at 15:27
  • Note: the ***equivalent for Python*** is `cv.imdecode(np.fromfile(fname, np.uint8), flags=cv.IMREAD_COLOR)` – Christoph Rackwitz Jun 08 '23 at 13:59
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;
}
hosseinkhosravi
  • 406
  • 6
  • 5
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().

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
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;
bweber
  • 3,772
  • 3
  • 32
  • 57