c++opencvunicodepath

How to read an image from a path with Unicode characters in C++?


I am trying to load an image with unicode characters in its path. cv::imread doesn't take a std::wstring as input, so I tried to convert the wide string to a common string first:

#pragma warning(push)
#pragma warning(disable: 4996)  
// See https://stackoverflow.com/questions/4804298/how-to-convert-wstring-into-string
inline std::string wsstring_2_string(const std::wstring& wstr)
{
    using convert_typeX = std::codecvt_utf8<wchar_t>;
    std::wstring_convert<convert_typeX, wchar_t> converterX;
    return converterX.to_bytes(wstr);
}
#pragma warning(pop)

Then I load the image:

const std::wstring imageWidePath = L"D:\\沙发模型12_0017.jpg";
const std::string imagePath = wsstring_2_string(imageWidePath);

const cv::Mat image = cv::imread(imagePath, cv::IMREAD_UNCHANGED);
assert(image.cols > 0 && image.rows > 0);

wsstring_2_string() returns D:\æ²™å‘æ¨¡åž‹12_0017.jpg, then cv::imread fails, which is legit.

I checked this answer. Basically, a solution is to first read the file as a byte array using an OS function that takes a wide string as input. Then build an input array (cv::InputArray in C++) that is later fed to cv::imdecode. But, how to build that InputArray in C++?

Or, maybe there is another way to support Unicode?

Note that the image format is not know in advance. For example, it can be CV_32FC4 as well as CV_8UC3.


Solution

  • wsstring_2_string() is returning a UTF-8 encoded std::string, but cv::imread() does not support UTF-8, which is why it fails.

    You don't need to use cv::InputArray directly. You can use any container that is convertible to InputArray, such includes cv::Mat, std::vector, etc.

    For example:

    #include <fstream>
    #include <vector>
    #include <filesystem>
    
    std::vector<char> readFileBytes(const std::wstring &filePath)
    {
        std::filesystem::path fsPath(filePath);
        std::ifstream inFile(fsPath, std::ios::binary);
        inFile.seekg(0, std::ios::end);
        auto const fileSize = inFile.tellg();
        inFile.seekg(0, std::ios::beg);
        std::vector<char> bytes(fileSize);
        inFile.read(reinterpret_cast<char*>(&bytes[0]), fileSize);
        bytes.resize(inFile.tellg());
        return bytes;
    }
    
    ...
    
    const std::wstring imageWidePath = L"D:\\沙发模型12_0017.jpg";
    std::vector<char> bytes = readFileBytes(imageWidePath);
    const cv::Mat image = cv::imdecode(bytes, cv::IMREAD_UNCHANGED);
    assert(image.cols > 0 && image.rows > 0);