news 2026/10/1 13:41:16

高分二号遥感图像语义分割全流程实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
高分二号遥感图像语义分割全流程实战指南

简介:本资源是一份面向遥感图像处理研究者、深度学习初学者及地理信息工程实践者的PyTorch语义分割实战教程,聚焦高分二号(GF-2)等高分辨率遥感影像的地物精细分割任务,解决环境监测、城市规划中像素级地物识别难、数据标注缺、模型复现弱等实际问题。压缩包共1029个文件,含819张遥感影像与真值标签PNG图、35个核心Python训练/推理脚本(含数据加载、U-Net结构实现、IoU评估等)、1个CSV元数据文件、1份README.md说明文档及可视化结果样例图,整体577.48MB,结构清晰,开箱即用。已有94人学习下载,覆盖从遥感图像预处理、多波段数据读取、标签制作到模型训练与结果融合的完整链路,附带真实场景预测效果图(predict.png、blend.png、img_gt_pre.png)及典型样本(classes_sample.jpg),显著降低遥感语义分割项目落地门槛。

1. 高分遥感图像语义分割实现:不是调个UNet就能跑通的“端到端流程”,而是从GF2原始影像到像素级地物标签的完整闭环

你手头有一张高分二号(GF2)PMS2传感器拍摄的遥感图,波段数4、空间分辨率1米、幅宽约10km——但直接扔进PyTorch训练?大概率在DataLoader卡死、loss不降反升、predict.png里全是噪点斑块。这不是模型不行,而是遥感图像语义分割根本不是CV通用任务的简单平移:它要求你同时处理多光谱对齐误差、大尺寸内存溢出、地物类别长尾分布、标注掩膜与原始影像坐标系错位这四大硬伤。本项目提供的不是“PyTorch语义分割Demo”,而是一套经过GF2_PMS2_E116.0_N39.1_20170302_L1A0002214760-MSS2实测验证的落地链路:含真实采集的4波段影像(含.csv元数据)、人工精标8类地物掩膜(classes_sample.jpg定义)、预处理脚本、轻量UNet++主干+ASPP增强结构、以及可复现的img_gt_pre.png可视化对比结果。适合遥感方向研究生做毕设基线、地信工程师快速部署小范围解译模块、或算法岗面试前突击遥感实战细节——尤其当你发现Cityscapes预训练权重在农田边缘完全失效时,这份资源能让你少踩3周坑。


2. 数据结构解析与预处理:为什么GF2影像必须重采样+归一化+切块,而不是直接resize?

2.1 GF2_PMS2原始数据的物理特性与格式陷阱

项目中提供的GF2_PMS2_E116.0_N39.1_20170302_L1A0002214760-MSS2_*.png是经L1A级辐射定标后的4波段影像(B1-B4对应蓝、绿、红、近红外),但注意:

  • 非标准RGB排列:文件名末尾MSS2表示多光谱传感器2号,其波段顺序为[B, G, R, NIR],而非OpenCV默认的[B, G, R];
  • DN值范围非0-255:原始DN值区间为0~1023(10bit量化),直接转uint8会丢失近红外波段动态范围;
  • 地理坐标未嵌入:.csv元数据包含成像时间、太阳高度角、卫星姿态角,但无GeoTIFF地理参考信息,因此不能用rasterio直接读取空间坐标,需依赖img_gt_pre.png中人工标注的像素级对应关系。

提示:classes_sample.jpg不是类别列表,而是8类地物的彩色编码图(如建筑=红色、道路=黄色、水体=蓝色),其RGB值即为训练时的label映射表。务必用cv2.IMREAD_UNCHANGED读取,避免PNG alpha通道被丢弃导致颜色失真。

2.2 四步预处理流水线:从原始影像到PyTorch DataLoader可接受张量

以下脚本需在data_preprocess.py中实现(项目已提供完整代码,此处拆解关键逻辑):

