How to combines 2D mat into a 3D one? - c++

Here is my problem, I have 8 mat grayscale images with 100*100,
I would like to combine them together into a 3d matrix like 100*100*8;
Here is my code: (8 mat are named from img1 to img8 with same size 100*100 and pixel value is double)
int sz[3] = {img1.rows,img1.cols,8};
Mat m(3,sz, CV_8UC1, Scalar::all(0));
m.at<double>(m.rows,m.cols,1)=img1;
I think this code can put img1 into the 1st plane of 3d matrix 100*100*8, but then I got a error:
The type cannot convert from MAT to double.
How could I fix this problem?

You have to convert the double type matirces into uchar matrices first, which can be done as follows:
cv::Mat img1u;
img1.convertTo(img1u);
m.at<double>(m.rows,m.cols,1)=img1u;

Related

OpenCV, DFT function don't use in the image with IMREAD_COLOR

While reading the image with IMREAD_COLOR, 'dft' function throws the error:
DFT function works just fine when reading an image with IMREAD_GRAYSCALE. But I want to read the image with IMREAD_COLOR.
main function
const char* filename = "face.jpg";
Mat I = imread(filename, IMREAD_COLOR);
if(I.empty()) return 0;
Mat padded;
I.convertTo(padded, CV_32F);
Mat fft;
Mat planes[2];
dft(padded, fft, DFT_SCALE|DFT_COMPLEX_OUTPUT);
Mat fftBlur = fft.clone();
fftBlur *= 0.5;
split(fftBlur, planes);
Mat ph, mag;
mag.zeros(planes[0].rows, planes[0].cols, CV_32F);
ph.zeros(planes[0].rows, planes[0].cols, CV_32F);
cartToPolar(planes[0], planes[1], mag, ph);
merge(planes, 2, fftBlur);
//inverse
Mat invfft;
dft(fftBlur, invfft, DFT_INVERSE|DFT_REAL_OUTPUT);
Mat result;
invfft.convertTo(result, CV_8U);
Mat image;
cvtColor(result, image, COLOR_GRAY2RGB);
imshow("Output", result);
imshow("Image", image);
waitKey();
The message you receive is an assertion it tells you DFT function only takes single precision floating point image with one or two channels (CV_32FC1, CV_32FC2, the letter C at the end of the flag mean channel) or double precision floating point images with one or two channels (CV_64FC1, CV_64FC2).
The two channel case is actually the representation of complex image in OpenCV data storage.
If you want you can split you image to std::vector<cv::Mat> where each element does represent one channel, using cv::split apply the DFT on each channels do the processing you want on it and recreate an multichannel image thanks to cv::merge.
From Learning OpenCV (about dft function):
The input array must be of floating-point type and may be single- or double-channel. In the single-channel case, the entries are assumed to be real numbers, and the output will be packed in a special space-saving format called complex conjugate symmetrical.
The same question is mentioned here in terms of matlab image processing.
You can check out cv::split function if you want to separate channels of your initial image.

Multiplying Mat matrices using reshape, Mat type issue in OpenCV

I'm trying to implement color conversion from RGB-LMS and LMS-RGB back and using reshape for multiplication matrix, following answer from this question : Fastest way to apply color matrix to RGB image using OpenCV 3.0?
My ori Mat object is from an image with 3 channel (RGB), and I need to multiply them with matrix of 1 channel (lms), it seems like I have an issue with the matrix type. I've read reshape docs and questions related to this issue, like Issues multiplying Mat matrices, and I believe I have followed the instructions.
Here's my code : [UPDATED : Convert into flat image]
void test(const Mat &forreshape, Mat &output, Mat &pic, int rows, int cols)
{
Mat lms(3, 3, CV_32FC3);
Mat rgb(3, 3, CV_32FC3);
Mat intolms(rows, cols, CV_32F);
lms = (Mat_<float>(3, 3) << 1.4671, 0.1843, 0.0030,
3.8671, 27.1554, 3.4557,
4.1194, 45.5161 , 17.884 );
/* switch the order of the matrix according to the BGR order of color on OpenCV */
Mat transpose = (3, 3, CV_32F, lms).t(); // this will do transpose from matrix lms
pic = forreshape.reshape(1, rows*cols);
Mat flatFloatImage;
pic.convertTo(flatFloatImage, CV_32F);
rgb = flatFloatImag*transpose;
output = rgb.reshape(3, cols);
}
I define my Mat object, and I have converted it into float using convertTo
Mat ori = imread("ori.png", CV_LOAD_IMAGE_COLOR);
int rows = ori.rows;
int cols = ori.cols;
Mat forreshape;
ori.convertTo(forreshape, CV_32F);
Mat pic(rows, cols, CV_32FC3);
Mat output(rows, cols, CV_32FC3);
Error is :
OpenCV Error: Assertion failed (type == B.type() && (type == CV_32FC1 || type == CV_64FC1 || type == CV_32FC2 || type == CV_64FC2)) ,
so it's the type issue.
I tried to change all type into either 32FC3 of 32FC1, but doesn't seem to work. Any suggestion ?
I believe what you need is to convert your input to a flat image and than multiply them
float lms [] = {1.4671, 0.1843, 0.0030,
3.8671, 27.1554, 3.4557,
4.1194, 45.5161 , 17.884};
Mat lmsMat(3, 3, CV_32F, lms );
Mat flatImage = ori.reshape(1, ori.rows * ori.cols);
Mat flatFloatImage;
flatImage.convertTo(flatFloatImage, CV_32F);
Mat mixedImage = flatFloatImage * lmsMat;
Mat output = mixedImage.reshape(3, imData.rows);
I might have messed up with lms matrix there, but I guess you will catch up from here.
Also see 3D matrix multiplication in opencv for RGB color mixing
EDIT:
Problem with distortion is that you got overflow after float to 8U conversion. This would do the trick:
rgb = flatFloatImage*transpose;
rgb.convertTo(pic, CV_32S);
output = pic.reshape(3, rows)
Output:
;
Also I'm not sure but quick google search gives me different matrix for LMS see here. Also note that opencv stores colors in B-G-R format instead of RGB so change your mix mtraixes recordingly.

