Converting point2d to Mat - c++

How can I convert vector<Point2d> to Mat.
Mat newImg = Mat(ImagePoints);
imwrite("E:/softwares/1.8.0.71/bin/newImg.png", newImg);
This is not working since imWrite() will only accept channel 1 or 3 or 4 and the Image Points are 2 channel.
I am using OpenCV version 3.

Here is the answer:
Dont worry about the type casting. Using integers in double. But this is just to give the gist of the solution.
std::vector< cv::Point2d> points;
for(int i =0; i < 10; i++)
{
points.push_back(cv::Point2d(i,i));
}
cv::Mat_<cv::Point2d> matrix(points);
std::cout<<matrix.at<cv::Point2d>(1);
But if you want to save this Mat then use XML. Imwrite wont write the Mat.

Related

Map BGR OpenCV Mat to Eigen Tensor

I'm trying to convert an OpenCV 3-channel Mat to a 3D Eigen Tensor.
So far, I can convert 1-channel grayscale Mat by:
cv::Mat mat = cv::imread("/image/path.png", cv::IMREAD_GRAYSCALE);
Eigen::MatrixXd myMatrix;
cv::cv2eigen(mat, myMatrix);
My attempt to convert a BGR mat to a Tensor have been:
cv::Mat mat = cv::imread("/image/path.png", cv::IMREAD_COLOR);
Eigen::MatrixXd temp;
cv::cv2eigen(mat, temp);
Eigen::Tensor<double, 3> myTensor = Eigen::TensorMap<Eigen::Tensor<double, 3>>(temp.data(), 3, mat.rows, mat.cols);
However, I'm getting the following error :
libc++abi.dylib: terminating with uncaught exception of type cv::Exception: OpenCV(4.1.0) /tmp/opencv-20190505-12101-14vk1fh/opencv-4.1.0/modules/core/src/matrix_wrap.cpp:1195:
error: (-215:Assertion failed) !fixedType() || ((Mat*)obj)->type() == mtype in function 'create'
in the line: cv::cv2eigen(mat, temp);
Any help is appreciated!
The answer might be disappointing for you.
After going through 12 pages, My conclusion is you have to separate the RGB to individual channel MAT and then convert to eigenmatrix. Or create your own Eigen type and opencv convert function
In OpenCV it is tested like this. It only allows a single channel greyscale image
https://github.com/daviddoria/Examples/blob/master/c%2B%2B/OpenCV/ConvertToEigen/ConvertToEigen.cxx
And in OpenCV it is implemented like this. Which dont give you much option for custom type aka cv::scalar to eigen std::vector
https://github.com/stonier/opencv2/blob/master/modules/core/include/opencv2/core/eigen.hpp
And according to this post,
https://stackoverflow.com/questions/32277887/using-eigen-array-of-arrays-for-rgb-images
I think Eigen was not meant to be used in this way (with vectors as
"scalar" types).
they also have the difficulting in dealing with RGB image in eigen.
Take note that Opencv Scalar and eigen Scalar has a different meaning
It is possible to do so if and only if you use your own datatype aka matrix
So you either choose to store the 3 channel info in 3 eigen matrix and you can use default eigen and opencv routing.
Mat src = imread("img.png",CV_LOAD_IMAGE_COLOR); //load image
Mat bgr[3]; //destination array
split(src,bgr);//split source
//Note: OpenCV uses BGR color order
imshow("blue.png",bgr[0]); //blue channel
imshow("green.png",bgr[1]); //green channel
imshow("red.png",bgr[2]); //red channel
Eigen::MatrixXd bm,gm,rm;
cv::cv2eigen(bgr[0], bm);
cv::cv2eigen(bgr[1], gm);
cv::cv2eigen(bgr[2], rm);
Or you can define your own type and write you own version of the opencv cv2eigen function
custom eigen type follow this. and it wont be pretty
https://eigen.tuxfamily.org/dox/TopicCustomizing_CustomScalar.html
https://eigen.tuxfamily.org/dox/TopicNewExpressionType.html
Rewrite your own cv2eigen_custom function similar to this
https://github.com/stonier/opencv2/blob/master/modules/core/include/opencv2/core/eigen.hpp
So good luck.
Edit
Since you need tensor. forget about cv function
Mat image;
image = imread(argv[1], CV_LOAD_IMAGE_COLOR);
Tensor<float, 3> t_3d(image.rows, image.cols, 3);
// t_3d(i, j, k) where i is row j is column and k is channel.
for (int i = 0; i < image.rows; i++)
for (int j = 0; j < image.cols; j++)
{
t_3d(i, j, 0) = (float)image.at<cv::Vec3b>(i,j)[0];
t_3d(i, j, 1) = (float)image.at<cv::Vec3b>(i,j)[1];
t_3d(i, j, 2) = (float)image.at<cv::Vec3b>(i,j)[2];
//cv ref Mat.at<data_Type>(row_num, col_num)
}
watch out for i,j as em not sure about the order. I only write the code based on reference. didnt compile for it.
Also watch out for image type to tensor type cast problem. Some times you might not get what you wanted.
this code should in principle solve your problem
Edit number 2
following the example of this
int storage[128]; // 2 x 4 x 2 x 8 = 128
TensorMap<Tensor<int, 4>> t_4d(storage, 2, 4, 2, 8);
Applied to your case is
cv::Mat frame=imread('myimg.ppm');
TensorMap<Tensor<float, 3>> t_3d(frame.data, image.rows, image.cols, 3);
problem is I'm not sure this will work or not. Even it works, you still have to figure out how the inside data is being organized so that you can get the shape correctly. Good luck
Updated answer - OpenCV now has conversion functions for Eigen::Tensor which will solve your problem. I needed this same functionality too so I made a contribution back to the project for everyone to use. See the documentation here:
https://docs.opencv.org/3.4/d0/daf/group__core__eigen.html
Note: if you want RGB order, you will still need to reorder the channels in OpenCV before converting to Eigen::Tensor

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

