从0开始 yolov5可以用灰度图像进行训练和检测吗

news/2024/7/11 1:21:43 标签: YOLO, 目标跟踪, 人工智能

yolov5可以用灰度图像进行训练吗,从0开始yolov5灰度图训练和检测


文章目录

  • yolov5可以用灰度图像进行训练吗,从0开始yolov5灰度图训练和检测
    • @[toc]
    • 1 预演
            • 【表1-1 模型结构截取】
    • 2 修改源码使可以灰度训练
      • 2.1 修改读取图片模式
      • 2.2 修改源码传参中的通道数
      • 2.3 运行train.py
      • 2.4 修改utils/general.py中的源码
      • 2.5 修改dataloaders.py中所有cv2读取图片的flags
      • 2.6 此时运行 ’train.py‘ 文件可正常epoch跑起来。
    • 3 模型测试
      • 3.1 RGB图训练的模型测试
        • 训练对比
        • 准确率的对比
        • val推理时间的对比
      • 3.2 GRAY图训练的模型检测测试
        • 训练对比
        • 准确率的对比
        • val推理时间的对比
      • 3.3 RGB和GRAY训练测试对比
            • 【表3-1 RGB和GRAY模型结论对比】
    • 4 结论
    • 5 测试RGB转Gray时间
    • 附1 未修改任何源码,直接使用灰度图像+yolov6_6.2训练
    • 附2 训练时数据集标签如下

1 预演

通常我们采用RGB图像做目标检测,考虑多路视频流同时做目标检测时,消耗显卡算力和推理时间较多。现通过实验对比对灰度图(GRAY)和RGB图做训练和测试对比。

本次使用yolov5_6.2尝试灰度图做目标检测的训练的目标检测的测试。

直接将彩色图通过opencv算法转换为灰度图,运行’python train.py’ 训练。

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

在train.py运行的时候,无报错,但是打印的模型结构的前两行如下所示:

【表1-1 模型结构截取】
fromnparamsmodulearguments
0-113520models.common.Conv[3, 32, 6, 2, 2]
1-1118560models.common.Conv[32, 64, 3, 2]

​ 通过上图打印可知模型的输入是固定3通道的,那么意味着即使训练的数据集是灰度图像,在读取图像时仍会对图像进行处理。

观察源码,在utils/dataloaders.py文件第248行

245        else:
246            # Read image
247            self.count += 1
248            img0 = cv2.imread(path)  # BGR
249            assert img0 is not None, f'Image Not Found {path}'
250            s = f'image {self.count}/{self.nf} {path}: '

​ 如下opencv的imread方法简介

img = cv2.imread(path, flag)   	# flag: the way in which img should be read
								# flag: default value is cv2.IMREAD_COLOR
# cv2.IMREAD_COLOR(1):    BGR
# cv2.IMREAD_GRAYSCALE(0): GRAY
# cv2.IMREAD_UNCHANGED(-1):

​ opencv在默认情况下,'cv2.imread(path)'会读取3个通道(BGR)的图像,如果是灰度图,会将图层复制三次(BGR缺省,BGR),因此读出来的图片是三通道。

2 修改源码使可以灰度训练

源码改变的位置,后加注释’Hlj2308’。

2.1 修改读取图片模式

在utils/dataloaders.py文件第248行,原

img0 = cv2.imread(path)  # BGR

改为

img0 = cv2.imread(path, 0)  # Hlj2308_GRAY

2.2 修改源码传参中的通道数

2.2.1 在train.py文件第122行、130行中的参数 ch=3 改为 ch=1。

model = Model(cfg or ckpt['model'].yaml, ch=1, nc=nc, anchors=hyp.get('anchors')).to(device)  # Hlj2308
model = Model(cfg, ch=1, nc=nc, anchors=hyp.get('anchors')).to(device)  # Hlj2308 create

2.2.2 在models/yolo.py文件第151行ch=3改为 ch=1。

