c++opencvdisparity-mapping

Saving disparity map using OpenCV in PFM format


I am trying to save a disparity map I got from stereo matching using cv::StereoSGBM in PFM format with this code from github.

But the output has the same image repeated 4 times over horizontally instead.

output

Same disparity saved in .jpg format for reference - .jpg image

Why is this happening and how do I fix this?

Code:

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");

Solution

  • The code for writing output PFM file in principle looks OK. However, you are accessing individual pixels with assumption that they are encoded as 3-channel 32-bit floating point values and that might not always be the case (usually they are 3-channel unsigned 8-bit integers, and the weird image in your question hints that there was some invalid out of bounds read). You can either check the image.type() and adjust your code accordingly, or convert your input matrix to desired CV_32FC3 (3-channel 32-bit floating point pixels) type. To convert, you can either use the built-in opencv function

      cv::Mat image;
      originalInput.convertTo(image, CV_32FC3);
    

    or if that doesn't work good enough (e.g. you would like to apply your custom 8bit -> 32bit mapping), just access the elements of the original image by

    image.at<cv::Vec3b>(height - 1 - i, j);
    

    and then apply some conversion from 8bit [0,255] to 32bit [0,1].

    That's my best guess, because you didn't provide complete reproducible example.