opencv vector<point3f> to 3 column mat

I have 2 vectors (p1 and p2) of point3f variables which represent 2 3D pointclouds. In order to match these two point clouds I want to use SVD to find a transformation for this. The problem is that SVD requires a matrix (p1*p2 transpose). My question is how do I convert a vector of size Y to a Yx3 matrix?
I tried cv::Mat p1Matrix(p1) but this gives me a row vector with two dimensions.I also found fitLine but I think this only works for 2D.
Thank you in advance.
How about something like:
cv::Mat p1copy(3, p1.size(), CV_32FC1);
for (size_t i = 0, end = p1.size(); i < end; ++i) {
p1copy.at<float>(0, i) = p1[i].x;
p1copy.at<float>(1, i) = p1[i].y;
p1copy.at<float>(2, i) = p1[i].z;
}
If this gives you the desired result, you can make the code faster by using a pointer instead of the rather slow at<>() function.
I use reshape function for convert vector of points to Mat.
vector<Point3f> P1,P2;
Point3f c1,c2;//center of two set
... //data association for two set
Mat A=Mat(P1).reshape(1).t();
Mat B=Mat(P2).reshape(1).t();
Mat AA,BB,CA,CB;
repeat(Mat(c1),1,P1.size(),CA);
repeat(Mat(c2),1,P2.size(),CB);
AA=A-CA;
BB=B-CB;
Mat H=AA*BB.t();
SVD svd(H);
Mat R_;
transpose(svd.u*svd.vt,R_);
if(determinant(R_)<0)
R_.at<float>(0,2)*=-1,R_.at<float>(1,2)*=-1,R_.at<float>(2,2)*=-1;
Mat t=Mat(c2)-R_*Mat(c1);

PCA project and backproject in Opencv

