news 2026/9/11 18:40:29

基于PyTorch的遥感影像滑坡场景分类实战:从数据到模型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于PyTorch的遥感影像滑坡场景分类实战:从数据到模型

简介:面向毕业设计、课程设计与项目开发场景,基于Python实现遥感影像滑坡场景分类的完整代码与项目文档。项目采用SVM分类器,通过光谱特征与GLCM纹理特征提取、K-Means视觉词袋聚类、LDA主题抽象,最终利用Libsvm工具完成场景分类,流程清晰、方法经典。压缩包共21个文件、大小仅1.86MB,以Python源码、文本配置/说明、pkl特征文件、模型文件及Markdown文档为主体,其中4个py文件为核心代码,4个pkl为中间特征,model/lda模型文件用于加载推理,md文档提供详细说明。项目源码已通过严格测试,可直接参考并二次开发。资源上传后已有55人学习,适合对遥感影像分类、传统机器学习流程感兴趣的初学者与工程开发人员。借助该资源可快速跑通SVM+K-Means+LDA的完整分类流程,理解视觉词袋在遥感场景识别中的应用,并基于现有代码扩展实验或完成课程作业。

1. 滑坡场景分类到底在解决什么问题,为什么选Python生态

滑坡是山区最常见的次生灾害之一,遥感影像上的滑坡识别长期以来依赖人工目视解译,效率低且标准不一。把“滑坡场景分类”做成代码任务,本质上是让模型自动判断某块影像区域是否包含滑坡体,或者更进一步,对每个像素输出“滑坡/非滑坡”的概率。这个任务的价值在于:一次训练好的模型可以快速应用于大范围卫星影像,辅助灾害应急和国土调查。对毕业设计或课程设计来说,它同时覆盖了图像处理、深度学习、数据标注和工程交付四个环节,是很典型的“有学术背景、有落地价值”的选题。

为什么用Python?因为从数据读取、模型搭建到可视化评估,Python生态几乎一条龙。GDAL处理栅格,rasterio读取TIFF,PyTorch训练网络,matplotlib画混淆矩阵,所有环节都有成熟库。相比C++或MATLAB,Python让你把精力放在模型设计和实验分析上,而不是重复造轮子。接下来的内容按一个完整项目应有的顺序展开:数据准备、模型实现、训练调参、最后交付成别人能跑起来的代码库。这套流程不是唯一方案,但我会尽量把常见做法和关键坑都讲清楚。

2. 遥感影像滑坡场景分类的数据准备:从Sentinel-2影像到滑坡标注

2.1 获取和预处理遥感影像的常见做法

滑坡场景分类的第一步是拿到带标注的影像数据。公开数据集方面,有毕设常用的 Landslide4Sense、Bijie 滑坡数据集等;如果没有现成数据,常见做法是用 Sentinel-2 影像自己标注。Sentinel-2 提供 10 米分辨率的多光谱波段(B2、B3、B4、B8),对滑坡这种百平方米到平方公里级的地表变化来说足够用。

拿到 L2A 级产品后,先做预处理:重投影到统一坐标系(常用 UTM),按研究区范围裁剪,然后合成 RGB 或 RGB+NIR 的多波段影像。不要直接拿原始 JPEG 或 PNG 用,因为不同时相的影像辐射值不一致,会导致模型泛化能力变差。我一般会先把影像整理成 GeoTIFF,保持地理参考,方便后续和标注矢量叠加。

典型预处理流程如下:

# 使用 gdal 命令行工具重投影并裁剪 gdalwarp -t_srs EPSG:32648 -cutline study_area.shp -crop_to_cutline \ -of GTiff input_B02.tif output_B02_crop.tif

如果要合成多波段,建议用 Python 脚本统一处理,因为滑坡数据往往来自不同轨道和时相,手动操作容易漏波段。下面的代码把单波段文件读入并堆叠成多波段 GeoTIFF:

import rasterio import numpy as np bands = ["B02.tif", "B03.tif", "B04.tif", "B08.tif"] # BGRN stacked = [] for b in bands: with rasterio.open(b) as src: stacked.append(src.read(1).astype(np.float32)) profile = src.profile # 复用最后一个文件的元数据 arr = np.stack(stacked, axis=0) profile.update(count=len(bands), dtype="float32") with rasterio.open("landslide_input.tif", "w", **profile) as dst: dst.write(arr)

