文章目录
- 使用总结
- src
- landsat
- manual_annotations
- train
- utils
- convert_patch_to_3channels_image
- copy_trained_weights_to_manual_annotations_evaluation
- count_fire_pixels
- crop_manual_annotations
- download_dataset.py
- download_manual_annotations
- download_weights
- generate_nonfire_masks
- make_masks
- masks_with_at_least_n_fire_pixels
- purge_logs
- split_dataset
- transform_mask
- unzip_download_weights
- upzip_manual_annotations
- unzip_patches
代码链接:Github
使用总结
1.文件目录结构
以下是从Google Drive下载的完整文件夹,目录结构如下:
active-fire-detection
- datasets:由已处理的256x256图像及火焰掩码组成,并不包含生成数据集的原始Landsat-8图像,仅包含已处理过的patches
- continents:数据按大洲进行了划分
- manually_annotated:手动注释的数据集
- weights:保存卷积神经网络的权重
- intersection:交集掩模训练得到的模型
- unet_16f_2conv_762:保存模型文件model_unet_Intersection_final_weights.h5
- unet_64f_2conv_10c:保存模型文件model_unet_Intersection_final_weights.h5
- unet_64f_2conv_762:保存模型文件model_unet_Intersection_final_weights.h5
- kumar-roy:kumar算法训练得到的模型
- unet_16f_2conv_762:保存模型文件model_unet_kumar-roy_final_weights.h5
- unet_64f_2conv_10c:保存模型文件model_unet_kumar-roy_final_weights.h5
- unet_64f_2conv_762:保存模型文件model_unet_kumar-roy_final_weights.h5
- murphy:murphy算法训练得到的模型
- unet_16f_2conv_762:保存模型文件model_unet_murphy_final_weights.h5
- unet_64f_2conv_10c:保存模型文件model_unet_murphy_final_weights.h5
- unet_64f_2conv_762:保存模型文件model_unet_murphy_final_weights.h5
- schroder:schroder算法训练得到的模型
- unet_16f_2conv_762:保存模型文件model_unet_schroder_final_weights.h5
- unet_64f_2conv_10c:保存模型文件model_unet_schroder_final_weights.h5
- unet_64f_2conv_762:保存模型文件model_unet_schroder_final_weights.h5
- voting:投票掩模训练得到的模型
- unet_16f_2conv_762:保存模型文件model_unet_voting_final_weights.h5
- unet_64f_2conv_10c:保存模型文件model_unet_voting_final_weights.h5
- unet_64f_2conv_762:保存模型文件model_unet_voting_final_weights.h5
2.下载完整数据集/样本数据集
涉及脚本:src/utils/download_dataset.py
、src/utils/unzip_patches.py
- 1.使用
download_dataset.py
下载完整数据集的压缩文件到输出目录中,可自定义的常量:DOWNLOAD_FULL_DATASET = True
:选择下载完整数据集或样本数据集。OUTPUT_DIR=r'D:\dataset'
:完整数据集压缩文件的输出目录。REGIONS
:自定义需要下载的大洲数据。OUTPUT_SAMPLES
:样本数据集压缩文件的输出目录。
此处设置DOWNLOAD_FULL_DATASET = True
、OUTPUT_DIR=r'D:\dataset'
,下载后得到:
文件目录如下:
South_America.zip
- z001054.zip
- z001054_masks.zip
- z001054_masks_derivates.zip
- ... ...
- 2.使用
src/utils/unzip_patches.py
压缩文件,可自定义常量:FULL_DATASET=True
:设置解压完整数据集或样本数据集。- 完整数据集相关常量:
FULL_DATASET_ZIPS_PATH
:完整数据集压缩路径。FULL_DATASET_UNZIP_PATH
:完整数据集解压路径。
- 样本数据集相关常量:
SAMPLES_ZIP_PATH
:样本数据集压缩路径。IMAGES_PATH
:样本数据集图片保存路径。MASKS_PATH
:样本数据集掩模保存路径。MANUAL_ANNOTATIONS_PATH
:手动注释数据保存路径。
当前设置:
FULL_DATASET_ZIPS_PATH = 'D:\dataset'
FULL_DATASET_UNZIP_PATH = 'D:\dataset\South_America-unzip'
解压成功后输出:
得到解压文件目录如下:
- South_America-unzip
- images
- patches:11875张图像,格式为tif
- masks
- intersection:交集掩模,格式为tif
- patches:三种算法生成的掩模文件,格式为tif
- voting:投票掩模,格式为tif
注意:
- 1.
FULL_DATASET_ZIPS_PATH
应设为压缩文件上级目录路径,如,若压缩文件South_America.zip
路径为D:\dataset\South_America.zip
,则应设为FULL_DATASET_ZIPS_PATH = 'D:\dataset'
。 - 2.
FULL_DATASET_UNZIP_PATH
相应地可设为FULL_DATASET_UNZIP_PATH = 'D:\dataset\South_America-unzip'
。 - 3.上述路径均采用
\
,且不带r
。
3.下载手动注释数据集
手动注释案例:
4.获取对应的模型
以下是src
5.下载模型权重
使用脚本src/utils/download_weights.py
、src/utils/unzip_download_weights.py
。
- 1.使用脚本
src/utils/download_weights.py
下载权重压缩包,可自定义常量如下:OUTPUT_DIR = '../../weights/'
:权重压缩包输出目录。WEIGHT_FILES
:自行选择数据集对应的模型,包括voting
、schroeder
、murphy
、kumar-roy
、intersection
。
此处设置OUTPUT_DIR = D:\weights
进行下载,得到压缩文件目录如下:
- voting.zip
- unet_16f_2conv_762:model_unet_Voting_final_weights.h5
- unet_64f_2conv_10c:model_unet_Voting_final_weights.h5
- unet_64f_2conv_762:model_unet_Voting_final_weights.h5
- schroeder.zip
- unet_16f_2conv_762:model_unet_Schroeder_final_weights.h5
- unet_64f_2conv_10c:model_unet_Schroeder_final_weights.h5
- unet_64f_2conv_762:model_unet_Schroeder_final_weights.h5
- murphy.zip
- unet_16f_2conv_762:model_unet_Murphy_final_weights.h5
- unet_64f_2conv_10c:model_unet_Murphy_final_weights.h5
- unet_64f_2conv_762:model_unet_Murphy_final_weights.h5
- kumar-roy.zip
- unet_16f_2conv_762:model_unet_Kumar-Roy_final_weights.h5
- unet_64f_2conv_10c:model_unet_Kumar-Roy_final_weights.h5
- unet_64f_2conv_762:model_unet_Kumar-Roy_final_weights.h5
- intersection.zip
- unet_16f_2conv_762:model_unet_Intersection_final_weights.h5
- unet_64f_2conv_10c:model_unet_Intersection_final_weights.h5
- unet_64f_2conv_762:model_unet_Intersection_final_weights.h5
- 2.使用脚本
src/utils/unzip_download_weights.py
解压权重压缩包到train
或manual_annotations/cnn_compare
目录下(不会解压缩到weights
下)。可自定义常量如下:ZIPED_WEIGHTS_DIR = '../../weights'
:指定权重压缩包目录。UNZIP_TO_TRAIN = True
:是否将文件解压缩到训练目录。TRAIN_DIR = '../train/'
:训练目录路径。UNZIP_TO_MANUAL_ANNOTATNIOS_CNN = True
:是否将文件解压缩到手动注释评估目录。UNZIP_TO_MANUAL_ANNOTATIONS_CNN = '../manual_annotations/cnn_compare'
:手动注释评估目录路径
此处设置:
ZIPED_WEIGHTS_DIR = 'D:\weights'
UNZIP_TO_TRAIN = True
TRAIN_DIR = 'D:\\train'#'D:\train'会报错
UNZIP_TO_MANUAL_ANNOTATNIOS_CNN = False
UNZIP_TO_MANUAL_ANNOTATIONS_CNN = None
得到目录D:\train
:
- voting
- unet_16f_2conv_762
- train_output:model_unet_Voting_final_weights.h5
- unet_64f_2conv_10c
- train_output:model_unet_Voting_final_weights.h5
- unet_64f_2conv_762
- train_output:model_unet_Voting_final_weights.h5
- schroeder
- unet_16f_2conv_762
- train_output:model_unet_Schroeder_final_weights.h5
- unet_64f_2conv_10c
- train_output:model_unet_Schroeder_final_weights.h5
- unet_64f_2conv_762
- train_output:model_unet_Schroeder_final_weights.h5
- murphy
- unet_16f_2conv_762
- train_output:model_unet_Murphy_final_weights.h5
- unet_64f_2conv_10c
- train_output:model_unet_Murphy_final_weights.h5
- unet_64f_2conv_762
- train_output:model_unet_Murphy_final_weights.h5
- kumar-roy
- unet_16f_2conv_762
- train_output:model_unet_Kumar-Roy_final_weights.h5
- unet_64f_2conv_10c
- train_output:model_unet_Kumar-Roy_final_weights.h5
- unet_64f_2conv_762
- train_output:model_unet_Kumar-Roy_final_weights.h5
- intersection
- unet_16f_2conv_762
- train_output:model_unet_Intersection_final_weights.h5
- unet_64f_2conv_10c
- train_output:model_unet_Intersection_final_weights.h5
- unet_64f_2conv_762
- train_output:model_unet_Intersection_final_weights.h5
6.查看/重新生成训练、测试、验证数据集CSV文件
训练网络前需将数据集分为训练、测试、验证共三个数据子集,在github中每个模型用于训练、测试、验证的样本(包括图像及掩码)均由对应模型路径下的CSV文件给出。例如,使用intersection
(交集数据集)训练的unet_16f_2conv_762
模型对应的数据集路径src/train/kumar-roy/unet_16f_2conv_762/dataset
:
images_masks.csv
:所有图像及掩模的tif文件名。images_train.csv
、masks_train.csv
:训练集图像及掩模的tif文件名。images_test.csv
、masks_test.csv
:测试集图像及掩模的tif文件名。images_val.csv
、masks_val.csv
:验证集图像及掩模的tif文件名。
研究中使用CSV文件保存图像、掩模的文件名来划分数据集。也可使用脚本src/utils/split_dataset.py
来创建新的训练集、测试集、验证集进行模型训练,即,创建新的CSV文件。split_dataset.py
可设置常量如下:
MASK_ALGORITHM = 'Kumar-Roy'
:使用的掩模算法名称,可选Schroeder
、Murphy
、Kumar-Roy
、Intersection
或Voting
。- 生成
src/train/kumar-roy/unet_*
模型:使用Kumar-Roy
。 - 生成
src/train/murphy/unet_*
模型:使用Murphy
。 - 生成
src/train/schroeder/unet_*
模型:使用Schroeder
。
- 生成
IMAGES_PATH='../../dataset/images/patches/'
:图像patches路径。MASKS_PATH='../../dataset/masks/patches/'
:掩模路径。OUTPUT_FOLDER='../../dataset/'
:新数据集输出目录。TRAIN_RATIO
、VALIDATION_RATIO
、TEST_RATIO
:设置训练、验证、测试数据的占比。
执行脚本后可生成images_masks.csv
、images_*.csv
和masks_*.csv
文件,代表完成了新的数据集(包含了训练、测试、验证集)。
7.训练模型
以使用kumar-roy数据集训练unet_16f_2conv_762为例,需使用脚本src/train/kumar-roy/unet_16f_2conv_762/train.py
。
import os
import warnings
warnings.filterwarnings("ignore")
import glob
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
import tensorflow as tf
from keras.backend.tensorflow_backend import set_session
import keras
from keras.optimizers import *
from keras.callbacks import ModelCheckpoint, EarlyStopping
from tensorflow.python.keras import backend as K
from generator import *
from models import *
from metrics import *
from plot_history import plot_history
import sys
import pandas as pd
from tqdm import tqdm
from keras.preprocessing.image import ImageDataGenerator
#是否绘制训练和验证的图表
PLOT_HISTORY = True
#指出所用的掩模算法,包括Schoeder、Murphy、Kumar-Roy
MASK_ALGORITHM = 'Kumar-Roy'
#滤波器、通道数目
N_FILTERS = 16
N_CHANNELS = 3
#模型训练超参数
EPOCHS = 50
BATCH_SIZE = 64
IMAGE_SIZE = (256, 256)
#指定使用unet模型
MODEL_NAME = 'unet'
RANDOM_STATE = 42
#图像和掩模的路径
IMAGES_PATH = '../../../../dataset/images/patches/'
MASKS_PATH = '../../../../dataset/masks/patches/'
#数据集中images_masks.csv的位置
IMAGES_DATAFRAME = './dataset/images_masks.csv'
#训练结果输出目录
OUTPUT_DIR = './train_output/'
#线程数
WORKERS = 4
#与EarlyStopping回调函数相关的参数,若训练过程中验证集上的损失在连续多少个epoch中没有改善,则训练会提前终止,依次防止过拟合。
EARLY_STOP_PATIENCE = 5
#检查点保存的周期,ModelCheckpoint回调函数会按照这个参数定义的时间间隔保存模型的权重。
CHECKPOINT_PERIOD = 5
#保存模型权重的文件名模板,可以根据模型名称、掩模算法和epoch数生成唯一的文件名。模板中的双大括号{{epoch:02d}}会被实际的epoch数替换,格式化为两位数字。
CHECKPOINT_MODEL_NAME = 'checkpoint-{}-{}-epoch_{{epoch:02d}}.hdf5'.format(MODEL_NAME, MASK_ALGORITHM)
#设置训练开始的初始轮次,若大于0则表示训练不是从第一轮开始,而是从INITIAL_EPOCH指定的轮数恢复训练。
INITIAL_EPOCH = 0
RESTART_FROM_CHECKPOINT = None
#INITIAL_EPOCH>0表示希望从检查点开始恢复训练
if INITIAL_EPOCH > 0:
RESTART_FROM_CHECKPOINT = os.path.join(OUTPUT_DIR, 'checkpoint-{}-{}-epoch_{:02d}.hdf5'.format(MODEL_NAME, MASK_ALGORITHM, INITIAL_EPOCH))
#最终权重文件名称
FINAL_WEIGHTS_OUTPUT = 'model_{}_{}_final_weights.h5'.format(MODEL_NAME, MASK_ALGORITHM)
CUDA_DEVICE = 1
if not os.path.exists(OUTPUT_DIR):
os.makedirs(OUTPUT_DIR)
#设置CUDA设备及TensorFlow会话
os.environ["CUDA_VISIBLE_DEVICES"] = str(CUDA_DEVICE)
try:
config = tf.compat.v1.ConfigProto()
config.gpu_options.allow_growth = True
sess = tf.compat.v1.Session(config=config)
K.set_session(sess)
except:
pass
try:
np.random.bit_generator = np.random._bit_generator
except:
pass
df = pd.read_csv(IMAGES_DATAFRAME, header=None, names=['images', 'masks'])
images_df = df[ ['images'] ]
masks_df = df[ ['masks'] ]
#获取训练、验证和测试数据集的CSV文件,这些文件中保存了图像patch/掩模文件对应的文件名,可通过x_train['images']/y_train['masks']获取
x_train = pd.read_csv('./dataset/images_train.csv')
y_train = pd.read_csv('./dataset/masks_train.csv')
x_val = pd.read_csv('./dataset/images_val.csv')
y_val = pd.read_csv('./dataset/masks_val.csv')
x_test = pd.read_csv('./dataset/images_test.csv')
y_test = pd.read_csv('./dataset/masks_test.csv')
#根据csv文件生成训练集图像及对应掩模的完整路径列表
images_train = [ os.path.join(IMAGES_PATH, image) for image in x_train['images'] ]
masks_train = [ os.path.join(MASKS_PATH, mask) for mask in y_train['masks'] ]
#根据csv文件生成验证集图像及对应掩模的完整路径列表
images_validation = [ os.path.join(IMAGES_PATH, image) for image in x_val['images'] ]
masks_validation = [ os.path.join(MASKS_PATH, mask) for mask in y_val['masks'] ]
#使用列表,创建训练和验证数据的数据加载器(类似DataLoader)
train_generator = generator_from_lists(images_train, masks_train, batch_size=BATCH_SIZE, random_state=RANDOM_STATE, image_mode="762")
validation_generator = generator_from_lists(images_validation, masks_validation, batch_size=BATCH_SIZE, random_state=RANDOM_STATE, image_mode="762")
#传入模型参数以获取模型
model = get_model(MODEL_NAME, input_height=IMAGE_SIZE[0], input_width=IMAGE_SIZE[1], n_filters=N_FILTERS, n_channels=N_CHANNELS)
#编译模型,使用Adam优化器和二元交叉熵损失函数
model.compile(optimizer = Adam(), loss = 'binary_crossentropy', metrics = ['accuracy'])
#打印模型摘要
model.summary()
#定义早期停止和模型检查点
es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=EARLY_STOP_PATIENCE)
checkpoint = ModelCheckpoint(os.path.join(OUTPUT_DIR, CHECKPOINT_MODEL_NAME), monitor='loss', verbose=1,
save_best_only=True, mode='auto', period=CHECKPOINT_PERIOD)
#若初始轮次大于0,则加载对应轮次的模型参数
if INITIAL_EPOCH > 0:
model.load_weights(RESTART_FROM_CHECKPOINT)
#训练模型
print('Training using {}...'.format(MASK_ALGORITHM))
history = model.fit_generator(
train_generator,
steps_per_epoch=len(images_train) // BATCH_SIZE,
validation_data=validation_generator,
validation_steps=len(images_validation) // BATCH_SIZE,
callbacks=[checkpoint, es],
epochs=EPOCHS,
workers=WORKERS,
initial_epoch=INITIAL_EPOCH
)
print('Train finished!')
#保存模型及权重
print('Saving weights')
model_weights_output = os.path.join(OUTPUT_DIR, FINAL_WEIGHTS_OUTPUT)
model.save_weights(model_weights_output)
print("Weights Saved: {}".format(model_weights_output))
#PLOT_HISTORY为True,则绘制训练和验证的图表
if PLOT_HISTORY:
plot_history(history, OUTPUT_DIR)
src
landsat
create_masks
处理Landsat 8卫星图像数据,检测图像中的火点,并生成火点掩模。
GC_L8 = 'https://storage.googleapis.com/gcp-public-data-landsat/LC08/01/'
:Landsat 8数据的Google Cloud存储路径。MTL_EXTENSION
:元数据文件的扩展名。IN_DIR = r'../../dataset/images/tif_images/'
:输入目录。OUT_DIR = r'../../dataset/masks/'
:输出目录。getMTLParameters(MTL)
:解析元数据文件(MTL)并返回文件相关参数。get_bounds(width,height,transform)
:计算图像的边界,返回包含图像边界像素的元组(left, bottom, right, top)
。get_extent(dataset)
:getReflectance(band, add_band, mult_band, sun_elevation)
:计算反射率并根据太阳高度角进行图像校正。get_saturation(BQA)
:检测饱和像素。save_masks(out_dir, image_name, profile, fire_mask, reference)
:保存火点掩模文件。get_split(fileIMG,out_path)
:将图像分割成小块(256x256)。- 以下是Schroeder、Kumar-Roy、Murphy三种算法的实现:
- Schroeder算法:
Seq1(bands, r75, diff75)
:Schroeder方程1。Seq2 (bands)
:Schroeder方程2。Seq3 (r75, diff75)
:Schroeder方程3。Seq4and5 (bands, r75, unamb_fires, potential_fires, water)
:Schroeder方程4、5。Seq6 (bands)
:Schroeder方程6。Seq7_8_9 (bands)
:Schroeder方程7、8、9。
- Kumar算法:
Geq12(bands)
:Kumar-Roy方程12。Geq13 (bands, eq12_mask)
:Kumar-Roy方程13。Geq14 (bands)
:Kumar-Roy方程14。Geq15(bands)
:Kumar-Roy方程15。Geq16 (bands)
:Kumar-Roy方程16。pixelVal(p7,ef,ep,ew)
:辅助函数。Geq8and9 (bands, valid, unamb_fires, potential_fires, water)
:- ---- Kumar-Roy方程8和9。
- Murphy算法:
Meq2 (bands)
:Murphy方程2。Meq3 (bands)
:Murphy方程3。
- Schroeder算法:
getFireMaskGOLI (bands)
:Kumar-Roy火点检测的函数,返回生成的掩模图像。getFireMaskMurphy (bands, saturated)
:Murphy火点检测的函数,返回生成的掩模图像。getFireMaskSchroeder (bands)
:Schroeder火点检测的函数,返回生成的掩模图像。processImage (in_dir, out_dir, image_name, Aref, Mref, SE, sat)
:读取图像、获取元数据、执行火点检测和保存结果。共包含包括输入输出目录、图像名称、辐射定标系数、反射率定标系数、太阳高度角和饱和像素掩模共七个参数。- 1.打开指定路径的图像文件,并获取其元数据,并读取1-7波段的数据。
- 2.对读取的波段数据计算反射率,并根据太阳高度角进行校正。
- 3.分别使用三种算法计算图像掩模,并使用
save_masks
保存掩模,同时将掩模图像分割为256x256大小,并保存在patches目录下。
- 执行部分:
- 1.循环遍历BQA图像文件,提取图像名称。
- 2.读取BQA图像文件,获取饱和像素掩模。
- 3.从AWS获取元数据文件并解析参数。
- 4.调用processImage函数处理图像,包括读取图像、执行火点检测和保存结果。
downloader
从Google Cloud Storage下载Landsat 8卫星图像数据,并将它们保存到本地目录。
GC_L8 = 'https://storage.googleapis.com/gcp-public-data-landsat/LC08/01/'
:Google Cloud Storage的Landsat 8数据路径。MTL_EXTENSION = '_MTL.txt'
:元数据文件的扩展名。ext = '.TIF'
:图像文件的扩展名。IN_DIR = r'../../dataset/'
:输入目录。OUT_DIR = r'../../dataset/images/tif_images/'
:输出目录。csv
:包含产品ID的CSV文件。
- 执行流程:
- 1.读取CSV文件中的卫星图像产品ID。
- 2.循环遍历每个产品ID,对于每个产品ID:
- 记录开始时间、检查日志文件是否存在,如果不存在,则执行下载操作。
- 下载BQA图像和所有波段图像,更新元数据并保存图像。
- 记录日志信息,记录结束时间并打印处理时间。
manual_annotations
evaluate_v1
评估多个火点检测算法的性能。它计算了各种统计指标,如Jaccard指数、F1分数、像素精度和混淆矩阵。
train
intersection
unet_16f_2conv_762
dataset
保存训练、测试、验证的图像及其掩模对应的tif文件名。
train_output
保存训练好的模型(.h5
文件)。
evaluate_v1
评估火灾检测模型的性能。通过加载真实标签和预测结果,计算各种统计指标,如Jaccard指数、F1分数、像素精度和混淆矩阵。
generate_images_csv
读取图像和掩模文件,生成一个包含图像和对应掩模文件名的CSV文件,并使用这些文件名来分割数据集为训练集、验证集和测试集。这些生成的数据集使用CSV文件格式保存在dataset目录下。
此模块生成了dataset下的images_train'
、masks_train
、images_val
、masks_val
、images_test
、masks_test
文件。
generator
定义线程安全的generator_from_lists()
,函数声明如下:
@threadsafe_generator
def generator_from_lists(images_path, masks_path, batch_size=32, shuffle = True, random_state=None, image_mode='10bands')
输入图像patches路径、图像掩模路径,生成一个包含图像及对应掩模数据的列表,当列表长度达到batch_size大小时,生成一个数据批次并清空列表。
inference
使用预训练的深度学习模型对测试集进行推断,并保存推断结果。
models
定义了FCN(全连接神经网络)和几个不同版本的unet模型。
- 全卷积神经网络模型(FCN):
FCN(nClasses, input_height=128, input_width=128, n_filters = 16, dropout = 0.1, batchnorm = True)
- UNet模型
def get_unet(nClasses, input_height=256, input_width=256, n_filters = 16, dropout = 0.1, batchnorm = True, n_channels=10):
- 通用卷积函数
def conv2d_block(input_tensor, n_filters, kernel_size = 3, batchnorm = True)
- 较小的UNet模型
def get_unet_small1(nClasses, input_height=128, input_width=128, n_filters = 16, dropout = 0.1, batchnorm = True, n_channels=3)
- 更小的UNet模型
def get_unet_small2(nClasses, input_height=128, input_width=128, n_filters = 16, dropout = 0.1, batchnorm = True, n_channels=3)
get_model()
:传入参数,并根据模型名传入相应参数、创建模型对象并返回。
def get_model(model_name, nClasses=1, input_height=128, input_width=128, n_filters = 16, dropout = 0.1, batchnorm = True, n_channels=10)
plot_history
绘制训练历史的准确性和损失图,用于可视化深度学习模型训练过程中的性能。
def plot_history(history, out_dir)
history
:模型训练过程中记录的历史对象,包含训练和验证的准确性和损失数据。out_dir
:保存绘图的输出目录。
train
UNet模型的完整深度学习训练流程,使用了TensorFlow和Keras库进行训练。
import os
import warnings
warnings.filterwarnings("ignore")
import glob
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
import tensorflow as tf
from keras.backend.tensorflow_backend import set_session
import keras
# from keras import optimizers
from keras.optimizers import *
from keras.callbacks import ModelCheckpoint, EarlyStopping
from tensorflow.python.keras import backend as K
from generator import *
from models import *
from metrics import *
from plot_history import plot_history
import sys
import pandas as pd
from tqdm import tqdm
from keras.preprocessing.image import ImageDataGenerator
#是否绘制训练和验证的图表
PLOT_HISTORY = True
#定义使用的掩模算法名称,可选:Schoeder、Murphy、Kumar-Roy、交集掩模、投票掩模
MASK_ALGORITHM = 'Intersection'
#滤波器、通道数目
N_FILTERS = 16
N_CHANNELS = 3
#定义模型训练超参数
EPOCHS = 50
BATCH_SIZE = 64
IMAGE_SIZE = (256, 256)
#指定使用unet模型
MODEL_NAME = 'unet'
RANDOM_STATE = 42
#图像和掩模的路径
IMAGES_PATH = '../../../../dataset/images/patches/'
MASKS_PATH = '../../../../dataset/masks/intersection/'
#输出目录
OUTPUT_DIR = './train_output/'
#线程数
WORKERS = 4
#与EarlyStopping回调函数相关的参数,若训练过程中验证集上的损失在连续多少个epoch中没有改善,则训练会提前终止,依次防止过拟合。
EARLY_STOP_PATIENCE = 5
#检查点保存的周期,ModelCheckpoint回调函数会按照这个参数定义的时间间隔保存模型的权重。
CHECKPOINT_PERIOD = 5
#保存模型权重的文件名模板,可以根据模型名称、掩模算法和epoch数生成唯一的文件名。模板中的双大括号{{epoch:02d}}会被实际的epoch数替换,格式化为两位数字。
CHECKPOINT_MODEL_NAME = 'checkpoint-{}-{}-epoch_{{epoch:02d}}.hdf5'.format(MODEL_NAME, MASK_ALGORITHM)
#设置训练开始的初始轮次,若大于0则表示训练不是从第一轮开始,而是从INITIAL_EPOCH指定的轮数恢复训练。
INITIAL_EPOCH = 0
#存储检查点文件的路径,如果设置了从检查点恢复训练,这个变量将被用来加载之前保存的模型权重。
RESTART_FROM_CHECKPOINT = None
#INITIAL_EPOCH>0表示希望从检查点开始恢复训练
if INITIAL_EPOCH > 0:
#构建检查点文件完整路径,即,将输出目录、模型名称、掩模算法和初始epoch数组合成文件名来加载模型权重
RESTART_FROM_CHECKPOINT = os.path.join(OUTPUT_DIR, 'checkpoint-{}-{}-epoch_{:02d}.hdf5'.format(MODEL_NAME, MASK_ALGORITHM, INITIAL_EPOCH))
#最终权重文件名称
FINAL_WEIGHTS_OUTPUT = 'model_{}_{}_final_weights.h5'.format(MODEL_NAME, MASK_ALGORITHM)
CUDA_DEVICE = 1
if not os.path.exists(OUTPUT_DIR):
os.makedirs(OUTPUT_DIR)
#设置CUDA设备及TensorFlow会话
os.environ["CUDA_VISIBLE_DEVICES"] = str(CUDA_DEVICE)
try:
config = tf.compat.v1.ConfigProto()
config.gpu_options.allow_growth = True
sess = tf.compat.v1.Session(config=config)
K.set_session(sess)
except:
pass
#设置Numpy随机数生成器
try:
np.random.bit_generator = np.random._bit_generator
except:
pass=
#获取训练、验证和测试数据集的CSV文件,这些文件中保存了图像patch/掩模文件对应的文件名,可通过x_train['images']/y_train['masks']获取
x_train = pd.read_csv('./dataset/images_train.csv')
y_train = pd.read_csv('./dataset/masks_train.csv')
x_val = pd.read_csv('./dataset/images_val.csv')
y_val = pd.read_csv('./dataset/masks_val.csv')
x_test = pd.read_csv('./dataset/images_test.csv')
y_test = pd.read_csv('./dataset/masks_test.csv')
#根据csv文件生成训练集图像及对应掩模的完整路径列表
images_train = [ os.path.join(IMAGES_PATH, image) for image in x_train['images'] ]
masks_train = [ os.path.join(MASKS_PATH, mask) for mask in y_train['masks'] ]
#根据csv文件生成验证集图像及对应掩模的完整路径列表
images_validation = [ os.path.join(IMAGES_PATH, image) for image in x_val['images'] ]
masks_validation = [ os.path.join(MASKS_PATH, mask) for mask in y_val['masks'] ]
#使用列表,创建训练和验证数据的数据加载器(类似DataLoader)
train_generator = generator_from_lists(images_train, masks_train, batch_size=BATCH_SIZE, random_state=RANDOM_STATE, image_mode="762")
validation_generator = generator_from_lists(images_validation, masks_validation, batch_size=BATCH_SIZE, random_state=RANDOM_STATE, image_mode="762")
#获取模型
model = get_model(MODEL_NAME, input_height=IMAGE_SIZE[0], input_width=IMAGE_SIZE[1], n_filters=N_FILTERS, n_channels=N_CHANNELS)
#编译模型,使用Adam优化器和二元交叉熵损失函数
model.compile(optimizer = Adam(), loss = 'binary_crossentropy', metrics = ['accuracy'])
#打印模型摘要
model.summary()
#定义早期停止和模型检查点
es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=EARLY_STOP_PATIENCE)
checkpoint = ModelCheckpoint(os.path.join(OUTPUT_DIR, CHECKPOINT_MODEL_NAME), monitor='loss', verbose=1,
save_best_only=True, mode='auto', period=CHECKPOINT_PERIOD)
#若初始轮次大于0,则加载对应轮次的模型参数
if INITIAL_EPOCH > 0:
model.load_weights(RESTART_FROM_CHECKPOINT)
#训练模型
print('Training using {}...'.format(MASK_ALGORITHM))
history = model.fit_generator(
train_generator,
steps_per_epoch=len(images_train) // BATCH_SIZE,
validation_data=validation_generator,
validation_steps=len(images_validation) // BATCH_SIZE,
callbacks=[checkpoint, es],
epochs=EPOCHS,
workers=WORKERS,
initial_epoch=INITIAL_EPOCH
)
print('Train finished!')
#保存模型及权重
print('Saving weights')
model_weights_output = os.path.join(OUTPUT_DIR, FINAL_WEIGHTS_OUTPUT)
model.save_weights(model_weights_output)
print("Weights Saved: {}".format(model_weights_output))
#PLOT_HISTORY为True,则绘制训练和验证的图表
if PLOT_HISTORY:
plot_history(history, OUTPUT_DIR)
流程包括:
- 数据准备
- 模型构建并训练
- 早期停止
- 检查点保存
- 结果绘图
metrics
计算了两个评估图像分割模型性能的指标,包括Dice系数、像素精度。
unet_64f_2conv_10c
unet_64f_2conv_762
utils
convert_patch_to_3channels_image
从TIF图像中读取特定波段,处理和归一化图像数据,并保存为 PNG 格式。
IMAGE
:要读取的TIF图像路径。OUTPUT_DIR
:输出图像的目录。OUTPUT_IMAGE_NAME
:保存的图像文件名。MAX_PIXEL_VALUE
:定义最大像素值,用于归一化图像数据。这里的最大值是 65535,表示 16 位图像。get_img_762bands(path)
:接受图像路径,读取TIF的7、6、2波段并归一化处理。D:\ActiveFireData\active-fire-detection\datasets\continents\patches_and_masks\Africa
下有相应图片可提取,注意,不应从_masks
、_masks_derivates
中提取,二者是掩膜二值图像,没有波段可提取。- 剩余部分代码:创建输出目录
OUTPUT_DIR
、将归一化后的像素转换到0~255,并转为PNG格式。
copy_trained_weights_to_manual_annotations_evaluation
遍历指定的训练目录,找到每个算法和架构的最终权重文件,并将它们复制到手动注释的 CNN 评估目录下的对应位置。
TRAINING_DIR
:训练数据目录。
count_fire_pixels
读取指定路径下的掩模图像,计算图像中火灾像素的数目,并打印结果。
MASK_NAME
:掩模图像名称。MASK_PATH
:掩膜图像路径。PATCHES_PATTERN
:若MASK_NAME
不以tif
或TIF
结尾,则加上PATCHES_PATTERN
。get_mask_arr(path)
:读取掩模图像,并将其转换为一维的像素数组。main
:- 给图像名称加上后缀名,得到
image_name
。 - 查找
MASK_PATH
下所有匹配image_name
的图像路径,并返回一个列表images_path
。 - 打印掩模名称和找到的图像总数。
- 获取掩膜对应的一维像素数组,计算火灾像素的数目。
- 给图像名称加上后缀名,得到
crop_manual_annotations
从输入目录中读取图像和相关的掩模及注释,使用 get_split 函数将这些文件分割成补丁(patches,指分割后的256x256图像),并保存到指定的输出目录。
INPUT_ANNOTATION_SCENE_DIR
:手工标注的文件目录。OUTPUT_ANNOTATION_PATCHES
:输出图像和掩模的目录。create_folder(folder_path)
:检查指定文件夹是否存在。split_images(input_dir, output_dir)
:接受输入目录、输出目录作为参数,分割图像。main
:定义图像文件目录images_dir
、掩模文件目录masks_dir
、手动标注文件目录manual_annotation_dir
,定于输出图像块目录output_images_dir
、output_masks_dir
输出掩模目录、output_manual_annotation_dir
手工注释补丁目录。调用split_images()
,分别处理图像、掩模和手动注释目录,将其分割并保存到对应的输出目录。
download_dataset.py
从Google Drive下载完整或部分数据集,patches,指分割后的256x256图像。文件夹结构如下:
可在REGIONS
常量中注释掉不需要的部分来选择性下载某些区域的数据集,以节省空间。
DOWNLOAD_FULL_DARASET=True
:布尔变量,True
:下载完整数据集。False
:只下载样本数据。
OUTPUT_DIR
:下载文件输出目录。BASE_URL = 'https://drive.google.com/uc?id={}'
:Google Drive文件下载的URL模板。REGIONS
:不同地区的名称及其对应的 Google Drive 文件 ID。SUBSET_SAMPLES
:样本数据的Google Drive文件ID。OUT_PUT_SAMPLES
:样本文件下载的输出目录。download_file(file_id, output)
:接收文件ID和输出路径,打印下载信息并下载文件。main
:DOWNLOAD_FULL_DARASET=True
:遍历REGIONS
中的所有区域,并为之构建zip文件夹、下载文件。DOWNLOAD_FULL_DARASET=False
:下载样本数据。
download_manual_annotations
从Google Drive下载手动标注的图像到本地目录,可选择下载手动标注的图像(patches,指分割后的256x256图像)或用于生成这些patches的完整场景图像。手动标注的图像(patches,指分割后的256x256图像)的文件夹位置为:
用于生成这些patches的完整场景图像位置为:
OUTPUT_DIR
:输出目录。DOWNLOAD_SCENES
:决定是否下载原始场景或图像块。BASE_URL
:Google Drive的基本下载链接格式,其中,{}
会被替换为文件ID。PATCHES
:保存patches文件的Google Drive ID。SCENES
:保存scenes文件Google Drive ID。download_file(file_id, output)
:根据文件ID和输出路径下载文件,并输出下载的URL。download_files(files)
:以文件夹路径作为参数,遍历其中的所有文件名,下载对应的ZIP文件。main
:创建输出目录、根据DOWNLOAD_SCENES
来决定下载scenes文件或patches文件。
download_weights
下载对应模型的参数,在Google Drive中,这些模型参数存储在:
代码中使用字典WEIGHT_FILES
记录了基本的Google Drive URL,其中,download_file()
用于下载指定文件,main
程序中遍历权重文件列表,将文件下载到指定目录。
generate_nonfire_masks
用于给patches(分割后的256x256图像)生成非火灾掩模(用0标记),并将其保存到输出目录下。
INPUT_PATCHES_DIR
:patches(分割后的256x256图像)的路径。OUTPUT_DIR
:生成掩模的输出路径。MASK_NOTATION
:在patches文件名中加上’v1’,表明是该patch对应的掩模。NON_FIRE_MASK
:创建的256x256全零数组,作为非火灾掩模。main
:将INPUT_PATCHES_DIR
下的所有patches,为其生成对应的非火灾掩模,该掩模文件名为原patch文件名中将_RT_
替换为_RT_v1_
得到。
make_masks
通过组合三种手工算法生成的掩模(原始掩模)来生成新的掩模,组合方式包括并集、交集和投票。若某个算法的掩模不存在,则生成一个仅包含无关值(全零或其他默认值)的掩模。
MASKS_FOR_COMPLETE_SCENE=True
:指示是否为整个图像生成掩膜。MASKS_DIR
:掩模路径。MASKS_ALGORITHMS = ['Schroeder', 'Murphy', 'Kumar-Roy']
:生成原始掩模的算法。OUTPUT_DIR = '../../dataset/manual_annotations/scenes/masks/'
:输出目录。OUTPUT_INTERSECTION = OUTPUT_DIR
:设置交集掩模的输出目录。OUTPUT_VOTING = OUTPUT_DIR
:设置投票掩模的输出目录。NUM_VOTINGS
:投票阈值。IMAGE_SIZE = (256, 256)
:图像大小。load_masks_in_dataframe()
:加载掩模文件并返回dataframe数据,该数据中保存了原始图像对应的三种算法生成的掩模数据,可通过算法名直接获取对应的掩模。make_intersection_masks(dataframes)
:通过与运算得到三种算法的交集掩模,并保存到输出目录。make_voting_masks(dataframes)
:获取三种算法的投票掩模。main
:根据MASKS_FOR_COMPLETE_SCENE
变量的值,决定计算整个原图像的掩模或是patches的掩模。之后计算交集掩模、投票掩模并保存到输出目录中。
masks_with_at_least_n_fire_pixels
由用户指定阈值NUM_PIXELS
(最小火灾像素的数目),打印超过该阈值的所有掩模路径,及掩模中火灾像素的数量。
purge_logs
用于删除训练、推理和评估过程中生成的日志文件夹。
TRAIN_DIR
:训练目录路径。MANUAL_ANNOTATIONS_DIR
:手动注释目录路径。delete_log_from_algorithms(base_path)
:用于删除基于算法的日志。delete_log_from_architecture(base_path)
:用于删除基于架构的日志。delete_folder(folder_path)
:用于删除指定路径的文件夹。
split_dataset
读取掩模图像,生成一个包含图像和对应掩模文件名的CSV文件,并使用这些文件名来分割数据集为训练集、验证集和测试集。
MASK_ALGORITHM = 'Kumar-Roy'
:使用的掩模算法名称。IMAGES_PATH
:图像patches路径。MASKS_PATH
:掩模路径。OUTPUT_FOLDER
:输出文件夹路径。IMAGES_DATAFRAME
:输出csv文件路径。- 执行流程:
- 1.找出掩模算法对应的掩模文件。
- 2.创建dataframe,将图像名及对应的掩模文件名存入csv文件中。
- 3.读取CSV文件并划分为训练集、验证集和测试集,并保存为CSV文件。
transform_mask
将掩模图像中的非零像素值设置为255(白色,代指火灾像素,即将图像转换为二值图像),然后将处理后的图像保存为PNG格式。
MASK_PATH
:指定掩模图像路径。OUTPUT_DIR
:PNG图像保存路径。OUTPUT_NAME
:PNG图像文件名。
unzip_download_weights
ZIPED_WEIGHTS_DIR
:模型权重文件路径。TRAIN_DIR
:训练目录路径。UNZIP_TO_MANUAL_ANNOTATIONS_CNN
:手动注释实现的CNN对照模型路径。UNZIP_TO_TRAIN
、UNZIP_TO_MANUAL_ANNOTATNIOS_CNN
:是否将解压后的权重文件复制到训练目录和CNN对照模型目录。create_folder(folder_path)
:指定路径,创建文件夹。unzip_to_folder(input_zip, output_folder)
:将zip文件解压到指定文件夹。- 执行流程:
- 1.将
ZIPED_WEIGHTS_DIR
下的压缩zip文件全部解压缩,并保存到临时目录。 - 2.遍历临时目录中的每个算法对应的架构文件夹,构建权重文件的路径,并根据设置复制权重文件到目标目录。
- 3.删除临时目录及其所有内容。
- 1.将
upzip_manual_annotations
解压图像、手动注释和掩模的zip文件到相应的输出目录。
INPUT_DIR
、OUTPUT_DIR
:定义压缩文件目录和解压文件的保存路径。UNZIP_SCENES
:指示解压整个场景还是图像patch。create_folder(folder_path)
:指定路径,创建文件夹。unzip_to_folder(input_zip, output_folder)
:指定路径,解压文件夹。- 执行流程:
- 1.根据
UNZIP_SCENES
解压整张图像或图像patch。 - 2.调用
unzip_to_folder
将zip文件解压到输出目录。
- 1.根据
unzip_patches
- 执行流程:
- 1.根据
FULL_DATASET
选择解压完整数据集或样本数据集。 - 2.若解压完整数据集,则解压Landsat 压缩文件,并将patches文件解压到相应目录。
- 3.若解压样本数据集,则使用临时目录解压,并重命名特定的掩模文件。
- 1.根据