Normalising an image in opencv - c++

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

Related

Matrix assignement value error in opencv C++ with mat.at<uchar>(i,j)

I am learning image processing with OpenCV in C++. To implement a basic down-sampling algorithm I need to work on the pixel level -to remove rows and columns. However, when I assign values with mat.at<>(i,j) other values are assign - things like 1e-38.
Here is the code :
Mat src, dst;
src = imread("diw3.jpg", CV_32F);//src is a 479x359 grayscale image
//dst will contain src low-pass-filtered I checked by displaying it works fine
Mat kernel;
kernel = Mat::ones(3, 3, CV_32F) / (float)(9);
filter2D(src, dst, -1, kernel, Point(-1, -1), 0, BORDER_DEFAULT);
// Now I try to remove half the rows/columns result is stored in downsampled
Mat downsampled = Mat::zeros(240, 180, CV_32F);
for (int i =0; i<downsampled.rows; i ++){
for (int j=0; j<downsampled.cols; j ++){
downsampled.at<uchar>(i,j) = dst.at<uchar>(2*i,2*j);
}
}
Since I read here OpenCV outputing odd pixel values that for cout I needed to cast, I wrote downsampled.at<uchar>(i,j) = (int) before dst.at<uchar> but it does not work also.
The second argument to cv::imread is cv::ImreadModes, so the line:
src = imread("diw3.jpg", CV_32F);
is not correct; it should probably be:
cv::Mat src_8u = imread("diw3.jpg", cv::IMREAD_GRAYSCALE);
src_8u.convertTo(src, CV_32FC1);
which will read the image as 8-bit grayscale image, and will convert it to floating point values.
The loop should look something like this:
Mat downsampled = Mat::zeros(240, 180, CV_32FC1);
for (int i = 0; i < downsampled.rows; i++) {
for (int j = 0; j < downsampled.cols; j++) {
downsampled.at<float>(i,j) = dst.at<float>(2*i,2*j);
}
}
note that the argument to cv::Mat::zeros is CV_32FC1 (1 channel, with 32-bit floating values), so Mat::at<float> method should be used.

OpenCV - remap() - getting black pixels

I'm using an STMap to map a .jpg image using remap().
I loaded my STMap, split the channels and converted each channel matrix to CV_32FC1.
I checked them and it worked - each matrix displays correctly and all of its values are between 0.0 and 1.0.
However, when i try to use the remap() function:
Mat dst;
remap(image4, dst,map_x,map_y,INTER_LINEAR,BORDER_CONSTANT,Scalar(0,0,0));
imshow( "Result", dst );
It just displays a black image.
image4 = my .jpg image
map_x = grayscale CV_32FC1 (red channel
of the original STMap)
map_y = grayscale CV_32FC1 (green channel
of the original STMap)
What could be the problem?
Thanks!
Black image when using cv::remap is due to using offsets instead of absolute locations in the passed map(s).
Optical flow algorithms usually export motion vectors, not absolute positions, whereas cv::remap expects the absolute coordinate (subpixel) to sample from.
To convert between the two, starting with a CV_32FC2 flow matrix we can do something like this:
// Convert from offsets to absolute locations.
Mat mapx(flow.size(), CV_32FC1);
Mat mapy(flow.size(), CV_32FC1);
for (int row = 0; row < flow.rows; row++)
{
for (int col = 0; col < flow.cols; col++)
{
Point2f f = flow.at<Point2f>(row, col);
mapx.at<float>(row, col) = col + f.x;
mapy.at<float>(row, col) = row + f.y;
}
}
Then mapx and mapy can be used in remap.

OpenCV-2.4.8.2: imshow differs from imwrite

