Reza2kn's picture
Bina 0.4 Rizeh: full 95k production pipeline
4b917c4
Raw History Blame Contribute Delete
2.89 kB
"""32M checkpoint consumer for RegionalPipeline's RTL bank; raw CTC retained."""
import json,math
from pathlib import Path
import numpy as np
import torch
from recognizer import Recognizer
from geometry import crop_line64,pad_batch
from visual_token import RTLVisualChar
class RTLReader:
def __init__(self, checkpoint, vocabulary, device='cpu', frame_budget=8192):
self.device = torch.device(device)
data = json.loads(Path(vocabulary).read_text())
self.alphabet = data['chars']
self.model = Recognizer(classes=len(self.alphabet) + 1, gradient_checkpointing=False)
state = torch.load(checkpoint, map_location='cpu', weights_only=True)
self.model.load_state_dict(state['model'])
self.model.to(self.device).eval()
self.frame_budget = frame_budget
@torch.inference_mode()
def __call__(self, gray, boxes):
from indra_ocr.pipeline import MIN_CONF
crops, receipts = [], []
for box in boxes:
crop, receipt = crop_line64(gray, box)
crops.append(crop)
receipts.append(receipt)
order = sorted(range(len(crops)), key=lambda i: crops[i].shape[1])
parts = []
for i in order:
frames = math.ceil(crops[i].shape[1] / 8) * 2
if parts and len(parts[-1]) < 32 and (len(parts[-1]) + 1) * frames <= self.frame_budget:
parts[-1].append(i)
else:
parts.append([i])
readings = [None] * len(crops)
for ids in parts:
x, widths = pad_batch([crops[i] for i in ids])
x = torch.from_numpy(x).to(self.device)
logits, lengths = self.model(x, torch.from_numpy(widths))
probs, best = logits.float().softmax(-1).max(-1)
best, probs = best.cpu().numpy(), probs.cpu().numpy()
for k, i in enumerate(ids):
n = int(lengths[k])
seq, pm = best[k, :n], probs[k, :n]
keep = (seq != 0) & np.concatenate(([True], seq[1:] != seq[:-1]))
steps = np.nonzero(keep)[0]
token_ids = seq[steps].tolist()
visual = ''.join(self.alphabet[t - 1] for t in token_ids)
conf = float(pm[keep].mean()) if keep.any() else 0.
g = receipts[i]
left, _, right, _ = g['padded_source_extent']
xs = left + (steps * 4 + 3.5) * (right - left) / crops[i].shape[1]
toks = list(zip([RTLVisualChar(self.alphabet[t - 1]) for t in token_ids], xs.tolist()))
receipts[i] = dict(g, raw_visual_text=visual, raw_token_ids=token_ids, confidence=conf, frame_centers=xs.tolist(), confidence_threshold=MIN_CONF, confidence_filtered=conf < MIN_CONF)
readings[i] = (visual, conf, toks) if conf >= MIN_CONF else ('', conf, [])
return readings, receipts