Thiago Resende
Thiago Resende

Reputation: 177

How to segment nearby elements in a binary image using Python/Opencv

I have this binary image in where each ‘curve' represents a hat from a pile of these objects. It was obtained by thresholding a region of the original image of stacked straw hats.

Binary Image

As you can see, these curves have many gaps and holes inside of its shapes, which dificults the use of a technique like cv.connectedcomponentes in order to obtain the amount of objects in the image, which is my goal.

I think if there was some technique to fill in these gaps and/or, mainly, the holes, in smaller parts of the original binary image, like the ones I'm showing bellow, maybe by connecting nearby elements or detecting and filling contours, would be possible to segment each curve as an individual element.

Smaller Region 1

Smaller Region 2

Upvotes: 1

Views: 1186

Answers (1)

Shai
Shai

Reputation: 114786

Not the most elegant way, but it should be simple enough.
Consider a vertical slice of with w (the same as the slices you posted in your question). If you sum the white pixels along the rows of the slice, you should get six nice "peaks" corresponding to the six rims of the hats:

enter image description here

However, since the rims are rounded, some vertical slices would be better than others for this sort of estimation.
Therefore, I suggest looking at all slices of width w and counting the peaks for each slice.
Here's a Matlab code that does this

img = imread('https://i.sstatic.net/69FfJ.jpg');  % read the image
bw = img(:,:,1)>128;  % convert to binary 

w = 75;  % width of slice
all_slices = imfilter(single(bw), ones(1,w)/w, 'symmetric')>.5;  % compute horizontal sum of all slices using filter
% a peak is a slice with more than 50% "white" pixels
peaks = diff( all_slices, 1, 1 ) > 0;  % detect the peaks using vertical diff
count_per_slice = sum( peaks, 1 );  % how many peaks each slice think it sees

Looking at the distribution of the count_per_slice:

enter image description here

You see that although many slices predict the wrong number of hats (between 4 to 9) the majority votes for the correct number 6:

num_hats = mode(count_per_slice);  % take the mode of the distribution.

A python code that does the same (assuming bw is a numpy array of shape (h,w) and of dtype bool):

from scipy import signal, stats
import numpy as np
w = 75;
all_slices = signal.convolve2d( bw.astype('f4'), np.ones((1,w),dtype='f4')/float(w), mode='same', boundary='symmetric')>0.5
peaks = np.diff( all_slices, n=1, axis=0 ) > 0
count_per_slice = peaks.sum( axis=0 )
num_hats = stats.mode( count_per_slice )

Upvotes: 2

Related Questions