Vector sizes in c++ - c++

So I'm working on a small math library for fun and I'm having a little bit of an issue with this vector. This method is for transposing a matrix, swapping rows for columns and vice versa. It performs this correctly. However, there's something strange going on. From my input of a 3 row x 2 column matrix, the transposition should come back as 2x3, which it does. The problem is the vector is coming back as a size of 4 instead of 2, despite being set to 2 in the method. I've tested it at various points leading up to and after. The size of the vector only changes to 4 once it has called #transpose. It's prepending two vectors of size 0 before the answer (resulting matrix vector). Any ideas as to how? I'm not familiar enough with c++ to know of any quirks.
Matrix Matrix::transpose() {
vector<vector<float>> result;
result.resize(columns);
for (int col = 0; col < columns; col++) {
vector<float> data;
data.resize(rows);
for (int row = 0; row < rows; row++) {
data[row] = vec[row][col];
}
result.push_back(data);
}
return Matrix(result);
}

The code first resizes result, and then appends to it in a loop using push_back. This causes the vector to be resized twice and end up with twice the intended size.
Since the code intends to set the size in advance, the correct fix is to change the push_back line to result[col] = data.

Related

What is the most efficient (time, memory) way to add a 2D-horizontal- and a 2D-vertical-aligned boost::multi_array resulting in a 3D array?

I want to add two boost multi-arrays in C++.
The first challenge is the alignment of the two arrays. One of these arrays is vertically aligned and the other is horizontally aligned as shown in the figure below. They shall be added, resulting in a 3-dimensional array (see also below). The easiest/laziest way would be to duplicate the vertically aligned array along its y-axis until it has the same width as the horizontally aligned array. Additionally duplicating the horizontally aligned array along its x-axis until it has the same height as the vertically aligned array. Then I would need a possibility to add two 3-dimensionally boost multi-arrays.
The second challenge is, that adding boost multi-arrays is not natively supported by boost, or I am wrong on this point? I want to avoid for-loops and take advantage of the straight allocated memory which makes e.g. copy operations less time intensive.
Does anybody have a good recommendation to handle this quest?
After a lot of thoughts about it, I think the best solution will be to extend one of these 2d arrays to an 3d array. Then I can sum the 2d matrix slide-wise to the 3d matrix.
A naive but correct way to do this is:
boost::multi_array<int, 2> A(boost::extents[5][5]);
boost::multi_array<int, 2> B(boost::extents[5][5]);
boost::multi_array<int, 3> C(boost::extents[5][5][5]);
for(auto i = 0; i != C.shape()[0]; ++i) {
for(auto j = 0; j != C.shape()[1]; ++j) {
for(auto k = 0; k != C.shape()[2]; ++k) {
C[i][j][k] = A[i][j] + B[j][k];
}
}
}
assert( C[1][2][3] == A[1][2] + B[2][3] );
https://godbolt.org/z/4jP1PbjYo
You are correct, Boost.MA doesn't have arithmetic operations for arrays.

Trouble writing to 4D vector in C++ (no viable overloaded '=')

