5
votes

Suppose I have a Mat variable small_image with dimensions 98x158x32 (type float). Now I want to zero pad this image (ie. add a border of zeros to the image). I want to add 7 zeros above and below the image, and 12 zeros left and right of the image. The first idea was to use the cv copyMakeBorder (see copyMakeBorder doc), which seems perfect for this:

    int old_size[3];
    old_size[0] = 98;
    old_size[1] = 158;
    old_size[2] = 32;

    int pad_size[3];
    pad_size[0] = old_size[0] + 2 * 7;
    pad_size[1] = old_size[1] + 2 * 12;
    pad_size[2] = old_size[2];

    cv::Mat image_padded(3, pad_size, CV_32FC1, cv::Scalar(0)); //Initialize the larger Mat to 0

    copyMakeBorder(small_image,image_padded,7,7,12,12,BORDER_CONSTANT,Scalar(0));

However, this code gives a memcopy error. Does anyone see the problem here?

The alternative as described in this post does not work either:

cv::Rect roi( cv::Point( 12, 7 ), small_image.size() );
small_image.copyTo( image_padded( roi ) );

It claims that "Assertion failed (m.dims <= 2)", while both Mat variables are 3D matrices.

Any help to achieve the zero padding would be greatly appreciated!

2
copyMakeBorder is most likely not working since it's designed for 2D matrices representing images (although can be multichannel). What you have here is a 3D Mat. - Andrzej Pronobis

2 Answers

3
votes

Neither of the methods you described work since they are meant to be used for 2D matrices. You have a 3D Mat which requires that you specify ranges in all three dimensions. You can use the () operator which works with an array of ranges combined with copyTo as in the example below.

#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

int main()
{
  int old_size[3];
  old_size[0] = 5;
  old_size[1] = 5;
  old_size[2] = 2;

  int pad_size[3];
  pad_size[0] = old_size[0] + 2 * 1;
  pad_size[1] = old_size[1] + 2 * 2;
  pad_size[2] = old_size[2];

  cv::Mat small_image(3, old_size, CV_32FC1, cv::Scalar(1));
  cv::Mat image_padded(3, pad_size, CV_32FC1, cv::Scalar(0));

  cv::Range ranges[3];
  ranges[0] = cv::Range(1, old_size[0]+1);
  ranges[1] = cv::Range(2, old_size[1]+2);
  ranges[2] = cv::Range(0, old_size[2]);

  small_image.copyTo(image_padded(ranges));

  for (int i = 0; i < pad_size[0]; i++)
    for (int j = 0; j < pad_size[1]; j++)
      for (int k = 0; k < pad_size[2]; k++)
        std::cout << image_padded.at<float>(i, j, k) << ", ";
}

which will give:

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
2
votes

We could also use a combination of rowRange and colRange methods to set our region of interest to a subsection of 3 dimensional padded image( all zeros ). That could easily followed by copying unpadded image onto it.

cv::Mat image_padded(3, pad_size, CV_32FC1, cv::Scalar(0));
int xPad = 12;
int yPad = 7;
Mat area = image_padded.rowRange(xPad, xPad + 98).colRange(yPad, yPad + 158);
small_image.copyTo( area );