Sefray commited on
Commit
1bae6d9
1 Parent(s): 6678905
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.png filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,593 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ import cv2
4
+ import json
5
+ import os
6
+ import numpy as np
7
+ from pathlib import Path
8
+ from pylena.scribo import line_detector
9
+ from pylena.scribo import VSegment, LSuperposition
10
+ from pylena.scribo import e_segdet_preprocess, e_segdet_process_extraction, e_segdet_process_tracking, e_segdet_process_traversal_mode
11
+ import time
12
+
13
+ from typing import List, Tuple, Dict
14
+
15
+
16
+ # Define all the default values
17
+ default_min_len = 10
18
+ default_preprocess = "NONE"
19
+ default_tracker = "KALMAN"
20
+ default_traversal_mode = "HORIZONTAL_VERTICAL"
21
+ default_extraction_type = "BINARY"
22
+ default_negate_image = False
23
+ default_dyn = 0.6
24
+ default_size_mask = 11
25
+ default_double_exponential_alpha = 0.6
26
+ default_simple_moving_average_memory = 30.0
27
+ default_exponential_moving_average_memory = 16.0
28
+ default_one_euro_beta = 0.007
29
+ default_one_euro_mincutoff = 1.0
30
+ default_one_euro_dcutoff = 1.0
31
+ default_bucket_size = 32
32
+ default_nb_values_to_keep = 30
33
+ default_discontinuity_relative = 0
34
+ default_discontinuity_absolute = 0
35
+ default_minimum_for_fusion = 15
36
+ default_default_sigma_position = 2
37
+ default_default_sigma_thickness = 2
38
+ default_default_sigma_luminosity = 57
39
+ default_min_nb_values_sigma = 10
40
+ default_sigma_pos_min = 1.0
41
+ default_sigma_thickness_min = 0.64
42
+ default_sigma_luminosity_min = 13.0
43
+ default_gradient_threshold = 30
44
+ default_llumi = 225
45
+ default_blumi = 225
46
+ default_ratio_lum = 1.0
47
+ default_max_thickness = 100
48
+ default_threshold_intersection = 0.8
49
+ default_remove_duplicates = True
50
+
51
+
52
+ def get_json_extract(full_json: dict) -> dict:
53
+ """Extract 5 samples from a json dictionnary
54
+
55
+ Args:
56
+ full_json (dict): The full json dictionnary
57
+
58
+ Returns:
59
+ dict: A sub sample of the full json dictionnary containing the first 5 samples.
60
+ """
61
+ extract_json = {}
62
+
63
+ count = 5
64
+ for key, value in full_json.items():
65
+ extract_json[key] = value
66
+
67
+ count -= 1
68
+ if count == 0:
69
+ break
70
+
71
+ return extract_json
72
+
73
+
74
+ def save_json(data: dict, path: Path) -> None:
75
+ """Save a json dictionnary to a file
76
+
77
+ Args:
78
+ data (dict): The json dictionnary to save
79
+ path (Path): The path to the file
80
+ """
81
+ with open(path, "w") as f:
82
+ json.dump(data, f)
83
+
84
+
85
+ def get_new_white(height: int, width: int) -> np.ndarray:
86
+ """Create a new white image
87
+
88
+ Args:
89
+ height (int): The height of the image
90
+ width (int): The width of the image
91
+
92
+ Returns:
93
+ np.ndarray: The new white image
94
+ """
95
+ img = np.ones((height, width, 3), dtype=np.uint8) * 255
96
+ return img
97
+
98
+ # fmt: off
99
+
100
+ def generate_vector_output(img_rgb_input: np.ndarray, lines: List[VSegment], lines_colors: Dict[int, np.ndarray]):
101
+ """Generate the vector output using the VSegment list
102
+
103
+ Args:
104
+ img_rgb_input (np.ndarray): Input image with 3 channels
105
+ lines (List[VSegment]): The identified lines in the image
106
+ lines_colors (Dict[int, np.ndarray]): Dictionary containing the color for each line according to their label
107
+
108
+ Returns:
109
+ Tuple[np.ndarray, np.ndarray, Path, dict]: The vector output
110
+ """
111
+
112
+ def draw_lines(img: np.ndarray, lines: List[VSegment]) -> np.ndarray:
113
+ """Draw the lines as vector on the image
114
+
115
+ Args:
116
+ img (np.ndarray): The image to draw on
117
+ lines (List[VSegment]): The lines to draw
118
+
119
+ Returns:
120
+ np.ndarray: The image with the lines drawn on it
121
+ """
122
+ for line in lines:
123
+ cv2.line(img, (line.x0, line.y0), (line.x1, line.y1), lines_colors[line.label].tolist(), 2)
124
+ return img
125
+
126
+ def get_vector_json(lines: List[VSegment]) -> dict:
127
+ """Generate the json dictionnary containing the vector output
128
+
129
+ Args:
130
+ lines (List[VSegment]): The lines to draw
131
+
132
+ Returns:
133
+ dict: The json dictionnary containing the vector output
134
+ """
135
+ ret = {}
136
+ for line in lines:
137
+ ret[str(line.label)] = {"x0": line.x0, "y0": line.y0, "x1": line.x1, "y1": line.y1}
138
+ return ret
139
+
140
+ img_empty = get_new_white(img_rgb_input.shape[0], img_rgb_input.shape[1])
141
+
142
+ out_vector_over_img = draw_lines(img_rgb_input.copy(), lines)
143
+ out_vector_label_img = draw_lines(img_empty, lines)
144
+
145
+ out_vector_file = Path("vector_output_full.json")
146
+ out_vector_file_full = get_vector_json(lines)
147
+ save_json(out_vector_file_full, out_vector_file)
148
+
149
+ out_vector_file_extract = get_json_extract(out_vector_file_full)
150
+
151
+ return out_vector_over_img, out_vector_label_img, out_vector_file, out_vector_file_extract,
152
+
153
+ def generate_pixel_output(img_rgb_input: np.ndarray, img_label: np.ndarray, superpositions: List[LSuperposition], lines_colors: Dict[int, np.ndarray]):
154
+ """Generate the pixel output using the LSuperposition list and the img_label
155
+
156
+ Args:
157
+ img_rgb_input (np.ndarray): Input image with 3 channels
158
+ img_label (np.ndarray): The labelized image
159
+ superpositions (List[LSuperposition]): The identified superpositions in the image
160
+ lines_colors (Dict[int, np.ndarray]): Dictionary containing the color for each line according to their label
161
+
162
+ Returns:
163
+ Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, Path, Path, dict]: The pixel output
164
+ """
165
+
166
+ def draw_pixels(img: np.ndarray, img_label: np.ndarray, lines_colors: Dict[int, np.ndarray]) -> np.ndarray:
167
+ """Draw the pixels as vector on the image
168
+
169
+ Args:
170
+ img (np.ndarray): The image to draw on
171
+ img_label (np.ndarray): The labelized image
172
+ lines_colors (Dict[int, np.ndarray]): Dictionary containing the color for each line according to their label
173
+
174
+ Returns:
175
+ np.ndarray: The image with the pixels drawn on it
176
+ """
177
+ for x in range(img.shape[0]):
178
+ for y in range(img.shape[1]):
179
+ if img_label[x, y] != 0 and img_label[x, y] != 1:
180
+ img[x, y, :] = lines_colors[img_label[x, y]]
181
+ return img
182
+
183
+ def draw_superposition(img: np.ndarray, superpositions: List[LSuperposition], lines_colors: Dict[int, np.ndarray]) -> np.ndarray:
184
+ """Draw the superpositions as vector on the image
185
+
186
+ Args:
187
+ img (np.ndarray): The image to draw on
188
+ superpositions (List[LSuperposition]): The superpositions to draw
189
+ lines_colors (Dict[int, np.ndarray]): Dictionary containing the color for each line according to their label
190
+
191
+ Returns:
192
+ np.ndarray: The image with the superpositions drawn on it
193
+ """
194
+ for superposition in superpositions:
195
+ img[superposition.y, superposition.x, :] = lines_colors[1]
196
+ return img
197
+
198
+ def get_superposition_json(superpositions: List[LSuperposition]) -> dict:
199
+ """Generate the json dictionnary containing the superposition output
200
+
201
+ Args:
202
+ superpositions (List[LSuperposition]): The superpositions
203
+
204
+ Returns:
205
+ dict: The json dictionnary containing the superposition output
206
+ """
207
+ ret = {}
208
+ for superposition in superpositions:
209
+ key = f"{superposition.x}_{superposition.y}"
210
+ if not key in ret:
211
+ ret[key] = []
212
+
213
+ ret[key].append(superposition.label)
214
+ return ret
215
+
216
+ def draw_full(img: np.ndarray, img_label: np.ndarray, superpositions: List[LSuperposition], lines_colors: Dict[int, np.ndarray]):
217
+ """Draw the full output (pixels and superpositions) on the image
218
+
219
+ Args:
220
+ img (np.ndarray): The image to draw on
221
+ img_label (np.ndarray): The labelized image
222
+ superpositions (List[LSuperposition]): The superpositions
223
+ lines_colors (Dict[int, np.ndarray]): Dictionary containing the color for each line according to their label
224
+
225
+ Returns:
226
+ np.ndarray: The image with the full output drawn on it
227
+ """
228
+ img = draw_pixels(img, img_label, lines_colors)
229
+ img = draw_superposition(img, superpositions, lines_colors)
230
+ return img
231
+
232
+ out_pixel_full_over_img = draw_full(img_rgb_input.copy(), img_label, superpositions, lines_colors)
233
+ out_pixel_line_over_img = draw_pixels(img_rgb_input.copy(), img_label, lines_colors)
234
+ out_pixel_superposition_over_img = draw_superposition(img_rgb_input.copy(), superpositions, lines_colors)
235
+
236
+ img_empty = get_new_white(img_rgb_input.shape[0], img_rgb_input.shape[1])
237
+ out_pixel_full_img = draw_full(img_empty.copy(), img_label, superpositions, lines_colors)
238
+ out_pixel_line_img = draw_pixels(img_empty.copy(), img_label, lines_colors)
239
+ out_pixel_superposition_img = draw_superposition(img_empty.copy(), superpositions, lines_colors)
240
+
241
+ out_pixel_file_label = Path("pixel_output_label.npy")
242
+ img_label.dump(out_pixel_file_label)
243
+ out_pixel_file_superposition = Path("pixel_output_superposition.json")
244
+ out_pixel_file_superposition_full = get_superposition_json(superpositions)
245
+ save_json(out_pixel_file_superposition_full, out_pixel_file_superposition)
246
+ out_pixel_file_superposition_extract = get_json_extract(out_pixel_file_superposition_full)
247
+
248
+ return out_pixel_full_over_img, out_pixel_line_over_img, out_pixel_superposition_over_img, out_pixel_full_img, out_pixel_line_img, out_pixel_superposition_img, out_pixel_file_label, out_pixel_file_superposition, out_pixel_file_superposition_extract
249
+
250
+ def generate_output(img_input: np.ndarray, img_label: np.ndarray, superpositions: List[LSuperposition], lines: List[VSegment]):
251
+ """Generate the output using the LSuperposition list and the img_label
252
+
253
+ Args:
254
+ img_input (np.ndarray): Input image with 1 channel
255
+ img_label (np.ndarray): The labelized image
256
+ superpositions (List[LSuperposition]): The identified superpositions in the image
257
+ lines (List[VSegment]): The identified lines in the image
258
+
259
+ Returns:
260
+ Tuple[np.ndarray, np.ndarray, Path, dict, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, Path, Path, dict]: The complete output for gradio application
261
+ """
262
+ def get_rgb_input_img(greyscale_input_img: np.ndarray) -> np.ndarray:
263
+ """Convert a greyscale image to a rgb image
264
+
265
+ Args:
266
+ greyscale_input_img (np.ndarray): The greyscale / 1 channel image
267
+
268
+ Returns:
269
+ np.ndarray: The 3 channels version of the input image
270
+ """
271
+ rgb_input_img: np.ndarray = np.zeros((greyscale_input_img.shape[0], greyscale_input_img.shape[1], 3), dtype=np.uint8)
272
+ rgb_input_img[:, :, 0] = greyscale_input_img
273
+ rgb_input_img[:, :, 1] = greyscale_input_img
274
+ rgb_input_img[:, :, 2] = greyscale_input_img
275
+
276
+ return rgb_input_img
277
+
278
+ def generate_line_colors(lines: List[VSegment]) -> Dict[int, np.ndarray]:
279
+ """Generate a color for each line
280
+
281
+ Args:
282
+ lines (List[VSegment]): The lines
283
+
284
+ Returns:
285
+ Dict[int, np.ndarray]: A dictionary containing the color for each line according to their label
286
+ """
287
+ np.random.seed(0)
288
+ color = np.random.randint(low=0, high=255, size=(len(lines), 3))
289
+
290
+ ret = {}
291
+ ret[0] = np.array([0, 0, 0])
292
+ ret[1] = np.array([255, 0, 0])
293
+ for i, line in enumerate(lines):
294
+ ret[line.label] = color[i, :].astype(np.uint8)
295
+ return ret
296
+
297
+ rgb_input_img: np.ndarray = get_rgb_input_img(img_input)
298
+ lines_colors: Dict[int, np.ndarray] = generate_line_colors(lines)
299
+
300
+ out_vector: Tuple[np.ndarray, np.ndarray, Path, dict]
301
+ out_vector = generate_vector_output(rgb_input_img, lines, lines_colors)
302
+
303
+ out_pixel: Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, Path, Path, dict]
304
+ out_pixel = generate_pixel_output(rgb_input_img, img_label, superpositions, lines_colors)
305
+
306
+ return *out_vector, *out_pixel
307
+
308
+ def app_function(
309
+ greyscale_input_img,
310
+ min_len,
311
+ preprocess,
312
+ tracker,
313
+ traversal_mode,
314
+ extraction_type,
315
+ negate_image,
316
+ dyn,
317
+ size_mask,
318
+ double_exponential_alpha,
319
+ simple_moving_average_memory,
320
+ exponential_moving_average_memory,
321
+ one_euro_beta,
322
+ one_euro_mincutoff,
323
+ one_euro_dcutoff,
324
+ bucket_size,
325
+ nb_values_to_keep,
326
+ discontinuity_relative,
327
+ discontinuity_absolute,
328
+ minimum_for_fusion,
329
+ default_sigma_position,
330
+ default_sigma_thickness,
331
+ default_sigma_luminosity,
332
+ min_nb_values_sigma,
333
+ sigma_pos_min,
334
+ sigma_thickness_min,
335
+ sigma_luminosity_min,
336
+ gradient_threshold,
337
+ llumi,
338
+ blumi,
339
+ ratio_lum,
340
+ max_thickness,
341
+ threshold_intersection,
342
+ remove_duplicates):
343
+
344
+ img_label: np.ndarray
345
+ superpositions: List[LSuperposition]
346
+ lines: List[VSegment]
347
+
348
+ def get_enum_value(enum, value):
349
+ return enum.__members__[value]
350
+
351
+ t0 = time.time()
352
+ img_label, superpositions, lines = line_detector(
353
+ greyscale_input_img, "full",
354
+ min_len=int(min_len),
355
+ preprocess=get_enum_value(e_segdet_preprocess, preprocess),
356
+ tracker=get_enum_value(e_segdet_process_tracking, tracker),
357
+ traversal_mode=get_enum_value(e_segdet_process_traversal_mode, traversal_mode),
358
+ extraction_type=get_enum_value(e_segdet_process_extraction, extraction_type),
359
+ negate_image=bool(negate_image),
360
+ dyn=float(dyn),
361
+ size_mask=int(size_mask),
362
+ double_exponential_alpha=float(double_exponential_alpha),
363
+ simple_moving_average_memory=int(simple_moving_average_memory),
364
+ exponential_moving_average_memory=int(exponential_moving_average_memory),
365
+ one_euro_beta=float(one_euro_beta),
366
+ one_euro_mincutoff=float(one_euro_mincutoff),
367
+ one_euro_dcutoff=float(one_euro_dcutoff),
368
+ bucket_size=int(bucket_size),
369
+ nb_values_to_keep=int(nb_values_to_keep),
370
+ discontinuity_relative=int(discontinuity_relative),
371
+ discontinuity_absolute=int(discontinuity_absolute),
372
+ minimum_for_fusion=int(minimum_for_fusion),
373
+ default_sigma_position=int(default_sigma_position),
374
+ default_sigma_thickness=int(default_sigma_thickness),
375
+ default_sigma_luminosity=int(default_sigma_luminosity),
376
+ min_nb_values_sigma=int(min_nb_values_sigma),
377
+ sigma_pos_min=float(sigma_pos_min),
378
+ sigma_thickness_min=float(sigma_thickness_min),
379
+ sigma_luminosity_min=float(sigma_luminosity_min),
380
+ gradient_threshold=int(gradient_threshold),
381
+ llumi=int(llumi),
382
+ blumi=int(blumi),
383
+ ratio_lum=float(ratio_lum),
384
+ max_thickness=int(max_thickness),
385
+ threshold_intersection=float(threshold_intersection),
386
+ remove_duplicates=bool(remove_duplicates)
387
+ )
388
+ t1 = time.time()
389
+
390
+ duration = t1 - t0
391
+
392
+ outputs = generate_output(greyscale_input_img, img_label, superpositions, lines)
393
+
394
+ return duration, *outputs
395
+
396
+
397
+
398
+ with gr.Blocks() as app:
399
+ gr.Markdown("""
400
+ # Pylena line detection demonstration
401
+
402
+ This is a demonstration of the line detector described in the article *Linear Object Detection in Document Images using Multiple Object Tracking*
403
+ accepted at ICDAR 2023. The article is available at: https://arxiv.org/abs/2305.16968.
404
+
405
+ ## How to use this demonstration ?
406
+
407
+ You can either upload your own (greyscale/8bit image) image or use one of the examples, then change the parameters and click on the run button.
408
+
409
+ The complete documentation is available at: http://olena.pages.lre.epita.fr/pylena/
410
+ """)
411
+
412
+
413
+ with gr.Row():
414
+ with gr.Column():
415
+ gr.Markdown("## Input")
416
+
417
+ img_input = gr.Image(type="numpy", image_mode="L", label="Greyscale input image")
418
+
419
+ with gr.Tab("Parameters"):
420
+ with gr.Tab("Tracking"):
421
+ min_len = gr.Number(label="min_len", value=default_min_len)
422
+ tracker = gr.Radio(label="tracker", choices=["KALMAN", "ONE_EURO", "DOUBLE_EXPONENTIAL", "LAST_INTEGRATION", "SIMPLE_MOVING_AVERAGE", "EXPONENTIAL_MOVING_AVERAGE"], value=default_tracker)
423
+ traversal_mode = gr.Radio(label="traversal_mode", choices=["HORIZONTAL_VERTICAL", "HORIZONTAL", "VERTICAL"], value=default_traversal_mode)
424
+
425
+ with gr.Tab("Observation extraction"):
426
+ blumi = gr.Number(label="blumi", value=default_blumi)
427
+ llumi = gr.Number(label="llumi", value=default_llumi)
428
+ max_thickness = gr.Number(label="max_thickness", value=default_max_thickness)
429
+
430
+ with gr.Tab("Discontinuity"):
431
+ discontinuity_relative = gr.Number(label="discontinuity_relative", value=default_discontinuity_relative)
432
+ discontinuity_absolute = gr.Number(label="discontinuity_absolute", value=default_discontinuity_absolute)
433
+
434
+ with gr.Tab("Advanced parameters"):
435
+ with gr.Tab("Preprocessing"):
436
+ preprocess = gr.Radio(label="preprocess", choices=["NONE", "Black top hat"], value=default_preprocess)
437
+ negate_image = gr.Checkbox(label="negate_image", value=default_negate_image)
438
+ dyn = gr.Number(label="dyn", value=default_dyn)
439
+ size_mask = gr.Number(label="size_mask", value=default_size_mask)
440
+
441
+ with gr.Tab("Tracker specific parameters"):
442
+ double_exponential_alpha = gr.Number(label="double_exponential_alpha", value=default_double_exponential_alpha)
443
+ simple_moving_average_memory = gr.Number(label="simple_moving_average_memory", value=default_simple_moving_average_memory)
444
+ exponential_moving_average_memory = gr.Number(label="exponential_moving_average_memory", value=default_exponential_moving_average_memory)
445
+ one_euro_beta = gr.Number(label="one_euro_beta", value=default_one_euro_beta)
446
+ one_euro_mincutoff = gr.Number(label="one_euro_mincutoff", value=default_one_euro_mincutoff)
447
+ one_euro_dcutoff = gr.Number(label="one_euro_dcutoff", value=default_one_euro_dcutoff)
448
+
449
+ with gr.Tab("Tracker parameters"):
450
+ nb_values_to_keep = gr.Number(label="nb_values_to_keep", value=default_nb_values_to_keep)
451
+ minimum_for_fusion = gr.Number(label="minimum_for_fusion", value=default_minimum_for_fusion)
452
+
453
+ with gr.Tab("Observation extraction"):
454
+ extraction_type = gr.Radio(label="extraction_type", choices=["BINARY", "GRADIENT"], value="BINARY")
455
+ gradient_threshold = gr.Number(label="gradient_threshold", value=default_gradient_threshold)
456
+
457
+ with gr.Tab("Observation matching"):
458
+ default_sigma_position = gr.Number(label="default_sigma_position", value=default_default_sigma_position)
459
+ default_sigma_thickness = gr.Number(label="default_sigma_thickness", value=default_default_sigma_thickness)
460
+ default_sigma_luminosity = gr.Number(label="default_sigma_luminosity", value=default_default_sigma_luminosity)
461
+ min_nb_values_sigma = gr.Number(label="min_nb_values_sigma", value=default_min_nb_values_sigma)
462
+ sigma_pos_min = gr.Number(label="sigma_pos_min", value=default_sigma_pos_min)
463
+ sigma_thickness_min = gr.Number(label="sigma_thickness_min", value=default_sigma_thickness_min)
464
+ sigma_luminosity_min = gr.Number(label="sigma_luminosity_min", value=default_sigma_luminosity_min)
465
+
466
+ with gr.Tab("Extraction"):
467
+ ratio_lum = gr.Number(label="ratio_lum", value=default_ratio_lum)
468
+
469
+ with gr.Tab("Post Processing"):
470
+ threshold_intersection = gr.Number(label="threshold_intersection", value=default_threshold_intersection)
471
+ remove_duplicates = gr.Checkbox(label="remove_duplicates", value=default_remove_duplicates)
472
+
473
+ with gr.Tab("Optimisation"):
474
+ bucket_size = gr.Number(label="bucket_size", value=default_bucket_size)
475
+
476
+ with gr.Column():
477
+ gr.Markdown("## Output")
478
+
479
+ out_duration = gr.Number(label="Line detection duration (in seconds)", value=-1, interactive=False)
480
+
481
+ with gr.Tab("Output Vector"):
482
+ with gr.Tab("Over input"):
483
+ out_vector_over_img = gr.Image(type="numpy", image_mode="RGB", interactive=False)
484
+ with gr.Tab("Line only"):
485
+ out_vector_label_img = gr.Image(type="numpy", image_mode="RGB", interactive=False)
486
+ with gr.Tab("File"):
487
+ out_vector_file = gr.File(label="Vector output full", interactive=False)
488
+ out_vector_file_extract = gr.Json(label="Vector sample")
489
+
490
+ with gr.Tab("Output Pixel"):
491
+ with gr.Tab("Line and Superposition over input"):
492
+ out_pixel_full_over_img = gr.Image(type="numpy", image_mode="RGB", interactive=False)
493
+ with gr.Tab("Line over input"):
494
+ out_pixel_line_over_img = gr.Image(type="numpy", image_mode="RGB", interactive=False)
495
+ with gr.Tab("Superposition over input"):
496
+ out_pixel_superposition_over_img = gr.Image(type="numpy", image_mode="RGB", interactive=False)
497
+ with gr.Tab("Line and Superposition"):
498
+ out_pixel_full_img = gr.Image(type="numpy", image_mode="RGB", interactive=False)
499
+ with gr.Tab("Line only"):
500
+ out_pixel_line_img = gr.Image(type="numpy", image_mode="RGB", interactive=False)
501
+ with gr.Tab("Superposition only"):
502
+ out_pixel_superposition_img = gr.Image(type="numpy", image_mode="RGB", label="Labelized image")
503
+ with gr.Tab("File"):
504
+ out_pixel_file_label = gr.File(label="Pixel output full", interactive=False)
505
+ out_pixel_file_superposition = gr.File(label="Pixel output full", interactive=False)
506
+ out_pixel_file_superposition_extract = gr.Json(label="Superposition sample")
507
+
508
+
509
+ run_button = gr.Button("Run")
510
+ run_button.click(
511
+ app_function,
512
+ inputs=[
513
+ img_input,
514
+ min_len,
515
+ preprocess,
516
+ tracker,
517
+ traversal_mode,
518
+ extraction_type,
519
+ negate_image,
520
+ dyn,
521
+ size_mask,
522
+ double_exponential_alpha,
523
+ simple_moving_average_memory,
524
+ exponential_moving_average_memory,
525
+ one_euro_beta,
526
+ one_euro_mincutoff,
527
+ one_euro_dcutoff,
528
+ bucket_size,
529
+ nb_values_to_keep,
530
+ discontinuity_relative,
531
+ discontinuity_absolute,
532
+ minimum_for_fusion,
533
+ default_sigma_position,
534
+ default_sigma_thickness,
535
+ default_sigma_luminosity,
536
+ min_nb_values_sigma,
537
+ sigma_pos_min,
538
+ sigma_thickness_min,
539
+ sigma_luminosity_min,
540
+ gradient_threshold,
541
+ llumi,
542
+ blumi,
543
+ ratio_lum,
544
+ max_thickness,
545
+ threshold_intersection,
546
+ remove_duplicates
547
+ ],
548
+ outputs=[
549
+ out_duration,
550
+
551
+ out_vector_over_img, out_vector_label_img,
552
+ out_vector_file, out_vector_file_extract,
553
+
554
+ out_pixel_full_over_img, out_pixel_line_over_img, out_pixel_superposition_over_img,
555
+ out_pixel_full_img, out_pixel_line_img, out_pixel_superposition_img,
556
+ out_pixel_file_label,
557
+ out_pixel_file_superposition, out_pixel_file_superposition_extract
558
+ ])
559
+
560
+
561
+ gr.Markdown("""
562
+ ## Examples
563
+
564
+ Be aware that parameters are not reset when you change example.
565
+ """)
566
+
567
+ current_dir = os.path.dirname(__file__)
568
+ with gr.Tab("trade_directory"):
569
+ gr.Examples(
570
+ examples=[[os.path.join(current_dir, "image", "trade_directories.png"), 200, 200, 200]],
571
+ inputs=[img_input, blumi, llumi, min_len]
572
+ )
573
+ with gr.Tab("music_sheet"):
574
+ gr.Examples(
575
+
576
+ examples=[[os.path.join(current_dir, "image", "music_sheet.png"), 30, 5, 20, "HORIZONTAL"]],
577
+ inputs=[img_input, discontinuity_relative, max_thickness, min_len, traversal_mode]
578
+ )
579
+ with gr.Tab("map"):
580
+ gr.Examples(
581
+ examples=[[os.path.join(current_dir, "image", "map.png"), 4, 180, 180, 20, 6]],
582
+ inputs=[img_input, discontinuity_relative, blumi, llumi, min_len, max_thickness]
583
+ )
584
+
585
+ gr.Markdown("""
586
+ ## A question ?
587
+
588
+ If you have any question, please contact us at: <philippe.bernet@epita.fr>
589
+ """)
590
+
591
+ # fmt: on
592
+
593
+ app.launch()
image/map.png ADDED

Git LFS Details

  • SHA256: b70150939b87993912c3d4d7fe6a46dca82133045c32f16cb940784baa259ba9
  • Pointer size: 132 Bytes
  • Size of remote file: 1 MB
image/music_sheet.png ADDED

Git LFS Details

  • SHA256: 53975d97cee6bb18055832a18feb10c2769995c3f63d228b8b506886380dcf29
  • Pointer size: 129 Bytes
  • Size of remote file: 6.71 kB
image/trade_directories.png ADDED

Git LFS Details

  • SHA256: 8bb08e6d086de7edb73dfd4facbc2809bf9bf13551eb090736c3d6caac824efd
  • Pointer size: 131 Bytes
  • Size of remote file: 972 kB
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ numpy
2
+ opencv-python
3
+ pylena