149 class DetectionModel(BaseModel):
150     # YOLOv5 detection model
151     def __init__(self, cfg='yolov5s.yaml', ch=1, nc=None, anchors=None):  
152         super().__init__()
153         if isinstance(cfg, dict):
154             self.yaml = cfg  # model dict

2.3 运行train.py

在运行 train.py 的过程中,反正就是一路报错,一路改错调试。最终实现可正常训练和测试的目的。

【报错1】

File "....packages\torch\nn\modules\conv.py", line 442, in _conv_forward
    return F.conv2d(input, weight, bias, self.stride,
RuntimeError: Given groups=1, weight of size [32, 1, 6, 6], expected input[8, 3, 640, 640] to have 1 channels, but got 3 channels instead

数据读入的通道数不对,应该是 1 通道,但是这里读入的是 3 通道。

可能是PIL模块’Image.open()'打开图像时的’mode’问题。修改models/common.py文件中 612 行

im, f = Image.open(requests.get(im, stream=True).raw if str(im).startswith('http') else im), im

后面添加如下两行代码

if im.mode != 'L':
    im = im.convert('L')

仍存在【报错1】

2.4 修改utils/general.py中的源码

修改utils/general.py中的源码1031-1032行如下

def imread(path, flags=cv2.IMREAD_COLOR):
    return cv2.imdecode(np.fromfile(path, np.uint8), flags)

改为

def imread(path, flags=cv2.IMREAD_GRAYSCALE):
    return cv2.imdecode(np.fromfile(path, np.uint8), cv2.IMREAD_GRAYSCALE)

仍存在【报错1】

2.5 修改dataloaders.py中所有cv2读取图片的flags

在utils/dataloaders.py文件第677、691、881、1042、1119、1122、1125行,原

cv2.imread(f)

改为如下,可通过Ctr+F搜索替换

cv2.imread(f, 0)

此时,运行train.py,【报错1】将不存在

【报错2】如下

File "..../yolov5_6.2_gray/utils/dataloaders.py", line 706, in load_mosaic
    img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8)  # base image with 4 tiles
IndexError: tuple index out of range

解决:原因是img.shape[2]索引不存在,因为图片是单通道的,所以只有二维,而非三维数组。这里将源码\utils\dataloaders.py的706行源码

img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8)  

改为

img4 = np.full((s * 2, s * 2, img.shape[2] if len(img.shape)==3 else 1), 114, dtype=np.uint8) 

【报错3】如下,是由于【报错2】的修改方式不对

File "..../yolov5_6.2_gray/utils/dataloaders.py", line 721, in load_mosaic
    img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b]  # img4[ymin:ymax, xmin:xmax]
ValueError: could not broadcast input array from shape (360,640) into shape (360,640,1)

解决:打印报错对应 img4 和 img 的 shape,有’img4.shape, img.shape = (1280, 1280, 1) (360, 640)',显然是图片尺寸不匹配导致的。将【报错2】重新修改为如下

img4 = np.full((s * 2, s * 2), 114, dtype=np.uint8)  # base image with 4 tiles

【报错4】如下

File "D:\yolov5train\yolov5_6.2_grayTrain\utils\dataloaders.py", line 642, in __getitem__
    augment_hsv(img, hgain=hyp['hsv_h'], sgain=hyp['hsv_s'], vgain=hyp['hsv_v'])
  File "D:\yolov5train\yolov5_6.2_grayTrain\utils\augmentations.py", line 69, in augment_hsv
    hue, sat, val = cv2.split(cv2.cvtColor(im, cv2.COLOR_BGR2HSV))
