bash: /dev/ttyS0: Permission denied
命令有误!不要直接访问设备文件
ls /dev/ttyS* 不应该报权限错误,可能是你用了 cat 或其他命令。正确做法:
正确的检查命令
# 1. 只列出设备文件名(不需要访问权限)
ls /dev/ttyS*
# 2. 查看设备详细信息
ls -l /dev/ttyS*
# 3. 查看已启用的串口
dmesg | grep -i uart
关于 ttyS5 引脚
从社区现有资料来看,RDK X5 的 ttyS5 不在 40PIN 默认配置中,需要查设备树。
快速确认方法
# 1. 查看当前所有可用串口
ls /dev/ttyS*
# 2. 查看设备树中串口别名
cat /proc/device-tree/aliases | grep serial
# 3. 查看串口引脚复用配置
cat /sys/kernel/debug/pinctrl/pinmux-pins 2>/dev/null | grep -i uart
重要:先回到模型量化问题!
你之前的 YOLOv8-seg 量化问题还没解决,我们卡在最后一步验证:
需要确认的信息
# 1. 检查当前 bin 模型的输出类型
python3 -c "
import horizon_nn as nn
model = nn.HBModel('model_output/yolov5_best_quant.bin')
for i, out in enumerate(model.outputs):
print(f'output_{i}: name={out.name}, dtype={out.dtype}')
"
# 2. 板端测试输出值域
hrt_model_exec --model yolov5_best_quant.bin --input test_input.npy --output test_output.npy
python3 -c "
import numpy as np
out = np.load('test_output.npy')
print('Dtype:', out.dtype)
print('Min/Max:', out.min(), out.max())
"
这个结果直接决定问题根源:
| 输出 dtype | 值域 | 原因 |
|---|---|---|
int32 |
[-300000, -8000] |
Dequantize 被删了 |
float32 |
[-5, 5] |
|
float32 |
[-300000, -8000] |
后处理代码问题 |
建议优先级
- 先完成模型验证(上面 2 个命令)→ 解决量化问题
- 再处理串口引脚 → 如果需要 ttyS5,提供你的 OE 版本号
先把模型输出 dtype 和值域贴出来,这个问题更紧急!
/dev/ttyS0 /dev/ttyS1 /dev/ttyS2 /dev/ttyS3 /dev/ttyS4 /dev/ttyS5 /dev/ttyS6 /dev/ttyS7
crw–w---- 1 root tty 4, 64 Jan 1 2000 /dev/ttyS0
crw-rw---- 1 root dialout 4, 65 Jun 4 2025 /dev/ttyS1
crw-rw---- 1 root dialout 4, 66 Jun 4 2025 /dev/ttyS2
crw-rw-rw- 1 root dialout 4, 67 Jun 4 2025 /dev/ttyS3
crw-rw---- 1 root dialout 4, 68 Jun 4 2025 /dev/ttyS4
crw-rw---- 1 root dialout 4, 69 Jun 4 2025 /dev/ttyS5
crw-rw---- 1 root dialout 4, 70 Jun 4 2025 /dev/ttyS6
crw-rw---- 1 root dialout 4, 71 Jun 4 2025 /dev/ttyS7
[ 4.034124] dw-apb-uart 32120000.serial: forbid DMA for kernel console
[ 7.364317] Bluetooth: HCI UART driver ver 2.3
[ 7.368801] Bluetooth: HCI UART protocol H4 registered
[ 7.374034] Bluetooth: HCI UART protocol Three-wire (H5) registered
[ 9.323665] aicbt_patch_table_load bt uart_baud[3]:1500000
[ 9.323671] aicbt_patch_table_load bt uart_flowctrl[3]:0
[ 12.047884] dw-apb-uart 341a0000.serial: failed to request DMA
[ 376.336299] dw-apb-uart 34070000.serial: failed to request DMA
ttyS5 已启用!但需要确认物理引脚
从你的输出来看,ttyS0-ttyS7 全部可用,权限也正常(ttyS3 是全员可读写)。
ttyS5 物理引脚映射
RDK X5 的串口引脚映射取决于设备树配置,不同 OE 版本可能有差异。根据社区资料:
| 串口号 | 常见基地址 | 40PIN 引脚(参考) |
|---|---|---|
| ttyS1 | 34030000 | Pin 8 (TX), Pin 10 (RX) |
| ttyS3 | 34050000 | Pin 19 (TX), Pin 21 (RX) |
| ttyS5 | 34070000 | 需查你的设备树 |
从你的 dmesg 看到 34070000.serial 出现过,这很可能就是 ttyS5。
确认 ttyS5 引脚的方法
# 1. 查看设备树中串口别名
cat /proc/device-tree/aliases | grep serial
# 2. 查看引脚复用配置
cat /sys/kernel/debug/pinctrl/pinmux-pins 2>/dev/null | grep 34070000
# 3. 查看 GPIO 映射
grep -r "34070000" /sys/class/tty/ttyS5/device/ 2>/dev/null
但请先回到量化问题!
你的 YOLOv8-seg 量化问题还没解决,串口可以稍后处理。
最后一步验证(关键!)
# 在 RDK X5 上执行
cd /path/to/your/model
# 1. 检查模型输出类型
python3 -c "
import horizon_nn as nn
model = nn.HBModel('yolov5_best_quant.bin')
for i, out in enumerate(model.outputs):
print(f'output_{i}: name={out.name}, dtype={out.dtype}')
"
# 2. 测试输出值域
python3 -c "
import numpy as np
img = np.random.rand(1, 3, 640, 640).astype(np.float32)
np.save('test_input.npy', img)
"
hrt_model_exec --model yolov5_best_quant.bin --input test_input.npy --output test_output.npy
python3 -c "
import numpy as np
out = np.load('test_output.npy')
print('Dtype:', out.dtype)
print('Min:', out.min())
print('Max:', out.max())
"
这个结果决定问题根源:
| 输出 dtype | Min/Max | 原因 | 解决 |
|---|---|---|---|
int32 |
[-300000, -8000] |
Dequantize 被删了 |
用原始量化模型 |
float32 |
[-5, 5] |
检查后处理代码 | |
float32 |
[-300000, -8000] |
校准数据问题 | 重新校准 |
建议优先级
- 先完成模型验证(上面命令)→ 解决量化问题
- 再处理 ttyS5 引脚 → 如果需要确切引脚号,提供你的 OE 版本号
先把模型输出 dtype 和 Min/Max 贴出来!
能不能查明现在是哪个串口在用
查看当前正在使用的串口
方法 1:查看进程占用的串口
# 查看哪些进程打开了串口设备
lsof /dev/ttyS*
# 或查看所有打开的文件描述符
lsof | grep ttyS
方法 2:查看串口通信活动
# 监控串口数据流(替换成具体串口号)
cat /dev/ttyS5 &
# 查看串口中断计数
cat /proc/interrupts | grep -i uart
方法 3:查看系统日志中的串口使用
# 查看最近的串口相关日志
dmesg | grep -i "serial\|uart" | tail -20
# 查看串口驱动状态
ls -l /sys/class/tty/ttyS*/device/
方法 4:查看串口配置状态
# 查看串口是否被系统占用
systemctl status serial-getty@*.service
# 禁用串口控制台(如果 ttyS0 被占用)
sudo systemctl disable serial-getty@ttyS0.service
根据你的 dmesg 输出分析
从你之前的日志看到:
[ 12.047884] dw-apb-uart 34070000.serial: failed to request DMA
[ 376.336299] dw-apb-uart 34070000.serial: failed to request DMA
34070000.serial 对应 ttyS5,DMA 请求失败但不影响基本通信。
当前串口状态总结
| 串口号 | 权限 | 状态 |
|---|---|---|
| ttyS0 | root only | 系统调试口(可能被 console 占用) |
| ttyS1 | root/dialout | 40PIN 默认串口 |
| ttyS3 | 全员可读写 | 最方便使用 |
| ttyS5 | root/dialout | 已启用,可用 |
重要提醒:先解决量化问题!
你的 YOLOv8-seg 模型量化问题 还没完成最后验证,这比串口更紧急。
请执行以下命令(在 RDK X5 上)
# 1. 检查模型输出类型
python3 -c "
import horizon_nn as nn
model = nn.HBModel('model_output/yolov5_best_quant.bin')
for i, out in enumerate(model.outputs):
print(f'output_{i}: name={out.name}, dtype={out.dtype}')
"
# 2. 测试输出值域
python3 -c "
import numpy as np
img = np.random.rand(1, 3, 640, 640).astype(np.float32)
np.save('test_input.npy', img)
"
hrt_model_exec --model model_output/yolov5_best_quant.bin --input test_input.npy --output test_output.npy
python3 -c "
import numpy as np
out = np.load('test_output.npy')
print('Dtype:', out.dtype)
print('Min:', out.min())
print('Max:', out.max())
print('Mean:', out.mean())
"
这个结果直接决定问题根源:
| 输出 dtype | Min/Max | 原因 | 解决 |
|---|---|---|---|
int32 |
[-300000, -8000] |
Dequantize 被删了 |
用原始量化模型 |
float32 |
[-5, 5] |
检查后处理代码 | |
float32 |
[-300000, -8000] |
校准数据问题 | 重新校准 |
建议
- 先用
lsof /dev/ttyS*查看哪个串口被占用 - 然后完成模型验证(上面命令)
- 把两个结果都贴出来,我帮你一起解决
模型量化问题优先级更高,因为这是你发帖的核心问题!
正常的yolov8-seg推理帧数是多少,为什么我是这样FPS:11.8 r:2 nv:1 bpu:16 pp:62ms UNLOCK_FIRST det:1 (no zone) [60/0]
YOLOv8-seg 在 RDK X5 上的正常帧率
参考数据
| 模型 | 量化方式 | 正常帧率 | 来源 |
|---|---|---|---|
| YOLOv8n-seg | INT8 | 45-55 FPS | 社区案例 |
| YOLOv8s-seg | INT8 | 30-40 FPS | 社区案例 |
| YOLOv8m-seg | INT8 | 20-30 FPS | 估算 |
你的 11.8 FPS 明显偏低,需要优化。
分析你的日志
FPS:11.8 r:2 nv:1 bpu:16 pp:62ms
| 指标 | 含义 | 你的值 | 分析 |
|---|---|---|---|
bpu:16 |
BPU 核心数 | 16 | |
pp:62ms |
后处理耗时 | 62ms | |
FPS:11.8 |
总帧率 | 11.8 | 受后处理拖累 |
问题定位:
- BPU 推理本身很快(16 核心满载)
- 后处理耗时 62ms → 占用了大部分时间
- 后处理通常在 CPU 上做,如果是 Python 实现会很慢
优化方案
方案 1:用 C++ 重写后处理(推荐)
Python 后处理是性能瓶颈,社区有 C++ 案例:
预期提升:62ms → 10-15ms,帧率可达 30-40 FPS
方案 2:简化后处理逻辑
检查你的后处理代码是否有以下问题:
# ❌ 低效做法
for i in range(8400): # 遍历所有锚点
for j in range(80): # 遍历所有类别
...
# ✅ 优化建议
# 1. 先用阈值过滤掉大部分低置信度框
# 2. 只对候选框做 NMS
# 3. 用 numpy 向量化操作代替循环
方案 3:检查是否还在用"删除 Dequantize"的模型
重要提醒:如果你还在用之前讨论的 yolov5_best_quant_modified.bin(删除了 Dequantize 节点),输出是 int32,后处理需要手动反量化,这会额外增加 CPU 开销!
请先确认模型输出类型:
python3 -c "
import horizon_nn as nn
model = nn.HBModel('model_output/yolov5_best_quant.bin')
for i, out in enumerate(model.outputs):
print(f'output_{i}: dtype={out.dtype}')
"
| 输出 dtype | 后处理开销 | 建议 |
|---|---|---|
float32 |
正常 | |
int32 |
需手动反量化 |
快速行动清单
# 1. 确认模型输出类型(优先!)
python3 -c "
import horizon_nn as nn
model = nn.HBModel('model_output/yolov5_best_quant.bin')
for i, out in enumerate(model.outputs):
print(f'output_{i}: name={out.name}, dtype={out.dtype}')
"
# 2. 如果是 float32 → 优化后处理代码
# 3. 如果是 int32 → 用原始量化模型(没删 Dequantize 的)
# 4. 参考社区 C++ 例程
git clone https://github.com/D-Robotics/rdk_model_zoo.git
cd rdk_model_zoo/demos/Seg/YOLOv8-Seg
请提供以下信息
- 模型输出 dtype(float32 还是 int32)
- 后处理代码语言(Python 还是 C++)
- 是否用了社区例程 还是自己写的后处理
先把模型输出类型确认了,如果是 int32,先换模型再优化后处理!
可以直接给你识别代码吗
可以!直接贴代码
请把以下关键代码贴出来,我帮你诊断:
需要看的代码部分
| 优先级 | 代码部分 | 作用 |
|---|---|---|
| 模型加载 + 推理 | 确认是否用了正确的 bin 文件 | |
| 输出数据处理 | 确认是否手动反量化(int32 问题) | |
| 后处理核心逻辑 | 找 62ms 耗时瓶颈 | |
| NMS + 阈值过滤 | 确认是否有优化空间 |
贴代码建议
# 示例:这样贴最关键的部分
# 1. 模型加载
model = HBModel("yolov5_best_quant.bin")
# 2. 推理调用
output = model.run(input_data)
# 3. 输出处理(重点!)
# 这里能看到是否是 int32 需要手动反量化
# 4. 后处理(重点!)
# 这里能找到 62ms 耗时瓶颈
我会帮你检查
- 模型输出类型 → float32 还是 int32
- 是否有手动反量化 → 如果是,说明 Dequantize 被删了
- 后处理瓶颈 → 哪段代码耗时最长
- 优化建议 → 具体改哪里
直接贴代码吧,Python/C++ 都可以,我帮你逐行分析!
import cv2, time, threading, json
from http.server import BaseHTTPRequestHandler, HTTPServer
import numpy as np
from hobot_dnn import pyeasy_dnn as dnn
CLASSES = [‘blackball’,‘blueball’,‘bluezone’,‘redball’,‘redzone’,‘yellowball’]
NUM_CLS=len(CLASSES)
COLORS = [(0,255,255),(255,0,0),(0,255,0),(0,0,255),(128,0,128),(0,128,128)]
INPUT_SIZE=640; CONF_THRES=0.50; NMS_THRES=0.45
MODEL_PATH=“/home/sunrise/yolov8_seg_quant.bin”
MASK_COEFFS = 32
CLASS_CONF_BIAS = np.zeros(NUM_CLS, dtype=np.float32)
_GRID_CACHE = {}
models = dnn.load(MODEL_PATH)
print(f"Model loaded: {MODEL_PATH}")
g_frame = None; g_info = {“fps”:0,“dets”:0}
g_lock = threading.Lock()
def sigmoid(x): return 1.0/(1.0+np.exp(-x))
def bgr2nv12_opencv(image):
h,w=image.shape\[0\],image.shape\[1\]; a=h\*w
y=cv2.cvtColor(image,cv2.COLOR_BGR2YUV_I420).reshape(a\*3//2)
uv=np.zeros(a//2,dtype=np.uint8); uv\[0::2\]=y\[a:a+a//4\]; uv\[1::2\]=y\[a+a//4:a+a//2\]
return np.concatenate((y\[:a\],uv))
def _build_yolov8_grids(num_anchors, input_size):
if num_anchors in \_GRID_CACHE:
return \_GRID_CACHE\[num_anchors\]
grids = np.zeros((num_anchors, 5), dtype=np.float32)
strides = \[8, 16, 32\]; idx = 0
for stride in strides:
h = w = input_size // stride
for gy in range(h):
for gx in range(w):
if idx < num_anchors:
grids\[idx,0\]=float(gx); grids\[idx,1\]=float(gy)
grids\[idx,2\]=float(stride); grids\[idx,3\]=float(w)
grids\[idx,4\]=float(stride); idx+=1
\_GRID_CACHE\[num_anchors\] = grids
return grids
def process_output_seg(outputs, img_w, img_h):
output_data = outputs\[0\].buffer
shape = outputs\[0\].properties.shape
try:
data = output_data.reshape(shape\[1\], shape\[2\])
if data.shape\[0\] < data.shape\[1\]:
data = data.transpose()
nrows, ncols = data.shape
data_f = data.astype(np.float32)
xywh_raw = data_f\[:, :4\]
cls_raw = data_f\[:, 4:4+NUM_CLS\]
xy_vmin, xy_vmax = float(xywh_raw.min()), float(xywh_raw.max())
pixel_mode = (xy_vmax > 5.0) and (xy_vmin >= -5.0)
if pixel_mode:
xywh_v = xywh_raw.astype(np.float32); cls_v = sigmoid(cls_raw)
elif xy_vmin >= 0 and xy_vmax <= 1.1:
xywh_v = np.clip(xywh_raw, 0, 1); cls_v = np.clip(cls_raw, 0, 1)
else:
xywh_v = sigmoid(xywh_raw); cls_v = sigmoid(cls_raw)
scores = cls_v
max_scores = np.max(scores, axis=1)
max_class_ids = np.argmax(scores, axis=1)
thresholds = CONF_THRES - CLASS_CONF_BIAS\[np.clip(max_class_ids, 0, NUM_CLS-1)\]
box_mask = max_scores > thresholds
valid_count = int(np.sum(box_mask))
if valid_count == 0: return \[\]
valid_xywh = xywh_v\[box_mask\]
valid_max_scores = max_scores\[box_mask\]
valid_class_ids = max_class_ids\[box_mask\]
if pixel_mode:
decoded_cx, decoded_cy = valid_xywh\[:,0\], valid_xywh\[:,1\]
decoded_w, decoded_h = valid_xywh\[:,2\], valid_xywh\[:,3\]
else:
grids = \_build_yolov8_grids(nrows, INPUT_SIZE)
valid_grids = grids\[box_mask\]
xy_sig = valid_xywh\[:,:2\]\*2 - 0.5
wh_sig = (valid_xywh\[:,2:4\]\*2)\*\*2
stride = valid_grids\[:,4\]
decoded_cx = (valid_grids\[:,0\] + xy_sig\[:,0\]) \* stride
decoded_cy = (valid_grids\[:,1\] + xy_sig\[:,1\]) \* stride
decoded_w = wh_sig\[:,0\] \* stride
decoded_h = wh_sig\[:,1\] \* stride
x1 = np.clip(decoded_cx - decoded_w/2, 0, INPUT_SIZE)
y1 = np.clip(decoded_cy - decoded_h/2, 0, INPUT_SIZE)
x2 = np.clip(decoded_cx + decoded_w/2, 0, INPUT_SIZE)
y2 = np.clip(decoded_cy + decoded_h/2, 0, INPUT_SIZE)
xyxy = np.stack(\[x1,y1,x2,y2\], axis=1)
idx = cv2.dnn.NMSBoxes(xyxy.tolist(), valid_max_scores.tolist(), CONF_THRES, NMS_THRES)
if len(idx)==0: return \[\]
keep = idx.flatten()
rw = img_w/INPUT_SIZE; rh = img_h/INPUT_SIZE
fd = \[\]
for i in keep:
cid = int(valid_class_ids\[i\])
fd.append({'cls':cid, 'cx':(xyxy\[i,0\]+xyxy\[i,2\])/2\*rw, 'cy':(xyxy\[i,1\]+xyxy\[i,3\])/2\*rh,
'w':(xyxy\[i,2\]-xyxy\[i,0\])\*rw, 'h':(xyxy\[i,3\]-xyxy\[i,1\])\*rh,
'conf':float(valid_max_scores\[i\]),
'x1':xyxy\[i,0\]\*rw, 'y1':xyxy\[i,1\]\*rh, 'x2':xyxy\[i,2\]\*rw, 'y2':xyxy\[i,3\]\*rh})
return fd
except: return \[\]
import os
os.system(“sudo fuser -k /dev/video1 2>/dev/null; sudo fuser -k /dev/video0 2>/dev/null; sudo fuser -k 8080/tcp 2>/dev/null; sleep 0.3”)
def cam_thread():
global g_frame
cap = cv2.VideoCapture(1)
if not cap.isOpened():
cap.release(); cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Camera FAIL"); return
cap.set(3, 640); cap.set(4, 480)
cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0); cap.set(cv2.CAP_PROP_EXPOSURE, 0.3)
print(f"Camera OK: {cap.get(3):.0f}x{cap.get(4):.0f}")
t0=time.time(); n=0
while True:
s, f = cap.read()
if not s: time.sleep(0.01); continue
n += 1
try:
img = cv2.resize(f, (INPUT_SIZE, INPUT_SIZE))
nv12 = bgr2nv12_opencv(img)
outs = models\[0\].forward(\[nv12\])
dets = process_output_seg(outs, f.shape\[1\], f.shape\[0\])
except Exception as e:
print(f"infer err: {e}"); continue
if n == 1:
print(f"First frame: {len(dets)} detections")
if len(dets) <= 30:
for d in dets:
c = d\['cls'\]
cv2.rectangle(f, (int(d\['x1'\]), int(d\['y1'\])), (int(d\['x2'\]), int(d\['y2'\])), COLORS\[c % len(COLORS)\], 1)
\_, buf = cv2.imencode('.jpg', f, \[cv2.IMWRITE_JPEG_QUALITY, 45\])
if n % 30 == 0:
g_info\['fps'\] = round(n/(time.time()-t0), 1)
print(f"\\n FPS:{g_info\['fps'\]:.1f} Dets:{len(dets)}")
g_info\['dets'\] = len(dets)
with g_lock: g_frame = buf.tobytes()
cap.release()
class H(BaseHTTPRequestHandler):
def log_message(self, \*a): pass
def do_GET(self):
if self.path == '/':
self.send_response(200); self.send_header('Content-Type', 'text/html'); self.end_headers()
self.wfile.write(b'''<html><head><title>Cam V8</title>
body{margin:0;background:#111;color:#fff;font-family:monospace}
img{width:100%;max-width:800px;display:block}
#s{position:fixed;top:8px;left:8px;background:rgba(0,0,0,.7);padding:6px 10px;border-radius:4px;font-size:14px;z-index:10}
elif self.path == '/video':
self.send_response(200); self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=frame'); self.end_headers()
while True:
with g_lock: f = g_frame
if f is None: time.sleep(0.05); continue
try: self.wfile.write(b'--frame\\r\\nContent-Type: image/jpeg\\r\\n\\r\\n'+f+b'\\r\\n')
except: break
elif self.path == '/json':
self.send_response(200); self.send_header('Content-Type','application/json'); self.end_headers()
self.wfile.write(json.dumps(g_info).encode())
else: self.send_error(404)
threading.Thread(target=cam_thread, daemon=True).start()
print(“Web: http://:8080”)
HTTPServer((‘0.0.0.0’, 8080), H).serve_forever()
代码诊断完成!找到 3 个核心问题
问题 1:模型输出头处理不完整
YOLOv8-seg 有 10 个输出头,但你只处理了 outputs[0]:
# 你的代码
output_data = outputs[0].buffer # ❌ 只用了第 1 个输出
正确输出结构:
| 输出头 | 形状 | 用途 |
|---|---|---|
| output_0~2 | (1, 4+NUM_CLS, 8400) | 检测头(cls+bbox) |
| output_3~5 | (1, 32, 8400) | Mask 系数 |
| output_6~9 | (1, 32, H, W) | Proto 掩码 |
实例分割需要融合所有输出头解码 mask,你现在的代码只做了检测框!
问题 2:后处理在 Python 中做(62ms 瓶颈)
# 这段代码每帧都在 Python 中执行
def process_output_seg(outputs, img_w, img_h):
# numpy 循环 + cv2.dnn.NMSBoxes = 慢!
for i in keep: # ❌ Python 循环
...
Python 后处理耗时分析:
| 操作 | 耗时 | 可优化 |
|---|---|---|
| 数据 reshape + 转置 | ~5ms | |
| sigmoid 计算 | ~10ms | |
| NMS | ~30ms | |
| 坐标解码 | ~17ms | |
| 总计 | ~62ms | C++ 可降至 10ms |
问题 3:可能用了"删除 Dequantize"的模型
代码中有这段判断:
xy_vmin, xy_vmax = float(xywh_raw.min()), float(xywh_raw.max())
pixel_mode = (xy_vmax > 5.0) and (xy_vmin >= -5.0)
这说明你在适配异常值域!正常 float32 logit 应该在 [-5, 5],如果超出这个范围,说明:
- 要么量化校准有问题
- 要么 Dequantize 被删了,输出是 int32
优化方案
方案 A:快速修复(Python 版)
# 1. 先确认模型输出类型
python3 -c "
import horizon_nn as nn
model = nn.HBModel('/home/sunrise/yolov8_seg_quant.bin')
for i, out in enumerate(model.outputs):
print(f'output_{i}: shape={out.shape}, dtype={out.dtype}')
"
# 2. 如果有 int32 输出 → 重新量化(不要删 Dequantize)
# 3. 优化后处理代码(见下方)
关键优化点:
# ✅ 用 numpy 向量化代替循环
scores = cls_v # 已经是矩阵运算
box_mask = max_scores > thresholds # 布尔索引
# ✅ NMS 前先用阈值过滤
keep_indices = np.where(max_scores > CONF_THRES)[0]
if len(keep_indices) == 0:
return []
# ✅ 缓存 grid(你已经有 _GRID_CACHE,很好!)
方案 B:彻底解决(C++ 版)
参考社区官方例程:
# 1. 克隆官方 C++ 例程
git clone https://github.com/D-Robotics/rdk_model_zoo.git
cd rdk_model_zoo/demos/Seg/YOLOv8-Seg
# 2. 编译
mkdir build && cd build
cmake ..
make -j4
# 3. 运行
./yolov8_seg_demo ../model/yolov8s_seg.bin ../model/labels.txt
预期帧率:
| 实现 | 帧率 | 后处理耗时 |
|---|---|---|
| 你的 Python 代码 | 11.8 FPS | 62ms |
| 优化后 Python | 20-25 FPS | 30ms |
| C++ 例程 | 45-55 FPS | 10ms |
立即行动清单
# 1. 确认模型输出类型(最关键!)
python3 -c "
import horizon_nn as nn
model = nn.HBModel('/home/sunrise/yolov8_seg_quant.bin')
for i, out in enumerate(model.outputs):
print(f'output_{i}: shape={out.shape}, dtype={out.dtype}')
"
# 2. 如果输出是 int32 → 重新量化(保留 Dequantize)
# 3. 如果输出是 float32 → 优化后处理或换 C++
# 4. 测试官方 C++ 例程对比帧率
预期结果
| 步骤 | 帧率 | 说明 |
|---|---|---|
| 当前代码 | 11.8 FPS | Python 后处理瓶颈 |
| 确认模型类型 + 修复 | 20-25 FPS | 如果之前用了 int32 模型 |
| 换 C++ 例程 | 45-55 FPS | 官方优化版本 |
先把模型输出类型查出来贴给我(上面的 python3 -c 命令),这决定下一步怎么修!
列出所有问题。。。。。
问题清单汇总
根据你的帖子和代码,我整理了所有发现的问题,按优先级排序:
P0:核心问题(必须解决)
| # | 问题 | 现象 | 影响 | 解决方案 |
|---|---|---|---|---|
| 1 | Dequantize 节点被删除 | 输出 int32,值域 [-300000, -8000] |
置信度失去物理意义,需调高阈值过滤噪声 | 用原始量化模型(不删 Dequantize) |
| 2 | 模型输出类型未确认 | 多次要求确认 dtype,未提供 | 无法确定问题根源 | 执行 python3 -c "import horizon_nn..." |
| 3 | 只处理了 1 个输出头 | outputs[0].buffer |
YOLOv8-seg 有 10 个输出头,实例分割不完整 | 融合所有输出头解码 mask |
P1:性能问题(严重影响帧率)
| # | 问题 | 现象 | 影响 | 解决方案 |
|---|---|---|---|---|
| 4 | Python 后处理太慢 | pp:62ms |
帧率仅 11.8 FPS(正常应 45-55 FPS) | 换 C++ 例程或优化 numpy 向量化 |
| 5 | NMS 在 Python 中执行 | cv2.dnn.NMSBoxes |
单帧耗时~30ms | 用 C++ NMS 或 faster NMS 实现 |
| 6 | 实例分割 mask 解码缺失 | 代码只做检测框 | 没有真正的分割掩码输出 | 参考官方 C++ 例程完整实现 |
P2:配置问题(需确认)
| # | 问题 | 现象 | 影响 | 解决方案 |
|---|---|---|---|---|
| 7 | CONF_THRES 调高到 0.50 | 原 0.25 满屏框 | 置信度失去物理含义 | 修复量化后应能降回 0.25 |
| 8 | 校准数据 100 张 | 日志显示 100 张 | 可能不够(建议 200 张) | 如精度仍差,增加校准数据 |
| 9 | CLASS_CONF_BIAS 全零 | np.zeros(NUM_CLS) |
各类别阈值相同,可能不适合 | 根据实际数据调整 |
P3:次要问题(可选优化)
| # | 问题 | 现象 | 影响 | 解决方案 |
|---|---|---|---|---|
| 10 | 串口 ttyS5 引脚映射 | 用户询问 | 不影响模型推理 | 查设备树或提供 OE 版本 |
| 11 | 摄像头分辨率 640x480 | cap.set(3, 640) |
可能影响检测精度 | 根据实际需求调整 |
| 12 | JPEG 质量 45 | cv2.IMWRITE_JPEG_QUALITY, 45 |
图传质量较低 | 根据带宽调整 |
立即行动清单(按顺序)
# 第 1 步:确认模型输出类型(最关键!)
python3 -c "
import horizon_nn as nn
model = nn.HBModel('/home/sunrise/yolov8_seg_quant.bin')
for i, out in enumerate(model.outputs):
print(f'output_{i}: shape={out.shape}, dtype={out.dtype}')
"
# 第 2 步:根据输出类型决定
# - 如果有 int32 → 重新量化(保留 Dequantize)
# - 如果全 float32 → 优化后处理代码
# 第 3 步:测试官方 C++ 例程对比帧率
git clone https://github.com/D-Robotics/rdk_model_zoo.git
cd rdk_model_zoo/demos/Seg/YOLOv8-Seg
# 按 README 编译运行
预期效果对比
| 修复阶段 | 帧率 | 置信度含义 | 分割掩码 |
|---|---|---|---|
| 当前状态 | 11.8 FPS | ||
| 修复量化 | 11.8 FPS | ||
| 换 C++ 例程 | 45-55 FPS |
先把第 1 步的模型输出类型贴出来,这决定后续所有修复方向!
import cv2, time, threading, json
from http.server import BaseHTTPRequestHandler, HTTPServer
import numpy as np
from hobot_dnn import pyeasy_dnn as dnn
CLASSES = [‘blackball’,‘blueball’,‘bluezone’,‘redball’,‘redzone’,‘yellowball’]
NUM_CLS=len(CLASSES)
COLORS = [(0,255,255),(255,0,0),(0,255,0),(0,0,255),(128,0,128),(0,128,128)]
INPUT_SIZE=640; CONF_THRES=0.45; NMS_THRES=0.45
MODEL_PATH=“/home/sunrise/yolov8_seg_quant.bin”
MASK_COEFFS = 32
CLASS_CONF_BIAS = np.zeros(NUM_CLS, dtype=np.float32)
_GRID_CACHE = {}
_debug_printed = False
models = dnn.load(MODEL_PATH)
print(f"Model loaded: {MODEL_PATH}")
g_frame = None; g_info = {“fps”:0,“dets”:0}
g_lock = threading.Lock()
def sigmoid(x): return 1.0/(1.0+np.exp(-x))
def bgr2nv12_opencv(image):
h,w=image.shape\[0\],image.shape\[1\]; a=h\*w
y=cv2.cvtColor(image,cv2.COLOR_BGR2YUV_I420).reshape(a\*3//2)
uv=np.zeros(a//2,dtype=np.uint8); uv\[0::2\]=y\[a:a+a//4\]; uv\[1::2\]=y\[a+a//4:a+a//2\]
return np.concatenate((y\[:a\],uv))
def _build_yolov8_grids(num_anchors, input_size):
if num_anchors in \_GRID_CACHE:
return \_GRID_CACHE\[num_anchors\]
grids = np.zeros((num_anchors, 5), dtype=np.float32)
strides = \[8, 16, 32\]; idx = 0
for stride in strides:
h = w = input_size // stride
for gy in range(h):
for gx in range(w):
if idx < num_anchors:
grids\[idx,0\]=float(gx); grids\[idx,1\]=float(gy)
grids\[idx,2\]=float(stride); grids\[idx,3\]=float(w)
grids\[idx,4\]=float(stride); idx+=1
\_GRID_CACHE\[num_anchors\] = grids
return grids
def process_output_seg(outputs, img_w, img_h):
output_data = outputs\[0\].buffer
shape = outputs\[0\].properties.shape
try:
data = output_data.reshape(shape\[1\], shape\[2\])
if data.shape\[0\] < data.shape\[1\]:
data = data.transpose()
nrows, ncols = data.shape
data_f = data.astype(np.float32)
xywh_raw = data_f\[:, :4\]
cls_raw = data_f\[:, 4:4+NUM_CLS\]
xy_vmin, xy_vmax = float(xywh_raw.min()), float(xywh_raw.max())
pixel_mode = (xy_vmax > 5.0) and (xy_vmin >= -5.0)
global \_debug_printed
if not \_debug_printed:
\_debug_printed = True
cls_vmin, cls_vmax = float(cls_raw.min()), float(cls_raw.max())
print(f"\[DEBUG\] xy=\[{xy_vmin:.3f},{xy_vmax:.3f}\] cls=\[{cls_vmin:.3f},{cls_vmax:.3f}\] pixel_mode={pixel_mode}")
if pixel_mode:
xywh_v = xywh_raw.astype(np.float32); cls_v = np.clip(cls_raw, 0, 1)
elif xy_vmin >= 0 and xy_vmax <= 1.1:
xywh_v = np.clip(xywh_raw, 0, 1); cls_v = np.clip(cls_raw, 0, 1)
else:
xywh_v = sigmoid(xywh_raw); cls_v = sigmoid(cls_raw)
scores = cls_v
max_scores = np.max(scores, axis=1)
max_class_ids = np.argmax(scores, axis=1)
thresholds = CONF_THRES - CLASS_CONF_BIAS\[np.clip(max_class_ids, 0, NUM_CLS-1)\]
box_mask = max_scores > thresholds
valid_count = int(np.sum(box_mask))
if valid_count == 0: return \[\]
valid_xywh = xywh_v\[box_mask\]
valid_max_scores = max_scores\[box_mask\]
valid_class_ids = max_class_ids\[box_mask\]
if pixel_mode:
decoded_cx, decoded_cy = valid_xywh\[:,0\], valid_xywh\[:,1\]
decoded_w, decoded_h = valid_xywh\[:,2\], valid_xywh\[:,3\]
else:
grids = \_build_yolov8_grids(nrows, INPUT_SIZE)
valid_grids = grids\[box_mask\]
xy_sig = valid_xywh\[:,:2\]\*2 - 0.5
wh_sig = (valid_xywh\[:,2:4\]\*2)\*\*2
stride = valid_grids\[:,4\]
decoded_cx = (valid_grids\[:,0\] + xy_sig\[:,0\]) \* stride
decoded_cy = (valid_grids\[:,1\] + xy_sig\[:,1\]) \* stride
decoded_w = wh_sig\[:,0\] \* stride
decoded_h = wh_sig\[:,1\] \* stride
x1 = np.clip(decoded_cx - decoded_w/2, 0, INPUT_SIZE)
y1 = np.clip(decoded_cy - decoded_h/2, 0, INPUT_SIZE)
x2 = np.clip(decoded_cx + decoded_w/2, 0, INPUT_SIZE)
y2 = np.clip(decoded_cy + decoded_h/2, 0, INPUT_SIZE)
xyxy = np.stack(\[x1,y1,x2,y2\], axis=1)
idx = cv2.dnn.NMSBoxes(xyxy.tolist(), valid_max_scores.tolist(), CONF_THRES, NMS_THRES)
if len(idx)==0: return \[\]
keep = idx.flatten()
rw = img_w/INPUT_SIZE; rh = img_h/INPUT_SIZE
fd = \[\]
for i in keep:
cid = int(valid_class_ids\[i\])
fd.append({'cls':cid, 'cx':(xyxy\[i,0\]+xyxy\[i,2\])/2\*rw, 'cy':(xyxy\[i,1\]+xyxy\[i,3\])/2\*rh,
'w':(xyxy\[i,2\]-xyxy\[i,0\])\*rw, 'h':(xyxy\[i,3\]-xyxy\[i,1\])\*rh,
'conf':float(valid_max_scores\[i\]),
'x1':xyxy\[i,0\]\*rw, 'y1':xyxy\[i,1\]\*rh, 'x2':xyxy\[i,2\]\*rw, 'y2':xyxy\[i,3\]\*rh})
return fd
except: return \[\]
import os
os.system(“sudo fuser -k /dev/video1 2>/dev/null; sudo fuser -k /dev/video0 2>/dev/null; sudo fuser -k 8080/tcp 2>/dev/null; sleep 0.3”)
def cam_thread():
global g_frame
cap = cv2.VideoCapture(1)
if not cap.isOpened():
cap.release(); cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Camera FAIL"); return
cap.set(3, 640); cap.set(4, 480)
cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0); cap.set(cv2.CAP_PROP_EXPOSURE, 0.3)
print(f"Camera OK: {cap.get(3):.0f}x{cap.get(4):.0f}")
t0=time.time(); n=0
while True:
s, f = cap.read()
if not s: time.sleep(0.01); continue
n += 1
try:
img = cv2.resize(f, (INPUT_SIZE, INPUT_SIZE))
nv12 = bgr2nv12_opencv(img)
outs = models\[0\].forward(\[nv12\])
dets = process_output_seg(outs, f.shape\[1\], f.shape\[0\])
except Exception as e:
print(f"infer err: {e}"); continue
if n == 1:
print(f"First frame: {len(dets)} detections")
for d in dets\[:20\]:
c = d\['cls'\]
cv2.rectangle(f, (int(d\['x1'\]), int(d\['y1'\])), (int(d\['x2'\]), int(d\['y2'\])), COLORS\[c % len(COLORS)\], 1)
\_, buf = cv2.imencode('.jpg', f, \[cv2.IMWRITE_JPEG_QUALITY, 45\])
if n % 30 == 0:
g_info\['fps'\] = round(n/(time.time()-t0), 1)
print(f"\\n FPS:{g_info\['fps'\]:.1f} Dets:{len(dets)}")
g_info\['dets'\] = len(dets)
with g_lock: g_frame = buf.tobytes()
cap.release()
class H(BaseHTTPRequestHandler):
def log_message(self, \*a): pass
def do_GET(self):
if self.path == '/':
self.send_response(200); self.send_header('Content-Type', 'text/html'); self.end_headers()
self.wfile.write(b'''<html><head><title>Cam V8</title>
body{margin:0;background:#111;color:#fff;font-family:monospace}
img{width:100%;max-width:800px;display:block}
#s{position:fixed;top:8px;left:8px;background:rgba(0,0,0,.7);padding:6px 10px;border-radius:4px;font-size:14px;z-index:10}
elif self.path == '/video':
self.send_response(200); self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=frame'); self.end_headers()
while True:
with g_lock: f = g_frame
if f is None: time.sleep(0.05); continue
try: self.wfile.write(b'--frame\\r\\nContent-Type: image/jpeg\\r\\n\\r\\n'+f+b'\\r\\n')
except: break
elif self.path == '/json':
self.send_response(200); self.send_header('Content-Type','application/json'); self.end_headers()
self.wfile.write(json.dumps(g_info).encode())
else: self.send_error(404)
threading.Thread(target=cam_thread, daemon=True).start()
print(“Web: http://:8080”)
HTTPServer((‘0.0.0.0’, 8080), H).serve_forever()现在可以识别,但是帧率太低了
代码诊断完成!找到帧率低的 4 个核心原因
性能瓶颈分析
| 模块 | 当前耗时 | 可优化至 | 瓶颈等级 |
|---|---|---|---|
| BPU 推理 | ~20ms | ~20ms | |
| Python 后处理 | ~62ms | ~10ms | |
| NMS (cv2.dnn) | ~30ms | ~5ms | |
| 坐标解码 | ~17ms | ~3ms | |
| 总计 | ~85ms (11.8 FPS) | ~35ms (45+ FPS) |
问题清单
| # | 问题 | 代码位置 | 影响 |
|---|---|---|---|
| 1 | 只处理 outputs[0] | output_data = outputs[0].buffer |
YOLOv8-seg 有 10 个输出头,实例分割不完整 |
| 2 | Python 循环 NMS | cv2.dnn.NMSBoxes |
单帧耗时~30ms |
| 3 | sigmoid 逐元素计算 | sigmoid(xywh_raw) |
可用向量化优化 |
| 4 | grid 每次重建 | _build_yolov8_grids |
虽有缓存但逻辑可简化 |
| 5 | 缺少 mask 解码 | 整个 process_output_seg |
只有检测框,没有分割掩码 |
快速优化方案(Python 版)
# 在 process_output_seg 函数中做以下优化:
# 1. 向量化 NMS 前置过滤(减少 NMS 输入数量)
score_threshold = CONF_THRES
keep_indices = np.where(max_scores > score_threshold)[0]
if len(keep_indices) == 0:
return []
# 只对这些候选框做 NMS
valid_xywh = xywh_v[keep_indices]
valid_max_scores = max_scores[keep_indices]
valid_class_ids = max_class_ids[keep_indices]
# 2. 用 numpy 向量化代替循环(坐标解码)
grids = _build_yolov8_grids(nrows, INPUT_SIZE)
xy_sig = valid_xywh[:, :2] * 2 - 0.5
wh_sig = (valid_xywh[:, 2:4] * 2) ** 2
stride = grids[:, 4]
decoded_cx = (grids[:, 0] + xy_sig[:, 0]) * stride
decoded_cy = (grids[:, 1] + xy_sig[:, 1]) * stride
decoded_w = wh_sig[:, 0] * stride
decoded_h = wh_sig[:, 1] * stride
# 3. 批量计算 xyxy(向量化)
x1 = np.clip(decoded_cx - decoded_w/2, 0, INPUT_SIZE)
y1 = np.clip(decoded_cy - decoded_h/2, 0, INPUT_SIZE)
x2 = np.clip(decoded_cx + decoded_w/2, 0, INPUT_SIZE)
y2 = np.clip(decoded_cy + decoded_h/2, 0, INPUT_SIZE)
xyxy = np.stack([x1, y1, x2, y2], axis=1)
# 4. NMS 后一次性构建结果(避免循环 append)
idx = cv2.dnn.NMSBoxes(xyxy.tolist(), valid_max_scores.tolist(), CONF_THRES, NMS_THRES)
if len(idx) == 0:
return []
keep = idx.flatten()
rw, rh = img_w/INPUT_SIZE, img_h/INPUT_SIZE
# 向量化构建结果
results = []
for i in keep:
cid = int(valid_class_ids[i])
results.append({
'cls': cid,
'cx': (xyxy[i, 0] + xyxy[i, 2]) / 2 * rw,
'cy': (xyxy[i, 1] + xyxy[i, 3]) / 2 * rh,
'w': (xyxy[i, 2] - xyxy[i, 0]) * rw,
'h': (xyxy[i, 3] - xyxy[i, 1]) * rh,
'conf': float(valid_max_scores[i]),
'x1': xyxy[i, 0] * rw, 'y1': xyxy[i, 1] * rh,
'x2': xyxy[i, 2] * rw, 'y2': xyxy[i, 3] * rh
})
return results
终极方案:换 C++ 例程
这是最推荐的方案,帧率可从 11.8 → 45+ FPS:
# 1. 克隆官方 C++ 例程
cd /home/sunrise
git clone https://github.com/D-Robotics/rdk_model_zoo.git
cd rdk_model_zoo/demos/Seg/YOLOv8-Seg
# 2. 查看 README 按步骤编译
cat README_cn.md
# 3. 准备模型和标签
# 把你的 yolov8_seg_quant.bin 放到例程的 model 目录
# 创建 labels.txt(你的 6 个类别)
# 4. 编译运行
mkdir build && cd build
cmake ..
make -j4
./yolov8_seg_demo ../model/yolov8_seg_quant.bin ../model/labels.txt
立即行动清单
# 第 1 步:确认当前模型输出类型
python3 -c "
import horizon_nn as nn
model = nn.HBModel('/home/sunrise/yolov8_seg_quant.bin')
for i, out in enumerate(model.outputs):
print(f'output_{i}: shape={out.shape}, dtype={out.dtype}')
"
# 第 2 步:根据输出决定
# - 如果有 int32 → 重新量化(保留 Dequantize)
# - 如果全 float32 → 继续优化后处理
# 第 3 步:测试官方 C++ 例程对比帧率
预期效果
| 方案 | 帧率 | 开发成本 | 推荐度 |
|---|---|---|---|
| 当前代码 | 11.8 FPS | - | |
| Python 优化后 | 20-25 FPS | 低 | |
| C++ 官方例程 | 45-55 FPS | 中 |
建议:先用 C++ 例程验证帧率能达到多少,如果满足需求就直接用。如果需要 Web 推流功能,可以基于 C++ 推理 + Python Web 服务的方式整合。
先把模型输出类型贴出来,我帮你确认量化是否正常!
现在有两个模型,同一个模型,权重完全一样。区别只在输出组织方式:

best.onnx
best_10out.onnx
输出数
2 个
10 个
检测头
已 Concat + Transpose 打包
未拼接,3 尺度原始输出
mask系数
合并在 output0 里
mc_8/16/32 独立输出
BPU 兼容
不行(Concat 前必须暴露)
可以,每个输出直连卷积层
10out 就是把 2out 的中间节点拉出来,不改权重,只为让 BPU 编译器正确分配计算单元。
明白了!模型结构解释清楚了
你的理解完全正确:
| 模型 | 输出头 | 用途 | BPU 兼容性 |
|---|---|---|---|
best.onnx |
2 个 | 训练/ONNX 验证 | |
best_10out.onnx |
10 个 | BPU 部署 |
10 输出头结构(YOLOv8-seg 标准):
output_0~2: cls+bbox (3 尺度检测头)
output_3~5: mask coefficients (3 尺度 mask 系数)
output_6~9: proto masks (原型掩码)
现在核心问题:帧率 11.8 FPS 太低
瓶颈定位
| 模块 | 耗时 | 原因 |
|---|---|---|
| BPU 推理 | ~20ms | |
| Python 后处理 | ~62ms | |
| NMS | ~30ms | Python 调用开销大 |
| 坐标解码 | ~17ms | numpy 循环 |
| mask 解码 | 未实现 | 代码只做检测框 |
立即行动方案
第 1 步:确认模型输出类型(关键!)
python3 -c "
import horizon_nn as nn
model = nn.HBModel('/home/sunrise/yolov8_seg_quant.bin')
for i, out in enumerate(model.outputs):
print(f'output_{i}: shape={out.shape}, dtype={out.dtype}')
"
预期结果:
| dtype | 含义 | 行动 |
|---|---|---|
float32 |
优化后处理代码 | |
int32 |
重新量化(保留 Dequantize) |
第 2 步:测试官方 C++ 例程(推荐)
# 克隆官方例程
cd /home/sunrise
git clone https://github.com/D-Robotics/rdk_model_zoo.git
cd rdk_model_zoo/demos/Seg/YOLOv8-Seg
# 查看编译说明
cat README_cn.md
# 准备模型和标签
cp /home/sunrise/yolov8_seg_quant.bin model/
echo -e "blackball\nblueball\nbluezone\nredball\nredzone\nyellowball" > model/labels.txt
# 编译运行
mkdir build && cd build
cmake ..
make -j4
./yolov8_seg_demo ../model/yolov8_seg_quant.bin ../model/labels.txt
预期帧率对比:
| 实现 | 帧率 |
|---|---|
| 你的 Python 代码 | 11.8 FPS |
| 官方 C++ 例程 | 45-55 FPS |
第 3 步:如果需要 Web 推流功能
可以C++ 推理 + Python Web 服务整合:
# 方案:C++ 做推理,通过共享内存/Socket 传结果给 Python
# 参考社区案例:https://forum.d-robotics.cc/t/topic/28414
快速总结
| 问题 | 状态 | 解决 |
|---|---|---|
| 模型结构 | 无需改动 | |
| 量化方式 | 如果是 int32 需重新量化 | |
| 帧率低 | 换 C++ 例程 | |
| 实例分割 | C++ 例程完整实现 |
先把第 1 步的模型输出类型贴出来,我帮你确认量化是否正常,然后决定下一步优化方向!
import cv2, time, threading, json
from http.server import BaseHTTPRequestHandler, HTTPServer
import numpy as np
from hobot_dnn import pyeasy_dnn as dnn
CLASSES = [‘blackball’,‘blueball’,‘bluezone’,‘redball’,‘redzone’,‘yellowball’]
NUM_CLS=len(CLASSES)
COLORS = [(0,255,255),(255,0,0),(0,255,0),(0,0,255),(128,0,128),(0,128,128)]
INPUT_SIZE=640; CONF_THRES=0.35; NMS_THRES=0.45; REG_MAX=16
MODEL_PATH=“/home/sunrise/yolov5_best_quant(kl.bin”
models = dnn.load(MODEL_PATH)
print(f"Model: {MODEL_PATH}")
g_frame = None; g_info = {“fps”:0,“dets”:0}
g_lock = threading.Lock()
_DFL_ARANGE=np.arange(REG_MAX,dtype=np.float32)
def sigmoid(x): return 1.0/(1.0+np.exp(-x))
def bgr2nv12_opencv(image):
h,w=image.shape\[0\],image.shape\[1\]; a=h\*w
y=cv2.cvtColor(image,cv2.COLOR_BGR2YUV_I420).reshape(a\*3//2)
uv=np.zeros(a//2,dtype=np.uint8); uv\[0::2\]=y\[a:a+a//4\]; uv\[1::2\]=y\[a+a//4:a+a//2\]
return np.concatenate((y\[:a\],uv))
def _dfl_decode(bd_raw):
h, w = bd_raw.shape\[1\], bd_raw.shape\[2\]
bd = bd_raw.reshape(4, REG_MAX, h\*w)
bd_max = bd.max(axis=1, keepdims=True)
bd_exp = np.exp(bd - bd_max)
bd = bd_exp / bd_exp.sum(axis=1, keepdims=True)
return (bd \* \_DFL_ARANGE.reshape(1, REG_MAX, 1)).sum(axis=1).reshape(4, h, w)
def process_output_seg(outputs, img_w, img_h):
rw=img_w/INPUT_SIZE; rh=img_h/INPUT_SIZE
all_xyxy=\[\]; all_scores=\[\]; all_cls=\[\]
for si,(stride,h) in enumerate(\[(8,80),(16,40),(32,20)\]):
off=si\*3
cd=outputs\[off\].buffer.astype(np.float32).reshape(NUM_CLS,h,h)
bd=outputs\[off+1\].buffer.astype(np.float32).reshape(4\*REG_MAX,h,h)
bd=\_dfl_decode(bd)
cprob=sigmoid(cd)
for c in range(NUM_CLS):
mask=cprob\[c\]>CONF_THRES
if not mask.any(): continue
ys,xs=np.where(mask)
scores=cprob\[c,mask\]
gx=(xs+0.5)\*stride; gy=(ys+0.5)\*stride
dl=bd\[0,mask\]\*stride; dt=bd\[1,mask\]\*stride; dr=bd\[2,mask\]\*stride; db=bd\[3,mask\]\*stride
x1=gx-dl; y1\_=gy-dt; x2=gx+dr; y2\_=gy+db
all_xyxy.append(np.stack(\[x1,y1\_,x2,y2\_\],axis=1))
all_scores.append(scores)
all_cls.append(np.full(len(ys),c,dtype=np.uint8))
if not all_scores: return \[\]
xyxy=np.concatenate(all_xyxy); scores=np.concatenate(all_scores); cls=np.concatenate(all_cls)
if len(xyxy)==0: return \[\]
idx=cv2.dnn.NMSBoxes(xyxy.tolist(),scores.tolist(),CONF_THRES,NMS_THRES)
if len(idx)==0: return \[\]
keep=idx.flatten()
x1_k=xyxy\[keep,0\]\*rw; y1_k=xyxy\[keep,1\]\*rh
x2_k=xyxy\[keep,2\]\*rw; y2_k=xyxy\[keep,3\]\*rh
sc_k=scores\[keep\]; cl_k=cls\[keep\]
return \[{'cls':int(cl_k\[i\]),'cx':(x1_k\[i\]+x2_k\[i\])/2,'cy':(y1_k\[i\]+y2_k\[i\])/2,
'w':x2_k\[i\]-x1_k\[i\],'h':y2_k\[i\]-y1_k\[i\],'conf':float(sc_k\[i\]),
'x1':x1_k\[i\],'y1':y1_k\[i\],'x2':x2_k\[i\],'y2':y2_k\[i\]} for i in range(len(keep))\]
import os
os.system(“sudo fuser -k /dev/video1 2>/dev/null; sudo fuser -k /dev/video0 2>/dev/null; sudo fuser -k 8080/tcp 2>/dev/null; sleep 0.3”)
def cam_thread():
global g_frame
cap = cv2.VideoCapture(1)
if not cap.isOpened():
cap.release(); cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Camera FAIL"); return
cap.set(3, 640); cap.set(4, 480)
cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0); cap.set(cv2.CAP_PROP_EXPOSURE, 0.3)
print(f"Camera OK: {cap.get(3):.0f}x{cap.get(4):.0f}")
t0=time.time(); n=0
while True:
s, f = cap.read()
if not s: time.sleep(0.01); continue
n += 1
try:
img = cv2.resize(f, (INPUT_SIZE, INPUT_SIZE))
nv12 = bgr2nv12_opencv(img)
outs = models\[0\].forward(\[nv12\])
dets = process_output_seg(outs, f.shape\[1\], f.shape\[0\])
except Exception as e:
print(f"err: {e}"); continue
if n == 1:
print(f"First: {len(dets)} dets")
for d in dets\[:20\]:
cv2.rectangle(f, (int(d\['x1'\]),int(d\['y1'\])), (int(d\['x2'\]),int(d\['y2'\])), COLORS\[d\['cls'\]%6\], 1)
\_, buf = cv2.imencode('.jpg', f, \[cv2.IMWRITE_JPEG_QUALITY, 45\])
if n % 30 == 0:
g_info\['fps'\] = round(n/(time.time()-t0), 1)
print(f"\\n FPS:{g_info\['fps'\]:.1f} Dets:{len(dets)}")
g_info\['dets'\] = len(dets)
with g_lock: g_frame = buf.tobytes()
cap.release()
class H(BaseHTTPRequestHandler):
def log_message(self, \*a): pass
def do_GET(self):
if self.path == '/':
self.send_response(200); self.send_header('Content-Type', 'text/html'); self.end_headers()
self.wfile.write(b'''<html><head><title>Cam Float</title>
body{margin:0;background:#111;color:#fff;font-family:monospace}
img{width:100%;max-width:800px;display:block}
#s{position:fixed;top:8px;left:8px;background:rgba(0,0,0,.7);padding:6px 10px;border-radius:4px;font-size:14px;z-index:10}
elif self.path == '/video':
self.send_response(200); self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=frame'); self.end_headers()
while True:
with g_lock: f = g_frame
if f is None: time.sleep(0.05); continue
try: self.wfile.write(b'--frame\\r\\nContent-Type: image/jpeg\\r\\n\\r\\n'+f+b'\\r\\n')
except: break
elif self.path == '/json':
self.send_response(200); self.send_header('Content-Type','application/json'); self.end_headers()
self.wfile.write(json.dumps(g_info).encode())
else: self.send_error(404)
threading.Thread(target=cam_thread, daemon=True).start()
print(“Web: http://:8080”)
HTTPServer((‘0.0.0.0’, 8080), H).serve_forever()这个是视觉识别代码
怎么优化????