I have an algorithm that does some stuff. Among them, there is a conversion that works fine if I'm working on a CV_8UC3
image but goes wrong if the file type is C_16UC3
. This is some code:
//new image is created Mat3w img(100,100,Vec3w(1000,0,0)); //Image Conversion - ERROR! cv::Mat inputSource; //saving the image here will work img.convertTo(inputSource, CV_64FC3); //saving the image here will not work -> black image
The problem is that the CV_16UC3
image's processing result is an image of the right dimensions but fully black. The problem is in the conversion because saving the image right before will give a legit one while saving it right after will give an almost completely white one.
EDIT:
I made some changes: cut off some useless code and added the inputSource declaration. Now, while I was trying stuff, I arrived at the conclusion that either I haven't understood the CV Types, or something strange is happening. I always thought that the number in the type was indicating the number of bits per channel. So, in my head, CV_16UC3
is a 3 channel with 16bits per channel. That idea is strengthened by the fact that the image I save during as tests (before the img.convertTo) actually had matching bits per channel number. The strange thing, is that the saved inputSource (type CV_64FC3
) is an 8bpc image.
What's am I missing?
You get confused with the way imwrite
and imread
work in OpenCV. From the OpenCV documentation
imwrite
The function imwrite
saves the image to the specified file. The image format is chosen based on the filename extension (see imread()
for the list of extensions). Only 8-bit (or 16-bit unsigned (CV_16U
) in case of PNG, JPEG 2000, and TIFF) single-channel or 3-channel (with ‘BGR’ channel order) images can be saved using this function. If the format, depth or channel order is different, use Mat::convertTo()
, and cvtColor()
to convert it before saving. Or, use the universal FileStorage
I/O functions to save the image to XML or YAML format.
imread
The function imread loads an image from the specified file and returns it. Possible flags are:
- IMREAD_UNCHANGED : If set, return the loaded image as is (with alpha channel, otherwise it gets cropped).
- IMREAD_GRAYSCALE : If set, always convert image to the single channel grayscale image.
- IMREAD_COLOR : If set, always convert image to the 3 channel BGR color image.
- IMREAD_ANYDEPTH : If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit.
- IMREAD_ANYCOLOR : If set, the image is read in any possible color format.
So for your case, CV_16U
are saved without conversion, while CV_64F
is converted and saved as CV_8U
. If you want to store double
data, you should use FileStorage
. You should also take care to use imread
the image with the appropriate flag.
This example should clarify:
#include <opencv2\opencv.hpp> using namespace cv; int main() { // Create a 16-bit 3 channel image Mat3w img16UC3(100, 200, Vec3w(1000, 0, 0)); img16UC3(Rect(0, 0, 20, 50)) = Vec3w(0, 2000, 0); // Convert to 64-bit (double) 3 channel image Mat3d img64FC3; img16UC3.convertTo(img64FC3, CV_64FC3); // Save to disk imwrite("16UC3.png", img16UC3); // No conversion imwrite("64FC3.png", img64FC3); // Converted to CV_8UC3 FileStorage fout("64FC3.yml", FileStorage::WRITE); fout << "img" << img64FC3; // No conversion fout.release(); Mat img_maybe16UC3_a = imread("16UC3.png" /*, IMREAD_COLOR*/); // Will be CV_8UC3 Mat img_maybe16UC3_b = imread("16UC3.png", IMREAD_ANYDEPTH); // Will be CV_16UC1 Mat img_maybe16UC3_c = imread("16UC3.png", IMREAD_UNCHANGED); // Will be CV_16UC3 Mat img_maybe64FC3_a = imread("64FC3.png" /*, IMREAD_COLOR*/); // Will be CV_8UC3 Mat img_maybe64FC3_b = imread("64FC3.png", IMREAD_ANYDEPTH); // Will be CV_8UC1 Mat img_maybe64FC3_c = imread("64FC3.png", IMREAD_UNCHANGED); // Will be CV_8UC3 Mat img_mustbe64FC3; FileStorage fin("64FC3.yml", FileStorage::READ); fin["img"] >> img_mustbe64FC3; // Will be CV_64FC3 fin.release(); return 0; }