Add cleaned RealSense/OpenCV CV under cv/ with secret-free example config, MQTT disabled by default, and canonical README coverage for web + CV relationship without claiming production integration. Co-authored-by: Cursor <cursoragent@cursor.com>
152 lines
4.8 KiB
Python
152 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Сбор логов классификации с камеры.
|
||
Кладите предметы по очереди — пишет CSV + печатает сводку.
|
||
|
||
.venv/bin/python collect_log.py
|
||
# Ctrl+C — стоп
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import csv
|
||
import sys
|
||
import time
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
|
||
import yaml
|
||
|
||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||
|
||
from camera import RealSenseV4L2
|
||
from measure import measure_object, segment_object
|
||
from stabilize import DecisionStabilizer
|
||
|
||
|
||
def main() -> int:
|
||
cfg = yaml.safe_load(Path("config.yaml").read_text(encoding="utf-8"))
|
||
cam_cfg = cfg["camera"]
|
||
cls = cfg["classification"]
|
||
thr = float(cls.get("circle_ratio_threshold", 0.8))
|
||
|
||
out_dir = Path("debug_frames")
|
||
out_dir.mkdir(exist_ok=True)
|
||
stamp = datetime.now().strftime("%H%M%S")
|
||
csv_path = out_dir / f"log_{stamp}.csv"
|
||
|
||
cam = RealSenseV4L2(
|
||
depth_device=cam_cfg.get("depth_device", "/dev/video0"),
|
||
color_device=cam_cfg.get("color_device", "/dev/video4"),
|
||
width=int(cam_cfg.get("width", 640)),
|
||
height=int(cam_cfg.get("height", 480)),
|
||
fps=int(cam_cfg.get("fps", 30)),
|
||
depth_scale_mm=float(cam_cfg.get("depth_scale_mm", 1.0)),
|
||
use_color=False,
|
||
)
|
||
belt = float(cfg.get("belt_distance_mm") or 600)
|
||
print(f"[log] belt={belt:.0f} mm thr={thr} → {csv_path}")
|
||
print("[log] Кладите КРУГ / ПРЯМОУГОЛЬНИК. Ctrl+C — стоп.\n")
|
||
|
||
stab = DecisionStabilizer(window=12, confirm_frames=8, lost_frames=12, enter_circle=thr)
|
||
fx, fy = float(cam_cfg["fx"]), float(cam_cfg["fy"])
|
||
cx, cy = float(cam_cfg["cx"]), float(cam_cfg["cy"])
|
||
|
||
f = csv_path.open("w", newline="", encoding="utf-8")
|
||
w = csv.writer(f)
|
||
w.writerow(
|
||
[
|
||
"t",
|
||
"present",
|
||
"L",
|
||
"W",
|
||
"H",
|
||
"top",
|
||
"sec",
|
||
"circle",
|
||
"raw_zone",
|
||
"lock",
|
||
"lock_zone",
|
||
"conf",
|
||
]
|
||
)
|
||
|
||
last_print = 0.0
|
||
n = 0
|
||
try:
|
||
while True:
|
||
pair = cam.read()
|
||
if pair is None:
|
||
time.sleep(0.02)
|
||
continue
|
||
n += 1
|
||
seg = segment_object(
|
||
pair.depth_mm,
|
||
belt_distance_mm=belt,
|
||
belt_tolerance_mm=float(cfg.get("belt_tolerance_mm", 25)),
|
||
min_object_height_mm=float(cfg.get("min_object_height_mm", 5)),
|
||
min_area_px=int(cfg.get("min_object_area_px", 400)),
|
||
)
|
||
m = None
|
||
if seg is not None:
|
||
mask, contour = seg
|
||
m = measure_object(
|
||
pair.depth_mm, mask, contour, belt, fx, fy, cx, cy
|
||
)
|
||
|
||
d = stab.update(
|
||
m,
|
||
min_mm=cls.get("min_mm", [10, 10, 10]),
|
||
max_mm=cls.get("max_mm", [450, 320, 320]),
|
||
)
|
||
|
||
if m is None:
|
||
raw_zone = "-"
|
||
row = [time.time(), 0, "", "", "", "", "", "", raw_zone, int(d.locked), "", d.confidence_pct]
|
||
else:
|
||
dims = sorted([m.length_mm, m.width_mm, m.height_mm], reverse=True)
|
||
raw = "C"
|
||
if all(dims[i] > 10 and dims[i] < [450, 320, 320][i] for i in range(3)):
|
||
raw = "D" if m.circle_ratio >= thr else "B"
|
||
lz = d.result.category.zone if (d.locked and d.result) else ""
|
||
row = [
|
||
time.time(),
|
||
1,
|
||
round(dims[0], 1),
|
||
round(dims[1], 1),
|
||
round(dims[2], 1),
|
||
round(m.top_ratio, 3),
|
||
round(m.section_ratio, 3),
|
||
round(m.circle_ratio, 3),
|
||
raw,
|
||
int(d.locked),
|
||
lz,
|
||
d.confidence_pct,
|
||
]
|
||
w.writerow(row)
|
||
if n % 5 == 0:
|
||
f.flush()
|
||
|
||
now = time.time()
|
||
if now - last_print > 0.45:
|
||
last_print = now
|
||
if m is None:
|
||
print(f"[{n:05d}] пусто")
|
||
else:
|
||
lz = d.result.category.zone if (d.locked and d.result) else "…"
|
||
print(
|
||
f"[{n:05d}] raw={row[8]} lock={lz or '—':1s} conf={d.confidence_pct:3d}% | "
|
||
f"LWH={row[2]:.0f}×{row[3]:.0f}×{row[4]:.0f} | "
|
||
f"top={m.top_ratio:.3f} sec={m.section_ratio:.3f} circ={m.circle_ratio:.3f}"
|
||
)
|
||
except KeyboardInterrupt:
|
||
print(f"\n[log] сохранено {csv_path}")
|
||
finally:
|
||
f.close()
|
||
cam.release()
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|