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

使用OpenCV保存StereoSGBM视差图为PFM格式时图像水平重复四次的问题

问题:视差图保存为PFM格式后水平方向重复四次

使用cv::StereoSGBM进行立体匹配得到视差图,调用GitHub上的PFM读写代码保存为PFM格式后,输出图像在水平方向重复了四次。对比保存为JPG格式的正常参考图,求该问题的原因及解决方法。

相关代码

PFMReadWrite.cpp

bool savePFM(const cv::Mat image, const std::string filePath)
{
    //Open the file as binary!
    ofstream imageFile(filePath.c_str(), ios::out | ios::trunc | ios::binary);

    if(imageFile)
    {
        int width(image.cols), height(image.rows);
        int numberOfComponents(image.channels());

        //Write the type of the PFM file and ends by a line return
        char type[3];
        type[0] = 'P';
        type[2] = 0x0a;

        if(numberOfComponents == 3)
        {
            type[1] = 'F';
        }
        else if(numberOfComponents == 1)
        {
            type[1] = 'f';
        }

        imageFile << type[0] << type[1] << type[2];

        //Write the width and height and ends by a line return
        imageFile << width << " " << height << type[2];

        //Assumes little endian storage and ends with a line return 0x0a
        //Stores the type
        char byteOrder[10];
        byteOrder[0] = '-'; byteOrder[1] = '1'; byteOrder[2] = '.'; byteOrder[3] = '0';
        byteOrder[4] = '0'; byteOrder[5] = '0'; byteOrder[6] = '0'; byteOrder[7] = '0';
        byteOrder[8] = '0'; byteOrder[9] = 0x0a;

        for(int i = 0 ; i<10 ; ++i)
        {
            imageFile << byteOrder[i];
        }

        //Store the floating points RGB color upside down, left to right
        float* buffer = new float[numberOfComponents];

        for(int i = 0 ; i<height ; ++i)
        {
            for(int j = 0 ; j<width ; ++j)
            {
                if(numberOfComponents == 1)
                {
                    buffer[0] = image.at<float>(height-1-i,j);
                }
                else
                {
                    Vec3f color = image.at<Vec3f>(height-1-i,j);

                   //OpenCV stores as BGR
                    buffer[0] = color.val[2];
                    buffer[1] = color.val[1];
                    buffer[2] = color.val[0];
                }

                //Write the values
                imageFile.write((char *) buffer, numberOfComponents*sizeof(float));

            }
        }

        delete[] buffer;

        imageFile.close();
    }
    else
    {
        cerr << "Could not open the file : " << filePath << endl;
        return false;
    }

    return true;
}

main.cpp

// Creating Disp map
stereomatching.computeDisparity(left, right, imgsize, disp);
    
// Writing PFM
cv::imwrite(outputpath + ".jpg", disp);
bool r = savePFM(disp, outputpath + ".pfm");

原因分析

核心问题是视差图的Mat数据类型与savePFM函数的预期不匹配:

  • cv::StereoSGBM输出的视差图默认是16位有符号/无符号整数类型(CV_16S或CV_16U),而savePFM函数直接用image.at<float>()访问像素,假设输入是32位浮点类型(CV_32F)。
  • 当用at<float>()读取非浮点类型的Mat时,会将连续4个字节(float占4字节)解析为一个浮点值。而16位整数仅占2字节,这就导致每读取一个"错误的float值",实际跳过了2个字节的像素数据,最终图像在水平方向被错误拉伸/重复4倍(字节数不匹配导致的读取范围异常)。

解决方法

方法1:提前转换视差图为浮点类型

在调用savePFM前,将16位整数视差图转换为32位浮点类型,同时注意SGBM的视差值通常需要除以16(因为SGBM的disparity参数一般设为16的倍数,输出视差是实际值×16):

cv::Mat disp_float;
disp.convertTo(disp_float, CV_32F, 1.0/16.0); // 转换为浮点型并还原真实视差
bool r = savePFM(disp_float, outputpath + ".pfm");

方法2:增强savePFM函数的类型兼容性

修改函数,自动处理不同输入类型的转换,避免依赖外部转换:

bool savePFM(const cv::Mat image, const std::string filePath)
{
    cv::Mat float_image;
    // 根据输入类型转换为浮点型
    if (image.type() != CV_32F && image.type() != CV_32FC3) {
        if (image.channels() == 1) {
            // 单通道整数转浮点,保持数值比例
            image.convertTo(float_image, CV_32F, 1.0);
        } else {
            // 多通道整数转浮点,同时转换BGR到RGB
            cv::cvtColor(image, float_image, cv::COLOR_BGR2RGB);
            float_image.convertTo(float_image, CV_32F, 1.0/255.0);
        }
    } else {
        float_image = image.clone();
        if (image.channels() == 3) {
            cv::cvtColor(float_image, float_image, cv::COLOR_BGR2RGB);
        }
    }

    // 后续使用转换后的float_image处理
    ofstream imageFile(filePath.c_str(), ios::out | ios::trunc | ios::binary);
    if(!imageFile)
    {
        cerr << "Could not open the file : " << filePath << endl;
        return false;
    }

    int width(float_image.cols), height(float_image.rows);
    int numberOfComponents(float_image.channels());

    char type[3];
    type[0] = 'P';
    type[2] = 0x0a;
    type[1] = (numberOfComponents == 3) ? 'F' : 'f';
    imageFile << type[0] << type[1] << type[2];

    imageFile << width << " " << height << type[2];

    // 写入字节序信息
    char byteOrder[] = "-1.00000\n";
    imageFile << byteOrder;

    float* buffer = new float[numberOfComponents];
    for(int i = 0 ; i<height ; ++i)
    {
        for(int j = 0 ; j<width ; ++j)
        {
            if(numberOfComponents == 1)
            {
                buffer[0] = float_image.at<float>(height-1-i,j);
            }
            else
            {
                Vec3f color = float_image.at<Vec3f>(height-1-i,j);
                buffer[0] = color.val[0];
                buffer[1] = color.val[1];
                buffer[2] = color.val[2];
            }
            imageFile.write((char *) buffer, numberOfComponents*sizeof(float));
        }
    }

    delete[] buffer;
    imageFile.close();
    return true;
}

方法3:验证输入Mat类型

在调用savePFM前打印视差图的类型,确认是否符合预期:

std::cout << "视差图Mat类型: " << cv::typeToString(disp.type()) << std::endl;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 08:24:51