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) 一张可视化结果(最好能同时给出原图和画图后的图)

我可以继续帮你一步步定位到底是“坐标系问题”还是“解码问题”。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