In [13]:
%pylab inline
from skimage import io
import matplotlib.pyplot as plt
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]:
In [17]:
from skimage.color import rgb2gray
gray = rgb2gray(small)
imshow(gray)
Out[17]:
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]:
In [28]:
from skimage.feature import canny
# Don't use the blurry image
edges = canny(gray, sigma=2)
imshow(edges)
Out[28]:
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]:
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]:
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]:
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]:
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)
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)
In [40]:
diameter = (19.05 / coin_radius) * (circle_model.params[2])
print(diameter )
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]:
In [ ]: