Jan Hackenberg Jan Hackenberg - 1 month ago 30
C++ Question

Access to CV_32SC1 nx1 opencv matrix

I have an artificial matrix which I pass to the EM Gaussian Mixture Model algorithm in OpenCV - version 3.0.0 of the form :

[1.1, 3.2;
1.15, 3.1500001;
3.0999999, 4.1999998;
3.2, 4.3000002;
5, 5]


I call the GMM prediction via:

cv::Ptr<cv::ml::EM> source_model = cv::ml::EM::create();
source_model->setClustersNumber(3);
cv::Mat logs;
cv::Mat labels;
cv::Mat probs;
source_model->trainEM( openCVPointCloud,logs,labels,probs)


The documentation states about the matrix labels which I am interested in:


labels – The optional output 'class label' for each sample:
labels_i = {arg max}k(p{i,k}), i=1..N (indices of the most probable mixture component for each sample). It has nsamples x 1 size and 'CV_32SC1' type.


My non working access to 'labels' prints (0,0,0,0,0) instead of expected (0,0,1,1,2) which is plotted via

std::cout << labels <<std::endl;


. I need though working with the integer indices to work with my original PCL point cloud which I want to cluster via poitn cloud features:

std::cout << labels.data[0] << std::endl;
std::cout << labels.data[1] << std::endl;
std::cout << labels.data[2] << std::endl;
std::cout << labels.data[3] << std::endl;
std::cout << labels.data[4] << std::endl;


The code fragments wrapped together in an untested minimal example (I have problems with qmake without using my frame work):

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/ml.hpp>

int main()
{
cv::Mat openCVPointCloud(5, 2, CV_32FC(1));
{
cv::Vec<float,1> & values1 = openCVPointCloud.at<cv::Vec<float,1> >(0,0);
values1.val[0] = 1.1;

cv::Vec<float,1> & values2 = openCVPointCloud.at<cv::Vec<float,1> >(0,1);
values2.val[0] = 3.2;
}

{
cv::Vec<float,1> & values1 = openCVPointCloud.at<cv::Vec<float,1> >(1,0);
values1.val[0] = 1.15;

cv::Vec<float,1> & values2 = openCVPointCloud.at<cv::Vec<float,1> >(1,1);
values2.val[0] = 3.15;
}

{
cv::Vec<float,1> & values1 = openCVPointCloud.at<cv::Vec<float,1> >(2,0);
values1.val[0] = 3.1;

cv::Vec<float,1> & values2 = openCVPointCloud.at<cv::Vec<float,1> >(2,1);
values2.val[0] = 4.2;
}

{
cv::Vec<float,1> & values1 = openCVPointCloud.at<cv::Vec<float,1> >(3,0);
values1.val[0] = 3.2;

cv::Vec<float,1> & values2 = openCVPointCloud.at<cv::Vec<float,1> >(3,1);
values2.val[0] = 4.3;
}

{
cv::Vec<float,1> & values1 = openCVPointCloud.at<cv::Vec<float,1> >(4,0);
values1.val[0] = 5;

cv::Vec<float,1> & values2 = openCVPointCloud.at<cv::Vec<float,1> >(4,1);
values2.val[0] = 5;
}

std::cout << openCVPointCloud << std::endl;

cv::Ptr<cv::ml::EM> source_model = cv::ml::EM::create();
source_model->setClustersNumber(3);
cv::Mat logs;
cv::Mat labels;
cv::Mat probs;
if(source_model->trainEM( openCVPointCloud,logs,labels,probs))
{
std::cout << "true train em";
std::cout << labels.data[0] << std::endl;
std::cout << labels.data[1] << std::endl;
std::cout << labels.data[2] << std::endl;
std::cout << labels.data[3] << std::endl;
std::cout << labels.data[4] << std::endl;
} else {
std::cout <<"false train em" << std::endl;
}

}


What can I do to get access to the integer numbers stored in labels?

This stackexchange topic states, that if I know the matrix element type I can use the templated at() function. The api states that the label Matrix is of type $CV_32SC1$. Accessing now via:

std::cout << labels.at<CV_32SC1>(2,0) << std::endl;


Results in the following error:

invalid template argument for '_Tp', type expected


At the time of creation of this question I was also 100% sure I tested

std::cout << labels.at<int>(2,0) << std::endl;


also which plotted 0 (and should have been 1). Code in front of me after accepted answer adaptian though prooves me wrong. Might be a duplicate because of a typo I did not see for some hours and the "typo" might have been QT's qdebug() usage instead of std::contcontainer. If still considered valuable someone might improve the constructor in the minimal example I provided and remove this sentence and the following. I still hope for a one line solution which I yet failed to perform.

Answer

Proper way to print Mat value is to use operator<<. You already have it.

Your cv::Mat labels has type CV_32SC1. It contain 32 bit signed integer elements. So you can access items by labels.at<int> method.

Better way to access items is to use iterator cv::MatIterator_< _Tp >:

for(cv::MatIterator_<int> it(labels.begin<int>()); it != labels.end<int>(); it++)
{
  std::cout << (*it) << std::endl; // int i = *it
}