OpenCV掩膜操作,C++中的逐元素赋值

3 投票
2 回答
819 浏览
提问于 2025-04-18 09:30

我想给一个叫 matA 的图像中的每个像素赋值,这个值是根据另一个图像 matB 的值来决定的。我写的代码是一个嵌套的for循环:

clock_t begint=clock();
for(size_t i=0; i<depthImg.rows; i++){
    for(size_t j=0; j<depthImg.cols; j++){
        datatype px=depthImg.at<datatype>(i, j);
        if(px==0)
            depthImg.at<datatype>(i, j)=lastDepthImg.at<datatype>(i, j);
    }
}
cout<<"~~~~~~~~time: "<<clock()-begint<<endl;

这样做大约需要40到70毫秒,处理的图像大小是640*480。

在Python的numpy库中,我可以用一种更简单的方法来实现这个功能,叫做花式索引:

In [18]: b=np.vstack((np.ones(3), np.arange(3)))

In [19]: b
Out[19]: 
array([[ 1.,  1.,  1.],
       [ 0.,  1.,  2.]])

In [22]: a=np.vstack((np.arange(3), np.zeros(3)))

In [23]: a=np.tile(a, (320, 160))

In [24]: a.shape
Out[24]: (640, 480)

In [25]: b=np.tile(b, (320, 160))

In [26]: %timeit a[a==0]=b[a==0]
100 loops, best of 3: 2.81 ms per loop

这样做的速度比我手动写的for循环快得多。

那么在opencv的C++接口中,有没有类似的操作呢?

2 个回答

1

在你的C++代码中,每处理一个像素,你都在调用一个函数,并传入两个索引,这两个索引会被转换成一个平面索引,像这样 i*depthImageCols + j

我的C++技能不太好,但参考这个,我想你可以尝试一些方法,这样可以减少大部分的开销:

MatIterator_<datatype> it1 = depthImg.begin<datatype>(),
                       it1_end = depthImg.end<datatype>();
MatConstIterator_<datatype> it2 = lastDepthImg.begin<datatype>();

for(; it1 != it1_end; ++it1, ++it2) {
    if (*it1 == 0) {
        *it1 = *it2;
    }
}
4

我在我的电脑上无法复现你的时间结果。你的C++代码在我这儿运行不到1毫秒。不过,每当你遇到慢的循环时,at<>()这个函数就应该引起注意。OpenCV有一个关于如何遍历图像的教程,我推荐你去看看。

不过,对于你描述的操作,有更好的方法。Mat::copyTo()可以进行带掩码的操作:

lastDepthImg.copyTo(depthImg, depthImg == 0);

这种方法不仅更快(大约快2倍),而且比你用嵌套循环的方案更容易理解。此外,它还可能利用一些硬件优化,比如SSE。

撰写回答