How can I write float image in OpenCV - c++

Someone gave me this function:
Mat tan_triggs_preprocessing(InputArray src, float alpha = 1, float gamma = 10.0,
float tau = 1000.0, int sigma1 = 2) {
Mat X = src.getMat();
Mat I, tmp, tmp2;
double meanI;
X.convertTo(X, CV_32FC1);
pow(X, gamma, I);
meanI = 0.0;
pow(abs(I), alpha, tmp);
meanI = mean(tmp).val[0];
I = I / pow(meanI, 1.0/alpha);
meanI = 0.0;
pow(min(abs(I), tau), alpha, tmp2);
meanI = mean(tmp2).val[0];
I = I / pow(meanI, 1.0/alpha);
for(int r = 0; r < I.rows; r++) {
for(int c = 0; c < I.cols; c++) {
I.at<float>(r,c) = tanh(I.at<float>(r,c) / tau);
}
}
I = tau * I;
return I;
}
The function takes an input as a gray scale image or CV_8UC1 type, and it outputs a matrix of CV_32FC1 type. All I know is the function makes the input image lighter, increases its contrast. When I show the image using imshow function, I can see the output of tan_triggs_preprocessing very clearly, and actually the output lighter, more contrast compares to the source image. But the problem is when I save it as image format (JPG for example) using imwrite function, it's totally black. I can't see anything.
I checked the value of elements in the output, and I saw that their values are between [0.06.., 2.3...]. Here are my questions, hopefully you can help me, thank you so much.
Can I write an CV_32FC1 as image file format?
Why is the file written by imwrite above totally black?
I also looked for min and max value in the output, so I can normalize it in to 256 bins for CV_8UC1, but it doesn't work, even when I use imshow or imwrite.
How can I convert it to CV_8UC1 or write it as image file format? I used convertTo but it doesn't work as well.
Thank a lot.