import numpy as np import cv2 from pathlib import Path def preprocess_gf2_image(img_path: str, label_path: str, target_size=(512, 512), norm_method='minmax') -> tuple[np.ndarray, np.ndarray]: # Step 1: 读取4波段影像并校验通道顺序 img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED) # shape: (H, W, 4) if img.shape[-1] != 4: raise ValueError(f"Expected 4 bands, got {img.shape[-1]} in {img_path}") # Step 2: DN值归一化(关键!避免NIR波段主导梯度) if norm_method == 'minmax': # 对每个波段独立归一化,保留光谱特征差异 img_norm = np.zeros_like(img, dtype=np.float32) for i in range(4): band = img[:, :, i] band_min, band_max = band.min(), band.max() img_norm[:, :, i] = (band - band_min) / (band_max - band_min + 1e-6) else: # z-score,适用于多时相联合训练 img_norm = (img - img.mean(axis=(0,1))) / (img.std(axis=(0,1)) + 1e-6) # Step 3: 切块(非简单resize!避免地物形变) h, w = img_norm.shape[:2] patches = [] for i in range(0, h, target_size[0]): for j in range(0, w, target_size[1]): patch = img_norm[i:i+target_size[0], j:j+target_size[1]] if patch.shape[0] == target_size[0] and patch.shape[1] == target_size[1]: patches.append(patch) # Step 4: 读取并同步label掩膜(需严格像素对齐) label = cv2.imread(label_path, cv2.IMREAD_UNCHANGED) # shape: (H, W, 3) label_patches = [] for i in range(0, h, target_size[0]): for j in range(0, w, target_size[1]): patch = label[i:i+target_size[0], j:j+target_size[1]] if patch.shape[0] == target_size[0] and patch.shape[1] == target_size[1]: # 将RGB label转为单通道class_id(查classes_sample.jpg映射表) label_id = rgb_to_class_id(patch) # 实现见下文 label_patches.append(label_id) return np.stack(patches), np.stack(label_patches) def rgb_to_class_id(rgb_label: np.ndarray) -> np.ndarray: """根据classes_sample.jpg定义的RGB→class_id映射,生成单通道label""" # classes_sample.jpg中各色块中心RGB值(实测提取): class_map = { (255, 0, 0): 0, # 建筑(红) (255, 255, 0): 1, # 道路(黄) (0, 0, 255): 2, # 水体(蓝) (0, 255, 0): 3, # 植被(绿) (255, 165, 0): 4, # 裸土(橙) (128, 0, 128): 5, # 农田(紫) (0, 255, 255): 6, # 湿地(青) (128, 128, 128): 7 # 其他(灰) } h, w, _ = rgb_label.shape label_id = np.zeros((h, w), dtype=np.uint8) for r in range(h): for c in range(w): key = tuple(rgb_label[r, c]) label_id[r, c] = class_map.get(key, 0) # 默认建筑类,避免未定义色块报错 return label_id

参数说明:

  • target_size=(512, 512):遥感影像切块尺寸,需满足GPU显存限制(RTX3090可支持512×512×4波段输入);
  • norm_method='minmax':推荐初学者使用,避免z-score在单景影像上因统计量偏差导致归一化失真;
  • rgb_to_class_id函数中class_map键值对必须与classes_sample.jpg实际像素值严格一致——建议用cv2.imshow逐像素校验,这是后续训练label错位的首要排查点。

2.3 预处理后数据集结构与加载器配置

执行预处理后生成的目录结构如下:

data/ ├── train/ │ ├── images/ # 512×512×4的npy文件(float32) │ └── labels/ # 512×512的npy文件(uint8,0-7) ├── val/ │ ├── images/ │ └── labels/ └── classes_sample.jpg # 类别定义源文件

torch.utils.data.Dataset需重写__getitem__以适配4波段输入:

class GF2Dataset(Dataset): def __init__(self, img_dir: Path, label_dir: Path, transform=None): self.img_paths = sorted(img_dir.glob("*.npy")) self.label_paths = sorted(label_dir.glob("*.npy")) assert len(self.img_paths) == len(self.label_paths) self.transform = transform def __getitem__(self, idx): img = np.load(self.img_paths[idx]) # shape: (512, 512, 4) label = np.load(self.label_paths[idx]) # shape: (512, 512) # PyTorch要求CHW格式,且4波段需保持通道维度 img = torch.from_numpy(img.transpose(2, 0, 1)) # → (4, 512, 512) label = torch.from_numpy(label).long() # → (512, 512) if self.transform: img = self.transform(img) return img, label

关键配置项:

  • batch_size=8:4波段×512×512输入在RTX3090上显存占用约12GB,batch_size=8为安全上限;
  • num_workers=4:预处理已转为npy,无需CPU实时解码,num_workers>0可加速数据搬运;
  • 禁止使用transforms.Normalize:因4波段归一化已在预处理阶段完成,此处再标准化会导致NIR波段数值坍缩。

3. 模型架构与训练策略:为什么UNet++比DeepLabV3+更适合GF2小样本场景?

3.1 针对遥感图像的网络结构选型依据

通用语义分割模型(如DeepLabV3+)在遥感任务中常表现不佳,核心矛盾在于:

  • 感受野过大:DeepLabV3+空洞卷积设计针对城市街景(物体尺度集中),而GF2影像中农田斑块可达1000×1000像素,建筑仅20×20像素,单一感受野无法兼顾;
  • 多尺度特征融合不足:遥感地物存在显著尺度差异(如水库vs电线杆),UNet++的嵌套跳跃连接能更精细地传递局部纹理与全局上下文;
  • 参数量敏感:本项目仅提供约200张切片(≈1000样本),大模型易过拟合,UNet++(约12M参数)比HRNet(≈28M)更适配小数据集。

项目采用UNet++ with ASPP backbone(非标准UNet++,主干替换为ResNet18+ASPP),结构如下:

Input (4,512,512) ├─ Encoder: ResNet18 (modified for 4-channel input) │ ├─ Stage0: Conv7x7 + BN + ReLU → (64,256,256) │ ├─ Stage1: 2×ResBlock → (64,128,128) │ ├─ Stage2: 2×ResBlock → (128,64,64) │ ├─ Stage3: 2×ResBlock → (256,32,32) │ └─ Stage4: ASPP module (rates=[1,6,12,18]) → (256,32,32) ├─ Decoder: UNet++ nested skip connections │ ├─ Level1: 4×concat(Stage4, Stage3_up) → (512,64,64) │ ├─ Level2: 3×concat(Stage2_up, Level1_up) → (256,128,128) │ └─ Level3: 2×concat(Stage1_up, Level2_up) → (128,256,256) └─ Output: Conv1x1 → (8,512,512) + Softmax

3.2 4波段输入适配与损失函数定制

ResNet18默认接收3通道输入,需修改第一层卷积:

import torchvision.models as models def build_unetpp_aspp(num_classes=8, pretrained=True): # 加载预训练ResNet18(仅用ImageNet权重初始化前几层) backbone = models.resnet18(pretrained=pretrained) # 替换第一层卷积以支持4通道输入 backbone.conv1 = nn.Conv2d(4, 64, kernel_size=7, stride=2, padding=3, bias=False) # 初始化新卷积层权重(保持ImageNet预训练特征迁移能力) if pretrained: # 复制原3通道权重,第4通道用均值初始化 with torch.no_grad(): backbone.conv1.weight[:, :3] = backbone.conv1.weight[:, :3].clone() backbone.conv1.weight[:, 3] = backbone.conv1.weight[:, :1].mean(dim=1) # 构建ASPP模块(简化版,避免引入过多参数) aspp = ASPP(in_channels=512, out_channels=256, rates=[1,6,12,18]) # UNet++ decoder部分(项目已提供完整实现,此处略) decoder = UNetPPDecoder(backbone, aspp, num_classes=num_classes) return nn.Sequential(backbone, aspp, decoder)

