Convert every column of an Eigen::Matrix to an std::vector? - c++

Lets assume I have the following Eigen::Matrix:
Eigen::MatrixXf mat(3, 4);
mat << 1.1, 2, 3, 50,
2.2, 2, 3, 50,
3.1, 2, 3, 50;
Now how can I convert every column into an std::vector<float>
I tried an adaptation of this solution typecasting Eigen::VectorXd to std::vector:
std::vector<float> vec;
vec.resize(mat.rows());
for(int col=0; col<mat.cols(); col++){
Eigen::MatrixXf::Map(&vec[0], mat.rows());
}
But that throws the following error:
n template: static_assert failed due to requirement 'Map<Eigen::Matrix<float, -1, -1, 0, -1, -1>, 0, Eigen::Stride<0, 0>>::IsVectorAtCompileTime' "YOU_TRIED_CALLING_A_VECTOR_METHOD_ON_A_MATRIX"
What is the right and most efficient solution?

I think the most elegant Solution would be to use Eigen::Map. In your case you would do it like this:
Eigen::MatrixXf mat(3, 4);
mat << 1.1, 2, 3, 50,
2.2, 2, 3, 50,
3.1, 2, 3, 50;
std::vector<float> vec;
vec.resize(mat.rows());
for(int col=0; col<mat.cols(); col++){
Eigen::Map<Eigen::MatrixXf>(vec.data(), mat.rows(), 1 ) = mat.col(col); }

The below program shows how you can extract the first column from Eigen::Matrix into a std::vector<float> .
Version 1: Extract only a single column at a time
int main()
{
Eigen::MatrixXf mat(3, 4);
mat << 1.1, 2, 3, 50,
2.2, 2, 3, 50,
3.1, 2, 3, 50;
std::vector<float> column1(mat.rows());
for(int j = 0; j < mat.rows(); ++j)
{
column1.at(j) = mat(j, 0);//this will put all the elements in the first column of Eigen::Matrix into the column3 vector
}
for(float elem: column1)
{
std::cout<<elem<<std::endl;
}
//similarly you can create columns corresponding to other columns of the Matrix. Note that you can also
//create std::vector<std::vector<float>> for storing all the rows and columns as shown in version 2 of my answer
return 0;
}
The output of version 1 is as follows:
1.1
2.2
3.1
Similarly you can extract other columns.
Note that if you want to extract all columns then you can create/use a std::vector<std::vector<float>> where you can store all the rows and columns as shown below:
Version 2: Extract all columns into a 2D std::vector
int main()
{
Eigen::MatrixXf mat(3, 4);
mat << 1.1, 2, 3, 50,
2.2, 2, 3, 50,
3.1, 2, 3, 50;
std::vector<std::vector<float>> vec_2d(mat.rows(), std::vector<float>(mat.cols(), 0));
for(int col = 0; col < mat.cols(); ++col)
{
for(int row = 0; row < mat.rows(); ++row)
{
vec_2d.at(row).at(col) = mat(row, col);
}
}
//lets print out i.e., confirm if our vec_2d contains the columns correctly
for(int col = 0; col < mat.cols(); ++col)
{ std::cout<<"This is the "<<col+1<< " column"<<std::endl;
for(int row = 0; row < mat.rows(); ++row)
{
std::cout<<vec_2d.at(row).at(col)<<std::endl;
}
}
return 0;
}
The output of version 2 is as follows:
This is the 1 column
1.1
2.2
3.1
This is the 2 column
2
2
2
This is the 3 column
3
3
3
This is the 4 column
50
50
50

Related

LeetCode 417 solution in C++. I'm getting a heap buffer overflow error

