I am having an issue when I want to combine two computer graphic libraries, namely OpenCV and Vigra. I want to use OpenCVs k-means clustering algorithm for grayscale image binarization. The framework of my image processing was built earlier and depends strongly on Vigra, that's why I have to combine both libraries.
So basically, I am loading the image employing Vigra functionality, than convert the Vigra object to an OpenCV matrix, run the k-means clustering, re-convert the matrix object to a vigra object and finally save the image again by employing Vigra functionality. Here is a code example:
#include <vigra/impex.hxx>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
int main()
{
std::string InputFilePath = "path/to/image/image_name.tif";
// Vigra functionality to load an image from path
vigra::FImage InputImg;
const char* cFile = InputFilePath.c_str();
vigra::ImageImportInfo info(cFile);
int b = info.width();
int h = info.height();
InputImg.resize(b,h);
vigra::importImage(info, destImage(InputImg));
vigra::FImage OutputImg(InputImg.width(), InputImg.height());
// Setting up OCV Matrix as an one channel, 32bit float grayscale image
cv::Mat InputMat(InputImg.width(), InputImg.height(), CV_32FC1);
// my workaround to convert vigra::FImage to cv::Mat
for(unsigned int i=0; i<InputImg.width(); i++){
for(unsigned int j=0; j<InputImg.height(); j++){
InputMat.at<float>(j,i) = InputImg(i,j);
}
}
// OCVs k-means clustering
const unsigned int singleLineSize = InputMat.rows*InputMat.cols;
const unsigned int k=2;
cv::Mat data = InputMat.reshape(1, singleLineSize);
std::vector<int> labels;
data.convertTo(data, CV_32FC1);
cv::Mat1f centers;
cv::kmeans(data, k, labels, cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 10, 1.0), 2, cv::KMEANS_RANDOM_CENTERS, centers);
for (unsigned int i = 0; i < singleLineSize; i++) {
data.at<float>(i) = centers(labels[i]);
}
cv::Mat OutputMat = data.reshape(1, InputMat.rows);
OutputMat.convertTo(OutputMat, CV_8UC1);
// re-convert cv::Mat to vigra::FImage
for(unsigned int i=0; i<InputImg.width(); i++){
for(unsigned int j=0; j<InputImg.height(); j++){
OutputImg(i,j) = OutputMat.at<float>(j,i);
}
}
std::string SaveFileName = "path/to/save_location/save_img_name.tif";
// vigra functionality to save the image
const char* cFile = SaveFileName.c_str();
vigra::ImageExportInfo exinfo(cFile);
vigra::exportImage(srcImageRange(OutputImg), exinfo.setPixelType("FLOAT")); // pixel type could also be "UINT8"
// for the sake of comparability
std::string SaveFileNameOCV = "path/to/save_location/save_mat_name.tif";
cv::imwrite(SaveFileNameOCV, OutputMat);
return 0;
}
k-means clustering works fine, and when I save the cv::Mat directly with
cv::imwrite()
everything is good. But when I re-convert the cv::Mat to a vigra::FImage object and save it, the image is corrupted. It looks as if the object (in the image) is mirrored or duplicated four times, allthough image width and heigth stay the same. I attached the images (InputImg, OutputImg and OutputMat).
Moreover, if I re-convert InputMat to OutputImg (after the k-means), and save this image, everything is fine (this image is also attached).
And finally, I do not understand why I have to switch the indices when converting from vigra::FImage to cv::Mat and vice versa:
InputMat.at<float>(j,i) = InputImg(i,j);
But if I don't, the resulting image is rotated.
Ok, so I am not quite sure if anybody uses Vigra AND OpenCV, I guess OpenCV is definitly more common than Vigra. But anyway, if anybody could help, this would be great.
BTW: I am running everything in Code::Blocks on OpenSuSE 15.1. Any library was installed via the official OpenSuSE repositories.
OK, first of all, I did not found out, why this happens. But what I now know, is that, if I am using the type-defined version Mat_ (e.g. Mat1f...) I can handle everything propperly and the saved results are as expected.
For conversion I wrote 2 methods:
cv::Mat1f convertImg2Mat(vigra::FImage &img){
int b = img.width();
int h = img.height();
cv::Mat1f mat(h, b);
for(unsigned int j=0; j<h; j++){
for(unsigned int i=0; i<b; i++){
mat(j,i) = img(i,j);
}
}
return mat;
}
and
vigra::FImage convertMat2Img(cv::Mat mat){
int b = mat.rows;
int h = mat.cols;
cv::Mat1f tmp = mat.clone();
vigra::FImage img(h, b);
for(unsigned int j=0; j<h; j++){
for(unsigned int i=0; i<b; i++){
img(i,j) = tmp(j,i);
}
}
return img;
}
which both work fine.
A stupid beginners mistake was the indexing, because vigra follows fortran order which is
img(cols, rows)
and OpenCV uses another convention which is
mat(rows, cols).
So from my side this question is not yet propperly answered, but I found a working solution anyways.
Related
I have a cv::Mat of a RGB image as
cv::Mat cv_img
I want to set zeros value for cv_img at some positions. For example from the bottom to the half location of the image will be filled by zero values. How can I do it in c++ and opencv? Thanks all.
I have searched a setTo function and mask may be a candidate solution, but how to define a binary mask is difficult for me.
cv_img.setTo(Scalar(0,0,0), mask);
You can achieve it by setting the pixels with a desired value. Just define the intervals of roi(region of interest.
Here is a simple code to guide:
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat img = imread("/ur/img/dir/img.jpg");
for(int i=img.rows/2; i<img.rows;i++)
{
for(int j=0; j<img.cols; j++)
{
img.at<Vec3b>(Point(j,i))[0] = 0;
img.at<Vec3b>(Point(j,i))[1] = 0;
img.at<Vec3b>(Point(j,i))[2] = 0;
}
}
imshow("Result",img);
waitKey(0);
return 0;
}
You can try this:
int w = cv_img.cols;
int h = cv_img.rows;
cv::Rect rectZero(0, h/2, w, h/2);
cv_img(rectZero) = cv::Scalar(0,0,0);
I am trying to build a distance matrix between frames in C++ with OpenCv 2.4.10. I think I need a mat of mats so I can put in the first row and col all the frames and make a XOR operator frame by frame. But to do so I need a structure like a matrix that contains in each position another matrix. Is there a thing like a Mat of Mats? Or can you suggest another solution? I thought of useing Vector but I need more than an array of Mat. Thank you I am new at this!
If I got it correctly, what you are looking for is a 2-dimensional Mat object, whose each element is another 2-dimensional Mat object. This is equivalent to creating a 4-dimensional Mat object. OpenCV has such a functionality - it just involves using one of less popular and less convenient Mat constructors:
const int num_of_dim = 4;
const int dimensions[num_of_dim] = { a, b, c, d }; // a, b, c, d - desired dimensions defined elsewhere
cv::Mat fourd_mat(num_of_dim, dimensions, CV_32F);
Check Mat::Mat(int ndims, const int* sizes, int type) constructor at openCV docs:
http://docs.opencv.org/2.4.10/modules/core/doc/basic_structures.html#Mat::Mat(int%20ndims,%20const%20int*%20sizes,%20int%20type)
as well as search for the phrase "multi-dimensional" and "n-dimensional" on that page to find more examples and docs.
EDIT:
As requested, I'm showing how to load an image into such a structure. It's not pretty, but I guess the easiest way is to copy the image pixel by pixel:
img = imread("path/img.jpg", 1);
for (int i = 0; i < 179; ++i)
{
for (int j = 0; i < img.rows; ++i)
{
for (int k = 0; j < img.cols; ++j)
{
const int coords1[4] = { i, 0, j, k };
const int coords2[4] = { 0, i, j, k };
fourd_mat.at<float>(coords1) = img.at<float>(j, k); //line 1
fourd_mat.at<float>(coords2) = img.at<float>(j, k); //line 2
}
}
}
The line commented as line1 is equivalent to your line struttura[i][0] = img; and line2 is equivalent to struttura[0][i] = img; after the two innermost for loops finish their work.
The code above assumes that your image type is CV_32F - if it's 8UC, you have to replace float with uchar in at() function.
I am trying to understand the following piece of code Take from: Opencv Mat
and more precisely this part:
Mat labels(0, 1, CV_32FC1);
Mat trainingData(0, dictionarySize, CV_32FC1);
From what I understand is that labels is equivalent to std::vector<float> and trainingData is equivalent to std::vector<std::vector<float>> and where std::vector<float> is of dimension dictionarySize. Is that correct?
I am asking this question because I want to convert bowDescriptor1 which is a MAT to std::vector<float>
Convert bowDescriptor1to vector:
std::vector<float> data;
for(size_t r = 0; r < bowDescriptor.rows;r++)
{
for(size_t c = 0; c < bowDescriptor.cols;c++)
{
data.push_back(bowDescriptor.at<float>(r,c));
}
}
Without testing:
from documentation you can see that bowDescriptor seems to be a matrix of size 1 x dictionarySize http://docs.opencv.org/modules/features2d/doc/object_categorization.html#bowimgdescriptorextractor-descriptorsize
so you have to go through that matrix and save each element (float) to your vector<float>
try this code:
std::vector<float> currentBowDescriptor;
for(int col = 0; col < bowDescriptor1.cols; ++col)
{
currentBowDescriptor.push_back(bowDescriptor.at<float>(0,col));
}
that's it. push_back those currentBowDescriptor s to another vector if you want.
If you want to save some computation time, you can even initialize the currentBowDescriptor in advance since you know the number of descriptor values (dictionarySize) and access those elements instead of pushing back.
hope this helps.
I'm working on a face recognition project and I am having problems when projecting on PCA subspace.
When I pass a mat vector to my funcion with the resized images, I project them, and then I reconstruct them to verify it's working well, but all I have in "Cam" window is a grey image (all same color).
I don't know what I am doing bad.
This is the function:
void doPCA (const vector<Mat>& images)
{
int nEigens = images.size()-1;
Mat data (images.size(), images[0].rows*images[0].cols, images[0].type() );
for (int i = 0; i < images.size(); i++)
{
Mat aux = data.row(i);
images[i].reshape(1,1).copyTo(aux);
}
PCA pca(data,Mat(),CV_PCA_DATA_AS_ROW,nEigens);
//Project images
Mat dataprojected(data.rows, nEigens, CV_32FC1) ;
for(int i=0; i<images.size(); i++)
{
pca.project(data.row(i), dataprojected.row(i));
}
//Backproject to reconstruct images
Mat datareconstructed (data.rows, data.cols, data.type());
for(int i=0; i<images.size(); i++)
{
pca.backProject (dataprojected.row(i), datareconstructed.row(i) );
}
for(int i=0; i<images.size(); i++)
{
imshow ("Cam", datareconstructed.row(i).reshape(1,images[0].rows) );
waitKey();
}
}
I think this post is a duplicate of:
PCA + SVM using C++ Syntax in OpenCV 2.2
Ah, I have found the error in your code. When you create the data matrix you do:
images[i].reshape(1,1).copyTo(aux);
You have to use convertTo to convert the data into the correct type and copy it to your data matrix:
images[i].reshape(1,1).convertTo(aux, CV_32FC1, 1/255.);
Then the normalized eigenvectors should be ok. And don't forget to to normalize the values between 0 and 255 before displaying them, you can use cv::normalize to do this, here's a simple function for turning it into grayscale:
Mat toGrayscale(const Mat& src) {
Mat srcnorm;
cv::normalize(src, srcnorm, 0, 255, NORM_MINMAX, CV_8UC1);
return srcnorm;
}
You may want to look at the example in my blog:
http://bytefish.de/blog/pca_in_opencv#simple_example
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!