【问题标题】:How to efficiently extract certain pixels from cv::Mat image in C++?如何有效地从 C++ 中的 cv::Mat 图像中提取某些像素?
【发布时间】:2019-11-17 16:10:30
【问题描述】:

我正在做一些图像处理,我想从灰度图像中提取某些像素值。我要提取的像素用与灰度图像具有相同尺寸的掩码阵列进行描述。

这很容易在 python 中使用 numpy 数组完成。示例:

pixels = img[mask != 0]

谁能建议我如何在 C++ 中使用 opencv 数据类型 cv::Mat 以有效的方式做到这一点?

更新

我将提供一个更广泛的示例来澄清我的问题。 假设我有一个名为 img 的灰度图像,尺寸为 (3,4)。我还有一个尺寸为 (3,4) 的 mask 数组。我想从 img 数组中提取与 mask 数组中非零值位置相对应的位置的值。 如果我们假设 mask 数组有 4 个非零元素,则需要将 img 数组中的 4 个元素提取(复制)到一个名为 pixels。

img = np.arange(12).reshape((3,4))
# img = array([[ 0,  1,  2,  3],
#              [ 4,  5,  6,  7],
#              [ 8,  9, 10, 11]])

mask = np.zeros_like(img)
mask[0:2, 1] = 255
mask[1, 2:4] = 255
# mask = array([[  0, 255,   0,   0],
#               [  0, 255, 255, 255],
#               [  0,   0,   0,   0]])

pixels = img[mask != 0]
# pixels = array([1, 5, 6, 7])

我想使用 cv::Mat 数组在 C++ 中实现相同的功能。我知道这可以使用 for 循环来完成,但我更喜欢更有效的(矢量化)解决方案,如果有的话。

【问题讨论】:

  • 很高兴见到一位 FE 学生 ;) 遗憾的是 C++ 有点复杂。最有效的方法是用指针扫描像素。检查两个维度是否匹配,然后使用 for 循环并仅比较索引处的值。迭代器在视觉上可能更容易一些,但速度会慢一些(下面的链接中有一个性能表)。如果您想根据值 LUT(查找表)转换这些像素,也可能是一个有趣的解决方案。 docs.opencv.org/3.4.6/db/da5/tutorial_how_to_scan_images.html
  • @MatejJeglič 循环是在 C++ 中执行此操作的最快方法。本教程向您展示了如何扫描图像中的所有像素,这是您需要做的。
  • @Anže,也向您问好!我看到你也潜入了计算机视觉领域。 :)

标签: python c++ numpy opencv image-processing


【解决方案1】:

您必须遍历所有图像像素。首先,您可以使用带有蒙版的参考图像创建图像:

srcImage.copyTo(dstImage, mask);

您现在可以创建函数来对像素进行操作:

//Your function
void doSomething(cv::Point3_<uint8_t> &pixel)
{
    //... in this example you can change value like this: pixel.x = 255 - x means first color channel
}

现在,当您迭代时,您必须检查像素是否等于 0。在 c++ 中,您可以通过多种方式进行迭代:

// .at method: 
// Loop over all rows
for (int r = 0; r < dstImage.rows; r++)
{
    // Loop over all columns
    for (int c = 0; c < dstImage.cols; c++)
    {
        // Obtain pixel
        Point3_<uint8_t> pixel = dstImage.at<Point3_<uint8_t>>(r, c);
        // check if values are zero
        if (pixel.x !=0 && pixel.y !=0 && pixel.z !=0)
        // function
             doSomething(pixel);
        // set result
        dstImage.at<Point3_<uint8_t>>(r, c) = pixel;
    }

}


//with pointers  
// Get pointer to first pixel
Point3_<uint8_t>* pixel = dstImage.ptr<Point3_<uint8_t>>(0, 0);
const Point3_<uint8_t>* endPixel = pixel + dstImage.cols * dstImage.rows;
// Loop over all pixels
for (; pixel != endPixel; pixel++)
{
    // check if values are zero
    if (pixel.x !=0 && pixel.y !=0 && pixel.z !=0)
          doSomething(*pixel);
}


//forEach - utilizes all the cores to apply any function at every pixel - the fastest way
//define Functor
struct Operator
{
    void operator ()(Point3_<uint8_t> &pixel, const int * position) const
    {           
          // check if values are zero
          if (pixel.x !=0 && pixel.y !=0 && pixel.z !=0)
                doSomething(pixel);
    }
};
//execute functor
dstImage.forEach<Point3_<uint8_t>>(Operator());

如果在参考图像中放置蒙版之前没有零值,它将起作用。如果是,您必须使用 forEach 遍历掩码图像。然后您可以使用const int * position 参数int x = position[0]; int y = position[1]; 来检查哪些坐标掩码像素等于0,并且仅对它们在参考图像上执行某些操作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-19
    • 1970-01-01
    • 2020-05-13
    • 1970-01-01
    • 2020-07-09
    • 2016-12-19
    • 1970-01-01
    相关资源
    最近更新 更多