Ask Your Question
4

is there any efficient/fast/best way to remove/delete a given row/col from a cv::Mat?

asked 2015-10-23 16:28:13 -0600

theodore gravatar image

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 flag offensive close merge delete

Comments

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.

pklab gravatar imagepklab ( 2015-10-24 05:30:43 -0600 )edit

2 answers

Sort by ยป oldest newest most voted
3

answered 2015-10-24 14:13:05 -0600

theodore gravatar image

updated 2015-10-24 14:14:03 -0600

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]
edit flag offensive delete link more
2

answered 2015-10-24 07:19:38 -0600

updated 2015-10-24 07:35:37 -0600

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[1]); // read input image
    int choose = atoi(argv[2]); // choose = 0 means removing row, others mean removing column
    int n = atoi(argv[3]); // 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;
}
edit flag offensive delete link more

Comments

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

LorenaGdL gravatar imageLorenaGdL ( 2015-10-24 07:26:23 -0600 )edit

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.

tuannhtn gravatar imagetuannhtn ( 2015-10-24 07:34:15 -0600 )edit

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 gravatar imageLorenaGdL ( 2015-10-24 07:45:04 -0600 )edit

@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).

tuannhtn gravatar imagetuannhtn ( 2015-10-24 09:35:02 -0600 )edit

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 gravatar imagetheodore ( 2015-10-24 11:29:50 -0600 )edit

@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.

tuannhtn gravatar imagetuannhtn ( 2015-10-24 11:42:33 -0600 )edit
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 gravatar imagepklab ( 2015-10-24 11:43:22 -0600 )edit

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

tuannhtn gravatar imagetuannhtn ( 2015-10-24 11:46:26 -0600 )edit
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.

theodore gravatar imagetheodore ( 2015-10-24 12:10:40 -0600 )edit

simple, clear and API compliant! +1 @theodore

pklab gravatar imagepklab ( 2015-10-24 12:38:17 -0600 )edit

Question Tools

1 follower

Stats

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

Seen: 19,652 times

Last updated: Oct 24 '15