这段代码的逻辑很简单:依次打开每个波段文件,读出通道数据,最后按波段顺序堆叠成(C, H, W)的数组。profile是从最后打开的源文件继承的,保证了输出 TIFF 的地理坐标信息不丢失。注意astype(np.float32)这一步——滑坡识别经常需要计算 NDVI、NDWI 等指数,浮点运算避免溢出。

2.2 制作标注数据:从矢量图斑到逐像素标签

滑坡标注通常是矢量图斑,在 ArcMap 或 ArcGIS Pro 里手工勾画滑坡边界,保存为面要素。这个步骤看起来简单,实际要花掉整个项目一半的时间。标注标准不统一,不同人的勾画比例尺不同,会导致模型学到边界噪声。

把矢量转成与影像对齐的栅格标签时,有几个关键点。一是确认矢量与影像的坐标系完全一致,二是设置与影像相同的分辨率,三是类别编码必须从 0 开始,0 表示背景,1 表示滑坡。Transform 步骤如下:

import geopandas as gpd from rasterio.features import rasterize gdf = gpd.read_file("landslide_labels.shp") gdf = gdf.to_crs(utm_crs) # 这里 utm_crs 需替换成影像的实际坐标系 with rasterio.open("landslide_input.tif") as src: meta = src.meta.copy() shapes = [(geom, 1) for geom in gdf.geometry] # 1 代表滑坡类别 label_raster = rasterize( shapes, out_shape=(meta["height"], meta["width"]), transform=meta["transform"], fill=0, dtype="uint8", ) with rasterio.open("landslide_label.tif", "w", **meta) as dst: dst.write(label_raster, 1)

rasterize是栅格化核心函数,shapes里每个元素是(几何对象, 像元值)out_shapetransform直接从影像元数据里取,保证标签和影像像素一一对应。这里有个隐藏坑:滑坡边界的羽化效果需要保留还是二值化?如果后续要做分割网络,建议保留 0/1 硬标签,因为多数损失函数按像素独立计算,模糊标签反而干扰训练。

2.3 窗口裁剪与数据增强的实用参数

影像尺寸通常几千乘几千像素,GPU 显存放不下,所以要把大影像裁剪成固定大小的 patch。常见做法是切 256×256 或 512×512,带重叠地切。重叠比例 50% 能显著增加样本量,同时避免滑坡体被切碎。对于走廊式滑坡,单纯正方滑动窗口可能会把多个滑坡目标切成两半,可以配合按标注框裁剪的方式。

裁剪后的增强操作必须谨慎。滑坡在影像上表现为土体裸露、色调与周边植被差异明显,所以水平翻转和垂直翻转是安全的;随机旋转 90° 也是无损操作。但亮度扰动和对比度扰动要小幅度,因为不同时相的 Sentinel-2 影像本身有辐射差异,过度增强会让模型学到错误的颜色关联。我常用以下参数:

from torchvision import transforms train_transform = transforms.Compose([ transforms.RandomHorizontalFlip(p=0.5), transforms.RandomVerticalFlip(p=0.5), transforms.RandomRotation(90, expand=False), transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.1, hue=0.02), ])

ColorJitter的四项参数建议这样设:brightnesscontrast在 0.2 以内,saturation0.1 以内,hue不要超过 0.05。滑坡体本身是土黄色,色调偏移过大会让模型分不清滑坡和裸地。如果你做的是多时相对比(灾前灾后影像拼接),还需要在通道层面对齐时相,先不急着增强。

数据准备好后,要划分训练集、验证集、测试集。滑坡数据集常见问题是分布不均衡:正样本像素往往不到 10%。建议按地理区域划分,而不是随机划分像素块,否则同一个滑坡的 patch 会同时出现在训练集和测试集里,评估结果虚高。划分比例 7:2:1 是常见做法,保存成三个 txt 文件记录 patch 路径,比文件目录划分更容易回溯。

3. 用PyTorch搭建滑坡场景分类模型:从CNN到分割网络的选型

3.1 为什么选语义分割而不是普通分类网络

“场景分类”四个字有两种理解:给整幅影像打一个“滑坡/非滑坡”的标签,或者对每个像素做分类。在遥感灾害领域,后者才是真正的需求,因为滑坡边界对救灾和损失评估至关重要。整图分类可以用 ResNet,但输出只是类别概率,拿不到滑坡位置;语义分割网络输出的特征图和原图同分辨率,可直接提取滑坡体范围。

