当 OpenCV cv::imwrite() 失败时如何获取错误详细信息

How to get error details when OpenCV cv::imwrite() fails

提问人:BullyWiiPlaza 提问时间:6/9/2023 最后编辑:BullyWiiPlaza 更新时间:6/10/2023 访问量:107

问:

我正在使用 cv::imwrite 使用 OpenCV 库在 C++ 中将图像写入磁盘。不幸的是,该方法只返回,所以我一直想知道如何获取实际的错误详细信息。该函数可能由于各种原因而失败,例如图像损坏、磁盘空间不足或文件路径错误。有什么方法可以获得准确的异常或错误消息吗?我的软件的某个 Windows 11 用户出现映像写入失败(返回),而相同的代码适用于许多其他用户,因此我想尽可能多地了解为什么该调用无法更轻松地缓解问题。boolcv::imwritefalse

C++ OpenCV 异常 错误处理

评论

0赞 Miki 6/9/2023
您可以先将图像“imencode”到缓冲区。然后,使用首选的错误感知方法将二进制内容转储到文件中。
1赞 Christoph Rackwitz 6/9/2023
常见原因:目录不存在,路径包含非ASCII符号,权限问题,输入Mat为空
0赞 BullyWiiPlaza 6/10/2023
@ChristophRackwitz:使用非 ASCII 符号应该没问题,对吧?我刚刚尝试了文件名中带有中文符号的文件路径,它工作正常。但是,你们俩的评论帮助我编写了一个具有更好错误检查的手动版本。imwrite
0赞 Christoph Rackwitz 6/10/2023
* Windows 上的非 ASCII。而不是解决这个问题(这是合法的,但是......),我建议浏览 opencv 问题以获取报告(现在肯定有几十个)。有人建议 fopen 是罪魁祸首,它应该在 Windows 上,或者 IDK Windows 需要什么才能不失败_wfopen
0赞 BullyWiiPlaza 6/10/2023
@ChristophRackwitz:我在 Windows 上,非 ASCII 文件路径仍然有效。也许它在 OpenCV4 或其他什么中修复了。我使用 vcpkg 提供的最新产品。

答:

1赞 BullyWiiPlaza 6/10/2023 #1

正如注释所暗示的那样,我们可以通过使用该函数并验证文件路径的有效性来手动检查错误,以提供更好的错误反馈:cv::imencode

bool is_file_path_writable(const std::filesystem::path& file_path)
{
    const auto status = std::filesystem::status(file_path);
    const auto permissions = status.permissions();

    // Check if the file or directory is writable
    return (permissions & std::filesystem::perms::owner_write) != std::filesystem::perms::none ||
        (permissions & std::filesystem::perms::group_write) != std::filesystem::perms::none ||
        (permissions & std::filesystem::perms::others_write) != std::filesystem::perms::none;
}

void write_string_to_file(const std::filesystem::path& file_path, const std::string& file_contents)
{
    std::ofstream file_writer;
    file_writer.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    file_writer.open(file_path.wstring(), std::ios::out | std::ios::binary);
    file_writer << file_contents;
}

void error_aware_imwrite(const std::filesystem::path& output_file_path, const cv::Mat& mat)
{
    if (const auto parent_path = output_file_path.parent_path();
        !is_directory(parent_path))
    {
        throw std::runtime_error("Parent directory did not exist: " + parent_path.string());
    }

    if (is_regular_file(output_file_path) && !is_file_path_writable(output_file_path))
    {
        throw std::runtime_error("File path is not writable");
    }

    const auto file_extension = output_file_path.extension().string();

    std::vector<uchar> buffer;
#define MB ((size_t)1024*1024)
    buffer.resize(10 * MB);

    if (const auto successfully_encoded = imencode(file_extension, mat, buffer);
        !successfully_encoded)
    {
        throw std::runtime_error("Imagine encoding failed");
    }

    // Write to the file
    const auto written_file_contents = std::string(buffer.begin(), buffer.end());
    if (written_file_contents.empty())
    {
        throw std::runtime_error("Written image bytes were empty");
    }

    write_string_to_file(output_file_path, written_file_contents);
}

请注意,检查并不是绝对必要的,但它使代码“看起来”更干净(例如,如果我们不检查不存在的文件的文件权限,这实际上不会引发异常)。is_regular_file