Files
vosonandCursor 9c5aa5b610 Reorganize repository into pc web shared monorepo
Separate the local recognition, web publishing, and shared data paths while preserving direct script execution and existing site content.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 14:29:08 +08:00

84 lines
2.8 KiB
Python

"""One-time ROI calibration.
Usage:
python calibrate.py samples/full_1080p.png # interactive: drag 10 slot boxes
python calibrate.py samples/full_1080p.png --check # draw current config on image
Interactive mode: for each of the 10 hero slots (any order), drag a box around
the portrait (include the whole parallelogram, exclude neighbors), then press
SPACE/ENTER. Press ESC when all 10 are done. Slots are sorted left-to-right
and stored as resolution-independent relative coordinates.
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import time
import cv2
from common import load_config, save_config, slot_rect_px
def calibrate(image_path: str) -> None:
img = cv2.imread(image_path)
if img is None:
sys.exit(f"cannot read image: {image_path}")
ih, iw = img.shape[:2]
print(f"image size: {iw}x{ih}")
print("Drag a box per slot (10 total), SPACE/ENTER to confirm each, ESC to finish.")
rois = cv2.selectROIs("calibrate - drag 10 slots", img, showCrosshair=True)
cv2.destroyAllWindows()
if len(rois) != 10:
sys.exit(f"expected 10 boxes, got {len(rois)} - please rerun")
rois = sorted(rois.tolist(), key=lambda r: r[0])
cfg = load_config()
cfg["slots"] = []
avg_w = sum(r[2] for r in rois) / 10
avg_h = sum(r[3] for r in rois) / 10
cfg["slot_w_rel"] = round(avg_w / ih, 5)
cfg["slot_h_rel"] = round(avg_h / ih, 5)
for i, (x, y, w, h) in enumerate(rois):
cfg["slots"].append(
{
"index": i + 1,
"cx_rel": round((x + w / 2 - iw / 2) / ih, 5),
"cy_rel": round((y + h / 2) / ih, 5),
}
)
cfg["calibrated_on"] = f"{iw}x{ih} {time.strftime('%Y-%m-%d %H:%M')}"
save_config(cfg)
print(f"saved {len(cfg['slots'])} slots to config.json")
check(image_path)
def check(image_path: str) -> None:
"""Draw configured slot rects onto the image for visual verification."""
img = cv2.imread(image_path)
if img is None:
sys.exit(f"cannot read image: {image_path}")
ih, iw = img.shape[:2]
cfg = load_config()
if not cfg["slots"]:
sys.exit("config.json has no slots - run calibration first")
for slot in cfg["slots"]:
x, y, w, h = slot_rect_px(slot, cfg, iw, ih)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.putText(img, str(slot["index"]), (x, y - 4), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
out = "calibrate_check.png"
cv2.imwrite(out, img)
print(f"wrote {out} - open it and verify the boxes sit on the 10 portraits")
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.exit(__doc__)
if "--check" in sys.argv:
check(sys.argv[1])
else:
calibrate(sys.argv[1])