所以这里采用分割网络的架构。最经典的是 U-Net,编码器部分用预训练的 ResNet34 或 ResNet50,解码器部分逐步上采样恢复空间分辨率。滑坡边界往往不规则,U-Net 的跳跃连接能把浅层边缘信息传到深层,比单纯 DeepLabV3+ 更容易学习到细微边界。如果你的设备显存只有 6GB,建议用 ResNet34 编码器,输入 256×256,batch size 8。

模型定义如下,基于 PyTorch 的torchvision.models.segmentation.deeplabv3_resnet50做微调更简单,但要定制解码器结构,还是直接用现成库segmentation_models_pytorch更省事:

import segmentation_models_pytorch as smp model = smp.Unet( encoder_name="resnet34", encoder_weights="imagenet", in_channels=4, # B2, B3, B4, B8 classes=1, # 二分类,输出一个通道 activation=None, # 不在这里接 sigmoid,在损失函数里处理 )

这里in_channels=4对应前面堆叠的四波段影像,如果你决定只用 RGB,改成 3。encoder_weights="imagenet"是迁移学习的关键:虽然遥感影像和 ImageNet 的自然图像分布不同,但浅层特征(边缘、纹理)是可迁移的,能显著加速收敛。activation=None是为了避免在模型内部固化 sigmoid,方便混合损失函数计算。

3.2 自定义数据集类的完整写法

PyTorch 训练要求把影像和标签封装成Dataset类。要点有两个:一是文件按配对方式读取,二是返回的影像张量需要归一化。遥感影像多波段的均值方差和自然图像不同,不能直接用 ImageNet 的mean=[0.485, 0.456, 0.406]。有人这么用也能收敛,但分割精度会差一点。正确做法是统计自己的训练集:

# 统计每个通道的均值和标准差,保存后用于训练 # 注意统计要在数据增强之前,且分通道计算 means = np.zeros(4) stds = np.zeros(4) for idx in range(len(image_list)): with rasterio.open(image_list[idx]) as src: arr = src.read().astype(np.float32) for c in range(4): means[c] += arr[c].mean() stds[c] += arr[c].std() means /= len(image_list) stds /= len(image_list) print("mean:", means, "std:", stds)

这个统计脚本会输出四维数组。实际训练时,把它硬编码到数据集类里,避免每次启动都重新统计。下面是标准 Dataset 写法:

import torch from torch.utils.data import Dataset import rasterio import numpy as np class LandslideDataset(Dataset): def __init__(self, img_paths, label_paths, transform=None): self.img_paths = img_paths self.label_paths = label_paths self.transform = transform def __len__(self): return len(self.img_paths) def __getitem__(self, idx): with rasterio.open(self.img_paths[idx]) as src: image = src.read().astype(np.float32) # (4, H, W) with rasterio.open(self.label_paths[idx]) as src: label = src.read(1).astype(np.int64) # (H, W) # 归一化:这里的 mean/std 来自上面的统计结果 mean = np.array([0.02, 0.03, 0.04, 0.05]).reshape(-1, 1, 1) std = np.array([0.05, 0.06, 0.07, 0.08]).reshape(-1, 1, 1) image = (image - mean) / std # 转成 torch tensor,注意 label 要加一个通道维 image = torch.from_numpy(image) label = torch.from_numpy(label).unsqueeze(0).float() # (1, H, W) if self.transform: # 这里简化处理,真正使用时需要同时变换 image 和 label seed = torch.initial_seed() torch.manual_seed(seed) image = self.transform(image) torch.manual_seed(seed) label = self.transform(label) return image, label

__getitem__中需要保证 data augmentation 对影像和标签做完全一致的随机操作。上面的方法是利用torch.initial_seed()让两次变换使用相同的随机种子,能保证翻转和旋转对二者的作用一致。但ColorJitter不能直接作用在 label 上,实际项目里更推荐把 transform 写成分离的复合函数,或使用 kornia 这类库的同步变换。

3.3 在单卡和CPU上跑通训练循环的最低配置

如果你只有 CPU 环境,先别急着跑全尺寸训练。把影像缩小到 128×128,epoch 设 3,batch size 4,先确认数据流和 loss 都在降低。这个过程能暴露出 90% 的代码错误:文件路径缺失、张量只读、显存不足等等。PyTorch 训练循环的标准写法,这里给出一个精简版:

device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = model.to(device) optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4) for epoch in range(epochs): model.train() epoch_loss = 0.0 for images, labels in train_loader: images = images.to(device) labels = labels.to(device) logits = model(images) # (B, 1, H, W) loss = mixed_loss(logits, labels) optimizer.zero_grad() loss.backward() optimizer.step() epoch_loss += loss.item() * images.size(0) print(f"Epoch {epoch} loss: {epoch_loss / len(train_loader.dataset):.4f}")

