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>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
"""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])
|
||||
Reference in New Issue
Block a user