foto / validation_and_analysis /fetch_figures.py
Hurum Maksora Tohfa
validation tests
45383d0 unverified
Raw
History Blame Contribute Delete
3.16 kB
"""Fetch specific figure images from ArxivCap on HuggingFace via DuckDB range requests."""
import os
import re
from collections import defaultdict
from pathlib import Path
HF_SRC = "hf://datasets/MMInstruction/ArxivCap/data"
def yymm_of(arxiv_id):
s = str(arxiv_id)
if "/" in s:
return re.sub(r"\D", "", s.split("/", 1)[1])[:4]
return s[:4]
def safe_name(arxiv_id, fig_index):
return re.sub(r"[^0-9a-zA-Z.]+", "_", str(arxiv_id)) + f"_f{fig_index}.jpg"
def fetch_images(needed, image_dir, hf_src=HF_SRC):
"""needed: set of (arxiv_id, fig_index 1-based). Skips files already on disk."""
import duckdb
image_dir = Path(image_dir)
image_dir.mkdir(parents=True, exist_ok=True)
todo = {(a, f) for (a, f) in needed
if not (image_dir / safe_name(a, f)).exists()}
if not todo:
print(" all images already cached")
return
by_chunk = defaultdict(set)
for a, f in todo:
by_chunk[yymm_of(a)].add((a, f))
print(f" fetching {len(todo)} figures from {len(by_chunk)} ArxivCap chunks")
con = duckdb.connect()
for setting in ["SET http_retries = 8", "SET http_retry_wait_ms = 2000",
"SET threads = 2"]:
try:
con.execute(setting)
except Exception:
pass
token = os.environ.get("HF_TOKEN", "")
if token:
con.execute(f"CREATE SECRET hf (TYPE HUGGINGFACE, TOKEN '{token}')")
else:
print(" WARNING: no HF_TOKEN; anonymous HF requests rate-limit fast")
for yymm, pairs in sorted(by_chunk.items()):
ids = sorted({a for a, _ in pairs})
idlist = ",".join("'" + i.replace("'", "''") + "'" for i in ids)
src = f"{hf_src}/arXiv_src_{yymm}_*.parquet"
try:
tab = con.execute(
f"SELECT arxiv_id, caption_images FROM read_parquet('{src}') "
f"WHERE arxiv_id IN ({idlist})").fetch_arrow_table()
except Exception as e:
print(f" chunk {yymm}: fetch failed ({str(e)[:120]}), skipping")
continue
got = 0
for row in tab.to_pylist():
a = row["arxiv_id"]
caps = row["caption_images"] or []
for (aa, f) in pairs:
if aa != a or f > len(caps):
continue
entry = caps[f - 1] or {}
cil = entry.get("cil_pairs") or []
img = (cil[0] or {}).get("image") if cil else None
data = img.get("bytes") if isinstance(img, dict) else img
if not data:
continue
(image_dir / safe_name(a, f)).write_bytes(data)
got += 1
print(f" chunk {yymm}: {got} figures saved")
if __name__ == "__main__":
import argparse
import csv
ap = argparse.ArgumentParser()
ap.add_argument("ids_csv", help="csv with columns arxiv_id,fig_index")
ap.add_argument("--image-dir", default="vision_images")
args = ap.parse_args()
needed = {(r["arxiv_id"], int(r["fig_index"]))
for r in csv.DictReader(open(args.ids_csv))}
fetch_images(needed, Path(args.image_dir))