| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- # -*- coding: utf-8 -*-
- """Extract screenshots from 8.28 manuals with captions."""
- import os, re, json
- from pathlib import Path
- import fitz
- SRC = Path(r"D:\产线\大洋\大洋操作手册汇总8.28")
- OUT = Path(r"D:\产线\大洋\大洋操作手册汇总9.19\images")
- MIN_W, MIN_H = 180, 80
- MIN_PIXELS = 25000
- FASCICLES = [
- "1、系统管理操作手册.pdf",
- "2、设备管理操作手册.pdf",
- "3、物料管理操作手册.pdf",
- "4、质量管理操作手册.pdf",
- "5、生产管理操作手册.pdf",
- "6、ERP对接操作手册.pdf",
- "7、生产线管理操作手册.pdf",
- "8、PDA操作说明操作手册.pdf",
- "9、MES系统客户端员工扫码操作手册.pdf",
- ]
- def caption_from_page(page, img_bbox):
- blocks = page.get_text("blocks")
- texts = []
- for b in blocks:
- x0, y0, x1, y1, text, *_ = b[:6]
- text = (text or "").strip()
- if not text:
- continue
- # caption usually below image, short
- texts.append((y0, y1, text.replace("\n", " ").strip()))
- # find nearest text below image
- below = [t for t in texts if t[0] >= img_bbox[3] - 8]
- below.sort(key=lambda t: t[0])
- for y0, y1, text in below[:3]:
- t = re.sub(r"\s+", " ", text)
- if 2 <= len(t) <= 40 and not t.startswith("第") and "PAGE" not in t:
- if not re.match(r"^\d+(\.\d+)*\.?\s*$", t):
- return t
- # fallback: last short line above
- above = [t for t in texts if t[1] <= img_bbox[1] + 8]
- if above:
- t = re.sub(r"\s+", " ", above[-1][2])
- if 2 <= len(t) <= 50:
- return t
- return ""
- def heading_from_page(page):
- blocks = page.get_text("blocks")
- heads = []
- for b in blocks:
- text = (b[4] or "").strip()
- if not text:
- continue
- first = text.split("\n")[0].strip()
- if re.match(r"^\d+(\.\d+)*\.?\s+\S+", first) or first.startswith("3."):
- heads.append(first)
- return heads[0] if heads else ""
- def safe_name(s):
- s = re.sub(r'[\\/:*?"<>|]', "", s)
- s = re.sub(r"\s+", "_", s.strip())
- return s[:40]
- def extract_one(pdf_name):
- doc = fitz.open(SRC / pdf_name)
- stem = pdf_name[:-4]
- dest = OUT / stem
- dest.mkdir(parents=True, exist_ok=True)
- meta = []
- seen_xref = {}
- idx = 0
- for pi, page in enumerate(doc, 1):
- heading = heading_from_page(page)
- images = page.get_images(full=True)
- # also get image bboxes via dict
- img_info = []
- d = page.get_text("dict")
- for block in d.get("blocks", []):
- if block.get("type") != 1:
- continue
- bbox = block.get("bbox")
- w = bbox[2] - bbox[0]
- h = bbox[3] - bbox[1]
- img_info.append((bbox, w, h))
- # extract by xref, map to bbox by order
- for ii, img in enumerate(images):
- xref = img[0]
- try:
- pix = fitz.Pixmap(doc, xref)
- except Exception:
- continue
- if pix.n - pix.alpha > 3:
- pix = fitz.Pixmap(fitz.csRGB, pix)
- if pix.width < MIN_W or pix.height < MIN_H or pix.width * pix.height < MIN_PIXELS:
- continue
- # skip near-duplicates of same xref on same page
- key = (pi, xref, pix.width, pix.height)
- if key in seen_xref:
- continue
- seen_xref[key] = True
- bbox = img_info[ii][0] if ii < len(img_info) else (0, 0, pix.width, pix.height)
- cap = caption_from_page(page, bbox)
- idx += 1
- fname = "%03d_p%02d_%s.png" % (idx, pi, safe_name(cap) or "img")
- path = dest / fname
- pix.save(str(path))
- meta.append({
- "file": str(path.relative_to(OUT.parent)).replace("\\", "/"),
- "page": pi,
- "caption": cap,
- "heading": heading,
- "w": pix.width,
- "h": pix.height,
- })
- doc.close()
- (dest / "manifest.json").write_text(json.dumps(meta, ensure_ascii=False, indent=2), encoding="utf-8")
- print(stem, "images", len(meta))
- return meta
- def main():
- OUT.mkdir(parents=True, exist_ok=True)
- allm = {}
- for name in FASCICLES:
- allm[name] = extract_one(name)
- (OUT / "all_manifest.json").write_text(json.dumps(allm, ensure_ascii=False, indent=2), encoding="utf-8")
- print("total", sum(len(v) for v in allm.values()))
- if __name__ == "__main__":
- main()
|