Opencv imread() для Windows для имен файлов, отличных от ASCII

У нас есть проблема с открытием (и записью) OpenCV, содержащая символы не-Ascii в Windows. Я видел вопросы OpenCV imread с иностранными символами и imread (openCV), QString unicodes, но до сих пор не понял правильный способ решения проблемы.

Насколько я понял в исходном коде OpenCV, он использует fopen даже в Windows (вместо _wfopen), а afaik fopen не обрабатывает символы не-ascii в Windows. Из приведенных выше вопросов я увидел, что может быть какой-то трюк с использованием QStrings, но если он работает, что он делает именно? Как он преобразует строку юникода в массив символов, который будет принят Windows fopen()?

P.S. Мы не используем QT

Спасибо заранее!

Ответ 1

Способ сделать это без взлома исходного кода OpenCV заключается в использовании _wfopen (как рекомендовал Remy) для чтения всего файла в буфер памяти. Затем используйте функцию OpenCV imdecode, чтобы создать cv::Mat из этого буфера.

Вы также можете сделать обратное, если необходимо - использовать imencode для записи изображения в буфер памяти, затем с помощью _wfopen открыть файл с именем UNICODE и записать в него буфер (альтернативно, вы могли бы просто imwrite во временный файл, а затем переместить/переименовать его с помощью соответствующей функции API).

Ответ 2

Версия Microsoft fopen() в Visual Studio поддерживает нестандартный флаг режима css для включения чтения/записи данных Unicode, но он не поддерживает имена файлов Unicode. Вы должны использовать _wfopen() для этого, поэтому вам придется настроить исходный код OpenCV, чтобы вы могли передавать имя файла Unicode и открывать его с помощью _wfopen() вместо fopen().

Ответ 3

Попробуйте следующее:

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

Ответ 4

Здесь мое решение с помощью 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;