draw_tensor2psd.py_cursor
import cv2import numpy as npimport mathimport osimport structfrom tqdm import tqdmfrom glob import globPALETTE = np.random.randint(0, 255, [255, 3], dtype=np.uint32)# 模型输入尺寸(W, H),用于把模型坐标缩放回原图MODEL_IN
import cv2
import numpy as np
import math
import os
import struct
from tqdm import tqdm
from glob import glob
PALETTE = np.random.randint(0, 255, [255, 3], dtype=np.uint32)
# 模型输入尺寸(W, H),用于把模型坐标缩放回原图
MODEL_IN_W = 608
MODEL_IN_H = 736
# zh
imagespath = "/ai/DataSets/OD_FSD_zh/TI_test/rm/4/image/"
# imagespath = "/ai/DataSets/OD_FSD_zh/TI_test/ppchen/DVR-20250804152834-2382380-PLR/image/"
# savepath = "/ai/zhdata/multiyolov5_point_v2/test_images/out"
zh1 = 0
def readTensor(tensorFile):
global zh1
tensor = open(tensorFile,'rb')
infer_data = np.fromfile(tensor, dtype=np.int32)
print(infer_data.shape)
soltnum = int(len(infer_data) / 20)
im0 = cv2.imread(imagespath + tensorFile.split('/')[-1][:-8] + '.bmp')
if im0 is None:
print("读取图片失败:", tensorFile)
return
h0, w0 = im0.shape[:2]
sx = w0 / float(MODEL_IN_W)
sy = h0 / float(MODEL_IN_H)
point_all =[]
for i in range(soltnum):
point_dict1={}
#
x1 = max(int(infer_data[20*i+6] * sx), 0)
y1 = max(int(infer_data[20*i+7] * sy), 0)
x2 = max(int(infer_data[20*i+8] * sx), 0)
y2 = max(int(infer_data[20*i+9] * sy), 0)
x3 = max(int(infer_data[20*i+10] * sx), 0)
y3 = max(int(infer_data[20*i+11] * sy), 0)
x4 = max(int(infer_data[20*i+12] * sx), 0)
y4 = max(int(infer_data[20*i+13] * sy), 0)
# zh = struct.unpack('!f',int(bin(infer_data[20*i+4])[2:],2).to_bytes(4,byteorder='big'))
point_dict1["conf"] = struct.unpack('!f',int(bin(infer_data[20*i+4])[2:],2).to_bytes(4,byteorder='big'))[0]
point_dict1["isOccupied"] = struct.unpack('!f',int(bin(infer_data[20*i+14])[2:],2).to_bytes(4,byteorder='big'))[0]
point_dict1["isVIP"] = struct.unpack('!f',int(bin(infer_data[20*i+15])[2:],2).to_bytes(4,byteorder='big'))[0]
point_dict1["iswoman"] = struct.unpack('!f',int(bin(infer_data[20*i+16])[2:],2).to_bytes(4,byteorder='big'))[0]
point_dict1["isdisabled"] = struct.unpack('!f',int(bin(infer_data[20*i+17])[2:],2).to_bytes(4,byteorder='big'))[0]
point_dict1["ischarging"] = struct.unpack('!f',int(bin(infer_data[20*i+18])[2:],2).to_bytes(4,byteorder='big'))[0]
point_dict1["step"] = struct.unpack('!f',int(bin(infer_data[20*i+19])[2:],2).to_bytes(4,byteorder='big'))[0]
point_dict1["name"] = str(struct.unpack('!f',int(bin(infer_data[20*i+5])[2:],2).to_bytes(4,byteorder='big'))[0])
point_dict1["delrule"] = 0
point_dict1["pointx"] = [x1,x2,x3,x4]
point_dict1["pointy"] = [y1,y2,y3,y4]
if x3 > 1000 or x4 > 1000 or x1 > 1000 or x2 > 1000 or y3 > 1000 or y4 > 1000 or y1 > 1000 or y2 > 1000:
zh1 +=1
kk = struct.unpack('!f',int(bin(infer_data[20*i+17])[2:],2).to_bytes(4,byteorder='big'))[0]
print("数据解析错误"+ tensorFile + str(x3) + '--'+ str(zh1))
break
point_all.append(point_dict1)
end = len(point_all)
for i in range(len(point_all)):
if point_all[i]["delrule"] == 0:
for j in range(i+1,end):
#简单就是求入口顶点之间的距离
xi1 = point_all[i]['pointx'][0]
yi1 = point_all[i]['pointy'][0]
xi2 = point_all[i]['pointx'][1]
yi2 = point_all[i]['pointy'][1]
xj1 = point_all[j]['pointx'][0]
yj1 = point_all[j]['pointy'][0]
xj2 = point_all[j]['pointx'][1]
yj2 = point_all[j]['pointy'][1]
if (abs(xi1 - xj1) + abs(yi1 - yj1)) < 40 or (abs(xi2 - xj2) + abs(yi2 - yj2)) < 40:
point_all[j]["delrule"] = 1
for i in range(len(point_all)):
if point_all[i]["delrule"] == 0:
line1 = [point_all[i]['pointx'][0],point_all[i]['pointy'][0],point_all[i]['pointx'][3],point_all[i]['pointy'][3]]
line2 = [point_all[i]['pointx'][1],point_all[i]['pointy'][1],point_all[i]['pointx'][2],point_all[i]['pointy'][2]]
vec1 =[line1[2]-line1[0],line1[3]-line1[1]]
vec2 =[line2[2]-line2[0],line2[3]-line2[1]]
#计算向量的点积和模长
dot_product = vec1[0] * vec2[0] + vec1[1] * vec2[1]
m1 = math.sqrt(vec1[0]**2 + vec1[1]**2) + 0.000000000001
m2 = math.sqrt(vec2[0]**2 + vec2[1]**2) + 0.000000000001
val = dot_product/(m1 * m2)
if val > 1:
val = 1
if val < -1:
val = -15
radians = math.acos(val)
du = math.degrees(radians)
if du > 20:
point_all[i]["delrule"] = 2
if 1:
for point_i in point_all:
if point_i["delrule"] == 0:
if point_i["conf"] > 0.45:#0.45
print(point_i["conf"])
cv2.putText(im0, f'{point_i["conf"]:.3f}',
(point_i["pointx"][0] + 6, point_i["pointy"][0] + 6),
cv2.FONT_HERSHEY_PLAIN, 1.5, (0, 255, 0),2) #置信度
cv2.putText(im0, point_i["name"],
(point_i["pointx"][0] + 6, point_i["pointy"][0] + 30),
cv2.FONT_HERSHEY_PLAIN, 1.5, (0, 255, 0),2) #类别
if float(point_i["isOccupied"])> 0.1: #0.5
cv2.putText(im0, "Occ :" + f'{point_i["isOccupied"]:.3f}',
(point_i["pointx"][0] + 6, point_i["pointy"][0] + 54),
cv2.FONT_HERSHEY_PLAIN, 1.5, (0, 255, 0),2) #是否被占用
if float(point_i["isVIP"]) > 0.5:
cv2.putText(im0, "VIP :" + f'{point_i["isVIP"]:.3f}',
(point_i["pointx"][0] + 6, point_i["pointy"][0] + 78),
cv2.FONT_HERSHEY_PLAIN, 1.5, (0, 255, 0),2) #是否VIP车位
if float(point_i["iswoman"]) > 0.5:
cv2.putText(im0, "woman :" + f'{point_i["iswoman"]:.3f}',
(point_i["pointx"][0] + 6, point_i["pointy"][0] + 102),
cv2.FONT_HERSHEY_PLAIN, 1.5, (0, 255, 0),2) #是否女性车位
if float(point_i["isdisabled"]) > 0.5:
cv2.putText(im0, "disab :" + f'{point_i["isdisabled"]:.3f}',
(point_i["pointx"][0] + 6, point_i["pointy"][0] + 126),
cv2.FONT_HERSHEY_PLAIN, 1.5, (0, 255, 0),2) #是否残疾人车位
if float(point_i["ischarging"]) > 0.5:
cv2.putText(im0, "charg :" + f'{point_i["ischarging"]:.3f}',
(point_i["pointx"][0] + 6, point_i["pointy"][0] + 150),
cv2.FONT_HERSHEY_PLAIN, 1.5, (0, 255, 0),2) #是否充电车位
if float(point_i["step"]) > 0.5:
cv2.putText(im0, "step :" + f'{point_i["step"]:.3f}',
(point_i["pointx"][0] + 6, point_i["pointy"][0] + 174),
cv2.FONT_HERSHEY_PLAIN, 1.5, (0, 255, 0),2) #是否阶梯形车位
cv2.arrowedLine(im0, (point_i["pointx"][0], point_i["pointy"][0]),(point_i["pointx"][1], point_i["pointy"][1]), (0, 255, 0), 1, cv2.LINE_AA)
cv2.arrowedLine(im0, (point_i["pointx"][1], point_i["pointy"][1]),(point_i["pointx"][2], point_i["pointy"][2]), (255, 255, 0), 1, cv2.LINE_AA)
cv2.arrowedLine(im0, (point_i["pointx"][2], point_i["pointy"][2]),(point_i["pointx"][3], point_i["pointy"][3]), (255, 255, 0), 1, cv2.LINE_AA)
cv2.arrowedLine(im0, (point_i["pointx"][3], point_i["pointy"][3]),(point_i["pointx"][0], point_i["pointy"][0]), (255, 255, 0), 1, cv2.LINE_AA)
else:
cv2.putText(im0, f'{point_i["conf"]:.3f}',
(point_i["pointx"][0] + 6, point_i["pointy"][0] + 6),
cv2.FONT_HERSHEY_PLAIN, 2, (0, 0, 255),3)
cv2.arrowedLine(im0, (point_i["pointx"][0], point_i["pointy"][0]),(point_i["pointx"][1], point_i["pointy"][1]), (0, 0, 255), 1, cv2.LINE_AA)
cv2.arrowedLine(im0, (point_i["pointx"][1], point_i["pointy"][1]),(point_i["pointx"][2], point_i["pointy"][2]), (0, 0, 255), 1, cv2.LINE_AA)
cv2.arrowedLine(im0, (point_i["pointx"][2], point_i["pointy"][2]),(point_i["pointx"][3], point_i["pointy"][3]), (0, 0, 255), 1, cv2.LINE_AA)
cv2.arrowedLine(im0, (point_i["pointx"][3], point_i["pointy"][3]),(point_i["pointx"][0], point_i["pointy"][0]), (0, 0, 255), 1, cv2.LINE_AA)
if point_i["delrule"] == 1:
cv2.putText(im0, f'{point_i["conf"]:.3f}',
(point_i["pointx"][0] + 6, point_i["pointy"][0] + 6),
cv2.FONT_HERSHEY_PLAIN, 2, (0, 0, 255),3)
cv2.arrowedLine(im0, (point_i["pointx"][0], point_i["pointy"][0]),(point_i["pointx"][1], point_i["pointy"][1]), (0, 0, 0), 1, cv2.LINE_AA)
cv2.arrowedLine(im0, (point_i["pointx"][1], point_i["pointy"][1]),(point_i["pointx"][2], point_i["pointy"][2]), (0, 0, 0), 1, cv2.LINE_AA)
cv2.arrowedLine(im0, (point_i["pointx"][2], point_i["pointy"][2]),(point_i["pointx"][3], point_i["pointy"][3]), (0, 0, 0), 1, cv2.LINE_AA)
cv2.arrowedLine(im0, (point_i["pointx"][3], point_i["pointy"][3]),(point_i["pointx"][0], point_i["pointy"][0]), (0, 0, 0), 1, cv2.LINE_AA)
if point_i["delrule"] == 2:
cv2.putText(im0, f'{point_i["conf"]:.3f}',
(point_i["pointx"][0] + 6, point_i["pointy"][0] + 6),
cv2.FONT_HERSHEY_PLAIN, 2, (0, 0, 255),3)
cv2.arrowedLine(im0, (point_i["pointx"][0], point_i["pointy"][0]),(point_i["pointx"][1], point_i["pointy"][1]), (0, 0, 0), 1, cv2.LINE_AA)
cv2.arrowedLine(im0, (point_i["pointx"][1], point_i["pointy"][1]),(point_i["pointx"][2], point_i["pointy"][2]), (0, 0, 0), 1, cv2.LINE_AA)
cv2.arrowedLine(im0, (point_i["pointx"][2], point_i["pointy"][2]),(point_i["pointx"][3], point_i["pointy"][3]), (0, 0, 0), 1, cv2.LINE_AA)
cv2.arrowedLine(im0, (point_i["pointx"][3], point_i["pointy"][3]),(point_i["pointx"][0], point_i["pointy"][0]), (0, 0, 0), 1, cv2.LINE_AA)
zh = "/ai/TopViewMul/4/psd_out/" + tensorFile.split('/')[-1][:-8] + '.bmp'
print(zh)
cv2.imwrite(zh, im0)
# zh
# for binpath in tqdm(glob(os.path.join("/ai/DataSets/OD_FSD_zh/TI_test/rm/2/psd/",'*.bin'))):
# readTensor(binpath)
num=0
for binpath in tqdm(glob(os.path.join("/ai/TopViewMul/4/out_480_544_2039/psd/",'*.bin'))):
readTensor(binpath)
num+=1
if num==100:
print('exit')
exit(0)
从你现在这张“修改后”的图看,框已经不集中在左上角了,但仍明显偏向左边。这类现象更像是可视化坐标系和真实图像尺寸不一致,而不是解码本身的问题。
关键原因(结合你的工程):
1) 下位机推理的输入会被强制 resize 到模型输入尺寸
在 main.c 的 readInput() 里,直接把原始 BMP resize 到 ioBufDesc->inWidth/Height:
- 也就是说,模型输出坐标一定是“模型输入尺寸坐标系”,而不是原图尺寸坐标系。
2) draw_tensor2psd.py 直接把坐标画到原图
脚本没有做任何缩放,默认坐标就是原图像素。
如果原图是大分辨率(比如 1472×1216),而模型输入是 608×736,那么坐标会被压缩到左上角区域。
3) 你这套模型的输入尺寸其实是 W=608, H=736
- train_608_736.py 里 img-size 标注为 HW,即 H=736, W=608
- TIDL 配置 inWidth=608 inHeight=736 也一致
如果你在可视化里当成 736×608(W=736),横向缩放会变小,框会偏左。
结论
现在剩下的主要问题很可能在可视化脚本:
需要把输出点坐标从“模型输入尺寸坐标系”缩放到“原图尺寸坐标系”。
你可以在 draw_tensor2psd.py 里这样做(逻辑示意):
# 假设模型输入尺寸(一定要用 W=608, H=736)
in_w, in_h = 608, 736
# 原图尺寸
h0, w0 = im0.shape[:2]
sx = w0 / in_w
sy = h0 / in_h
x1 = int(x1 * sx); y1 = int(y1 * sy)
x2 = int(x2 * sx); y2 = int(y2 * sy)
x3 = int(x3 * sx); y3 = int(y3 * sy)
x4 = int(x4 * sx); y4 = int(y4 * sy)
或者更简单:
把 im0 换成和模型输入一致的 608×736 图,这样就无需缩放。
为什么这很符合你的现象
- 修改后“不再聚左上角”,说明解码坐标已基本正确
- 但仍偏左,说明坐标系仍然比原图小(典型就是 608×736 画在大图)
如果你方便,再补充两点我可以进一步确认:
- 这张图的原始 BMP 实际尺寸(宽、高)
- 你想把结果画在原图还是模型输入图
我也可以直接帮你把 draw_tensor2psd.py 改成自适应缩放版本。
已改好 draw_tensor2psd.py,现在会把模型输出坐标缩放回原图再画:
- 增加 MODEL_IN_W/ MODEL_IN_H(默认 608×736)
- 读取原图尺寸后计算 sx/sy,对四个点坐标做缩放
- 增加了图片读取失败的保护
你现在只需要确认:
模型输入尺寸是否确实是 W=608, H=736(从你的 TIDL 配置和训练脚本看是对的)。
如果不是,改这两个常量就行。
建议你再跑一张图验证位置是否对齐。
如果还有偏移,我可以继续根据你的原图尺寸和输入尺寸做更精细的映射(比如考虑裁剪或 letterbox)。
下面给你一套到公司后可直接照做的调试思路,按优先级从“最可能”到“次可能”排:
1) 先确认坐标系是否一致(最常见)
目标:验证“模型输出坐标”是基于哪张图的尺寸。
检查点:
- 原图尺寸 W0,H0(BMP 实际大小)
- 模型输入尺寸 Win,Hin(TIDL inWidth/inHeight)
- draw_tensor2psd.py 里 MODEL_IN_W/H 是否与 Win/Hin 一致
- 训练脚本 img-size 标注为 HW,所以输入是 W=608, H=736,不要弄反
快速验证:
- 把 draw_tensor2psd.py 临时改成直接画在 resize 后的图上(把原图 resize 到 608×736 再画),如果这样位置正确,说明问题就是“原图坐标系映射”,不是模型输出问题。
2) 判断是不是“缩放比例 + 误差”
如果框整体偏左/偏上/偏小:
- 打印 sx = W0/Win, sy = H0/Hin
- 观察是否 水平和垂直比例不同(W0/H0 不是 608/736 的等比)
- 如果图像原始比例和模型输入比例不一致,不做 letterbox,直接 resize,会造成几何扭曲;你现在的映射应与这个扭曲一致(用不同的 sx/sy),否则会偏。
3) 检查是否存在 letterbox / padding
你当前下位机预处理是 直接 resize,没有 letterbox。
但如果你在上位机或训练时用了 letterbox,且模型输出是基于 letterbox 坐标,那么必须考虑 padding 偏移。
验证方法:
- 在下位机 readInput() 里确认是 resizeImage(你当前确实是 resize)
- 如果以后切换成 letterbox,draw_tensor2psd.py 需增加 padX/padY 的反向补偿
4) 校验 bin 输出是否是“输入尺度”
最直接的方法:
- 在下位机输出前打印 一个 bbox 的点坐标范围(比如 min/max)
- 如果输出点范围接近 Win/Hin,说明坐标是输入尺度
- 如果输出点范围接近 W0/H0,说明已经是原图尺度,那你就不该再缩放
5) 排除 W/H 颠倒
极易踩坑:
一秒验证:
- 把 MODEL_IN_W/H 交换试一次
如果交换后框明显“更接近”,说明之前 W/H 搞反了。
6) 逐步定位到“一个样本”
建议用一张固定样本,不要批量。
把这 4 个值记下来:
- 原图大小 W0/H0
- 模型输入 Win/Hin
- 任意一个输出点 x1,y1
- 画到图上后的 x1',y1'
确认映射公式:
x' = x1 * W0/Win
y' = y1 * H0/Hin
7) 如果仍不对,排查输出解码
可能是:
- 某个 head 的 inWidth/inHeight 仍然没设置
- 输出张量顺序/通道顺序与模型不一致(虽然你已确认 head 索引,但通道组织也可能变)
- 量化 scale 不一致导致数值偏差(一般不会全局偏移这么明显)
验证:
- 打印 yolov5params.inWidth[]/inHeight[](每个 head)
- 打印 ioBufDesc.outWidth/outHeight/outNumChannels 是否与训练期模型一致
如果你明天运行后仍不对,直接把以下三项发我,我可以继续定位:
1) 原图尺寸 W0×H0
2) 模型输入尺寸 Win×Hin
3) 一张可视化结果(最好能同时给出原图和画图后的图)
我可以继续帮你一步步定位到底是“坐标系问题”还是“解码问题”。
更多推荐



所有评论(0)