PyTorch实战:用Transformer搞定无人机影像中的农作物识别(附完整代码)

去年夏天,我在一个农业科技公司的项目里遇到了一个棘手的问题:他们用无人机拍摄了上千亩的玉米田影像,希望我们能自动识别出不同生长阶段的玉米植株,并估算出每块区域的植株密度。最初我们尝试了传统的CNN模型,比如ResNet和U-Net,但在处理那些边缘模糊、光照变化剧烈的航拍图像时,效果总是不尽如人意。模型要么把田埂误判成作物,要么对幼苗期的稀疏植株视而不见。直到我们把目光转向了Transformer架构,局面才真正打开。

你可能已经听说过Transformer在自然语言处理领域的统治地位,但它在计算机视觉,特别是遥感影像分析上的潜力,可能比你想象的还要大。传统的卷积神经网络(CNN)依靠局部感受野来提取特征,这在处理纹理规则的自然图像时很有效,但面对无人机拍摄的农田影像——那些作物排列看似随机、尺度变化极大、背景复杂的场景——CNN的局部归纳偏置反而成了限制。Transformer的自注意力机制能够捕捉图像中任意两个像素之间的长程依赖关系,这让它特别擅长理解整片农田的全局布局和上下文信息。

这篇文章就是为你准备的,如果你是一位智慧农业领域的算法工程师、农业科技公司的技术负责人,或者是对AI+农业交叉应用感兴趣的研究者,想要一个即拿即用、可落地的解决方案,而不仅仅是理论探讨。我会带你从零开始,构建一个基于PyTorch和Transformer的轻量化农作物识别模型,重点解决小样本训练、数据增强策略、模型部署优化这些实际工程中真正卡脖子的问题。文末会提供完整的、可运行的代码仓库链接。

1. 为什么是Transformer?重新思考无人机影像的识别难题

在深入代码之前,我们有必要先厘清一个根本问题:为什么在农作物识别这个特定任务上,Transformer可能比CNN更合适?这得从无人机影像的特点说起。

无人机通常在低空飞行(几十米到几百米),拍摄的影像分辨率很高,但随之而来的是一系列挑战:

  • 尺度多样性极大:同一张图片里,近处的作物叶片纹理清晰,远处的则可能只是几个像素点。
  • 光照与阴影影响显著:不同时间、不同天气下拍摄,颜色和对比度差异巨大。
  • 背景复杂:农田中混杂着土壤、田埂、灌溉设施、杂草,甚至还有农机具的影子。
  • 类内差异大,类间差异小:不同健康状态的同种作物(如受旱与正常)可能看起来差别很大,而某些杂草在苗期可能与作物幼苗极为相似。

CNN的卷积核在固定尺寸的局部窗口内操作,虽然通过堆叠层数可以扩大感受野,但这种对长距离依赖的建模是间接且低效的。而Transformer的核心——自注意力机制,允许模型直接计算图像中所有位置(patch)之间的关系权重。这意味着,模型可以同时“看到”图像左上角的一株玉米和右下角的一株玉米,并判断它们是否属于同一种类别、处于何种生长阶段,这对于理解作物的行距、种植密度、病害传播模式至关重要。

注意:这里说的Transformer通常指Vision Transformer (ViT)及其变种。ViT将图像分割成固定大小的图像块(patch),将每个块线性投影为序列向量,然后送入标准的Transformer编码器进行处理。这种处理方式完全放弃了卷积,纯粹依赖注意力机制。

为了更直观地对比,我们来看一个简单的特性对照表:

特性维度卷积神经网络 (CNN)Vision Transformer (ViT)
核心操作局部卷积、池化全局自注意力
归纳偏置平移不变性、局部性较少,更依赖数据
感受野随网络深度增大从一开始就是全局的
数据需求相对较少,可利用预训练通常需要更多数据,或更强的数据增强
计算复杂度与图像尺寸呈线性或平方关系与序列长度(patch数)的平方成正比
对遮挡/噪声相对鲁棒可能更敏感,但上下文建模能力强
在无人机影像上的优势纹理特征提取强,部署成熟长程依赖建模强,对不规则排列、尺度变化适应更好

从表格可以看出,Transformer并非全面碾压CNN,它的数据饥渴性和计算开销是众所周知的挑战。这也引出了我们实战中的第一个核心策略:如何为Transformer设计一个轻量化的、适合小样本农业数据集的架构? 我们不会直接使用原始的、参数庞大的ViT-Base或ViT-Large,而是会采用一种混合架构或更小巧的变体。

2. 实战准备:数据、环境与一个轻量化的Transformer模型

我们的目标是识别无人机影像中的农作物,假设任务是一个语义分割任务——即为图像中的每一个像素点分类,比如分为“玉米”、“大豆”、“土壤”、“杂草”、“背景”等。我们选择PyTorch作为框架,因为它动态图的特点非常适合研究和快速迭代。

2.1 数据准备与增强:针对农业影像的“特效药”

农业数据集往往规模有限,标注成本高昂。因此,数据增强不是可选项,而是必选项。除了通用的旋转、翻转、裁剪,我们需要针对农业影像的特点设计增强策略。

  1. 多光谱/颜色增强:无人机影像常常包含近红外等波段。即使我们只用RGB,模拟不同光照条件也至关重要。我们可以使用albumentations库,它比torchvision的transform更强大。

    import albumentations as A
    from albumentations.pytorch import ToTensorV2
    
    def get_train_transform():
        return A.Compose([
            A.RandomRotate90(p=0.5),
            A.Flip(p=0.5),
            # 模拟光照变化:随机调整亮度、对比度、饱和度
            A.RandomBrightnessContrast(brightness_limit=0.2, contrast_limit=0.2, p=0.75),
            A.HueSaturationValue(hue_shift_limit=10, sat_shift_limit=20, val_shift_limit=10, p=0.5),
            # 模拟云雾或噪声
            A.RandomGamma(gamma_limit=(80, 120), p=0.3),
            A.GaussNoise(var_limit=(10.0, 50.0), p=0.2),
            # 针对农田:随机模拟作物缺失(模拟病害或稀疏区域)
            A.CoarseDropout(max_holes=8, max_height=32, max_width=32, fill_value=0, p=0.3),
            # 归一化并转换Tensor
            A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
            ToTensorV2(),
        ])
    
  2. 标签自适应:在进行空间变换(如旋转、弹性形变)时,必须同步处理标注的掩码(mask)。albumentations可以很好地处理这一点。

  3. 构建DataLoader:我们将创建一个继承自torch.utils.data.Dataset的类,来读取图像-掩码对,并应用上述增强。

2.2 模型选择:SegFormer——为语义分割而生的高效Transformer

直接使用ViT做密集预测(如分割)需要处理高分辨率特征图,计算量巨大。因此,我们选择SegFormer这个模型。它由MIT的华人研究者提出,核心创新在于:

  • 分层Transformer编码器:像CNN一样输出多尺度特征(1/4, 1/8, 1/16, 1/32分辨率),这对分割任务至关重要。
  • 轻量化的注意力机制:使用了混合注意力,在计算效率和性能间取得了很好的平衡。
  • 简单的MLP解码器:无需复杂的FPN或U-Net式跳跃连接,仅通过MLP层融合多尺度特征,简单高效。

我们将采用segformer的轻量级版本MiT-B0作为编码器。首先安装必要的库:

pip install torch torchvision
pip install mmsegmentation  # 或者使用timm库中的segformer实现

如果使用timm库(推荐,更轻便):

pip install timm

下面是我们构建SegFormer模型的核心代码块:

import torch
import torch.nn as nn
import torch.nn.functional as F
from timm.models.vision_transformer import VisionTransformer
# 注意:timm可能没有直接导出SegFormer,这里我们简化实现其核心思想
# 实际项目中,可以考虑使用开源实现,如 https://github.com/NVlabs/SegFormer

class MixFFN(nn.Module):
    """ SegFormer中使用的混合FFN,包含深度卷积 """
    def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.):
        super().__init__()
        out_features = out_features or in_features
        hidden_features = hidden_features or in_features
        self.fc1 = nn.Linear(in_features, hidden_features)
        self.dwconv = nn.Conv2d(hidden_features, hidden_features, 3, 1, 1, groups=hidden_features) # 深度卷积
        self.act = act_layer()
        self.fc2 = nn.Linear(hidden_features, out_features)
        self.drop = nn.Dropout(drop)

    def forward(self, x, H, W):
        # x: (B, N, C)
        B, N, C = x.shape
        x = self.fc1(x)
        x = x.transpose(1, 2).view(B, C, H, W) # 重塑为2D进行卷积
        x = self.dwconv(x)
        x = x.flatten(2).transpose(1, 2) # 恢复序列
        x = self.act(x)
        x = self.drop(x)
        x = self.fc2(x)
        x = self.drop(x)
        return x

