最近有个需求,是在OpenCV中,把从相机过来的原始图像保存下来,并在离线环境中无损还原成原先Mat中的数据。之前是直接使用相机SDK中附带的实用工具采集成tiff的,但不知道什么原因,采集下来的图像数据和实际用OpenCV在线跑的有点区别。所以为了保证可靠,也需要对使用某种方法保存下来的数据做完整性验证。

图像数据的保存方法

首先,听说OpenCV里可以用保存无损的PNG格式,所以谷歌之,写完的保存代码直接贴了。

void wirePNG() {
    const std::string saveDir = "/tmp";

    // 无损压缩选项
    std::vector<int> compressionParams;
    compressionParams.push_back(cv::IMWRITE_PNG_COMPRESSION);
    compressionParams.push_back(0); // PNG无损的预设值.
    compressionParams.push_back(cv::IMWRITE_PNG_STRATEGY);
    compressionParams.push_back(cv::IMWRITE_PNG_STRATEGY_DEFAULT);

    cameraInput cameraInputer;  // 自用的相机输入源
    for (int i = 0; i < 10000; ++i) {
        cv::Mat image = cameraInputer.getFrame();   // 从相机读取一帧
        std::string pngFilename = saveDir + "image" + std::to_string(i) + ".png";

        // 和平常imwrite一样调用就行
        cv::imwrite(pngFilename, image, compressionParams);

        std::cout << pngFilename << std::endl;
        usleep(20000);
    }
}

数据的验证

OpenCV的原始数据存储

在OpenCV中,可以使用FileStorage直接将Mat矩阵保存成数据文件(一般是xml)。不过这种保存方法多用在浮点型数据上。我们这边就用相关方法来作为一个可靠的验证源吧。

扩展一下上面的方法,我们就可以将一帧分别以无损PNG和xml的形式保存下来。

int wirtePngAndXml() {
    const std::string saveDir = "/tmp";

    // 无损压缩选项
    std::vector<int> compressionParams;
    compressionParams.push_back(cv::IMWRITE_PNG_COMPRESSION);
    compressionParams.push_back(0); // PNG无损的预设值.
    compressionParams.push_back(cv::IMWRITE_PNG_STRATEGY);
    compressionParams.push_back(cv::IMWRITE_PNG_STRATEGY_DEFAULT);

    cameraInput cameraInputer;  // 自用的相机输入源

    // 打开一个数据文件
    std::string fileName = saveDir + "/image-datas.xml";
    cv::FileStorage fs(fileName, cv::FileStorage::WRITE);

    for (int i = 0; i < 10; ++i) {
        cv::Mat image = cameraInputer.getFrame();   // 从相机读取一帧
        std::string pngFilename = saveDir + "/image" + std::to_string(i) + ".png";

        // 和平常imwrite一样调用就行
        cv::imwrite(pngFilename, image, compressionParams);

        // 一个xml中可以放多个图像数据,所以要给名字
        std::string name = "image" + std::to_string(i);
        fs << name << image;    // 流式传输

        std::cout << pngFilename << std::endl;
        usleep(20000);
    }
}

读取保存的数据并验证

我们现在有了同一帧图像的PNG格式和XML格式的数据,这下可以对保存的数据进行对比了。

int varify() {
    const std::string saveDir = "/tmp";
  
    // 打开数据文件
    std::string fileName = saveDir + "/image-datas.xml";
    cv::FileStorage fs(fileName, cv::FileStorage::READ);
  
    for (int i = 0; i < 9; ++i) {
        // 读取数据文件
        cv::Mat fsImage;
        std::string name = "image" + std::to_string(i);
        fs[name] >> fsImage;
      
        // 读取png图像
        std::string pngFilename = saveDir + "/image" + std::to_string(i + 1) + ".png";
        cv::Mat pngImage = cv::imread(pngFilename);

        // 验证性质代码,就简单粗暴点遍历每个像素的差的绝对值,并累加
        int sub[3] = {0};
        for (int j = 0; j < fsImage.rows; ++j) {
            for (int k = 0; k < fsImage.cols; ++k) {
                sub[0] += abs(fsImage.at<cv::Vec3b>(j, k)[0] - pngImage.at<cv::Vec3b>(j, k)[0]);
                sub[1] += abs(fsImage.at<cv::Vec3b>(j, k)[1] - pngImage.at<cv::Vec3b>(j, k)[1]);
                sub[2] += abs(fsImage.at<cv::Vec3b>(j, k)[2] - pngImage.at<cv::Vec3b>(j, k)[2]);
            }
        }
        std::cout << sub[0] << " " << sub[1] << " " << sub[2] << std::endl;
    }
}

验证代码中是对两个矩阵的每个通道的每个对应像素求绝对差值并累加,只要确保每帧图像的输出都是0 0 0就可以简单粗暴地证明这两个矩阵是相同的。

现在可以安心采集数据并且在离线环境做测试了。

P.S.最后注意在采集的时候加上相关的实时预览代码。

Last modification:June 23rd, 2021 at 09:15 pm