I'm working through this problem: https://leetcode.com/problems/pacific-atlantic-water-flow/
After battling through my own solution for several hours, I ended up not being able to fully solve it, so I'm tracing through how others did it. My new code is based on this: https://leetcode.com/problems/pacific-atlantic-water-flow/discuss/608490/C%2B%2B-Implementation-(DFS)
It's basically the same, except I made it more verbose, and I added some comments for clarity. Even though the code is virtually the same, I'm getting a heap overflow error for very large inputs. I'm not sure what's causing it, but I'm guessing that I'm not correctly passing references to vectors and end up making copies in memory. Can someone please help me figure out the inefficiency?
#include <vector>
#include <assert.h>
using namespace std;
bool isOutOfBounds(int row, int col, vector<vector<int>>& matrix) {
bool rowOutOfBound = row < 0 || row == matrix.size();
bool colOutOfBound = col < 0 || col == matrix[0].size();
return rowOutOfBound || colOutOfBound;
}
void explore(vector<vector<int>>& matrix, int i, int j, int prevHeight, vector<vector<bool>>& explored) {
// if we are out of bounds, or our height is greater, or if this cell was already explored
if (isOutOfBounds(i, j, matrix) || matrix[i][j] < prevHeight || explored[i][j]) return;
int height = matrix[i][j];
explored[i][j] = true;
explore(matrix, i - 1, j, height, explored); // flow up
explore(matrix, i, j + 1, height, explored); // flow right
explore(matrix, i + 1, j, height, explored); // flow down
explore(matrix, i, j - 1, height, explored); // flow left
}
vector<vector<int>> pacificAtlantic(vector<vector<int>>& matrix) {
vector<vector<int>> result;
if (matrix.size() == 0) return result;
// create boolean grids of explored paths, one for pacific, one for atlantic
vector<vector<bool>> pacific(matrix.size(), vector<bool>(matrix[0].size(), false));
vector<vector<bool>> atlantic(pacific);
for (int i = 0; i < matrix.size(); i++) {
explore(matrix, i, 0, INT_MIN, pacific); // explore first column
explore(matrix, i, matrix.size() - 1, INT_MIN, atlantic); // explore last column
}
for (int j = 0; j < matrix[0].size(); j++) {
explore(matrix, 0, j, INT_MIN, pacific); // explore first row
explore(matrix, matrix[j].size() - 1, j, INT_MIN, atlantic); // explore last row
}
// for every column of each row
for (int i = 0; i < matrix.size(); i++) {
for (int j = 0; j < matrix[i].size(); j++) {
if (pacific[i][j] && atlantic[i][j]) {
result.push_back({ i, j });
}
}
}
return result;
}
void main() {
vector<vector<int>> matrix {
{ 1, 2, 2, 3, 5 },
{ 3, 2, 3, 4, 4 },
{ 2, 4, 5, 3, 1 },
{ 6, 7, 1, 4, 5 },
{ 5, 1, 1, 2, 4 }
};
vector<vector<int>> expected{ {0, 4}, {1, 3}, {1, 4}, {2, 2}, {3, 0}, {3, 1}, {4, 0} };
vector<vector<int>> result = pacificAtlantic(matrix);
assert(result == expected);
}
I had an error when exploring last column, and last row, so was accessing invalid vector indexes.
when I fixed
for (int i = 0; i < matrix.size(); i++) {
explore(matrix, i, 0, INT_MIN, pacific); // explore first column
explore(matrix, i, matrix.size() - 1, INT_MIN, atlantic); // explore last column
}
for (int j = 0; j < matrix[0].size(); j++) {
explore(matrix, 0, j, INT_MIN, pacific); // explore first row
explore(matrix, matrix[j].size() - 1, j, INT_MIN, atlantic); // explore last row
}
the solution worked.
The code should have read as:
for (int i = 0; i < matrix.size(); i++) {
explore(matrix, i, 0, INT_MIN, pacific); // explore first column
explore(matrix, i, matrix[0].size() - 1, INT_MIN, atlantic); // explore last column
}
for (int j = 0; j < matrix[0].size(); j++) {
explore(matrix, 0, j, INT_MIN, pacific); // explore first row
explore(matrix, matrix.size() - 1, j, INT_MIN, atlantic); // explore last row
}

Copy one OpenCV OutputArrayOfArrays object to another

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

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.

Pixel-wise median of sequence of cv::Mat's

