30  计算机视觉分析实验图片

本项目以发表在 Plant Phenomics 杂志上的一篇论文为例 (Serouart et al. 2022) ,讲述如何使用一种名为 SegVeg 的两阶段语义分割方法,将高分辨率 RGB 图像分割成背景、绿色植被和衰老植被三类。以用来评估植被的生长状态。

30.1 研究内容简介

30.1.1 研究背景

植被覆盖度(Vegetation Fraction, VF)是描述作物状态和产量的重要指标,但绿色植被覆盖度(Green Fraction, GF)更能反映作物的功能特性。GF 用于估算绿色面积指数(GAI),而衰老植被覆盖度(Senescent Fraction, SF)则用于表征生物或非生物胁迫、营养循环和老化过程。当前的遥感方法在估计这些参数时面临一些挑战,特别是在高分辨率 RGB 图像中准确分割绿色和衰老植被。

30.1.2 研究目的

本文旨在开发一种名为 SegVeg 的两阶段语义分割方法,该方法结合深度学习和浅层学习技术,将高分辨率 RGB 图像分割成背景、绿色植被和衰老植被三类。SegVeg 方法的目标是减少手动标注的工作量,同时保持较高的分割精度。

30.1.3 研究方法

SegVeg 方法分为两个阶段: 1. 使用 U-net 模型将图像分为植被和背景。 2. 使用支持向量机(SVM)将植被像素进一步分为绿色和衰老植被。

30.1.4 数据来源与实验设计

  • 数据集:
    • Dataset #1: 包含 8 个子数据集,总共 2015 个 512x512 像素的补丁,用于训练 U-net 2C 模型。
    • Dataset #2: 包含 441 个带有网格注释的图像,用于训练 SVM 模型。
    • Dataset #3: 使用 SegVeg 方法生成的完全自动注释的补丁,用于训练 3 类 U-net 模型(U-net 3C)。

30.1.5 实验设计

  • 第一阶段:
    • 使用 U-net 模型将图像分为植被和背景。
    • 使用 EfficientNet-B2 架构作为骨干网络,通过 Dice loss 函数和 Adam 优化器进行训练。
  • 第二阶段:
    • 使用 SVM 对植被像素进行分类,使用多个颜色空间和变换(如 RGB、HSV、CIELab 等)作为输入特征。
    • 通过前向包装方法选择最合适的输入特征,并使用网格搜索算法调整超参数。

30.1.6 核心发现

  • SegVeg 方法能够准确地将图像分割为背景、绿色植被和衰老植被三类。在分割绿色和衰老植被方面表现出较好的性能。
  • 在某些情况下,背景和衰老植被之间存在混淆,尤其是在图像的暗区和亮区。光照条件对分割结果有显著影响。
  • U-net 3C 模型的表现与 SegVeg 方法相似,但在绿色植被的分割上略有下降。

30.2 环境依赖

本项目需要下列 Python 库的支持:

  • datasets:用于从 Huggingface 加载数据集
  • PyTorch Lightning: 用于构建神经网络模型和训练循环。
  • Segmentation Models PyTorch: 提供了许多预训练的图像分割模型,如 U-net、DeepLabV3、PSPNet 等。
  • OpenCV: 用于图像处理和可视化。
  • Matplotlib: 用于绘制图表和图像。
  • 其他库: 用于数据处理、评估指标计算等。

30.3 数据集

VegAnn 是一个包含 3,775 张多作物 RGB 图像的集合,旨在增强作物植被分割研究。

  • VegAnn 数据集包含 3775 张图片
  • 图片尺寸为 512*512 像素
  • 对应的二值掩膜中,0 表示土壤和作物残留物(背景),255 表示植被(前景)
  • 该数据集包含 26 种以上作物物种,各物种的图片数量分布不均匀
  • VegAnn 数据集由使用不同采集系统和配置拍摄的户外图像编译而成

VegAnn 项目的数据集可以在 Huggingface 上访问。请参阅以下链接:https://huggingface.co/datasets/simonMadec/VegAnn。数据预处理的步骤参见 数据预处理

数据来源与组成

  • Dataset #1
    • 内容:2015个512×512像素的RGB图像块,覆盖8个子数据集(UTokyo、P2S2、Wuhan、CVPPP、GEVES、Phenofix、Phenomobile、Bonirob)。
    • 标注:手动标注为“植被(绿色+衰老)”和“背景”。
    • 多样性:涵盖水稻、小麦、玉米、棉花等多种作物,不同生长阶段(营养期至衰老期),以及不同光照条件和土壤背景。
    • 空间分辨率:0.3–2 mm,确保细节捕捉。
  • Dataset #2
    • 内容:441张512×512像素图像,通过规则网格(8–11像素间隔)标注像素点,共19,738个标注像素(训练集6132,测试集13,606)。
    • 标注类别:绿色植被、衰老植被、背景、不确定像素(未用于训练)。
    • 子数据集:LITERAL(手持设备)、PHENOMOBILE(无人车+闪光灯)、P2S2(多作物多平台)。
    • 挑战:不确定像素(16%)主要因光照过暗/过亮或混合像素导致。
  • Dataset #3
    • 生成方式:通过SegVeg对Dataset #1图像生成伪标签(3类:背景、绿色、衰老),用于训练U-net 3C模型。

