从图像中删除像素值总和 < 阈值的连通分量
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 代码的一部分,如果有用的话(labels 是 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
输入是一个简单的二维矩阵。这是一个连接组件的例子:
【问题讨论】:
定义“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 轻松完成,因为它很简单)以上是关于从图像中删除像素值总和 < 阈值的连通分量的主要内容,如果未能解决你的问题,请参考以下文章