Copy one OpenCV OutputArrayOfArrays object to another - c++

I want to copy one OutputArrayOfArrays object to another. Something like this:
void function(OutputArrayOfArrays contour) {
std::vector<std::vector<cv::Point>> contours;
OutputArrayOfArrays _contour(contours);
....Doing something....
contour = _contour;
}
But I'm getting following error:
no viable overloaded '='
contour = _contour;

I do not think you can do it for varying types. You will have to handle types specifically. Take a look at functions that use this type in the opencv sources and you will see they are handled in specific ways: for example, findContours and split functions.
For your specific case of std::vector<std::vector<cv::Point>>, you can follow the findContours way of doing it. Below I have written a simple function to demonstrate this.
void testfunction(OutputArrayOfArrays contour) {
std::vector<std::vector<cv::Point>> contours;
// fill some data
std::vector<cv::Point> v1;
v1.push_back(cv::Point(10, 1));
v1.push_back(cv::Point(11, 2));
v1.push_back(cv::Point(12, 3));
std::vector<cv::Point> v2;
v2.push_back(cv::Point(20, 10));
v2.push_back(cv::Point(21, 20));
contours.push_back(v1);
contours.push_back(v2);
// output
contour.create(contours.size(), 1, 0, -1, true);
for (size_t i = 0; i < contours.size(); i++) {
std::vector<cv::Point>& v = contours[i];
contour.create(v.size(), 1, CV_32SC2, i, true);
Mat m = contour.getMat(i);
for (size_t j = 0; j < v.size(); j++) {
m.at<int>(2*j) = v[j].x;
m.at<int>(2*j+1) = v[j].y;
}
std::cout << m << std::endl;
}
}
And the output looks like
[10, 1, 11, 2, 12, 3]
[20, 10, 21, 20]
2
3
[10, 1]
[11, 2]
[12, 3]
2
[20, 10]
[21, 20]
using the code
std::vector<std::vector<cv::Point>> contours;
testfunction(contours);
std::cout << contours.size() << std::endl;
for (size_t i = 0; i < contours.size(); i++) {
std::vector<cv::Point>& cont = contours[i];
std::cout << cont.size() << std::endl;
for (size_t j = 0; j < cont.size(); j++) {
std::cout << cont[j] << std::endl;
}
}

Related

Find connected components with OpenCV based on existed labeling, but not binary image

OpenCV has a function to find connected components on a binary image:(cv::connectedComponents()), but it doesn't account for existed labels. What is the proper way to find connected components only within pixels with the same labels?
For example, I have the code:
Mat test = Mat::zeros(1, 4, DataType<int>::type);
test.at<int>(0, 0) = 1;
test.at<int>(0, 1) = 2;
test.at<int>(0, 2) = 0;
test.at<int>(0, 3) = 1;
test.convertTo(test, CV_8U);
connectedComponents(test, test);
std::cout << test << std::endl;
It has input matrix [1, 2, 0, 1], and labels it as [1, 1, 0, 2]. But I want to get [1, 2, 0, 3]. Is there a way to do it with OpenCV?
My solution for the problem:
Mat connected_components(const Mat &labels)
{
Mat res, input;
labels.convertTo(input, CV_8U);
connectedComponents(input, res);
res.convertTo(res, DataType<int>::type);
double n_labels;
minMaxLoc(res, nullptr, &n_labels);
res += labels * (n_labels + 1);
std::map<int, int> new_ids;
for (int row = 0; row < labels.rows; ++row)
{
auto row_res_data = res.ptr<int>(row);
for (int col = 0; col < labels.cols; ++col)
{
auto cur_lab = row_res_data[col];
if (cur_lab == 0)
continue;
auto iter = new_ids.emplace(cur_lab, new_ids.size() + 1);
row_res_data[col] = iter.first->second;
}
}
return res;
}

How to make the function can process different type image in OpenCV

