OpenCV Remap parameters and How to use it? - c++

I'm not familiar with opencv, but I need to use the function ‘remap’ to rectify the image.
I have an image with 960x1280, and a remap file called ‘remap.bin’ with 9.8MB(is equaled to 960x1280x4x2, which means the two floats in one position(x,y));
Applies a generic geometrical transformation to an image.
C++: void remap(InputArray src, OutputArray dst, InputArray map1, InputArray map2, int interpolation, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
map1 – The first map of either (x,y) points or just x values having the type CV_16SC2 , CV_32FC1 , or CV_32FC2 . See convertMaps() for details on converting a floating point representation to fixed-point for speed.
map2 – The second map of y values having the type CV_16UC1 , CV_32FC1 , or none (empty map if map1 is (x,y) points), respectively.
According to the explain,
I code like this:
int main(int argc, char* argv[]){
if(argc != 3){
printf("Please enter one path of image and one path of mapdata!\n");
return 0;
}
std::string image_path = argv[1];
char* remap_path = argv[2];
cv::Mat src = cv::imread(image_path);
cv::Mat dst;
dst.create( src.size(), src.type());
cv::Mat map2;
map2.create( src.size(), CV_32FC1);
map2.data = NULL;
cv::Mat mapXY;
mapXY.create( src.rows, src.cols, CV_64FC1);
FILE *fp;
fp = fopen(remap_path, "rb");
fread(mapXY.data, sizeof(float), mapXY.cols*mapXY.rows*2, fp);
fclose(fp);
imshow("src", src);
printf("remap!\n");
cv::remap(src, dst, mapXY, map2, cv::INTER_LINEAR);
imshow("dst", dst);
cv::waitKey(0);
return 0;
But when I run the program I get this error:
OpenCV Error: Assertion failed (((map1.type() == CV_32FC2 || map1.type() == CV_16SC2) && !map2.data) || (map1.type() == CV_32FC1 && map2.type() == CV_32FC1)) in remap, file /home/liliming/opencv-2.4.13/modules/imgproc/src/imgwarp.cpp, line 3262 terminate called after throwing an instance of 'cv::Exception' what(): /home/liliming/opencv-2.4.13/modules/imgproc/src/imgwarp.cpp:3262: error: (-215) ((map1.type() == CV_32FC2 || map1.type() == CV_16SC2) && !map2.data) || (map1.type() == CV_32FC1 && map2.type() == CV_32FC1) in function remap Aborted (core dumped)
I have no idea about it.
Could anyone help me? or give some sample codes?
Thank you very much!

The documentation for OpenCV 3.1 says:
map1 The first map of either (x,y) points or just x values having the type
CV_16SC2 , CV_32FC1, or CV_32FC2.
The assert says that map1 doesn't have a type of CV_32FC2
This is because you are creating and reading it with a type of CV_64FC1.
You need to convert it to the correct type: array of two dimensions of type CV_32FC2 (two 32-bit floats per element.)
The documentation goes on to say:
See `convertMaps` for details on converting a
floating point representation to fixed-point for speed.
Documentation can be found here: https://docs.opencv.org/3.1.0/da/d54/group__imgproc__transform.html#gab75ef31ce5cdfb5c44b6da5f3b908ea4

I separate the remap table into two tables remapX, remapY.
Like this:
float *data_xy = (float *)malloc(sizeof(float)*960*1280*2);
FILE *fp;
fp = fopen(remap_path, "rb");
fread(data_xy, sizeof(float), 960*1280*2, fp);
fclose(fp);
for(int y=0; y<1280; ++y){
for(int x=0; x<960; ++x){
map_x.at<float>(y, x) = data_xy[(y*960+x)*2];
map_y.at<float>(y, x) = data_xy[(y*960+x)*2+1];
}
}
And then use the
cv::remap(src, dst, map_x, map_y, cv::INTER_LINEAR);
It works well.
But I don't know how to use one parameter map1 to finish remap.

Related

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.

Trying to find Magnitude of Mat image

I'm trying to learn OpenCV (Using version 3.0.0).
Right now I'm trying to see what the point operatioins do to various images, everything is going fine until I tried to do the magnitude operation, which requires inputs be in the form of
magnitude(InputArray x, InputArray y, OutputArray magnitude)
It also describes that x and y should be floating-point arrays of x/y-coordinates of the vectors and also the same size.
I've tried making a Vector of Mat's and splitting up the input image into these vectors and then doing the magnitude operator on them, but this didn't work. So I think I need to pass the arguments as columns and rows, but now I'm getting the error
OpenCV Error: Assertion failed (src1.size() == src2.size() && type == src2.type() && (depth == CV_32F || depth == CV_64F)) in magnitude, file /home/<user>/opencv-3.0.0-beta/modules/core/src/mathfuncs.cpp, line 521
terminate called after throwing an instance of 'cv::Exception'
what(): /home/<user>/opencv-3.0.0-beta/modules/core/src/mathfuncs.cpp:521: error: (-215) src1.size() == src2.size() && type == src2.type() && (depth == CV_32F || depth == CV_64F) in function magnitude
Aborted (core dumped)
And I'm not sure why, because I am clearly converting the input Mats to CV_64F types.
Am I using the magnitude function wrong? Or just passing it the wrong data?
void Magnitude(Mat img, Mat out)
{
img.convertTo(img, CV_64F);
out.convertTo(out, CV_64F);
for(int i = 0 ; i < img.rows ; i ++)
for(int j = 0 ; j < img.cols ; j++)
cv::magnitude(img.row(i), img.col(j), out.at<cv::Vec2f>(i,j));
cv::normalize(out,out,0,255,cv::NORM_MINMAX);
cv::convertScaleAbs(out,out);
cv::imshow("Magnitude", out);
waitKey();
}
void magnitude(InputArray x, InputArray y, OutputArray magnitude)
where x, y and magnitude must have the same size. In your case it means that your image have to be quadratic. Is it right?
A sample usage:
cv::Sobel(img, gx, img.depth(), 1, 0, 3);
cv::Sobel(img, gy, img.depth(), 0, 1, 3);
cv::Mat mag(gx.size(), gx.type());
cv::magnitude(gx, gy, mag);

OpenCV: Error copying one image to another

I am trying to copy one image to another pixel by pixel (I know there are sophisticated methods available. I am trying to solve another problem and answer to this will be useful).
This is my code:
int main()
{
Mat Img;
Img = imread("../../../stereo_images/left01.jpg");
Mat copyImg = Mat::zeros(Img.size(), CV_8U);
for(int i=0; i<Img.rows; i++){
for(int j=0; j<Img.cols; j++){
copyImg.at<uchar>(j,i) = Img.at<uchar>(j,i);
}}
namedWindow("Image", CV_WINDOW_AUTOSIZE );
imshow("Image", Img);
namedWindow("copyImage", CV_WINDOW_AUTOSIZE );
imshow("copyImage", copyImg);
waitKey(0);
return 0;
}
When I run this code in visual studio I get the following error
OpenCV Error: Assertion failed (dims <= 2 && data && (unsigned)i0 < (unsigned)si
ze.p[0] && (unsigned)(i1*DataType<_Tp>::channels) < (unsigned)(size.p[1]*channel
s()) && ((((sizeof(size_t)<<28)|0x8442211) >> ((DataType<_Tp>::depth) & ((1 << 3
) - 1))*4) & 15) == elemSize1()) in cv::Mat::at, file c:\opencv\opencv-2.4.9\ope
ncv\build\include\opencv2\core\mat.hpp, line 537
I know for fact that Img's type is CV_8U. Why does this happen ?
Thanks!
// will read in a rgb image , no matter what the content is
Img = imread("../../../stereo_images/left01.jpg");
to make it read grayscale images use:
Img = imread("../../../stereo_images/left01.jpg", CV_LOAD_IMAGE_GRAYSCALE);
then, you don't need to copy per pixel (and you should even avoid that), just use:
Mat im2 = Img.clone();
if you do per-pixel loops, watch out to get the indices right. it's row-col world here, not x,y, so it should be:
copyImg.at<uchar>(i,j) = Img.at<uchar>(i,j);
in your case
I know for fact that Img's type is CV_8U.
But CV_8U is just the image depth (8-bit U-nsigned). The type also specifies the number of channels, which is usually three. One for blue, one for green and one for red in this order as default for OpenCV. The type would be CV_8UC3 (C-hannels = 3). imread will convert even a black and white image to a 3-channel image by default. imread(filename, CV_LOAD_IMAGE_GRAYSCALE) will load a 1-channel image (CV_8UC1). But if you're not sure the easiest solution is
Mat copyImg = Mat::zeros(Img.size(), Img.type());
To access the array elements you have to know the size of it. Using .at<uchar>() on a 3-channel image will only access the first channel because you have 3*8 bit per pixel. So on a 3-channel image you have to use
copyImg.at<Vec3b>(i,j) = Img.at<Vec3b>(i,j);
where Vec3b is a cv::Vec<uchar, 3>. You should also note that the first argument of at<>(,) is the index along dim 0 which are the rows and second argument cols. Or in other words in classic 2d-xy-chart order you access a pixel with .at<>(y,x) == .at<>(Point(x,y)).
your problem is with this line :
copyImg.at<uchar>(j,i) = Img.at<uchar>(j,i);
It should be :
copyImg.at<uchar>(i,j) = Img.at<uchar>(i,j);
Note that if you want to copy image you can simply do this :
Mat copyImg = Img.clone();

Issues multiplying Mat matrices

I am trying to project an image to eigenface convariance matrix that EigenFacesRecognizer of opencv returns. I use the following code to load eigenfaces parameters loading an image and trying to project the sample image to pca subspace.
Ptr<FaceRecognizer> model = createEigenFaceRecognizer();
model->load("eigenfaces.yml"); // Load eigenfaces parameters
Mat eigenvalues = model->getMat("eigenvalues"); // Eigen values of PCA
Mat convMat = model->getMat("eigenvectors"); //Convariance matrix
Mat mean = model->getMat("mean"); // Mean value
string path = fileName;
Mat sample ,pca_ed_sample;
sample = imread(path, CV_LOAD_IMAGE_GRAYSCALE); //size 60x60
Mat nu = sample.reshape(1,3600 ).t(); //1x3600
pca_ed_sample = (nu - mean)*(convMat);
I am keeping 5 eigenvectors, so the size of eigenvalues 5x1, convMat3600x5 mean 1x3600. When I am trying to calculate pca_ed_sample it returns me:
cv::Exception at memory location 0x0011d300.Dimensionality reduction using default opencv eigenfaces...
OpenCV Error: Assertion failed (type == B.type() && (type == CV_32FC1 || type ==
CV_64FC1 || type == CV_32FC2 || type == CV_64FC2)) in unknown function, file .\
src\matmul.cpp, line 711`
The problem stands in nu Mat since when I trying to calculate nu*.nu.t();(1x3600* 3600x1) it returns the same issue. Am I having troubles due to reshape function?? I am trying to transform my sample mat to a vector, it seems to work but I cant understand why I cant even multiply nu with nu_transposed.
Matrix multiplication is only possible with floating point data, which is what the assertion error is trying to tell you.
Your image is loaded as type CV_8U, and you must first rescale that to float using the convertTo member.
sample = imread(path, CV_LOAD_IMAGE_GRAYSCALE); //size 60x60
cv::Mat sample_float;
sample.convertTo(sample_float, CV_32F); // <-- Convert to CV_32F for matrix mult
Mat nu = sample_float.reshape(1,3600 ).t(); //1x3600
pca_ed_sample = (nu - mean)*(convMat);

How should I name my RGB channels, using cv::Mat_

I want to access my matrix elements in the following manner:
frame[i][j].Red
, that is, the (i,j)-th pixe's red channel.
I have tried:
typedef struct{unsigned char Blue,Green,Red;}Pixel;
typedef cv::Mat_<Pixel> Image;
However when trying to imread(), imwrite() or whatever with the thus defined type, g++ greets me with:
OpenCV Error: Assertion failed (func != 0) in convertTo, file /home/users/mvitkov/projects/opencv-legacy/OpenCV-2.3.1/modules/core/src/convert.cpp, line 937
terminate called after throwing an instance of 'cv::Exception'
what(): /home/users/mvitkov/projects/opencv-legacy/OpenCV-2.3.1/modules/core/src/convert.cpp:937: error: (-215) func != 0 in function convertTo
Update: So no answer to my probably badly asked question. Too bad. The essence of the question is how to address the individual channels with sensible names (red, green, bkue), and not the c-era array indexing notation [2]. Duh!
Here's how you access each channel:
blue = frame.at<cv::Vec3b>(i,j)[0];
green = frame.at<cv::Vec3b>(i,j)[1];
red = frame.at<cv::Vec3b>(i,j)[2];
The above code assumes that you have a 3-channel image where each value is an 8-bit unsigned char (CV_8UC3). This type is used in many common image formats. However, if you have a different type of 3-channel image, here's what you do:
If the image type is 3-channel float (CV_32FC3), then replace cv::Vec3b with cv::Vec3f
If the image type is 3-channel double (CV_64FC3), then replace cv::Vec3b with cv::Vec3d
If the image type is 3-channel int (CV_32SC3), then replace cv::Vec3b with cv::Vec3i
If the image type is 3-channel short int (CV_16SC3) or 16-bit uchar (CV_16UC3), then replace cv::Vec3b with cv::Vec3s
Not sure what image format you're using? Try calling getImgType(frame) (see the code below).
string getImgType(cv::Mat frame)
{
int imgTypeInt = frame.type();
int numImgTypes = 28; // 7 base types, with 4 channel options each (C1, ..., C4)
int enum_ints[] = {CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4, CV_8SC1, CV_8SC2, CV_8SC3, CV_8SC4, CV_16UC1, CV_16UC2, CV_16UC3, CV_16UC4, CV_16SC1, CV_16SC2, CV_16SC3, CV_16SC4, CV_32SC1, CV_32SC2, CV_32SC3, CV_32SC4, CV_32FC1, CV_32FC2, CV_32FC3, CV_32FC4, CV_64FC1, CV_64FC2, CV_64FC3, CV_64FC4};
string enum_strings[] = {"CV_8U", "CV_8UC1", "CV_8UC2", "CV_8UC3", "CV_8UC4", "CV_8SC1", "CV_8SC2", "CV_8SC3", "CV_8SC4", "CV_16UC1", "CV_16UC2", "CV_16UC3", "CV_16UC4", "CV_16SC1", "CV_16SC2", "CV_16SC3", "CV_16SC4", "CV_32SC1", "CV_32SC2", "CV_32SC3", "CV_32SC4", "CV_32FC1", "CV_32FC2", "CV_32FC3", "CV_32FC4", "CV_64FC1", "CV_64FC2", "CV_64FC3", "CV_64FC4"};
for(int i=0; i<numImgTypes; i++)
{
if(imgTypeInt == enum_ints[i]) return enum_strings[i];
}
return "unknown image type";
}
If the image type is 3-channel short int (CV_16SC3) or 16-bit uchar (CV_16UC3), then replace cv::Vec3b with cv::Vec3s
this is simply not correct. I adressed a 3 channel 16 bit unsigned image once with < Vec3s>, and got a negative value G:29096 B:-21671 R:23413 returned.
If CV_16UC3, adress Mat with < Vec3w>. Using this I got G:29096 B:43865 R:23413
the "s" in CV_16SC3 stands for signed and not short.