当前位置: 首页 > news >正文

智慧农业农业智能诊断、植物保护 葡萄叶片病害分割数据集 基于 PyTorch + Torchvision 的 DeepLabV3+ 训练葡萄叶片分割数据集

智慧农业农业智能诊断、植物保护 葡萄叶片病害分割数据集

🍇 葡萄叶片病害分割数据集概览表

项目指标详细数据/描述
数据集名称葡萄叶片病害分割数据集 (Grape Leaf Disease Segmentation Dataset)
图像总数15,034张 (含数据增强)
图像分辨率1200 × 1600 像素
标注格式支持YOLOVOC(XML) 两种格式
任务类型实例分割 / 语义分割

📊 数据集划分统计

数据集子集图像数量 (Images)占比 (约)用途
训练集 (Train)12,12180.6%模型训练与参数学习
验证集 (Val)1,4559.7%超参数调整与模型选择
测试集 (Test)1,4589.7%最终模型性能评估
总计15,034100%-

🦠 病害类别与标注详情

类别 ID英文名称中文名称图像数量(含该类的图片数)标注数量(实例/掩膜总数)平均每图标注数病害特征描述
0Healthy健康叶片7,74654,070~6.98无病斑,叶片状态良好 (通常指健康区域分割)
1Birds_Eye_Rot鸟眼斑腐烂6,15016,084~2.62圆形斑点,中心灰白边缘深褐,形似鸟眼
2Powdery_Mildew白粉病7,94724,593~3.09叶片表面覆盖白色粉状霉层,影响光合作用
-总计/平均-15,034*94,747~6.30-

注:图像数量总和超过15,034是因为单张图片可能同时包含多种病害或同时包含健康与病斑区域(多标签/多实例情况)。


在这里插入图片描述

💡 数据集特点分析

  1. 高分辨率
    • 所有图像均为1200×1600高清分辨率,有利于捕捉细微的病斑纹理(如白粉病的粉末状细节、鸟眼斑的边缘特征)。
  2. 类别平衡性
    • 健康样本最多,作为背景参照。
    • 白粉病图像覆盖面最广 (7,947张),标注实例丰富。
    • 鸟眼斑虽然图像数略少,但标注清晰,适合训练小目标检测能力。
  3. 标注密度
    • 总标注数高达94,747个,平均每张图约有6.3个标注对象,数据密度大,利于模型学习复杂的叶片遮挡和多病斑并发场景。
  4. 格式兼容性
    • 同时提供YOLO(txt) 和VOC(xml) 格式,可无缝对接 YOLOv8/v9/v10-seg、Mask R-CNN、DeepLabV3+ 等主流分割网络。

训练需要特别注意以下几点:

  1. 数据格式转换:DeepLabV3+ 通常使用 PNG 掩膜图(Mask)作为标签,而您提供的是 YOLO (txt) 或 VOC (xml) 格式。需要编写脚本将其转换为单通道的索引色 PNG 图片。
  2. 显存优化:由于原图分辨率高达1200x1600,直接训练会爆显存。必须使用Crop(裁剪)Resize(缩放)策略。代码中将默认采用随机裁剪。
  3. 类别映射:需要将您的3个类别(健康、鸟眼斑、白粉病)映射为 0, 1, 2。

数据预处理脚本基于 PyTorch + Torchvision 的 DeepLabV3+ 训练代码


📂 第一步:项目目录结构准备

建议将数据处理后的文件整理为以下结构(Cityscapes/Pascal VOC 风格):

Grape_Dataset/ ├── images/ # 存放原始 jpg 图片 (train/val/test 子文件夹) ├── masks/ # 存放转换后的 png 掩膜 (train/val/test 子文件夹) ├── splits/ # 存放训练/验证列表 txt 文件 └── train_deeplab.py # 训练脚本

🛠️ 第二步:数据预处理脚本 (VOC/YOLO -> Mask PNG)

DeepLabV3+ 需要标签是单通道图片,像素值代表类别ID(0=背景/健康, 1=鸟眼斑, 2=白粉病)。

注意:如果您的“Healthy”是指整张健康的叶子作为背景,通常在分割任务中,我们只标注病害区域(1和2),健康区域视为背景(0)。如果您的数据中“Healthy”也是被标注出来的多边形,请根据实际需求调整下面的逻辑。以下代码假设:0=背景(含健康叶肉), 1=鸟眼斑, 2=白粉病

