Spaces:
Running on Zero
Running on Zero
Download layout_worker.py from VincentHancoder/RAP: direct link, hf CLI and curl.
- Browser
- Download file 4.23 kB
-
https://huggingface.co/spaces/VincentHancoder/RAP/resolve/c58c39d08fed13232b5e843040977eb5b82810ce/layout_worker.py
- Command line
-
hf download hf://spaces/VincentHancoder/RAP@c58c39d08fed13232b5e843040977eb5b82810ce/layout_worker.py
-
curl -L -o layout_worker.py https://huggingface.co/spaces/VincentHancoder/RAP/resolve/c58c39d08fed13232b5e843040977eb5b82810ce/layout_worker.py
4.23 kB
| """CPU LayoutLMv3 extraction in an isolated legacy-Transformers environment.""" | |
| import json | |
| import os | |
| import sys | |
| from pathlib import Path | |
| def run(pdf, destination): | |
| import fitz | |
| import numpy as np | |
| import torch | |
| from PIL import Image | |
| from huggingface_hub import hf_hub_download | |
| from transformers import modeling_utils, pytorch_utils | |
| for name in ('find_pruneable_heads_and_indices', 'prune_linear_layer'): | |
| if not hasattr(modeling_utils, name): | |
| setattr(modeling_utils, name, getattr(pytorch_utils, name)) | |
| from detectron2.config import get_cfg | |
| from detectron2.engine import DefaultPredictor | |
| from layout_vendor.model_init import add_vit_config | |
| import layout_vendor.backbone | |
| import layout_vendor.rcnn_vl | |
| torch.set_num_threads(max(1, min(4, os.cpu_count() or 2))) | |
| revision = 'ed6b654c018d742e65a17671e379c5e6ecc87ec9' | |
| model_dir = None | |
| for name in ('config.json', 'model_final.pth'): | |
| p = hf_hub_download('opendatalab/PDF-Extract-Kit-1.0', | |
| 'models/Layout/LayoutLMv3/' + name, revision=revision, token=False) | |
| model_dir = str(Path(p).parent) | |
| cfg = get_cfg() | |
| add_vit_config(cfg) | |
| cfg.merge_from_file(str(Path(__file__).parent/'layout_vendor/layoutlmv3_base_inference.yaml')) | |
| cfg.MODEL.DEVICE = 'cpu' | |
| cfg.MODEL.CONFIG_PATH = model_dir | |
| cfg.MODEL.WEIGHTS = str(Path(model_dir)/'model_final.pth') | |
| cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.2 | |
| cfg.freeze() | |
| predictor = DefaultPredictor(cfg) | |
| classes = ['title', 'plain text', 'abandon', 'figure', 'figure_caption', 'table', | |
| 'table_caption', 'table_footnote', 'isolate_formula', 'formula_caption'] | |
| kinds = {'figure':'figure','table':'table','isolate_formula':'formula'} | |
| out = Path(destination); (out/'assets').mkdir(parents=True, exist_ok=True) | |
| layout, assets, counters = [], [], {k:0 for k in kinds} | |
| def iou(a, b): | |
| w = max(0, min(a[2],b[2])-max(a[0],b[0])) | |
| h = max(0, min(a[3],b[3])-max(a[1],b[1])) | |
| return w*h/max(1, (a[2]-a[0])*(a[3]-a[1])+(b[2]-b[0])*(b[3]-b[1])-w*h) | |
| with fitz.open(pdf) as doc: | |
| if doc.needs_pass: | |
| raise ValueError('Password-protected PDFs are not supported.') | |
| n = min(12, len(doc)) | |
| for i in range(n): | |
| page = doc[i] | |
| if page.rect.width*page.rect.height*(200/72)**2 > 20_000_000: | |
| raise ValueError('PDF page dimensions are too large.') | |
| pix = page.get_pixmap(matrix=fitz.Matrix(200/72,200/72), colorspace=fitz.csRGB, alpha=False) | |
| image = Image.frombytes('RGB', (pix.width,pix.height), pix.samples) | |
| pred = predictor(np.asarray(image)[:,:,::-1])['instances'].to('cpu') | |
| dets=[] | |
| for box, score, cls in zip(pred.pred_boxes.tensor.tolist(),pred.scores.tolist(),pred.pred_classes.tolist()): | |
| dets.append({'page':i+1,'class_name':classes[cls], 'bbox':box,'score':score}) | |
| layout.extend(dets) | |
| selected=[] | |
| for d in sorted(dets,key=lambda d:-d['score']): | |
| b=d['bbox']; area=(b[2]-b[0])*(b[3]-b[1]) | |
| if d['class_name'] not in kinds or d['score']<.60 or area<.004*pix.width*pix.height: | |
| continue | |
| if any(iou(b,k['bbox'])>=.75 for k in selected): | |
| continue | |
| selected.append(d) | |
| cls=d['class_name']; counters[cls]+=1; aid=f'{kinds[cls]}_{counters[cls]}' | |
| crop=image.crop((max(0,int(b[0])-6),max(0,int(b[1])-6), | |
| min(pix.width,int(b[2])+6),min(pix.height,int(b[3])+6))) | |
| crop.save(out/'assets'/f'{aid}.png') | |
| assets.append({'id':aid,'class':cls,'page':i+1,'bbox':b,'score':d['score'], | |
| 'w':crop.width,'h':crop.height,'aspect':round(crop.width/max(1,crop.height),2)}) | |
| print(f'LAYOUT_PAGE {i+1}/{n}', flush=True) | |
| (out/'assets.json').write_text(json.dumps({'dpi':200,'n_pages':n,'assets':assets,'counts':counters})) | |
| (out/'layout.json').write_text(json.dumps(layout)) | |
| print('LAYOUT_DONE',flush=True) | |
| if __name__ == '__main__': | |
| run(sys.argv[1],sys.argv[2]) | |