You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用nlohmann/json序列化含cv::Mat的Camera类时出现段错误求助

解决nlohmann/json序列化cv::Mat及自定义Camera类的段错误问题

你的问题核心在于错误地尝试直接序列化cv::Mat的内部私有类型(cv::MatSize、cv::MatStep),并且对cv::Mat的序列化逻辑完全错误,导致了无限递归或非法内存访问,最终引发段错误。下面是具体的解决思路和完整代码:

为什么之前的写法会失败?

  1. cv::Mat的序列化逻辑错误:你在adl_serializer<cv::Mat>中写了j = opt;,这会触发nlohmann/json再次尝试调用to_json来处理cv::Mat,形成无限递归,直接导致栈溢出或段错误。
  2. 不必要的内部类型序列化:cv::MatSize和cv::MatStep是cv::Mat的内部辅助类型,它们和内存布局强绑定,根本不需要(也不应该)单独序列化。当我们正确序列化cv::Mat的可移植数据后,这些内部结构会自动被创建。

正确的解决方案:序列化cv::Mat的可移植数据

我们需要手动实现cv::Mat的序列化逻辑,将其可移植的核心数据(行数、列数、类型、像素数据)转换成json格式,反序列化时再根据这些数据重建cv::Mat。

步骤1:正确特化adl_serializercv::Mat

#include <nlohmann/json.hpp>
#include <opencv2/opencv.hpp>

namespace nlohmann {
template<> struct adl_serializer<cv::Mat> {
    static void to_json(json& j, const cv::Mat& mat) {
        // 处理空Mat的特殊情况
        if (mat.empty()) {
            j = json{{"empty", true}};
            return;
        }

        // 存储cv::Mat的基本属性
        j["rows"] = mat.rows;
        j["cols"] = mat.cols;
        j["type"] = mat.type();

        // 将Mat的像素数据转换为vector存储(保证可移植性)
        std::vector<uchar> data_buffer;
        data_buffer.reserve(mat.total() * mat.elemSize());
        
        if (mat.isContinuous()) {
            // 连续内存直接拷贝,效率更高
            std::copy(mat.data, mat.data + mat.total() * mat.elemSize(), std::back_inserter(data_buffer));
        } else {
            // 非连续内存逐行拷贝(比如ROI区域)
            for (int row = 0; row < mat.rows; ++row) {
                const uchar* row_ptr = mat.ptr<uchar>(row);
                std::copy(row_ptr, row_ptr + mat.cols * mat.elemSize(), std::back_inserter(data_buffer));
            }
        }
        j["data"] = data_buffer;
    }

    static void from_json(const json& j, cv::Mat& mat) {
        // 重建空Mat
        if (j.contains("empty") && j["empty"].get<bool>()) {
            mat = cv::Mat();
            return;
        }

        // 读取基本属性
        int rows = j["rows"].get<int>();
        int cols = j["cols"].get<int>();
        int type = j["type"].get<int>();
        std::vector<uchar> data_buffer = j["data"].get<std::vector<uchar>>();

        // 重建cv::Mat,注意用clone()将数据拷贝到Mat的独立内存中
        mat = cv::Mat(rows, cols, type, data_buffer.data()).clone();
    }
};
} // namespace nlohmann

步骤2:移除不必要的cv::MatSize和cv::MatStep序列化代码

直接删除你之前写的adl_serializer<cv::MatSize>和adl_serializer<cv::MatStep>特化,完全不需要它们。

步骤3:测试代码调整

你的测试代码中默认构造的Camera包含空的intrinsics_和distortion_,上面的序列化逻辑已经处理了空Mat的情况。如果要测试完整功能,可以初始化一个带有效数据的Camera:

int main() {
    // 创建测试用的相机参数
    cv::Mat intrinsics = (cv::Mat_<double>(3,3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1);
    cv::Mat distortion = (cv::Mat_<double>(1,5) << 0.1, -0.05, 0, 0, 0);

    Camera camera("front_camera", "calibration_front.yml");
    camera.intrinsics_ = intrinsics;
    camera.distortion_ = distortion;

    // 序列化
    nlohmann::json j = camera;
    std::cout << "序列化结果:\n" << j.dump(4) << std::endl;

    // 反序列化
    Camera camera2 = j.get<Camera>();
    std::cout << "\n反序列化完成,相机名称:" << camera2.name_ << std::endl;
    std::cout << "内参矩阵行数:" << camera2.intrinsics_.rows << std::endl;

    return 0;
}

关键注意点

  • 避免直接赋值cv::Mat到json:必须手动拆解cv::Mat的可移植数据,不能依赖j = mat这种写法。
  • 处理空Mat:空Mat的序列化需要特殊判断,否则会访问空指针引发错误。
  • clone()的使用:反序列化时用clone()确保cv::Mat拥有独立的内存,避免依赖临时vector的生命周期。

内容的提问来源于stack exchange,提问作者Hannes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 17:42:41