Write OpenCV Mat in 16Bit one channel - c++

I'm stuck on writing a OpenCV Mat in 16 Bit. Whatever I try the result is always an 8 Bit (0-255) image. I checked for relating questions on SO but nothig here solved this issue.
The Mat contains 0-65535 greyscale values before writing it to disk. I already tried the following (and many more approaches):
cv::Mat depth;
depth.convertTo(depth, CV_16UC1);
imwrite("depth.png", depth);
as in the documentation for imwrite() they say, that it is possible to save a Mat with imwrite() when it is in CV_16U
What is wrong with the code? Anybody knows how to solve this?

Both the following work:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
int main( int, char** argv )
{
// Start with unsigned shorts and write to PNG
unsigned short data[4] = {0,12000,24000,65535};
Mat src = Mat(1,4,CV_16UC1,data);
imwrite("depth.png",src);
// Start with floats and convert to 16-bit then write to PNG
float dataf[4]={0.0,12000.0,25000.0,65000.0};
Mat a = Mat(1,4,CV_32FC1,dataf);
a.convertTo(a,CV_16UC1);
imwrite("d2.png",a);
}
I can check them with ImageMagick as follows, and both are 16-bit greyscale PNGs:
convert depth.png txt:
# ImageMagick pixel enumeration: 4,1,65535,gray
0,0: (0) #000000000000 gray(0)
1,0: (12000) #2EE02EE02EE0 gray(18.3108%)
2,0: (24000) #5DC05DC05DC0 gray(36.6217%)
3,0: (65535) #FFFFFFFFFFFF gray(255)
and
convert d2.png txt:
# ImageMagick pixel enumeration: 4,1,65535,gray
0,0: (0) #000000000000 gray(0)
1,0: (12000) #2EE02EE02EE0 gray(18.3108%)
2,0: (25000) #61A861A861A8 gray(38.1476%)
3,0: (65000) #FDE8FDE8FDE8 gray(99.1836%)
I suggest your problem is elsewhere. Please provide an MCVE.
Also, maybe try writing it to FileStorage or PGM format instead of PNG and look at the file in a normal editor to see if it really looks like 16-bit data - and if it works at all.

Related

OpenCV load CIE L*a*b* image

I'm trying to load a CIE Lab* image using openCV in C++.
Online I can find only examples that load an RGB image and convert it into a LAB image but I already have the LAB image so how can I load it and than access to the values of L, a and b?
The only way I find is to load the LAB image considering it an RGB image and convert it into a Lab image using:
cvtColor(source, destination, CV_BGR2Lab);
But I think this is not a good way to solve the problem because if I do this, the converted image looks very different from the original.
With a test image and the following code:
originalImage = imread(originalImagePath, CV_LOAD_IMAGE_UNCHANGED);
cout << originalImage.type() << endl;
Mat originalImageSplitted[3];
split(originalImage, originalImageSplitted);
cout << originalImageSplitted[0] << endl;
cout << originalImageSplitted[1] << endl;
cout << originalImageSplitted[2] << endl;
I get the result:
0
[]
[]
[]
Not really an answer, but too much for a comment.
You can make a Lab colourspace TIF file for testing like this with ImageMagick from the Terminal in Linux, macOS or Windows:
convert -depth 8 xc:black xc:white xc:red xc:lime xc:blue +append -colorspace Lab result.tif
That will look like this if I scale it up as it is currently only 5 pixels wide and 1 pixel tall:
You can then dump the pixels to see their values and hopefully work out what OpenCV is doing:
convert result.tif txt:
Sample Output
# ImageMagick pixel enumeration: 5,1,65535,cielab
0,0: (0,-0.5,-0.5) #000000 cielab(0%,-0.000762951%,-0.000762951%) <--- black pixel
1,0: (65535,-0.5,-0.5) #FF0000 cielab(100%,-0.000762951%,-0.000762951%) <--- white pixel
2,0: (34952,20559.5,17218.5) #885043 cielab(53.3333%,31.3718%,26.2737%) <--- red pixel
3,0: (57568,-22102.5,21330.5) #E00053 cielab(87.8431%,-33.7263%,32.5483%) <--- green pixel
4,0: (21074,20302.5,-27756.5) #524F00 cielab(32.1569%,30.9796%,-42.3537%) <--- blue pixel
Looking at the red pixel, you get:
L=53.33%
a=31.37% of 256, i.e. 80.3
b=26.27% of 256, i.e. 67.2
To keep the image unchanged you should read it into a Mat image similarly:
Mat image;
image = imread(<path_of_image>, CV_LOAD_IMAGE_UNCHANGED)
In this case the second argument should preserve your image color channels as is.
With #DanMašek using #MarkSetchell image we solved the problem.
Using imread function the image is automatically converted into an RGB image so it's needed to convert it into a Lab image again.
Another problem is releated to 8bit images. The resulted image has modified values of L,a and b following this rule:
L * 255/100
a as a+128
b as b+128
So I solved doing the following:
originalImage = imread(originalImagePath, CV_LOAD_IMAGE_UNCHANGED);
Mat originalImageLab;
cvtColor(originalImage, originalImageLab, COLOR_RGB2Lab);
Mat originalImageSplitted[3];
split(originalImageLab, originalImageSplitted);
Thank you all!

