从图像中删除像素值总和 < 阈值的连通分量

Posted

技术标签:

【中文标题】从图像中删除像素值总和 < 阈值的连通分量【英文标题】:Remove connected components from image which have a sum of pixel values < threshold 【发布时间】:2018-02-07 16:52:45 【问题描述】:

简单来说,这是一个可用输入和我想要的相应输出的示例:

In: [ 0 0 0 0 0 0 1 0 1 0
      0 6 6 0 0 0 0 1 1 0
      0 8 9 0 0 0 0 0 1 1
      8 0 0 0 9 9 0 0 0 0
      0 0 0 0 8 8 0 0 0 0
      0 0 0 0 0 0 0 0 0 0
      9 9 7 0 0 0 0 0 1 0
      0 6 8 0 0 0 0 3 2 0
      0 0 0 0 0 0 0 2 1 0
      0 0 0 0 0 0 0 0 0 0 ]

使用cv2.connectedComponents()二值化并获得标记图像后:

labels: [ 0 0 0 0 0 0 1 0 1 0
          0 2 2 0 0 0 0 1 1 0
          0 2 2 0 0 0 0 0 1 1
          2 0 0 0 3 3 0 0 0 0
          0 0 0 0 3 3 0 0 0 0
          0 0 0 0 0 0 0 0 0 0
          4 4 4 0 0 0 0 0 5 0
          0 4 4 0 0 0 0 5 5 0
          0 0 0 0 0 0 0 5 5 0
          0 0 0 0 0 0 0 0 0 0 ]

从这里,我想要以下输出:

Out: [0 0 0 0 0 0 0 0 0 0
      0 6 6 0 0 0 0 0 0 0
      0 8 9 0 0 0 0 0 0 0
      8 0 0 0 9 9 0 0 0 0
      0 0 0 0 8 8 0 0 0 0
      0 0 0 0 0 0 0 0 0 0
      9 9 7 0 0 0 0 0 0 0
      0 6 8 0 0 0 0 0 0 0
      0 0 0 0 0 0 0 0 0 0
      0 0 0 0 0 0 0 0 0 0 ]

存在许多连接组件(在本例中:8 连接的 5 个组件)。 (In 的)像素总和 Out 中被删除。

如何在 C++ 中实现这一点(可能使用 OpenCV)?

我已经使用 OpenCV 在 Python 上完成了它,但无法在 C++ 上实现。

这是我 Python 代码的一部分,如果有用的话(labelscv2.connectedComponents() 的输出):

for cnt in range(1, labels.max()+1): 
        idx = np.where(labels.copy() == cnt)
        valMat = In[idx]
        sum_valMat = sum(valMat)
        if sum_valMat > 3000:
            fingerNodes[idx] = 0

输入是一个简单的二维矩阵。这是一个连接组件的例子:

【问题讨论】:

定义“blob”。 ... 我已经编辑了这个问题。我希望它足够清楚。 并非如此。连通性与零有关,对吗?非零点的对角相邻是否构成连接?还是必须是左右连接? 零是背景。我的意思是所有非零元素之间的连通性。 从图中可以看出,对角线算作连接。原著中没有这样的例子。 【参考方案1】:

你已经在 Python 中实现了这个,所以你知道如何解决这个问题。我想能够在 C++ 中实现它是知道你想要使用的库的问题。

您的 Python 实现效率非常低:您遍历标签,并为每个标签访问每个图像像素 (idx = np.where(labels.copy() == cnt))。如果您有多个标签,这可能会变得非常昂贵。

在下面的代码中,我循环遍历图像一次以累积每个标签的图像强度总和,然后使用计算的总和绘制图像(对于带有标签的每个像素,查找总和为该标签),然后再对该图像进行阈值处理。然后,我将此阈值图像用作掩码,将输入图像中您不想保留的像素设置为 0。

我在这里使用DIPlib。虽然我确信如果你真的想使用它,你可以使用 OpenCV 以某种方式复制它。

#include "diplib.h"
#include "dipviewer.h"
#include "diplib/file_io.h"
#include "diplib/regions.h"
#include "diplib/measurement.h"

int main() 
   // Create a test image with similar properties to your example
   dip::Image input = -dip::ImageReadICS( "/Users/cris/newdip/examples/cermet.ics" );
   input.At( input < 120 ) = 0;
   // Display
   dip::viewer::ShowSimple( input, "input image" );
   // Threshold and label
   dip::Image label = dip::Label( input > 0 );
   // Obtain sum of pixels per label
   dip::MeasurementTool measurementTool;
   auto msr = measurementTool.Measure( label, input,  "Mass"  );
   // Paint each label with the measured value
   dip::Image feature = dip::ObjectToMeasurement( label, msr[ "Mass" ] );
   // Create output as a copy of the input, with low feature values set to 0
   dip::Image output = input.Copy();
   output.At( feature < 100000 ) = 0;
   // Display
   dip::viewer::ShowSimple( output, "output image" );
   dip::viewer::Spin();

【讨论】:

感谢您的详细解答!你是对的,我也知道要使用的函数,但我就是不知道如何编码(我是 C++ 新手)。这正是我想要的,除了我更喜欢使用 OpenCV。这只是使用 OpenCV 的较大代码的一小部分。我会用你的答案,它可能对我有帮助:) 谢谢!【参考方案2】:

如果你想坚持使用 opencv,你可以使用cv::calcHist 来计算每个标签的出现次数,以及对应于小于 10 的 bin 值的 filrerout 值。 然后应将生成的标记图像二值化,并按元素乘以源以获得所需的结果。

【讨论】:

我尝试用 C 或 C++ 编写代码,结果卡在了那里(我可以用 Python 轻松完成,因为它很简单)

以上是关于从图像中删除像素值总和 < 阈值的连通分量的主要内容,如果未能解决你的问题,请参考以下文章

二值图像连通分量的提取(python+opencv)

如何使用python OpenCV在单通道图像中找到与特定值匹配的最大连通分量?

Image 中每个像素的连通分量的高度矩阵

从图像中删除区域 - ImageJ

基于连通分量特征的文本检测与分割

LabVIEW彩色图像分割