损失函数选择:

  • 主损失:Dice Loss + CrossEntropy Loss加权组合(α=0.5),缓解农田/植被等大类对loss的主导;
  • 辅助损失:Boundary-aware Loss(在label边缘1像素内加权),提升道路、水体等线状地物边界精度;
  • 禁用Focal Loss:因GF2数据集各类别样本量相对均衡(classes_sample.jpg显示8类覆盖均匀),Focal Loss反而降低小类召回。

3.3 训练超参与收敛监控要点

# 推荐训练命令(基于项目提供的train.py) python train.py \ --data_dir ./data \ --model unetpp_aspp \ --batch_size 8 \ --lr 1e-3 \ --epochs 120 \ --scheduler cosine \ --warmup_epochs 5 \ --weight_decay 1e-4 \ --save_dir ./checkpoints/gf2_unetpp_v1

关键参数解释:

  • --lr 1e-3:4波段输入使梯度方差增大,初始学习率需比RGB任务低10倍;
  • --scheduler cosine:配合--warmup_epochs 5,避免早期loss震荡(遥感影像噪声导致初始梯度不稳定);
  • --weight_decay 1e-4:防止ASPP模块中大卷积核过拟合;
  • 监控指标必须包含Boundary F1:在val阶段额外计算label边缘1像素内的F1-score,GF2任务中该指标比mIoU更能反映工程可用性。

4. 预测与后处理:如何让predict.png真正可用,而非仅满足可视化?

4.1 滑动窗口预测与重叠区域融合

单张GF2原始影像尺寸通常为8000×8000,远超GPU显存承载能力。项目采用滑动窗口+重叠融合策略:

def sliding_window_predict(model, img_tensor: torch.Tensor, window_size=512, overlap=128, device='cuda'): """ img_tensor: (4, H, W) 归一化后的4波段张量 返回: (C, H, W) 概率图 """ model.eval() h, w = img_tensor.shape[1:] prob_map = torch.zeros((8, h, w), device=device) # 8类概率图 count_map = torch.zeros((h, w), device=device) # 每像素被预测次数 # 生成滑动窗口坐标 for i in range(0, h - window_size + 1, overlap): for j in range(0, w - window_size + 1, overlap): window = img_tensor[:, i:i+window_size, j:j+window_size] window = window.unsqueeze(0).to(device) # (1,4,512,512) with torch.no_grad(): pred_prob = torch.softmax(model(window), dim=1)[0] # (8,512,512) # 融合到全局prob_map(加权平均,边缘区域权重衰减) weight = generate_gaussian_weight(window_size, overlap) prob_map[:, i:i+window_size, j:j+window_size] += \ pred_prob * weight.unsqueeze(0) count_map[i:i+window_size, j:j+window_size] += weight # 归一化 prob_map = prob_map / count_map.unsqueeze(0) return prob_map def generate_gaussian_weight(win_size, overlap): """生成高斯衰减权重,中心区域权重=1,边缘线性衰减至0.5""" x = torch.arange(win_size) y = torch.arange(win_size) xx, yy = torch.meshgrid(x, y, indexing='ij') center = win_size // 2 dist = torch.sqrt((xx - center)**2 + (yy - center)**2) weight = torch.exp(-dist**2 / (2 * (overlap/2)**2)) weight = torch.clamp(weight, min=0.5, max=1.0) # 限制最小权重 return weight

参数说明:

  • overlap=128:保证相邻窗口重叠25%,避免切块边界出现伪影;
  • generate_gaussian_weight:比简单平均更鲁棒,抑制窗口边缘因感受野截断导致的预测偏差;
  • 必须在GPU上执行:count_map和prob_map需全程驻留显存,CPU-GPU频繁拷贝会导致预测速度下降5倍以上。

4.2 后处理:形态学优化与矢量化导出

predict.png直接输出的是概率图argmax结果,需经三步后处理才具备GIS系统导入条件:

步骤操作OpenCV函数参数说明
1. 连通域过滤剔除小于50像素的噪声斑点cv2.connectedComponentsWithStatsmin_area=50,保留STAT_AREA≥50的连通域
2. 边界平滑消除锯齿状边缘(尤其道路/水体)cv2.morphologyEx+cv2.GaussianBlurkernel_size=5,sigma=1.0,先高斯模糊再二值化
3. 矢量化导出生成GeoJSON(需坐标系信息)rasterio.features.shapes使用img_gt_pre.png中已知的像素-地理坐标映射关系

注意:项目未提供地理坐标转换模块,因GF2_PMS2_*.csv中缺少RPC参数。实际工程中需联系数据提供商获取RPC文件,或用gdal_translate -a_srs EPSG:4326手动指定WGS84坐标系。

4.3blend.png与img_gt_pre.png的生成逻辑

项目中的blend.png是原始影像与预测结果的叠加图,img_gt_pre.png则并排显示Ground Truth与Prediction。生成代码需确保色彩映射严格一致:

def create_blend_visualization(img_path, pred_label, save_path): # 读取原始影像(4波段→RGB假彩色:R=NIR, G=R, B=G) img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED) rgb_img = np.stack([img[:,:,3], img[:,:,2], img[:,:,1]], axis=2) # NIR-R-G # 将pred_label转为彩色图(复用classes_sample.jpg的RGB映射) color_map = np.array([ [255, 0, 0], # 建筑 [255, 255, 0], # 道路 [0, 0, 255], # 水体 [0, 255, 0], # 植被 [255, 165, 0], # 裸土 [128, 0, 128], # 农田 [0, 255, 255], # 湿地 [128, 128, 128] # 其他 ], dtype=np.uint8) color_pred = color_map[pred_label] # (H,W,3) # 叠加:原始影像透明度0.6 + 预测掩膜透明度0.4 blend = cv2.addWeighted(rgb_img, 0.6, color_pred, 0.4, 0) cv2.imwrite(save_path, blend) def create_comparison_plot(gt_label, pred_label, save_path): """生成GT与Pred并排对比图""" color_map = ... # 同上 gt_color = color_map[gt_label] pred_color = color_map[pred_label] fig, axes = plt.subplots(1, 2, figsize=(12, 6)) axes[0].imshow(gt_color); axes[0].set_title('Ground Truth') axes[1].imshow(pred_color); axes[1].set_title('Prediction') plt.savefig(save_path, bbox_inches='tight')

避坑重点:

  • rgb_img构造必须用[NIR,R,G]而非[R,G,B],否则植被在假彩色中不显绿色;
  • color_map索引必须与classes_sample.jpg中实际RGB值完全一致,任何1像素偏差都会导致整类地物着色错误。

5. 避坑指南:GF2语义分割中80%失败源于这5个隐蔽问题

5.1 现象:训练loss震荡剧烈,10个epoch内从2.5跳到0.8又回到2.1

原因:GF2影像DN值范围(0~1023)未归一化,导致NIR波段(B4)梯度爆炸,反向传播时权重更新失稳。
解决:强制在data_preprocess.py中添加波段级min-max归一化,禁止使用全局归一化(img / 1023会压缩NIR动态范围)。验证方法:打印img[:, :, 3].std(),归一化后应≈0.28(非0.001或1.0)。

5.2 现象:predict.png中所有像素均为类别0(建筑),其他类全为0

原因:rgb_to_class_id函数中classes_sample.jpg读取时被cv2.IMREAD_COLOR自动转为BGR,导致RGB元组匹配失败(如(255,0,0)读成(0,0,255))。
解决:改用cv2.IMREAD_UNCHANGED读取,并用print(np.unique(label_rgb, axis=0))校验前10个像素RGB值。血泪经验:每次更换classes_sample.jpg必须重新校验。

5.3 现象:验证集mIoU稳定在65%,但blend.png中道路边缘严重锯齿

原因:未启用Boundary-aware Loss,且滑动窗口预测时未加高斯权重,导致窗口拼接处边缘不连续。
解决:在损失函数中加入boundary_loss = dice_loss(pred_boundary, gt_boundary),其中gt_boundary用cv2.Canny(gt_label, 100, 200)生成;同时sliding_window_predict中必须启用generate_gaussian_weight。

5.4 现象:DataLoader报错OSError: Too many open files

原因:Linux系统默认ulimit -n为1024,而GF2切片数量超2000,num_workers>0时子进程打开文件句柄超限。
解决:终端执行ulimit -n 65536,并在Python中设置torch.multiprocessing.set_sharing_strategy('file_system')。玄学操作:重启Python kernel后首次运行必成功,第二次必失败——这是Linux文件句柄泄漏的典型表现。

5.5 现象:predict.png与img_gt_pre.png尺寸不一致,无法并排比较

原因:原始影像GF2_*.png存在1-2像素的JPEG压缩伪影,cv2.imread读取后尺寸微变(如8000×8000读成7999×7999),导致切块数不匹配。
解决:预处理前强制cv2.resize(img, (8000,8000)),或用PIL.Image.open().convert('RGB')替代cv2.imread(PIL对JPEG伪影鲁棒性更强)。后悔药:在README.md中增加# 数据校验章节,要求用户运行verify_dimensions.py脚本确认所有影像尺寸一致。


6. 工程级验证技巧:用img_gt_pre.png反推模型缺陷,而非只看mIoU

6.1 三步定位法:从可视化图直击模型弱点

img_gt_pre.png不是装饰品,而是诊断报告。我习惯按此顺序排查:

  1. 聚焦错误高发区域:用cv2.threshold二值化GT与Pred的差异图(diff = cv2.absdiff(gt, pred)),观察错误像素是否集中在特定地物交界处(如农田-道路、水体-裸土);
  2. 检查类别混淆矩阵:对diff图按GT类别统计错误像素数,若“裸土”被误判为“农田”占比超40%,说明模型未学好土壤光谱特征;
  3. 回溯原始影像:在GF2_*.png中定位错误区域,查看该处NIR波段(B4)DN值是否异常(如云阴影导致NIR值骤降),确认是否为数据质量问题而非模型缺陷。

6.2 动态阈值评估:为什么固定0.5阈值会误判湿地

遥感影像中湿地(class=6)在NIR波段反射率极低,其预测概率图常呈“弥散状低置信度”,若用pred_prob.argmax(dim=0)硬分类,会将大量湿地像素判为邻近的水体(class=2)。解决方案:

# 对湿地类单独设置动态阈值 wetland_prob = pred_prob[6] # (H,W) wetland_mask = (wetland_prob > 0.3) & (pred_prob[2] < 0.2) # NIR低+水体概率低 final_pred = torch.where(wetland_mask, torch.full_like(final_pred, 6), final_pred)

参数依据:在val集上统计湿地像素的pred_prob[6].mean()≈0.28,故阈值设为0.3;同时要求水体概率<0.2排除混叠。

6.3 长尾类别增强:农田(class=5)的过采样策略

classes_sample.jpg显示农田占比约25%,但训练时仍易被忽略。我采用在线过采样而非离线复制:

class BalancedSampler(Sampler): def __init__(self, dataset, class_weights): self.dataset = dataset self.class_weights = class_weights # [1.0,1.0,1.0,1.0,1.0,1.5,1.0,1.0] self.weights = [] for idx in range(len(dataset)): _, label = dataset[idx] cls_id = label.flatten().mode()[0].item() self.weights.append(self.class_weights[cls_id]) def __iter__(self): return iter(torch.multinomial(torch.tensor(self.weights), len(self.dataset), replacement=True))

关键点:class_weights[5]=1.5(农田)而非2.0,因过度增强会导致模型在农田内部产生虚假边界——这是我在3个GF2项目中验证过的平衡点。

6.4 部署前必做的三件事

  1. 显存压力测试:用nvidia-smi -l 1监控sliding_window_predict全程显存占用,确保峰值≤90%显存容量;
  2. 跨设备验证:在Jetson AGX Orin上用TensorRT推理predict.png,检查blend.png色彩是否偏移(NVIDIA驱动版本差异导致YUV转RGB异常);
  3. 坐标系校验:将img_gt_pre.png中某栋建筑角点坐标,在QGIS中叠加GF2原始影像,确认像素偏移≤2像素(否则需重做地理配准)。

从那以后我每次交付遥感分割模型,都强制走一遍这三步——哪怕客户只要一张predict.png。因为真正的工程价值不在mIoU数字,而在blend.png里那条光滑的道路边缘是否能直接导入GIS系统做面积统计。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 13:39:07

SpringBoot实战:家庭设备维修系统从设计到部署全流程解析

做毕设或者练手SpringBoot的兄弟们&#xff0c;别再死磕电商系统了&#xff0c;我最近把一个“基于SpringBoot的家庭设备维修服务系统”从需求到上线完整跑通了一遍&#xff0c;今天把技术选型、数据库设计、核心代码和踩过的坑一次性整理出来。说白了&#xff0c;这个系统就是…

作者头像 李华
网站建设 2026/10/1 13:39:01

IoTDB编译报错thrift did not exit cleanly的排查与解决

上午九点&#xff0c;CI 上亮起一个红叉&#xff1a;[ERROR] iotdb-thrift-commons: thrift did not exit cleanly. Review output for more...。我第一时间以为又是网络波动导致依赖没拉全&#xff0c;删掉~/.m2重新编译&#xff0c;结果一样。后来翻遍 Maven 日志、看了iotdb…

作者头像 李华
网站建设 2026/10/1 13:38:59

Spring Boot Maven打包失败:Unable to find main class的排查与解决

1. 打包失败的现场&#xff1a;先搞清楚这个报错在说什么先说结论&#xff1a;repackage failed: Unable to find main class这个问题&#xff0c;十有八九不是你的代码逻辑写错了&#xff0c;而是 Spring Boot Maven 插件在 repackage 阶段找不到可执行入口。换句话说&#xf…

作者头像 李华
网站建设 2026/10/1 13:38:42

医学CT图像肺炎分类实战:从DICOM预处理到Grad-CAM可解释部署

简介&#xff1a;本资源是一份面向高校计算机类专业学生的深度学习与计算机视觉课程设计实践项目&#xff0c;聚焦新冠肺炎医学图像分类预测任务&#xff0c;以Python为开发语言&#xff0c;兼顾教学性与工程可行性。项目完整包含可直接运行的源代码&#xff08;main.py、load_…

作者头像 李华
网站建设 2026/10/1 13:37:58

463个AI视频案例拆解:开源187个可复用Skill与提示语模版

1. 463个AI视频拆成Skill和提示语模版&#xff0c;这件事到底在解决什么问题先说说我为什么要干这件事。过去大半年&#xff0c;我几乎每天都在跟AI视频生成工具打交道——文生视频、图生视频、视频风格迁移、人物替换、超分修复&#xff0c;各种工具轮着用。用得多了就发现一个…

作者头像 李华
网站建设 2026/10/1 13:36:59

LLM智能自助分析系统搭建实战:从RAG到NL2SQL的工程化落地

最近我把内部的数据分析平台做了一次大改造&#xff0c;核心方向就是围绕“基于大模型&#xff08;LLM&#xff09;的智能化自助分析系统”这条路子展开。折腾了几个月&#xff0c;踩了不少坑&#xff0c;也沉淀了一些能直接复用的经验。这次就专门写一篇完整的搭建探索记录&am…

作者头像 李华