fcossio commited on
Commit
e6d6732
β€’
1 Parent(s): 4bda818

first commit of app

Browse files
Files changed (10) hide show
  1. README.md +4 -4
  2. app.py +96 -0
  3. line_fit.py +168 -0
  4. packages.txt +1 -0
  5. requirements.txt +5 -0
  6. test0000.png +0 -0
  7. test0001.png +0 -0
  8. test0002.png +0 -0
  9. test0003.png +0 -0
  10. test0004.png +0 -0
README.md CHANGED
@@ -1,12 +1,12 @@
1
  ---
2
  title: Measure Fiber Diameter
3
- emoji: πŸ“ˆ
4
- colorFrom: red
5
- colorTo: gray
6
  sdk: gradio
7
  sdk_version: 3.0.26
8
  app_file: app.py
9
- pinned: false
10
  license: apache-2.0
11
  ---
12
 
 
1
  ---
2
  title: Measure Fiber Diameter
3
+ emoji: πŸ“
4
+ colorFrom: yellow
5
+ colorTo: blue
6
  sdk: gradio
7
  sdk_version: 3.0.26
8
  app_file: app.py
9
+ pinned: true
10
  license: apache-2.0
11
  ---
12
 
app.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import tensorflow as tf
3
+ import cv2
4
+ import numpy as np
5
+ from itertools import islice
6
+ from PIL import Image, ImageDraw, ImageColor
7
+ from line_fit import LineFit
8
+ import random
9
+
10
+ MAX_SELECTIONS = 8
11
+ input_shape = (MAX_SELECTIONS,256,256,2)
12
+ def load_model():
13
+ model = tf.keras.models.load_model('vivid-sweep-model-best.hdf5')
14
+ return model
15
+
16
+ model = load_model()
17
+ colors = list(ImageColor.colormap.keys())
18
+ linefit = LineFit(30, 0.3)
19
+
20
+ def get_blob_centroids(mask):
21
+ centers = []
22
+ print(mask.dtype)
23
+ print(mask.shape)
24
+ contours, hierarchies = cv2.findContours(
25
+ mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
26
+ for i in contours:
27
+ M = cv2.moments(i)
28
+ if M['m00'] != 0:
29
+ cx = int(M['m10']/M['m00'])
30
+ cy = int(M['m01']/M['m00'])
31
+ centers.append([cx,cy])
32
+ print(cx,cy)
33
+ return centers
34
+
35
+ def predict_mask(input_img, threshold):
36
+ # unpack and reshape input
37
+ im, mask = input_img["image"], input_img["mask"]
38
+ mask = mask[:,:,0].astype(np.uint8)
39
+ im = im.astype(np.float32)/256
40
+
41
+ # get centroids to measure the fibers
42
+ centers = get_blob_centroids(mask)
43
+
44
+ # create a batch of input for the model
45
+ batch = np.zeros([MAX_SELECTIONS,256,256,2], dtype=np.float32)
46
+
47
+ for i, (cx, cy) in enumerate(islice(centers, MAX_SELECTIONS)):
48
+ batch[i,:,:,0] = im
49
+ batch[i,cy,cx,1] = 1.0
50
+
51
+ pred = model.predict(batch, verbose=0).squeeze()
52
+ # create a single image with the background and the foreground
53
+ im = Image.fromarray(im*255).convert("RGBA")
54
+ # m = Image.fromarray(pred[0]>threshold).convert("RGBA")
55
+ # im = Image.blend(im, m, 0.5)
56
+ imgd = ImageDraw.Draw(im)
57
+ ds = []
58
+ for p in islice(pred, len(centers)):
59
+ d, lines = linefit.predict((p>threshold).astype(np.uint8)*255)
60
+ ds.append(d)
61
+ m = Image.fromarray(p>threshold)
62
+ imgd.bitmap([0,0], m, fill=random.choice(colors))
63
+ for line in lines:
64
+ imgd.line(line, fill ="blue", width = 1)
65
+
66
+ return im, ds
67
+
68
+ demo = gr.Blocks()
69
+
70
+ with demo:
71
+ with gr.Row():
72
+ with gr.Column():
73
+ img = gr.Image(
74
+ tool="sketch",
75
+ source="upload",
76
+ label="Mask",
77
+ image_mode='L',
78
+ shape=[256,256],
79
+ value='test0000.png'
80
+ )
81
+ threshold = gr.Slider(
82
+ label='Segmentation threshold', minimum=0, maximum=1, value=0.5)
83
+
84
+ with gr.Row():
85
+ btn = gr.Button("Run")
86
+ with gr.Column():
87
+ img2 = gr.Image()
88
+ text = gr.Text()
89
+
90
+ btn.click(fn=predict_mask, inputs=[img, threshold], outputs=[img2,text], )
91
+ examples = gr.Examples(examples=['test0001.png',
92
+ 'test0002.png',
93
+ 'test0003.png'],
94
+ inputs=img)
95
+
96
+ demo.launch()
line_fit.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import numpy as np
3
+ from scipy.optimize import curve_fit
4
+ from typing import Tuple, List
5
+ import cv2
6
+
7
+ point = Tuple[float,float]
8
+ line = Tuple[point, point]
9
+ measurements = List[line]
10
+
11
+ class LineFit:
12
+ def __init__(self, n: int, step_size:float) -> None:
13
+ """Model that fits a line in a binary image and measures diameter of fibers
14
+
15
+ :param n: number of measurements to do along the fitted line.
16
+ :param step_size: step size of diameter measurement (in pixels). Can be fraction.
17
+ """
18
+ self.n = n
19
+ self.step_size = step_size
20
+
21
+ def get_coordinates(self, im, value_for_mask):
22
+ #I = rgb2gray(I_orig) #we can delete this if we get binary images
23
+ mask = im > value_for_mask
24
+ fiber_coor = np.argwhere(mask)
25
+ x = fiber_coor[:, 1]
26
+ y = fiber_coor[:, 0]
27
+ return x, y
28
+
29
+ def func_line(self, x, a, b):
30
+ return a * x + b
31
+
32
+ def func_line_inv(self, y, a, b):
33
+ return (y - b)/a
34
+
35
+ def get_fited_line_x_y(self, im):
36
+ value_for_mask = (int(np.max(im))+int(np.min(im)))/2 # Pixels to mask in get_coordinate
37
+ x, y = self.get_coordinates(im, value_for_mask)
38
+ popt, pcov = curve_fit(self.func_line, x, y)
39
+ return x, y, popt, pcov
40
+
41
+ def get_fited_line_y_x(self, im):
42
+ value_for_mask = (int(np.max(im))+int(np.min(im)))/2 # Pixels to mask in get_coordinate
43
+ x, y = self.get_coordinates(im, value_for_mask)
44
+ popt, pcov = curve_fit(self.func_line, y, x)
45
+ return x, y, popt, pcov
46
+
47
+ def get_better_fit(self, x, y, popt, popt_inv, pcov, pcov_inv):
48
+ diagonal = np.diagonal(pcov)
49
+ diagonal_inv = np.diagonal(pcov_inv)
50
+ if np.less(diagonal, diagonal_inv).all() == True:
51
+ popt_fit = popt
52
+ x_line = np.arange(0, max(x), 1)
53
+ y_line = []
54
+ for i in x_line:
55
+ a = self.func_line(x_line[i], *popt)
56
+ y_line.append(a)
57
+ y_fit = y_line
58
+ x_fit = x_line
59
+ p1 = [x_fit[0],y_fit[0]]
60
+ p2 = [x_fit[-1],y_fit[-1]]
61
+ elif np.less(diagonal, diagonal_inv).all() == False:
62
+ popt_fit = [1/popt_inv[0], (-popt_inv[1])/popt_inv[0]]
63
+ y_line = np.arange(0, max(y), 1)
64
+ x_line = []
65
+ for i in y_line:
66
+ a = self.func_line(y_line[i], *popt_inv)
67
+ x_line.append(a)
68
+ y_fit = y_line
69
+ x_fit = x_line
70
+ p1 = [x_fit[0],y_fit[0]]
71
+ p2 = [x_fit[-1],y_fit[-1]]
72
+ else:
73
+ print("One of the pcov values is True and the rest are False")
74
+ return popt_fit, x_fit, y_fit, p1, p2
75
+
76
+ def get_point(self, t, p1, p2):
77
+ dx = p2[0]-p1[0]
78
+ dy = p2[1]-p1[1]
79
+ p = [(dx * t + p1[0]), (dy * t + p1[1])]
80
+ return p, dx, dy
81
+
82
+ def get_normal_vector(self, t, dx, dy, p3):
83
+ n_pos = [-dy, dx]
84
+ mag_pos = np.linalg.norm(n_pos)
85
+ nu_pos = n_pos/mag_pos
86
+ u_pos = [(nu_pos[0] * t + p3[0]), (nu_pos[1] * t + p3[1])]
87
+ return u_pos
88
+
89
+ def is_inside(self, im, pos):
90
+ if not (0 <= pos[0] < im.shape[0]):
91
+ return False
92
+ if not (0 <= pos[1] < im.shape[1]):
93
+ return False
94
+ return True
95
+
96
+ def get_pixels_half (self, pos_or_neg, im, dx, dy, p3):
97
+ color_threshold = (int(np.max(im))+int(np.min(im)))/2
98
+ for ts in (range(len(im[0]))):
99
+ u = self.get_normal_vector((pos_or_neg*(ts+(self.step_size))), dx, dy, p3)
100
+ test_point = round(u[1]),round(u[0])
101
+ if not self.is_inside(im, test_point):
102
+ return None, None
103
+ test = im[test_point[0], test_point[1]] > color_threshold
104
+ if test == False:
105
+ pixels = ts - 1
106
+ break
107
+ # plt.plot(u[0], u[1], 'c.', markersize=12)
108
+ return pixels, (u[0], u[1])
109
+
110
+
111
+ def get_calculated_diameter(self, im, p1, p2):
112
+ color_threshold = (int(np.max(im))+int(np.min(im)))/2
113
+ diameters = []
114
+ lines = []
115
+ #mask_meas_lines = np.zeros_like(im)
116
+ for n in range(1, self.n+1):
117
+ t = 1/(self.n+1)
118
+ p3, dx, dy = self.get_point((t * n), p1, p2)
119
+ test_point = round(p3[1]),round(p3[0])
120
+ if not self.is_inside(im, test_point):
121
+ continue
122
+ true_point = im[test_point[0], test_point[1]] > color_threshold
123
+ if true_point == False:
124
+ continue
125
+ if true_point == True:
126
+ radius_p, cp1 = self.get_pixels_half(1, im, dx, dy, p3)
127
+ radius_n, cp2 = self.get_pixels_half(-1, im, dx, dy, p3)
128
+ if (radius_p != None) and (radius_n != None):
129
+ max_val = max(radius_p, radius_n)
130
+ min_val = min(radius_p, radius_n)
131
+ equal = abs((max_val - min_val)/(max_val + 1e-5))
132
+ if equal < 0.1:
133
+ lines.append((cp1,cp2))
134
+ diameters.append(radius_p+radius_n)
135
+ # mask_meas_lines = self.mask_measured_lines(im, lines)
136
+ # plt.plot(p3[0], p3[1], 'r.', markersize=12)
137
+ calculated_diameter = np.array(diameters).mean()
138
+ return calculated_diameter, lines
139
+
140
+ def line_to_arrays(self, line):
141
+ return [line[0][0], line[1][0]], [line[0][1], line[1][1]]
142
+
143
+ def mask_measured_lines(self, im, lines):
144
+ mask = np.zeros_like(im)
145
+ for p1, p2 in lines:
146
+ if not (p1 == None or p2 == None):
147
+ cv2.line(mask, np.array(p1).astype(np.int32), np.array(p2).astype(np.int32), 1, 1)
148
+ return mask
149
+
150
+ def predict(self, im: np.ndarray):
151
+ x, y, popt, pcov = self.get_fited_line_x_y(im)
152
+ _, _, popt_inv, pcov_inv = self.get_fited_line_y_x(im)
153
+ popt_fit, x_fit, y_fit, p1, p2 = self.get_better_fit(x, y, popt, popt_inv, pcov, pcov_inv)
154
+ calculated_diameter, lines = self.get_calculated_diameter(im, p1, p2)
155
+ mask_meas_lines = self.mask_measured_lines(im, lines)
156
+ #for line in lines:
157
+ # plt.plot(*self.line_to_arrays(line), 'c-')
158
+ return calculated_diameter, mask_meas_lines
159
+
160
+ if __name__ == "__main__":
161
+ import os
162
+
163
+ model = LineFit(10, 0.5)
164
+ dataset_path = "/Users/carmenlopez/dev/diameterY/scratch/dataset_files"
165
+ example_path = os.path.join(dataset_path, "test_0005.npz")
166
+ example = np.load(example_path)
167
+ diameter_pred, mask_meas_lines = model.predict(example["x"])
168
+ print(diameter_pred, example["d"])
packages.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ python3-opencv
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ tensorflow
2
+ opencv-python
3
+ gradio
4
+ scipy
5
+ hugging_face_hub
test0000.png ADDED
test0001.png ADDED
test0002.png ADDED
test0003.png ADDED
test0004.png ADDED