AdamW是 Adam 的修正版,weight decay 用 1e-4 能有效控制过拟合。model(images)的输出 shape 是(B, 1, H, W),因为classes=1,后面计算 Dice loss 时需要做torch.sigmoid(logits)。训练时最好只打印 loss,不要每步都打印 IoU,否则会拖慢训练好几倍。

4. 训练、验证与调参:滑坡分类任务的损失函数、评估指标与迁移学习

4.1 为什么 BCE + Dice 混合损失对滑坡有效

滑坡分类正负像素比例悬殊,一幅 512×512 影像中滑坡可能只占几百个像素。如果只用 BCE Loss,模型会快速收敛到“全预测为背景”,因为背景占比 95% 以上,全预测背景的 loss 也很小。Dice Loss 直接优化 DICE 系数,对正负样本不敏感,但单独用 Dice Loss 时梯度在零点附近不稳定,组合起来效果更可靠。

混合损失的定义:

import torch import torch.nn.functional as F def dice_loss(pred, target, smooth=1.0): pred = torch.sigmoid(pred) pred = pred.contiguous().view(-1) target = target.contiguous().view(-1) intersection = (pred * target).sum() dice = (2. * intersection + smooth) / (pred.sum() + target.sum() + smooth) return 1 - dice def mixed_loss(pred, target, bce_weight=0.5): bce = F.binary_cross_entropy_with_logits(pred, target) dice = dice_loss(pred, target) return bce_weight * bce + (1 - bce_weight) * dice

bce_weight的默认值取 0.5,但实际项目中建议先试 0.3 和 0.7。当滑坡目标很小、边界模糊时,bce_weight=0.7让模型更依赖逐像素交叉熵,边界更干净;当滑坡面积较大时,Dice 权重可以提高到 0.6。smooth参数是平滑系数,防止交集为 0 时除以 0,一般取 1.0 或 1e-5,对最终精度影响不大。

4.2 验证集上的评估指标怎么算才可信

评估滑坡分类模型不能只看准确率,因为背景占比太高,准确率即使 99% 也可能是废模型。需要关注 IoU、F1-score、Recall。滑坡救灾场景中,漏检比误检更危险,所以 Recall 权重通常高于 Precision。

验证阶段需要每个 epoch 计算一次,不能只在最后算。实现方法:

def calculate_metrics(outputs, labels): preds = (torch.sigmoid(outputs) > 0.5).int() # 阈值默认 0.5 tp = ((preds == 1) & (labels == 1)).sum().item() fp = ((preds == 1) & (labels == 0)).sum().item() fn = ((preds == 0) & (labels == 1)).sum().item() iou = tp / (tp + fp + fn + 1e-6) recall = tp / (tp + fn + 1e-6) precision = tp / (tp + fp + 1e-6) f1 = 2 * precision * recall / (precision + recall + 1e-6) return iou, f1, recall

注意1e-6是防止除零,而不是平滑指标本身。阈值 0.5 是默认选择,但如果召回率太低,可以调低阈值到 0.3,这会增加预测面积,提升召回率。具体阈值应该在验证集上选,而不是在测试集上选,否则会过拟合验证集。

4.3 迁移学习的几个坑:冻结编码器、学习率、训练多少轮

预训练权重的合理使用能减少 30% 以上的训练时间。对于小数据集(几千个 patch),常见做法是冻结编码器,只训练解码器。方式如下:

model = smp.Unet(encoder_name="resnet34", encoder_weights="imagenet") for param in model.encoder.parameters(): param.requires_grad = False

冻结后,初始学习率可以从 1e-3 提高到 1e-3 到 3e-3,因为解码器是随机初始化的,需要较大学习率快速拟合。训练 10 到 15 个 epoch 后,解冻编码器,把学习率降到原来的十分之一,再继续训练 20 个 epoch。这样做的理由是:先让解码器学会把编码器输出的特征图映射为分割结果,解冻后再用较低学习率微调编码器,避免破坏预训练特征。