Cross post here
I have build two function with different name to drop the specfiy lines from difference Mat object, this is the code:
Mat drop_rows_int(Mat mat, vector<int> v) {
Mat mat_new = Mat::zeros(mat.rows - v.size(), mat.cols, CV_32SC1);
for (int i = 0, j = 0; i < mat.rows; i++) {
if (find(v.begin(), v.end(), i) != v.end())
{
continue;
}
else
{
int*pmat = mat.ptr<int>(i);
int*pmat_new = mat_new.ptr<int>(j);
for (int w = 0; w < mat.cols; w++) {
pmat_new[w] = pmat[w];
}
j++;
}
}
return mat_new;
}
Mat drop_rows_uchar(Mat mat, vector<int> v) {
Mat mat_new = Mat::zeros(mat.rows - v.size(), mat.cols, CV_8UC1);
for (int i = 0, j = 0; i < mat.rows; i++) {
if (find(v.begin(), v.end(), i) != v.end())
{
continue;
}
else
{
uchar*pmat = mat.ptr<uchar>(i);
uchar*pmat_new = mat_new.ptr<uchar>(j);
for (int w = 0; w < mat.cols; w++) {
pmat_new[w] = pmat[w];
}
j++;
}
}
return mat_new;
}
Then I can use it in my main() function like
int main()
{
Mat mat_uchar = (Mat_<uchar>(5, 4) << 5, 6, 0, 4, 0, 1, 9, 9, 100, 3, 5, 8, 200, 33, 1, 4, 8, 88, 23, 6);
Mat new_mat_uchar = drop_rows_uchar(mat_uchar, {2,4});
Mat mat_int = (Mat_<int>(5, 4) << 5, 6, 0, 4, 0, 1, 9, 9, 100, 3, 5, 8, 200, 33, 1, 4, 8, 88, 23, 6);
Mat new_mat_int = drop_rows_int(mat_int, { 2,4 });
return 0;
}
Yes, I made it. but as I know, the Mat can have 7 kinds of depth, such as CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F and CV_64F, So I have to build 7 functions with different name to do such thing?? Can anyone tell me how to use one function to implement it??
You cannot do that with cv::Mat. However, you can use cv::Mat_ and do some templating:
template<typename T>
cv::Mat_<T> drop_rows_int(cv::Mat_ mat, vector<int> v) {
...
}
And here you extract pointers of type T.
Just a piece of advice, for efficiency purposes I suggest sending the vector v as a const reference, if possible.
Here is the full solution:
#include "opencv/cv.h"
#include <vector>
#include <iostream>
template<typename T>
cv::Mat_<T> drop_rows(cv::Mat_<T> mat, const std::vector<int> &v) {
cv::Mat_<T> mat_new = cv::Mat_<T>::zeros(mat.rows - v.size(), mat.cols);
for (int i = 0, j = 0; i < mat.rows; i++) {
if (find(v.begin(), v.end(), i) != v.end())
continue;
else {
for (int w = 0; w < mat.cols; w++) {
mat_new(j, w) = mat(i, w);
}
j++;
}
}
return mat_new;
}
int main() {
cv::Mat_<uchar> mat = (cv::Mat_<uchar>(5, 4) << 5, 6, 0, 4, 0, 1, 9, 9, 100, 3, 5, 8, 200, 1, 2, 3, 4, 5, 6, 7);
auto result = drop_rows(mat, {2, 4});
std::cout << mat << std::endl;;
std::cout << result << std::endl;;
return 0;
}
Note that it works only for Mat_, not for Mat.

Understanding of planes in NAryMatIterator

I have 3-dimension matrix:
const int n_mat_size = 5;
const int n_mat_sz[] = { n_mat_size , n_mat_size, n_mat_size };
cv::Mat m1(3, n_mat_sz, CV_32FC1);
Now I'd like to iterate its planes and expect that it should be three two-dimensional matrices:
const cv::Mat* arrays[] = { &m1, 0 };
cv::Mat planes[3];
cv::NAryMatIterator it(arrays, planes);
std::cout << it.nplanes << ", " << it.planes[0].rows << ", " << it.planes[0].cols;
I expect to get output "3, 5, 5", but instead I get "1, 1, 125". Where is the slice of matrix?
Because the matrix m1 is continuous, there is only one plane (or slice).
Please refer to the documentation for NAryMatIterator:
It iterates through the slices (or planes), not the elements, where "slice" is a continuous part of the arrays.
For example, the matrix m2 in the following code is not continuous:
const int n_mat_size = 5;
const int n_mat_sz[] = { n_mat_size , n_mat_size, n_mat_size };
cv::Mat m1(3, n_mat_sz, CV_32FC1);
// Get plane 2 and 3 of m1
// and row 2, row 3 and row 4 of every selected plane
// m2 is not continuous
cv::Mat m2 = m1(cv::Range(2,4), cv::Range(2,5));
const cv::Mat* arrays[] = { &m2, 0 };
cv::Mat planes[3];
cv::NAryMatIterator it(arrays, planes);
std::cout << it.nplanes << ", " << it.planes[0].rows << ", " << it.planes[0].cols << std::end;
The output of the above code is: 2, 1, 15.
Note that the number of rows of every plane is always 1 and the number of columns is the number of elements contained in the plane.
There is a statement:
planes[i] = Mat(1, (int)size, A.type(), A.data);
in the function void NAryMatIterator::init, which can be found at https://github.com/opencv/opencv/blob/master/modules/core/src/matrix.cpp#L4596 .
The above statement sets the size of the plane.
To separate the matrix you gave into planes, you could use cv::InputArray::getMatVector.
The following code shows it usage.
int main()
{
const int n_mat_size = 3;
const int n_mat_sz[] = { n_mat_size , n_mat_size, n_mat_size };
cv::Mat m1(3, n_mat_sz, CV_8U);
cv::MatIterator_<uchar> it = m1.begin<uchar>();
cv::MatIterator_<uchar> end = m1.end<uchar>();
for (uchar i = 0; it != end; ++it, ++i)
{
*it = i;
}
cv::InputArray arr(m1);
std::vector<cv::Mat> planes;
arr.getMatVector(planes);
for (size_t i = 0; i < planes.size(); ++i)
{
std::cout << "-------" << std::endl
<< planes[i] << std::endl << "******" << std::endl;
}
}
Its output is as follows:
-------
[ 0, 1, 2;
3, 4, 5;
6, 7, 8]
******
-------
[ 9, 10, 11;
12, 13, 14;
15, 16, 17]
******
-------
[ 18, 19, 20;
21, 22, 23;
24, 25, 26]
******
Maybe the easiest one is to use the method cv::Mat::row(int). The corresponding code is:
int main()
{
const int n_mat_size = 3;
const int n_mat_sz[] = { n_mat_size , n_mat_size, n_mat_size };
cv::Mat m1(3, n_mat_sz, CV_8U);
cv::MatIterator_<uchar> it = m1.begin<uchar>();
cv::MatIterator_<uchar> end = m1.end<uchar>();
for (uchar i = 0; it != end; ++it, ++i)
{
*it = i;
}
int n = m1.size[0];
for (int i = 0; i < n; ++i)
{
cv::Mat three_d_plane = m1.row(i);
// three_d_plane has a size 1x3x3
// std::cout supports only 2-d matrix. Therefore, we change it to 2-d here
cv::Mat two_d_plane(three_d_plane.size[1], three_d_plane.size[2], three_d_plane.type(), three_d_plane.data);
std::cout << two_d_plane << std::endl << "----" << std::endl;
}
}
The output is
[ 0, 1, 2;
3, 4, 5;
6, 7, 8]
----
[ 9, 10, 11;
12, 13, 14;
15, 16, 17]
----
[ 18, 19, 20;
21, 22, 23;
24, 25, 26]
----

SVM training issue for a simple dataset (Opencv 2.4.9)