imwrite/imread can only handle 8/16/24/32bit integral data, not floats (if you don't count Ilm/exr)
you probably want :
Mat gray_in = ...
Mat gray_out;
cv::normalize( tan_triggs_preprocessing(gray_in), gray_out, 0, 255, NORM_MINMAX, CV_8UC1);
(admittedly hard to spot, but it's even in the small print of bytefish's code ;)
also, please look at alternatives to that, like equalizehist and CLAHE

Related

Normalising an image in opencv

I have a RGB image stored in a Mat datastructure. I am converting the image into grayscale using cvtColor function in opencv. After that I am trying to normalise the image to the range [0,1]. I am using the default normalize function of opencv. To check the correctness, I tried printing the pixel values and equate it with matlab values(Matlab values are already in the range [0,1]). But the values differ a lot. Help me to make both results almost same. Below are the opencv and matlab codes.
Mat img1 = imread("D:/input.png", CV_LOAD_IMAGE_COLOR);
cvtColor(img1, img1, CV_BGR2GRAY);
img1.convertTo(img1, CV_32FC1);
cv::normalize(img1, img1, 0.0, 1.0, NORM_MINMAX, CV_32FC1);
for (int i = 0; i < img1.rows; i++)
{
for (int j = 0; j < img1.cols; j++)
{
cout << img1.at<float>(i, j) << endl;
}
}
Matlab code:
I=im2double(imread('input.png'));
gI=rgb2gray(I);
display(gI)
I don't think you want to normalize here. The Matlab conversion rgb2gray uses this equation: 0.2989 * R + 0.5870 * G + 0.1140 * B. So there's no expectation that you have the minimum value of 0.0 or the maximum value of 1.0 in your output greyscale image. You would only expect 0 and 1 if you had pure white (255,255,255) and pure black (0,0,0) pixels.
Try this:
img *= 1./255;
cvtColor(img, img, CV_BGR2GRAY);

Convert OGRE::Image to cv::Mat

I am new to OGRE library. I have a human model in OGRE, I get the projection of the model in 'orginalImage' variable. I would like to perform some image processing using openCV. So I am trying to achieve OGRE::Image to cv::Mat conversion.
Ogre::Image orginalImage = get2dProjection();
//This is an attempt to convert the image
cv::Mat destinationImage(orginalImage.getHeight(), orginalImage.getWidth(), CV_8UC3, orginalImage.getData());
imwrite("out.png", destinationImage);
I get following error:
realloc(): invalid pointer: 0x00007f9e2ca13840 ***
On the similar note, I tried following as my second attempt
cv::Mat cvtImgOGRE2MAT(Ogre::Image imgIn) {
//Result image intialisation:
int imgHeight = imgIn.getHeight();
int imgWidth = imgIn.getWidth();
cv::Mat imgOut(imgHeight, imgWidth, CV_32FC1);
Ogre::ColourValue color;
float gray;
cout << "Converting " << endl;
for(int r = 0; r < imgHeight; r++){
for(int c = 0; c < imgWidth; c++){
color = imgIn.getColourAt(r,c,0);
gray = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
imgOut.at<float>(r,c) = gray;
}
}
return imgOut;
}
I get same error when I do one of the following:
imshow("asdfasd", imgOut);
imwrite("asdfasd.png", imgOut);
unfortunately I have no experience with OGRE, so I can just talk about OpenCV and what I've seen in Ogre documentation and poster's comments.
The first thing to mention is that the Ogre image' PixelFormat is PF_BYTE_RGBA (from comments) which is (according to OGRE documentation) a 4 byte pixel format, so the cv::Mat type should be CV_8UC4 if image data should be given by pointer. In addition, openCV best supports BGR images, so a color conversion might be best to save/display.
please try:
Ogre::Image orginalImage = get2dProjection();
//This is an attempt to convert the image
cv::Mat destinationImage(orginalImage.getHeight(), orginalImage.getWidth(), CV_8UC4, orginalImage.getData());
cv::Mat resultBGR;
cv::cvtColor(destinationImage, resultBGR, CV_RGBA2BGR);
imwrite("out.png", resultBGR);
in your second example I wondered what is wrong there, until I saw color = imgIn.getColourAt(r,c,0); which might be wrong since most image APIs use .getPixel(x,y) so I confirmed that this is the same for OGRE. Please try this:
cv::Mat cvtImgOGRE2MAT(Ogre::Image imgIn)
{
//Result image intialisation:
int imgHeight = imgIn.getHeight();
int imgWidth = imgIn.getWidth();
cv::Mat imgOut(imgHeight, imgWidth, CV_32FC1);
Ogre::ColourValue color;
float gray;
cout << "Converting " << endl;
for(int r = 0; r < imgHeight; r++)
{
for(int c = 0; c < imgWidth; c++)
{
// next line was changed
color = imgIn.getColourAt(c,r,0);
gray = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
// this access is right
imgOut.at<float>(r,c) = gray;
}
}
return imgOut;
// depending of what you want to do with the image, "float" Mat type assumes what image intensity values are within 0..1 (displaying) or 0..255 (imwrite)
}
if you still get realloc errors, can you please try to find the exact line of code where it happens?
One thing I didnt consider yet is the real memory layout of OGRE images. It might be possible that they use some kind of aligned memory, where each pixel-row is aligned to have a memory size as a multiple of 4 or 16 or sth. (which might be more efficient, e.g. to use SSE instructions or sth.) If that is the case, you can't use the first method but you would have to change it to cv::Mat destinationImage(orginalImage.getHeight(), orginalImage.getWidth(), CV_8UC4, orginalImage.getData(), STEPSIZE); where STEPSIZE is the number of BYTES for each pixel ROW! But the second version should work then!

How to display HSV image after using RGBtoHSV converter function in c++?

Good evening, on the internet we can find a lot of algorithm to convert RGB pixel values to HSV, but I can't find function to display it. I'm using MS Visual Studio 2013 and openCV library. I know there is built function to get HSV image: cvtColor(obrazeczek1, obrazeczek1, CV_BGR2HSV); but I try to do this without this function. For example, to get gray images I using function:
#define NORMALIZE_RGB(x) \
(x > 255 ? 255 : (x < 0 ? 0 : x))
cv::Mat rgb2grey(cv::Mat& I)
{
CV_Assert(I.depth() != sizeof(uchar));
cv::Mat res(I.rows, I.cols, CV_8UC3);
switch (I.channels()) {
case 3:
cv::Mat_<cv::Vec3b> _I = I;
cv::Mat_<cv::Vec3b> _R = res;
for (int i = 0; i < I.rows; ++i)
for (int j = 0; j < I.cols; ++j){
int grey = ((_I(i, j)[0]) + (_I(i, j)[1]) + (_I(i, j)[2])) / 3;
_I(i, j)[0] = NORMALIZE_RGB(grey);
_I(i, j)[1] = NORMALIZE_RGB(grey);
_I(i, j)[2] = NORMALIZE_RGB(grey);
}
res = _I;
break;
}
return res;
}
and to call function and display image:
cv::Mat image = cv::imread("name.jpg");
cv::Mat img = rgb2grey(image);
cv::imshow("Grey image", img);
I found here Algorithm to convert RGB to HSV and HSV to RGB in range 0-255 for both tips. I know how to convert RGB pixel to HSV, but how to display this matrix using imshow? I also found function rgb2hsv but I dont have any idea what to change, to display it. This is a function:
void rgb2hsv(double r, double g, double b, double &h, double &s, double &v)
{
v = max(max(r, g), b);
double t = min(min(r, g), b);
double delta = v - t;
if (v != 0.0)
s = delta / v;
else
s = 0.0;
if (s == 0.0)
h = 0.0;
else
{
if (r == v)
h = (g - b) / delta;
else
if (g == v)
h = 2.0 + (b - r) / delta;
else
if (b == v)
h = 4.0 + (r - g) / delta;
h = h * 60.0;
if (h < 0.0)
h += 360;
}
}
There is not here similar question so plese help.
It doesn´t make sense to display an image that has been converted to HSV using imshow().
imshow() thinks, the Matrix is in BGR order. Converting the image to HSV just converts the RGB values to HSV channel values.
It was before 3 channel and after the conversion it is also 3 channel but other values. imshow() still wants to display the image as RGB image but then with HSV values which leads to an invalid image.
Just convert it back to RGB to make imshow() show the image correctly.
Edit
Mat hsv, bgr; // hsv is the hsv image you want to display
cvtColor(hsv, bgr, CV_HSV2BGR);
imshow("lolz", bgr);
You have a misunderstanding about color space conversion. You are probably expecting that the image will look visually different when doing color space conversion, but no.
The image will still contain the same information, just in a different format. Like when you are storing the image file in the form of .jpg, .png, etc. They are still semantically the same image. You don't need to convert from jpg to png and view it again because you know it will look the same. The binary content will be different but the semantic content is exactly the same.
HSV, BGR, RGB, LAB, are simply different representations of the same image. The image will look the same no matter what color space you are using.
OpenCV expects that imshow() will be fed with BGR color space so you need to convert whatever image into BGR before displaying.
You already had the BGR image, so you don't need to convert it to HSV to display it.
The only time that the image will be truly changed is when you convert it to grayscale. That's when you lose some information and the image will look different.
If you are displaying HSV image using imshow(), it will show you an invalid image that you might interpret as something useful or cool. But it's not. It's like a beautiful bug that you think it's a feature.
So, just show the BGR image, it has the same semantic content as HSV.

Trying to implement a tiny part of matlab code in C++ using opencv

I am trying to convert an image to double precision using opencv. I am trying to imitate the im2double function available in MATLAB in c++. So, for this what i did was..
Mat IMG = imread("lena.bmp");
Size size(8,8);
Mat img,img_re,grey;
cvtColor( IMG, img, CV_BGR2GRAY );
resize(img,img_re,size);
img_re.convertTo( grey, CV_64FC3, 1.0/255.0 );
std::cout<<grey<<std::endl;
unsigned char *input = (unsigned char*)(grey.data);
grey: [0.3764705882352941, 0.5176470588235293, 0.4352941176470588, 0.8274509803921568;
0.392156862745098, 0.5254901960784314, 0.7372549019607844, 0.6431372549019607;
0.4431372549019608, 0.6431372549019607, 0.7176470588235294, 0.5607843137254902;
0.5333333333333333, 0.3254901960784314, 0.6862745098039216, 0.8431372549019608]
The data stored in grey is almost similar to the data obtained from matlab. the pixels have a range of [0,1]here. But ,my problem starts here. I want to now access the pixel values from 'grey' and save it to a boost matrix.
So for this i use..
for (unsigned i=0; i < height; ++i)
{
for (unsigned j=0; j < width; ++j )
{
image(i,j) = input[grey.step * j + i ];
}
}
image:: [4,4]((24,24,144,144),(24,24,144,144),(24,216,144,224),(24,63,144,63))
After this step all the values in the matrix have a range of [0,255]. grey scale images are between [0,255] but why do it get the values between [0,1] in the first case.
please stay away from accessing Mat's raw 'data' pointer, and use:
grey.at<double>(i,j);
instead.
also, if im_re is a 1 channel, grayscale image, your typeflag is wrong, should be:
img_re.convertTo( grey, CV_64F, 1.0/255.0 );

Normalize pixel values between 0 and 1

I am looking to normalize the pixel values of an image to the range [0..1] using C++/OpenCV. However, when I do the normalization using either image *= 1./255 or the normalize function the pixel values are rounded down to zero. I have tried setting the image to type CV_32FC3.
Below is the code I have:
Mat image;
image = imread(imageLoc, CV_LOAD_IMAGE_COLOR | CV_LOAD_IMAGE_ANYDEPTH);
Mat tempImage;
// (didn't work) tempImage *= 1./255;
image.convertTo(tempImage, CV_32F, 3);
normalize(image, tempImage, 0, 1, CV_MINMAX);
int r = 100;
int c = 150;
uchar* ptr = (uchar*)(tempImage.data + r * tempImage.step);
Vec3f tempVals;
tempVals.val[0] = ptr[3*c+1];
tempVals.val[1] = ptr[3*c+2];
tempVals.val[2] = ptr[3*c+3];
cout<<" temp image - "<< tempVals << endl;
uchar* ptr2 = (uchar*)(image.data + r * image.step);
Vec3f imVals;
imVals.val[0] = ptr2[3*c+1];
imVals.val[1] = ptr2[3*c+2];
imVals.val[2] = ptr2[3*c+3];
cout<<" image - "<< imVals << endl;
This produces the following output in the console:
temp image - [0, 0, 0]
image - [90, 78, 60]
You can make convertTo() do the normalization for you:
image.convertTo(tempImage, CV_32FC3, 1.f/255);
You are passing 3 to convertTo(), presumably as channel-count, but that's not the correct signature.
I used the normalize function and it worked (Java):
Core.normalize(src,dst,0.0,1.0,Core.NORM_MINMAX,CvType.CV_32FC1);
You should use a 32F depth for your destination image. I believe the reason for this, is that since you need to get decimal values, you should use an a non-integer OpenCV data type. According to this table, the float types correspond to the 32F depth. I chose the number of channels to be 1 and it worked; CV_32FC1
Remember also that it's unlikely to spot any visual difference in the image.
Finally, since you probably have thousands of pixels in your image, your console might seem that it's printing only zeros. However due to the large amount of data, try to use CTRL+F to see what's going on. Hope this helps.