PyQt6+Yolov5使用过程中出现摄像头图像张量的尺寸不匹配的问题以及解决方案

青旬

        在笔记本电脑上使用python调用yolov5模型做图像识别测试过程中,本地摄像头没有异常,但是在调用外接摄像头进行识别时出现摄像头图像张量的尺寸不匹配

        在这之前先了解一些变量和函数:

  1. self.yolov5_cap = cv2.VideoCapture(0) # yolov5识别使用的摄像头
  2. def yolov5_settings(self, # yolov5参数设置及调用
  3. max_det=1000, # maximum detections per image
  4. # device='', # cuda device, i.e. 0 or 0,1,2,3 or cpu
  5. classes=None, # filter by class: --class 0, or --class 0 2 3
  6. agnostic_nms=False, # class-agnostic NMS
  7. line_thickness=1, # bounding box thickness (pixels)
  8. half=False, # use FP16 half-precision inference
  9. ): # 打开摄像头
  10. self.max_det = max_det
  11. self.device = self.ui.device.text()
  12. self.classes = classes
  13. self.agnostic_nms = agnostic_nms
  14. self.line_thickness = line_thickness
  15. self.half = half
  16. # Initialize
  17. set_logging()
  18. self.device = select_device(self.device)
  19. print(self.device)
  20. half &= self.device.type != 'cpu' # half precision only supported on CUDA
  21. self.model = attempt_load(self.ui.model.text(), map_location=self.device) # load FP32 model
  22. self.names = self.model.module.names if hasattr(self.model,
  23. 'module') else self.model.names # get class names
  24. ascii = is_ascii(self.names) # names are ascii (use PIL for UTF-8)

        以下是项目代码中调用yolov5处理图像的函数:

  1. def yolov5_operation_recognize(self): # yolov5处理每一帧图像
  2. # 获取一帧
  3. ret, frame = self.yolov5_cap.read()
  4. # self.decodeDisplay(frame)
  5. img = torch.from_numpy(frame).to(select_device(self.device))
  6. img = img.half() if self.half else img.float() # uint8 to fp16/32
  7. img = img / 255.0 # 0 - 255 to 0.0 - 1.0
  8. if len(img.shape) == 3:
  9. img = img[None] # expand for batch dim
  10. img = img.transpose(2, 3)
  11. img = img.transpose(1, 2)
  12. print(img)
  13. # Inference
  14. pred = self.model(img, augment=False, visualize=False)[0]
  15. # NMS
  16. pred = non_max_suppression(pred, self.conf_thres, self.iou_thres, self.classes, self.agnostic_nms, max_det=self.max_det)
  17. # Process predictions
  18. for i, det in enumerate(pred): # detections per image
  19. s = ''
  20. arr = {'person':0, '组织钳':0, '弯止血钳':0, '插值针':0, '直头剪刀':0, '翘头剪刀':0}
  21. annotator = Annotator(frame, line_width=self.line_thickness, pil=not ascii)
  22. if len(det):
  23. # Rescale boxes from img_size to im0 size
  24. det[:, :4] = scale_coords(img.shape[2:], det[:, :4], frame.shape).round()
  25. # Print results
  26. for c in det[:, -1].unique():
  27. n = (det[:, -1] == c).sum() # detections per class
  28. arr[str(self.names[int(c)])] = n.item()
  29. s += str(n.item()) + ' ' + str(self.names[int(c)]) + ' ' # add to string
  30. # Write results
  31. for *xyxy, conf, cls in reversed(det):
  32. c = int(cls) # integer class
  33. label = f'{self.names[c]} {conf:.2f}'
  34. annotator.box_label(xyxy, label, color=colors(c, True))
  35. print(xyxy)
  36. print('result:' + s)
  37. self.add_tableWidget(arr)
  38. show = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 视频色彩转换回RGB,这样才是现实的颜色
  39. show_image = QImage(show.data, show.shape[1], show.shape[0],
  40. QImage.Format_RGB888) # 把读取到的视频数据变成QImage形式
  41. self.ui.DispLb_3.setPixmap(QPixmap.fromImage(
  42. show_image.scaled(self.ui.DispLb_3.width(), self.ui.DispLb_3.height()))) # 往显示视频的Label里 显示QImage

        控制台报错内容为“RuntimeError: Sizes of tensors must match except in dimension 1. Expected size 76 but got size 75 for tensor number 1 in the list. YOLOv5 a93734c torch 1.12.0 CUDA:0 (NVIDIA GeForce GTX 1060, 6144MiB)”

        报错信息中提到的问题是张量的尺寸不匹配。具体来说,在第一次调用self.model的时候,传递给它的图像张量的尺寸不正确,导致出现维度不匹配的错误。

        在代码中,有一行将图像转换为张量的代码:

img = torch.from_numpy(frame).to(select_device(self.device))

        根据报错信息,我们可以推测到问题出现在这里。为了解决这个问题,我们直接开始排错:

  1. 检查输入图像的维度和形状是否正确。确保frame是一个正确的图像数组,并且具有正确的尺寸和通道顺序(通常是RGB顺序)。

  2. 检查self.model期望的输入图像尺寸。在YOLOv5模型中,通常会定义一个固定的输入图像尺寸,例如416x416或608x608。确保将输入图像调整为正确的尺寸。

  3. 在转换图像为张量之前,对图像进行预处理,例如缩放、裁剪或填充,以使其符合模型的输入要求。

  4. 确保self.device指定的设备是可用的,并且与YOLOv5模型兼容。如果你使用的是GPU加速,确保你的GPU驱动和CUDA版本与模型要求的兼容。

        根据报错信息,模型期望的张量尺寸是76,但实际上得到的尺寸是75。这意味着模型期望每个张量的第一个维度大小为76,但你的输入图像尺寸与此不匹配。因为不同摄像头录制的图像尺寸不一定相同,所以需要在将图像转换成张量之前先固定好图像尺寸。问题锁定在图像尺寸大小上。

        一般情况下使用的YOLOv5的默认输入尺寸为416x416或者608x608。这些尺寸是YOLOv5常用的输入大小。

        这里我使用OpenCVresize函数将图像调整为608x608:

frame = cv2.resize(frame, (608, 608))

        修改之后的完整函数代码为

  1. def yolov5_operation_recognize(self): # yolov5处理每一帧图像
  2. # 获取一帧
  3. ret, frame = self.yolov5_cap.read()
  4. frame = cv2.resize(frame, (608, 608)) # 控制图像尺寸大小
  5. # self.decodeDisplay(frame)
  6. img = torch.from_numpy(frame).to(select_device(self.device))
  7. img = img.half() if self.half else img.float() # uint8 to fp16/32
  8. img = img / 255.0 # 0 - 255 to 0.0 - 1.0
  9. if len(img.shape) == 3:
  10. img = img[None] # expand for batch dim
  11. img = img.transpose(2, 3)
  12. img = img.transpose(1, 2)
  13. # Inference
  14. pred = self.model(img, augment=False, visualize=False)[0]
  15. # NMS
  16. pred = non_max_suppression(pred, self.conf_thres, self.iou_thres, self.classes, self.agnostic_nms, max_det=self.max_det)
  17. # Process predictions
  18. for i, det in enumerate(pred): # detections per image
  19. s = ''
  20. arr = {'person':0, '组织钳':0, '弯止血钳':0, '插值针':0, '直头剪刀':0, '翘头剪刀':0}
  21. annotator = Annotator(frame, line_width=self.line_thickness, pil=not ascii)
  22. if len(det):
  23. # Rescale boxes from img_size to im0 size
  24. det[:, :4] = scale_coords(img.shape[2:], det[:, :4], frame.shape).round()
  25. # Print results
  26. for c in det[:, -1].unique():
  27. n = (det[:, -1] == c).sum() # detections per class
  28. arr[str(self.names[int(c)])] = n.item()
  29. s += str(n.item()) + ' ' + str(self.names[int(c)]) + ' ' # add to string
  30. # Write results
  31. for *xyxy, conf, cls in reversed(det):
  32. c = int(cls) # integer class
  33. label = f'{self.names[c]} {conf:.2f}'
  34. annotator.box_label(xyxy, label, color=colors(c, True))
  35. print(xyxy)
  36. print('result:' + s)
  37. self.add_tableWidget(arr)
  38. show = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 视频色彩转换回RGB,这样才是现实的颜色
  39. show_image = QImage(show.data, show.shape[1], show.shape[0],
  40. QImage.Format_RGB888) # 把读取到的视频数据变成QImage形式
  41. self.ui.DispLb_3.setPixmap(QPixmap.fromImage(
  42. show_image.scaled(self.ui.DispLb_3.width(), self.ui.DispLb_3.height()))) # 往显示视频的Label里 显示QImage

        运行后问题得到了解决。根据排错过程再做一些知识的延伸

        YOLOv5中默认的输入尺寸为416x416或608x608是因为这些尺寸在性能和准确性之间提供了一种平衡。

        YOLOv5是一种基于单阶段检测器的目标检测算法,其主要特点是速度快且具有较高的准确性。为了实现高效的目标检测,YOLOv5采用了一系列的网络结构和技术。

        输入图像的尺寸在YOLOv5中影响着算法的速度和检测精度。较小的输入尺寸(如416x416)可以加快模型的推理速度,但可能会降低检测的准确性,特别是对于小目标。较大的输入尺寸(如608x608)可以提高检测的准确性,尤其是对于小目标的检测,但会增加计算量和推理时间。

        因此,416x416和608x608被视为一种平衡,提供了较好的速度和准确性之间的折衷方案。这些尺寸通常在YOLOv5的默认配置中,可以根据特定任务的需求进行调整。

        需要注意的是,YOLOv5支持任意大小的输入尺寸,并且可以通过调整配置文件或代码中的相关参数来设置所需的输入尺寸。但要注意,更大的输入尺寸可能需要更高的计算资源和更长的推理时间。在选择输入尺寸时,需要考虑到应用场景、硬件资源和实时性等因素。