OpenCV convert CV_8U to CV_64F - c++

I am trying to convert a grayscale image to the type CV64F. From the OpenCv documentation I have understood that a grayscale image is of the type CV_8U. I have also found that imshow plots different types differently, hence I need to divide by 255 before converting. But after converting the image I still get many saturated pixels.
I am using this image, saved as a jpg:
http://www.ele.uri.edu/~hansenj/projects/ele585/lab2/cameraman.gif
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <cstring>
#include <cmath>
int main()
{
Mat I, input_image;
string path = "C:/<your_path>/camera_man.jpg";
input_image = imread(path.c_str(), 0); // Read the file as grayscale
imshow("Original", input_image);
// Convert image to CV_64F
input_image *= (double) 1 / 255;
input_image.convertTo(I, CV_64F);
imshow("Converted", I);
}

When you do this:
input_image *= (double) 1 / 255; // (1)
input_image.convertTo(I, CV_64F); // (2)
You are dividing each value in a CV_8UC1 matrix by 255 in (1), so each pixel will be:
new_value = static_cast<uchar>(old_value / 255)
so that new_value can have only values 0 for 0 <= old_value < 255, and 1 for old_value = 255. Then the conversion is applied in (2) on truncated values.
So, you need either to first convert to CV_64FC1 and then divide:
input_image.convertTo(I, CV_64F);
I *= (double)1 / 255;
or apply scaling directly during the conversion:
input_image.convertTo(I, CV_64F, 1.0 / 255.0);

Related

OpenCV Image substraction signed output

I wouls like to subtract two gray scale images (CV_8UC1) and get their signed difference as result(CV_16SC1) .
I have tried the code below but i get as difference a CV_8UC1 matrix insted of a signed CV_16SC1.
Could you please help properly defining the Mask matrix and data type parameter?
Thanks!
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include "Imagesubstraction.h"
using namespace cv;
using namespace std;
int main(void)
{
Mat M1, M2,Dif;
M1 = imread("../data/difference/a.bmp", CV_LOAD_IMAGE_GRAYSCALE);
M2 = imread("../data/difference/b.bmp", CV_LOAD_IMAGE_GRAYSCALE);
Mat Mask(1024, 1024, CV_8UC1, Scalar(1));
subtract(M1, M2, Dif,Mask,3);
imwrite("../data/difference/c.bmp", Dif);
return 0;
}
Of course you're going to get an unsigned matrix after saving it to BMP format. As stated in OpenCV documentation:
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.
But if you take a look at your Dif matrix before saving it, you will see it is a 16-bit signed matrix.
At least, the following code snippet works as expected:
cv::Mat m1(100, 100, CV_8U, cv::Scalar(50));
cv::Mat m2(100, 100, CV_8U, cv::Scalar(30));
cv::Mat dif;
cv::Mat mask(100, 100, CV_8U, cv::Scalar(255));
cv::subtract(m2, m1, dif, mask, CV_16S);
std::cout << dif << std::endl;

Saving an image with imwrite in opencv writes all black but imshow shows correctly

Original Question
This example code will display the image created correctly, but will save a png with only black pixels. The Mat is in CV_32FC3 format, so 3 channels of floats.
The answered questions I've found deal with image manipulation issues or converting incorrectly or saving in jpeg with various compression.
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
int i = 0;
int j = 0;
Vec3f intensity;
cv::Mat imageF;
imageF= cv::Mat::zeros(36,36,CV_32FC3);
for(j=0;j<imageF.cols;++j){
for(i=0;i<imageF.rows;++i){
intensity = imageF.at<Vec3f>(j, i);
intensity.val[2] = 0.789347;
intensity.val[1] = 0.772673;
intensity.val[0] = 0.692689;
imageF.at<Vec3f>(j, i) = intensity;
}}
imshow("Output", imageF);
imwrite("test.png", imageF);
waitKey(0);
return 0;
}
What changes need to be made to make it save as expected?
Berriel's Solution
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main() {
int i = 0;
int j = 0;
Vec3f intensity;
cv::Mat imageF;
cv::Mat image;
imageF= cv::Mat::zeros(36,36,CV_32FC3);
for(j=0; j<imageF.cols; ++j) {
for(i=0; i<imageF.rows; ++i) {
intensity = imageF.at<Vec3f>(j, i);
intensity.val[2] = 0.789347;
intensity.val[1] = 0.772673;
intensity.val[0] = 0.692689;
imageF.at<Vec3f>(j, i) = intensity;
}
}
imshow("Output", imageF);
Mat3b imageF_8UC3;
imageF.convertTo(imageF_8UC3, CV_8UC3, 255);
imwrite("test.png", imageF_8UC3);
waitKey(0);
return 0;
}
As you can read in the documentation:
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.
You should use convertTo to convert from CV_32FC3 to CV_8UC3 to get the same result:
Mat3b imageF_8UC3;
imageF.convertTo(imageF_8UC3, CV_8UC3, 255);
imwrite("test.png", imageF_8UC3);
By the way, imshow() displays correctly because...
If the image is 8-bit unsigned, it is displayed as is.
If the image is 16-bit unsigned or 32-bit integer, the pixels are divided by 256. That is, the value range [0,255*256] is mapped to
[0,255].
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].
Basically, the same trick is what you need to do before writing.
I came to this question, because I also had a problem with black ".png" images. Eventually I realised, that my 32 bit image with channels (Red, Green, Blue, Alpha) had a zero-valued alpha channel (full transparency). Thus, programs that are aware of transparency just show the "black background behind the image". After changing transparency to "255" (no transparency) my saved png-image could be visualized just fine:
MyImage[:,:,3] = 255
You can check that behaviour by assigning a value of 127 and you'll get a pale/greyed version of your image.

