ultralytics-8.3.28版本debug记录
1传入文件
代码太多不粘贴在这里了,完整代码写在了篇三
def open_src_file(self):config_file = 'config/fold.json'config = json.load(open(config_file, 'r', encoding='utf-8'))open_fold = config['open_fold']if not os.path.exists(open_fold):open_fold = os.getcwd()name, _ = QFileDialog.getOpenFileName(self, 'Video/image', open_fold, "Pic File(*.mp4 *.mkv *.avi *.flv *.jpg *.png)")if name:self.yolo_predict.source = name #将图片或者视频传入到source中print(name)self.show_status('Load File:{}'.format(os.path.basename(name)))config['open_fold'] = os.path.dirname(name)config_json = json.dumps(config, ensure_ascii=False, indent=2) # 重新保存json的信息with open(config_file, 'w', encoding='utf-8') as f:f.write(config_json)self.stop()
最终我们发现,打开的图片其实保存到了YoloPredictor下面的source,但是BasePredictor(yolo检测器)这个文件下是没有source这个属性的。因此这一步并没有完成图片与yolo检测器之间的链接
# 设置输入源
self.setup_source(self.source if self.source is not None else self.args.source)# 打开的图片从这被self.args.source被加载进去
- 在setup_source函数中,发现传入的source被包含到了self.dataset当中,我们要在这里找出self.dataset对于图片检测和视频检测都有什么区别。
1图片检测
“”“yolo检测器”“”
def setup_source(self, source):"""Sets up source and inference mode."""self.imgsz = check_imgsz(self.args.imgsz, stride=self.model.stride, min_dim=2) # check image sizeself.transforms = (getattr(self.model.model,"transforms",classify_transforms(self.imgsz[0], crop_fraction=self.args.crop_fraction),)if self.args.task == "classify"else None)self.dataset = load_inference_source(source=source,batch=self.args.batch,vid_stride=self.args.vid_stride,buffer=self.args.stream_buffer,)self.source_type = self.dataset.source_typeif not getattr(self, "stream", True) and (self.source_type.streamor self.source_type.screenshotor len(self.dataset) > 1000 # many imagesor any(getattr(self.dataset, "video_flag", [False]))): # videosLOGGER.warning(STREAM_WARNING)self.vid_writer = {}
‘’‘新版本yolo’‘’
def load_inference_source(source=None, batch=1, vid_stride=1, buffer=False):"""Loads an inference source for object detection and applies necessary transformations.Args:source (str, Path, Tensor, PIL.Image, np.ndarray): The input source for inference.batch (int, optional): Batch size for dataloaders. Default is 1.vid_stride (int, optional): The frame interval for video sources. Default is 1.buffer (bool, optional): Determined whether stream frames will be buffered. Default is False.Returns:dataset (Dataset): A dataset object for the specified input source."""source, stream, screenshot, from_img, in_memory, tensor = check_source(source) # 打断点source_type = source.source_type if in_memory else SourceTypes(stream, screenshot, from_img, tensor)# Dataloaderif tensor:dataset = LoadTensor(source)elif in_memory:dataset = sourceelif stream:dataset = LoadStreams(source, vid_stride=vid_stride, buffer=buffer)elif screenshot:dataset = LoadScreenshots(source)elif from_img:dataset = LoadPilAndNumpy(source)else:dataset = LoadImagesAndVideos(source, batch=batch, vid_stride=vid_stride)# Attach source types to the datasetsetattr(dataset, "source_type", source_type)return dataset
输入图片之后load_inference_source函数打断点,发现图片走的是这行代码。
else:dataset = LoadImagesAndVideos(source, batch=batch, vid_stride=vid_stride)
再仔细分析check_source(source)返回的全是false,在这个函数内部判定is_file:True。我们的source确实是file文件,因此这一步并没有问题。
我们再仔细分析一下这行代码
else:dataset = LoadImagesAndVideos(source, batch=batch, vid_stride=vid_stride)
我们返回的dataset是一个迭代器,使用方式如下面代码所示:
Examples:>>> loader = LoadImagesAndVideos("path/to/data", batch=32, vid_stride=1)>>> for paths, imgs, info in loader:... # Process batch of images or video frames... pass
在接下来的代码中有:
batch = next(self.dataset) # 使用 next(self.dataset) 可以显式地获取下一批数据,而不是依赖 for 循环或隐式的迭代器行为。这种方式适用于需要更细粒度控制数据加载的场景,例如在某个特定条件下才加载下一批数据。self.batch = batch # 保存当前批次
path, im, im0s = batch # 从批次中提取路径、图像、原始图像、视频捕获和其他信息
这种情况明显和新版的dataset是不一致的。具体表现在im0s以前是原图,但如今表示注释信息info。
2视频检测
我们发现输入为视频,在check_source(source)
函数中依旧会被判定为is_file:True。因此最后依旧会走LoadImagesAndVideos
这个类,所生成的dataset依旧是没有属性的。setattr(dataset, "source_type", source_type)
这里的属性是没有的。
LoadImagesAndVideos会将MP4装到一个列表里面
后续LoadImagesAndVideos会用视频解释器给装起来,变成self.frames
def _new_video(self, path):"""Creates a new video capture object for the given path and initializes video-related attributes."""self.frame = 0self.cap = cv2.VideoCapture(path)self.fps = int(self.cap.get(cv2.CAP_PROP_FPS))if not self.cap.isOpened():raise FileNotFoundError(f"Failed to open video {path}")self.frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT) / self.vid_stride)
在提取的时候数据的时候,最后一个batch所提取的是path表示地址,im当前帧numpy数组,info其他信息。
batch = next(self.dataset) # 使用 next(self.dataset) 可以显式地获取下一批数据,而不是依赖 for 循环或隐式的迭代器行为。这种方式适用于需要更细粒度控制数据加载的场景,例如在某个特定条件下才加载下一批数据。
self.batch = batch # 保存当前批次,注意如果是视频,那么则为当前帧数据
path, im, info = batch # path表示图片或者视频的地址,im当前帧numpy数组,info其他信息。
然后我发现了一个V11的缺陷,就是一些尺寸的图片无法完成检测,我就报错了
但是换了一个图片之后有可以了,估计是模型在cat的时候有地方是不满足的。
Sizes of tensors must match except in dimension 1. Expected size 136 but got size 135 for tensor number 1 in the list.
2数据集使用
在run函数将dataset将数据集转化为一个迭代器。这个self.dataset也是新版本yolo与旧版本最大的区别。
def run()
...batch = iter(self.dataset) # 将数据集转化为迭代器
3Debug
class YoloPredictor(BasePredictor, QObject):
。。。
self.yolo_predict = YoloPredictor() # Createa a Yolo instance
。。。
self.yolo_predict.source = name #将图片或者视频传入到source中
- 问题代码1——这里报错——报错expected np.ndarray (got list)
ultralytics-8.3.28版本batch是3个维度首先无法被解压为5个。
path, im, im0,svid_cap, s = batch (错误源头)
。。。
- 因此我将其这么调整
self.batch = batch # 保存当前批次
print(batch) # 打印 batch 的内容
print(len(batch)) # 打印 batch 的长度
path, im, im0s = batch # 从批次中提取路径、图像、原始图像、视频捕获和其他信息
path, im, im0s = batch # 从批次中提取路径、图像、原始图像、视频捕获和其他信息
vid_cap, s = None, None # 其他变量设为默认值
- 然而遇见了这个报错——expected np.ndarray (got list) 这里的im要求输入是图片而不是列表。
# 预处理图像
with self.dt[0]:im = self.preprocess(im) # 预处理图像if len(im.shape) == 3: # 如果图像维度为3,则扩展批次维度im = im[None] # 扩展为批次维度
- 于是我调整为
with self.dt[0]:im = self.preprocess(im[count-1]) # 预处理图像if len(im.shape) == 3: # 如果图像维度为3,则扩展批次维度im = im[None] # 扩展为批次维度im = im.permute(0, 3, 1, 2) # 交换维度
- 有报错为’str’ object has no attribute ‘shape’
pred[:, :4] = ops.scale_boxes(img.shape[2:], pred[:, :4], shape).round()
- batch = iter(self.dataset) 这个到底有啥作用
- 报错KeyError(-1),这里的字典没有-1索引?
if isinstance(self.vid_writer[-1], cv2.VideoWriter):
self.vid_writer[-1].release() # 释放视频写入器
太晚了明天继续debug。