3
votes

My title may not be clear enough, but please look carefully on the following description.Thanks in advance.

I have a RGB image and a binary mask image:

Mat img = imread("test.jpg")
Mat mask = Mat::zeros(img.rows, img.cols, CV_8U);

Give some ones to the mask, assume the number of ones is N. Now the nonzero coordinates are known, based on these coordinates, we can surely obtain the corresponding pixel RGB value of the origin image.I know this can be accomplished by the following code:

Mat colors = Mat::zeros(N, 3, CV_8U);
int counter = 0;
for (int i = 0; i < mask.rows; i++)
{
    for (int j = 0; j < mask.cols; j++)
    {
        if (mask.at<uchar>(i, j) == 1)
        {
            colors.at<uchar>(counter, 0) = img.at<Vec3b>(i, j)[0];
            colors.at<uchar>(counter, 1) = img.at<Vec3b>(i, j)[1];
            colors.at<uchar>(counter, 2) = img.at<Vec3b>(i, j)[2];
            counter++;
        }
    }
}

And the coords will be as follows: enter image description here

However, this two layer of for loop costs too much time. I was wondering if there is a faster method to obatin colors, hope you guys can understand what I was trying to convey.

PS:If I can use python, this can be done in only one sentence:

colors = img[mask == 1]
3
Why is the destination Mat called coords, when you're not storing coordinates in it, but rather pixel values from the input image? | Also, that Python code is incorrect, numpy arrays are not callable. Did you mean img[mask==1]? Furthermore, it doesn't produce a list of coordinates either.Dan Mašek
Thank you for your kind remind:)Terry

3 Answers

1
votes

The .at() method is the slowest way to access Mat values in C++. Fastest is to use pointers, but best practice is an iterator. See the OpenCV tutorial on scanning images.

Just a note, even though Python's syntax is nice for something like this, it still has to loop through all of the elements at the end of the day---and since it has some overhead before this, it's de-facto slower than C++ loops with pointers. You necessarily need to loop through all the elements regardless of your library, you're doing comparisons with the mask for every element.

1
votes

If you are flexible with using any other open source library using C++, try Armadillo. You can do all linear algebra operations with it and also, you can reduce above code to one line(similar to your Python code snippet).

Or

Try findNonZero()function and find all coordinates in image containing non-zero values. Check this: https://stackoverflow.com/a/19244484/7514664

0
votes

Compile with optimization enabled, try profiling this version and tell us if it is faster:

vector<Vec3b> colors;
if (img.isContinuous() && mask.isContinuous()) {
    auto pimg = img.ptr<Vec3b>();
    for (auto pmask = mask.datastart; pmask < mask.dataend; ++pmask, ++pimg) {
        if (*pmask)
            colors.emplace_back(*pimg);
    }
}
else {
    for (int r = 0; r < img.rows; ++r) {
        auto prowimg = img.ptr<Vec3b>(r);
        auto prowmask = img.ptr(r);
        for (int c = 0; c < img.cols; ++c) {
            if (prowmask[c])
                colors.emplace_back(prowimg[c]);
        }
    }
}

If you know the size of colors, reserve the space for it beforehand.