Note: I am NOT asking about Median Filter.
I have a sequence of images let us say:
std::array<cv::Mat,N> sequence;
I want to blend all those images in one. This one image should satisfies:
Each pixel of the new image is the median of its corresponding pixels from the sequence. In other words:
Result(i,j)=median(sequence[0](i,j), sequence[1](i,j), ..., sequence[N](i,j));
Is there built-in function for doing that? What would be the fastest way?
What I tried till now: To iterate over each pixel from all the sequence and sort then take the median then store it in the result. However, it is so overkill.
You can compute the sequential median for each position using histograms.
Assuming that you're using Mat1b images, each histogram would have 256 values.
You need to store the histogram, as well as the sum of all bins:
struct Hist {
vector<short> h;
int count;
Hist() : h(256, 0), count(0) {};
};
The median value is the index in the histogram that corresponds to half of the pixels count / 2. Snippet from Rosetta Code:
int i;
int n = hist.count / 2; // 'hist' is the Hist struct at a given pixel location
for (i = 0; i < 256 && ((n -= hist.h[i]) >= 0); ++i);
// 'i' is the median value
When you add or remove an image, you update the histogram for each pixel location, and recompute the median value. This operation is quite fast because you don't need to sort.
There are some drawback to this:
This will work only for uchar values, otherwise the length of each histogram will be too large
This approach will use a lot of RAM since it needs rows x cols histograms. It may not work for large images.
The following implementation works for single channel images, but it can be easily extended to 3 channels.
You can use an approach based on two heaps, or approximate methods. You can find details here:
Find running median from a stream of integers
Rolling median algorithm in C
Find median value from a growing set
Code:
#include <vector>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
struct Hist {
vector<short> h;
int count;
Hist() : h(256, 0), count(0) {};
};
void addImage(vector<Mat1b>& images, Mat1b& img, vector<vector<Hist>>& M, Mat1b& med)
{
assert(img.rows == med.rows);
assert(img.cols == med.cols);
for (int r = 0; r < img.rows; ++r) {
for (int c = 0; c < img.cols; ++c){
// Add pixel to histogram
Hist& hist = M[r][c];
++hist.h[img(r, c)];
++hist.count;
// Compute median
int i;
int n = hist.count / 2;
for (i = 0; i < 256 && ((n -= hist.h[i]) >= 0); ++i);
// 'i' is the median value
med(r,c) = uchar(i);
}
}
// Add image to my list
images.push_back(img.clone());
}
void remImage(vector<Mat1b>& images, int idx, vector<vector<Hist>>& M, Mat1b& med)
{
assert(idx >= 0 && idx < images.size());
Mat1b& img = images[idx];
for (int r = 0; r < img.rows; ++r) {
for (int c = 0; c < img.cols; ++c){
// Remove pixel from histogram
Hist& hist = M[r][c];
--hist.h[img(r, c)];
--hist.count;
// Compute median
int i;
int n = hist.count / 2;
for (i = 0; i < 256 && ((n -= hist.h[i]) >= 0); ++i);
// 'i' is the median value
med(r, c) = uchar(i);
}
}
// Remove image from list
images.erase(images.begin() + idx);
}
void init(vector<vector<Hist>>& M, Mat1b& med, int rows, int cols)
{
med = Mat1b(rows, cols, uchar(0));
M.resize(rows);
for (int i = 0; i < rows; ++i) {
M[i].resize(cols);
}
}
int main()
{
// Your images... be sure that they have the same size
Mat1b img0 = imread("path_to_image", IMREAD_GRAYSCALE);
Mat1b img1 = imread("path_to_image", IMREAD_GRAYSCALE);
Mat1b img2 = imread("path_to_image", IMREAD_GRAYSCALE);
resize(img0, img0, Size(800, 600));
resize(img1, img1, Size(800, 600));
resize(img2, img2, Size(800, 600));
int rows = img0.rows;
int cols = img0.cols;
vector<Mat1b> images; // All your images, needed only if you need to remove an image
vector<vector<Hist>> M; // histograms
Mat1b med; // median image
// Init data strutctures
init(M, med, rows, cols);
// Add images. 'med' will be the median image and will be updated each time
addImage(images, img0, M, med);
addImage(images, img1, M, med);
addImage(images, img2, M, med);
// You can also remove an image from the median computation
remImage(images, 2, M, med);
// Hey, same median as img0 and img1 ;D
return 0;
}
You can use the following technique if the number of images in your sequence is odd.
Prepare an N (which is odd) channel image from the sequence of images that you have
Reshape this image into a 1-channel column vector
Now apply a median filter of size N to this column vector. As the image is a 1-channel column vector, the filter will calculate the median of the channels(of course, there will be some additional calculations that are not useful to us)
Reshape this filtered image into its original form having N channels and the original number of rows and columns
Pick the middle channel of this N-channel image, which contains the median image of the sequence
Below I've illustrated the above items with a simple code and its output.
individual channels
channel 0:
[1, 1, 1;
1, 1, 1;
1, 1, 1]
channel 1:
[2, 2, 2;
2, 2, 2;
2, 2, 2]
channel 2:
[3, 3, 3;
3, 3, 3;
3, 3, 3]
channel 3:
[4, 4, 4;
4, 4, 4;
4, 4, 4]
channel 4:
[5, 5, 5;
5, 5, 5;
5, 5, 5]
output for N = 3
3-channel image data:
[1, 2, 3, 1, 2, 3, 1, 2, 3;
1, 2, 3, 1, 2, 3, 1, 2, 3;
1, 2, 3, 1, 2, 3, 1, 2, 3]
1-channel column vector image data:
[1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3; 1; 2; 3]
median of the 1-channel column vector image data:
[1; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 3]
reshaped filtered image data:
[1, 2, 2, 2, 2, 2, 2, 2, 2;
2, 2, 2, 2, 2, 2, 2, 2, 2;
2, 2, 2, 2, 2, 2, 2, 2, 3]
median image data:
[2, 2, 2;
2, 2, 2;
2, 2, 2]
output for N = 5
5-channel image data:
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5;
1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5;
1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
1-channel column vector image data:
[1; 2; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2
; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2; 3; 4; 5; 1; 2; 3; 4; 5]
median of the 1-channel column vector image data:
[1; 2; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3
; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 3; 4; 5]
reshaped filtered image data:
[1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3;
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3;
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5]
median image data:
[3, 3, 3;
3, 3, 3;
3, 3, 3]
Code:
// number of channels (= number of images in the sequence)
// N MUST BE ODD
const int N = 5;
// channel data
uchar ch0[] = {1, 1, 1, 1, 1, 1, 1, 1, 1};
uchar ch1[] = {2, 2, 2, 2, 2, 2, 2, 2, 2};
uchar ch2[] = {3, 3, 3, 3, 3, 3, 3, 3, 3};
uchar ch3[] = {4, 4, 4, 4, 4, 4, 4, 4, 4};
uchar ch4[] = {5, 5, 5, 5, 5, 5, 5, 5, 5};
// images
Mat m0 = Mat(3, 3, CV_8U, ch0);
Mat m1 = Mat(3, 3, CV_8U, ch1);
Mat m2 = Mat(3, 3, CV_8U, ch2);
Mat m3 = Mat(3, 3, CV_8U, ch3);
Mat m4 = Mat(3, 3, CV_8U, ch4);
// prepare image sequence
Mat channels[] = {m0, m1, m2, m3, m4};
// put the images into channels of matrix m
Mat m;
merge(channels, N, m);
// reshape data so that we have a single channel column vector as the image
Mat n = m.reshape(1, m.rows * m.cols * m.channels());
// apply median filter to the 1-channel column vector image. filter size must be the number of channels
Mat med;
medianBlur(n, med, N);
cout << N << "-channel image data:" << endl;
cout << m << endl;
cout << "1-channel column vector image data:" << endl;
cout << n << endl;
cout << "median of the 1-channel column vector image data:" << endl;
cout << med << endl;
// reshape the filtered 1-channel column vector image into its original form having N channels
med = med.reshape(N, m.rows);
cout << "reshaped filtered image data:" << endl;
cout << med << endl;
// split the image
split(med, channels);
// extract the middle channel which contains the median image of the sequence
cout << "median image data:" << endl;
cout << channels[(N+1)/2 - 1] << endl;