importosimportxml.etree.ElementTreeasETimportcv2importnumpyasnpfromPILimportImage# 配置路径DATASET_ROOT='Grape_Dataset_Raw'# 原始数据根目录OUTPUT_ROOT='Grape_Dataset'# 输出目录IMG_DIR=os.path.join(OUTPUT_ROOT,'images')MASK_DIR=os.path.join(OUTPUT_ROOT,'masks')# 类别映射 (根据实际标注调整)# 假设 VOC XML 中的 name 字段如下,若为 YOLO 需解析 txt 坐标CLASS_MAP={'Birds_Eye_Rot':1,'Powdery_Mildew':2,# Healthy 通常不作为前景标注,或者如果标注了,设为 0 或其他,这里假设只分割病害}defcreate_dirs():forsplitin['train','val','test']:os.makedirs(os.path.join(IMG_DIR,split),exist_ok=True)os.makedirs(os.path.join(MASK_DIR,split),exist_ok=True)defvoc_xml_to_mask(xml_path,img_size):"""将 VOC XML 转换为单通道 Mask"""mask=np.zeros(img_size[:2],dtype=np.uint8)tree=ET.parse(xml_path)root=tree.getroot()forobjinroot.findall('object'):name=obj.find('name').textifnamenotinCLASS_MAP:continuebbox=obj.find('bndbox')xmin=int(float(bbox.find('xmin').text))ymin=int(float(bbox.find('ymin').text))xmax=int(float(bbox.find('xmax').text))ymax=int(float(bbox.find('ymax').text))# 简单处理:将边界框内填充为类别ID# 如果是实例分割(YOLO Segmentation),需要解析多边形点坐标绘制 fillPoly# 此处以 VOC 检测框为例演示,若有分割多边形数据,请使用 cv2.fillPolymask[ymin:ymax,xmin:xmax]=CLASS_MAP[name]returnmaskdefprocess_dataset():create_dirs()# 此处仅为示例逻辑,实际需根据您的原始文件结构遍历# 假设原始数据在 Grape_Dataset_Raw/VOCAnnotations 和 Grape_Dataset_Raw/JPEGImages# 模拟划分 (实际请读取您的 train.txt, val.txt)splits={'train':list(range(12121)),'val':list(range(12121,12121+1455)),'test':list(range(12121+1455,15034))}print("⚠️ 请根据实际文件路径修改此函数的遍历逻辑!")print("核心逻辑:读取原图 -> 读取标注(XML/TXT) -> 生成单通道PNG (0,1,2) -> 保存")if__name__=='__main__':process_dataset()

💡 重要提示:如果您拥有的是YOLO 分割格式 (txt containing polygons),需要使用cv2.fillPoly将多边形点画在 mask 上,而不是画矩形框。


🚀 第三步:DeepLabV3+ 训练代码 (PyTorch)

此代码使用了torchvision内置的 DeepLabV3+ (ResNet101 backbone),并针对高分辨率图像做了随机裁剪处理。

