简体   繁体   中英

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

We have an OpenCV problem of opening (and writing) file paths that contain 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.

As far I 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. 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()?

PS We don't use QT

Thanks in advance!

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

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

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

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM