Change type of Mat object from CV_32F to CV_8U

Karthik Murugan picture Karthik Murugan · Jan 26, 2013 · Viewed 70.7k times · Source

I tried to display an image of CV_32F type using imshow function but it showed a WHITE image. In the Documentation its given that floating point images will be mapped to 0-255 and displayed but it just showed a white image.I tried to convert it to CV_8U using

Mat A=Mat::ones(300,300,CV_32FC1)*1000;

do some processing - assigning float values to pixels in A

......

Mat B;

A.convertTo(B,CV_8U)

When I imshow 'B' i get a black & white image, there are no shades of gray. Are the float valued pixels in A properly mapped to 0-255 ? Am I doing anything wrong?

Few values in A are 1000 as initialized and rest are some floating point numbers which are assigned during processing.

Answer

sgarizvi picture sgarizvi · Jan 26, 2013

In OpenCV, if the image is of floating point type, then only those pixels can be visualized using imshow, which have value from 0.0 to 1.0, if the value is greater than 1.0, it will be shown as a white pixel and if less than 0.0, it will be shown as black pixel. To visualize a floating point image, scale its values to the range 0.0 - 1.0.

As for the conversion part.... When used with default arguments, the cv::Mat::convertTo function just creates a matrix of the specified type, and then copies the values from the source matrix and then rounds them to the nearest possible value of the destination data type. If the value is out of range, it is clamped to the minimum or maximum values.

In the documentation of imshow, it is written that:

If the image is 32-bit floating-point, the pixel values are multiplied by 255. That is, the value range [0,1] is mapped to [0,255].

It means that only the values in the range 0.0 to 1.0 will be mapped to 0 to 255. If a value is greater than 1.0, and multiplied by 255, it will become greater than 255. Then it will be clamped to the range of CV_8U and eventually it will also become 255.

In your example, all the values which are 1000, will become 255 in the destination matrix as the destination type is CV_8U and the maximum possible value is 255. All the floating point values will be floored. No automatic mapping is done.

To appropriately map the values to the range of CV_8U use the 3rd and 4th parameters of the function cv::Mat::convertTo, so that the values are scaled before the conversion is done.

Suppose the matrix A has minimum and maximum values Min and Max, where Min!=Max.

To properly scale the values from 0 to 255, you can do the following:

if (Min!=Max){ 
    A -= Min;
    A.convertTo(B,CV_8U,255.0/(Max-Min));
}

You can also do this directly like this:

if (Min!=Max)
    A.convertTo(B,CV_8U,255.0/(Max-Min),-255.0*Min/(Max-Min));

(edited taking into account zhangxaochen's comment)