从图像中移除具有像素值之和<1的连通分量。阈

时间:2018-02-07 16:52:45

标签: c++ opencv image-processing opencv3.0

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

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 中删除10(阈值)。

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

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

这是我的Python代码的一部分,如果有用(标签 cv2.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

输入是一个简单的2D矩阵。这是连接组件的示例:

enter image description here

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

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