receiptiq / data /preprocessor.py
ilaa-chenjeri-15's picture
Initial commit
29e0671
Raw
History Blame Contribute Delete
3.33 kB
import json
import os
import torch
from PIL import Image
from tqdm import tqdm
DEFAULT_SAVE_DIR = "./data/processed"
def clean_price(price_str: str) -> str:
return str(price_str).strip().rstrip('.')
def parse_ground_truth(gt_string: str) -> dict:
gt = json.loads(gt_string)
parsed = gt.get("gt_parse", {})
items = []
menu = parsed.get("menu", [])
if not isinstance(menu, list):
menu = []
for item in menu:
if not isinstance(item, dict):
continue
items.append({
"name" : str(item.get("nm", "")).strip(),
"count": str(item.get("cnt", "")).strip(),
"price": clean_price(item.get("price", "0")),
})
sub = parsed.get("sub_total", {})
if not isinstance(sub, dict):
sub = {}
total_obj = parsed.get("total", {})
if not isinstance(total_obj, dict):
total_obj = {}
return {
"vendor" : "unknown",
"items" : items,
"subtotal": clean_price(sub.get("subtotal_price", "0")),
"tax" : clean_price(sub.get("tax_price", "0")),
"service" : clean_price(sub.get("service_price", "0")),
"total" : clean_price(total_obj.get("total_price", "0")),
}
def format_instruction(sample: dict) -> dict:
img = sample["image"].convert("RGB")
parsed = parse_ground_truth(sample["ground_truth"])
prompt = (
"Extract the receipt information and return ONLY valid JSON.\n"
'Schema: {"vendor":"","items":[{"name":"","count":"","price":""}],"subtotal":"","tax":"","service":"","total":""}'
)
target = json.dumps(parsed, ensure_ascii=False)
return {
"image" : img,
"prompt": prompt,
"target": target,
}
def save_splits(splits: dict, save_dir: str = DEFAULT_SAVE_DIR) -> None:
os.makedirs(save_dir, exist_ok=True)
for split, data in splits.items():
path = os.path.join(save_dir, f"{split}.pt")
torch.save(data, path)
print(f" Saved {len(data):>5} samples → {path}")
def load_splits(save_dir: str = DEFAULT_SAVE_DIR) -> dict:
splits = {}
for split in ["train", "validation", "test"]:
path = os.path.join(save_dir, f"{split}.pt")
if not os.path.exists(path):
raise FileNotFoundError(
f"Expected '{path}' — run the pipeline first to generate it."
)
splits[split] = torch.load(path)
print(f" Loaded {len(splits[split]):>5} samples ← {path}")
return splits
def preprocess_dataset(dataset: dict, save_dir: str = None) -> dict:
splits = {}
for split in ["train", "validation", "test"]:
processed = []
skipped = 0
for sample in tqdm(dataset[split], desc=f" {split:<12}", unit="sample"):
try:
formatted = format_instruction(sample)
processed.append(formatted)
except Exception as e:
skipped += 1
tqdm.write(f" [skip] {split} sample: {e}")
continue
if skipped:
print(f" {split}: skipped {skipped} samples")
splits[split] = processed
if save_dir is not None:
print("\nSaving processed splits...")
save_splits(splits, save_dir)
return splits