| 1 | """Crop a region of a frame at native pixel resolution and save it.""" |
| 2 | from __future__ import annotations |
| 3 | |
| 4 | import sys |
| 5 | from pathlib import Path |
| 6 | |
| 7 | import cv2 |
| 8 | from rich.console import Console |
| 9 | |
| 10 | console = Console() |
| 11 | |
| 12 | def main() -> int: |
| 13 | if len(sys.argv) < 6: |
| 14 | console.print("usage: crop.py <in.png> <x> <y> <w> <h> [out.png]") |
| 15 | return 2 |
| 16 | inp = Path(sys.argv[1]) |
| 17 | x, y, w, h = int(sys.argv[2]), int(sys.argv[3]), int(sys.argv[4]), int(sys.argv[5]) |
| 18 | out = Path(sys.argv[6]) if len(sys.argv) >= 7 else ( |
| 19 | Path(__file__).resolve().parents[1] / "logs" / f"crop_{inp.stem}_{x}_{y}_{w}x{h}.png" |
| 20 | ) |
| 21 | frame = cv2.imread(str(inp), cv2.IMREAD_COLOR) |
| 22 | if frame is None: |
| 23 | console.print(f"[red]Can't read {inp}[/red]") |
| 24 | return 2 |
| 25 | H, W = frame.shape[:2] |
| 26 | x0, y0 = max(0, x), max(0, y) |
| 27 | x1, y1 = min(W, x + w), min(H, y + h) |
| 28 | if x1 <= x0 or y1 <= y0: |
| 29 | console.print(f"[red]Invalid crop: image is {W}x{H}[/red]") |
| 30 | return 2 |
| 31 | crop = frame[y0:y1, x0:x1] |
| 32 | cv2.imwrite(str(out), crop) |
| 33 | console.print(f"saved {crop.shape[1]}x{crop.shape[0]} -> {out}") |
| 34 | return 0 |
| 35 | |
| 36 | if __name__ == "__main__": |
| 37 | sys.exit(main()) |