I'm using OpenCV2.4.8.2 on Mac OS 10.9.5.
I have the following snippet of code:
static void compute_weights(const vector<Mat>& images, vector<Mat>& weights)
{
weights.clear();
for (int i = 0; i < images.size(); i++) {
Mat image = images[i];
Mat mask = Mat::zeros(image.size(), CV_32F);
int x_start = (i == 0) ? 0 : image.cols/2;
int y_start = 0;
int width = image.cols/2;
int height = image.rows;
Mat roi = mask(Rect(x_start,y_start,width,height)); // Set Roi
roi.setTo(1);
weights.push_back(mask);
}
}
static void blend(const vector<Mat>& inputImages, Mat& outputImage)
{
int maxPyrIndex = 6;
vector<Mat> weights;
compute_weights(inputImages, weights);
// Find the fused pyramid:
vector<Mat> fused_pyramid;
for (int i = 0; i < inputImages.size(); i++) {
Mat image = inputImages[i];
// Build Gaussian Pyramid for Weights
vector<Mat> weight_gaussian_pyramid;
buildPyramid(weights[i], weight_gaussian_pyramid, maxPyrIndex);
// Build Laplacian Pyramid for original image
Mat float_image;
inputImages[i].convertTo(float_image, CV_32FC3, 1.0/255.0);
vector<Mat> orig_guassian_pyramid;
vector<Mat> orig_laplacian_pyramid;
buildPyramid(float_image, orig_guassian_pyramid, maxPyrIndex);
for (int j = 0; j < orig_guassian_pyramid.size() - 1; j++) {
Mat sized_up;
pyrUp(orig_guassian_pyramid[j+1], sized_up, Size(orig_guassian_pyramid[j].cols, orig_guassian_pyramid[j].rows));
orig_laplacian_pyramid.push_back(orig_guassian_pyramid[j] - sized_up);
}
// Last Lapalcian layer is the same as the Gaussian layer
orig_laplacian_pyramid.push_back(orig_guassian_pyramid[orig_guassian_pyramid.size()-1]);
// Convolve laplacian original with guassian weights
vector<Mat> convolved;
for (int j = 0; j < maxPyrIndex + 1; j++) {
// Create 3 channels for weight gaussian pyramid as well
vector<Mat> gaussian_3d_vec;
for (int k = 0; k < 3; k++) {
gaussian_3d_vec.push_back(weight_gaussian_pyramid[j]);
}
Mat gaussian_3d;
merge(gaussian_3d_vec, gaussian_3d);
//Mat convolved_result = weight_gaussian_pyramid[j].clone();
Mat convolved_result = gaussian_3d.clone();
multiply(gaussian_3d, orig_laplacian_pyramid[j], convolved_result);
convolved.push_back(convolved_result);
}
if (i == 0) {
fused_pyramid = convolved;
} else {
for (int j = 0; j < maxPyrIndex + 1; j++) {
fused_pyramid[j] += convolved[j];
}
}
}
// Blending
for (int i = (int)fused_pyramid.size()-1; i > 0; i--) {
Mat sized_up;
pyrUp(fused_pyramid[i], sized_up, Size(fused_pyramid[i-1].cols, fused_pyramid[i-1].rows));
fused_pyramid[i-1] += sized_up;
}
Mat final_color_bgr;
fused_pyramid[0].convertTo(final_color_bgr, CV_32F, 255);
final_color_bgr.copyTo(outputImage);
imshow("final", outputImage);
waitKey(0);
imwrite(outputImagePath, outputImage);
}
This code is doing some basic pyramid blending for 2 images. The key issues are related to imshow and imwrite in the last line. They gave me drastically different results. I apologize for displaying such a long/messy code, but I am afraid this difference is coming from some other parts of the code that can subsequently affect the imshow and imwrite.
The first image shows the result from imwrite and the second image shows the result from imshow, based on the code given. I'm quite confused about why this is the case.
I also noticed that when I do these:
Mat float_image;
inputImages[i].convertTo(float_image, CV_32FC3, 1.0/255.0);
imshow("float image", float_image);
imshow("orig image", image);
They show exactly the same thing, that is they both show the same picture in the original rgb image (in image).
IMWRITE functionality
By default, imwrite, converts the input image into 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.
So whatever format you feed in for imwrite, it blindly converts into CV_8U with a range 0(black) - 255(white) in BGR format.
IMSHOW - problem
So when noticed your function, fused_pyramid[0].convertTo(final_color_bgr, CV_32F, 255); fused_pyramid is already under mat type 21 (floating point CV_32F). You tried to convert into floating point with a scale factor 255. This scaling factor 255 caused the problem # imshow. Instead to visualize, you can directly feed in fused_pyramid without conversion as already it is scaled to floating point between 0.0(black) - 1.0(white).
Hope it helps.

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

How can I write float image in OpenCV

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