Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shape recognition with numpy/scipy (perhaps watershed)

My goal is to trace drawings that have a lot of separate shapes in them and to split these shapes into individual images. It is black on white. I'm quite new to numpy,opencv&co - but here is my current thought:

  • scan for black pixels
  • black pixel found -> watershed
  • find watershed boundary (as polygon path)
  • continue searching, but ignore points within the already found boundaries

I'm not very good at these kind of things, is there a better way?

First I tried to find the rectangular bounding box of the watershed results (this is more or less a collage of examples):

from numpy import *
import numpy as np
from scipy import ndimage

np.set_printoptions(threshold=np.nan)

a = np.zeros((512, 512)).astype(np.uint8) #unsigned integer type needed by watershed
y, x = np.ogrid[0:512, 0:512]
m1 = ((y-200)**2 + (x-100)**2 < 30**2)
m2 = ((y-350)**2 + (x-400)**2 < 20**2)
m3 = ((y-260)**2 + (x-200)**2 < 20**2)
a[m1+m2+m3]=1

markers = np.zeros_like(a).astype(int16)
markers[0, 0] = 1
markers[200, 100] = 2
markers[350, 400] = 3
markers[260, 200] = 4

res = ndimage.watershed_ift(a.astype(uint8), markers)
unique(res) 

B = argwhere(res.astype(uint8))
(ystart, xstart), (ystop, xstop) = B.min(0), B.max(0) + 1 
tr = a[ystart:ystop, xstart:xstop]

print tr

Somehow, when I use the original array (a) then argwhere seems to work, but after the watershed (res) it just outputs the complete array again.

The next step could be to find the polygon path around the shape, but the bounding box would be great for now!

Please help!

like image 815
user978250 Avatar asked Mar 13 '12 17:03

user978250


2 Answers

@Hooked has already answered most of your question, but I was in the middle of writing this up when he answered, so I'll post it in the hopes that it's still useful...

You're trying to jump through a few too many hoops. You don't need watershed_ift.

You use scipy.ndimage.label to differentiate separate objects in a boolean array and scipy.ndimage.find_objects to find the bounding box of each object.

Let's break things down a bit.

import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt

def draw_circle(grid, x0, y0, radius):
    ny, nx = grid.shape
    y, x = np.ogrid[:ny, :nx]
    dist = np.hypot(x - x0, y - y0)
    grid[dist < radius] = True
    return grid

# Generate 3 circles...
a = np.zeros((512, 512), dtype=np.bool)
draw_circle(a, 100, 200, 30)
draw_circle(a, 400, 350, 20)
draw_circle(a, 200, 260, 20)

# Label the objects in the array. 
labels, numobjects = ndimage.label(a)

# Now find their bounding boxes (This will be a tuple of slice objects)
# You can use each one to directly index your data. 
# E.g. a[slices[0]] gives you the original data within the bounding box of the
# first object.
slices = ndimage.find_objects(labels)

#-- Plotting... -------------------------------------
fig, ax = plt.subplots()
ax.imshow(a)
ax.set_title('Original Data')

fig, ax = plt.subplots()
ax.imshow(labels)
ax.set_title('Labeled objects')

fig, axes = plt.subplots(ncols=numobjects)
for ax, sli in zip(axes.flat, slices):
    ax.imshow(labels[sli], vmin=0, vmax=numobjects)
    tpl = 'BBox:\nymin:{0.start}, ymax:{0.stop}\nxmin:{1.start}, xmax:{1.stop}'
    ax.set_title(tpl.format(*sli))
fig.suptitle('Individual Objects')

plt.show()

enter image description hereenter image description hereenter image description here

Hopefully that makes it a bit clearer how to find the bounding boxes of the objects.

like image 62
Joe Kington Avatar answered Nov 19 '22 06:11

Joe Kington


Use the ndimage library from scipy. The function label places a unique tag on each block of pixels that are within a threshold. This identifies the unique clusters (shapes). Starting with your definition of a:

from scipy import ndimage

image_threshold = .5
label_array, n_features =  ndimage.label(a>image_threshold)

# Plot the resulting shapes
import pylab as plt
plt.subplot(121)
plt.imshow(a)
plt.subplot(122)
plt.imshow(label_array)
plt.show()

enter image description here

like image 29
Hooked Avatar answered Nov 19 '22 06:11

Hooked