The issue I am facing is that via the openCV library I am reading in a series of images as their own "Mat" format: an image matrix.
Basically I need to write any pixel value that's > 0 as "true" to a 4D vector and any that == 0 as "false".
Why 4 dimensions?
vector<vector<vector<bool>>>pointVector;
The 3 vector levels refer to X,Y,Z axes. Bool is just the true/false. The images are Y by Z and are stacked in 3D along axis X.
Basically we have a series of images representing points that are stacked in 3D.
(Poor explanation? Probably)
Anyway, the issue comes in my function to read the points in a single photo then write them out to the 4D vector.
Note: xVal is a global storing the ID number of the photo addressed. It's used for the X dimension (layers of images).
Int lineTo3DVector (Mat image)
{
// Takes in matrix and converts to 4D vector.
// This will be exported and all vectors added together into a point cloud
vector<vector<vector<bool>>>pointVector;
for (int x=0; x<image.rows; x++)
{
for (int y = 0; y<image.cols; y++)
{
if((image.at<int>(x,y)) > 0)
{
pointVector[xVal*image.cols*image.rows + x*image.cols + y] = true;
}
}
}
}
I haven't finished writing all of the function because the if statement intended to write pointVector at address xVal, x, y with bool 'true' throws up an error saying:
No viable overloaded '='
Any idea what's going wrong? I've scoured the web and given myself a headache trying to dig up info (yeah, noob in the deep-end again) so any suggestions would be appreciated.
You're accessing only the first vector (the outer one), without actually accessing any of the vectors inside.
The syntax would be pointVector[x][y][z] = true, where x, y and z are the values you want to use to access the three nested vectors.
What you want is:
pointVector[xVal][x][y] = true
What you were using is a way to access a 3D array that is laid out in memory as a 1D array, but it's not what you want in your case.
Make sure you don't go out of bounds
Make sure the elements you are accessing actually exist! If the code you have shown is the actual code, pointVector will have no elements when you try to use it.
To fix that, you have to resize all the vectors (outer and inner). This can get cumbersome, and you may want to go with the 1D approach, i.e. allocate a large, one-dimensional bool array and access it using the method you were using (largeArray[xVal*image.cols*image.rows + x*image.cols + y]).
1D approach
In the following code numberOfValues is the maximum number of elements you will access.
int lineTo3DVector (Mat image)
{
// Takes in matrix and converts to 4D vector.
// This will be exported and all vectors added together into a point cloud
std::vector<bool> pointVector;
pointVector.resize(numberOfValues);
for (int x=0; x < image.rows; x++)
{
for (int y = 0; y < image.cols; y++)
{
if(image.at<int>(x,y) > 0)
{
pointVector[xVal*image.cols*image.rows + x*image.cols + y] = true;
}
}
}
// Return whatever.
}

how to use opencv Mat reserve? with push_back

I want to use the opencv cv::Mat function push_back to add new rows to a matrix, but I want to precache the size of the matrix so that the data doesn't need to be constantly reallocated. The cv::mat::reserve function has a number of rows parameter, but that implies that you have to specify the number of columns (and data size first). So the code below I assume would give me one empty row at the beginning which I don't want. Is there a proper way to do this using reserve and push_back??
cv::Mat M(1, size_cols, CV_32FC1);
M.reserve(size_rows);
for (int i = 0; i < size_rows; i++)
{
GetInputMatrix(A);
M.push_back(A.row(i));
}
Note: though the example code doesn't show it I'm not sure of the exact size of the final matrix, but I can get a max value size to reserve.
Using an empty mat in the beginning will be just fine. The column number and type will be determined via the first push_back.
cv::Mat M; // empty mat in the beginning
M.reserve(size_rows);
for (int i = 0; i < size_rows; i++) {
GetInputMatrix(A);
M.push_back(A.row(i));
}
Well, according to documentation,
reserve does:
The method reserves space for sz rows. If the matrix already has enough space to store sz rows, nothing happens. If the matrix is reallocated, the first Mat::rows rows are preserved. The method emulates the corresponding method of the STL vector class.
That being said, as long as you don't that huge amount of data to store, nothing will happen.

How to copy elements of 2D matrix to 1D array vertically using c++

