PyTorch框架 隧道结构健康监测、基础设施自动化巡检、AI辅助土木工程评估 UNET模型如何训练隧道裂缝分割数据集

📅 2026/7/12 19:37:44 👁️ 阅读次数 📝 编程学习
PyTorch框架 隧道结构健康监测、基础设施自动化巡检、AI辅助土木工程评估 UNET模型如何训练隧道裂缝分割数据集

1

1

1

1

**隧道裂缝图像数据集 含约1000张256×256像素的隧道裂缝图像,附带对应的像素级标签。
标签有三种格式:

  1. 裂缝为1像素,背景为0像素
  2. 裂缝为255像素,背景为0像素
  3. 裂缝为0像素,背景为255像素
    【模型支持】
    适用于FCN、U-Net、SegNet、DeepLab、SegFormer等深度学习语义分割模型。**

以下是隧道裂缝图像数据集的详细表格化描述:


📊 一、数据集总体信息

项目描述
数据集名称隧道裂缝语义分割数据集(Tunnel Crack Segmentation Dataset)
任务类型像素级语义分割(Binary Semantic Segmentation)
图像总数约 1,000 张
图像尺寸256 × 256 像素(统一尺寸,便于批量训练)
图像格式.png.jpg(通常为灰度或RGB)
标签类型像素级二值掩膜(Binary Mask)
适用模型FCN、U-Net、SegNet、DeepLabv3+、SegFormer、PSPNet 等主流语义分割架构
应用场景隧道结构健康监测、基础设施自动化巡检、AI辅助土木工程评估

🏷️ 二、标签格式说明(三种可选)

格式编号裂缝像素值背景像素值说明
格式 110适用于 PyTorch 默认的LongTensor 类别标签(类别 ID:0=背景,1=裂缝)
格式 22550兼容 OpenCV / PIL 可视化标准(白色裂缝,黑色背景),常用于传统图像处理流程
格式 30255“反色”格式(黑色裂缝,白色背景),部分旧系统使用,需注意转换

🔁使用建议

  • 若用于深度学习训练(如 PyTorch/TensorFlow),推荐统一转换为格式 1(0/1)
  • 若用于可视化或与 OpenCV 工具链集成,可保留格式 2(0/255)

📁 三、典型数据组织结构【博主建议】

tunnel_crack_dataset/ ├── images/# 原始图像(1000张)│ ├── img_0001.png │ ├── img_0002.png │ └──... └── masks/# 对应掩膜(三种格式之一,或提供多版本子目录)├── mask_0001.png ├── mask_0002.png └──...

⚙️ 四、预处理与训练适配建议

框架/模型推荐标签格式注意事项
PyTorch (U-Net等)格式 1(0/1)损失函数用CrossEntropyLossBCEWithLogitsLoss
TensorFlow/Keras格式 1 或 格式 2(归一化后)若用sigmoid输出,标签需为 0/1;若用softmax,需 one-hot
OpenCV 可视化格式 2(0/255)直接cv2.imshow()显示清晰裂缝
LabelMe / 数据标注工具导出可能为格式 2 或 3需脚本统一标准化

该数据集结构规范、尺寸统一、标注明确,是开展基础设施智能检测研究工业级裂缝识别系统开发的理想基准数据集。

基于U-Net模型对隧道裂缝图像数据集进行训练的详细代码示例。假设你已经按照上述结构组织好了数据集,并选择了标签格式1(裂缝为1像素,背景为0像素),适用于PyTorch框架。


📁 一、推荐项目结构

tunnel_crack_unet/ ├── dataset.py# 数据集加载与预处理├── train.py# 主训练脚本├── model.py# U-Net模型定义├── predict_demo.py# 推理演示└── data/ ├── images/# 原始图像(1000张)└── masks/# 对应掩膜(裂缝为1像素,背景为0像素)

📄 二、数据集加载与预处理dataset.py

# dataset.pyimportosfromtorch.utils.dataimportDataset,DataLoaderfromPILimportImageimportnumpyasnpimporttorchvision.transformsastransformsclassCrackSegmentationDataset(Dataset):def__init__(self,image_dir,mask_dir,transform=None):self.image_paths=sorted([os.path.join(image_dir,img)forimginos.listdir(image_dir)])self.mask_paths=sorted([os.path.join(mask_dir,mask)formaskinos.listdir(mask_dir)])assertlen(self.image_paths)==len(self.mask_paths),"Mismatched number of images and masks"self.transform=transformdef__len__(self):returnlen(self.image_paths)def__getitem__(self,idx):image_path=self.image_paths[idx]mask_path=self.mask_paths[idx]image=Image.open(image_path).convert("RGB")mask=Image.open(mask_path).convert("L")ifself.transform:image=self.transform(image)mask=transforms.ToTensor()(mask)# 裂缝为1像素,背景为0像素return{'image':image,'mask':mask}defget_loader(image_dir,mask_dir,batch_size=8,num_workers=4):transform=transforms.Compose([transforms.Resize((256,256)),# 统一尺寸transforms.ToTensor(),])dataset=CrackSegmentationDataset(image_dir,mask_dir,transform=transform)loader=DataLoader(dataset,batch_size=batch_size,shuffle=True,num_workers=num_workers)returnloader

📄 三、U-Net模型定义model.py

# model.pyimporttorchimporttorch.nnasnnclassUNet(nn.Module):def__init__(self,in_channels=3,out_channels=1):super(UNet,self).__init__()defconv_block(in_channels,out_channels):returnnn.Sequential(nn.Conv2d(in_channels,out_channels,kernel_size=3,padding=1),nn.ReLU(inplace=True),nn.Conv2d(out_channels,out_channels,kernel_size=3,padding=1),nn.ReLU(inplace=True))# Encoderself.enc1=conv_block(in_channels,64)self.pool1=nn.MaxPool2d(kernel_size=2,stride=2)self.enc2=conv_block(64,128)self.pool2=nn.MaxPool2d(kernel_size=2,stride=2)self.enc3=conv_block(128,256)self.pool3=nn.MaxPool2d(kernel_size=2,stride=2)self.enc4=conv_block(256,512)self.pool4=nn.MaxPool2d(kernel_size=2,stride=2)self.bottleneck=conv_block(512,1024)# Decoderself.upconv4=nn.ConvTranspose2d(1024,512,kernel_size=2,stride=2)self.dec4=conv_block(1024,512)self.upconv3=nn.ConvTranspose2d(512,256,kernel_size=2,stride=2)self.dec3=conv_block(512,256)self.upconv2=nn.ConvTranspose2d(256,128,kernel_size=2,stride=2)self.dec2=conv_block(256,128)self.upconv1=nn.ConvTranspose2d(128,64,kernel_size=2,stride=2)self.dec1=conv_block(128,64)self.final_conv=nn.Conv2d(64,out_channels,kernel_size=1)defforward(self,x):enc1=self.enc1(x)enc2=self.enc2(self.pool1(enc1))enc3=self.enc3(self.pool2(enc2))enc4=self.enc4(self.pool3(enc3))bottleneck=self.bottleneck(self.pool4(enc4))dec4=self.upconv4(bottleneck)dec4=torch.cat((dec4,enc4),dim=1)dec4=self.dec4(dec4)dec3=self.upconv3(dec4)dec3=torch.cat((dec3,enc3),dim=1)dec3=self.dec3(dec3)dec2=self.upconv2(dec3)dec2=torch.cat((dec2,enc2),dim=1)dec2=self.dec2(dec2)dec1=self.upconv1(dec2)dec1=torch.cat((dec1,enc1),dim=1)dec1=self.dec1(dec1)returntorch.sigmoid(self.final_conv(dec1))# 输出概率图defget_model():model=UNet()returnmodel

🚀 四、主训练代码train.py

# train.pyimporttorchimporttorch.optimasoptimfromtorch.utils.tensorboardimportSummaryWriterfrommodelimportget_modelfromdatasetimportget_loaderimporttorch.nn.functionalasFdeftrain(model,dataloader,optimizer,criterion,device,epochs=50):writer=SummaryWriter('runs/crack_segmentation_experiment')model.train()forepochinrange(epochs):epoch_loss=0fori,sampleinenumerate(dataloader):images=sample['image'].to(device)masks=sample['mask'].to(device)optimizer.zero_grad()outputs=model(images)loss=criterion(outputs,masks)loss.backward()optimizer.step()epoch_loss+=loss.item()ifi%10==9:# Print every 10 batchesprint(f'Epoch [{epoch+1}/{epochs}], Batch [{i+1}/{len(dataloader)}], Loss:{loss.item():.4f}')avg_loss=epoch_loss/len(dataloader)print(f'Epoch [{epoch+1}/{epochs}], Average Loss:{avg_loss:.4f}')writer.add_scalar('Training Loss',avg_loss,epoch)torch.save(model.state_dict(),'crack_segmentation_model.pth')if__name__=='__main__':image_dir='data/images'mask_dir='data/masks'device=torch.device('cuda'iftorch.cuda.is_available()else'cpu')model=get_model().to(device)dataloader=get_loader(image_dir,mask_dir)optimizer=optim.Adam(model.parameters(),lr=0.001)criterion=nn.BCELoss()# 使用Binary Cross Entropy Losstrain(model,dataloader,optimizer,criterion,device)

🔍 五、推理演示predict_demo.py

# predict_demo.pyfrommodelimportget_modelfromdatasetimportCrackSegmentationDataset,transformsimporttorchfromPILimportImageimportmatplotlib.pyplotaspltdefmain():model=get_model()model.load_state_dict(torch.load('crack_segmentation_model.pth'))model.eval()image_path='data/images/sample.png'mask_path='data/masks/sample.png'transform=transforms.Compose([transforms.Resize((256,256)),transforms.ToTensor(),])image=Image.open(image_path).convert("RGB")mask=Image.open(mask_path).convert("L")tensor_image=transform(image).unsqueeze(0)withtorch.no_grad():output=model(tensor_image)output=(output>0.5).float()plt.figure(figsize=(12,4))plt.subplot(1,3,1)plt.imshow(image)plt.title('Input Image')plt.axis('off')plt.subplot(1,3,2)plt.imshow(mask,cmap='gray')plt.title('Ground Truth Mask')plt.axis('off')plt.subplot(1,3,3)plt.imshow(output.squeeze().cpu().numpy(),cmap='gray')plt.title('Predicted Mask')plt.axis('off')plt.show()if__name__=='__main__':main()

⚙️ 六、训练优化建议

问题解决方案
小目标漏检(细小裂缝)使用多尺度特征融合(如DeepLabv3+中的ASPP模块),或增加数据增强策略(旋转、缩放等)
类别不平衡(背景远多于裂缝)使用加权交叉熵损失函数(BCEWithLogitsLoss(pos_weight=...)),或Focal Loss
过拟合引入正则化(Dropout、L2正则)、早停机制;使用预训练模型初始化权重