class EfficientSelfAttention(nn.Module):
    """ 一种简化版的高效自注意力,通过降维减少计算量 """
    def __init__(self, dim, num_heads=8, qkv_bias=False, attn_drop=0., proj_drop=0., sr_ratio=1):
        super().__init__()
        # 省略了序列缩减(SR)的实现以保持简洁
        self.num_heads = num_heads
        self.scale = (dim // num_heads) ** -0.5
        self.q = nn.Linear(dim, dim, bias=qkv_bias)
        self.kv = nn.Linear(dim, dim * 2, bias=qkv_bias)
        self.attn_drop = nn.Dropout(attn_drop)
        self.proj = nn.Linear(dim, dim)
        self.proj_drop = nn.Dropout(proj_drop)

    def forward(self, x, H, W):
        B, N, C = x.shape
        q = self.q(x).reshape(B, N, self.num_heads, C // self.num_heads).permute(0, 2, 1, 3)
        # 简化处理,实际SegFormer的KV有序列缩减
        kv = self.kv(x).reshape(B, -1, 2, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
        k, v = kv[0], kv[1]
        attn = (q @ k.transpose(-2, -1)) * self.scale
        attn = attn.softmax(dim=-1)
        attn = self.attn_drop(attn)
        x = (attn @ v).transpose(1, 2).reshape(B, N, C)
        x = self.proj(x)
        x = self.proj_drop(x)
        return x

class TransformerBlock(nn.Module):
    """ SegFormer编码器的基础块 """
    def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, drop=0., attn_drop=0.,
                 act_layer=nn.GELU, norm_layer=nn.LayerNorm):
        super().__init__()
        self.norm1 = norm_layer(dim)
        self.attn = EfficientSelfAttention(dim, num_heads=num_heads, qkv_bias=qkv_bias,
                                          attn_drop=attn_drop, proj_drop=drop)
        self.norm2 = norm_layer(dim)
        mlp_hidden_dim = int(dim * mlp_ratio)
        self.mlp = MixFFN(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop)

    def forward(self, x, H, W):
        x = x + self.attn(self.norm1(x), H, W)
        x = x + self.mlp(self.norm2(x), H, W)
        return x

# 由于完整实现SegFormer较复杂,在实际项目中,我们可以直接使用timm中已有的模型或引用开源实现。
# 以下是一个使用预训练模型构建分割头的示意性流程:
import timm
class SegFormerForAgriculture(nn.Module):
    def __init__(self, num_classes=5, pretrained=True):
        super().__init__()
        # 加载timm中的预训练MixTransformer (MiT) backbone
        self.backbone = timm.create_model('mit_b0', pretrained=pretrained, features_only=True)
        # 获取backbone各阶段的通道数
        feature_channels = self.backbone.feature_info.channels() # 例如 [32, 64, 160, 256]
        # 构建一个简单的全MLP解码器(仿SegFormer)
        self.linear_fuse = nn.Conv2d(sum(feature_channels), 256, kernel_size=1)
        self.linear_pred = nn.Conv2d(256, num_classes, kernel_size=1)
        # 上采样到原图尺寸的卷积
        self.up_sample = nn.Upsample(scale_factor=4, mode='bilinear', align_corners=False)

    def forward(self, x):
        # 提取多尺度特征
        features = self.backbone(x) # 返回一个list,包含4个不同尺度的特征图
        # 将特征图统一上采样到1/4尺寸并拼接
        H, W = x.shape[2] // 4, x.shape[3] // 4
        features = [F.interpolate(f, size=(H, W), mode='bilinear', align_corners=False) for f in features]
        x = torch.cat(features, dim=1)
        x = self.linear_fuse(x)
        x = self.linear_pred(x)
        x = self.up_sample(x) # 上采样回原图尺寸
        return x

这个SegFormerForAgriculture类定义了我们用于农作物分割的模型主干。它利用timm库中预训练的mit_b0模型作为编码器,这是一个非常轻量化的Transformer架构,参数约3.7M,非常适合在资源受限的边缘设备或需要快速迭代的实验中使用。

3. 训练策略:让小样本数据喂饱Transformer

Transformer模型通常需要海量数据,但我们的农业数据集可能只有几百或几千张标注图像。如何克服这个矛盾?这里分享三个经过验证的策略。

策略一:利用大规模自然图像预训练权重 这是最关键的一步。我们使用的mit_b0模型已经在ImageNet-1K或更大的数据集上预训练过。这相当于让模型先学会了识别边缘、纹理、形状等通用视觉特征,我们只需要在其基础上进行“微调”,让它适应农作物这个特定领域。在PyTorch中,通过pretrained=True参数即可轻松加载。

策略二:渐进式微调与分层学习率 不要一上来就更新所有参数。我们可以采用分层设置学习率的策略:Transformer编码器的深层(靠近输入)参数,学习率设置得小一些,因为这些层提取的是通用特征;而解码器和最后的分类层,学习率可以设置得大一些,因为它们需要快速适应新任务。

from torch.optim import AdamW

model = SegFormerForAgriculture(num_classes=5, pretrained=True)
param_groups = [
    {'params': model.backbone.parameters(), 'lr': 1e-5}, # backbone学习率小
    {'params': model.linear_fuse.parameters(), 'lr': 1e-4},
    {'params': model.linear_pred.parameters(), 'lr': 1e-4},
]
optimizer = AdamW(param_groups, weight_decay=1e-4)

策略三:强正则化与早停 为了防止在小数据集上过拟合,我们必须加强正则化。

  • 权重衰减:如上代码中的weight_decay。
  • Dropout:在Transformer块和MLP中使用Dropout。
  • 标签平滑:在计算损失时使用标签平滑,防止模型对训练数据过于自信。
  • 早停:密切监控验证集损失,当其在连续多个epoch不再下降时,停止训练。

损失函数我们选择在分割任务中表现稳健的交叉熵损失与Dice损失的组合。Dice损失特别适用于处理前景(作物)和背景(土壤)像素数量不平衡的情况。

class CombinedLoss(nn.Module):
    def __init__(self, alpha=0.5):
        super().__init__()
        self.alpha = alpha
        self.ce_loss = nn.CrossEntropyLoss(ignore_index=255, label_smoothing=0.1) # label_smoothing是标签平滑
        # Dice Loss实现
    def dice_loss(self, pred, target):
        smooth = 1.
        pred = F.softmax(pred, dim=1)
        target_one_hot = F.one_hot(target, num_classes=pred.shape[1]).permute(0, 3, 1, 2).float()
        intersection = (pred * target_one_hot).sum(dim=(2,3))
        union = pred.sum(dim=(2,3)) + target_one_hot.sum(dim=(2,3))
        dice = (2. * intersection + smooth) / (union + smooth)
        return 1 - dice.mean()

    def forward(self, pred, target):
        ce = self.ce_loss(pred, target)
        dice = self.dice_loss(pred, target)
        return self.alpha * ce + (1 - self.alpha) * dice

4. 部署优化:让模型在田间地头跑起来

模型训练好了,精度也不错,但怎么把它部署到实际的农业巡检系统里?无人机机载计算机或边缘计算盒子的计算资源通常有限。这里有几个关键的优化方向。

4.1 模型量化 将模型参数从32位浮点数(FP32)转换为8位整数(INT8),可以显著减少模型体积和内存占用,并提升推理速度,而精度损失通常很小。PyTorch提供了方便的量化API。

# 动态量化(后训练量化,最简单)
import torch.quantization
quantized_model = torch.quantization.quantize_dynamic(
    model, {torch.nn.Linear, torch.nn.Conv2d}, dtype=torch.qint8
)
# 保存量化后的模型
torch.jit.save(torch.jit.script(quantized_model), 'quantized_agriculture_model.pth')

4.2 使用TensorRT或ONNX Runtime加速 对于NVIDIA的硬件,可以将PyTorch模型导出为ONNX格式,然后用TensorRT进行优化和推理,能获得数倍的加速比。对于其他硬件,ONNX Runtime也是一个高性能的推理引擎。

# 导出为ONNX格式(简化示例)
dummy_input = torch.randn(1, 3, 512, 512).to(device)
torch.onnx.export(model, dummy_input, "agriculture_model.onnx",
                  input_names=['input'], output_names=['output'],
                  dynamic_axes={'input': {0: 'batch_size'}, 'output': {0: 'batch_size'}})

4.3 设计高效的推理流水线 无人机影像通常很大(如4000x3000像素),直接输入网络不现实。我们需要采用滑动窗口或分块推理的策略。

  1. 将大图分割成重叠的小块(如512x512)。
  2. 对每个小块进行推理。
  3. 将小块的预测结果拼接回原图尺寸,重叠区域可以采用加权平均来平滑边缘。

这个过程需要注意内存管理和并行计算,以提升吞吐量。下面是一个简单的分块推理函数框架:

def inference_large_image(model, large_img, patch_size=512, overlap=64):
    """
    对大图像进行分块推理
    Args:
        model: 训练好的模型
        large_img: 输入大图像,形状为 (C, H, W)
        patch_size: 分块大小
        overlap: 块之间的重叠像素,用于减少拼接缝隙
    Returns:
        整张图的预测结果 (H, W)
    """
    C, H, W = large_img.shape
    output = torch.zeros((model.num_classes, H, W), device=large_img.device)
    count = torch.zeros((H, W), device=large_img.device)

    stride = patch_size - overlap
    for y in range(0, H, stride):
        for x in range(0, W, stride):
            # 计算当前块的坐标,防止越界
            y1, y2 = y, min(y + patch_size, H)
            x1, x2 = x, min(x + patch_size, W)
            patch = large_img[:, y1:y2, x1:x2]

            # 如果块大小不足,进行填充
            if patch.shape[1] < patch_size or patch.shape[2] < patch_size:
                pad_h = patch_size - patch.shape[1]
                pad_w = patch_size - patch.shape[2]
                patch = F.pad(patch, (0, pad_w, 0, pad_h), mode='constant', value=0)

            # 推理
            with torch.no_grad():
                pred_patch = model(patch.unsqueeze(0)).squeeze(0) # (C, patch_size, patch_size)

            # 将预测结果放回对应位置,并累加计数(用于平均)
            output[:, y1:y1+patch.shape[1], x1:x1+patch.shape[2]] += pred_patch[:, :y2-y1, :x2-x1]
            count[y1:y2, x1:x2] += 1

    # 平均重叠区域的预测
    output = output / count.unsqueeze(0).clamp(min=1)
    final_pred = output.argmax(dim=0) # 得到最终的类别索引图
    return final_pred.cpu().numpy()

4.4 模型轻量化备选方案 如果MiT-B0在目标设备上仍然运行缓慢,可以考虑更极致的轻量化方案:

  • 知识蒸馏:用一个大的“教师模型”指导一个小的“学生模型”训练,让学生模型模仿教师模型的输出。
  • 神经架构搜索:自动搜索适合特定硬件和任务的最优小模型。
  • 使用MobileViT等移动端Transformer:专门为移动设备设计的Transformer变体,在精度和速度间取得了更好的平衡。

5. 超越识别:从像素到业务洞察

准确识别出农作物只是第一步。在智慧农业的实际应用中,我们需要从识别结果中提炼出有价值的业务指标。这里提供几个思路和代码片段。

5.1 植株计数与密度估算 对于条播作物(如玉米、小麦),我们可以通过连通域分析来统计植株数量。

import cv2
import numpy as np

def count_plants(mask, class_id=1, min_area=10):
    """
    从分割掩码中统计特定类别(如玉米)的连通域数量,用于植株计数。
    Args:
        mask: 预测得到的单通道类别索引图,值域为[0, num_classes-1]
        class_id: 需要统计的作物类别ID
        min_area: 最小连通域面积,用于过滤噪声
    Returns:
        count: 植株数量
        labeled_mask: 标记了连通域的图像(用于可视化)
    """
    # 创建二值图像:作物区域为255,其他为0
    binary = (mask == class_id).astype(np.uint8) * 255
    # 形态学操作,去除小噪声,连接断裂部分
    kernel = np.ones((3,3), np.uint8)
    binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
    binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
    # 连通域分析
    num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary, connectivity=8)
    # 过滤掉面积太小的连通域(可能是噪声)
    plant_count = 0
    for i in range(1, num_labels): # 跳过背景(标签0)
        if stats[i, cv2.CC_STAT_AREA] >= min_area:
            plant_count += 1
    return plant_count, labels

5.2 长势评估与异常检测 通过分析作物区域的像素特征(如颜色、纹理),可以对其长势进行初步评估。例如,计算玉米区域的归一化植被指数(NDVI,如果有多光谱数据)或简单计算绿色像素的平均强度。

def assess_growth_health(rgb_img, plant_mask):
    """
    一个简单的长势评估示例:通过作物区域的绿色通道强度来粗略判断健康状况。
    Args:
        rgb_img: 原始RGB图像,形状(H,W,3),值域[0,255]
        plant_mask: 作物区域的二值掩码,形状(H,W)
    Returns:
        avg_green_intensity: 平均绿色强度
        health_score: 一个简单的健康评分(示例)
    """
    # 将RGB转换为HSV,更容易提取绿色信息
    hsv = cv2.cvtColor(rgb_img, cv2.COLOR_RGB2HSV)
    # 定义绿色的HSV范围(粗略估计)
    lower_green = np.array([35, 50, 50])
    upper_green = np.array([85, 255, 255])
    green_mask = cv2.inRange(hsv, lower_green, upper_green)
    # 只关注作物区域内的绿色
    green_in_plants = cv2.bitwise_and(green_mask, green_mask, mask=plant_mask)
    # 计算绿色像素的比例和平均强度
    plant_pixel_count = np.sum(plant_mask > 0)
    if plant_pixel_count > 0:
        green_ratio = np.sum(green_in_plants > 0) / plant_pixel_count
        # 也可以计算绿色通道的平均值
        green_channel = rgb_img[:, :, 1] # G通道
        avg_green_intensity = np.mean(green_channel[plant_mask > 0])
        # 一个简单的健康评分(可根据实际数据校准)
        health_score = min(green_ratio * avg_green_intensity / 100.0, 1.0)
        return avg_green_intensity, health_score
    else:
        return 0.0, 0.0

5.3 生成处方图 将识别和评估结果与地理坐标(无人机影像通常带有GPS信息)结合,可以生成农田的“处方图”。例如,一张显示植株密度过低或疑似病害区域的地图,可以直接导入到变量施肥或喷药农机中,实现精准作业。

# 假设我们有每个像素的经纬度坐标(从无人机影像元数据获得)
def generate_prescription_map(mask, health_scores, gps_coords):
    """
    生成简单的处方图GeoJSON(示例)
    Args:
        mask: 分类掩码
        health_scores: 每个评估单元的健康评分网格
        gps_coords: 图像四个角点的GPS坐标 (lat, lon)
    Returns:
        prescription_geojson: 包含多边形和属性的GeoJSON字典
    """
    # 1. 将健康评分网格二值化,找出需要处理的区域(如健康评分<0.5)
    need_treatment = health_scores < 0.5
    # 2. 找到这些区域的轮廓
    contours, _ = cv2.findContours(need_treatment.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # 3. 将轮廓像素坐标转换为地理坐标(需要根据GPS坐标进行仿射变换,此处简化)
    features = []
    for contour in contours:
        if cv2.contourArea(contour) > 100: # 忽略太小的区域
            # 简化轮廓
            epsilon = 0.01 * cv2.arcLength(contour, True)
            approx = cv2.approxPolyDP(contour, epsilon, True)
            # 将像素坐标转换为经纬度(这里需要实际的坐标转换函数)
            # lon_lat_coords = pixel_to_lonlat(approx, gps_coords, mask.shape)
            # 构建GeoJSON特征
            feature = {
                "type": "Feature",
                "geometry": {
                    "type": "Polygon",
                    "coordinates": [lon_lat_coords.tolist()] # 假设已转换
                },
                "properties": {
                    "treatment": "fertilizer", # 处方类型
                    "intensity": "high" # 强度
                }
            }
            features.append(feature)
    prescription_geojson = {"type": "FeatureCollection", "features": features}
    return prescription_geojson

走到这一步,你已经拥有了一个从数据准备、模型构建、训练优化到部署应用和结果分析的完整技术闭环。这个方案的核心优势在于其轻量化和实用性,它没有追求极致的学术指标,而是在精度、速度和工程可行性之间找到了一个平衡点。在实际项目中,我遇到过因为模型太大而无法在边缘设备上实时运行的情况,也遇到过因为数据增强不够而导致模型在阴天图片上完全失效的坑。最终让我们项目成功上线的,正是这套结合了先进Transformer架构和大量工程实践经验的方案。代码仓库里包含了文中提到的所有关键代码模块和数据预处理脚本,你可以直接克隆下来,用自己的数据试试看。记住,最好的模型永远是那个能在实际场景中稳定运行的模型。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