Buckets:
| """Render target-centered, source-label review sheets for the KFGOD audit queue.""" | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| from math import ceil | |
| from pathlib import Path | |
| from PIL import Image, ImageDraw | |
| def crop_bounds(vertices: list[list[float]], width: int, height: int) -> tuple[int, int, int, int]: | |
| xs, ys = zip(*vertices) | |
| side = max(256, max(xs) - min(xs), max(ys) - min(ys)) * 2.2 | |
| side = min(768, side) | |
| cx, cy = (min(xs) + max(xs)) / 2, (min(ys) + max(ys)) / 2 | |
| left = max(0, min(width - side, cx - side / 2)) | |
| top = max(0, min(height - side, cy - side / 2)) | |
| return round(left), round(top), round(left + side), round(top + side) | |
| def main() -> None: | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--queue", type=Path, required=True) | |
| parser.add_argument("--output-dir", type=Path, required=True) | |
| args = parser.parse_args() | |
| queue = json.loads(args.queue.read_text()) | |
| records = queue["records"] | |
| columns, rows, cell = 4, 4, 320 | |
| header = 38 | |
| args.output_dir.mkdir(parents=True, exist_ok=True) | |
| for page_index in range(ceil(len(records) / (columns * rows))): | |
| page_records = records[page_index * columns * rows:(page_index + 1) * columns * rows] | |
| sheet = Image.new("RGB", (columns * cell, rows * (cell + header)), "white") | |
| draw = ImageDraw.Draw(sheet) | |
| for index, record in enumerate(page_records): | |
| with Image.open(record["image"]) as image: | |
| image = image.convert("RGB") | |
| bounds = crop_bounds(record["vertices"], image.width, image.height) | |
| crop = image.crop(bounds) | |
| scale_x, scale_y = cell / crop.width, cell / crop.height | |
| crop = crop.resize((cell, cell)) | |
| crop_draw = ImageDraw.Draw(crop) | |
| polygon = [((x - bounds[0]) * scale_x, (y - bounds[1]) * scale_y) for x, y in record["vertices"]] | |
| crop_draw.line(polygon + [polygon[0]], fill=(0, 220, 0), width=3) | |
| x = (index % columns) * cell | |
| y = (index // columns) * (cell + header) | |
| sheet.paste(crop, (x, y + header)) | |
| draw.text((x + 6, y + 6), f"{record['canonical_class']} / {record['source_label']}", fill="black") | |
| draw.text((x + 6, y + 20), f"{record['scene_id']} #{record['object_index']}", fill="black") | |
| sheet.save(args.output_dir / f"page-{page_index + 1:02d}.jpg", "JPEG", quality=92) | |
| print(json.dumps({"records": len(records), "pages": ceil(len(records) / (columns * rows))})) | |
| if __name__ == "__main__": | |
| main() | |
Xet Storage Details
- Size:
- 2.59 kB
- Xet hash:
- 4a42aca9466c994b1a917f9eb2ca6d3517b1b1e955101746f1469aac8b6474c2
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.