c++ - FreeImage+OpenCV - 16bit image get distorted

I'm trying to load an image because I have to apply an algorithm on it.
If I load an 8 bit-per-channel image there are no problems, but if I load a 16bpc image then it get "ruined". Unfortunatly, since I don't have enough reputation I can't uplad images.
Those are the links to them:
Either the source and the 8bpc processing result
http://postimg.org/image/gc0zf2lp5/
..result if I process the same image saved as 16bpc
http://postimg.org/image/5nnwee7df/
And this is the code:
FreeImage_Initialise();
FREE_IMAGE_FORMAT formato = FreeImage_GetFileType(argv[1], 0);
FIBITMAP* imagetmp = FreeImage_Load(format, argv[1]);
FIBITMAP* image = FreeImage_Rotate(imagetmp, 180);
FreeImage_FlipHorizontal(image);
int depth = FreeImage_GetBPP(image);
printf("depth = %d\n", FreeImage_GetPitch(image));
cv::Mat img(FreeImage_GetHeight(image), FreeImage_GetWidth(image), CV_MAKETYPE(depth/3, 3), FreeImage_GetBits(image), FreeImage_GetPitch(image));
FreeImage_DeInitialise();
What could it be?
The value of depth is not what you expected. It refers to OpenCV depths defined as:
#define CV_8U 0
#define CV_8S 1
#define CV_16U 2
#define CV_16S 3
#define CV_32S 4
#define CV_32F 5
#define CV_64F 6
So, if you know that your FreeImage is of type FIT_RGB16, you should use as depth the value CV_16U. You should also convert from RGB to BGR, since OpenCV Mats are in BGR format.
Example here:
#include <FreeImage.h>
#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
FreeImage_Initialise();
FREE_IMAGE_FORMAT format = FreeImage_GetFileType("path_to_image", 0);
FIBITMAP* imagetmp = FreeImage_Load(format, "path_to_image");
FIBITMAP* image = FreeImage_Rotate(imagetmp, 180);
FreeImage_FlipHorizontal(image);
int depth = FreeImage_GetBPP(image);
printf("depth = %d\n", FreeImage_GetPitch(image));
// FreeImage to Mat conversion
cv::Mat img(FreeImage_GetHeight(image), FreeImage_GetWidth(image), CV_MAKETYPE(CV_16U, 3), FreeImage_GetBits(image), FreeImage_GetPitch(image));
cvtColor(img, img, CV_BGR2RGB);
FreeImage_DeInitialise();
return 0;
}
Note that you may also avoid to create an additional FreeImage image just to flip it, and let OpenCV Mat to do that:
#include <FreeImage.h>
#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
FreeImage_Initialise();
FREE_IMAGE_FORMAT format = FreeImage_GetFileType("path_to_image", 0);
FIBITMAP* image = FreeImage_Load(format, "path_to_image");
// FreeImage to Mat conversion
cv::Mat img(FreeImage_GetHeight(image), FreeImage_GetWidth(image), CV_MAKETYPE(CV_16U, 3), FreeImage_GetBits(image), FreeImage_GetPitch(image));
cvtColor(img, img, CV_BGR2RGB);
flip(img,img,0);
FreeImage_DeInitialise();
return 0;
}
You can't show this image directly with cv::imshow. You need to convert it to CV_8UC3 type to see it. You can do that for example calling convertScaleAbs(img, img); before imshow.
Or you can refer to this answer for a function to convert all types of FreeImage to OpenCV Mats.

Copy Mat in opencv

