An Nguyen
An Nguyen

Reputation: 31

Extend/Pad matrix in Eigen

Say I have

A = [1 2 3]
    [4 5 6]
    [7 8 9]

I want to pad it with the first row and first column or last row and last column as many times as needed to create A nxn. For example, A 4x4 would be

 A = [1 1 2 3]
     [1 1 2 3]
     [4 4 5 6]
     [7 7 8 9]

and A 5x5 would be

 A = [1 1 2 3 3]
     [1 1 2 3 3]
     [4 4 5 6 6]
     [7 7 8 9 9]
     [7 7 8 9 9]

I'm aware that I could do A.conservativeResize(4,4) which gets me

 A = [1 2 3 0]
     [4 5 6 0]
     [7 8 9 0]
     [0 0 0 0]

then I could copy things around one by one, but is there a more efficient way to do this using Eigen?

Upvotes: 3

Views: 2859

Answers (2)

ggael
ggael

Reputation: 29225

You can workaround using a nullary-expression:

#include <iostream>
#include <Eigen/Dense>
using namespace Eigen;
using namespace std;

int main()
{
  Matrix3i A;
  A.reshaped() = VectorXi::LinSpaced(9,1,9);
  cout << A << "\n\n";
  int N = 5;
  MatrixXi B(N,N);
  B = MatrixXi::NullaryExpr(N, N, [&A,N] (Index i,Index j) {
        return A( std::max<Index>(0,i-(N-A.rows())),
                  std::max<Index>(0,j-(N-A.cols())) ); } );
  cout << B << "\n\n";
}

Another approach would be to create a clamped sequence of indices like [0 0 0 1 2]:

struct pad {
  Index size() const { return m_out_size; }
  Index operator[] (Index i) const { return std::max<Index>(0,i-(m_out_size-m_in_size)); }
  Index m_in_size, m_out_size;
};

B = A(pad{3,N}, pad{3,N});

This version requires the head of Eigen.

You can easily build on those examples to make them even more general and/or wrap them within functions.

Upvotes: 3

Lack
Lack

Reputation: 1830

Just as a note, it's not true that A.conservativeResize(4,4) will get you a matrix with the added rows filled with zeros. The Eigen documentation says,

In case values need to be appended to the matrix they will be uninitialized.

The new rows and columns will be filled with garbage, and seeing zeros is only a coincidence (unless you are compiling with a special preprocessor directive to Eigen). But this means that no unnecessary time is wasted writing zeros that you will overwrite anyway.

Note: this code demonstrates how to get a matrix with your original matrix in the top left corner:

The best way to fill multiple values at once is to use Eigen's block operations and setConstant. For example, if A is a matrix of size old_sizexold_size:

A.conservativeResize(n, n);
for (int i = 0; i < n; ++i) {
    // Fill the end of each row and column
    A.row(i).tail(n - old_size).setConstant(A(i, old_size - 1));
    A.col(i).tail(n - old_size).setConstant(A(old_size - 1, i));
}
// Fill the bottom right block
A.bottomRightCorner(n - old_size, n - old_size).setConstant(A(old_size - 1, old_size - 1));

More importantly than being "efficient", these functions express your intent as a programmer.

Edit: To get a padded matrix with your original matrix in the middle:

I just noticed your example pads around the original matrix in the middle, not in the top left. In this case, there is little point to using conservativeResize(), because the original values will only be copied to the top left corner. An outline of the solution is:

  • Construct a new nxn matrix B of the desired size
  • Copy your original matrix to the middle using

    int start = (n - old_size + 1)/2; B.block(start, start, old_size, old_size) = A;

  • Fill in the outside values using block operations similar to my example above.

Upvotes: 1

Related Questions