I'm trying a simple example to learn SVM in OpenCV, I'm not getting the right support vectors after training. Need some help in understanding the issue.
My code is :
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>
using namespace cv;
using namespace std;
int main() {
Mat frame(Size(640,360), CV_8UC3, Scalar::all(255));
float train[15][2] = { {296, 296}, {296, 312}, {312, 8}, {312, 56}, {312, 88}, {328, 88}, {328, 104}, {328, 264}, {344, 8}, {344, 40}, {360, 8}, {360, 56}, {376, 8}, {376, 40}, {376, 56} };
Mat trainingDataMat(15, 2, CV_32FC1, train);
float labels[15] = { -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1 };
Mat labelsMat(15, 1, CV_32FC1, labels);
CvSVMParams param;
param.svm_type = CvSVM::C_SVC;
param.C = 0.1;
param.kernel_type = SVM::LINEAR;
param.term_crit = TermCriteria(CV_TERMCRIT_ITER, 1000, 1e-6);
CvSVM SVM;
SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), param);
cout<< "Training Finished..." << endl;
for(int i = 0; i < frame.rows; ++i) {
for(int j = 0; j < frame.cols; ++j) {
Mat sampleMat = (Mat_<float>(1,2) << i,j);
float response = SVM.predict(sampleMat);
//cout << response << endl;
if(response == 1) {
frame.at<Vec3b>(i,j)[2] = 0;
} else {
frame.at<Vec3b>(i,j)[0] = 0;
}
}
}
for(int dis = 0; dis < trainingDataMat.rows; dis++) {
if(labels[dis] == 1) {
circle(frame, Point((int)train[dis][0], (int)train[dis][1]), 3, Scalar (0, 0, 0), -1);
} else {
circle(frame, Point((int)train[dis][0], (int)train[dis][1]), 3, Scalar (0, 255, 0), -1);
}
}
int n = SVM.get_support_vector_count();
for(int i = 0; i < n; i++) {
const float* v = SVM.get_support_vector(i);
cout << "support Vectors : " << v[0] << " " << v[1] <<endl;
circle(frame,Point((int)v[0], (int)v[1]), 6, Scalar(128, 128, 128), 2, 8);
}
imwrite("frame.jpg",frame);
imshow("output", frame);
waitKey(0);
return 0;
}
Output image is attached
The SVM line is not separating the two classes as I expect.
Result for Support Vector is
support Vectors : 0 0.0125
The SVM should be OK. I think the problem lies in your display. When you call your circle(frame, Point((int)train[dis][0], (int)train[dis][1]), 3, Scalar (0, 0, 0), -1);, OpenCV understands that you want a circle in row number train[dis][1] and column number train[dis][0]. This is not what you want because a specificity of OpenCV is that it uses different coordinate systems for matrices and points. image.at<float>(Point(i,j)) is equivalent to image.at<float>(j,i).
Try replacing your circle calls with this:
if(labels[dis] == 1) {
circle(frame, Point((int)train[dis][1], (int)train[dis][0]), 3, Scalar (0, 0, 0), -1);
} else {
circle(frame, Point((int)train[dis][1], (int)train[dis][0]), 3, Scalar (0, 255, 0), -1);
}

OpenCV CVTrees : double free or corruption on Eclipse

I have a class and there is a member vector<cvtrees*> vect. I generate many cvtrees object and push on vect. I use this function for train:
Mat trainingDataMat(trainSize, featureSize, CV_32FC1);
// fill trainingDataMat
for(int i = 0; i < LOOP; i++) {
Mat labelMat(trainSize, 1, CV_32FC1);
// fill labelMat
// learn classifier
CvRTrees *rtrees = new CvRTrees();
(*rtrees).train( trainingDataMat, CV_ROW_SAMPLE, labelMat, Mat(), Mat(), Mat(), Mat(), CvRTParams());
this->rtreesVector.push_back(rtrees);
}
And I use a function for predict. When I run below code, I get an error no source.
Mat testSample(1, featureSize, CV_32FC1);
for(int k = 0; k < featureSize; k++) {
testSample.at<float>(k) = (float)this->trainInvoiceVector[i]->at(j,k);
}
for(int i = 0; i < this->rtreesVector.size(); i++) {
int response = (int)((*(this->rtreesVector[i])).predict( testSample )); // !!!! THIS LINE IS THE PROBLEM
cout << "response" << response << endl;
}