|
a |
|
b/FastRCNN/utils/postProcessing.py |
|
|
1 |
from .imageUtils import cropImage |
|
|
2 |
from skimage import exposure, morphology, measure, draw |
|
|
3 |
import numpy as np |
|
|
4 |
import matplotlib.pyplot as plt |
|
|
5 |
import math |
|
|
6 |
|
|
|
7 |
|
|
|
8 |
def watershed_image(img): |
|
|
9 |
""" |
|
|
10 |
use watershed flooding algorithm to extract the loop contour |
|
|
11 |
:param img: type(numpy.ndarray) image in CHW format |
|
|
12 |
:return: type(numpy.ndarray) image in HW format |
|
|
13 |
""" |
|
|
14 |
img_gray = img[1,:,:] |
|
|
15 |
h, w = img_gray.shape |
|
|
16 |
img1 = exposure.equalize_hist(img_gray) |
|
|
17 |
# invert the image |
|
|
18 |
img2 = np.max(img1) - img1 |
|
|
19 |
inner = np.zeros((h, w), np.bool) |
|
|
20 |
centroid = [round(a) for a in findCentroid(img2)] |
|
|
21 |
inner[centroid[0], centroid[1]] = 1 |
|
|
22 |
min_size = round((h + w) / 20) |
|
|
23 |
kernel = morphology.disk(min_size) |
|
|
24 |
inner = morphology.dilation(inner, kernel) |
|
|
25 |
|
|
|
26 |
out = np.zeros((h,w), np.bool) |
|
|
27 |
out[0, 0] = 1 |
|
|
28 |
out[h - 1, 0] = 1 |
|
|
29 |
out[0, w - 1] = 1 |
|
|
30 |
out[h - 1, w - 1] = 1 |
|
|
31 |
out = morphology.dilation(out, kernel) |
|
|
32 |
out[0, :] = 1 |
|
|
33 |
out[h - 1, :] = 1 |
|
|
34 |
out[:, w - 1] = 1 |
|
|
35 |
out[:, 0] = 1 |
|
|
36 |
|
|
|
37 |
markers = np.zeros((h, w), np.int) |
|
|
38 |
markers[inner] = 2 |
|
|
39 |
markers[out] = 1 |
|
|
40 |
|
|
|
41 |
labels = morphology.watershed(img2, markers) |
|
|
42 |
|
|
|
43 |
return labels |
|
|
44 |
|
|
|
45 |
def findCentroid(img): |
|
|
46 |
""" |
|
|
47 |
find the centroid position of a image by weighted method |
|
|
48 |
:param img: (numpy.ndarray) image in HW format |
|
|
49 |
:return: (tuple) (y,x) coordinates of the centroid |
|
|
50 |
""" |
|
|
51 |
h, w = img.shape |
|
|
52 |
# add weighted method later |
|
|
53 |
return h/2, w/2 |
|
|
54 |
|
|
|
55 |
|
|
|
56 |
def flood_fitting(img): |
|
|
57 |
""" |
|
|
58 |
Use watershed flooding algorithm and regional property analysis |
|
|
59 |
to output the fitted ellipse parameters |
|
|
60 |
:param img: (numpy.ndarray) image in CHW format |
|
|
61 |
:return: region property, where property can be accessed through attributes |
|
|
62 |
example: |
|
|
63 |
area, bbox, centroid, major_axis_length, minor_axis_length, orientation |
|
|
64 |
""" |
|
|
65 |
labels = watershed_image(img) |
|
|
66 |
results = measure.regionprops(labels - 1) |
|
|
67 |
sorted(results, key=lambda k: k['area'],reverse=True) |
|
|
68 |
# return the one with largest area |
|
|
69 |
return results[0] |
|
|
70 |
|
|
|
71 |
def show_fitted_ellipse(img): |
|
|
72 |
""" |
|
|
73 |
Show fitted ellipse on the image |
|
|
74 |
:param img: img in CHW format |
|
|
75 |
:return: plot ellipse on top of the image |
|
|
76 |
""" |
|
|
77 |
region1 = flood_fitting(img) |
|
|
78 |
rr, cc = draw.ellipse_perimeter(int(region1['centroid'][0]), int(region1['centroid'][1]), |
|
|
79 |
int(region1['minor_axis_length'] / 2), |
|
|
80 |
int(region1['major_axis_length'] / 2), -region1['orientation'], img.shape[1:]) |
|
|
81 |
plt.imshow(img[1,:,:], cmap='gray') |
|
|
82 |
plt.plot(cc, rr, '.') |
|
|
83 |
|
|
|
84 |
|
|
|
85 |
def img_ellipse_fitting(img, bboxes): |
|
|
86 |
subimages, bboxes = cropImage(img, bboxes) |
|
|
87 |
y_points = np.array([]) |
|
|
88 |
x_points = np.array([]) |
|
|
89 |
for subim, bbox in zip(subimages, bboxes): |
|
|
90 |
region1 = flood_fitting(subim) |
|
|
91 |
result = (int(region1['centroid'][0]+bbox[0]), int(region1['centroid'][1]+bbox[1]), |
|
|
92 |
int(region1['minor_axis_length'] / 2), int(region1['major_axis_length'] / 2), |
|
|
93 |
-region1['orientation']) |
|
|
94 |
rr,cc = draw.ellipse_perimeter(*result) |
|
|
95 |
y_points = np.concatenate((y_points,rr)) |
|
|
96 |
x_points = np.concatenate((x_points,cc)) |
|
|
97 |
fig = plt.figure(figsize=(10,10)) |
|
|
98 |
plt.imshow(img[0,:,:], cmap='gray') |
|
|
99 |
plt.scatter(x_points,y_points,s=(1*72./fig.dpi)**2,alpha=0.5) |
|
|
100 |
|
|
|
101 |
def img_ellipse_fitting_area(img, bboxes): |
|
|
102 |
subimages, bboxes = cropImage(img, bboxes) |
|
|
103 |
ellipse_info_list = list() |
|
|
104 |
for subim, bbox in zip(subimages, bboxes): |
|
|
105 |
region1 = flood_fitting(subim) |
|
|
106 |
result = (int(region1['centroid'][0]+bbox[0]), int(region1['centroid'][1]+bbox[1]), |
|
|
107 |
int(region1['minor_axis_length'] / 2), int(region1['major_axis_length'] / 2), |
|
|
108 |
-region1['orientation']) |
|
|
109 |
ellipse_info_list.append(result) |
|
|
110 |
area = list() |
|
|
111 |
for item in enumerate(ellipse_info_list): |
|
|
112 |
area.append(math.pi * item[1][1] * item[1][2]) |
|
|
113 |
plt.hist(area, bins=50) |
|
|
114 |
plt.xlabel('Area of ellipse') |
|
|
115 |
plt.ylabel('Frequency') |
|
|
116 |
plt.title(r'$\mathrm{Distribution\ of\ Ellipse\ Area}$') |
|
|
117 |
fig = plt.figure(figsize=(10, 10)) |
|
|
118 |
plt.show() |