importosimporttorchimporttorch.nnasnnimporttorch.utils.dataasdataimporttorchvisionimporttorchvision.transformsasTimporttorchvision.modelsasmodelsimporttorchvision.models.segmentationasmsfromPILimportImageimportnumpyasnpfromtqdmimporttqdmimportmatplotlib.pyplotasplt# ================= 配置区 =================CONFIG={'data_root':'Grape_Dataset','num_classes':3,# 0:Background/Healthy, 1:Birds_Eye, 2:Powdery_Mildew'batch_size':4,# 根据显存调整,1200x1600裁剪后建议 4-8'epochs':50,'lr':0.001,'img_size':512,# 训练时裁剪大小,解决 1200x1600 显存爆炸问题'device':'cuda'iftorch.cuda.is_available()else'cpu','workers':4,}# ================= 数据集类 =================classGrapeSegmentationDataset(data.Dataset):def__init__(self,root,split,img_size):self.root=root self.split=split self.img_size=img_size self.images=[]self.masks=[]img_dir=os.path.join(root,'images',split)mask_dir=os.path.join(root,'masks',split)# 获取所有图片路径forfnameinsorted(os.listdir(img_dir)):iffname.endswith('.jpg')orfname.endswith('.png'):self.images.append(os.path.join(img_dir,fname))# 假设 mask 文件名与图片一致,只是后缀为 .pngmask_fname=os.path.splitext(fname)[0]+'.png'self.masks.append(os.path.join(mask_dir,mask_fname))print(f"Loaded{len(self.images)}images for{split}")def__getitem__(self,index):# 加载图像img=Image.open(self.images[index]).convert('RGB')mask=Image.open(self.masks[index])# 数据增强与变换# 1. 随机裁剪 (解决高分辨率问题)# 2. 随机水平翻转# 3. 转为 Tensor 并归一化transform_img=T.Compose([T.RandomCrop(self.img_size),T.RandomHorizontalFlip(0.5),T.ToTensor(),T.Normalize(mean=[0.485,0.456,0.406],std=[0.229,0.224,0.225]),])transform_mask=T.Compose([T.RandomCrop(self.img_size),T.RandomHorizontalFlip(0.5),])# 注意:为了保持 img 和 mask 同步增强,需要使用相同的随机种子或自定义变换# 这里简化处理,实际生产建议使用 torchvision.transforms.v2 或自定义类# 简易同步裁剪实现:i,j,h,w=T.RandomCrop.get_params(img,output_size=(self.img_size,self.img_size))img=T.functional.crop(img,i,j,h,w)mask=T.functional.crop(mask,i,j,h,w)ifnp.random.rand()>0.5:img=T.functional.hflip(img)mask=T.functional.hflip(mask)img=T.ToTensor()(img)img=T.Normalize(mean=[0.485,0.456,0.406],std=[0.229,0.224,0.225])(img)mask=T.ToTensor()(mask).squeeze(0).long()# Mask 不需要归一化,只需转为 Longreturnimg,maskdef__len__(self):returnlen(self.images)# ================= 模型构建 =================defget_model(num_classes):# 加载预训练的 DeepLabV3+ (ResNet101 backbone)model=ms.deeplabv3_resnet101(pretrained=True,progress=True)# 修改分类器头以适配我们的类别数 (3类)# 原来的辅助分类器也需修改model.classifier[4]=nn.Conv2d(256,num_classes,kernel_size=1)model.aux_classifier[4]=nn.Conv2d(256,num_classes,kernel_size=1)returnmodel# ================= 训练函数 =================deftrain():device=CONFIG['device']print(f"Using device:{device}")# 1. 加载数据train_dataset=GrapeSegmentationDataset(CONFIG['data_root'],'train',CONFIG['img_size'])val_dataset=GrapeSegmentationDataset(CONFIG['data_root'],'val',CONFIG['img_size'])train_loader=data.DataLoader(train_dataset,batch_size=CONFIG['batch_size'],shuffle=True,num_workers=CONFIG['workers'])val_loader=data.DataLoader(val_dataset,batch_size=CONFIG['batch_size'],shuffle=False,num_workers=CONFIG['workers'])# 2. 初始化模型model=get_model(CONFIG['num_classes']).to(device)# 3. 损失函数 (忽略背景0?如果不忽略则不要 ignore_index)# 通常分割任务中,如果背景也是类别之一,则不忽略。如果只想训练病害,可设置 ignore_index=0criterion=nn.CrossEntropyLoss(ignore_index=-1)# 这里假设所有像素都有标签,不设忽略# 4. 优化器optimizer=torch.optim.SGD(model.parameters(),lr=CONFIG['lr'],momentum=0.9,weight_decay=1e-4)scheduler=torch.optim.lr_scheduler.StepLR(optimizer,step_size=10,gamma=0.1)best_miou=0.0# 5. 训练循环forepochinrange(CONFIG['epochs']):model.train()running_loss=0.0pbar=tqdm(train_loader,desc=f"Epoch{epoch+1}/{CONFIG['epochs']}")forimages,masksinpbar:images,masks=images.to(device),masks.to(device)optimizer.zero_grad()outputs=model(images)['out']# DeepLabV3+ 输出字典loss=criterion(outputs,masks)loss.backward()optimizer.step()running_loss+=loss.item()pbar.set_postfix({'loss':f"{loss.item():.4f}"})avg_loss=running_loss/len(train_loader)scheduler.step()# 验证阶段 (简化版,仅计算 Loss)model.eval()val_loss=0.0withtorch.no_grad():forimages,masksinval_loader:images,masks=images.to(device),masks.to(device)outputs=model(images)['out']loss=criterion(outputs,masks)val_loss+=loss.item()print(f"Epoch{epoch+1}: Train Loss={avg_loss:.4f}, Val Loss={val_loss/len(val_loader):.4f}")# 保存最佳模型if(val_loss/len(val_loader))<best_miouorepoch==0:# 这里用 Loss 代替 mIoU 做简单判断best_miou=val_loss/len(val_loader)torch.save(model.state_dict(),f"deeplabv3_grape_epoch{epoch+1}.pth")print(f"✅ Model saved at epoch{epoch+1}")print("🎉 Training Finished!")if__name__=='__main__':train()

