is there any efficient/fast/best way to remove/delete a given row/col from a cv::Mat? Hi people, I was playing around with some code and I came into the demand of removing a specific given row/col from a cv::Mat. So, lets say we have the following cv::Mat:

Mat a = [1, 2, 3, 4, 5;
7, 8, 9, 10, 11;
12, 13, 14, 15, 16;
17, 18, 19, 20, 21;
22, 23, 24, 25, 26]

to transform it into:

[1, 2, 3, 4, 5;
7, 8, 9, 10, 11;
12, 13, 14, 15, 16;
22, 23, 24, 25, 26]

or

[1, 2, 4, 5;
7, 8, 10, 11;
12, 13, 15, 16;
17, 18, 20, 21;
22, 23, 25, 26]

Searching around I found some approaches mainly in SO, e.g. here and here but none of them applies a direct way to do it. Therefore, I would like to ask if there is such a way or I will have to stick with one of these solutions?

I understand that it might be quite complicated for rows/cols in the middle of the cv::Mat, what about the case that I want to remove the first and last rows and cols respectively.

Thanks.

edit retag close merge delete

1

What do you mean for a direct way ? Definitely a cv::Mat data is a memory block (more than one for sparse matrix) than you have to create a new memory block excluding bytes related to unwanted rows or cols. I think that use of Range/Roi and copyTo is the safest way.

Sort by » oldest newest most voted Considering also @tuannhtn answer and playing a bit around I managed to achieve the in between cols/rows removal by the following code:

Mat a = (Mat_<int>(5, 5) << 1, 2, 3, 4, 5,
7, 8, 9, 10, 11,
12, 13, 14, 15, 16,
17, 18, 19, 20, 21,
22, 23, 24, 25, 26);

cout << endl << a << endl;

[1, 2, 3, 4, 5;
7, 8, 9, 10, 11;
12, 13, 14, 15, 16;
17, 18, 19, 20, 21;
22, 23, 24, 25, 26]

Mat b, c;

a(Range(0, a.rows - 2), Range(0, a.cols)).copyTo(b);
a(Range(a.rows - 1, a.rows), Range(0, a.cols)).copyTo(c);

vconcat(b, c, b);
cout << endl << b <<endl;

[1, 2, 3, 4, 5;
7, 8, 9, 10, 11;
12, 13, 14, 15, 16;
22, 23, 24, 25, 26]

Mat d, e;
a(Range(0, a.rows), Range(0, a.cols - 3)).copyTo(d);
a(Range(0, a.rows), Range(a.cols - 2, a.cols)).copyTo(e);

hconcat(d, e, d);
cout << endl << d <<endl;

[1, 2, 4, 5;
7, 8, 10, 11;
12, 13, 15, 16;
17, 18, 20, 21;
22, 23, 25, 26]
more

You can remove n last rows of a matrix (image) by using the constructor of Mat class. In the same way, after generating a transformation matrix, you can remove n last columns. See the code below for gray scale images:

// Remove n last rows or columns
#include <iostream>
#include <opencv2/imgproc.hpp> // for cvtColor
#include <opencv2/highgui.hpp> // for imshow
#include <opencv2/core.hpp> // for core OpenCV components

using namespace std;
using namespace cv;
// run: <program> <image path> <0:remove rows, 1: remove columns> <n>

int main(int argc, char * argv[])
{
Mat im = imread(argv); // read input image
int choose = atoi(argv); // choose = 0 means removing row, others mean removing column
int n = atoi(argv); // number of rows or columns to remove
if (im.channels() > 0)
cvtColor(im, im, CV_RGB2GRAY);
cout << "Rows:" << im.rows << endl;
cout << "Cols:" << im.cols << endl;
if (0!=choose) // for columns remove, transform original image to its transformed form
im = im.t();
Mat im2 = Mat(im.rows-n, im.cols, CV_8UC1, im.data);
if (0 != choose) // for columns remove, converting result and im to their correct forms
{
im2 = im2.t();
im = im.t(); //covert back to original
}

cout << "Rows:" << im2.rows << endl;
cout << "Cols:" << im2.cols << endl;
imshow("Original image", im);
imshow("Result image", im2);
cvWaitKey(0);
return 0;
}
more

Something seems wrong with that code. There's no implementation for row removal, and you have 2 identical if (0 != choose) conditions.

I run without any error with OpenCV 3.0 on VS 2013 64 bit. Of course, there must be 2 if(0!=choose), both are for removing n last column: the first for converting the original image to its transformed version, then remove its n last rows (meaning n last column of the origin), and the second for converting original image and result image to correct forms.

So sorry, misread the code (I thought there were a pair of brackets where they are not). In any case, I agree with @pklab, rowRange/colRange seem a much easier and less error-prone way to achieve this

@LorenaGdL, I agree with you, however my way has an advantage: in case users want to remove n last rows, my approach gives the highest performance (speed).

for the case of removing first and last rows cols using the rowRange/colRange an example can be seen here, and indeed it seems much simpler:

Mat a = (Mat_<int>(5, 5) << 1, 2, 3, 4, 5,
7, 8, 9, 10, 11,
12, 13, 14, 15, 16,
17, 18, 19, 20, 21,
22, 23, 24, 25, 26);

cout << endl << a << endl;

[1, 2, 3, 4, 5;
7, 8, 9, 10, 11;
12, 13, 14, 15, 16;
17, 18, 19, 20, 21;
22, 23, 24, 25, 26]

Mat b = a.colRange(1, a.cols - 1).rowRange(1, a.rows - 1).clone();

cout << endl << b <<endl;

[8, 9, 10;
13, 14, 15;
18, 19, 20]

@theodore: I do not agree with your comments. When removing n last rows:

• My method is: Mat im2 = Mat(im.rows-n, im.cols, CV_8UC1, im.data);
• Range based approach is: im(Range(0, im.rows - n), Range(0, im.cols)).copyTo(im2);

When removing n first rows:

• My method is: im2 = Mat(im.rows - n, im.cols, CV_8UC1, im.data + n*im.cols);
• Range based approach is: im(Range(n, im.rows), Range(0, im.cols)).copyTo(im2);

Both approaches are simpler and faster than yours.

1

In my opinion performance is a bad counselor. Clarity, generality, portability... are good directions. Take deep care of performance only when it's really needed.

@pklab: I agree with you. But when there are options to choose, we should choose the best performance one.

1

@tuannhtn no problem I am just trying things. For example according to your example the two alternatives below:

Mat b = a.colRange(1, a.cols - 1).rowRange(1, a.rows - 1).clone();

and / or

Mat b;
a(Range(1, a.rows - 1), Range(1, a.cols-1)).copyTo(b);

are giving the same result and are at the same time simple to understand. I haven't test speed performance but I believe you that your method might be faster.

Stats

Asked: 2015-10-23 16:28:13 -0500

Seen: 9,408 times

Last updated: Oct 24 '15