OpenCV image conversion goes wrong

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;
}

openCV warning component data type mismatch

I am using OpenCV 2.4.4 on a Cent OS machine. My code currently loads an image with the warning: component data type mismatch
here is the code:
#include <cv.h>
#include <highgui.h>
#include "imglib.h"
int main( int argc, char** argv )
{
Mat image = imread( argv[1], CV_LOAD_IMAGE_ANYDEPTH);
imwrite("debugwriteout.jp2", image);
}
I pass the name of a .jp2 greyscale file in the args. The image has a 14-bit pixel depth, but when I print out the pixel values I get values over 20000, and my image is now a completely black square. Any advice would be appreciated.
Additional information:
When I change the imread flag to CV_LOAD_IMAGE_GRAYSCALE it successfully convert the image to an 8-bit pixel depth and prints useful output so I can tell that the jasper module is working at least somewhat correctly.
Any advice would be appreciated,
Thanks
SZman,
I solved my problem.
The solution is the position of the high bit.
On 16 bits, for a 14 bits depth, you have xxxxxxxxxxxxxx00 instead of 00xxxxxxxxxxxxxx.
If you want the correct value, you must decal of 2 bits on the right.
Please read the image using those flags
Mat image = imread( argv[1], CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);

Viewing 8 bit RAW image file in openCV

I have a raw file which contains a header of 5 bytes containing the number of rows and columns in first two bits each . The 5th byte contains the number of bits for each pixel in the image which is 8 bits in all cases. The image data follows after that.
Since I am new to openCV, i want to ask how to view this RAW image file as an greyscale image using C++?
I know how to read binary data in C++ and have stored the image as a 2-D unsigned char array (since each pixel is 8 bit).
Can anyone please tell me how to view this data as image using openCV ?
I am using the below code , but getting a completely weird image :
void openRaw() {
cv::Mat img(numRows, numCols,CV_8U,&(image[0][0]));
//img.t();
cv::imshow("img",img);
cv::waitKey();
}
Any help will be greatly appreciated.
Thanks,
Rohit
You have to convert it to an IplImage.
If you want to see it as a pure grey-scale image, its actually rather easy.
Example code I use in one application:
CvSize mSize;
mSize.height = 960;
mSize.width = 1280;
IplImage* image1 = cvCreateImage(mSize, 8, 1);
memcpy( image1->imageData, rawDataPointer, sizeOfImage);
cvNamedWindow( "corners1", 1 );
cvShowImage( "corners1", image1 );
At that point you have a valid IplImage, which you can then display. (last 2 lines of code display it)
If the image is bayer-tiled, you will have to convert to RGB.
c++ notation:
cv::Mat img(rows,cols,CV_8U,ptrToDat);
cv::imwhow("img",img);
cv::waitkey();
*data should be saved columwise, otherewise use:
cv::Mat img(cols,rows,CV_8U,ptrToDat);
img=img.t();
cv::imwhow("img",img);
cv::waitkey();

How can i change a pixel value from a grayscaled image using Opencv 2.3?

When i read a grayscaled image using for example in Opencv 2.3:
Mat src = imread("44.png" ,0);
How can i access the pixel value of it?
I know if its RGB i can use:
std::cout << src.at<cv::Vec3b>(i,j)[0].
Thanks in advance.
Since a grayscale image contains only one component instead of 3, the resulting matrix/image is of type CV_8UC1 instead of CV_8UC3. And this in turn means, that individual pixels are not 3-vectors of bytes (cv::Vec3b) but just single bytes (unsigned char or OpenCV's uchar). So you can just use:
src.at<unsigned char>(i, j)