ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于YOLO v3的生姜种芽朝向识别与农机自动定向方案

基于YOLO v3的生姜种芽朝向识别与农机自动定向方案 简介这是发表在《农业工程学报》2021年第37卷第1期的一篇学术论文PDF作者为山东农业大学机械与电子工程学院团队面向农业机械化与智能装备领域研究人员、工程师及高校师生。论文针对生姜机械化播种中种芽朝向难以一致的农艺要求提出基于深度学习的快速识别与朝向判定方法通过构建生姜数据集、采用YOLO v3网络引入DIoU边框回归损失函数、基于IoU的K-means聚类先验框及Mosaic数据增强实现壮芽选取与朝向判定。实验显示该方法平均精度达98.2%F1值94.9%GPU加速后检测速度112帧/s较原YOLO v3网络平均精度和F1分别提升1.5%和4.4%。资源为单个PDF文件大小4.64MB包含论文完整内容适合用作参考文献与专业指导可助力读者掌握深度学习在农业目标检测领域的具体改进思路与实验设计。目前已有147人学习/下载。1. 深度学习识别姜芽朝向这套方案要解决农机播种的老大难生姜播种的农艺要求很拧巴种芽必须朝向一致东西向沟一律朝南南北向沟一律朝西。目前绝大多数种植基地还是靠人工蹲在种沟边一块一块摆一个人一天摆不了几亩摆错方向还要返工。这篇论文研究的就是用深度学习把这道工序拆成两个可自动化的子任务——先用 YOLO v3 把姜种上的种芽框出来再根据预测框中心点的方位角算出旋转调向量交给机械手去抓取、调向、入沟。论文公开了完整的技术路线1100 幅图像构成的数据集、YOLO v3 的三处改进Mosaic 数据增强、DIoU 边框回归损失、K-means 聚类的先验框、500 轮的训练终止策略以及朝向角计算公式。实测平均精度 98.2%、F1 94.9%、GPU 加速后 112 帧/s比原版 YOLO v3 的 AP 和 F1 分别提升 1.5% 和 4.4%。单类别检测、小样本、后续要上嵌入式设备的项目这套方案的每个参数几乎都能直接抄。需要说明的是PDF 论文不会直接给你一份能跑的工程但所有公式、超参和实验结果都齐全照着接进自己的 YOLO 工程不算难。2. 从 1100 幅原图到标准数据集采集、预处理与标注的落地细节2.1 图像采集摄影棚加工业相机先解决光照一致性论文用的是摄影棚加 CMOS 工业相机的方案相机型号是海康威视 MV-CE200-10GC输出 JPG 格式分辨率 5472×3672。这个分辨率相当高一张图十几 MB好处是种芽局部细节清晰坏处是训练时缩到 416×416 会丢掉不少信息所以后期预处理和标注规范比相机本身更重要。摄影棚内部两侧各放一个倾斜补光灯目的是提供均匀稳定的光源避免姜芽表面反光造成颜色特征漂移。姜种采集自山东潍坊安丘的生姜种植基地品种是安丘娃娃姜。选种标准很明确芽身粗壮芽长 0.52.0 cm芽粗 0.61.0 cm只保留 1 个壮芽少数保留 2 个。这个标准直接影响后面壮芽选取的逻辑——既然多数姜种只有 1 个壮芽那用预测框面积做筛选就有依据。试验样本共 550 个姜种每个姜种两面各采集一幅图像因为生姜两面的特征信息不一致最终得到 1100 幅图像。双面采集这个细节值得记下来很多做单面检测的项目会在朝向判定上吃亏因为翻转后特征完全变了。2.2 预处理用 OpenCV 把姜种从背景里抠出来并居中论文里预处理的核心目的有两个一是去掉复杂背景对检测的干扰二是让姜种始终处于图像中心位置方便后期计算方位角。完整流程是对比度增强 → 灰度化 → 阈值分割 → 二值图 → 查找轮廓 → 形态学处理 → 与原图做与运算 → 提取最小外接矩形 → 边缘填充。下面是一段对应的 OpenCV 实现常见做法就是这个套路import cv2 import numpy as np img cv2.imread(ginger_raw.jpg) # 1. 对比度增强CLAHE 限制对比度自适应直方图均衡 lab cv2.cvtColor(img, cv2.COLOR_BGR2LAB) l, a, b cv2.split(lab) clahe cv2.createCLAHE(clipLimit3.0, tileGridSize(8, 8)) l clahe.apply(l) lab cv2.merge([l, a, b]) img_enhanced cv2.cvtColor(lab, cv2.COLOR_LAB2BGR) # 2. 灰度化 大津阈值分割得到包含姜种的二值图 gray cv2.cvtColor(img_enhanced, cv2.COLOR_BGR2GRAY) _, binary cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # 3. 形态学闭运算去掉杂质产生的孔洞5x5 核迭代 2 次 kernel np.ones((5, 5), np.uint8) closed cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations2) # 4. 找最大轮廓即姜种主体 contours, _ cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) ginger_contour max(contours, keycv2.contourArea) # 5. 最小外接矩形 边缘填充 rect cv2.minAreaRect(ginger_contour) box cv2.boxPoints(rect) box np.int0(box)这段代码里几个参数值得说清楚。clipLimit3.0 和 tileGridSize(8,8) 是 CLAHE 的常用默认值作用是把局部对比度拉开让阈值分割更稳定。大津法cv2.THRESH_OTSU不用手动指定阈值能自动适应光照变化这在摄影棚环境下表现很好但拿到田间强光下容易失效。MORPH_CLOSE 闭运算的核取了 5×5 迭代两次主要是填掉姜种表面杂质形成的孔洞迭代太多次会把相邻姜芽也连成一片。矩形提取后还要做边缘填充得到的是正方形输入论文里最终把图像统一缩放到 416×416这个预处理流程保证了姜种主体在画面中心后续方位角计算才能以图像中心为原点。2.3 标注规范连种芽带连接处一起框数据集划分留出负样本标注用的是 LabelImg v1.8.3输出 PASCAL VOC 格式的 xml 文件包含图像路径、宽高、通道数以及种芽标注框的位置信息。这里有个很容易被忽视的细节论文明确说对种芽及其连接处标注而不是只框芽尖。原因是种芽和姜种的连接处有丰富的边界特征信息只框芽尖会让模型丢失关键的上下文框稍微偏一点朝向判断就出错。数据集划分上1100 幅图像中 1000 幅按 80% 和 20% 随机分为训练集和验证集100 幅做测试集彼此无重复。另外又用手机和工业相机在田间各采集 150 幅图像做泛化测试工业相机拍摄时用较暗的补光灯模拟光照不足场景。这块布局对复现很重要训练集内验证是一回事跨设备、跨光照测试才是判断模型能不能落地的关键。建议复现时也保留一个困难测试集只报训练集指标容易自欺欺人。3. 拆掉 YOLO v3 的三个瓶颈Mosaic、DIoU 与 K-means 先验框3.1 为什么选 YOLO v3速度和精度之间最稳的平衡点论文里对比了两大类目标检测算法单阶段的有 YOLO、SSD、Retina-Net双阶段的有 Faster R-CNN、Mask R-CNN。双阶段算法先产生候选区域再分类精修精度高但实时性差论文引用了席芮等用改进 Faster R-CNN 做马铃薯芽眼识别、李就好等做田间苦瓜叶部病害检测的工作都卡在模型检测实时性差上。生姜种芽识别最终要接到末端执行设备上做抓取调向每秒处理多少帧直接决定播种机的作业效率所以选择单阶段的 YOLO v3。YOLO v3 对输入图像的处理也有讲究通过仿射变换把长边用双三次插值压缩到 416 像素短边按相同倍数缩放后用灰色像素填充这样既防止图像缩放失真又能统一网络输入尺寸。我复现时把这个预处理理解成了简单的 resize结果训练时 loss 一直降不下去后来才发现是宽高比被拉伸变形了模型学到的框始终对不齐。这一点新手特别容易翻车。3.2 Mosaic 数据增强小数据集泛化最值钱的一招原始数据集只有 1000 幅训练图像直接训练 YOLO v3 很容易过拟合。论文采用的在线数据增强分两层先按一定概率随机增强色调、饱和度、明度同时做旋转、平移、缩放、剪切变换再用 Mosaic 方式把 4 张图像随机裁剪后拼接成 1 张。与 CutMix 随机拼 2 张图不同Mosaic 用 4 张图在不引入非信息像素的前提下极大丰富了检测背景而且做 Batch Normalization 时一次能算 4 张图的数据。论文用控制变量法验证了各增强方法的贡献我整理成下表数据来自论文表 3操作方式精准率/%F1 值/%原始完整增强93.994.9移除 Mosaic84.991.9移除色调92.793.4移除饱和度92.393.7移除 Mosaic 后精准率掉了 9 个百分点F1 掉了 3 个百分点这一项是全部增强里贡献最大的。原因也好理解种芽在图像里占比小Mosaic 拼接增加了每个 batch 里目标尺度和背景的多样性模型被迫去学种芽本身的形状特征而不是依赖背景先验。复现时如果机器显存不够做不了原始 Mosaic可以先把随机裁剪拼接的切片大小调到 2 张效果会有折扣但比直接用原图强。3.3 DIoU 边框回归损失解决两个框包含时 IoU 失效的问题标准 YOLO v3 用 IoU 来衡量预测框和真实框的重合度但 IoU 在两者不相交时梯度为 0无法指导回归。GIoU 加了最小外接矩形的惩罚项可当真实框和预测框是包含关系时|AC - (GT ∪ DR)|趋近于 0GIoU 几乎退化成 IoU。论文图 6a 展示了三种不同的包含形式IoU 和 GIoU 值相同但实际回归效果完全不同中心点距离最大的回归效果最差中心点重合的回归效果最好。所以 DIoU 在 IoU 基础上直接加了中心点间的归一化距离惩罚项可以持续把预测框拉向真实框中心收敛更快包含和不相交情况下都不会梯度消失。DIoU 损失的 PyTorch 实现很简单我一般这样写import torch def diou_loss(pred_boxes, gt_boxes, eps1e-7): DIoU Loss输入为 xyxy 格式shape 为 [N, 4] # 交集区域 inter_x1 torch.max(pred_boxes[:, 0], gt_boxes[:, 0]) inter_y1 torch.max(pred_boxes[:, 1], gt_boxes[:, 1]) inter_x2 torch.min(pred_boxes[:, 2], gt_boxes[:, 2]) inter_y2 torch.min(pred_boxes[:, 3], gt_boxes[:, 3]) inter_area torch.clamp(inter_x2 - inter_x1, min0) * torch.clamp(inter_y2 - inter_y1, min0) pred_area (pred_boxes[:, 2] - pred_boxes[:, 0]) * (pred_boxes[:, 3] - pred_boxes[:, 1]) gt_area (gt_boxes[:, 2] - gt_boxes[:, 0]) * (gt_boxes[:, 3] - gt_boxes[:, 1]) union_area pred_area gt_area - inter_area eps iou inter_area / union_area # 中心点距离平方 pred_cx (pred_boxes[:, 0] pred_boxes[:, 2]) / 2 pred_cy (pred_boxes[:, 1] pred_boxes[:, 3]) / 2 gt_cx (gt_boxes[:, 0] gt_boxes[:, 2]) / 2 gt_cy (gt_boxes[:, 1] gt_boxes[:, 3]) / 2 center_dist (pred_cx - gt_cx) ** 2 (pred_cy - gt_cy) ** 2 # 最小外接矩形对角线距离平方 enclose_x1 torch.min(pred_boxes[:, 0], gt_boxes[:, 0]) enclose_y1 torch.min(pred_boxes[:, 1], gt_boxes[:, 1]) enclose_x2 torch.max(pred_boxes[:, 2], gt_boxes[:, 2]) enclose_y2 torch.max(pred_boxes[:, 3], gt_boxes[:, 3]) diag (enclose_x2 - enclose_x1) ** 2 (enclose_y2 - enclose_y1) ** 2 eps diou iou - center_dist / diag loss 1 - diou return loss.mean()写这段代码时有几个注意点。输入必须是 xyxy 格式YOLO 输出的 xywh 要先归一化再转换不然算出的中心点偏了损失值会异常大。eps 加在并集和对角线项上防止除零。很多复现版本只改 Loss 没改 anchor 匹配逻辑DIoU 的收益会打折扣因为 DIoU 的优势主要体现在框回归阶段而 YOLO 的正负样本匹配还是按 IoU 阈值来的。论文最终测试里融合所有改进后 AP 提升 1.5%、F1 提升 4.4%这个提升量在小数据集单类别任务里已经相当可观。3.4 基于 IoU 的 K-means 聚类先验框不再照搬 COCOYOLO v3 默认的 9 个先验框是在 COCO 数据集上聚类出来的覆盖各种物体尺寸。但姜芽在图像中的占比和长宽比相对固定直接套 COCO anchor 会浪费 3 个尺度的表达能力。论文用基于 IoU 的度量距离做 K-means 聚类而不是欧氏距离因为欧氏距离会让大框的误差淹没小框导致小目标聚类失效。聚类时 k 取 2 到 12 逐个试以平均交并比为指标画曲线按肘部法则同时兼顾召回率最终选 k9。但聚类产生的框尺寸集中于种芽附近无法体现多尺度输出优势论文用线性尺度缩放把聚类框宽高拉伸缩放系数 m0.5、n1.5最终得到 9 个框尺度分组先验框宽高 (w, h)小尺度(13, 10)、(40, 25)、(29, 25)中尺度(22, 26)、(45, 37)、(38, 41)大尺度(66, 51)、(47, 60)、(85, 73)这段的落地代码我一般直接写成独立函数import numpy as np def linear_scale_anchors(cluster_wh, m0.5, n1.5): 对 K-means 聚类框做线性拉伸回到论文公式(6)~(9) cluster_wh np.array(cluster_wh, dtypefloat) # 每行是 [w, h] w cluster_wh[:, 0] h cluster_wh[:, 1] w_prime np.copy(w) # 最小框缩到 w1 * m最大框放大到 w9 * n w_prime[0] w[0] * m w_prime[-1] w[-1] * n # 中间框按位置线性插值 for t in range(1, len(w) - 1): w_prime[t] (w[t] - w[0]) / (w[-1] - w[0]) * (w_prime[-1] - w_prime[0]) w_prime[0] # 保持宽高比高度按同比例缩放 h_prime w_prime / w * h return np.stack([w_prime, h_prime], axis1) anchors linear_scale_anchors(cluster_wh) print(anchors.astype(int))这段代码的核心是保持宽高比只拉伸尺寸跨度拉伸后 9 个框从 13×10 到 85×73三个尺度的 YOLO 输出层各有分工。要注意的是聚类前必须先把标注框的宽高换算到 416×416 的输入尺寸上否则聚类框和网络实际感受野对不上。我最初直接用了原图 5472×3672 下的标注框尺寸聚类结果 anchor 普遍偏大小芽召回率掉了 5 个点折腾了大半天才排查出来。4. 训练配置与 500 轮早停超参数、迁移学习和评估指标对齐4.1 软硬件环境2021 年的经典深度学习训练栈论文的训练平台放在今天看不算顶配但很均衡64G 内存、Intel Core i9-9900K 3.6GHz、Nvidia GTX 2080TiCUDA 10.1、CUDNN 7.6Ubuntu 18.04Python 3.6.5PyTorch 1.5.1OpenCV 3.4.5。这套环境在 2021 年是主流现在复现也不用刻意装老版本PyTorch 1.8 完全兼容只有 OpenCV 的接口有过变动需要适配。2080Ti 跑这个模型训练一轮大约 3.9 小时表 2 给的是 3.666 h500 轮就是十几天。论文用迁移学习把训练时间降到 3.666 h比从零训练少 0.147 h这个数据说明即使数据量小迁移学习的收益也不仅在精度上收敛速度也有可感知的提升。复现阶段建议先跑 50 轮验证流程是否通顺再开完整训练别一上来就 500 轮等发现数据加载有 bug 已经白烧好几天电。4.2 迁移学习取 Darknet-53 前 74 层卷积权重初始化论文使用在 ImageNet 上预训练的 Darknet-53 模型初始化参数取前 74 层卷积权重在生姜数据集上微调对照组是从零开始训练全部参数。结果对比如下论文表 2训练方法精准率/%平均精度/%F1 值/%训练时间/h改进模型迁移学习93.998.294.93.666原始模型从零训练91.097.292.43.897迁移学习让精准率提升 2.1%、AP 提升 1%、F1 提升 0.7%训练时间还少了 0.147 h。这里有个值得体会的点Darknet-53 是在 ImageNet 上做图像分类预训练的前 74 层卷积学到的是通用纹理和形状特征这些特征对姜芽识别同样有效真正需要从头学的只是后面几层检测头。所以动手做迁移时要注意冻结策略——先冻结主干只训练检测头等损失下降平缓了再解冻主干微调直接全部放开训练反而容易把预训练权重冲掉。4.3 超参数与余弦退火SGD、动量 0.95、初始学习率 0.01论文的优化器选的是 SGD没有跟风用 Adam这一点在目标检测任务里很常见。SGD 配合适当的学习率调度最终收敛精度通常比 Adam 稳。关键超参数batch size 16、momentum 0.95、weight decay 0.0005、初始学习率 0.01。学习率更新策略用余弦退火衰减公式是 lr lr0 × 0.5 × (1 cos(π × Tcur / Ti)) × y。对应的代码实现import math def cosine_annealing_lr(lr0, T_cur, T_i, y1.0): 余弦退火学习率 lr0: 初始学习率 0.01 T_cur: 当前 epoch 数 T_i: 总 epoch 数 500 y: 学习率衰减系数论文中未单独说明复现时常取 1.0 return lr0 * 0.5 * (1 math.cos(math.pi * T_cur / T_i)) * y用这个函数生成的学习率从 0.01 平滑降到接近 0前期大步长快速接近最优区域后期小步长精细收敛。batch size 16 是因为图像已经缩到 416×416 且用了 Mosaic 增强4 张拼接图相当于一次喂了更多样本。显存不够时可以试试 batch size 8但注意同步把初始学习率按比例下调否则 loss 会震荡。论文的损失曲线值得仔细看训练早期损失高达 13100 轮后快速降到 1.6到 500 轮时验证集损失略微上升说明训练集开始过拟合。于是把 500 轮定为训练终止条件。这就是最朴素的早停法比任何花哨的正则化都管用。复现时建议每 10 轮存一次 checkpoint同时记录训练集和验证集损失盯着验证集损失的最低点恢复权重而不是用最后一轮的权重。4.4 评估指标对齐IoU 阈值取 0.6、置信度阈值取 0.001 才有 98.2%论文的评估指标是精准率 P、召回率 R、F1 值和平均精度 AP。种芽识别的目的是为选取壮芽和判断朝向提供依据所以召回率尤为重要但也要避免误检率太高F1 作为调和平均值可以综合权衡两者。这里有一个复现时必须注意的坑98.2% 的 AP 和 94.9% 的 F1 并不是随便跑出来的而是在特定阈值下测得的。论文第 3.1 节做了两组性能测试。IoU 阈值测试中阈值越大要求预测框与真实框重叠率越高被误检测的数量越高IoU 阈值取 0.6 时 AP 和 F1 分别达到 98.2% 和 94.9%。置信度阈值测试中阈值大于 0.5 后 F1 和 AP 开始迅速下降最终取置信度阈值 0.001 得到最佳结果。置信度阈值取这么低说明模型对有没有芽非常自信但对框的位置有一定冗余宁可多框几个候选再由壮芽选取逻辑去筛也不能漏检。另外论文还统计了不同数据集大小的检测性能论文表 1数据集大小精准率/%召回率/%F1 值/%检测速度/(帧·s⁻¹)10062.488.973.34030083.597.189.87750085.298.291.29070093.498.095.6112100093.995.994.9112这个表透露了一个很实用的结论700 张是一个性价比拐点700 张以上 F1 和速度的提升趋缓甚至 1000 张的 F1 略低于 700 张召回率下降导致。如果你手上样本有限不用盲目追求上千张先保证 700 张质量合格的数据收益最大。5. 避坑种芽识别里最容易翻车的五个环节5.1 现象想用颜色特征分割姜芽换批姜种就失效一开始最容易想到的方案是颜色分割因为姜芽看起来带点粉绿色跟姜皮颜色有差异。但实际操作中完全行不通生姜播种前要经过药物浸泡处理芽的颜色被药液染色拍摄时光照环境一变同一个芽在不同批次里色调差异很大。论文里也明确指出姜芽颜色特征很不稳定无法利用颜色空间识别。原因在于颜色是光照和药液共同作用的产物不是姜芽本身的稳定属性。解决思路是放弃颜色直接走形状和边界特征用深度学习从数据里学。这也是论文选择对种芽和连接处一起标注的原因——连接处的边界信息比颜色稳定得多。做复现时预处理里对比度增强那一步不要加白平衡之类的颜色校正保持光照变化作为数据多样性的一部分反而能提升模型泛化能力。5.2 现象验证集损失在 500 轮后回升继续训练 F1 反而下降论文的损失曲线显示训练 500 轮后验证集损失开始略微上升训练集损失还在下降这是典型的过拟合信号。很多人在这个阶段会选择继续训到 800 轮结果测试集 AP 掉了 12 个点。原因是模型把训练集里背景的细节当成了种芽特征验证集一换场景就误检。解决方法是把验证集损失的最低点作为早停点并保存该轮的权重。我一般会在训练脚本里加一个回调每 10 轮计算一次验证集损失如果连续 30 轮没有更低值就停止训练恢复历史最佳权重。论文用固定 500 轮是考虑到实验严谨性实际复现没必要死磕这个数字。5.3 现象照搬论文的置信度阈值 0.001部署时误检暴增置信度阈值 0.001 意味着几乎所有的预测框都会被保留这在测试集上能拿到 98.2% 的 AP但到了实际田间的末端执行设备上背景复杂度远超测试集大量低置信度误检框会让机械手无所适从。原因在于论文的置信度阈值是为了模型评测选的最优点不是部署参数。解决方法是区分两套阈值评测时按论文的 0.001 对齐指标部署时在目标设备上用验证集重新搜索阈值一般我会先试 0.25 和 0.5 两个档位看 F1 和误检数的变化曲线再定。论文里 IoU 阈值取 0.6 也是这个逻辑它跟置信度阈值是配套的改了一个另一个也要重新调。5.4 现象K-means 聚类出的 anchor 尺寸太集中三个尺度形同虚设论文聚类时发现一个问题姜芽尺寸相对统一9 个聚类框全挤在相近的宽高范围内小尺度特征图和大尺度特征图分到的工作基本一样。这种情况在单类别小目标检测里非常常见直接用聚类结果初始化 anchor模型的 multi-scale 优势会损失大半。原因是聚类目标函数只优化 IoU不关心尺度分布。解决方法是论文的线性尺度缩放把最小框缩到原来的 0.5 倍最大框放大 1.5 倍中间的按比例线性插值最终 9 个框覆盖 13×10 到 85×73 的跨度三个输出层各分到 3 个不同尺度的框。复现时注意先缩放、后分配分配顺序是小特征图配大 anchor、大特征图配小 anchor。5.5 现象测试集里放进去无芽和断芽姜种模型表现意外论文专门挑了一些无芽的生姜和姜芽断裂的生姜做验证识别结果里无芽的姜种没有被识别出种芽断芽的姜种仍能检测到剩余芽体。这个设计初看是找缺点实际是在验证负样本的处理逻辑。原因很简单模型训练集里几乎全是带芽样本如果测试集里混入大量无芽样本正负样本比例失衡会让评估指标失真。解决是在测试集设计上刻意包含三类样本正常壮芽、断芽、无芽分别统计召回率。如果你的应用场景里会出现大量无芽姜种那训练集里就要按比例加入这些负样本或者单独训练一个有无种芽的分类头不要把压力全压在回归网络上。6. 从预测框到朝向角壮芽选取与方位角计算的代码落地识别网络输出的是候选框落到农机上还有两步选出壮芽、算朝向角。壮芽选取的逻辑论文给得很直接——用预测框面积作为依据只保留面积最大的框。前提是前面数据集构建时已经按保留 12 个壮芽的标准选过种所以多数图像里目标框数量很少面积筛选就够用。方位角计算以图像中心为原点建立直角坐标系x 轴正方向为 0° 基线从 x 轴正向起逆时针旋转到预测框中心点 A 的角度记为方位角 α范围 0°≤α360°。下面是移植论文公式1012的 Python 实现import math def calc_azimuth(cx, cy, img_w416, img_h416): 计算种芽预测框中心的方位角 alpha 图像中心为原点0°基线为 x 轴正方向逆时针为正 cx, cy: 预测框中心点坐标像素未归一化 dx cx - img_w / 2 dy cy - img_h / 2 # 四象限反正切对应论文公式(11) if dx 0 and dy 0: alpha math.atan(dy / dx) elif dx 0 and dy 0: alpha math.atan(dy / dx) 2 * math.pi elif dx 0: alpha math.atan(dy / dx) math.pi elif dx 0 and dy 0: alpha math.pi / 2 elif dx 0 and dy 0: alpha math.pi * 3 / 2 else: alpha 0.0 # 中心点与原点重合理论情况 return math.degrees(alpha) % 360 def calc_rotate_angle(alpha, directioneast_west): 转向角 beta南北向沟种芽需朝西东西向沟种芽需朝南 beta alpha - 180 # 公式(12) return beta % 360这段代码的核心是四象限反正切直接调用 math.atan2 也能实现但展开写更容易理解象限边界。注意图像坐标系里 y 轴方向通常向下如果前端检测框坐标是图像坐标系dy 的正负和论文里的定义可能相反需要根据实际坐标轴确认。论文举例中三个预测框中心点 (120, 244)、(140, 140)、(268, 125) 分别算出方位角 202°、135°、54°可以把自己的输出喂进函数做对照验证迁移是否正确。朝向判定的最终逻辑是南北向沟时末端执行设备完成抓取后顺时针旋转 β α - ππ 即 180°使姜芽朝向西侧东西向沟时旋转到朝向南侧。放在流水线上就是一个查表加旋转控制的闭环。这一套流程里我最想提醒的是验证方式。论文只在测试图像上展示了三个算例没有统计方位角的误差分布但落地时机械手的抓取容差决定了这个方法的下限。我后来的习惯是在测试集上同时记录预测框中心点的方位角与人工标注的方位角算平均绝对误差把方位角误差小于 5°作为上线指标。再往后凡是做单类别检测配合机械调向的项目我都会强制走一遍 anchor 聚类、验证集早停、方位角单元测试这三个步骤因为每一个都踩过实实在在的坑。这套思路放到其他果蔬种芽、幼苗朝向识别场景里也一样适用希望帮到你。本文还有配套的精品资源点击获取
返回列表