💡 关键训练技巧与建议

  1. 关于高分辨率 (1200x1600)

    • 不要直接将图片 Resize 到 512x512 进行训练,这会导致细小的“鸟眼斑”丢失。
    • 推荐做法:代码中使用了RandomCrop(512)。这样既保留了原始高分辨率的细节,又适应了 GPU 显存限制。
    • 推理时:可以使用Sliding Window Inference (滑动窗口推理),将大图切块预测后再拼接,或者直接 Resize 后预测(如果精度要求不高)。
  2. 类别不平衡

    • 葡萄叶片中,背景(健康部分)通常占据 90% 以上的像素,病害区域很小。
    • 解决方案:在CrossEntropyLoss中添加weight参数。
    # 计算各类别频率后,给少数类更高的权重class_weights=torch.tensor([0.1,1.0,1.0]).to(device)# 示例:降低背景权重criterion=nn.CrossEntropyLoss(weight=class_weights)
  3. 评价指标 (mIoU)

    • 上面的代码为了简洁只计算了 Loss。正式训练建议引入torchmetrics库来计算Mean IoU,这是分割任务最核心的指标。
    pipinstalltorchmetrics

    在代码中加入MeanIoU(num_classes=3)并在验证集更新。

  4. 数据增强

    • 除了随机裁剪和翻转,还可以加入ColorJitter(亮度、对比度变化) 来模拟不同光照下的葡萄园环境,提高模型鲁棒性。
http://www.cnnetsun.cn/news/1344725.html

相关文章:

  • 移动端适配的隐藏坑:这5个错误90%的人在犯
  • 【已解决】java文件未被识别 显示咖啡杯图标
  • Python学习路线图(如果你计划快速的学习掌握python)
  • 文献检索如何限制文献类型(期刊 / 会议 / 综述)?3 个技巧让结果更精准
  • 欧姆龙FinsUdp协议报文例子
  • 【最新版】2026年OpenClaw阿里云5分钟搭建及使用保姆级教程
  • 文华财经精准识别高低点20日均线 + 极值标注指标编写教程
  • Java入门第154课——Oracle 数据库入门与基础操作
  • 2026年中秋手抄报模板返工一次后,我总结的模板筛选与修改经验
  • 在AI Agent爆发前夜,我们必须对自己进行一次“颠覆”
  • Kotlin的reified泛型:在运行时保留类型信息
  • 代差级突破|2026 女性经期新选择:专属特殊膳食饮品深度评测
  • android studio安装及配置jdk17
  • Git误删急救:30秒拯救你的代码
  • PhpStorm下载、安装、配置教程
  • Redis值数据类型——sorted set
  • 双令牌机制:让认证更安全、体验更流畅
  • 互联网大厂Java面试实战:以智慧物流场景为例深入探讨Spring Boot、微服务与Redis缓存
  • Metasploitable靶机的下载与安装
  • Claude国内镜像站实测:可扩展监督与宪法AI,推理架构的范式革命
  • OpenClaw 高效配置与集成指南:从模型选择到 API 对接
  • 游戏盾终极奥义:湘情盾“源站隐身”与“报文基因”实战解析
  • Claude Code超详细完整指南附最佳实践(最新版2026年,可接入GLM)
  • 3.17打卡day31
  • KIHU快狐|带API协议户外液晶显示器支持第三方平台对接
  • 测试学习Day6——Linux和数据库(1)
  • 2026年DeepSeek写的论文AI率太高?3款降AI工具实测推荐
  • Gradle框架:现代软件构建的灵活引擎
  • 二氢视黄醛价格
  • 解决AI模型版本同步问题:架构师的3套方案