--- a +++ b/FastRCNN/utils/postProcessing.py @@ -0,0 +1,118 @@ +from .imageUtils import cropImage +from skimage import exposure, morphology, measure, draw +import numpy as np +import matplotlib.pyplot as plt +import math + + +def watershed_image(img): + """ + use watershed flooding algorithm to extract the loop contour + :param img: type(numpy.ndarray) image in CHW format + :return: type(numpy.ndarray) image in HW format + """ + img_gray = img[1,:,:] + h, w = img_gray.shape + img1 = exposure.equalize_hist(img_gray) + # invert the image + img2 = np.max(img1) - img1 + inner = np.zeros((h, w), np.bool) + centroid = [round(a) for a in findCentroid(img2)] + inner[centroid[0], centroid[1]] = 1 + min_size = round((h + w) / 20) + kernel = morphology.disk(min_size) + inner = morphology.dilation(inner, kernel) + + out = np.zeros((h,w), np.bool) + out[0, 0] = 1 + out[h - 1, 0] = 1 + out[0, w - 1] = 1 + out[h - 1, w - 1] = 1 + out = morphology.dilation(out, kernel) + out[0, :] = 1 + out[h - 1, :] = 1 + out[:, w - 1] = 1 + out[:, 0] = 1 + + markers = np.zeros((h, w), np.int) + markers[inner] = 2 + markers[out] = 1 + + labels = morphology.watershed(img2, markers) + + return labels + +def findCentroid(img): + """ + find the centroid position of a image by weighted method + :param img: (numpy.ndarray) image in HW format + :return: (tuple) (y,x) coordinates of the centroid + """ + h, w = img.shape + # add weighted method later + return h/2, w/2 + + +def flood_fitting(img): + """ + Use watershed flooding algorithm and regional property analysis + to output the fitted ellipse parameters + :param img: (numpy.ndarray) image in CHW format + :return: region property, where property can be accessed through attributes + example: + area, bbox, centroid, major_axis_length, minor_axis_length, orientation + """ + labels = watershed_image(img) + results = measure.regionprops(labels - 1) + sorted(results, key=lambda k: k['area'],reverse=True) + # return the one with largest area + return results[0] + +def show_fitted_ellipse(img): + """ + Show fitted ellipse on the image + :param img: img in CHW format + :return: plot ellipse on top of the image + """ + region1 = flood_fitting(img) + rr, cc = draw.ellipse_perimeter(int(region1['centroid'][0]), int(region1['centroid'][1]), + int(region1['minor_axis_length'] / 2), + int(region1['major_axis_length'] / 2), -region1['orientation'], img.shape[1:]) + plt.imshow(img[1,:,:], cmap='gray') + plt.plot(cc, rr, '.') + + +def img_ellipse_fitting(img, bboxes): + subimages, bboxes = cropImage(img, bboxes) + y_points = np.array([]) + x_points = np.array([]) + for subim, bbox in zip(subimages, bboxes): + region1 = flood_fitting(subim) + result = (int(region1['centroid'][0]+bbox[0]), int(region1['centroid'][1]+bbox[1]), + int(region1['minor_axis_length'] / 2), int(region1['major_axis_length'] / 2), + -region1['orientation']) + rr,cc = draw.ellipse_perimeter(*result) + y_points = np.concatenate((y_points,rr)) + x_points = np.concatenate((x_points,cc)) + fig = plt.figure(figsize=(10,10)) + plt.imshow(img[0,:,:], cmap='gray') + plt.scatter(x_points,y_points,s=(1*72./fig.dpi)**2,alpha=0.5) + +def img_ellipse_fitting_area(img, bboxes): + subimages, bboxes = cropImage(img, bboxes) + ellipse_info_list = list() + for subim, bbox in zip(subimages, bboxes): + region1 = flood_fitting(subim) + result = (int(region1['centroid'][0]+bbox[0]), int(region1['centroid'][1]+bbox[1]), + int(region1['minor_axis_length'] / 2), int(region1['major_axis_length'] / 2), + -region1['orientation']) + ellipse_info_list.append(result) + area = list() + for item in enumerate(ellipse_info_list): + area.append(math.pi * item[1][1] * item[1][2]) + plt.hist(area, bins=50) + plt.xlabel('Area of ellipse') + plt.ylabel('Frequency') + plt.title(r'$\mathrm{Distribution\ of\ Ellipse\ Area}$') + fig = plt.figure(figsize=(10, 10)) + plt.show() \ No newline at end of file