Spaces:
Running
on
Zero
Running
on
Zero
amlpai04
commited on
Commit
·
13ea8e1
1
Parent(s):
ba34352
remove login button bug
Browse files- app/main.py +4 -2
- app/plot_viewer.py +200 -0
- app/tabs/adv_htrflow_tab.py +10 -2
- app/tabs/data_explorer_tab.py +9 -4
- pyproject.toml +1 -2
- uv.lock +0 -0
app/main.py
CHANGED
@@ -23,7 +23,9 @@ LANG_CHOICES = ["ENG", "SWE"]
|
|
23 |
|
24 |
with gr.Blocks(title="HTRflow", theme=theme, css=css) as demo:
|
25 |
with gr.Row():
|
26 |
-
local_language = gr.BrowserState(
|
|
|
|
|
27 |
main_language = gr.State(value="ENG")
|
28 |
|
29 |
with gr.Column(scale=1):
|
@@ -94,4 +96,4 @@ with gr.Blocks(title="HTRflow", theme=theme, css=css) as demo:
|
|
94 |
demo.queue()
|
95 |
|
96 |
if __name__ == "__main__":
|
97 |
-
demo.launch(server_name="0.0.0.0", server_port=
|
|
|
23 |
|
24 |
with gr.Blocks(title="HTRflow", theme=theme, css=css) as demo:
|
25 |
with gr.Row():
|
26 |
+
local_language = gr.BrowserState(
|
27 |
+
default_value="ENG", storage_key="selected_language"
|
28 |
+
)
|
29 |
main_language = gr.State(value="ENG")
|
30 |
|
31 |
with gr.Column(scale=1):
|
|
|
96 |
demo.queue()
|
97 |
|
98 |
if __name__ == "__main__":
|
99 |
+
demo.launch(server_name="0.0.0.0", server_port=7862, enable_monitoring=False)
|
app/plot_viewer.py
ADDED
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import xml.etree.ElementTree as ET
|
2 |
+
from io import BytesIO
|
3 |
+
|
4 |
+
import cv2
|
5 |
+
import gradio as gr
|
6 |
+
import numpy as np
|
7 |
+
import requests
|
8 |
+
from PIL import Image
|
9 |
+
|
10 |
+
|
11 |
+
def parse_alto_xml(xml_file):
|
12 |
+
"""Parse the ALTO XML file to extract polygons and text content for each TextLine."""
|
13 |
+
tree = ET.parse(xml_file)
|
14 |
+
root = tree.getroot()
|
15 |
+
ns = {"alto": "http://www.loc.gov/standards/alto/ns-v4#"}
|
16 |
+
|
17 |
+
annotations = []
|
18 |
+
transcriptions = {}
|
19 |
+
|
20 |
+
for text_block in root.findall(".//alto:TextBlock", ns):
|
21 |
+
for text_line in text_block.findall("alto:TextLine", ns):
|
22 |
+
shape = text_line.find("alto:Shape", ns)
|
23 |
+
|
24 |
+
if shape is not None:
|
25 |
+
polygon = shape.find("alto:Polygon", ns)
|
26 |
+
if polygon is not None:
|
27 |
+
polygon_points = polygon.attrib["POINTS"]
|
28 |
+
points = [
|
29 |
+
tuple(map(int, point.split(",")))
|
30 |
+
for point in polygon_points.split()
|
31 |
+
]
|
32 |
+
else:
|
33 |
+
hpos = int(text_line.attrib["HPOS"])
|
34 |
+
vpos = int(text_line.attrib["VPOS"])
|
35 |
+
width = int(text_line.attrib["WIDTH"])
|
36 |
+
height = int(text_line.attrib["HEIGHT"])
|
37 |
+
points = [
|
38 |
+
(hpos, vpos),
|
39 |
+
(hpos + width, vpos),
|
40 |
+
(hpos + width, vpos + height),
|
41 |
+
(hpos, vpos + height),
|
42 |
+
]
|
43 |
+
|
44 |
+
content = " ".join(
|
45 |
+
[
|
46 |
+
string.attrib["CONTENT"]
|
47 |
+
for string in text_line.findall("alto:String", ns)
|
48 |
+
]
|
49 |
+
)
|
50 |
+
label = text_line.attrib["ID"]
|
51 |
+
|
52 |
+
annotations.append((points, label))
|
53 |
+
transcriptions[label] = content
|
54 |
+
|
55 |
+
text_area_content = "\n".join(transcriptions[label] for label in transcriptions)
|
56 |
+
|
57 |
+
return annotations, transcriptions, text_area_content
|
58 |
+
|
59 |
+
|
60 |
+
def visualize_polygons_on_image(
|
61 |
+
image, annotations, alpha=0.5, include_reading_order=False
|
62 |
+
):
|
63 |
+
"""Visualize polygons on the image with an optional reading order overlay."""
|
64 |
+
overlay = image.copy()
|
65 |
+
for _, (polygon, label) in enumerate(annotations):
|
66 |
+
color = (
|
67 |
+
np.random.randint(0, 255),
|
68 |
+
np.random.randint(0, 255),
|
69 |
+
np.random.randint(0, 255),
|
70 |
+
)
|
71 |
+
cv2.fillPoly(overlay, [np.array(polygon, dtype=np.int32)], color)
|
72 |
+
|
73 |
+
if include_reading_order:
|
74 |
+
centroid = np.mean(np.array(polygon), axis=0).astype(int)
|
75 |
+
cv2.putText(
|
76 |
+
overlay,
|
77 |
+
str(label),
|
78 |
+
tuple(centroid),
|
79 |
+
cv2.FONT_HERSHEY_SIMPLEX,
|
80 |
+
0.5,
|
81 |
+
(0, 0, 0),
|
82 |
+
1,
|
83 |
+
cv2.LINE_AA,
|
84 |
+
)
|
85 |
+
|
86 |
+
return cv2.addWeighted(overlay, alpha, image, 1 - alpha, 0)
|
87 |
+
|
88 |
+
|
89 |
+
def visualize(
|
90 |
+
xml_file, image_source, image_id, uploaded_image, include_reading_order=False
|
91 |
+
):
|
92 |
+
if image_source == "Use IIIF image":
|
93 |
+
if not image_id:
|
94 |
+
raise gr.Error("Please enter an Image ID.")
|
95 |
+
image_url = f"https://iiifintern.ra.se/arkis!{image_id}/full/max/0/default.jpg"
|
96 |
+
response = requests.get(image_url)
|
97 |
+
if response.status_code != 200:
|
98 |
+
raise gr.Error(f"Failed to download image from {image_url}")
|
99 |
+
image = np.array(Image.open(BytesIO(response.content)))
|
100 |
+
else:
|
101 |
+
if uploaded_image is None:
|
102 |
+
raise gr.Error("Please upload an image.")
|
103 |
+
image = uploaded_image
|
104 |
+
|
105 |
+
annotations, transcriptions, text_area_content = parse_alto_xml(xml_file)
|
106 |
+
annotated_image = visualize_polygons_on_image(
|
107 |
+
image, annotations, include_reading_order=include_reading_order
|
108 |
+
)
|
109 |
+
|
110 |
+
return annotated_image, annotations, transcriptions, text_area_content
|
111 |
+
|
112 |
+
|
113 |
+
def get_transcription_from_coords(annotations, transcriptions, evt: gr.SelectData):
|
114 |
+
"""Get the transcription for the polygon clicked in the annotated image."""
|
115 |
+
x, y = evt.index[0], evt.index[1]
|
116 |
+
for points, label in annotations:
|
117 |
+
polygon = np.array(points, dtype=np.int32)
|
118 |
+
if cv2.pointPolygonTest(polygon, (x, y), False) >= 0:
|
119 |
+
return transcriptions.get(label, "No transcription available.")
|
120 |
+
return "No transcription available."
|
121 |
+
|
122 |
+
|
123 |
+
with gr.Blocks(title="XML Visualization App") as app:
|
124 |
+
with gr.Tab("Visualize"):
|
125 |
+
annotations_state = gr.State()
|
126 |
+
transcriptions_state = gr.State()
|
127 |
+
|
128 |
+
with gr.Row():
|
129 |
+
with gr.Column():
|
130 |
+
xml_input = gr.File(label="Upload ALTO XML File", file_types=[".xml"])
|
131 |
+
with gr.Column():
|
132 |
+
image_source = gr.Radio(
|
133 |
+
choices=["Use IIIF image", "Upload your own image"],
|
134 |
+
label="Image Source",
|
135 |
+
value="Use IIIF image",
|
136 |
+
)
|
137 |
+
image_id_input = gr.Textbox(
|
138 |
+
label="Image ID",
|
139 |
+
placeholder="Enter image ID (e.g., 30003365_00001)",
|
140 |
+
visible=True,
|
141 |
+
)
|
142 |
+
image_upload = gr.Image(
|
143 |
+
label="Upload Image", type="numpy", visible=False
|
144 |
+
)
|
145 |
+
include_reading_order_input = gr.Checkbox(label="Include Reading Order")
|
146 |
+
process_button = gr.Button("Visualize Alto", scale=0, variant="primary")
|
147 |
+
|
148 |
+
def update_image_source(choice):
|
149 |
+
if choice == "Use IIIF image":
|
150 |
+
return [gr.update(visible=True), gr.update(visible=False)]
|
151 |
+
else:
|
152 |
+
return [gr.update(visible=False), gr.update(visible=True)]
|
153 |
+
|
154 |
+
image_source.change(
|
155 |
+
update_image_source,
|
156 |
+
inputs=image_source,
|
157 |
+
outputs=[image_id_input, image_upload],
|
158 |
+
)
|
159 |
+
|
160 |
+
with gr.Row():
|
161 |
+
with gr.Column(scale=3):
|
162 |
+
annotated_image_output = gr.Image(
|
163 |
+
label="Annotated Image", interactive=True
|
164 |
+
)
|
165 |
+
with gr.Column(scale=2):
|
166 |
+
transcription_output = gr.TextArea(
|
167 |
+
label="Transcription",
|
168 |
+
interactive=False,
|
169 |
+
show_copy_button=True,
|
170 |
+
lines=30,
|
171 |
+
)
|
172 |
+
transcription_selected = gr.Textbox(
|
173 |
+
label="Selected Polygon", interactive=False, show_copy_button=True
|
174 |
+
)
|
175 |
+
|
176 |
+
process_button.click(
|
177 |
+
visualize,
|
178 |
+
inputs=[
|
179 |
+
xml_input,
|
180 |
+
image_source,
|
181 |
+
image_id_input,
|
182 |
+
image_upload,
|
183 |
+
include_reading_order_input,
|
184 |
+
],
|
185 |
+
outputs=[
|
186 |
+
annotated_image_output,
|
187 |
+
annotations_state,
|
188 |
+
transcriptions_state,
|
189 |
+
transcription_output,
|
190 |
+
],
|
191 |
+
)
|
192 |
+
|
193 |
+
annotated_image_output.select(
|
194 |
+
get_transcription_from_coords,
|
195 |
+
inputs=[annotations_state, transcriptions_state],
|
196 |
+
outputs=transcription_selected,
|
197 |
+
)
|
198 |
+
|
199 |
+
app.queue()
|
200 |
+
app.launch()
|
app/tabs/adv_htrflow_tab.py
CHANGED
@@ -6,7 +6,13 @@ with gr.Blocks() as adv_htrflow_pipeline:
|
|
6 |
with gr.Row(variant="panel"):
|
7 |
with gr.Column(scale=2):
|
8 |
image_mask2 = gr.ImageEditor(
|
9 |
-
label="Uploaded image",
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
)
|
11 |
|
12 |
image_mask = gr.Gallery(
|
@@ -20,12 +26,14 @@ with gr.Blocks() as adv_htrflow_pipeline:
|
|
20 |
|
21 |
with gr.Group():
|
22 |
with gr.Row(visible=True) as yaml_pipeline:
|
23 |
-
with gr.Accordion(label="Insert Yaml", open=
|
24 |
custom_template_yaml = gr.Code(
|
25 |
value="Paste your custom pipeline here",
|
26 |
language="yaml",
|
27 |
label="yaml",
|
|
|
28 |
interactive=True,
|
|
|
29 |
)
|
30 |
gr.Checkbox(value=True, label="Batch", container=True, scale=0)
|
31 |
|
|
|
6 |
with gr.Row(variant="panel"):
|
7 |
with gr.Column(scale=2):
|
8 |
image_mask2 = gr.ImageEditor(
|
9 |
+
label="Uploaded image",
|
10 |
+
interactive=True,
|
11 |
+
layers=False,
|
12 |
+
eraser=False,
|
13 |
+
brush=False,
|
14 |
+
height=500,
|
15 |
+
canvas_size=(300, 300),
|
16 |
)
|
17 |
|
18 |
image_mask = gr.Gallery(
|
|
|
26 |
|
27 |
with gr.Group():
|
28 |
with gr.Row(visible=True) as yaml_pipeline:
|
29 |
+
with gr.Accordion(label="Insert Yaml here:", open=True):
|
30 |
custom_template_yaml = gr.Code(
|
31 |
value="Paste your custom pipeline here",
|
32 |
language="yaml",
|
33 |
label="yaml",
|
34 |
+
# show_label=False,
|
35 |
interactive=True,
|
36 |
+
lines=3,
|
37 |
)
|
38 |
gr.Checkbox(value=True, label="Batch", container=True, scale=0)
|
39 |
|
app/tabs/data_explorer_tab.py
CHANGED
@@ -2,22 +2,27 @@ import gradio as gr
|
|
2 |
|
3 |
|
4 |
def display_dataset(dataset_repo):
|
5 |
-
return gr.HTML(
|
|
|
6 |
src="https://huggingface.co/datasets/{dataset_repo}/embed/viewer/default/train"
|
7 |
frameborder="0"
|
8 |
width="100%"
|
9 |
height="700px"
|
10 |
-
></iframe>"""
|
|
|
11 |
|
12 |
|
13 |
with gr.Blocks() as data_explorer:
|
14 |
with gr.Row(variant="panel"):
|
15 |
with gr.Column(scale=0, min_width=160):
|
16 |
input_datasets_path = gr.Textbox(
|
17 |
-
label="HF datasets",
|
|
|
|
|
|
|
18 |
)
|
19 |
view_dataset = gr.Button("View dataseet", variant="primary", scale=0)
|
20 |
-
gr.LoginButton("Login to HF", variant="secondary", scale=0)
|
21 |
with gr.Column():
|
22 |
iframe_output = gr.HTML()
|
23 |
|
|
|
2 |
|
3 |
|
4 |
def display_dataset(dataset_repo):
|
5 |
+
return gr.HTML(
|
6 |
+
f"""<iframe
|
7 |
src="https://huggingface.co/datasets/{dataset_repo}/embed/viewer/default/train"
|
8 |
frameborder="0"
|
9 |
width="100%"
|
10 |
height="700px"
|
11 |
+
></iframe>"""
|
12 |
+
)
|
13 |
|
14 |
|
15 |
with gr.Blocks() as data_explorer:
|
16 |
with gr.Row(variant="panel"):
|
17 |
with gr.Column(scale=0, min_width=160):
|
18 |
input_datasets_path = gr.Textbox(
|
19 |
+
label="HF datasets",
|
20 |
+
placeholder="Gabriel/linkoping",
|
21 |
+
scale=0,
|
22 |
+
container=False,
|
23 |
)
|
24 |
view_dataset = gr.Button("View dataseet", variant="primary", scale=0)
|
25 |
+
# gr.LoginButton("Login to HF", variant="secondary", scale=0)
|
26 |
with gr.Column():
|
27 |
iframe_output = gr.HTML()
|
28 |
|
pyproject.toml
CHANGED
@@ -17,9 +17,8 @@ classifiers = [
|
|
17 |
requires-python = ">=3.10,<3.13"
|
18 |
|
19 |
dependencies = [
|
20 |
-
"torch==2.0.1",
|
21 |
"htrflow==0.1.3",
|
22 |
-
"gradio
|
23 |
"datasets>=3.2.0",
|
24 |
"pandas>=2.2.3",
|
25 |
"jinja2>=3.1.4",
|
|
|
17 |
requires-python = ">=3.10,<3.13"
|
18 |
|
19 |
dependencies = [
|
|
|
20 |
"htrflow==0.1.3",
|
21 |
+
"gradio>=5.11.0",
|
22 |
"datasets>=3.2.0",
|
23 |
"pandas>=2.2.3",
|
24 |
"jinja2>=3.1.4",
|
uv.lock
CHANGED
The diff for this file is too large to render.
See raw diff
|
|