Computer Vision for Image Feature Extraction


In [13]:
%pylab inline
from skimage import io
import matplotlib.pyplot as plt


Populating the interactive namespace from numpy and matplotlib
WARNING: pylab import has clobbered these variables: ['gray']
`%matplotlib` prevents importing * from pylab and numpy

A smaller size allows computation intensive algoritms to run reasonably on a PC


In [14]:
image = io.imread('uploads/df947b7905ec613a239a1c4d531e8eab45ccbd6d.jpg')

In [15]:
from skimage.transform import rescale
small = rescale(image, 0.1)

In [16]:
imshow(small)


Out[16]:
<matplotlib.image.AxesImage at 0x7f76b28e3390>

In [17]:
from skimage.color import rgb2gray
gray = rgb2gray(small)
imshow(gray)


Out[17]:
<matplotlib.image.AxesImage at 0x7f76b28bbdd8>

Part of the canny edge detection algorithm is a gaussian blur. This image is just to demonstrate that gaussian blur reduces noise while keeping major edges intact.


In [27]:
from skimage.filters import gaussian_filter
blurry = gaussian_filter(gray, 2)
imshow(blurry)


Out[27]:
<matplotlib.image.AxesImage at 0x7f76b2671a20>

In [28]:
from skimage.feature import canny
# Don't use the blurry image
edges = canny(gray, sigma=2)
imshow(edges)


Out[28]:
<matplotlib.image.AxesImage at 0x7f76b25cea20>

Hough Transforms

Finds circles (or ellipsis) in images


In [48]:
from skimage.transform import hough_circle

hough_radii = np.arange(15, 30)
hough_res = hough_circle(edges, hough_radii)

This is largely a sorting and aggregation step


In [52]:
from skimage.feature import peak_local_max
from skimage.draw import circle_perimeter
from skimage.color import gray2rgb

centers = []
accums = []
radii = []

for radius, h in zip(hough_radii, hough_res):
    # For each radius, extract two circles
    num_peaks = 2
    peaks = peak_local_max(h, num_peaks=num_peaks)
    centers.extend(peaks)
    accums.extend(h[peaks[:, 0], peaks[:, 1]])
    radii.extend([radius] * num_peaks)

coin_center = 0
coin_radius = 0
    
# Draw the most prominent 5 circles
gray_copy = gray2rgb(gray)
for idx in np.argsort(accums)[::-1][:1]:
    coin_center = centers[idx]
    center_x, center_y = centers[idx]
    coin_radius = radii[idx]
    cx, cy = circle_perimeter(center_y, center_x, coin_radius)
    gray_copy[cy, cx] = (220, 20, 20)

In [53]:
imshow(gray_copy)


Out[53]:
<matplotlib.image.AxesImage at 0x7f76a252b2b0>

Equalizes the histogram of the image to stretch image across the spectrum


In [32]:
from skimage.exposure import equalize_hist

equal = equalize_hist(gray)
imshow(equal)


Out[32]:
<matplotlib.image.AxesImage at 0x7f76b2508da0>

On the server the center (cx, cy) and the radius is retrieved from the user


In [34]:
y,x = np.ogrid[:gray.shape[0],:gray.shape[1]]
cx = 90
cy = 45
radius = 30
r2 = (x-cx)*(x-cx) + (y-cy)*(y-cy)

mask = r2 <= radius * radius

We surveyed several algorithms:

Canny -> Marching Squares -> Fill Polygon

Faired the best


In [35]:
from skimage.feature import canny

mole_edge = canny(equal, sigma=2, mask=mask)
imshow(mole_edge)


Out[35]:
<matplotlib.image.AxesImage at 0x7f76b2240160>

In [36]:
from skimage.measure import find_contours
contours = find_contours(mole_edge, 0.9, fully_connected='high')

fig, ax = plt.subplots()
ax.imshow(equal, interpolation='nearest', cmap=plt.cm.gray)

ax.plot(contours[0][:, 1], contours[0][:, 0], linewidth=1)


Out[36]:
[<matplotlib.lines.Line2D at 0x7f76b24edcf8>]

In [37]:
from mahotas.polygon import fill_polygon
from skimage.transform import resize

canvas = np.zeros((gray.shape[0], gray.shape[1]))
fill_polygon(contours[0].astype(np.int), canvas)

In [38]:
import numpy.ma as ma
from skimage.color import rgb2hsv

hsv = rgb2hsv(small)

deviations = []
for color in (0,1,2):
    masked = ma.array(hsv[:,:,color], mask=~canvas.astype(np.bool))
    deviations.append(masked.std())
    
print(deviations)


[0.24497806590678006, 0.051157599482450827, 0.090721389305638056]

Circle estimation is a suboptimal measure for symmetry, but sufficient for our purposes.


In [39]:
from skimage.measure import CircleModel

circle_model = CircleModel()
circle_model.estimate(contours[0])
symmetry = circle_model.residuals(contours[0]).mean()
print(symmetry)


-4.22007751741e-11

In [40]:
diameter = (19.05 / coin_radius) * (circle_model.params[2])
print(diameter )


3.47429150024

In [51]:
from skimage.draw import circle
from skimage.color import gray2rgb
gray_copy = gray2rgb(gray)
cx, cy = circle(circle_model.params[1], circle_model.params[0], circle_model.params[2])
gray_copy[cy, cx] = (220, 20, 20)
center_x, center_y = coin_center
cx, cy = circle(center_y, center_x, coin_radius)
gray_copy[cy, cx] = (220, 20, 20)
imshow(gray_copy)


Out[51]:
<matplotlib.image.AxesImage at 0x7f76a25ccbe0>

In [ ]: