尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
基于U-Net的风机叶片语义分割实战:从数据预处理到推理部署
简介面向风电叶片监测场景的风扇语义分割数据集及配套Python训练代码适合计算机视觉研究人员、风电运维算法工程师及深度学习者使用。全部数据由1994个tif文件构成包含风扇叶片图像及对应标签图涵盖多种工作环境和光照条件可用于磨损、裂缝、污渍等区域的分割识别6个Python脚本覆盖数据集随机划分、图像预处理、U-Net网络定义、模型训练与预测推理可直接运行或在此框架上做二次开发。压缩包共2000个文件整体大小约810.93MB当前已有112人学习。这份资源既提供了可直接用于训练的标注数据也给出了从数据准备到模型部署的完整代码框架可有效缩短风力发电机叶片检测模型的开发周期适用于高校课题、算法对比与工程预研等场景。1. 从 TCGA 文件名到风机叶片这个数据集的第一眼陷阱拿到资源先看目录几个.tif文件以TCGA_DU_开头第一反应是病理切片数据——TCGA 是癌症基因组图谱的缩写。但文件名只是历史遗留的命名习惯里面装的全是风力发电机风扇叶片图像和对应的逐像素标签。这种「文件名迷惑」在工业数据集里很常见真正需要关注的是结构和代码。这套资源解决的痛点很具体风机叶片在图像里是细长、弯曲、与背景天空、山体、云层对比度不稳定的目标。用目标检测只能给矩形框框里既有叶片也有背景用语义分割才能逐像素区分叶片与背景为后续磨损、裂缝检测做前置分割。对做工业视觉语义分割、想快速跑通 U-Net 基线、或者要自建分割数据集的人来说这套代码把数据划分、预处理、训练、预测串成了一条完整链路省去从零搭 pipeline 的时间。资源不是生产级工程但作为基线足够扎实。2. 数据组织与预处理先把标签和图像对齐在处理任何语义分割任务之前第一步永远是确认标签格式是否与模型输出一致。这套资源的 DATASET 文件夹下存放图像和对应 mask虽然未给出具体目录树但根据dataset.py的常见写法可以推断图像与标签同名、同尺寸、统一放在两个子目录或混放后用后缀区分。先按这个思路拆pre_process.py。2.1 预处理脚本的逻辑拆解pre_process.py负责原始图像到模型输入的转换。针对风机叶片场景至少有四个处理步骤是必须的import cv2 import numpy as np from glob import glob import os def preprocess_images(src_dir, dst_dir, target_size(512, 512)): os.makedirs(dst_dir, exist_okTrue) for img_path in glob(os.path.join(src_dir, *.tif)): img cv2.imread(img_path, cv2.IMREAD_UNCHANGED) # 如果 tif 是 16bit需先转到 8bit if img.dtype np.uint16: img (img / 257).astype(np.uint8) # 叶片图像常因逆光产生强烈阴影做 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 cv2.cvtColor(lab, cv2.COLOR_LAB2BGR) # 统一尺寸mask 用最近邻插值避免标签错乱 img cv2.resize(img, target_size, interpolationcv2.INTER_LINEAR) name os.path.basename(img_path).replace(.tif, .jpg) cv2.imwrite(os.path.join(dst_dir, name), img, [cv2.IMWRITE_JPEG_QUALITY, 95])这段代码把 16bit 深度的 tif 压缩到 8bit原因是大部分语义分割模型输入约定在 0-255 范围直接用 16bit 数值会导致权重初始化后梯度异常。CLAHE 局部对比度增强针对的是风机叶片最常见的逆光场景——叶片背光面与天空灰度接近不做增强的话分割模型很容易把叶片暗部并进背景。tileGridSize(8, 8)表示将图像分成 8×8 的块分别做直方图均衡太大无法修正局部光照太小则过度放大噪声。标签图mask一般不需要做 CLAHE直接转灰度 二值化 最近邻缩放。常见错误是用线性插值缩放 mask这会在叶片边缘产生介于 0 和 1 之间的灰度值训练时 loss 无法收敛。这里补充一句预处理脚本应当将src_dir和dst_dir分离避免处理后的图像覆盖原始数据一旦参数调错还能回退。2.2 数据划分的一个隐蔽问题split_data.py的作用是划分 train/val/test。表面上是随机 shuffle 后按比例切分但工业数据里有个容易被忽略的问题风机的多张连续帧图像高度相似如果随机分割同一风机的相似帧会同时进入训练集和验证集导致验证分数虚高。import os import random import shutil from glob import glob def split_dataset(img_dir, mask_dir, train_ratio0.8, val_ratio0.1, seed42): images sorted(glob(os.path.join(img_dir, *.jpg))) random.seed(seed) random.shuffle(images) total len(images) train_end int(total * train_ratio) val_end int(total * (train_ratio val_ratio)) splits { train: images[:train_end], val: images[train_end:val_end], test: images[val_end:] } for split_name, img_list in splits.items(): os.makedirs(fdata/{split_name}/images, exist_okTrue) os.makedirs(fdata/{split_name}/masks, exist_okTrue) for img_path in img_list: base os.path.basename(img_path).replace(.jpg, .png) shutil.copy(img_path, fdata/{split_name}/images/{base}) mask_path os.path.join(mask_dir, base) if os.path.exists(mask_path): shutil.copy(mask_path, fdata/{split_name}/masks/{base})如果有时间戳或风机编号信息更好的做法是按风机分组后划分——同一风机的所有帧进同一个集合。资源里没有给出分组信息退而求其次用随机划分时建议固定seed42保证每次实验可复现。验证集的作用是判断早停时机test 集是最终评估标准二者混用会导致模型选择失真。train_ratio0.8, val_ratio0.1的比例在数据集超过 500 张时是合理起点数据少则调整到 0.7/0.15/0.15保证验证集有足够样本。2.3 语义分割任务的标签语义风机叶片分割在标签编码上通常是二分类0 表示背景天空、大地、塔架1 表示叶片像素。之所以不做多分类是因为资源提供的数据只标注了前景/背景磨损、裂缝、污渍等细粒度类别需要更高成本的标注。二类分割也是验证分割 pipeline 是否跑通的最短路径——先确认模型能精确区分叶片和背景再谈细粒度分类。如果后续要扩展多分类只需将mask中的灰度值按类别映射到 0、1、2、3 等索引损失函数换成CrossEntropyLoss即可。3. U-Net 结构与实现要点分割小数据集的理性选择Unet.py实现了经典的 U-Net 结构。在数据集规模不大的前提下选择 U-Net 不是因为它先进而是因为它在小样本语义分割上的收敛速度和标注需求达到最优平衡。风机叶片图像通常只有数百到数千张Vision Transformer 类模型需要大规模预训练权重才能发挥效果而 U-Net 从零训练即可获得可接受的精度。3.1 编码器中的关键设计import torch import torch.nn as nn import torch.nn.functional as F class DoubleConv(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.conv nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), nn.Conv2d(out_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue) ) def forward(self, x): return self.conv(x) class Down(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.mpconv nn.Sequential( nn.MaxPool2d(2), DoubleConv(in_ch, out_ch) ) def forward(self, x): return self.mpconv(x) class Up(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.up nn.ConvTranspose2d(in_ch, in_ch // 2, 2, stride2) self.conv DoubleConv(in_ch, out_ch) def forward(self, x1, x2): x1 self.up(x1) diffY x2.size()[2] - x1.size()[2] diffX x2.size()[3] - x1.size()[3] x1 F.pad(x1, [diffX // 2, diffX - diffX // 2, diffY // 2, diffY - diffY // 2]) x torch.cat([x2, x1], dim1) return self.conv(x)DoubleConv是 U-Net 的基本单元连续两个 3×3 卷积后接批归一化。批归一化在这类小数据集上的作用是稳定训练——叶片图像的光照分布差异较大BN 可以缓解不同 batch 之间分布偏移对梯度的影响。Down模块采用 MaxPooling 降采样保留纹理响应最强的特征Up模块用转置卷积上采样并用F.pad处理特征图尺寸不一致的问题。torch.cat([x2, x1], dim1)这一步是跳跃连接的核心将编码器下采样前的细节特征位置信息直接拼到解码器对应层弥补上采样过程丢失的空间分辨率。叶片边缘的精细分割依赖这些跳跃连接来恢复。3.2 U-Net 在处理风机叶片上的偏差叶片这项任务有两个独特之处目标细长且跨越图像对角线背景区域占比极大常超过 90%。U-Net 能较好地应对这两种情况第一细长目标依赖多尺度特征。编码器下采样四次后整个叶片可能收缩成几个像素宽的线条但跳跃连接将每一层的高分辨率特征直接传递给解码器低层特征保留边缘锐度高层特征提供语义完整性。实验里如果把跳跃连接去掉IoU 至少掉 10 个点。第二类别不平衡问题。背景占比过高导致模型倾向把所有像素预测为背景。常见做法是在损失函数中加入 Dice 项缓解前景和背景的像素数量差异。U-Net 的最后一层输出通道数为 1前向函数如下class UNet(nn.Module): def __init__(self, n_channels3, n_classes1): super().__init__() self.inc DoubleConv(n_channels, 64) self.down1 Down(64, 128) self.down2 Down(128, 256) self.down3 Down(256, 512) self.down4 Down(512, 512) self.up1 Up(1024, 256) self.up2 Up(512, 128) self.up3 Up(256, 64) self.up4 Up(128, 64) self.outc nn.Conv2d(64, n_classes, 1) def forward(self, x): x1 self.inc(x) x2 self.down1(x1) x3 self.down2(x2) x4 self.down3(x3) x5 self.down4(x4) x self.up1(x5, x4) x self.up2(x, x3) x self.up3(x, x2) x self.up4(x, x1) logits self.outc(x) return logits输入 3 通道彩色图输出是未经过 sigmoid 的 logits形状为(B, 1, H, W)。第一层通道数从 64 起是在显存和表达能力的折中。如果显卡只有 6GB可以把初始通道改为 32如果叶片背景特别复杂则增到 96——但要注意下采样后每层翻倍总显存消耗会指数级增长。n_classes1表示二分类输出若做多分类输出通道数改为类别数并配合CrossEntropyLoss。4. 训练闭环从 Dataset 加载到 loss 收敛分割项目的核心难点不在模型而在训练 pipeline。dataset.py和train.py各自承担关键职责Dataset 决定模型看到什么数据训练循环决定模型如何从数据中学到区分叶片和背景的能力。4.1 Dataset 的加载方式与 On-the-fly 增强dataset.py的标准实现逻辑是遍历图像列表在__getitem__中同步读取图像和 mask应用数据增强返回(tensor_image, tensor_mask)对。import torch from torch.utils.data import Dataset import cv2 import numpy as np from albumentations import Compose, HorizontalFlip, RandomBrightnessContrast class WindTurbineDataset(Dataset): def __init__(self, img_dir, mask_dir, image_size(512, 512), augmentFalse): self.img_paths sorted(os.listdir(img_dir)) self.mask_dir mask_dir self.img_dir img_dir self.image_size image_size self.augment augment def __len__(self): return len(self.img_paths) def __getitem__(self, idx): img_name self.img_paths[idx] img cv2.imread(os.path.join(self.img_dir, img_name)) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) mask cv2.imread(os.path.join(self.mask_dir, img_name).replace(.jpg, .png), cv2.IMREAD_GRAYSCALE) mask (mask 127).astype(np.float32) if self.augment: aug Compose([ HorizontalFlip(p0.5), RandomBrightnessContrast(p0.3) ]) augmented aug(imageimg, maskmask) img, mask augmented[image], augmented[mask] img torch.from_numpy(img).permute(2, 0, 1).float() / 255.0 mask torch.from_numpy(mask).unsqueeze(0).float() return img, mask这里是几个值得注意的参数。image_size(512, 512)是训练分辨率的默认值风机叶片原始 tif 往往是 1024 以上直接全分辨率训练显存不够512 是速度与精度的平衡点。RandomBrightnessContrast是专门针对叶片场景设计的增强——叶片在不同天候下的亮度变化极大模拟这些光照变化能提高模型泛化性。mask 读取后做了(mask 127)二值化防止标签图像中的 JPEG 压缩伪影或者标注软化边缘产生中间值。对比常见的RandomResizedCrop这里刻意没有使用因为叶片占图像比例较大随机裁剪可能把整片叶片裁掉一半造成不必要的学习困难。4.2 损失函数与训练超参train.py中损失函数的选择直接影响分割边缘质量。纯BCEWithLogitsLoss在背景占 90% 以上时会让模型快速收敛到全背景预测加入 Dice loss 将梯度重心放到前景区域这是工业分割项目的标准组合。import torch import torch.nn.functional as F def dice_loss(pred, target, smooth1.0): pred torch.sigmoid(pred) intersection (pred * target).sum(dim(2, 3)) dice (2.0 * intersection smooth) / (pred.sum(dim(2, 3)) target.sum(dim(2, 3)) smooth) return 1.0 - dice.mean() def combined_loss(pred, target): bce F.binary_cross_entropy_with_logits(pred, target) dice dice_loss(pred, target) return bce dicedice_loss中smooth1.0防止分母为 0同时缓解小区块如叶片尖端带来的数值不稳定。pred.sum(dim(2, 3))是对 H/W 维求和得到的形状是(B,)——每个样本单独计算 Dice再取平均。这样做的原因是不同图像中叶片占比差异大逐样本归一化比全局归一化稳定。组合损失里 BCE 保证分类正确Dice 保证前景/背景不平衡时分割区域完整。训练循环中常用的超参数组合参数名推荐值调整说明batch_size86GB 显存512×512 输入上限epochs100配合早停实际收敛约 50-80 轮optimizerAdam, lr1e-4Adam 适合分割初跑调优可换 SGD momentum 0.9lr_schedulerReduceLROnPlateaupatience10, factor0.5early_stop_patience20连续 20 轮 val_loss 不降则终止patience20意味着验证集上连续 20 轮没有更好则停止训练。叶片数据集小训练波动较大patience 太小会过早停止20 是经验值。ReduceLROnPlateau 降低学习率时模型已经从陡峭区域进入平缓区域factor0.5 减半是保守策略防止跳过局部最优点。4.3 评估指标的计算细节train.py 里通常每轮结束计算验证集 IoU。二分类场景 IoU 计算如下def compute_iou(pred_mask, true_mask): pred_mask (torch.sigmoid(pred_mask) 0.5) intersection (pred_mask true_mask).sum().float() union (pred_mask | true_mask).sum().float() iou intersection / (union 1e-6) return iou.item()阈值取0.5是缺省做法。但风机叶片前景占比低负样本占多数时0.5 阈值往往得到保守结果漏检多实践中可以降低阈值到 0.35 增加召回率。IoU 不能只看单张要在整个验证集上累计 intersection 和 union 后统一计算逐张平均和先累积再除有差异后者更贴近全局指标。5. 推理部署的三个细节滑窗、TTA 与可视化输出predict.py走的是「加载权重 → 推理 → 保存可视化结果」链路但直接对原图推理会遇到一个实际问题训练时图像经过 resize推理时输入任意尺寸会破坏 U-Net 的空间假设。卷积天然支持任意尺寸输入但多次下采样后特征图尺寸可能不是 2 的整数倍导致解码器转置卷积与跳跃连接拼接时尺寸不匹配。常见做法是推理时也统一 resize 到与训练一致的 512×512输出后再 resize 回原始尺寸但叶片边缘的精细结构会有一定程度损失。更稳妥的做法是滑窗推理将大图切块每块输入模型再按位置拼接。def sliding_window_predict(model, image, window_size512, stride384): model.eval() h, w image.shape[:2] padded_h ((h stride - 1) // stride) * stride padded_w ((w stride - 1) // stride) * stride padded cv2.copyMakeBorder(image, 0, padded_h - h, 0, padded_w - w, cv2.BORDER_REFLECT) prob_map np.zeros((padded_h, padded_w), dtypenp.float32) weight_map np.zeros((padded_h, padded_w), dtypenp.float32) for y in range(0, padded_h - window_size 1, stride): for x in range(0, padded_w - window_size 1, stride): patch padded[y:y window_size, x:x window_size] patch_tensor torch.from_numpy(patch).permute(2, 0, 1).unsqueeze(0).float() / 255.0 with torch.no_grad(): logits model(patch_tensor) prob torch.sigmoid(logits).cpu().numpy()[0, 0] prob_map[y:y window_size, x:x window_size] prob weight_map[y:y window_size, x:x window_size] 1.0 prob_map / np.maximum(weight_map, 1.0) return prob_map[:h, :w]stride384小于window_size512相邻窗口有 128 像素重叠重叠区域的概率取两次预测的平均值消除边缘块因缺少上下文导致的预测跳变。BORDER_REFLECT在图像边界处做镜像填充避免零填充在边缘产生黑色区块影响概率输出。多尺度预测是另一个有用的技巧——同一张图缩放 0.75、1.0、1.25 倍分别推理再按原始坐标平均能提高叶片边界稳定性。最后是可视化输出。分割结果叠加到原图上的推荐方式是生成半透明的绿色蒙版配合保存概率图供后续分析。标签连通域分析cv2.connectedComponentsWithStats可以去除面积小于设定阈值的孤立预测块——这种误检通常来自云层边缘的纹理干扰而不是真正的叶片。打印最终的 IoU、每类像素数再输出output/overlay_*.jpg整条推理链路就完整闭合了。本文还有配套的精品资源点击获取
RELATED

相关推荐

ORB图像拼接实战:轻量级特征匹配与单应性变换详解

ORB图像拼接实战:轻量级特征匹配与单应性变换详解

简介:本资源是一套基于OpenCV与ORB特征匹配算法实现的图像拼接C完整源码,面向计算机、人工智能、软件工程等专业的在校学生、教师及初级开发者,适用于毕业设计、课程设计、项目原型开发与算法原理学习。代码经过完整测试并成功通过答辩&#…

📅 2026/9/14 2:45:33
大模型测试用例生成选型:看懂设计稿与自动跑单测是关键

大模型测试用例生成选型:看懂设计稿与自动跑单测是关键

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/9/14 2:40:33
Python通用表单类设计与实现:基于tkinter的高效GUI开发

Python通用表单类设计与实现:基于tkinter的高效GUI开发

1. Python通用表单类设计概述在Python GUI开发中,表单是最常见的交互元素之一。一个设计良好的通用表单类能够显著提升开发效率,避免重复造轮子。本文将基于tkinter和ttk模块,构建一个可复用的表单基类,支持多种控件类型、数据验证…

📅 2026/9/14 2:40:33
MORE NEWS

更多资讯

📰

C++ Qt坦克大战实战:从类设计到碰撞检测的完整实现

简介:面向C初学者的坦克大战游戏源码工程,基于Qt 5.14.1与C编写,在Qt Creator 4.11.0中开发,完整实现经典坦克对战玩法。资源为可编译运行的Qt工程,共设置35个关卡,每关包含20个敌方坦克,玩家拥…

📰

从会回答到懂场景:ADP智能体开发引擎如何落地企业级Agent

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

用C语言实现网络Sniffer:raw socket抓包与协议解析实战

简介:基于C语言实现的网络嗅探器课程设计项目,面向网络编程学习者、信息安全专业学生以及需要完成抓包类课程设计的开发者。项目以WinPcap与MFC为双核心,实现在混杂模式下对网卡数据包的捕获、过滤与解析,支持TCP、UDP、ARP、ICMP…

📰

基于Java的记账系统毕业设计:从数据库设计到部署实战

简介:面向Java初学者和需要完成课程设计的开发者,这份基于Java的记账系统毕业设计资源,可帮助解决毕业设计选题难、项目不完整、环境搭建复杂等常见问题,既适合直接作为毕业设计二次开发,也适合用于Java Web实战练习。…

📰

Python实现有限元作业代码:CST单元单刚组装与求解全流程

简介:浙江大学2020至2021学年春夏学期有限元方法课程作业代码包,面向需要将有限元理论转化为可运行程序的学生与研究人员,内容覆盖网格生成、弱形式建立、插值函数选择、矩阵组装、线性系统求解及后处理等核心编程环节。压缩包共16个文件&…

📰

长沙跨境电商静态页开发:HTML5语义化+CSS响应式+本地JS交互

简介:本资源是一套基于HTML、CSS与JavaScript实现的长沙跨境电商平台Demo源码,面向前端初学者及Web开发实践者,旨在通过真实业务场景帮助掌握静态网页构建、响应式布局与基础交互逻辑。压缩包共66个文件,含32个JPG、19个PNG、7个J…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