I am working in Ubuntu Opencv.I am trying to do PCA analysis of a single image.I take the 3 channel image and change it to a single channel image with 3 columns and r*c number of rows.r and c being the rows and columns of the original image.When I try to display the reconstructed image after doing the backprojection on the PCA it gives me a green image.Here is my code
Mat pcaset=cvCreateMat(image->height*image->width,image->nChannels,CV_8UC1);
for(int i=0;i<image->height;i++)
{
for(int j=0;j<image->width;j++)
{
for(int k=0;k<image->nChannels;k++)
(ptrpcaset+i*pcaset.step)[k]=((ptrimage+i*image->widthStep)[3*j+k]);
}
}
int nEigens=3;
Mat databackprojected;
PCA pca(pcaset,Mat(),CV_PCA_DATA_AS_ROW,nEigens);
Mat dataprojected(pcaset.rows,nEigens,CV_8UC1);
pca.project(pcaset,dataprojected);
pca.backProject(dataprojected,databackprojected);
Mat backprojectnorm;//(databackprojected.rows,nEigens,CV_8UC1);
normalize(databackprojected,backprojectnorm,0,255,NORM_MINMAX,-1);
Mat finaldataafterreshaping(image->height,image->width,CV_8UC3);
uchar* finalptr=(uchar*)finaldataafterreshaping.data;
uchar* ptrnorm=(uchar*)backprojectnorm.data;
int x=0,y=0,i=0;
while(i<backprojectnorm.rows)
{
while(x<image->height)
{
while(y<image->width)
{
for(int k=0;k<image->nChannels;k++)
{
(finalptr+x*finaldataafterreshaping.step)[3*y+k]=(ptrnorm+i*backprojectnorm.step)[k];
}
y=y+1;i=i+1;
}
x=x+1;y=0;
}
}
imshow("Reconstructed data",finaldataafterreshaping);
You need to make the following changes:
(ptrpcaset+(j + i*image->width)*pcaset.step)[k]=((ptrimage+i*image->widthStep)[3*j+k]);
because you are not taking the j coordinate into account when you transform your data so that at the end you only save the last line of your image in the new matrix.
When you reshape your data, you need to do something like this:
float* val = (float*)&(ptrnorm+i*backprojectnorm.step)[(k*4)];
(finalptr+x*finaldataafterreshaping.step)[3*y+k]=*val;
because the matrix you get as a result is of type float and not uchar. So you need to some kind of conversion. I am not sure, if it is a good idea to do it this way, but it works. I would suggest that you have a look at the C++ API of OpenCV 2, which can handle this things in a much nicer way.
Also, the whole while(i<backprojectnrom.rows) loop is not needed.

PCA + SVM using C++ Syntax in OpenCV 2.2