cv2.error: OpenCV(4.2.0) c:\projects\opencv-python\opencv\modules\imgproc\src\color.simd_helpers.hpp:92: error: (-2:Unspecified error) in function '__cdecl cv::impl::`anonymous-namespace'::CvtHelper<struct cv::impl::`anonymous namespace'::Set<3,4,-1>,struct cv::impl::A0x3b52564f::Set<3,-1,-1>,struct cv::impl::A0x3b52564f::Set<0,5,-1>,2>::CvtHelper(const class cv::_InputArray &,const class cv::_OutputArray &,int)'
> Invalid number of channels in input image:
>     'VScn::contains(scn)'
> where
>     'scn' is 1

解决:上述问题是由于hsv通道拆分导致的,此处不需要,将\utils\dataloaders.py中的 line 642对应代码注释掉,如下

# augment_hsv(img, hgain=hyp['hsv_h'], sgain=hyp['hsv_s'], vgain=hyp['hsv_v'])

【报错5】如下

File "D:\yolov5train\yolov5_6.2_grayTrain\utils\dataloaders.py", line 665, in __getitem__
    img = img.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGB
ValueError: axes don't match array

解决:上述问题是猜测是由于BGR to RGB转换导致的,将\utils\dataloaders.py的665行源码

img = img.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGB

改为如下是不对的

img = img.transpose((2, 0, 1))  # HWC to CHW

opencv通过HWC加载图片,而Pytorch需要CHW,故需要将图片通过 'transpose((2, 0, 1)) ’ 转为CHW。但此时需要进行 ‘transpose’ 操作的 'img’的尺寸是(640, 640),无法直接进行 ‘transpose’ 操作。故改为如下【报错5】可避免。

img = img.reshape(1, img.shape[0], img.shape[1])

2.6 此时运行 ’train.py‘ 文件可正常epoch跑起来。

​ 此时未涉及修改模型cfg文件。其中train.py中的参数设置如下

'--weights', type=str, default='',
'--cfg', type=str, default='./models/yolov5s.yaml',
'--data', type=str, default= './data/my_yolo5.yaml',
'--epochs', type=int, default=50
'--batch-size', type=int, default=8,
'--imgsz', '--img', '--img-size', type=int, default= 640,

'./models/yolov5s.yaml’参数包含如下

# Parameters
nc: 11  # number of classes
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.50  # layer channel multiple

'./data/my_yolo5.yaml’参数包含如下

train: ../datasTrain4_LQTest_gray/images/train/
val: ../datasTrain4_LQTest_gray/images/val/

# number of classes
nc: 11

# class names
names: [ 'pedes', 'car', 'bus', 'truck', 'bike', 'elec', 'tricycle','coni', 'warm', 'tralight', 'speVeh']

3 模型测试

​ 本次 RGB 和 GRAY 均采用yolov5_6.2无预训练weight训练50epochs。数据集是同一组图像的RGB和GRAY格式,标签文件一模一样。–> 因为数据集较少,2491(train)、360(val),且没有使用预训练模型,epoch=50,所以P、R、mAP相对较低。

3.1 RGB图训练的模型测试

训练对比

​ 一个epoch的train的时间在 4:24-4:34 不等,一个epoch的val的时间在 0:13-0:15 不等。

准确率的对比

​ trian结束,all:P(0.869) R(0.653) mAP(0.717)@.5 mAP(0.48)@.5:.95

val: all:P(0.712) R(0.651) mAP(0.71)@.5 mAP(0.512)@.5:.95

val推理时间的对比

Speed: 0.3ms pre-process, 7.1ms inference, 1.8ms NMS per image at shape (8, 3, 640, 640)

3.2 GRAY图训练的模型检测测试

训练对比

​ 一个epoch的train的时间在 2:48-2:54 不等,一个epoch的val的时间在 0:08-0:09 不等。

准确率的对比

​ trian结束,all:P(0.905) R(0.617) mAP(0.705)@.5 mAP(0.464)@.5:.95

val: all:P(0.728) R(0.619) mAP(0.696)@.5 mAP(0.497)@.5:.95

val推理时间的对比

Speed: 0.1ms pre-process, 4ms inference, 2.3ms NMS per image at shape (8, 1, 640, 640)

上述运行val.py过程中,

【报错6】如下

...
File "D:/yolov5train/yolov5_6.2_grayTrain/val.py", line 169, in run
    model.warmup(imgsz=(1 if pt else batch_size, 3, imgsz, imgsz))  # warmup
...
File "D:\AppData\anaconda3.8\envs\yolov5t\lib\site-packages\torch\nn\modules\conv.py", line 442, in _conv_forward
    return F.conv2d(input, weight, bias, self.stride,
RuntimeError: Given groups=1, weight of size [32, 1, 6, 6], expected input[1, 3, 640, 640] to have 1 channels, but got 3 channels instead

解决:将val.py源码中169行代码

model.warmup(imgsz=(1 if pt else batch_size, 3, imgsz, imgsz))  # warmup

改为如下,【报错6】得以解决。

model.warmup(imgsz=(1 if pt else batch_size, 1, imgsz, imgsz))  # warmup

3.3 RGB和GRAY训练测试对比

​ 本次 RGB 和 GRAY 均采用yolov5_6.2无预训练weight训练50epochs。数据集是同一组图像的RGB和GRAY格式,标签文件一模一样。 因为数据集分配:2491(train)、360(val)。对比结果如下:

【表3-1 RGB和GRAY模型结论对比】

在这里插入图片描述

4 结论

综合表3-1可得结论如下:

(1)通过采用RGB和GRAY对图像的对比,采用灰度图像训练和推理的时间确有减少。我们关注的推理时间从7.1降到4.1,减少了3 / 7.1 = 42%的时间。

(2)测试的P、R、mAP@.5值存在差异,但50轮epochs的差异并不大。由于在无预训练模型的状态下,训练50轮。

5 测试RGB转Gray时间

​ 通过 ‘detect.py’ 测试。将模型传入的参数 ch=3 改为 1 。 ‘–source’ 参数取文件夹中均为 RGB 的图像。发现直接检测完会按照gray格式保存在了 ‘runs/val’ 目录下。原来是自己在 ‘utils/dataloaders.py’ 源码中 ‘class LoadImages:’ 类里的方法 ‘def next(self):’ 中,已经改为了 ‘img0 = cv2.imread(path, 0)’ 。于是,自己测了一下 ‘cv2.imread()’ 方法的耗时,如下:

读取RGB图的耗时(s): 0.02003192901611328
读取灰度图的耗时(s): 0.011004924774169922

​ 其实,‘cv2.imread()’ 方法的耗时对比是完全没有必要的,我们通常通过rtsp流或者相机SDK取流,拿到的摄相机数据流为yuv格式,而y通道的数据就是gray数据。反而要获取RGB数据需要yuv转RGB公式的计算。

​ 有必要研究一下,① 目标检测在部署的时候,是怎么来读取相机中的视频的。② 相机SDK给出来的是什么样的数据形式。③ 目标检测的前处理当中,是对rgb图做处理吗,还是视频流过来的其它格式(或形式的数据),是否需要转为rgb的格式后再做其它的前处理操作。考虑包含相机的编解码操作在内的原理。

【上段落中提到的有必要研究的3点,有没有好一点的资源推荐呢,感谢~】

如下为图像所有像素点的 R、G、B 分量与 Y、U、V 分量之间相互转换的公式。
{ Y = 0.299 ∗ R + 0.587 ∗ G + . 0114 ∗ B U = − 0.147 ∗ R − 0.289 ∗ G + 0.436 ∗ B V = 0.615 ∗ R − 0.515 ∗ G − 0.100 ∗ B \begin{cases} Y = 0.299*R + 0.587*G + .0114*B\\ U = -0.147*R -0.289*G + 0.436*B\\ V = 0.615*R -0.515*G -0.100*B \end{cases} Y=0.299R+0.587G+.0114BU=0.147R0.289G+0.436BV=0.615R0.515G0.100B

{ R = Y + 1.14 ∗ V G = Y − 0.39 ∗ U − 0.58 ∗ V B = Y + 2.03 ∗ U \begin{cases} R = Y + 1.14*V\\ G = Y - 0.39*U - 0.58*V\\ B = Y + 2.03*U \end{cases} R=Y+1.14VG=Y0.39U0.58VB=Y+2.03U


附1 未修改任何源码,直接使用灰度图像+yolov6_6.2训练

模型参数如图1

在这里插入图片描述

图1 也能跑起来,根据理解,也是3通道跑的训练,并且三个通道都是GRAY通道的值。按照yuv理解的话,y通道认为是灰度通道。即相当于3个通道都是y对应的灰度的值。

附2 训练时数据集标签如下

表1 目标标签详情

IDlabel描述备注
0pedes行人(骑着平衡车 平板车也划到此类)
1car轿车(包括SUV、 MPV(皮卡)、 VAN(面包车))
2bus大巴车、公交车
3truck卡车、货车
4bike自行车
5elec摩托车(电摩托车)
6tricycle三轮车(电三轮车、油三轮车)
7coni锥桶
8warm警示柱
9tralight交通信号灯
10speVeh紧急或特殊车辆 (救护车、消防车、工程车辆如吊车挖掘机渣土车等

http://www.niftyadmin.cn/n/5002517.html

相关文章

深度学习学习笔记——解决过拟合问题的方法:权重衰减和暂退法,与正则化之间的关系

解决过拟合问题是机器学习和深度学习中关键的任务之一&#xff0c;因为它会导致模型在训练数据上表现良好&#xff0c;但在未见数据上表现不佳。以下是一些解决过拟合问题的常见方法&#xff1a; 增加训练数据&#xff1a; 增加更多的训练数据可以帮助模型更好地捕捉数据的真实…

数字化流程中台详解:专业的服务化这样做

上期文章提到&#xff0c;在不具备实施企业级统一流程平台的企业中&#xff0c;BPM往往会采用服务化流程中台模式&#xff0c;以流程能力赋能的形式进行实施。那么流程中台及服务化需要具备哪些核心能力&#xff1f;实施后又会有怎样的体验呢&#xff1f;本文将逐一详解。 数字…

【深入解析spring cloud gateway】03 网关内置过滤器工厂( GatewayFilter Factories)

一、快速开始 路由过滤器允许修改传⼊的HTTP请求和传出的HTTP响应。Spring Cloud Gateway包括了许多内置的GatewayFilter Factories。 filters一般和predicates一起配合来使用。当满足xxx条件&#xff0c;即执行某filter的逻辑。 可以通过配置文件和代码两种方式&#xff0c;…

如何使用代理配置快速定位接口测试脚本问题?

在调试接口用例过程中&#xff0c;如果响应结果和预期结果不一致&#xff0c;则需要检查请求信息。通过代理获取自动化测试中的请求响应信息&#xff0c;对比与正常请求响应的区别&#xff0c;就能够更直观的排查请求错误&#xff0c;相当于编写代码时的 debug 功能。 实战练习…

网络安全-子域名收集

本文为作者学习文章&#xff0c;按作者习惯写成&#xff0c;如有错误或需要追加内容请留言&#xff08;不喜勿喷&#xff09; 本文为追加文章&#xff0c;后期慢慢追加 子域名 子域名指二级域名,二级域名是顶级域名(一级域名)的下一级比如mail.heetian.com和bbs.heetian.com…

Golang - api中生产数据,另一个进程控制并发数去消费api中生产的数据

api示例&#xff1a;该实例主要功能是实现一个API&#xff0c;API在调用的时候会向channel中发送任务数据。Consumer函数去消费channel中的任务数据&#xff0c;并且可以通过maxConcurrency去控制消费的并发数 package mainimport ("fmt""github.com/kataras/i…

【农业生产模拟】WOFOST模型与PCSE模型实践

查看原文>>>【农业生产模拟】WOFOST模型与PCSE模型实践 实现作物产量的准确估算对于农田生态系统响应全球变化、可持续发展、科学粮食政策制定、粮食安全维护都至关重要。传统的经验模型、光能利用率模型等估产模型原理简单&#xff0c;数据容易获取&#xff0c;但是…

Unittest自动化测试框架vs Pytest自动化测试框架

引言   前面一篇文章Python单元测试框架介绍已经介绍了python单元测试框架&#xff0c;大家平时经常使用的是unittest&#xff0c;因为它比较基础&#xff0c;并且可以进行二次开发&#xff0c;如果你的开发水平很高&#xff0c;集成开发自动化测试平台也是可以的。而这篇文章…