Reputation: 625
In simple words, this is an example of input available and the corresponding output I want:
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 ]
After binarizing and obtaining labelled image using 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 ]
From here, I want the following output:
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 ]
There are many connected components present (in this case: 5 components of 8-connectivity). The components (of In) which have their sum of pixels < 10 (a threshold) are removed in Out.
How do I implement this in C++ (possibly using OpenCV)?
I have already done it on Python using OpenCV but not able to implement on C++.
This is a part of my Python code, if useful (labels is the output of 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
Input is a simple 2D matrix. This is an example of connected component:
Upvotes: 3
Views: 1562
Reputation: 1965
If you would like to stick with opencv, you can use cv::calcHist
to count the number of occurrences of each label, and filrerout values which corresponds to bin values smaller than 10.
The resulting labeld image should then be binarized, and element-wise multiplied by the source in order to get you desired result.
Upvotes: 0
Reputation: 60504
You've already implemented this in Python, so you know how to solve the problem. I guess being able to implement it in C++ is a matter of knowing the library you want to use.
Your Python implementation is very inefficient: you iterate over the labels, and for each label visit each image pixel (idx = np.where(labels.copy() == cnt)
). This can get really expensive if you have more than a handful of labels.
In the code below, I loop over the image once to accumulate the sum of the image intensities for each of the labels, then once to paint the image with the computed sum (for each pixel with a label, look up the sum for that label), and then one more time to threshold this image. This thresholded image I then use as a mask to set to 0 the pixels in the input image that you don't want to keep.
I use DIPlib here. Though I'm sure you can replicate this using OpenCV in some way if you really want to use it.
#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();
}
Upvotes: 2