Spaces:
Sleeping
Sleeping
feat: embed extracted images as base64 data URIs in preview cards
Browse files
app.py
CHANGED
|
@@ -6,28 +6,52 @@ import shutil
|
|
| 6 |
import tempfile
|
| 7 |
import traceback
|
| 8 |
import zipfile
|
|
|
|
| 9 |
|
| 10 |
import gradio as gr
|
| 11 |
|
|
|
|
|
|
|
| 12 |
try:
|
| 13 |
import markdown as _md
|
| 14 |
-
def _md2html(text: str) -> str:
|
| 15 |
-
"""Markdown โ HTML.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
html = _md.markdown(text, extensions=["nl2br", "tables"])
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
return (
|
| 23 |
'<span style="display:inline-flex;align-items:center;gap:4px;'
|
| 24 |
'background:#f3f4f6;border:1px solid #d1d5db;border-radius:4px;'
|
| 25 |
-
'padding:1px 7px;font-size:12px;color:#6b7280">'
|
| 26 |
-
f'๐ท {alt}</span>'
|
| 27 |
)
|
| 28 |
-
|
|
|
|
|
|
|
| 29 |
except ImportError:
|
| 30 |
-
def _md2html(text: str) -> str:
|
| 31 |
return text.replace("\n", "<br>")
|
| 32 |
|
| 33 |
_REPO_ROOT = os.path.dirname(os.path.abspath(__file__))
|
|
@@ -116,7 +140,8 @@ EXAMPLES = [
|
|
| 116 |
# โโ Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 117 |
|
| 118 |
|
| 119 |
-
def _render_preview(jsonl_path: str, lang: str = "en") -> str:
|
|
|
|
| 120 |
if not jsonl_path or not os.path.exists(jsonl_path):
|
| 121 |
return ""
|
| 122 |
items = []
|
|
@@ -142,14 +167,16 @@ def _render_preview(jsonl_path: str, lang: str = "en") -> str:
|
|
| 142 |
|
| 143 |
cards = []
|
| 144 |
for i, item in enumerate(items):
|
| 145 |
-
q_html = _md2html(str(item.get("question", "")))
|
| 146 |
-
a_html = _md2html(str(item.get("answer", "")))
|
| 147 |
-
sol_raw = str(item.get("solution", ""))
|
| 148 |
name = item.get("name", "")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
# Truncate solution before converting (avoids cutting mid-tag)
|
| 151 |
sol_short = (sol_raw[:400] + "\n\nโฆ") if len(sol_raw) > 400 else sol_raw
|
| 152 |
-
sol_html = _md2html(sol_short)
|
| 153 |
sol_block = (
|
| 154 |
f'<div style="margin-top:12px;padding-top:10px;border-top:1px solid #e5e7eb">'
|
| 155 |
f'<span style="font-weight:600;color:#374151">{label_s}:</span>'
|
|
@@ -358,7 +385,7 @@ def run_vqa_extraction(
|
|
| 358 |
if lang == "en" else
|
| 359 |
f"โ
ๅฎๆ๏ผๅ
ฑๆๅ {count} ๆก QA ๅฏน{img_note}ใไธ่ฝฝ zip ๅฏ่ทๅพ JSONL ๅๅพ็ใ"
|
| 360 |
)
|
| 361 |
-
yield zip_path, done, _render_preview(jsonl_path, lang)
|
| 362 |
|
| 363 |
except Exception:
|
| 364 |
yield None, f"โ Unexpected error:\n{traceback.format_exc()}", ""
|
|
|
|
| 6 |
import tempfile
|
| 7 |
import traceback
|
| 8 |
import zipfile
|
| 9 |
+
import base64
|
| 10 |
|
| 11 |
import gradio as gr
|
| 12 |
|
| 13 |
+
_MIME = {"png": "png", "jpg": "jpeg", "jpeg": "jpeg", "gif": "gif", "webp": "webp"}
|
| 14 |
+
|
| 15 |
try:
|
| 16 |
import markdown as _md
|
| 17 |
+
def _md2html(text: str, img_dir: str = None) -> str:
|
| 18 |
+
"""Markdown โ HTML.
|
| 19 |
+
If img_dir is given, <img src="vqa_images/x.png"> tags are replaced with
|
| 20 |
+
base64 data URIs so the browser renders the actual image.
|
| 21 |
+
Otherwise a grey badge placeholder is shown.
|
| 22 |
+
"""
|
| 23 |
html = _md.markdown(text, extensions=["nl2br", "tables"])
|
| 24 |
+
|
| 25 |
+
def _img_handler(m):
|
| 26 |
+
tag = m.group(0)
|
| 27 |
+
src_m = re.search(r'src="([^"]*)"', tag)
|
| 28 |
+
alt_m = re.search(r'alt="([^"]*)"', tag)
|
| 29 |
+
src = src_m.group(1) if src_m else ""
|
| 30 |
+
alt = alt_m.group(1) if alt_m else "image"
|
| 31 |
+
|
| 32 |
+
if img_dir and src:
|
| 33 |
+
img_name = os.path.basename(src)
|
| 34 |
+
img_path = os.path.join(img_dir, img_name)
|
| 35 |
+
if os.path.exists(img_path):
|
| 36 |
+
ext = img_name.rsplit(".", 1)[-1].lower()
|
| 37 |
+
mime = _MIME.get(ext, "png")
|
| 38 |
+
with open(img_path, "rb") as f:
|
| 39 |
+
b64 = base64.b64encode(f.read()).decode()
|
| 40 |
+
return (
|
| 41 |
+
f'<img src="data:image/{mime};base64,{b64}" alt="{alt}" '
|
| 42 |
+
f'style="max-width:100%;border-radius:6px;margin:6px 0;display:block;">'
|
| 43 |
+
)
|
| 44 |
+
# Fallback badge when image file is not found
|
| 45 |
return (
|
| 46 |
'<span style="display:inline-flex;align-items:center;gap:4px;'
|
| 47 |
'background:#f3f4f6;border:1px solid #d1d5db;border-radius:4px;'
|
| 48 |
+
f'padding:1px 7px;font-size:12px;color:#6b7280">๐ท {alt}</span>'
|
|
|
|
| 49 |
)
|
| 50 |
+
|
| 51 |
+
return re.sub(r'<img\b[^>]*/?>', _img_handler, html)
|
| 52 |
+
|
| 53 |
except ImportError:
|
| 54 |
+
def _md2html(text: str, img_dir: str = None) -> str:
|
| 55 |
return text.replace("\n", "<br>")
|
| 56 |
|
| 57 |
_REPO_ROOT = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
| 140 |
# โโ Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 141 |
|
| 142 |
|
| 143 |
+
def _render_preview(jsonl_path: str, lang: str = "en", output_dir: str = None) -> str:
|
| 144 |
+
"""Render up to 3 QA items as styled HTML cards with real image rendering."""
|
| 145 |
if not jsonl_path or not os.path.exists(jsonl_path):
|
| 146 |
return ""
|
| 147 |
items = []
|
|
|
|
| 167 |
|
| 168 |
cards = []
|
| 169 |
for i, item in enumerate(items):
|
|
|
|
|
|
|
|
|
|
| 170 |
name = item.get("name", "")
|
| 171 |
+
# Images live at output_dir/{name}/vqa_images/
|
| 172 |
+
img_dir = os.path.join(output_dir, name, "vqa_images") if output_dir else None
|
| 173 |
+
q_html = _md2html(str(item.get("question", "")), img_dir)
|
| 174 |
+
a_html = _md2html(str(item.get("answer", "")), img_dir)
|
| 175 |
+
sol_raw = str(item.get("solution", ""))
|
| 176 |
|
| 177 |
# Truncate solution before converting (avoids cutting mid-tag)
|
| 178 |
sol_short = (sol_raw[:400] + "\n\nโฆ") if len(sol_raw) > 400 else sol_raw
|
| 179 |
+
sol_html = _md2html(sol_short, img_dir)
|
| 180 |
sol_block = (
|
| 181 |
f'<div style="margin-top:12px;padding-top:10px;border-top:1px solid #e5e7eb">'
|
| 182 |
f'<span style="font-weight:600;color:#374151">{label_s}:</span>'
|
|
|
|
| 385 |
if lang == "en" else
|
| 386 |
f"โ
ๅฎๆ๏ผๅ
ฑๆๅ {count} ๆก QA ๅฏน{img_note}ใไธ่ฝฝ zip ๅฏ่ทๅพ JSONL ๅๅพ็ใ"
|
| 387 |
)
|
| 388 |
+
yield zip_path, done, _render_preview(jsonl_path, lang, output_dir)
|
| 389 |
|
| 390 |
except Exception:
|
| 391 |
yield None, f"โ Unexpected error:\n{traceback.format_exc()}", ""
|