TDATR / app.py
aoyama's picture
Deploy TDATR ZeroGPU Space
3bb7378 verified
Raw
History Blame Contribute Delete
10.1 kB
import html
import json
import os
import socket
import sys
import tempfile
import threading
import traceback
from pathlib import Path
import gradio as gr
import numpy as np
from huggingface_hub import hf_hub_download
from PIL import Image
try:
import spaces
except ImportError:
class _SpacesFallback:
@staticmethod
def GPU(*decorator_args, **decorator_kwargs):
if decorator_args and callable(decorator_args[0]) and len(decorator_args) == 1:
return decorator_args[0]
def _decorate(fn):
return fn
return _decorate
spaces = _SpacesFallback()
ROOT = Path(__file__).resolve().parent
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "TDATR" / "eval"))
os.chdir(ROOT)
os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")
os.environ.setdefault("HYDRA_FULL_ERROR", "1")
os.environ.setdefault("NCCL_DEBUG", "WARN")
MODEL_REPO = os.environ.get("TDATR_MODEL_REPO", "CCWM/TDATR")
MODEL_FILE = os.environ.get("TDATR_MODEL_FILE", "model.pt")
_runner = None
_runner_lock = threading.Lock()
_predict_lock = threading.Lock()
def _free_port() -> int:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind(("127.0.0.1", 0))
return int(sock.getsockname()[1])
def _checkpoint_path() -> str:
local_path = os.environ.get("TDATR_CKPT_PATH")
if local_path and Path(local_path).exists():
return local_path
return hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE)
def _build_cfg(max_new_tokens: int, temperature: float, seed: int):
from omegaconf import OmegaConf, open_dict
from TDATR_utils.dataclass import HulkConfig
from TDATR_utils.utils import add_defaults
base_cfg = OmegaConf.structured(HulkConfig)
file_cfg = OmegaConf.load(ROOT / "configs" / "config.yaml")
cfg = OmegaConf.merge(base_cfg, file_cfg)
add_defaults(cfg)
with open_dict(cfg):
cfg.common.user_dir = str(ROOT / "TDATR")
cfg.common.npu = False
cfg.common.npu_jit_compile = False
cfg.common.suppress_crashes = False
cfg.distributed_training.distributed_world_size = 1
cfg.distributed_training.distributed_num_procs = 1
cfg.distributed_training.distributed_rank = 0
cfg.distributed_training.distributed_local_rank = 0
cfg.distributed_training.distributed_no_spawn = True
cfg.distributed_training.distributed_backend = "nccl"
cfg.distributed_training.distributed_master_addr = "127.0.0.1"
cfg.distributed_training.distributed_master_port = _free_port()
cfg.distributed_training.distributed_init_method = (
f"tcp://127.0.0.1:{cfg.distributed_training.distributed_master_port}"
)
cfg.distributed_training.zero_sharding = "none"
cfg.model_parallel.recompute_granularity = None
with open_dict(cfg.model), open_dict(cfg.model.lora):
cfg.model.ckpt = _checkpoint_path()
cfg.model.lora.apply_lora = False
cfg.model.use_naiive = True
cfg.model.use_vit_encoder = False
cfg.model.use_donut_encoder = True
cfg.model.use_cfgi = True
cfg.model.use_flash = False
cfg.model.cross_flash_attn = False
cfg.model.CFGI_CFG.use_flash = False
cfg.model.CFGI_CFG.cross_flash_attn = False
cfg.model.rectification_rotate_flag = False
cfg.model.rectification_textline_height_flag = False
cfg.task.use_ocr_plug = False
cfg.task.seed = int(seed)
cfg.generation.max_len = int(max_new_tokens)
cfg.generation.temperature = float(temperature)
cfg.generation.sampling_topk = 1
cfg.generation.sampling_topp = 0.0
return cfg
class TDATRRunner:
def __init__(self, max_new_tokens: int, temperature: float, seed: int):
import torch
from TDATR.eval.infer import Dataset_infer
from TDATR.models.detect.structures_.det_data_sample import DetDataSample
from TDATR.models.detect.structures_.instance_data import InstanceData
from TDATR.models.mini_gpt4_ipt_v2 import MiniGPT4
from TDATR_utils.global_context import global_context as gpc
from TDATR_utils.initialize import initialize_hulk
self.torch = torch
self.DetDataSample = DetDataSample
self.InstanceData = InstanceData
self.dataset = Dataset_infer()
self.cfg = _build_cfg(max_new_tokens, temperature, seed)
if not torch.distributed.is_initialized():
initialize_hulk(self.cfg)
elif gpc.config is None:
gpc.config = self.cfg
torch.cuda.set_device(0)
self.device = torch.device("cuda")
self.model = MiniGPT4(self.cfg).half()
self.tokenizer = self.model.ipt_tokenizer
self.model.eval()
self.model = self.model.to(device=self.device)
self.model.cfgi_decoder.neck.train()
self.model.cfgi_decoder.encoder.train()
def predict(self, image_path: str, max_new_tokens: int, temperature: float, seed: int):
import cv2
from TDATR.eval.infer import encode_img, single_prompt_process_cfgi
from TDATR_utils.global_context import global_context as gpc
self.cfg.generation.max_len = int(max_new_tokens)
self.cfg.generation.temperature = float(temperature)
self.cfg.task.seed = int(seed)
gpc.config = self.cfg
image_embed, det_input = encode_img(self.model, self.dataset, image_path, self.device)
_, scale, img_shape, raw_scale, image_padding_shape = det_input
data_sample = self.DetDataSample()
data_sample.gt_instances = self.InstanceData()
data_sample.set_metainfo({"img_shape": img_shape, "batch_input_shape": img_shape})
query_text = "将图片中的表格转换为HTML语言。<iflytek_ret>"
(
_raw_query,
raw_answer,
clear_answer,
_gen_tokens,
_embs,
_context_length,
cell_boxes_pred,
cell_span_html,
cell_texts,
) = single_prompt_process_cfgi(
self.model,
self.tokenizer,
"<end>",
query_text,
image_embed,
max_new_tokens=int(max_new_tokens),
sampling_topk=1,
sampling_topp=0.0,
temperature=float(temperature),
max_length=int(max_new_tokens),
random_seed=int(seed),
image_shape=image_padding_shape,
donuts_out=det_input[0],
gt_inst=data_sample,
)
cell_boxes_pred = self.dataset.recover_pred_cell_box2raw_image(
img_shape, scale, raw_scale, cell_boxes_pred
)
cells = self.dataset.process_cell_info(cell_boxes_pred, cell_texts, cell_span_html)
image = cv2.imread(image_path)
for cell in cell_boxes_pred:
image = cv2.rectangle(
image,
cell[:2].tolist(),
cell[2:].tolist(),
(0, 0, 255),
3,
)
vis_path = str(Path(tempfile.mkdtemp()) / "tdatr_cells.png")
cv2.imwrite(vis_path, image)
result = {
"image_path": image_path,
"answer": {
"query": query_text,
"clear_answer": clear_answer,
"raw_answer": raw_answer,
"cells": cells,
},
}
return clear_answer, result, vis_path
def _get_runner(max_new_tokens: int, temperature: float, seed: int) -> TDATRRunner:
global _runner
with _runner_lock:
if _runner is None:
_runner = TDATRRunner(max_new_tokens, temperature, seed)
return _runner
def _save_upload(image: Image.Image) -> str:
if image is None:
raise gr.Error("画像をアップロードしてください。")
image = image.convert("RGB")
tmp_dir = Path(tempfile.mkdtemp())
image_path = tmp_dir / "input.png"
image.save(image_path)
return str(image_path)
@spaces.GPU(size="xlarge", duration=600)
def run_tdatr(image, max_new_tokens, temperature, seed):
try:
import torch
if not torch.cuda.is_available():
return (
"<p>This Space is currently running without CUDA. "
"TDATR needs ZeroGPU or another CUDA GPU hardware to run inference.</p>",
"",
None,
)
image_path = _save_upload(image)
runner = _get_runner(int(max_new_tokens), float(temperature), int(seed))
with _predict_lock:
table_html, result, vis_path = runner.predict(
image_path,
int(max_new_tokens),
float(temperature),
int(seed),
)
return table_html, json.dumps(result, ensure_ascii=False, indent=2), vis_path
except gr.Error:
raise
except Exception:
escaped = html.escape(traceback.format_exc())
return f"<pre>{escaped}</pre>", "", None
with gr.Blocks(title="TDATR") as demo:
gr.Markdown("# TDATR")
with gr.Row():
image_input = gr.Image(type="pil", label="Table image")
with gr.Column():
max_tokens = gr.Slider(256, 4096, value=2048, step=128, label="Max tokens")
temperature = gr.Slider(0.1, 1.0, value=0.5, step=0.1, label="Temperature")
seed = gr.Number(value=42, precision=0, label="Seed")
run_button = gr.Button("Run", variant="primary")
with gr.Row():
html_output = gr.HTML(label="HTML")
vis_output = gr.Image(type="filepath", label="Cell boxes")
json_output = gr.Code(language="json", label="Result JSON")
run_button.click(
fn=run_tdatr,
inputs=[image_input, max_tokens, temperature, seed],
outputs=[html_output, json_output, vis_output],
)
if __name__ == "__main__":
demo.queue(max_size=8).launch(show_error=True)