OpenCV apply my own filter to a color image

I'm working in OpenCV C++ to filtering image color. I want to filter the image using my own matrix. See this code:
img= "c:/Test/tes.jpg";
Mat im = imread(img);
And then i want to filtering/multiply with my matrix (this matrix can replaced with another matrix 3x3)
Mat filter = (Mat_<double>(3, 3) <<17.8824, 43.5161, 4.11935,
3.45565, 27.1554, 3.86714,
0.0299566, 0.184309, 1.46709);
How to multiply the img mat matrix with my own matrix? I'm still not understand how to multiply 3 channel (RGB) matrix with another matrix (single channel) and resulted image with new color.
you should take a look at the opencv documentation. You could use this function:
filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor=Point(-1,-1), double delta=0, int borderType=BORDER_DEFAULT )
which would give you something like this in your code:
Mat output;
filter2D(im, output, -1, filter);
About your question for 3-channel matrix; it is specified in the documentation:
kernel – convolution kernel (or rather a correlation kernel), a single-channel floating point matrix; if you want to apply different kernels to different channels, split the image into separate color planes using split() and process them individually.
So by default your "filter" matrix will be applied equally to each color plane.
EDIT You find a fully functional example on the opencv site: http://docs.opencv.org/doc/tutorials/imgproc/imgtrans/filter_2d/filter_2d.html

How to declare Mat that stores float values between 0 and 1 in opencv

I want to compare the values of a Mat with RBG image (i.e. Mat with 3 channels) with a threshold Mat th = [0.2,0.2,0.2] where each value in Mat th corresponds to threshold for each channel i.e. th=[th for Red,th for blue,th for Green].
When I declare th as Mat th(3,1,CV_32F,Scalar(0.2,0.2,0.2));
The output of cout<<th; shows th=[0,0,0]. What mistake am I doing in declaring the datatype?
And for 3 channels, do I need to declare it as Mat th(1,1,CV_32UC3,Scalar(0.2,0.2,0.2))?
Which data type helps to capture the logic for 3 channels and non-integer numbers ?
I believe you are looking for CV_32FC3. Another option would be to use Vec3f if you only want a single pixel.
Mat th(1,1,CV_32FC3,Scalar(0.2,0.2,0.2));
or
Vec3f th(0.2, 0.2, 0.2);
Use type CV_32FC3 or CV_64FC3 for float in 3-channel Mat.
Mat th(3,1,CV_32FC3,Scalar(0.2,0.2,0.2));
or
Mat th(3,1,CV_64FC3,Scalar(0.2,0.2,0.2));

Convert uchar Mat to float Mat in OpenCV?

In OpenCV, if I have a Mat img that contains uchar data, how do I convert the data into float? Is there a function available? Thank you.
If you meant c++ then you have
#include<opencv2/opencv.hpp>
using namespace cv;
Mat img;
img.create(2,2,CV_8UC1);
Mat img2;
img.convertTo(img2, CV_32FC1); // or CV_32F works (too)
details in opencv2refman.pdf.
UPDATE:
CV_32FC1 is for 1-channel (C1, i.e. grey image) float valued (32F) pixels
CV_8UC1 is for 1-channel (C1, i.e. grey image) unsigned char (8UC) valued ones.
UPDATE 2:
According to Arthur Tacca, only CV_32F is correct (or presumably CV_8U), since convertTo should not change the number of channels. It sounds logical right? Nevertheless, when I have checked opencv reference manual, I could not find any info about this, but I agree with him.
Use cvConvert function. In Python:
import cv
m = cv.CreateMat(2, 2, cv.CV_8UC1)
m1 = cv.CreateMat(2, 2, cv.CV_32FC1)
cv.Convert(m, m1)