滑坡任务常见的过拟合表现是验证集 IoU 在第 10 个 epoch 达到峰值后开始下降,而训练集 IoU 还在上升。此时可以早停,也可以加更强的正则。注意遥感影像的太阳高度角、云影都会干扰,如果模型在验证集特定区域表现差,先检查标注是否把阴影误标成了滑坡。

4.4 超参数选择速查表

下面这张表整理了我做滑坡分类时最常用的参数范围和选择思路,你也可以直接复制作为项目的实验记录表。

参数常见范围我的默认值调整依据
影像尺寸256×256 到 512×512512×512显存 8GB 以上用 512,否则用 256
batch size8 到 3216显存不足时先降 batch size,而不是降分辨率
学习率1e-5 到 1e-31e-4用 cos warmup 或 ReduceLROnPlateau
编码器resnet34 / resnet50resnet34数据量大用 resnet50,否则 resnet34 更稳
损失权重bce 0.3~0.70.5正样本比例小于 5% 时提高 dice 权重
训练轮数30 到 6040结合早停 patience=10
优化器AdamW / SGDAdamW小数据集上 AdamW 收敛更快

学习率的调节参考ReduceLROnPlateau

scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, mode="max", factor=0.5, patience=5, verbose=True ) # 每个 epoch 验证后调用 scheduler.step(val_f1)

mode="max"表示监控指标最大化,factor=0.5表示学习率缩小一半,patience=5表示连续 5 个 epoch 没有提升就缩。这个调度器特别适合滑坡分类这种验证指标波动大的情况。

5. 把模型和项目文档变成可交付的毕设/课设代码库

5.1 项目目录结构和 requirements.txt 的一次到位写法

课程设计和毕业设计交付时,老师第一眼看的是能不能跑通,第二眼看结构是否规范。我见过很多代码只有一个训练脚本,没有数据说明,别人拿到根本无从下手。推荐目录如下:

landslide_classification/ ├── data/ │ ├── images/ │ ├── labels/ │ └── train_val_test.txt ├── src/ │ ├── dataset.py │ ├── model.py │ ├── loss.py │ ├── train.py │ ├── predict.py │ └── utils.py ├── checkpoints/ ├── visualize/ ├── requirements.txt └── README.md

requirements.txt要写清楚版本范围,但不能固定死某个具体版本导致别人装不上。参考下面写法:

torch>=2.0.0 torchvision>=0.15.0 segmentation-models-pytorch>=0.3.0 rasterio>=1.3.0 geopandas>=0.13.0 numpy>=1.24.0 matplotlib>=3.7.0 tqdm>=4.64.0

建议用conda create -n landslide python=3.8 -y新建环境,然后pip install -r requirements.txt。Python 3.8 到 3.10 兼容性最好,不要用 3.12,部分旧版 GDAL 和 rasterio 轮子可能装不上。

5.2 模型推理脚本:加载权重输出滑坡概率图

交付的项目必须包含一个独立的预测脚本,能读入任意尺寸的 GeoTIFF,输出带地理坐标的滑坡概率图。关键点在于推理时不需要再裁剪 256×256 固定 patch,用滑窗叠加预测再拼接,能避免拼接缝。

import torch import numpy as np import rasterio from tqdm import tqdm def predict_tile(model, image_path, output_path, window_size=512, stride=256, device="cuda"): with rasterio.open(image_path) as src: image = src.read().astype(np.float32) profile = src.profile.copy() h, w = image.shape[1], image.shape[2] prob_map = np.zeros((h, w), dtype=np.float32) count_map = np.zeros((h, w), dtype=np.float32) # 归一化参数要和训练时保持一致 mean = np.array([0.02, 0.03, 0.04, 0.05]).reshape(-1, 1, 1) std = np.array([0.05, 0.06, 0.07, 0.08]).reshape(-1, 1, 1) for y in tqdm(range(0, h - window_size, stride)): for x in range(0, w - window_size, stride): patch = image[:, y:y+window_size, x:x+window_size] patch_tensor = torch.from_numpy((patch - mean) / std).unsqueeze(0).to(device) with torch.no_grad(): out = torch.sigmoid(model(patch_tensor)).cpu().numpy()[0, 0] prob_map[y:y+window_size, x:x+window_size] += out count_map[y:y+window_size, x:x+window_size] += 1 # 边缘处理:最后一行和一列可能没被覆盖,单独处理 prob_map[count_map > 0] = prob_map[count_map > 0] / count_map[count_map > 0] profile.update(count=1, dtype="float32") with rasterio.open(output_path, "w", **profile) as dst: dst.write(prob_map, 1)

