8

A program I am using is reading some bitmaps, and expects 32FC1 images.

I am trying to create these images

cv::Mat M1(255, 255, CV_32FC1, cv::Scalar(0,0,0));
cv::imwrite( "my_bitmap.bmp", M1 );

but when I check the depth - it is always CV_8U

How can I create the files so that they will contain the correct info ?

Update: It makes no difference if I use a different file extension - e.g. tif or png

I am reading it - using code that is already implemented - with cvLoadImage.

I am trying to CREATE the files that the existing code - that checks for the image type - can use.

I cannot convert files in the existing code. The existing code does not try to read random image type and convert it to desired type, but checks that the files are of the type it needs.

I found out - thank you for the answers - that cv::imwrite only writes integer type images.

Is there another way - either using OpenCV or something else - to write the images so that I end up with CV_32F type ?

Update again: The code to read image... if into a cv::Mat:

cv::Mat x = cv::imread(x_files, CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR);

The existing code:

IplImage *I = cvLoadImage(x_files.c_str(), CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR);
Thalia
  • 13,637
  • 22
  • 96
  • 190

3 Answers3

4

cv::imwrite() .bmp encoder assumes 8 bit channels.

If you only need to write .bmp files with OpenCV , you can convert your 32FC1 image to 8UC4, then use cv::imwrite() to write it and you will get a 32bits per pixel .bmp file. I am guessing that your program that reads the file will interpret the 32 bit pixels as a 32FC1. The .bmp format doesn't have an explicit channel structure, just a number of bits per pixel. Therefore you should be able to write 32 bit pixels as 4 channels of 8 bits in OpenCV and read them as single channel 32 bit pixels in another program - if you do this you need to be aware of endianness assumptions by the reader. Someting like the following should work:

cv::Mat m1(rows, cols, CV_32FC1);
...  // fill m1
cv::Mat m2(rows, cols, CV_8UC4, m1.data); // provide different view of m1 data
// depending on endianess of reader, you may need to swap byte order of m2 pixels
cv::imwrite("my_bitmap.bmp", m2);

You will not be able to read properly the files you created in OpenCV because the .bmp decoder in OpenCV assumes the file is 1 or 3 channel of 8 bit data (i.e. it can't read 32 bit pixels).


EDIT

Probably a much better option would be to use the OpenEXR format, for which OpenCV has a codec. I assume you just need to save your files with a .exr extension.

Bull
  • 11,771
  • 9
  • 42
  • 53
  • Thank you, I didn't know that imwrite always creates CV_8U. Please see update. Even if I convert, prior to writing, I still get CV_8U (which is according to documentation). So... is my case hopeless ? Or is there any way to create these images ? – Thalia Jul 23 '13 at 23:23
  • Do you have the source code for the program reading the .bmp file? I am wondering about what assumptions it makes about the pixel format. – Bull Jul 23 '13 at 23:55
3

Your problem is that bitmaps store data internally as integers not floats. If your problem is rounding error when saving you will need to either use a different file format or scale your data up before saving and then back down after saving. If you just want to convert the matrix you get after reading the file to a float you can use cv::convertto

Hammer
  • 10,109
  • 1
  • 36
  • 52
  • Unfortunately I can't convert type after reading, the images have to be of the right type. I experimented with file formats and scaling prior to writing... then I found out from documentation that imwrite only writes CV_8U. Is there an alternative ? – Thalia Jul 23 '13 at 23:25
  • out of curiosity, why wouldn't you be able to convert the type after reading? depending on the precision you require and if you have a known set of values, you could multiply your image by some constant (say 100) save it as a 16 bit png, then when you read it back divide by 100. even that requires changing the type though. – Hammer Jul 29 '13 at 16:50
0

I was struggling with the same problem. At the end i decided it would just be easier to write a custom function that can write and load an arbitrary CV Mat.

bool writeRawImage(const cv::Mat& image, const std::string& filename)
{
    ofstream file;
    file.open (filename, ios::out|ios::binary);
    if (!file.is_open())
        return false;
    file.write(reinterpret_cast<const char *>(&image.rows), sizeof(int));
    file.write(reinterpret_cast<const char *>(&image.cols), sizeof(int));
    const int depth = image.depth();
    const int type  = image.type();
    const int channels = image.channels();
    file.write(reinterpret_cast<const char *>(&depth), sizeof(depth));
    file.write(reinterpret_cast<const char *>(&type), sizeof(type));
    file.write(reinterpret_cast<const char *>(&channels), sizeof(channels));
    int sizeInBytes = image.step[0] * image.rows;
    file.write(reinterpret_cast<const char *>(&sizeInBytes), sizeof(int));
    file.write(reinterpret_cast<const char *>(image.data), sizeInBytes);
    file.close();
    return true;
}

bool readRawImage(cv::Mat& image, const std::string& filename)
{
    int rows, cols, data, depth, type, channels;
    ifstream file (filename, ios::in|ios::binary);
    if (!file.is_open())
        return false;
    try {
        file.read(reinterpret_cast<char *>(&rows), sizeof(rows));
        file.read(reinterpret_cast<char *>(&cols), sizeof(cols));
        file.read(reinterpret_cast<char *>(&depth), sizeof(depth));
        file.read(reinterpret_cast<char *>(&type), sizeof(type));
        file.read(reinterpret_cast<char *>(&channels), sizeof(channels));
        file.read(reinterpret_cast<char *>(&data), sizeof(data));
        image = cv::Mat(rows, cols, type);
        file.read(reinterpret_cast<char *>(image.data), data);
    } catch (...) {
        file.close();
        return false;
    }

    file.close();
    return true;
}
Jaka
  • 1,353
  • 2
  • 10
  • 16