I'm having problems getting PCA and Eigenfaces working using the latest C++ syntax with the Mat and PCA classes. The older C syntax took an array of IplImage* as a parameter to perform its processing and the current API only takes a Mat that is formatted by Column or Row. I took the Row approach using the reshape function to fit my image's matrix to fit in a single row. I eventually want to take this data and then use the SVM algorithm to perform detection, but when I do that all my data is just a stream of 0s. Can someone please help me out? What am I doing wrong? Thanks!
I saw this question and it's somewhat related, but I'm not sure what the solution is.
This is basically what I have:
vector<Mat> images; //This variable will be loaded with a set of images to perform PCA on.
Mat values(images.size(), 1, CV_32SC1); //Values are the corresponding values to each of my images.
int nEigens = images.size() - 1; //Number of Eigen Vectors.
//Load the images into a Matrix
Mat desc_mat(images.size(), images[0].rows * images[0].cols, CV_32FC1);
for (int i=0; i<images.size(); i++) {
desc_mat.row(i) = images[i].reshape(1, 1);
}
Mat average;
PCA pca(desc_mat, average, CV_PCA_DATA_AS_ROW, nEigens);
Mat data(desc_mat.rows, nEigens, CV_32FC1); //This Mat will contain all the Eigenfaces that will be used later with SVM for detection
//Project the images onto the PCA subspace
for(int i=0; i<images.size(); i++) {
Mat projectedMat(1, nEigens, CV_32FC1);
pca.project(desc_mat.row(i), projectedMat);
data.row(i) = projectedMat.row(0);
}
CvMat d1 = (CvMat)data;
CvMat d2 = (CvMat)values;
CvSVM svm;
svm.train(&d1, &d2);
svm.save("svmdata.xml");
What etarion said is correct.
To copy a column or row you always have to write:
Mat B = mat.col(i);
A.copyTo(B);
The following program shows how to perform a PCA in OpenCV. It'll show the mean image and the first three Eigenfaces. The images I used in there are available from http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html:
#include "cv.h"
#include "highgui.h"
using namespace std;
using namespace cv;
Mat normalize(const Mat& src) {
Mat srcnorm;
normalize(src, srcnorm, 0, 255, NORM_MINMAX, CV_8UC1);
return srcnorm;
}
int main(int argc, char *argv[]) {
vector<Mat> db;
// load greyscale images (these are from http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html)
db.push_back(imread("s1/1.pgm",0));
db.push_back(imread("s1/2.pgm",0));
db.push_back(imread("s1/3.pgm",0));
db.push_back(imread("s2/1.pgm",0));
db.push_back(imread("s2/2.pgm",0));
db.push_back(imread("s2/3.pgm",0));
db.push_back(imread("s3/1.pgm",0));
db.push_back(imread("s3/2.pgm",0));
db.push_back(imread("s3/3.pgm",0));
db.push_back(imread("s4/1.pgm",0));
db.push_back(imread("s4/2.pgm",0));
db.push_back(imread("s4/3.pgm",0));
int total = db[0].rows * db[0].cols;
// build matrix (column)
Mat mat(total, db.size(), CV_32FC1);
for(int i = 0; i < db.size(); i++) {
Mat X = mat.col(i);
db[i].reshape(1, total).col(0).convertTo(X, CV_32FC1, 1/255.);
}
// Change to the number of principal components you want:
int numPrincipalComponents = 12;
// Do the PCA:
PCA pca(mat, Mat(), CV_PCA_DATA_AS_COL, numPrincipalComponents);
// Create the Windows:
namedWindow("avg", 1);
namedWindow("pc1", 1);
namedWindow("pc2", 1);
namedWindow("pc3", 1);
// Mean face:
imshow("avg", pca.mean.reshape(1, db[0].rows));
// First three eigenfaces:
imshow("pc1", normalize(pca.eigenvectors.row(0)).reshape(1, db[0].rows));
imshow("pc2", normalize(pca.eigenvectors.row(1)).reshape(1, db[0].rows));
imshow("pc3", normalize(pca.eigenvectors.row(2)).reshape(1, db[0].rows));
// Show the windows:
waitKey(0);
}
and if you want to build the matrix by row (like in your original question above) use this instead:
// build matrix
Mat mat(db.size(), total, CV_32FC1);
for(int i = 0; i < db.size(); i++) {
Mat X = mat.row(i);
db[i].reshape(1, 1).row(0).convertTo(X, CV_32FC1, 1/255.);
}
and set the flag in the PCA to:
CV_PCA_DATA_AS_ROW
Regarding machine learning. I wrote a document on machine learning with the OpenCV C++ API that has examples for most of the classifiers, including Support Vector Machines. Maybe you can get some inspiration there: http://www.bytefish.de/pdf/machinelearning.pdf.
data.row(i) = projectedMat.row(0);
This will not work. operator= is a shallow copy, meaning no data is actually copied. Use
cv::Mat sample = data.row(i); // also a shallow copy, points to old data!
projectedMat.row(0).copyTo(sample);
The same also for:
desc_mat.row(i) = images[i].reshape(1, 1);
I would suggest looking at the newly checked in tests in svn head
modules/core/test/test_mat.cpp
online here : https://code.ros.org/svn/opencv/trunk/opencv/modules/core/test/test_mat.cpp
has examples for PCA in the old c and new c++
Hope that helps!