所有
This commit is contained in:
403
test/test_algo_preview_live.py
Normal file
403
test/test_algo_preview_live.py
Normal file
@@ -0,0 +1,403 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
实时摄像头预览:叠加与射箭存图相同的算法标注(YOLO ROI、三角/圆心、激光十字等),默认不写盘。
|
||||
|
||||
在 MaixCAM 上从项目根目录运行:
|
||||
python3 test/test_algo_preview_live.py
|
||||
python3 test/test_algo_preview_live.py --interval 1.5
|
||||
python3 test/test_algo_preview_live.py --every-frame
|
||||
|
||||
说明:
|
||||
- 完整算法走 shoot_manager.analyze_shot(与 process_shot 一致,含 YOLO + 三角/圆心)。
|
||||
- 画面标注对齐 process_shot 存图前绘制 + vision._draw_yolo_roi_on_rgb_numpy / 圆心存图线。
|
||||
- 预览模式会关闭 Stage2 裁切 JPEG 落盘,避免写满 /root/phot。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if _ROOT not in sys.path:
|
||||
sys.path.insert(0, _ROOT)
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from maix import image, time as maix_time
|
||||
|
||||
import config
|
||||
from camera_manager import camera_manager
|
||||
from laser_manager import laser_manager
|
||||
from shoot_manager import analyze_shot, preload_triangle_calib
|
||||
from target_roi_yolo import preload_yolo_detector
|
||||
from vision import _draw_yolo_roi_on_rgb_numpy
|
||||
|
||||
|
||||
def _copy_maix_frame(frame):
|
||||
"""相机下一帧可能复用缓冲区,异步分析前先复制。"""
|
||||
img_cv = image.image2cv(frame, False, False)
|
||||
return image.cv2image(np.ascontiguousarray(img_cv), False, False)
|
||||
|
||||
|
||||
def _patch_preview_config():
|
||||
"""预览不写调试 JPEG,避免刷屏占存储。"""
|
||||
config.TRIANGLE_BLACK_YOLO_SAVE_ROI_CROP = False
|
||||
config.TRIANGLE_SAVE_DEBUG_IMAGE = False
|
||||
|
||||
|
||||
def _annotate_like_saved_shot(analysis: dict):
|
||||
"""
|
||||
将 analyze_shot 结果绘制成与 process_shot -> enqueue_save_shot 存盘前一致的 Maix 图。
|
||||
"""
|
||||
result_img = analysis.get("result_img")
|
||||
if result_img is None:
|
||||
return None
|
||||
|
||||
center = analysis.get("center")
|
||||
radius = analysis.get("radius")
|
||||
method = analysis.get("method")
|
||||
ellipse_params = analysis.get("ellipse_params")
|
||||
laser_point = analysis.get("laser_point")
|
||||
dx = analysis.get("dx")
|
||||
dy = analysis.get("dy")
|
||||
distance_m = analysis.get("distance_m")
|
||||
offset_method = analysis.get("offset_method", "")
|
||||
distance_method = analysis.get("distance_method", "")
|
||||
tri_markers = analysis.get("tri_markers") or []
|
||||
tri_markers_completed = analysis.get("tri_markers_completed") or []
|
||||
tri_homography = analysis.get("tri_homography")
|
||||
yolo_roi_xyxy = analysis.get("yolo_roi_xyxy")
|
||||
|
||||
if laser_point is None:
|
||||
return result_img
|
||||
|
||||
x, y = laser_point
|
||||
draw_yolo_roi = (
|
||||
yolo_roi_xyxy is not None
|
||||
and getattr(config, "TRIANGLE_YOLO_DRAW_ROI_ON_SHOT", True)
|
||||
)
|
||||
|
||||
if tri_markers:
|
||||
img_cv = image.image2cv(result_img, False, False).copy()
|
||||
|
||||
if draw_yolo_roi:
|
||||
_draw_yolo_roi_on_rgb_numpy(img_cv, yolo_roi_xyxy)
|
||||
|
||||
for m in tri_markers:
|
||||
corners = np.array(m["corners"], dtype=np.int32)
|
||||
cv2.polylines(img_cv, [corners], True, (0, 255, 0), 2)
|
||||
cx, cy = int(m["center"][0]), int(m["center"][1])
|
||||
cv2.circle(img_cv, (cx, cy), 4, (0, 0, 255), -1)
|
||||
cv2.putText(
|
||||
img_cv,
|
||||
f"T{m['id']}",
|
||||
(cx - 18, cy - 12),
|
||||
cv2.FONT_HERSHEY_SIMPLEX,
|
||||
0.55,
|
||||
(0, 255, 0),
|
||||
1,
|
||||
)
|
||||
|
||||
for m in tri_markers_completed:
|
||||
if not m.get("is_virtual"):
|
||||
continue
|
||||
cx, cy = int(m["center"][0]), int(m["center"][1])
|
||||
cv2.circle(img_cv, (cx, cy), 6, (255, 0, 255), 2)
|
||||
cv2.putText(
|
||||
img_cv,
|
||||
f"VT{m['id']}",
|
||||
(cx - 22, cy - 12),
|
||||
cv2.FONT_HERSHEY_SIMPLEX,
|
||||
0.55,
|
||||
(255, 0, 255),
|
||||
1,
|
||||
)
|
||||
|
||||
if tri_homography is not None:
|
||||
try:
|
||||
H_inv = np.linalg.inv(tri_homography)
|
||||
c_img = cv2.perspectiveTransform(
|
||||
np.array([[[0.0, 0.0]]], dtype=np.float32), H_inv
|
||||
)[0][0]
|
||||
ocx, ocy = int(c_img[0]), int(c_img[1])
|
||||
cv2.circle(img_cv, (ocx, ocy), 5, (0, 0, 255), -1)
|
||||
cv2.circle(img_cv, (ocx, ocy), 9, (0, 0, 255), 1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
lines = []
|
||||
if dx is not None and dy is not None:
|
||||
r_cm = math.hypot(float(dx), float(dy))
|
||||
lines.append(f"offset=({float(dx):.2f},{float(dy):.2f})cm |r|={r_cm:.2f}cm")
|
||||
if distance_m is not None:
|
||||
lines.append(f"cam_dist={float(distance_m):.2f}m ({distance_method})")
|
||||
if method:
|
||||
lines.append(f"method={method} ({offset_method})")
|
||||
y0 = 22
|
||||
for i, t in enumerate(lines):
|
||||
cv2.putText(
|
||||
img_cv,
|
||||
t,
|
||||
(10, y0 + i * 18),
|
||||
cv2.FONT_HERSHEY_SIMPLEX,
|
||||
0.5,
|
||||
(0, 255, 0),
|
||||
1,
|
||||
)
|
||||
|
||||
out = image.cv2image(img_cv, False, False)
|
||||
else:
|
||||
img_cv = image.image2cv(result_img, False, False).copy()
|
||||
if draw_yolo_roi:
|
||||
_draw_yolo_roi_on_rgb_numpy(img_cv, yolo_roi_xyxy)
|
||||
|
||||
if center and radius:
|
||||
cx, cy = center
|
||||
if ellipse_params:
|
||||
(ell_center, (width, height), angle) = ellipse_params
|
||||
cx_ell, cy_ell = int(ell_center[0]), int(ell_center[1])
|
||||
cv2.ellipse(
|
||||
img_cv,
|
||||
(cx_ell, cy_ell),
|
||||
(int(width / 2), int(height / 2)),
|
||||
angle,
|
||||
0,
|
||||
360,
|
||||
(0, 255, 0),
|
||||
2,
|
||||
)
|
||||
cv2.circle(img_cv, (cx_ell, cy_ell), 3, (255, 0, 0), -1)
|
||||
minor_length = min(width, height) / 2
|
||||
minor_angle = angle + 90 if width >= height else angle
|
||||
minor_angle_rad = math.radians(minor_angle)
|
||||
dx_minor = minor_length * math.cos(minor_angle_rad)
|
||||
dy_minor = minor_length * math.sin(minor_angle_rad)
|
||||
pt1 = (int(cx_ell - dx_minor), int(cy_ell - dy_minor))
|
||||
pt2 = (int(cx_ell + dx_minor), int(cy_ell + dy_minor))
|
||||
cv2.line(img_cv, pt1, pt2, (0, 0, 255), 2)
|
||||
else:
|
||||
cv2.circle(img_cv, (int(cx), int(cy)), int(radius), (0, 0, 255), 2)
|
||||
cv2.circle(img_cv, (int(cx), int(cy)), 2, (0, 0, 255), -1)
|
||||
cv2.line(img_cv, (int(x), int(y)), (int(cx), int(cy)), (255, 255, 0), 1)
|
||||
|
||||
lines = []
|
||||
if dx is not None and dy is not None:
|
||||
lines.append(f"offset=({float(dx):.2f},{float(dy):.2f})cm")
|
||||
if distance_m is not None:
|
||||
lines.append(f"dist={float(distance_m):.2f}m ({distance_method})")
|
||||
if method:
|
||||
lines.append(f"method={method}")
|
||||
for i, t in enumerate(lines):
|
||||
cv2.putText(
|
||||
img_cv,
|
||||
t,
|
||||
(10, 22 + i * 18),
|
||||
cv2.FONT_HERSHEY_SIMPLEX,
|
||||
0.5,
|
||||
(0, 255, 0),
|
||||
1,
|
||||
)
|
||||
out = image.cv2image(img_cv, False, False)
|
||||
|
||||
lc = image.Color(config.LASER_COLOR[0], config.LASER_COLOR[1], config.LASER_COLOR[2])
|
||||
out.draw_line(
|
||||
int(x - config.LASER_LENGTH),
|
||||
int(y),
|
||||
int(x + config.LASER_LENGTH),
|
||||
int(y),
|
||||
lc,
|
||||
config.LASER_THICKNESS,
|
||||
)
|
||||
out.draw_line(
|
||||
int(x),
|
||||
int(y - config.LASER_LENGTH),
|
||||
int(x),
|
||||
int(y + config.LASER_LENGTH),
|
||||
lc,
|
||||
config.LASER_THICKNESS,
|
||||
)
|
||||
out.draw_circle(int(x), int(y), 1, lc, config.LASER_THICKNESS)
|
||||
return out
|
||||
|
||||
|
||||
class _AlgoWorker:
|
||||
def __init__(self):
|
||||
self._lock = threading.Lock()
|
||||
self._busy = False
|
||||
self._latest_preview = None
|
||||
self._latest_meta = ""
|
||||
self._last_ms = 0.0
|
||||
|
||||
@property
|
||||
def busy(self):
|
||||
with self._lock:
|
||||
return self._busy
|
||||
|
||||
@property
|
||||
def last_ms(self):
|
||||
with self._lock:
|
||||
return self._last_ms
|
||||
|
||||
def get_preview(self):
|
||||
with self._lock:
|
||||
return self._latest_preview, self._latest_meta
|
||||
|
||||
def run_async(self, frame):
|
||||
with self._lock:
|
||||
if self._busy:
|
||||
return False
|
||||
self._busy = True
|
||||
|
||||
def _job():
|
||||
t0 = time.perf_counter()
|
||||
meta = ""
|
||||
preview = None
|
||||
try:
|
||||
analysis = analyze_shot(frame)
|
||||
if not analysis.get("success"):
|
||||
reason = analysis.get("reason", "unknown")
|
||||
meta = f"fail:{reason}"
|
||||
else:
|
||||
preview = _annotate_like_saved_shot(analysis)
|
||||
dx, dy = analysis.get("dx"), analysis.get("dy")
|
||||
method = analysis.get("method") or "?"
|
||||
if dx is not None and dy is not None:
|
||||
meta = f"ok {method} ({dx:.2f},{dy:.2f})cm"
|
||||
else:
|
||||
meta = f"ok {method} no_offset"
|
||||
except Exception as e:
|
||||
meta = f"err:{e}"
|
||||
elapsed = (time.perf_counter() - t0) * 1000.0
|
||||
with self._lock:
|
||||
self._latest_preview = preview
|
||||
self._latest_meta = f"{meta} {elapsed:.0f}ms"
|
||||
self._last_ms = elapsed
|
||||
self._busy = False
|
||||
|
||||
threading.Thread(target=_job, daemon=True).start()
|
||||
return True
|
||||
|
||||
|
||||
def _draw_status(frame, lines, color=None):
|
||||
if color is None:
|
||||
color = image.COLOR_YELLOW
|
||||
y = 4
|
||||
for line in lines:
|
||||
frame.draw_string(4, y, line, color=color)
|
||||
y += 16
|
||||
|
||||
|
||||
def _save_preview_jpeg(maix_img, out_dir):
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
fn = os.path.join(out_dir, f"preview_{int(time.time() * 1000)}.jpg")
|
||||
maix_img.save(fn)
|
||||
return fn
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="实时预览射箭算法存图效果")
|
||||
parser.add_argument(
|
||||
"--interval",
|
||||
type=float,
|
||||
default=2.0,
|
||||
help="两次完整 analyze_shot 的最小间隔(秒);--every-frame 时忽略",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--every-frame",
|
||||
action="store_true",
|
||||
help="每帧都触发算法(很慢,仅调试用)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--width",
|
||||
type=int,
|
||||
default=getattr(config, "CAMERA_WIDTH", 640),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--height",
|
||||
type=int,
|
||||
default=getattr(config, "CAMERA_HEIGHT", 480),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--save-dir",
|
||||
default=config.PHOTO_DIR,
|
||||
help="按板子按键无;用 --save-every N 每 N 次成功分析存一张",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--save-every",
|
||||
type=int,
|
||||
default=0,
|
||||
help="每成功分析 N 次自动存一张到 --save-dir(0=不自动存)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
_patch_preview_config()
|
||||
print("[INFO] 预览模式:已关闭 TRIANGLE_BLACK_YOLO_SAVE_ROI_CROP / TRIANGLE_SAVE_DEBUG_IMAGE")
|
||||
|
||||
laser_manager.load_laser_point()
|
||||
preload_triangle_calib()
|
||||
if getattr(config, "TRIANGLE_YOLO_PRELOAD_ON_BOOT", False) or getattr(
|
||||
config, "TRIANGLE_BLACK_YOLO_PRELOAD_ON_BOOT", False
|
||||
):
|
||||
print("[INFO] 预加载 YOLO …")
|
||||
preload_yolo_detector()
|
||||
|
||||
camera_manager.init_camera(args.width, args.height)
|
||||
camera_manager.init_display()
|
||||
worker = _AlgoWorker()
|
||||
|
||||
interval_s = 0.0 if args.every_frame else max(0.3, float(args.interval))
|
||||
last_trigger = 0.0
|
||||
ok_count = 0
|
||||
frame_idx = 0
|
||||
|
||||
print(
|
||||
f"[INFO] 摄像头 {args.width}x{args.height} "
|
||||
f"interval={'每帧' if args.every_frame else f'{interval_s}s'}"
|
||||
)
|
||||
print("[INFO] 退出:Ctrl+C")
|
||||
|
||||
try:
|
||||
while True:
|
||||
frame = camera_manager.read_frame()
|
||||
frame_idx += 1
|
||||
now = time.perf_counter()
|
||||
|
||||
due = args.every_frame or (now - last_trigger >= interval_s)
|
||||
if due and not worker.busy:
|
||||
last_trigger = now
|
||||
worker.run_async(_copy_maix_frame(frame))
|
||||
|
||||
preview, meta = worker.get_preview()
|
||||
if preview is not None:
|
||||
show_img = preview
|
||||
status = [f"#{frame_idx}", meta]
|
||||
if args.save_every > 0 and meta.startswith("ok"):
|
||||
ok_count += 1
|
||||
if ok_count % args.save_every == 0:
|
||||
try:
|
||||
fn = _save_preview_jpeg(preview, args.save_dir)
|
||||
status.append(f"saved:{fn}")
|
||||
except Exception as e:
|
||||
status.append(f"save_err:{e}")
|
||||
else:
|
||||
show_img = frame
|
||||
if worker.busy:
|
||||
status = [f"#{frame_idx}", "analyzing…"]
|
||||
else:
|
||||
status = [f"#{frame_idx}", "waiting…"]
|
||||
|
||||
_draw_status(show_img, status)
|
||||
camera_manager.show(show_img)
|
||||
maix_time.sleep_ms(1)
|
||||
except KeyboardInterrupt:
|
||||
print("[INFO] 已退出")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
18
test/test_yolo26.py
Normal file
18
test/test_yolo26.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from maix import camera, display, image, nn, app
|
||||
|
||||
# 1. 初始化模型 (请确保模型文件 .mud 路径正确)
|
||||
detector = nn.YOLOv5(model="/root/model_279350.mud", dual_buff=True)
|
||||
|
||||
# 2. 初始化摄像头,分辨率与模型输入匹配
|
||||
cam = camera.Camera(detector.input_width(), detector.input_height(), detector.input_format())
|
||||
disp = display.Display()
|
||||
|
||||
# 3. 主循环:实时检测与显示
|
||||
while not app.need_exit():
|
||||
img = cam.read() # 从摄像头读取一帧
|
||||
objs = detector.detect(img, conf_th=0.5, iou_th=0.45) # 执行YOLO11推理
|
||||
for obj in objs: # 绘制所有检测到的目标
|
||||
img.draw_rect(obj.x, obj.y, obj.w, obj.h, color=image.COLOR_RED)
|
||||
msg = f'{detector.labels[obj.class_id]}: {obj.score:.2f}'
|
||||
img.draw_string(obj.x, obj.y, msg, color=image.COLOR_RED)
|
||||
disp.show(img) # 更新屏幕显示
|
||||
209
test/test_yolo_camera_simple.py
Normal file
209
test/test_yolo_camera_simple.py
Normal file
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
摄像头实时 YOLOv5 简易测试脚本。
|
||||
|
||||
特点:
|
||||
- 完全独立脚本,直接 python test/test_yolo_camera_simple.py 运行,不需要传参。
|
||||
- 不 import config,不依赖项目模块。
|
||||
- 直接调用 maix.nn.YOLOv5(model=..., dual_buff=False)。
|
||||
- camera.read() 得到的 Maix image 直接送 det.detect()。
|
||||
- 在画面上画检测框、类别、置信度,并显示到屏幕。
|
||||
|
||||
运行环境:MaixCAM / MaixPy。
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
CAMERA_WIDTH = 640
|
||||
CAMERA_HEIGHT = 480
|
||||
# 默认与主项目 config.TRIANGLE_YOLO_MODEL_PATH 一致(勿用 /root/yolo26_int8.mud,那是占位路径)
|
||||
_MODEL_DEFAULT = "/maixapp/apps/t11/model_270139.mud"
|
||||
try:
|
||||
import config as _cfg
|
||||
|
||||
MODEL_PATH = getattr(_cfg, "TRIANGLE_YOLO_MODEL_PATH", _MODEL_DEFAULT) or _MODEL_DEFAULT
|
||||
except Exception:
|
||||
MODEL_PATH = _MODEL_DEFAULT
|
||||
CONF_TH = 0.7
|
||||
IOU_TH = 0.45
|
||||
# native: Maix detect 返回框已映射到 camera.read() 图像坐标;letterbox: 需要从网络输入坐标反算
|
||||
COORD_MODE = "native"
|
||||
# 只用于 DRAW_ONLY_CLASS_IDS=True 时过滤显示;默认画所有框
|
||||
CLASS_IDS = (0,)
|
||||
DRAW_ONLY_CLASS_IDS = False # True=只画 CLASS_IDS 里的类别;False=画所有 YOLO 返回框
|
||||
|
||||
|
||||
def _det_obj_class_id(o):
|
||||
for key in ("class_id", "cls", "label", "category", "cat_id", "id"):
|
||||
if hasattr(o, key):
|
||||
v = getattr(o, key)
|
||||
if v is None:
|
||||
continue
|
||||
try:
|
||||
return int(float(v))
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
return None
|
||||
|
||||
|
||||
def _det_obj_from_seq(t):
|
||||
if not isinstance(t, (list, tuple)) or len(t) < 6:
|
||||
return None
|
||||
|
||||
class Box:
|
||||
pass
|
||||
|
||||
b = Box()
|
||||
b.x = float(t[0])
|
||||
b.y = float(t[1])
|
||||
b.w = float(t[2])
|
||||
b.h = float(t[3])
|
||||
b.score = float(t[4])
|
||||
b.class_id = int(float(t[5]))
|
||||
return b
|
||||
|
||||
|
||||
def _normalize_objs(objs):
|
||||
out = []
|
||||
for o in objs or []:
|
||||
if isinstance(o, (list, tuple)):
|
||||
m = _det_obj_from_seq(o)
|
||||
if m is not None:
|
||||
out.append(m)
|
||||
else:
|
||||
out.append(o)
|
||||
return out
|
||||
|
||||
|
||||
def _letterbox_net_to_src_xyxy(x, y, w, h, src_w, src_h, net_w, net_h):
|
||||
scale = min(net_w / float(src_w), net_h / float(src_h))
|
||||
new_w = src_w * scale
|
||||
new_h = src_h * scale
|
||||
pad_x = (net_w - new_w) * 0.5
|
||||
pad_y = (net_h - new_h) * 0.5
|
||||
x0 = (x - pad_x) / scale
|
||||
y0 = (y - pad_y) / scale
|
||||
x1 = (x + w - pad_x) / scale
|
||||
y1 = (y + h - pad_y) / scale
|
||||
return x0, y0, x1, y1
|
||||
|
||||
|
||||
def _det_to_src_xyxy(o, coord_mode, src_w, src_h, net_w, net_h):
|
||||
x = float(getattr(o, "x", 0.0))
|
||||
y = float(getattr(o, "y", 0.0))
|
||||
w = float(getattr(o, "w", 0.0))
|
||||
h = float(getattr(o, "h", 0.0))
|
||||
if coord_mode in ("native", "source", "camera", "full"):
|
||||
return x, y, x + w, y + h
|
||||
return _letterbox_net_to_src_xyxy(x, y, w, h, src_w, src_h, net_w, net_h)
|
||||
|
||||
|
||||
def _clip_xywh(x0, y0, x1, y1, src_w, src_h):
|
||||
x0 = max(0, min(int(round(x0)), src_w - 1))
|
||||
y0 = max(0, min(int(round(y0)), src_h - 1))
|
||||
x1 = max(x0 + 1, min(int(round(x1)), src_w))
|
||||
y1 = max(y0 + 1, min(int(round(y1)), src_h))
|
||||
return x0, y0, x1 - x0, y1 - y0
|
||||
|
||||
|
||||
def _label(det, cid):
|
||||
labels = getattr(det, "labels", None)
|
||||
if labels is None:
|
||||
return str(cid)
|
||||
try:
|
||||
return str(labels[int(cid)])
|
||||
except Exception:
|
||||
return str(cid)
|
||||
|
||||
|
||||
def main():
|
||||
from maix import camera, display, nn, time, image
|
||||
|
||||
if not MODEL_PATH or not os.path.isfile(MODEL_PATH):
|
||||
print("[ERR] 模型文件不存在:", MODEL_PATH)
|
||||
return
|
||||
|
||||
print("[INFO] 初始化 YOLO 模型:", MODEL_PATH)
|
||||
det = nn.YOLOv26(model=MODEL_PATH, dual_buff=False)
|
||||
net_w = int(det.input_width())
|
||||
net_h = int(det.input_height())
|
||||
print(
|
||||
"[INFO] net_in=%dx%d conf=%.2f iou=%.2f coord=%s class_ids=%s"
|
||||
% (net_w, net_h, CONF_TH, IOU_TH, COORD_MODE, str(CLASS_IDS))
|
||||
)
|
||||
|
||||
print("[INFO] 初始化摄像头: %dx%d" % (CAMERA_WIDTH, CAMERA_HEIGHT))
|
||||
cam = camera.Camera(CAMERA_WIDTH, CAMERA_HEIGHT)
|
||||
disp = display.Display()
|
||||
|
||||
color_cycle = []
|
||||
for name in ("RED", "GREEN", "BLUE", "ORANGE", "YELLOW", "CYAN", "MAGENTA"):
|
||||
c = getattr(image, "COLOR_" + name, None)
|
||||
if c is not None:
|
||||
color_cycle.append(c)
|
||||
if not color_cycle:
|
||||
color_cycle = [getattr(image, "COLOR_RED", 0)]
|
||||
|
||||
frame_idx = 0
|
||||
last_log_ms = time.ticks_ms()
|
||||
fps_count = 0
|
||||
|
||||
while True:
|
||||
frame = cam.read()
|
||||
src_w = frame.width()
|
||||
src_h = frame.height()
|
||||
|
||||
t0 = time.ticks_ms()
|
||||
raw = det.detect(frame, conf_th=CONF_TH, iou_th=IOU_TH)
|
||||
detect_ms = time.ticks_ms() - t0
|
||||
objs = _normalize_objs(raw if raw is not None else [])
|
||||
|
||||
draw_count = 0
|
||||
for i, o in enumerate(objs):
|
||||
cid = _det_obj_class_id(o)
|
||||
if cid is None:
|
||||
cid = -1
|
||||
if DRAW_ONLY_CLASS_IDS and cid not in CLASS_IDS:
|
||||
continue
|
||||
try:
|
||||
score = float(getattr(o, "score", 0.0))
|
||||
except Exception:
|
||||
score = 0.0
|
||||
|
||||
x0, y0, x1, y1 = _det_to_src_xyxy(o, COORD_MODE, src_w, src_h, net_w, net_h)
|
||||
ix, iy, iw, ih = _clip_xywh(x0, y0, x1, y1, src_w, src_h)
|
||||
col = color_cycle[cid % len(color_cycle)] if cid >= 0 else color_cycle[0]
|
||||
frame.draw_rect(ix, iy, iw, ih, color=col)
|
||||
frame.draw_string(ix, max(0, iy - 16), "%s %.2f" % (_label(det, cid), score), color=col)
|
||||
draw_count += 1
|
||||
|
||||
frame.draw_string(4, 4, "YOLO boxes:%d draw:%d %dms" % (len(objs), draw_count, detect_ms), color=color_cycle[0])
|
||||
disp.show(frame)
|
||||
|
||||
frame_idx += 1
|
||||
fps_count += 1
|
||||
now = time.ticks_ms()
|
||||
if now - last_log_ms >= 1000:
|
||||
print(
|
||||
"[INFO] frame=%d fps=%d raw_boxes=%d draw_boxes=%d detect_ms=%d"
|
||||
% (frame_idx, fps_count, len(objs), draw_count, detect_ms)
|
||||
)
|
||||
fps_count = 0
|
||||
last_log_ms = now
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("[INFO] exit")
|
||||
except Exception as e:
|
||||
print("[ERR]", e)
|
||||
try:
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
except Exception:
|
||||
pass
|
||||
@@ -1,10 +1,9 @@
|
||||
from maix import image, nn, display
|
||||
|
||||
# 1. 加载模型
|
||||
detector = nn.YOLOv8(model="/root/models/yolov8n.mud", dual_buff=True)
|
||||
|
||||
detector = nn.YOLOv8(model="/root/279350.mud", dual_buff=False)
|
||||
# 2. 加载指定图片(根据模型输入尺寸自动缩放宽高)
|
||||
img = image.load("/root/test.jpg")
|
||||
img = image.load("/root/tes.jpg")
|
||||
if img is None:
|
||||
raise FileNotFoundError("图片加载失败,请检查路径")
|
||||
|
||||
Reference in New Issue
Block a user