数据特点与局限

  • 优势:覆盖广泛作物、生长阶段和光照条件,增强模型泛化能力。
  • 局限
    • 标注不一致性:不同操作者对“衰老”与“背景”的主观判断差异。
    • 不确定像素处理:排除部分像素可能导致模型对极端光照场景适应不足。

30.4 模型架构

作者自定义了一个 VegAnnModel 类。这个类初始化一个 U-net 模型,使用 ResNet34 作为编码器,输入通道数为 3(RGB 图像),输出类别数为 1(二值分割)。

SegVeg两阶段模型

  1. 第一阶段(U-net 2C)
    • 架构:基于EfficientNet-B2骨干网络(ImageNet预训练),编码器-解码器结构,输出植被与背景二分类分割。
    • 输入/输出:512×512 RGB图像 → 二值掩膜(植被 vs 背景)。
  2. 第二阶段(SVM分类器)
    • 输入特征:从植被像素中提取14个颜色空间特征(R、G、B、H、S、a、b、GE、M、YE、Cb、Cr、I、Q),通过前向选择法筛选。
    • 分类目标:绿色植被 vs 衰老植被。
    • 超参数:RBF核,C=1,γ=10⁻³,通过网格搜索优化。

对比模型(U-net 3C)

  • 架构:与U-net 2C相同,但输出三分类(背景、绿色、衰老)。
  • 训练数据:使用SegVeg生成的伪标签(弱监督),未依赖手动标注的全类别掩膜。

30.5 训练过程

U-net 2C训练细节

  • 损失函数:Dice Loss(优化分割边界)。
  • 优化器:Adam,初始学习率0.01,逐步降至10⁻⁶。
  • 数据增强:Albumentations库(旋转、缩放、翻转等),提升模型鲁棒性。
  • 硬件:NVIDIA GeForce RTX 3090 GPU,批大小32。

SVM训练细节

  • 特征归一化:标准化颜色空间特征以消除量纲差异。
  • 交叉验证:留一法验证,确保泛化能力。

U-net 3C训练

  • 弱监督策略:利用SegVeg生成的伪标签,减少人工标注成本。
  • 潜在问题:伪标签误差可能传播至U-net 3C训练中。

30.6 预测性能

SegVeg性能

  • 像素级精度
    • 绿色植被:F1=94%(R²=0.94),背景:F1=73%(R²=0.73),衰老植被:F1=70%(R²=0.70)。
    • 主要混淆:衰老植被与背景(尤其暗区/高光区)。
  • 图像级分数预测
    • 绿色分数误差:1%(95%置信区间),衰老和背景误差:2.1–2.7%。

U-net 3C性能对比

  • 像素级精度:与SegVeg接近,但绿色植被F1略低(90% vs 94%)。
  • 细节处理:卷积特性导致边缘模糊,而SVM像素分类更清晰(如图像细小结构保留)。

30.7 结果分析

优点

  1. 标注效率:两阶段设计减少全图像标注需求(仅需部分像素标注)。
  2. 颜色空间融合:SVM结合多颜色空间特征(如CMYK、YIQ),提升衰老植被区分能力。
  3. 适用性:公开代码与预训练模型(GitHub),支持快速部署。

缺点

  1. 光照敏感性:暗区/高光区分类性能下降(如PHENOMOBILE数据集因闪光灯导致暗区混淆)。
  2. 颜色连续性挑战:衰老早期与绿色植被颜色过渡区域易误判。
  3. 弱监督限制:U-net 3C依赖SegVeg伪标签,可能继承其误差。

30.8 预测性能

import torch
import cv2
import numpy as np
import matplotlib.pyplot as plt

# Load the model - 统一使用一种加载方式
ckt_path = "data/vegann/epoch5.ckpt"
checkpoint = torch.load(ckt_path, map_location=torch.device('cpu'))
model = VegAnnModel("Unet","resnet34",in_channels = 3, out_classes=1 )
model.load_state_dict(checkpoint["state_dict"])
# 只保留一个预处理函数
preprocess_input = get_preprocessing_fn('resnet34', pretrained='imagenet')
model.eval()
imname = "data/vegann/test.jpg"

image = cv2.imread(imname)
im = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# 使用已定义的预处理函数
image = preprocess_input(im)
image = image.astype('float32')


inputs = torch.tensor(image) # , dtype=float
# print(inputs.size)
inputs = inputs.permute(2,0,1)
inputs = inputs[None,:,:,:]
# print(inputs.shape)
logits = model(inputs)
pr_mask = logits.sigmoid()

pred = (pr_mask > 0.5).numpy().astype(np.uint8) 

im1_pred = colorTransform_VegGround(im,pred,0.8,0.2)
im2_pred = colorTransform_VegGround(im,pred,0.2,0.8)

fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.imshow(im)
ax1.set_title("Input Image")

ax2.imshow(im2_pred)
ax2.set_title("Prediction")
plt.show()