删除图像中像素值总和小于阈值的连通组件。

3
简单来说,这是一个可用输入和我想要的相应输出的示例:
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个组件)。那些像素和小于10(一个阈值)的In的组件将被移除到Out中。

我该如何用C++实现它(可能要使用OpenCV)?

我已经在Python中使用OpenCV完成了它,但无法在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

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

enter image description here


定义"blob"。 - Jive Dadson
我已经编辑了问题。希望现在更清楚了。 - Saania
不完全是。连通性与零有关,对吧?非零点的对角线相邻是否构成连接?还是连接必须是侧面相邻的? - Jive Dadson
零是背景。我的意思是所有非零元素之间的连接。 - Saania
从图像中可以看出,对角线被视为连接。原始文件中没有这方面的示例。 - Jive Dadson
显示剩余4条评论
2个回答

2
您已经在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();
}

enter image description here


感谢您提供详细的答案!您说得对,我也知道要使用哪些函数,但是我不知道如何编写代码(我是C++新手)。这正是我想要的,只是我更喜欢使用OpenCV。这只是一个使用OpenCV的大型代码的一小部分。我会使用您的答案,它可能会帮助我 :) 谢谢! - Saania

0
如果您想继续使用opencv,可以使用cv::calcHist来计算每个标签的出现次数,并过滤掉对应于小于10的bin值的值。然后,应将结果标记图像二值化,并逐元素地与源相乘,以获得所需的结果。

我试图用C或C++编写它,但卡在那里了(如果用Python很容易做到,因为它很简单) - Saania

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接