Spaces:
Runtime error
Runtime error
v1
Browse files- .gitattributes +1 -0
- app.py +593 -0
- image/map.png +3 -0
- image/music_sheet.png +3 -0
- image/trade_directories.png +3 -0
- requirements.txt +3 -0
.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: <[email protected]>
|
589 |
+
""")
|
590 |
+
|
591 |
+
# fmt: on
|
592 |
+
|
593 |
+
app.launch()
|
image/map.png
ADDED
![]() |
Git LFS Details
|
image/music_sheet.png
ADDED
![]() |
Git LFS Details
|
image/trade_directories.png
ADDED
![]() |
Git LFS Details
|
requirements.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
numpy
|
2 |
+
opencv-python
|
3 |
+
pylena
|