GSoftTTS commited on
Commit
606a303
·
1 Parent(s): ba4afb1

Upload 5 files

Browse files
app.py ADDED
@@ -0,0 +1,1403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
3
+
4
+ import gc
5
+ import cv2
6
+ import math
7
+ import time
8
+ import random
9
+ import tf_clahe
10
+ import numpy as np
11
+ import pandas as pd
12
+ from tqdm import tqdm
13
+ from scipy import ndimage
14
+ from PIL import Image
15
+ # from keras_cv.utils import conv_utils
16
+ import streamlit as st
17
+ import matplotlib.pyplot as plt
18
+ from matplotlib.colors import Normalize
19
+ from matplotlib.cm import ScalarMappable
20
+
21
+ import matplotlib
22
+ matplotlib.use('Agg')
23
+
24
+ from skimage import exposure
25
+ from skimage.filters import gaussian
26
+ from skimage.restoration import denoise_nl_means, estimate_sigma
27
+ from sklearn.metrics import classification_report, confusion_matrix, ConfusionMatrixDisplay
28
+
29
+
30
+ import tensorflow as tf
31
+ import tensorflow_addons as tfa
32
+ from tensorflow.keras.optimizers import Adam
33
+ from tensorflow.keras.utils import plot_model
34
+ from tensorflow.keras.models import Sequential, Model
35
+ from tensorflow.keras.__internal__.layers import BaseRandomLayer
36
+
37
+ from tensorflow.keras import layers
38
+ from tensorflow.keras.layers import (
39
+ Dense, Flatten, Conv2D, Activation, BatchNormalization,
40
+ MaxPooling2D, AveragePooling2D, GlobalAveragePooling2D,
41
+ Dropout, Input, concatenate, add, Conv2DTranspose, Lambda,
42
+ SpatialDropout2D, Cropping2D, UpSampling2D, LeakyReLU,
43
+ ZeroPadding2D, Reshape, Concatenate, Multiply, Permute, Add
44
+ )
45
+
46
+ from tensorflow.keras.applications import (
47
+ InceptionResNetV2, DenseNet201, ResNet152V2, VGG19,
48
+ EfficientNetV2M, ResNet50V2, Xception, InceptionV3,
49
+ EfficientNetV2S, EfficientNetV2B3, ResNet50, ConvNeXtBase,
50
+ RegNetX032
51
+ )
52
+
53
+ st.set_option('deprecation.showPyplotGlobalUse', False)
54
+
55
+ import ultralytics
56
+ ultralytics.checks()
57
+ from ultralytics import YOLO
58
+
59
+ IMAGE_SIZE = 224
60
+ NUM_CLASSES = 3
61
+
62
+ yolo_weight = './weights_yolo/oai_s_best4.pt'
63
+ seg_model = YOLO(yolo_weight)
64
+
65
+
66
+ def find_boundaries(mask, start, end, top=True, verbose=0):
67
+ # nếu top = True, tìm đường bao bên trên cùng từ left đến right
68
+ # nếu top = False, tìm đường bao dưới cùng từ left đến right
69
+ boundaries = []
70
+ height, width = mask.shape
71
+
72
+ contours, _ = cv2.findContours(255 * mask.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
73
+
74
+ areas = np.array([cv2.contourArea(cnt) for cnt in contours])
75
+ contour = contours[areas.argmax()]
76
+ contour = contour.reshape(-1, 2)
77
+ org_contour = contour.copy()
78
+
79
+ start_idx = ((start - contour) ** 2).sum(axis=-1).argmin()
80
+ end_idx = ((end - contour) ** 2).sum(axis=-1).argmin()
81
+ if start_idx <= end_idx:
82
+ contour = contour[start_idx:end_idx + 1]
83
+ else:
84
+ contour = np.concatenate([contour[start_idx:], contour[:end_idx + 1]])
85
+
86
+ if top:
87
+ sorted_indices = np.argsort(contour[:, 1])[::-1]
88
+ else:
89
+ sorted_indices = np.argsort(contour[:, 1])
90
+ contour = contour[sorted_indices]
91
+
92
+ unique_indices = sorted(np.unique(contour[:, 0], return_index=True)[1])
93
+ contour = contour[unique_indices]
94
+ sorted_indices = np.argsort(contour[:, 0])
95
+ contour = contour[sorted_indices]
96
+ if verbose:
97
+ temp = draw_points(127 * mask.astype(np.uint8), contour, thickness=5)
98
+ temp = draw_points(temp, [start, end], color=[155, 155], thickness=15)
99
+ cv2_imshow(temp)
100
+
101
+ return np.array(contour), np.array(org_contour)
102
+
103
+
104
+ def get_contours(mask, verbose=0):
105
+ limit_points = detect_limit_points(mask, verbose=verbose)
106
+ upper_contour, full_upper = find_boundaries(mask == 1, limit_points[0], limit_points[1], top=False, verbose=verbose)
107
+ lower_contour, full_lower = find_boundaries(mask == 2, limit_points[3], limit_points[2], top=True, verbose=verbose)
108
+ if verbose:
109
+ temp = draw_points(127 * mask, full_upper, thickness=3, color=(255, 0, 0))
110
+ temp = draw_points(temp, full_lower, thickness=3)
111
+ cv2_imshow(temp)
112
+ cv2.imwrite('full.png', temp)
113
+ temp = draw_points(temp, limit_points, thickness=7, color=(0, 0, 255))
114
+ cv2_imshow(temp)
115
+ cv2.imwrite('limit_points.png', temp)
116
+ if verbose:
117
+ temp = draw_points(127 * mask, upper_contour, thickness=3, color=(255, 0, 0))
118
+ temp = draw_points(temp, lower_contour, thickness=3)
119
+ cv2_imshow(temp)
120
+ cv2.imwrite('cropped.png', temp)
121
+
122
+ return upper_contour, lower_contour
123
+
124
+
125
+ def cv2_imshow(images):
126
+ if not isinstance(images, list):
127
+ images = [images]
128
+
129
+ num_images = len(images)
130
+
131
+ # Hiển thị ảnh đơn lẻ trực tiếp bằng imshow
132
+ if num_images == 1:
133
+ image = images[0]
134
+ if len(image.shape) == 3 and image.shape[2] == 3:
135
+ # Ảnh màu (RGB)
136
+ image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
137
+ plt.imshow(image_rgb)
138
+ else:
139
+ # Ảnh xám
140
+ plt.imshow(image, cmap='gray')
141
+
142
+ plt.axis("off")
143
+ plt.show()
144
+ else:
145
+ # Hiển thị nhiều ảnh trên cùng một cột
146
+ fig, ax = plt.subplots(num_images, 1, figsize=(4, 4 * num_images))
147
+
148
+ for i in range(num_images):
149
+ image = images[i]
150
+ if len(image.shape) == 3 and image.shape[2] == 3:
151
+ # Ảnh màu (RGB)
152
+ image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
153
+ ax[i].imshow(image_rgb)
154
+ else:
155
+ # Ảnh xám
156
+ ax[i].imshow(image, cmap='gray')
157
+
158
+ ax[i].axis("off")
159
+
160
+ plt.tight_layout()
161
+ plt.show()
162
+
163
+
164
+ def to_color(image):
165
+ if len(image.shape) == 3 and image.shape[-1] == 3:
166
+ return image
167
+ return cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
168
+
169
+
170
+ def to_gray(image):
171
+ if len(image.shape) == 3 and image.shape[-1] == 3:
172
+ return cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
173
+ return image
174
+
175
+
176
+ def apply_clahe(image, clip_limit=2.0, tile_grid_size=(8, 8)):
177
+ # Convert the image to grayscale if it's a color image
178
+ if len(image.shape) == 3:
179
+ gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
180
+ else:
181
+ gray_image = image
182
+
183
+ # Create a CLAHE object
184
+ clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_grid_size)
185
+
186
+ # Apply CLAHE to the grayscale image
187
+ equalized_image = clahe.apply(gray_image)
188
+
189
+ return equalized_image
190
+
191
+
192
+ def detect_edge(image, minVal=100, maxVal=200, blur_size=(5, 5)):
193
+ image_gray = to_gray(image)
194
+
195
+ blurred_image = cv2.GaussianBlur(image_gray, blur_size, 0)
196
+
197
+ # Phát hiện biên cạnh bằng thuật toán Canny
198
+ edges = cv2.Canny(blurred_image, minVal, maxVal)
199
+
200
+ return edges
201
+
202
+
203
+ def show_mask2(image, mask, label2color={1: (255, 255, 0), 2: (0, 255, 255)}, alpha=0.1):
204
+ # Tạo hình ảnh mask từ mask và bảng ánh xạ màu
205
+ image = to_color(image)
206
+ mask_image = np.zeros_like(image)
207
+ for label, color in label2color.items():
208
+ mask_image[mask == label] = color
209
+
210
+ mask_image = cv2.addWeighted(image, 1 - alpha, mask_image, alpha, 0)
211
+
212
+ # Hiển thị hình ảnh và mask
213
+ fig, ax = plt.subplots(1, 2, figsize=(10, 5))
214
+ ax[0].imshow(image)
215
+ ax[0].set_title("Image")
216
+ ax[0].axis("off")
217
+
218
+ ax[1].imshow(mask_image)
219
+ ax[1].set_title("Mask")
220
+ ax[1].axis("off")
221
+
222
+ plt.show()
223
+
224
+
225
+ def combine_mask(image, mask, label2color={1: (255, 255, 0), 2: (0, 255, 255)}, alpha=0.1):
226
+ image = to_color(image)
227
+ mask_image = np.zeros_like(image)
228
+ for label, color in label2color.items():
229
+ mask_image[mask == label] = color
230
+
231
+ mask_image = cv2.addWeighted(image, 1 - alpha, mask_image, alpha, 0)
232
+ return mask_image
233
+
234
+
235
+ ## help function
236
+ import random
237
+
238
+
239
+ def draw_points(image, points, color=None, random_color=False, same=True, thickness=1):
240
+ if color is None and not random_color:
241
+ color = (0, 255, 0) # Màu mặc định là xanh lá cây (BGR)
242
+ if random_color:
243
+ color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
244
+
245
+ image = to_color(image)
246
+
247
+ for point in points:
248
+ if random_color and not same:
249
+ color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
250
+
251
+ x, y = point
252
+ image = cv2.circle(image, (x, y), thickness, color, -1) # Vẽ điểm lên ảnh
253
+ return image
254
+
255
+
256
+ def draw_lines(image, pairs, color=None, random_color=False, same=True, thickness=1):
257
+ image_with_line = to_color(np.copy(image))
258
+
259
+ if color is None and not random_color:
260
+ color = (0, 255, 0) # Màu mặc định là xanh lá cây (BGR)
261
+ if random_color:
262
+ color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
263
+
264
+ # Vẽ đường thẳng dựa trên danh sách các cặp điểm
265
+ for pair in pairs:
266
+
267
+ if random_color and not same:
268
+ color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
269
+
270
+ start_point = pair[0]
271
+ end_point = pair[1]
272
+ image_with_line = cv2.line(image_with_line, start_point, end_point, color, thickness)
273
+ image_with_line = cv2.circle(image_with_line, start_point, thickness + 1, color, -1)
274
+ image_with_line = cv2.circle(image_with_line, end_point, thickness + 1, color, -1)
275
+
276
+ return image_with_line
277
+
278
+
279
+ def detect_limit_points(mask, verbose=0):
280
+ # tìm giới hạn hai bên của khớp gối
281
+ h, w = mask.shape
282
+ res = []
283
+ upper_pivot = np.array([0, w // 2]) # r c
284
+ lower_pivot = np.array([h, w // 2]) # r c
285
+
286
+ left_slice = slice(0, w // 2)
287
+ right_slice = slice(w // 2, None)
288
+ center_slice = slice(int(0.2 * h), int(0.8 * h))
289
+
290
+ left = np.zeros_like(mask)
291
+ left[center_slice, left_slice] = mask[center_slice, left_slice]
292
+
293
+ right = np.zeros_like(mask)
294
+ right[center_slice, right_slice] = mask[center_slice, right_slice]
295
+
296
+ if verbose:
297
+ cv2_imshow([left, right])
298
+
299
+ pivot = np.array([0, w])
300
+ coords = np.argwhere(left == 1)
301
+ distances = ((coords - pivot) ** 2).sum(axis=-1)
302
+ point = coords[distances.argmax()][::-1]
303
+ res.append(point)
304
+
305
+ pivot = np.array([0, 0])
306
+ coords = np.argwhere(right == 1)
307
+ distances = ((coords - pivot) ** 2).sum(axis=-1)
308
+ point = coords[distances.argmax()][::-1]
309
+ res.append(point)
310
+
311
+ pivot = np.array([h, w])
312
+ coords = np.argwhere(left == 2)
313
+ distances = ((coords - pivot) ** 2).sum(axis=-1)
314
+ point = coords[distances.argmax()][::-1]
315
+ res.append(point)
316
+
317
+ pivot = np.array([h, 0])
318
+ coords = np.argwhere(right == 2)
319
+ distances = ((coords - pivot) ** 2).sum(axis=-1)
320
+ point = coords[distances.argmax()][::-1]
321
+ res.append(point)
322
+
323
+ if verbose:
324
+ cv2_imshow(draw_points(127 * mask, res))
325
+
326
+ return res
327
+
328
+
329
+ def center(contour):
330
+ # array = contour[:,1]
331
+ # min_value = np.min(array)
332
+ # argmax_indices = np.argwhere(array == min_value)
333
+ # if len(argmax_indices) == 1:
334
+ # i = argmax_indices[0]
335
+ # else:
336
+ # i = int(np.median(argmax_indices))
337
+ # return contour[i]
338
+ idx = len(contour) // 2
339
+ return contour[idx]
340
+
341
+
342
+ def pooling_array(array, n, mode='mean'):
343
+ if mode == 'mean':
344
+ pool = lambda x: np.mean(x)
345
+ elif mode == 'min':
346
+ pool = lambda x: np.min(x)
347
+ elif mode == 'sum':
348
+ pool = lambda x: np.sum(x)
349
+
350
+ if n == 1:
351
+ return pool(array)
352
+
353
+ array_length = len(array)
354
+ if array_length < n:
355
+ return array
356
+ segment_length = array_length // n
357
+ remaining_elements = array_length % n
358
+
359
+ if remaining_elements == 0:
360
+ segments = np.split(array, n)
361
+ else:
362
+ mid = remaining_elements * (segment_length + 1)
363
+ segments = np.split(array[:mid], remaining_elements)
364
+ segments += np.split(array[mid:], n - remaining_elements)
365
+
366
+ segments = [pool(segment) for segment in segments]
367
+
368
+ return np.array(segments)
369
+
370
+
371
+ def distance(mask, upper_contour, lower_contour, p=0.12, verbose=0):
372
+ x_center = (center(lower_contour)[0] + center(upper_contour)[0]) // 2
373
+ length = (lower_contour[-1, 0] - lower_contour[0, 0] + upper_contour[-1, 0] - upper_contour[0, 0]) / 2
374
+ crop_length = int(p * length)
375
+ left = x_center - crop_length // 2
376
+ right = x_center + crop_length // 2
377
+ x_min = max(lower_contour[0, 0], upper_contour[0, 0])
378
+ x_max = min(lower_contour[-1, 0], upper_contour[-1, 0])
379
+
380
+ left_idx = np.where(lower_contour[:, 0] == left)[0][0]
381
+ right_idx = np.where(lower_contour[:, 0] == right)[0][0]
382
+ left_lower_contour = lower_contour[left_idx:]
383
+ right_lower_contour = lower_contour[:right_idx + 1][::-1]
384
+
385
+ left_lower_contour = lower_contour[(lower_contour[:, 0] <= left) & (lower_contour[:, 0] >= x_min)]
386
+ right_lower_contour = lower_contour[(lower_contour[:, 0] >= right) & (lower_contour[:, 0] <= x_max)][::-1]
387
+
388
+ left_upper_contour = upper_contour[(upper_contour[:, 0] <= left) & (upper_contour[:, 0] >= x_min)]
389
+ right_upper_contour = upper_contour[(upper_contour[:, 0] >= right) & (upper_contour[:, 0] <= x_max)][::-1]
390
+
391
+ if verbose == 1:
392
+ temp = draw_points(mask * 127, left_lower_contour, color=(0, 255, 0), thickness=3)
393
+ temp = draw_points(temp, right_lower_contour, color=(0, 255, 0), thickness=3)
394
+ temp = draw_points(temp, left_upper_contour, color=(255, 0, 0), thickness=3)
395
+ temp = draw_points(temp, right_upper_contour, color=(255, 0, 0), thickness=3)
396
+ cv2_imshow(temp)
397
+ cv2.imwrite('center_cropped.png', temp)
398
+ links = list(zip(left_upper_contour, left_lower_contour)) + list(zip(right_upper_contour, right_lower_contour))
399
+
400
+ temp = left_upper_contour, right_upper_contour, left_lower_contour, right_lower_contour
401
+
402
+ return left_lower_contour[:, 1] - left_upper_contour[:, 1], right_lower_contour[:, 1] - right_upper_contour[:,
403
+ 1], links, temp
404
+
405
+
406
+ # return None, None, links,temp
407
+ def getMiddle(mask, contour, verbose=0):
408
+ X = contour[:, 0].reshape(-1, 1)
409
+ y = contour[:, 1]
410
+ reg = LinearRegression().fit(X, y)
411
+ i_min = np.argmin(y[int(len(y) * 0.2):int(len(y) * 0.8)]) + int(len(y) * 0.2)
412
+ left = i_min - 1
413
+ right = i_min + 1
414
+ left_check = False
415
+ right_check = False
416
+ if verbose == 1:
417
+ cmask = draw_points(mask, contour, thickness=2, color=(255, 0, 0))
418
+ cmask = draw_points(cmask, np.hstack([X, reg.predict(X).reshape(-1, 1).astype('int')]))
419
+ cv2_imshow(cmask)
420
+ plt.show()
421
+ while True:
422
+ while not left_check:
423
+ if y[left] > reg.predict(X[left].reshape(-1, 1)):
424
+ break
425
+ left -= 1
426
+ while not right_check:
427
+ if y[right] > reg.predict(X[right].reshape(-1, 1)):
428
+ break
429
+ right += 1
430
+ if verbose == 1:
431
+ cmask = draw_points(cmask, [contour[left]], thickness=10, color=(255, 255, 0))
432
+ cmask = draw_points(cmask, [contour[right]], thickness=7, color=(255, 0, 255))
433
+ cv2_imshow(cmask)
434
+ plt.show()
435
+ left_min = np.argmin(y[int(len(y) * 0.2):left]) + int(len(y) * 0.2) if int(len(y) * 0.2) < left else left
436
+ right_min = np.argmin(y[right:int(len(y) * 0.8)]) + right if right < int(len(y) * 0.8) else right
437
+ if y[left_min] > reg.predict(X[left_min].reshape(-1, 1)):
438
+ left_check = True
439
+ if y[right_min] > reg.predict(X[right_min].reshape(-1, 1)):
440
+ right_check = True
441
+ if right_check and left_check:
442
+ break
443
+ left = left_min - 1
444
+ right = right_min + 1
445
+ return min(X.flatten()[left], X.flatten()[right]), max(X.flatten()[left], X.flatten()[right])
446
+
447
+
448
+ def get_JSW(mask, dim=None, pool='mean', p=0.3, verbose=0):
449
+ if isinstance(mask, str):
450
+ mask = cv2.imread(mask, 0)
451
+ if mask is None:
452
+ return np.zeros(10), np.zeros(10)
453
+ uc, lc = get_contours(mask, verbose=verbose)
454
+ left_distances, right_distances, links, contours = distance(mask, uc, lc, p=p, verbose=verbose)
455
+ if verbose:
456
+ print('in getjsw')
457
+ temp = draw_points(mask * 127, contours[0], thickness=3, color=(255, 0, 0))
458
+ temp = draw_points(temp, contours[1], thickness=3, color=(255, 0, 0))
459
+ temp = draw_points(temp, contours[2], thickness=3, color=(0, 255, 0))
460
+ temp = draw_points(temp, contours[3], thickness=3, color=(0, 255, 0))
461
+ temp = draw_lines(temp, links[::6], color=(0, 0, 255))
462
+ cv2_imshow(temp)
463
+ cv2.imwrite("drawn_lines.png", temp)
464
+ if dim:
465
+ left_distances = pooling_array(left_distances, dim, pool)
466
+ right_distances = pooling_array(right_distances, dim, pool)
467
+ return left_distances, right_distances
468
+
469
+
470
+ def seg(img_path, model=seg_model, verbose=0, combine=False):
471
+ img = cv2.imdecode(np.fromstring(img_path.read(), np.uint8), 1)
472
+ # img = cv2.imdecode(np.frombuffer(img_path.read(), np.uint8), 1)
473
+ img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
474
+ # img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
475
+ eimg = cv2.equalizeHist(img)
476
+ clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
477
+ eimg = clahe.apply(eimg)
478
+ eimg = to_color(eimg)
479
+ res = seg_model(eimg, verbose=False)
480
+ mask = res[0].masks.data[0] * (res[0].boxes.cls[0] + 1) + res[0].masks.data[1] * (res[0].boxes.cls[1] + 1)
481
+ mask = mask.cpu().numpy()
482
+ if verbose == 1:
483
+ cv2_imshow(eimg)
484
+ cv2.imwrite('original.png', eimg)
485
+ cv2_imshow(combine_mask(eimg, mask))
486
+ plt.show()
487
+ if combine:
488
+ mask = combine_mask(eimg, mask)
489
+ s1 = np.sum(mask == 1)
490
+ s2 = np.sum(mask == 2)
491
+
492
+ return mask
493
+
494
+
495
+ def split_img(img):
496
+ img_size = img.shape
497
+ return img[:, :(img_size[1] // 3), :], img[:, (img_size[1] // 3 * 2):, :]
498
+
499
+
500
+ def combine_mask(image, mask, label2color={1: (255, 255, 0), 2: (0, 255, 255)}, alpha=0.1):
501
+ image = to_color(image)
502
+ image = cv2.resize(image, mask.shape)
503
+ mask_image = np.zeros_like(image)
504
+ for label, color in label2color.items():
505
+ mask_image[mask == label] = color
506
+
507
+ mask_image = cv2.addWeighted(image, 1 - alpha, mask_image, alpha, 0)
508
+ return mask_image
509
+
510
+
511
+ def check_outliers(mask):
512
+ pass
513
+
514
+
515
+ def find_boundaries_v2(mask, top=True, verbose=0):
516
+ boundaries = []
517
+ height, width = mask.shape
518
+
519
+ contours, _ = cv2.findContours(255 * mask.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
520
+
521
+ areas = np.array([cv2.contourArea(cnt) for cnt in contours])
522
+ contour = contours[areas.argmax()]
523
+ contour = contour.reshape(-1, 2)
524
+ org_contour = contour.copy()
525
+ pos = (contour[:, 1].max() + contour[:, 1].min()) // 2
526
+ idx = np.where(contour[:, 1] == pos)
527
+ if contour[idx[0][0]][0] < contour[idx[0][1]][0] and not top:
528
+ start = contour[idx[0][0]]
529
+ end = contour[idx[0][1]]
530
+ else:
531
+ end = contour[idx[0][0]]
532
+ start = contour[idx[0][1]]
533
+ start_idx = ((start - contour) ** 2).sum(axis=-1).argmin()
534
+ end_idx = ((end - contour) ** 2).sum(axis=-1).argmin()
535
+ if start_idx <= end_idx:
536
+ contour = contour[start_idx:end_idx + 1]
537
+ else:
538
+ contour = np.concatenate([contour[start_idx:], contour[:end_idx + 1]])
539
+ if verbose:
540
+ temp = draw_points(127 * mask.astype(np.uint8), contour, thickness=5)
541
+ temp = draw_points(temp, [start, end], color=[155, 155], thickness=15)
542
+ cv2_imshow(temp)
543
+
544
+ return np.array(contour), np.array(org_contour)
545
+
546
+
547
+ def get_contours_v2(mask, verbose=0):
548
+ upper_contour, full_upper = find_boundaries_v2(mask == 1, top=False, verbose=verbose)
549
+ lower_contour, full_lower = find_boundaries_v2(mask == 2, top=True, verbose=verbose)
550
+ if verbose:
551
+ temp = draw_points(127 * mask, full_upper, thickness=3, color=(255, 0, 0))
552
+ temp = draw_points(temp, full_lower, thickness=3)
553
+ plt.imshow(temp)
554
+ plt.title("Segmentation")
555
+ plt.axis('off')
556
+ plt.show()
557
+ st.pyplot()
558
+ # cv2.imwrite('full.png', temp)
559
+ # temp = draw_points(temp, limit_points, thickness = 7, color = (0, 0, 255))
560
+ # cv2_imshow(temp)
561
+ # cv2.imwrite('limit_points.png', temp)
562
+ if verbose:
563
+ temp = draw_points(127 * mask, upper_contour, thickness=3, color=(255, 0, 0))
564
+ temp = draw_points(temp, lower_contour, thickness=3)
565
+ cv2_imshow(temp)
566
+ # st.pyplot()
567
+ # cv2.imwrite('cropped.png', temp)
568
+
569
+ return upper_contour, lower_contour
570
+
571
+ def normalize_tuple(value, n, name, allow_zero=False):
572
+ """Transforms non-negative/positive integer/integers into an integer tuple.
573
+ Args:
574
+ value: The value to validate and convert. Could an int, or any iterable of
575
+ ints.
576
+ n: The size of the tuple to be returned.
577
+ name: The name of the argument being validated, e.g. "strides" or
578
+ "kernel_size". This is only used to format error messages.
579
+ allow_zero: Default to False. A ValueError will raised if zero is received
580
+ and this param is False.
581
+ Returns:
582
+ A tuple of n integers.
583
+ Raises:
584
+ ValueError: If something else than an int/long or iterable thereof or a
585
+ negative value is
586
+ passed.
587
+ """
588
+ error_msg = (
589
+ f"The `{name}` argument must be a tuple of {n} "
590
+ f"integers. Received: {value}"
591
+ )
592
+
593
+ if isinstance(value, int):
594
+ value_tuple = (value,) * n
595
+ else:
596
+ try:
597
+ value_tuple = tuple(value)
598
+ except TypeError:
599
+ raise ValueError(error_msg)
600
+ if len(value_tuple) != n:
601
+ raise ValueError(error_msg)
602
+ for single_value in value_tuple:
603
+ try:
604
+ int(single_value)
605
+ except (ValueError, TypeError):
606
+ error_msg += (
607
+ f"including element {single_value} of "
608
+ f"type {type(single_value)}"
609
+ )
610
+ raise ValueError(error_msg)
611
+
612
+ if allow_zero:
613
+ unqualified_values = {v for v in value_tuple if v < 0}
614
+ req_msg = ">= 0"
615
+ else:
616
+ unqualified_values = {v for v in value_tuple if v <= 0}
617
+ req_msg = "> 0"
618
+
619
+ if unqualified_values:
620
+ error_msg += (
621
+ f" including {unqualified_values}"
622
+ f" that does not satisfy the requirement `{req_msg}`."
623
+ )
624
+ raise ValueError(error_msg)
625
+
626
+ return value_tuple
627
+
628
+ def adjust_pretrained_weights(model_cls, input_size, name=None):
629
+ weights_model = model_cls(weights='imagenet',
630
+ include_top=False,
631
+ input_shape=(*input_size, 3))
632
+ target_model = model_cls(weights=None,
633
+ include_top=False,
634
+ input_shape=(*input_size, 1))
635
+ weights = weights_model.get_weights()
636
+ weights[0] = np.sum(weights[0], axis=2, keepdims=True)
637
+ target_model.set_weights(weights)
638
+
639
+ del weights_model
640
+ tf.keras.backend.clear_session()
641
+ gc.collect()
642
+ if name:
643
+ target_model._name = name
644
+ return target_model
645
+
646
+ from keras import backend as K
647
+
648
+
649
+ def squeeze_excite_block(input, ratio=16):
650
+ ''' Create a channel-wise squeeze-excite block
651
+
652
+ Args:
653
+ input: input tensor
654
+ filters: number of output filters
655
+
656
+ Returns: a keras tensor
657
+
658
+ References
659
+ - [Squeeze and Excitation Networks](https://arxiv.org/abs/1709.01507)
660
+ '''
661
+ init = input
662
+ channel_axis = 1 if K.image_data_format() == "channels_first" else -1
663
+ filters = int(init.shape[channel_axis])
664
+ se_shape = (1, 1, filters)
665
+
666
+ se = GlobalAveragePooling2D()(init)
667
+ se = Reshape(se_shape)(se)
668
+ se = Dense(filters // ratio, activation='relu', kernel_initializer='he_normal', use_bias=False)(se)
669
+ se = Dense(filters, activation='sigmoid', kernel_initializer='he_normal', use_bias=False)(se)
670
+
671
+ if K.image_data_format() == 'channels_first':
672
+ se = Permute((3, 1, 2))(se)
673
+
674
+ x = Multiply()([init, se])
675
+ return x
676
+
677
+
678
+ def spatial_squeeze_excite_block(input):
679
+ ''' Create a spatial squeeze-excite block
680
+
681
+ Args:
682
+ input: input tensor
683
+
684
+ Returns: a keras tensor
685
+
686
+ References
687
+ - [Concurrent Spatial and Channel Squeeze & Excitation in Fully Convolutional Networks](https://arxiv.org/abs/1803.02579)
688
+ '''
689
+
690
+ se = Conv2D(1, (1, 1), activation='sigmoid', use_bias=False,
691
+ kernel_initializer='he_normal')(input)
692
+
693
+ x = Multiply()([input, se])
694
+ return x
695
+
696
+
697
+ def channel_spatial_squeeze_excite(input, ratio=16):
698
+ ''' Create a spatial squeeze-excite block
699
+
700
+ Args:
701
+ input: input tensor
702
+ filters: number of output filters
703
+
704
+ Returns: a keras tensor
705
+
706
+ References
707
+ - [Squeeze and Excitation Networks](https://arxiv.org/abs/1709.01507)
708
+ - [Concurrent Spatial and Channel Squeeze & Excitation in Fully Convolutional Networks](https://arxiv.org/abs/1803.02579)
709
+ '''
710
+
711
+ cse = squeeze_excite_block(input, ratio)
712
+ sse = spatial_squeeze_excite_block(input)
713
+
714
+ x = Add()([cse, sse])
715
+ return x
716
+
717
+ def DoubleConv(filters, kernel_size, initializer='glorot_uniform'):
718
+ def layer(x):
719
+
720
+ x = Conv2D(filters, kernel_size, padding='same', kernel_initializer=initializer)(x)
721
+ x = BatchNormalization()(x)
722
+ x = Activation('swish')(x)
723
+ x = Conv2D(filters, kernel_size, padding='same', kernel_initializer=initializer)(x)
724
+ x = BatchNormalization()(x)
725
+ x = Activation('swish')(x)
726
+
727
+ return x
728
+
729
+ return layer
730
+
731
+ def UpSampling2D_block(filters, kernel_size=(3, 3), upsample_rate=(2, 2), interpolation='bilinear',
732
+ initializer='glorot_uniform', skip=None):
733
+ def layer(input_tensor):
734
+
735
+ x = UpSampling2D(size=upsample_rate, interpolation=interpolation)(input_tensor)
736
+
737
+ if skip is not None:
738
+ x = Concatenate()([x, skip])
739
+
740
+ x = DoubleConv(filters, kernel_size, initializer=initializer)(x)
741
+ x = channel_spatial_squeeze_excite(x)
742
+ return x
743
+
744
+ return layer
745
+
746
+ def Conv2DTranspose_block(filters, transpose_kernel_size=(3, 3), upsample_rate=(2, 2),
747
+ initializer='glorot_uniform', skip=None, met_input=None, sat_input=None):
748
+ def layer(input_tensor):
749
+ x = Conv2DTranspose(filters, transpose_kernel_size, strides=upsample_rate, padding='same')(input_tensor)
750
+ if skip is not None:
751
+ x = Concatenate()([x, skip])
752
+
753
+ x = DoubleConv(filters, transpose_kernel_size, initializer=initializer)(x)
754
+ x = channel_spatial_squeeze_excite(x)
755
+ return x
756
+
757
+ return layer
758
+
759
+ def PixelShuffle_block(filters, kernel_size=(3, 3), upsample_rate=2,
760
+ initializer='glorot_uniform', skip=None, met_input=None, sat_input=None):
761
+ def layer(input_tensor):
762
+ x = Conv2D(filters * (upsample_rate ** 2), kernel_size, padding="same",
763
+ activation="swish", kernel_initializer='Orthogonal')(input_tensor)
764
+ x = tf.nn.depth_to_space(x, upsample_rate)
765
+ if skip is not None:
766
+ x = Concatenate()([x, skip])
767
+
768
+ x = DoubleConv(filters, kernel_size, initializer=initializer)(x)
769
+ x = channel_spatial_squeeze_excite(x)
770
+ return x
771
+
772
+ return layer
773
+
774
+ class DropBlockNoise(BaseRandomLayer):
775
+ def __init__(
776
+ self,
777
+ rate,
778
+ block_size,
779
+ seed=None,
780
+ **kwargs,
781
+ ):
782
+ super().__init__(seed=seed, **kwargs)
783
+ if not 0.0 <= rate <= 1.0:
784
+ raise ValueError(
785
+ f"rate must be a number between 0 and 1. " f"Received: {rate}"
786
+ )
787
+
788
+ self._rate = rate
789
+ (
790
+ self._dropblock_height,
791
+ self._dropblock_width,
792
+ ) = normalize_tuple(
793
+ value=block_size, n=2, name="block_size", allow_zero=False
794
+ )
795
+ self.seed = seed
796
+
797
+ def call(self, x, training=None):
798
+ if not training or self._rate == 0.0:
799
+ return x
800
+
801
+ _, height, width, _ = tf.split(tf.shape(x), 4)
802
+
803
+ # Unnest scalar values
804
+ height = tf.squeeze(height)
805
+ width = tf.squeeze(width)
806
+
807
+ dropblock_height = tf.math.minimum(self._dropblock_height, height)
808
+ dropblock_width = tf.math.minimum(self._dropblock_width, width)
809
+
810
+ gamma = (
811
+ self._rate
812
+ * tf.cast(width * height, dtype=tf.float32)
813
+ / tf.cast(dropblock_height * dropblock_width, dtype=tf.float32)
814
+ / tf.cast(
815
+ (width - self._dropblock_width + 1)
816
+ * (height - self._dropblock_height + 1),
817
+ tf.float32,
818
+ )
819
+ )
820
+
821
+ # Forces the block to be inside the feature map.
822
+ w_i, h_i = tf.meshgrid(tf.range(width), tf.range(height))
823
+ valid_block = tf.logical_and(
824
+ tf.logical_and(
825
+ w_i >= int(dropblock_width // 2),
826
+ w_i < width - (dropblock_width - 1) // 2,
827
+ ),
828
+ tf.logical_and(
829
+ h_i >= int(dropblock_height // 2),
830
+ h_i < width - (dropblock_height - 1) // 2,
831
+ ),
832
+ )
833
+
834
+ valid_block = tf.reshape(valid_block, [1, height, width, 1])
835
+
836
+ random_noise = self._random_generator.random_uniform(
837
+ tf.shape(x), dtype=tf.float32
838
+ )
839
+ valid_block = tf.cast(valid_block, dtype=tf.float32)
840
+ seed_keep_rate = tf.cast(1 - gamma, dtype=tf.float32)
841
+ block_pattern = (1 - valid_block + seed_keep_rate + random_noise) >= 1
842
+ block_pattern = tf.cast(block_pattern, dtype=tf.float32)
843
+
844
+ window_size = [1, self._dropblock_height, self._dropblock_width, 1]
845
+
846
+ # Double negative and max_pool is essentially min_pooling
847
+ block_pattern = -tf.nn.max_pool(
848
+ -block_pattern,
849
+ ksize=window_size,
850
+ strides=[1, 1, 1, 1],
851
+ padding="SAME",
852
+ )
853
+
854
+ return (
855
+ x * tf.cast(block_pattern, x.dtype)
856
+ )
857
+
858
+ def get_efficient_unet(name=None,
859
+ option='full',
860
+ input_shape=(IMAGE_SIZE, IMAGE_SIZE, 1),
861
+ encoder_weights=None,
862
+ block_type='conv-transpose',
863
+ output_activation='sigmoid',
864
+ kernel_initializer='glorot_uniform'):
865
+
866
+ if encoder_weights == 'imagenet':
867
+ encoder = adjust_pretrained_weights(EfficientNetV2S, input_shape[:-1], name)
868
+ elif encoder_weights is None:
869
+ encoder = EfficientNetV2S(weights=None,
870
+ include_top=False,
871
+ input_shape=input_shape)
872
+ encoder._name = name
873
+ else:
874
+ raise ValueError(encoder_weights)
875
+
876
+ if option == 'encoder':
877
+ return encoder
878
+
879
+ MBConvBlocks = []
880
+
881
+ skip_candidates = ['1b', '2d', '3d', '4f']
882
+
883
+ for mbblock_nr in skip_candidates:
884
+ mbblock = encoder.get_layer('block{}_add'.format(mbblock_nr)).output
885
+ MBConvBlocks.append(mbblock)
886
+
887
+ head = encoder.get_layer('top_activation').output
888
+ blocks = MBConvBlocks + [head]
889
+
890
+ if block_type == 'upsampling':
891
+ UpBlock = UpSampling2D_block
892
+ elif block_type == 'conv-transpose':
893
+ UpBlock = Conv2DTranspose_block
894
+ elif block_type == 'pixel-shuffle':
895
+ UpBlock = PixelShuffle_block
896
+ else:
897
+ raise ValueError(block_type)
898
+
899
+ o = blocks.pop()
900
+ o = UpBlock(512, initializer=kernel_initializer, skip=blocks.pop())(o)
901
+ o = UpBlock(256, initializer=kernel_initializer, skip=blocks.pop())(o)
902
+ o = UpBlock(128, initializer=kernel_initializer, skip=blocks.pop())(o)
903
+ o = UpBlock(64, initializer=kernel_initializer, skip=blocks.pop())(o)
904
+ o = UpBlock(32, initializer=kernel_initializer, skip=None)(o)
905
+ o = Conv2D(input_shape[-1], (1, 1), padding='same', activation=output_activation, kernel_initializer=kernel_initializer)(o)
906
+
907
+ model = Model(encoder.input, o, name=name)
908
+
909
+ if option == 'full':
910
+ return model, encoder
911
+ elif option == 'model':
912
+ return model
913
+ else:
914
+ raise ValueError(option)
915
+
916
+
917
+ def acc(y_true, y_pred, threshold=0.5):
918
+ threshold = tf.cast(threshold, y_pred.dtype)
919
+ y_pred = tf.cast(y_pred > threshold, y_pred.dtype)
920
+ return tf.reduce_mean(tf.cast(tf.equal(y_true, y_pred), tf.float32))
921
+
922
+ def mae(y_true, y_pred):
923
+ return tf.reduce_mean(tf.abs(y_true-y_pred))
924
+
925
+ def inv_ssim(y_true, y_pred):
926
+ return 1 - tf.reduce_mean(tf.image.ssim(y_true, y_pred, 1.0))
927
+
928
+ def inv_msssim(y_true, y_pred):
929
+ return 1 - tf.reduce_mean(tf.image.ssim_multiscale(y_true, y_pred, 1.0, filter_size=4))
930
+
931
+ def inv_msssim_l1(y_true, y_pred, alpha=0.8):
932
+ return alpha*inv_msssim(y_true, y_pred) + (1-alpha)*mae(y_true, y_pred)
933
+
934
+ def inv_msssim_gaussian_l1(y_true, y_pred, alpha=0.8):
935
+ l1_diff = tf.abs(y_true-y_pred)
936
+ gaussian_l1 = tfa.image.gaussian_filter2d(l1_diff, filter_shape=(11, 11), sigma=1.5)
937
+ return alpha*inv_msssim(y_true, y_pred) + (1-alpha)*gaussian_l1
938
+
939
+ def psnr(y_true, y_pred):
940
+ return tf.reduce_mean(tf.image.psnr(y_true, y_pred, 1.0))
941
+
942
+
943
+ class MultipleTrackers():
944
+ def __init__(self, callback_lists: list):
945
+ self.callbacks_list = callback_lists
946
+
947
+ def __getattr__(self, attr):
948
+ def helper(*arg, **kwarg):
949
+ for cb in self.callbacks_list:
950
+ getattr(cb, attr)(*arg, **kwarg)
951
+ if attr in self.__class__.__dict__:
952
+ return getattr(self, attr)
953
+ else:
954
+ return helper
955
+
956
+ class DCGAN():
957
+ def __init__(self,
958
+ input_shape=(IMAGE_SIZE, IMAGE_SIZE, 1),
959
+ architecture='two-stage',
960
+ pretrain_weights=None,
961
+ output_activation='sigmoid',
962
+ block_type='conv-transpose',
963
+ kernel_initializer='glorot_uniform',
964
+ noise=None,
965
+ C=1.):
966
+
967
+ self.C = C
968
+ # Build
969
+ kwargs = dict(input_shape=input_shape,
970
+ output_activation=output_activation,
971
+ encoder_weights=pretrain_weights,
972
+ block_type=block_type,
973
+ kernel_initializer=kernel_initializer)
974
+
975
+ if architecture == 'two-stage':
976
+ encoder = get_efficient_unet(name='dcgan_disc',
977
+ option='encoder',
978
+ **kwargs)
979
+
980
+ self.generator = get_efficient_unet(name='dcgan_gen', option='model', **kwargs)
981
+ elif architecture == 'shared':
982
+
983
+ self.generator, encoder = get_efficient_unet(name='dcgan', option='full', **kwargs)
984
+ else:
985
+ raise ValueError(f'Unsupport architecture: {architecture}')
986
+
987
+ gpooling = GlobalAveragePooling2D()(encoder.output)
988
+ prediction = Dense(1, activation='sigmoid')(gpooling)
989
+ self.discriminator = Model(encoder.input, prediction, name='dcgan_disc')
990
+
991
+ tf.keras.backend.clear_session()
992
+ _ = gc.collect()
993
+
994
+ if noise:
995
+ gen_inputs = self.generator.input
996
+ corrupted_inputs = noise(gen_inputs)
997
+ outputs = self.generator(corrupted_inputs)
998
+ self.generator = Model(gen_inputs, outputs, name='dcgan_gen')
999
+
1000
+ tf.keras.backend.clear_session()
1001
+ _ = gc.collect()
1002
+
1003
+ if output_activation == 'tanh':
1004
+
1005
+ self.process_input = layers.Lambda(lambda img: (img*2.-1.), name='dcgan_normalize')
1006
+ self.process_output = layers.Lambda(lambda img: (img*0.5+0.5), name='dcgan_denormalize')
1007
+ gen_inputs = self.generator.input
1008
+ process_inputs = self.process_input(gen_inputs)
1009
+ process_inputs = self.generator(process_inputs)
1010
+ gen_outputs = self.process_output(process_inputs)
1011
+ self.generator = Model(gen_inputs, gen_outputs, name='dcgan_gen')
1012
+
1013
+ disc_inputs = self.discriminator.input
1014
+ process_inputs = self.process_input(disc_inputs)
1015
+ disc_outputs = self.discriminator(process_inputs)
1016
+ self.discriminator = Model(disc_inputs, disc_outputs, name='dcgan_disc')
1017
+
1018
+ tf.keras.backend.clear_session()
1019
+ _ = gc.collect()
1020
+
1021
+ def summary(self):
1022
+ self.generator.summary()
1023
+ self.discriminator.summary()
1024
+
1025
+ def compile(self,
1026
+ generator_optimizer=Adam(5e-4, 0.5),
1027
+ discriminator_optimizer=Adam(5e-4),
1028
+ reconstruction_loss=mae,
1029
+ discriminative_loss=tf.keras.losses.BinaryCrossentropy(from_logits=False),
1030
+ reconstruction_metrics=[],
1031
+ discriminative_metrics=[]):
1032
+
1033
+ self.discriminator_optimizer = discriminator_optimizer
1034
+ self.discriminator.compile(optimizer=self.discriminator_optimizer)
1035
+
1036
+ self.generator_optimizer = generator_optimizer
1037
+ self.generator.compile(optimizer=self.generator_optimizer)
1038
+
1039
+ self.loss = discriminative_loss
1040
+ self.reconstruction_loss = reconstruction_loss
1041
+ self.d_loss_tracker = tf.keras.metrics.Mean()
1042
+ self.g_loss_tracker = tf.keras.metrics.Mean()
1043
+ self.g_recon_tracker = tf.keras.metrics.Mean()
1044
+ self.g_disc_tracker = tf.keras.metrics.Mean()
1045
+
1046
+ self.g_metric_trackers = [(tf.keras.metrics.Mean(), metric) for metric in reconstruction_metrics]
1047
+ self.d_metric_trackers = [(tf.keras.metrics.Mean(), tf.keras.metrics.Mean(), tf.keras.metrics.Mean(), metric) for metric in discriminative_metrics]
1048
+
1049
+ all_trackers = [self.d_loss_tracker, self.g_loss_tracker, self.g_recon_tracker, self.g_disc_tracker] + \
1050
+ [tracker for tracker,_ in self.g_metric_trackers] + \
1051
+ [tracker for t in self.d_metric_trackers for tracker in t[:-1]]
1052
+ self.all_trackers = MultipleTrackers(all_trackers)
1053
+
1054
+ def discriminator_loss(self, real_output, fake_output):
1055
+ real_loss = self.loss(tf.ones_like(real_output), real_output)
1056
+ fake_loss = self.loss(tf.zeros_like(fake_output), fake_output)
1057
+ total_loss = 0.5*(real_loss + fake_loss)
1058
+ return total_loss
1059
+
1060
+ def generator_loss(self, fake_output):
1061
+ return self.loss(tf.ones_like(fake_output), fake_output)
1062
+
1063
+ @tf.function
1064
+ def train_step(self, images):
1065
+ masked, original = images
1066
+ n_samples = tf.shape(original)[0]
1067
+
1068
+ with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
1069
+ generated_images = self.generator(masked, training=True)
1070
+
1071
+ real_output = self.discriminator(original, training=True)
1072
+ fake_output = self.discriminator(generated_images, training=True)
1073
+
1074
+ gen_disc_loss = self.generator_loss(fake_output)
1075
+ recon_loss = self.reconstruction_loss(original, generated_images)
1076
+ gen_loss = self.C*recon_loss + gen_disc_loss
1077
+ disc_loss = self.discriminator_loss(real_output, fake_output)
1078
+
1079
+ gradients_of_generator = gen_tape.gradient(gen_loss, self.generator.trainable_variables)
1080
+ gradients_of_discriminator = disc_tape.gradient(disc_loss, self.discriminator.trainable_variables)
1081
+
1082
+ self.generator_optimizer.apply_gradients(zip(gradients_of_generator, self.generator.trainable_variables))
1083
+ self.discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, self.discriminator.trainable_variables))
1084
+
1085
+ self.d_loss_tracker.update_state(tf.repeat([[disc_loss]], repeats=n_samples, axis=0))
1086
+ self.g_loss_tracker.update_state(tf.repeat([[gen_loss]], repeats=n_samples, axis=0))
1087
+ self.g_recon_tracker.update_state(tf.repeat([[recon_loss]], repeats=n_samples, axis=0))
1088
+ self.g_disc_tracker.update_state(tf.repeat([[gen_disc_loss]], repeats=n_samples, axis=0))
1089
+
1090
+ logs = {'d_loss': self.d_loss_tracker.result()}
1091
+
1092
+ for tracker, real_tracker, fake_tracker, metric in self.d_metric_trackers:
1093
+ v_real = metric(tf.ones_like(real_output), real_output)
1094
+ v_fake = metric(tf.zeros_like(fake_output), fake_output)
1095
+ v = 0.5*(v_real + v_fake)
1096
+ tracker.update_state(tf.repeat([[v]], repeats=n_samples, axis=0))
1097
+ real_tracker.update_state(tf.repeat([[v_real]], repeats=n_samples, axis=0))
1098
+ fake_tracker.update_state(tf.repeat([[v_fake]], repeats=n_samples, axis=0))
1099
+
1100
+ metric_name = metric.__name__
1101
+ logs['d_' + metric_name] = tracker.result()
1102
+ logs['d_real_' + metric_name] = real_tracker.result()
1103
+ logs['d_fake_' + metric_name] = fake_tracker.result()
1104
+
1105
+ logs['g_loss'] = self.g_loss_tracker.result()
1106
+ logs['g_recon'] = self.g_recon_tracker.result()
1107
+ logs['g_disc'] = self.g_disc_tracker.result()
1108
+
1109
+ for tracker, metric in self.g_metric_trackers:
1110
+ v = metric(original, generated_images)
1111
+ tracker.update_state(tf.repeat([[v]], repeats=n_samples, axis=0))
1112
+ logs['g_' + metric.__name__] = tracker.result()
1113
+
1114
+ return logs
1115
+
1116
+ @tf.function
1117
+ def val_step(self, images):
1118
+ masked, original = images
1119
+ n_samples = tf.shape(original)[0]
1120
+
1121
+ generated_images = self.generator(masked, training=False)
1122
+
1123
+ real_output = self.discriminator(original, training=False)
1124
+ fake_output = self.discriminator(generated_images, training=False)
1125
+
1126
+ gen_disc_loss = self.generator_loss(fake_output)
1127
+ recon_loss = self.reconstruction_loss(original, generated_images)
1128
+ gen_loss = self.C*recon_loss + gen_disc_loss
1129
+ disc_loss = self.discriminator_loss(real_output, fake_output)
1130
+
1131
+ self.d_loss_tracker.update_state(tf.repeat([[disc_loss]], repeats=n_samples, axis=0))
1132
+ self.g_loss_tracker.update_state(tf.repeat([[gen_loss]], repeats=n_samples, axis=0))
1133
+ self.g_recon_tracker.update_state(tf.repeat([[recon_loss]], repeats=n_samples, axis=0))
1134
+ self.g_disc_tracker.update_state(tf.repeat([[gen_disc_loss]], repeats=n_samples, axis=0))
1135
+
1136
+ logs = {'val_d_loss': self.d_loss_tracker.result()}
1137
+
1138
+ for tracker, real_tracker, fake_tracker, metric in self.d_metric_trackers:
1139
+ v_real = metric(tf.ones_like(real_output), real_output)
1140
+ v_fake = metric(tf.zeros_like(fake_output), fake_output)
1141
+ v = 0.5*(v_real + v_fake)
1142
+ tracker.update_state(tf.repeat([[v]], repeats=n_samples, axis=0))
1143
+ real_tracker.update_state(tf.repeat([[v_real]], repeats=n_samples, axis=0))
1144
+ fake_tracker.update_state(tf.repeat([[v_fake]], repeats=n_samples, axis=0))
1145
+
1146
+ metric_name = metric.__name__
1147
+ logs['val_d_' + metric_name] = tracker.result()
1148
+ logs['val_d_real_' + metric_name] = real_tracker.result()
1149
+ logs['val_d_fake_' + metric_name] = fake_tracker.result()
1150
+
1151
+ logs['val_g_loss'] = self.g_loss_tracker.result()
1152
+ logs['val_g_recon'] = self.g_recon_tracker.result()
1153
+ logs['val_g_disc'] = self.g_disc_tracker.result()
1154
+
1155
+ for tracker, metric in self.g_metric_trackers:
1156
+ v = metric(original, generated_images)
1157
+ tracker.update_state(tf.repeat([[v]], repeats=n_samples, axis=0))
1158
+ logs['val_g_' + metric.__name__] = tracker.result()
1159
+
1160
+ return logs
1161
+
1162
+ def fit(self,
1163
+ trainset,
1164
+ valset=None,
1165
+ trainsize=-1,
1166
+ valsize=-1,
1167
+ epochs=1,
1168
+ display_per_epochs=5,
1169
+ generator_callbacks=[],
1170
+ discriminator_callbacks=[]):
1171
+
1172
+ print('🌊🐉 Start Training 🐉🌊')
1173
+ gen_callback_tracker = tf.keras.callbacks.CallbackList(
1174
+ generator_callbacks, add_history=True, model=self.generator
1175
+ )
1176
+
1177
+ disc_callback_tracker = tf.keras.callbacks.CallbackList(
1178
+ discriminator_callbacks, add_history=True, model=self.discriminator
1179
+ )
1180
+
1181
+ callbacks_tracker = MultipleTrackers([gen_callback_tracker, disc_callback_tracker])
1182
+
1183
+ logs = {}
1184
+ callbacks_tracker.on_train_begin(logs=logs)
1185
+
1186
+ for epoch in range(epochs):
1187
+ print(f'Epochs {epoch+1}/{epochs}:')
1188
+ callbacks_tracker.on_epoch_begin(epoch, logs=logs)
1189
+
1190
+ batches = tqdm(trainset,
1191
+ desc="Train",
1192
+ total=trainsize,
1193
+ unit="step",
1194
+ position=0,
1195
+ leave=True)
1196
+
1197
+ for batch, image_batch in enumerate(batches):
1198
+
1199
+ callbacks_tracker.on_batch_begin(batch, logs=logs)
1200
+ callbacks_tracker.on_train_batch_begin(batch, logs=logs)
1201
+
1202
+ train_logs = {k:v.numpy() for k, v in self.train_step(image_batch).items()}
1203
+ logs.update(train_logs)
1204
+
1205
+ callbacks_tracker.on_train_batch_end(batch, logs=logs)
1206
+ callbacks_tracker.on_batch_end(batch, logs=logs)
1207
+ batches.set_postfix({'d_loss': train_logs['d_loss'],
1208
+ 'g_loss': train_logs['g_loss']
1209
+ })
1210
+
1211
+ # Presentation
1212
+ stats = ", ".join("{}={:.3g}".format(k, v) for k, v in logs.items() if 'val_' not in k and 'loss' not in k)
1213
+ print('Train:', stats)
1214
+
1215
+ batches.close()
1216
+ if valset:
1217
+ self.all_trackers.reset_state()
1218
+
1219
+ batches = tqdm(valset,
1220
+ desc="Valid",
1221
+ total=valsize,
1222
+ unit="step",
1223
+ position=0,
1224
+ leave=True)
1225
+
1226
+ for batch, image_batch in enumerate(batches):
1227
+ callbacks_tracker.on_batch_begin(batch, logs=logs)
1228
+ callbacks_tracker.on_test_batch_begin(batch, logs=logs)
1229
+ val_logs = {k:v.numpy() for k, v in self.val_step(image_batch).items()}
1230
+ logs.update(val_logs)
1231
+
1232
+ callbacks_tracker.on_test_batch_end(batch, logs=logs)
1233
+ callbacks_tracker.on_batch_end(batch, logs=logs)
1234
+ # Presentation
1235
+ batches.set_postfix({'val_d_loss': val_logs['val_d_loss'],
1236
+ 'val_g_loss': val_logs['val_g_loss']
1237
+ })
1238
+
1239
+ stats = ", ".join("{}={:.3g}".format(k, v) for k, v in logs.items() if 'val_' in k and 'loss' not in k)
1240
+ print('Valid:', stats)
1241
+
1242
+ batches.close()
1243
+
1244
+ if epoch % display_per_epochs == 0:
1245
+ print('-'*128)
1246
+ self.visualize_samples((image_batch[0][:2], image_batch[1][:2]))
1247
+
1248
+ self.all_trackers.reset_state()
1249
+
1250
+ callbacks_tracker.on_epoch_end(epoch, logs=logs)
1251
+ # tf.keras.backend.clear_session()
1252
+ _ = gc.collect()
1253
+
1254
+ if self.generator.stop_training or self.discriminator.stop_training:
1255
+ break
1256
+ print('-'*128)
1257
+
1258
+ callbacks_tracker.on_train_end(logs=logs)
1259
+ tf.keras.backend.clear_session()
1260
+ _ = gc.collect()
1261
+ gen_history = None
1262
+ for cb in gen_callback_tracker:
1263
+ if isinstance(cb, tf.keras.callbacks.History):
1264
+ gen_history = cb
1265
+ gen_history.history = {k:v for k,v in cb.history.items() if 'd_' not in k}
1266
+
1267
+ disc_history = None
1268
+ for cb in disc_callback_tracker:
1269
+ if isinstance(cb, tf.keras.callbacks.History):
1270
+ disc_history = cb
1271
+ disc_history.history = {k:v for k,v in cb.history.items() if 'g_' not in k}
1272
+
1273
+ return {'generator':gen_history,
1274
+ 'discriminator':disc_history}
1275
+
1276
+ def visualize_samples(self, samples, figsize=(12, 2)):
1277
+ x, y = samples
1278
+ y_pred = self.generator.predict(x[:2], verbose=0)
1279
+ fig, axs = plt.subplots(1, 6, figsize=figsize)
1280
+ for i in range(2):
1281
+ pos = 3*i
1282
+ axs[pos].imshow(x[i], cmap='gray', vmin=0., vmax=1.)
1283
+ axs[pos].set_title('Masked')
1284
+ axs[pos].axis('off')
1285
+ axs[pos+1].imshow(y[i], cmap='gray', vmin=0., vmax=1.)
1286
+ axs[pos+1].set_title('Original')
1287
+ axs[pos+1].axis('off')
1288
+ axs[pos+2].imshow(y_pred[i], cmap='gray', vmin=0., vmax=1.)
1289
+ axs[pos+2].set_title('Predicted')
1290
+ axs[pos+2].axis('off')
1291
+ plt.show()
1292
+
1293
+ # tf.keras.backend.clear_session()
1294
+ del y_pred
1295
+ _ = gc.collect()
1296
+
1297
+ dcgan = DCGAN(input_shape=(IMAGE_SIZE, IMAGE_SIZE, 1),
1298
+ architecture='two-stage',
1299
+ output_activation='sigmoid',
1300
+ noise=DropBlockNoise(rate=0.1, block_size=16),
1301
+ pretrain_weights=None,
1302
+ block_type='pixel-shuffle',
1303
+ kernel_initializer='glorot_uniform',
1304
+ C=1.)
1305
+
1306
+ restore_model = dcgan.generator
1307
+
1308
+ restore_model.load_weights("./weights_gae/gan_efficientunet_full_augment-hist_equal_generator.h5")
1309
+ restore_model.trainable = False
1310
+
1311
+ def show_image(image, title='Image', cmap_type='gray'):
1312
+ plt.imshow(image, cmap=cmap_type)
1313
+ plt.title(title)
1314
+ plt.axis('off')
1315
+ plt.show()
1316
+
1317
+
1318
+ # đảo màu những ảnh bị ngược màu
1319
+ def remove_negative(img):
1320
+ outside = np.mean(img[ : , 0])
1321
+ inside = np.mean(img[ : , int(IMAGE_SIZE / 2)])
1322
+ if outside < inside:
1323
+ return img
1324
+ else:
1325
+ return 1 - img
1326
+
1327
+ # lựa chọn tiền xử lý: ảnh gốc, Equalization histogram, CLAHE
1328
+ def preprocess(img):
1329
+ img = remove_negative(img)
1330
+
1331
+ img = exposure.equalize_hist(img)
1332
+ img = exposure.equalize_adapthist(img)
1333
+ img = exposure.equalize_hist(img)
1334
+ return img
1335
+
1336
+
1337
+ # dilate contour
1338
+ def dilate(mask_img):
1339
+ kernel_size = 2 * 20 + 1
1340
+ kernel = np.ones((kernel_size, kernel_size), dtype=np.uint8)
1341
+ return ndimage.binary_dilation(mask_img == 0, structure=kernel)
1342
+
1343
+ # Tiêu đề của ứng dụng
1344
+ st.title("Tải và hiển thị ảnh")
1345
+
1346
+ # Hiển thị widget tải tệp tin ảnh
1347
+ uploaded_file = st.file_uploader("Chọn một tệp tin ảnh", type=["jpg", "jpeg", "png"])
1348
+
1349
+ if uploaded_file is not None:
1350
+ # Đọc dữ liệu ảnh từ tệp tin tải lên
1351
+ mask = seg(uploaded_file)
1352
+
1353
+
1354
+
1355
+ # Sử dụng Matplotlib để đọc và hiển thị ảnh C
1356
+ img = plt.imread(uploaded_file, 0)
1357
+ img = np.array(Image.fromarray(img).resize((224, 224)))
1358
+ img = preprocess(img)
1359
+
1360
+ # Hiển thị ảnh gốc
1361
+ show_image(img, title="Original image")
1362
+ plt.axis('off')
1363
+ st.pyplot()
1364
+
1365
+
1366
+
1367
+ uc, lc = get_contours_v2(mask, verbose=1)
1368
+ # img = cv2.imread(filepath)
1369
+ mask = np.zeros((640, 640)).astype('uint8')
1370
+ mask = draw_points(mask, lc, thickness=1, color=(255, 255, 255))
1371
+ mask = draw_points(mask, uc, thickness=1, color=(255, 255, 255))
1372
+ mask = cv2.resize(mask, (224, 224), cv2.INTER_NEAREST)
1373
+ mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
1374
+ mask = mask / 255.
1375
+
1376
+ show_image(mask, title = "Contour")
1377
+ plt.axis('off')
1378
+ st.pyplot()
1379
+ # sử dụng equalization histogram
1380
+ mask = 1 - mask
1381
+ dilated = gaussian(dilate(mask), sigma=50, truncate=0.2)
1382
+
1383
+ im = np.expand_dims(img * (1 - dilated), axis=0)
1384
+ im = tf.convert_to_tensor(im, dtype=tf.float32)
1385
+
1386
+ restored_img = restore_model(im)
1387
+
1388
+ res = tf.squeeze(tf.squeeze(restored_img, axis=-1), axis=0)
1389
+
1390
+ show_image(im[0], title="Masked Image")
1391
+ plt.axis('off')
1392
+ st.pyplot()
1393
+
1394
+ show_image(res, title="Reconstructed image")
1395
+ plt.axis('off')
1396
+ st.pyplot()
1397
+
1398
+ show_image(dilated*tf.abs(img-res), title="Anomaly map", cmap_type='turbo')
1399
+ plt.axis('off')
1400
+ st.pyplot()
1401
+
1402
+
1403
+
requirements.txt ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ keras-core
2
+ tensorflow
3
+ tensorflow-addons
4
+ tf-clahe
5
+ opencv-python
6
+ tf_clahe
7
+ numpy
8
+ pandas
9
+ array-record
10
+ tqdm
11
+ keras_cv~=0.5.0
12
+ scipy
13
+ matplotlib
14
+ scikit-image
15
+ scikit-learn
16
+ ultralytics
17
+ streamlit
18
+ Pillow
weights_gae/.gitattributes ADDED
@@ -0,0 +1 @@
 
 
1
+ *.h5 filter=lfs diff=lfs merge=lfs -text
weights_gae/gan_efficientunet_full_augment-hist_equal_generator.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e6f23b0e3b9bd3f7a860b4c213ea7c99b4d7d17ebf1f56f7dd39c37e73e1c0f8
3
+ size 230002208
weights_yolo/oai_s_best4.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0159a91db899213c34db9cc93db1b5a31576eb3b78eb61bd1d9a29a4ef92e843
3
+ size 6771320