5
votes

I am trying to fit minimum bounding boxes to each of these "speckles" shown below. As part of the image processing pipeline, I use findContours to detect contours in my data, and then draw a minimum bounding box given an array of discovered contours.

The minimum bounding boxes are not very accurate - some features are clearly missed whereas others fail to completely "encapsulate" a full connected feature (and instead is segmented into several small minimum bounding boxes). I have played around with the retrieval modes (RETR_TREE shown below) and the contour approximation method (CHAIN_APPROX_TC89_L1 shown below), but could not find something I really liked. Can someone suggest a more robust strategy to capture these contours more accurately using OpenCV Python?

image 1image 2

import numpy as np
import cv2

# load image from series of frames
for x in range(1, 20):
    convolved = cv2.imread(x.jpg)
    original = convolved.copy

    #convert to grayscale   
    gray = cv2.cvtColor(convolved, cv2.COLOR_BGR2GRAY)

    #find all contours in given frame, store in array
    contours, hierarchy = cv2.findContours(gray,cv2.RETR_TREE, cv2.CHAIN_APPROX_TC89_L1)
    boxArea = []

    #draw minimum bounding box around each discovered contour
    for cnt in contours:
        area = cv2.contourArea(cnt)
        if area > 2 and area < 100:
            rect = cv2.minAreaRect(cnt)
            box = cv2.cv.BoxPoints(rect)
            box = np.int0(box)
            cv2.drawContours(original,[box], 0, (128,255,0),1)           
            boxArea.append(area)

    #save box-fitted image        
    cv2.imwrite('x_boxFitted.jpg', original)
    cv2.waitKey(0)

** Edit: Per Sturkman's suggestion, drawing all possible contours seemed to cover all visually detectable features.

enter image description here

2
please post your codesturkmen
Please see new edit.batlike
I do not know this method of opencv, but usually you can play around with some threshold. Probably canny edge detection. Another strategy would be to use blob detection.Moritz
Is this rather a problem with the detection, or with the drawContours method ?Moritz
try cv2.drawContours( original, contours,-1, (0,0,255), 2) to test drawing all contourssturkmen

2 Answers

2
votes

I know the question is about opencv. But since I am used to skimage, here some ideas (which are certainly available in opencv).

import numpy as np
from matplotlib import pyplot as plt
from skimage import measure
from scipy.ndimage import imread
from skimage import feature
%matplotlib inline

'''
Contour detection using a marching square algorithm.
http://scikit-image.org/docs/dev/auto_examples/plot_contours.html
Not quite sure if this is the best approach since some centers are
biased. Probably, due to some interpolation issue.
'''

image = imread('irregular_blobs.jpg')
contours = measure.find_contours(image,25,
                                 fully_connected='low',
                                positive_orientation='high')
fig, ax = plt.subplots(ncols=1)
ax.imshow(image,cmap=plt.cm.gray)
for n, c in enumerate(contours):
    ax.plot(c[:,1],c[:,0],linewidth=0.5,color='r')

ax.set_ylim(0,250)
ax.set_xlim(0,250)
plt.savefig('skimage_contour.png',dpi=150)

Contour detection

'''
Personally, I would start with some edge detection. For example,
Canny edge detection. Your Image is really nice and it should work.
'''
edges = feature.canny(image, sigma=1.5)
edges = np.asarray(edges)
# create a masked array in order to set the background transparent
m_edges = np.ma.masked_where(edges==0,edges)
fig,ax = plt.subplots()
ax.imshow(image,cmap=plt.cm.gray,alpha=0.25)
ax.imshow(m_edges,cmap=plt.cm.jet_r)


plt.savefig('skimage_canny_overlay.png',dpi=150)

Canny edge detection

In essence, there is no "best method". While for example, edge detection detects the positions very well, some structures remain open. Contour finding on the other hand, yields closed structures but the centers are biased, You have to try and play around with the parameters. If your image has some disturbing background you can use dilation in order to subtract the background. Here are some informations how to perform dilation. Sometimes, closing operations are also useful.

From your posted image, it seems that your threshold is too high or your background too noisy. Lowering the threshold and/or dilation might help.

0
votes

As suggested by Moritz, it would be a good idea to try with a SimpleBlobDetector.

Playing with the different options you will probably find settings suitable for your problem.