I try to copy a image to other image using opencv, but I got a problem. Two image is not the same, like this:
This is the code I used:
#include <opencv2\opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <cmath>
#include <iostream>
#include <opencv2\opencv.hpp>
int main()
{
cv::Mat inImg = cv::imread("C:\\Users\\DUY\\Desktop\\basic_shapes.png");
//Data point copy
unsigned char * pData = inImg.data;
int width = inImg.rows;
int height = inImg.cols;
cv::Mat outImg(width, height, CV_8UC1);
//data copy using memcpy function
memcpy(outImg.data, pData, sizeof(unsigned char)*width*height);
//processing and copy check
cv::namedWindow("Test");
imshow("Test", inImg);
cv::namedWindow("Test2");
imshow("Test2", outImg);
cvWaitKey(0);
}
Simply use .clone() function of cv::Mat:
cv::Mat source = cv::imread("basic_shapes.png");
cv::Mat dst = source.clone();
This will do the trick.
You are making an image with one channel only (which means only shades of gray are possible) with CV_8UC1, you could use CV_8UC3 or CV_8UC4 but for simply copying stick with the clone function.
You actually don't want to copy the data, since you start with a RGB CV_8UC3 image, and you want to work on a grayscale CV_8UC1 image.
You should use cvtColor, that will convert your RGB data into grayscale.
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
int main()
{
Mat inImg = cv::imread("C:\\Users\\DUY\\Desktop\\basic_shapes.png"); // inImg is CV_8UC3
Mat outImg;
cvtColor(inImg, outImg, COLOR_RGB2GRAY); // Now outImg is CV_8UC1
//processing and copy check
imshow("Test", inImg);
imshow("Test2", outImg);
waitKey();
}
With a simple memcopy you're copying a sequence of uchar like this:
BGR BGR BGR BGR ...
into an image that expects them to be (G for gray):
G G G G ...
and that's is causing your outImg to be uncorrect.
Your code will be correct if you define outImage like:
cv::Mat outImg(width, height, CV_8UC3); // Instead of CV_8UC1
the best way is to use the opencv clone method:
cv::Mat outImg = inImg.clone();
Your original image is in color. cv::Mat outImg(width, height, CV_8UC1); says that your new image is of data type CV_8UC1 which is an 8-bit grayscale image. So you know that is not correct. Then you try to copy the amount of data from the original image to the new image that corresponds to total pixels * 8-bits which is at best 1/3 of the actual image (assuming the original image was 3 color, 8-bits per color, aka a 24-bit image) and perhaps even 1/4 (if it had an alpha channel, making it 4 channels of 8-bits or a 32-bit image).
TLDR: you're matrices aren't the same type, and you are making assumptions about the size of the data to be copied off of an incorrect, and incorrectly sized type.
Here is a simple code to copy image.
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <cmath>
int main()
{
cv::Mat inImg = cv::imread("1.jpg");
cv::Mat outImg = inImg.clone();
cv::namedWindow("Test");
imshow("Test", inImg);
cv::namedWindow("Test2");
imshow("Test2", outImg);
cvWaitKey(0);
}
Mat source = imread("1.png", 0);
Mat dest;
source.copyTo(dest);

Knowing a pixel value after making an RGBtoHSV conversion OpenCv

Im trying to get the H,S and V Values of an image, so i convert an RGB image to HSV, and then just ask for the desired values, and then print them.. Im not quite sure im making this right, because when printing the Value (V of hsV) i get values of 100+ and i understand that the V just goes to 0-100, maybe im not using a correct method, here's the code:
#include "opencv/highgui.h"
#include "opencv/cv.h"
#include <cstdlib>
#include <iostream>
#include <stdio.h>
using namespace std;
int main(int argc, char** argv) {
int i=0,total=0;
IplImage* img = cvLoadImage( argv[1] );
IplImage* hsv;
CvSize size;
int key = 0, depth;
size = cvGetSize(img);
depth = img->depth;
hsv = cvCreateImage(size, depth, 3);
cvCvtColor( img, hsv, CV_BGR2HSV );
for(i=0;i<480;i++){ //asking for the values in \ form (1,1)(2,2),...(480,480)
CvScalar s;
s = cvGet2D(hsv,i,i);
printf("s=%f\n,s.val[2]); //s.val[2] equals to hs**V** right?
}
cvReleaseImage(&img);
cvReleaseImage(&val);
return 0;
}
The other answer here is correct but here is a code snippet that I have to calculate the V channel in opencv. I get the value from the Gimp app and this function gives me the opencv value.
//Max values: App HSV H=360 S=100 V=100 OpenCV H=180 S=255 V=255
double newHSV(double value)
{
//new_val = value * opencv_max_range / other_app_max_range
double newValue = value * 255 / 100;
return newValue;
}
To check your opencv HSV values in another application like Gimp, just calculate the formula to:
gimp_value = opencv_value * other_app_max_range / opencv_max_range
The way you're doing it is correct. Just that values are a little different.
H should ideally go from 0-360. But because a byte can only hold 0-255, H values are halved. So the range is 0-180.
V and S use the full range of 0-255 to specify value and saturation.
You can read more about it here: http://opencv.willowgarage.com/documentation/python/miscellaneous_image_transformations.html#cvtcolor