【问题标题】:Opencv obatin certain pixel RGB value based on maskOpencv根据掩码获取某些像素RGB值
【发布时间】:2017-09-21 09:28:41
【问题描述】:

我的标题可能不够清楚,但请仔细看下面的描述。提前谢谢。

我有一个 RGB 图像和一个二进制掩码图像:

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

给掩码一些1,假设1的个数是N。现在知道非零坐标,根据这些坐标,我们肯定可以得到原图对应的像素RGB值。我知道这是可以做到的通过以下代码:

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++;
        }
    }
}

坐标如下: enter image description here

但是,这两层 for 循环花费了太多时间。我想知道是否有更快的方法来获得颜色,希望你们能理解我试图传达的意思。

PS:如果我会用python的话,一句话就能搞定:

colors = img[mask == 1]

【问题讨论】:

  • 为什么目的地Mat 被称为coords,而不是存储坐标,而是来自输入图像的像素值? |此外,该 Python 代码不正确,numpy 数组不可调用。你的意思是img[mask==1]?此外,它也不会生成坐标列表。
  • 感谢您的善意提醒:)

标签: c++ opencv


【解决方案1】:

.at() 方法是在 C++ 中访问Mat 值的最慢方法。最快的是使用指针,但最佳实践是迭代器。请参阅 OpenCV tutorial on scanning images

请注意,尽管 Python 的语法对于这样的事情来说很不错,但它仍然必须在一天结束时循环遍历所有元素 —— 因为在此之前它有一些开销,所以它是事实上的比带有指针的 C++ 循环慢。无论您的库如何,您都必须遍历所有元素,您正在与每个元素的掩码进行比较。

【讨论】:

  • 好吧,我想我没有说清楚...如果掩码矩阵稀疏怎么办?您将花费大量时间遍历矩阵。一些现有的函数可能有他们修改过的算法来处理这种情况,这就是我想要找到的。
  • 稀疏矩阵的存储方式不同,非零元素与其索引一起存储,因此您可以使用它们进行索引。如果您想要一个稀疏矩阵进行索引,则创建一个并将其用于索引。但是,通用掩码不会存储为稀疏矩阵。如果你想从你的掩码创建一个稀疏矩阵,那么你将不得不循环遍历掩码来创建它。
  • 假设你有一个稀疏的二进制掩码;也就是说,您只需存储与非零元素相对应的索引(请注意,这是您使用findNonZero() 得到的,正如其他答案所提到的)。然后,您只需在这些点索引图像。
【解决方案2】:

如果您可以灵活地使用任何其他使用 C++ 的开源库,请尝试 Armadillo。您可以使用它进行所有线性代数运算,还可以将上述代码减少到一行(类似于您的 Python 代码 sn-p)。

或者

尝试findNonZero()function 并查找图像中包含非零值的所有坐标。检查这个:https://stackoverflow.com/a/19244484/7514664

【讨论】:

  • findNonZero(mask, nonzeroCoordinates);整数计数器 = 0; for(int i=0; i(counter, 0) = img.at(nonzeroCoordinates.at(i).x, nonzeroCoordinates .at(i).y)[0]; coords.at(counter, 1) = img.at(nonzeroCoordinates.at(i).x, nonzeroCoordinates.at(i).y)[1]; coords.at(counter, 2) = img.at(nonzeroCoordinates.at(i).x, nonzeroCoordinates.at(i).y)[2];计数器++; }
  • 然后尝试使用犰狳库。我已经使用 Armadillo 进行此类操作,与 OpenCV 相比,它的速度相对较快。
  • 您也可以在 OpenCV 中尝试此方法: 1. 将图像与蒙版相乘。 2. 将图像矩阵转换为向量。 3. 按升序对向量的值进行排序。 3. 拒绝所有的零值坐标。如果你能提供图片和mas,我可以试试
  • 谢谢,我搜索了犰狳,发现它是一个非常强大的工具,但我的项目只允许我使用 opencv。
  • 你可以自己定义掩码,图片如下:rd.com/wp-content/uploads/sites/2/2016/02/…
【解决方案3】:

在启用优化的情况下编译,尝试分析此版本并告诉我们它是否更快:

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]);
        }
    }
}

如果您知道颜色的大小,请事先为其预留空间。

【讨论】:

  • 我试过你的版本,但是当我使用 for 循环查找颜色时,它是 for(int i=0; i
  • 你是怎么检查的?我试过了,结果和你的代码一样。在这里你可以找到一系列测试:ideone.com/1nJuBV
猜你喜欢
  • 2012-02-14
  • 1970-01-01
  • 2015-10-15
  • 2021-12-01
  • 1970-01-01
  • 2020-07-20
  • 1970-01-01
  • 2017-06-08
  • 1970-01-01
相关资源
最近更新 更多