滑窗的stride小于window_size时,每个像素会被多次预测,平均后能减少拼接边界突变。推理时torch.no_grad()是必须的,否则 PyTorch 会构建计算图,显存会被撑爆。最后的profile.update将输出写为单波段 float32 GeoTIFF,可以在 ArcGIS Pro 里直接加载做密度分析。

5.3 在项目文档里写出老师想看的三个部分

项目文档不需要八股文,但要把“实验、结果、反思”写到能让答辩老师快速看懂。第一部分是数据描述:用了哪些影像、多少样本、标注来源、滑坡体面积分布。第二部分是实验表格:至少对比 CNN 与 U-Net、有无预训练、不同损失函数在 IoU 上的差异。第三部分是结果失败分析,这是区分高分和及格的关键。

举例来说,如果模型在阴影区域产生误检,文档里要写清楚原因和尝试过的方案,例如“尝试用 DEM 坡度因子过滤,但当前数据未包含坡度信息,后续计划融合”。这比单纯贴一张精度表更有说服力。可视化脚本可以用matplotlib把原始影像、预测概率图和标注叠加成三联图,输出到visualize/目录,答辩时直接展示。

import matplotlib.pyplot as plt def visualize_sample(image_path, label_path, prob_path, save_path): with rasterio.open(image_path) as src: img_rgb = src.read([3, 2, 1]).transpose(1, 2, 0) # 假设是B8,B3,B2 with rasterio.open(prob_path) as src: prob = src.read(1) fig, axes = plt.subplots(1, 2, figsize=(12, 5)) axes[0].imshow(img_rgb / img_rgb.max()) axes[0].set_title("RGB Image") axes[1].imshow(prob, cmap="Reds", vmin=0, vmax=1) axes[1].set_title("Landslide Probability") plt.tight_layout() plt.savefig(save_path, dpi=150, bbox_inches="tight")

src.read([3, 2, 1])是按通道索引读取,如果你的影像波段顺序是 B2、B3、B4、B8,那么索引 3 是 NIR,索引 2 是 Red,索引 1 是 Green,组合出的“假彩色”能让植被显示为红色,滑坡体显示为灰黄色,对比更明显。保存图片时一定要关掉坐标轴刻度,否则地理坐标和像元坐标混在一起会很乱。

最终提交前,从头到尾跑一遍train.pypredict.py,用conda创建全新环境验证依赖完整。课程设计评审常常因为缺少某个pip install导致现场跑崩,这一分钟检查能救回不少印象分。把预测结果叠加到影像上保存为 PNG,也方便写进报告里,作为你项目成果的直接证据。

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

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

定时任务学习

一.理论基础前置复习:什么是完全二叉树:除了最后一层外其他层都达到最大节点数,且最后一层节点都靠左排列1.小顶堆小顶堆(Min-Heap)是一种特殊的完全二叉树结构,核心原则只有一条:堆序性质:任意一个父节点的值,都小于或等于它的子节点的值。也…

作者头像 李华
网站建设 2026/9/11 18:37:10

小麦病害目标检测数据集 | 小麦病害 锈病检测 智慧农业9060期

小麦病害目标检测数据集 | 小麦病害 锈病检测 智慧农业9060期 数据集概述 本数据集专注于小麦常见病害的视觉检测与识别,服务于智慧农业、病害监测及精准植保。数据涵盖六类小麦健康状况,适配田间巡检、无人机病害普查及植保决策支持等应用。数据集核心…

作者头像 李华
网站建设 2026/9/11 18:27:42

改简历改到“很满“没用,90%的人卡在:只求写得全,没管读得懂

求职简历优化 手把手改简历三步法:模板骨架 → 结果句内容 → AI润色。附三张实操图。投简历没回音,别急着怪行情差。先看简历本身——很多人改简历只做一件事:把纸面填满。模板上实习、项目、证书、技能一个不落,填完自我感觉良…

作者头像 李华
网站建设 2026/9/11 18:27:34

Java学习一 环境配置2 安装和基本使用Idea1

1.下载Idea 下载 IntelliJ IDEA 2.安装idea 自定义一个安装路径,点击下一步,如下图选择,再点击下一步 直接点击安装 3.使用idea 3.1.新建项目 3.2.新建包 右键src 起个包名,有规则 输入完成,回车 新建包,完成 打开包…

作者头像 李华