I have a 2D matrix and I want to copy its values to a 1D array vertically in an efficient way as the following way.
Matrice(3x3)
[1 2 3;
4 5 6;
7 8 9]
myarray:
{1,4,7,2,5,8,3,6,9}
Brute force takes 0.25 sec for 1000x750x3 image. I dont want to use vector because I give myarray to another function(I didnt write this function) as input. So, is there a c++ or opencv function that I can use? Note that, I'm using opencv library.
Copying matrix to array is also fine, I can first take the transpose of the Mat, then I will copy it to array.
cv::Mat transposed = myMat.t();
uchar* X = transposed.reshape(1,1).ptr<uchar>(0);
or
int* X = transposed.reshape(1,1).ptr<int>(0);
depending on your matrix type. It might copy data though.
You can optimize to make it more cache friendly, i.e. you can copy blockwise, keeping track of the positions in myArray, where the data should go to. The point is, that you brute force approach will most likely make each access to the matrix being off-cache, which has a tremendous performance impact. Hence it is better to copy vertical/horizontal taking the cache line size into account.
See the idea bbelow (I didn't test it, so it has most likely bugs, but it should make the idea clear).
size_t cachelinesize = 128/sizeof(pixel); // assumed cachelinesize of 128 bytes
struct pixel
{
char r;
char g;
char b;
};
array<array<pixel, 1000>, 750> matrice;
vector<pixel> vec(1000*750);
for (size_t row = 0; row<matrice.size; ++row)
{
for (size_t col = 0; col<matrice[0].size; col+=cachelinesize)
{
for (size_t i = 0; i<cachelinesize; ++i)
{
vec[row*(col+i)]=matrice[row][col+i]; // check here, if right copy order. I didn't test it.
}
}
}
If you are using the matrix before the vertical assignment/querying, then you can cache the necessary columns when you hit each one of the elements of columns.
//Multiplies and caches
doCalcButCacheVerticalsByTheWay(myMatrix,calcType,myMatrix2,cachedColumns);
instead of
doCalc(myMatrix,calcType,myMatrix2); //Multiplies
then use it like this:
...
tmpVariable=cachedColumns[i];
...
For example, upper function multiplies the matrix with another one, then when the necessary columns are reached, caching into a temporary array occurs so you can access elements of it later in a contiguous order.
I think Mat::reshape is what you want. It does not copying data.

Converting a row of cv::Mat to std::vector

I have a fairly simple question: how to take one row of cv::Mat and get all the data in std::vector? The cv::Mat contains doubles (it can be any simple datatype for the purpose of the question).
Going through OpenCV documentation is just very confusing, unless I bookmark the page I can not find a documentation page twice by Googling, there's just to much of it and not easy to navigate.
I have found the cv::Mat::at(..) to access the Matrix element, but I remember from C OpenCV that there were at least 3 different ways to access elements, all of them used for different purposes... Can't remember what was used for which :/
So, while copying the Matrix element-by-element will surely work, I am looking for a way that is more efficient and, if possible, a bit more elegant than a for loop for each row.
It should be as simple as:
m.row(row_idx).copyTo(v);
Where m is cv::Mat having CV_64F depth and v is std::vector<double>
Data in OpenCV matrices is laid out in row-major order, so that each row is guaranteed to be contiguous. That means that you can interpret the data in a row as a plain C array. The following example comes directly from the documentation:
// compute sum of positive matrix elements
// (assuming that M is double-precision matrix)
double sum=0;
for(int i = 0; i < M.rows; i++)
{
const double* Mi = M.ptr<double>(i);
for(int j = 0; j < M.cols; j++)
sum += std::max(Mi[j], 0.);
}
Therefore the most efficient way is to pass the plain pointer to std::vector:
// Pointer to the i-th row
const double* p = mat.ptr<double>(i);
// Copy data to a vector. Note that (p + mat.cols) points to the
// end of the row.
std::vector<double> vec(p, p + mat.cols);
This is certainly faster than using the iterators returned by begin() and end(), since those involve extra computation to support gaps between rows.
From the documentation at here, you can get a specific row through cv::Mat::row, which will return a new cv::Mat, over which you can iterator with cv::Mat::begin and cv::Mat::end. As such, the following should work:
cv::Mat m/*= initialize */;
// ... do whatever...
cv::Mat first_row(m.row(0));
std::vector<double> v(first_row.begin<double>(), first_row.end<double>());
Note that I don't know any OpenCV, but googling "OpenCV mat" led directly to the basic types documentation and according to that, this should work fine.
The matrix iterators are random-access iterators, so they can be passed to any STL algorithm, including std::sort() .
This is also from the documentiation, so you could actually do this without a copy:
cv::Mat m/*= initialize */;
// ... do whatever...
// first row begin end
std::vector<double> v(m.begin<double>(), m.begin<double>() + m.size().width);
To access more than the first row, I'd recommend the first snippet, since it will be a lot cleaner that way and there doesn't seem to be any heavy copying since the data types seem to be reference-counted.
You can also use cv::Rect
m(cv::Rect(0, 0, 1, m.cols))
will give you first row.
matrix(cv::Rect(x0, y0, len_x, len_y);
means that you will get sub_matrix from matrix whose upper left corner is (x0,y0) and size is (len_x, len_y). (row,col)
I think this works,
an example :
Mat Input(480, 720, CV_64F, Scalar(100));
cropping the 1st row of the matrix:
Rect roi(Point(0, 0), Size(720, 1));
then:
std::vector<std::vector<double> > vector_of_rows;
vector_of_rows.push_back(Input(roi));