一、ResNet网络
ResNet-50是一种流行的深度学习模型,属于残差网络(ResNet)的一个变种,由微软研究院的Kaiming He等人在2015年提出。这个模型在处理图像识别和图像分类任务上表现出色,是计算机视觉领域常用的预训练模型之一。
ResNet-50的核心特点:
- 层级结构:ResNet-50包含48个卷积层,1个全连接层和1个平均池化层,共50层。这些层通过使用跳跃连接(skip connections)策略相互连接。
- 残差学习(Residual Learning):
- ResNet的主要创新是引入了残差学习的概念,以解决深度神经网络训练过程中的退化问题(即随着层数增加,网络性能反而下降的现象)。
- 在ResNet中,输入不仅传递给下一层,还通过跳跃连接直接传递到后面的层,这样模型只需要学习输入和输出之间的残差(即差异),而不是直接学习输出。
- 跳跃连接(Skip Connections):
- 跳跃连接允许输入直接跳过一个或多个层,连接到更深层的网络部分。
- 这种连接帮助梯度直接传递到较浅层,避免了深层网络训练时可能出现的梯度消失问题。
- 广泛应用:
- ResNet-50因其出色的性能和相对较低的计算复杂度,在许多图像处理任务中被广泛采用,如图像分类、物体检测和分割等。
- 通常作为许多视觉任务的特征提取器的基础,常常在预训练的状态下使用,利用在大型数据集(如ImageNet)上学习到的丰富特征。
二、U-net网络
U-Net 是一种专为医学图像分割而设计的卷积神经网络架构,最初由 Olaf Ronneberger、Philipp Fischer 和 Thomas Brox 在 2015 年提出。它的名称来源于其U形的结构,这种结构有效地结合了图像的位置和上下文信息,使得网络能够在进行像素级预测时具有非常高的准确性。U-Net 的设计特别适用于需要精确定位的应用,如医学成像中的器官分割、细胞计数等。
U-Net 的核心特点:
- 对称结构:
- U-Net 的架构包括一个收缩路径(向下采样或编码器)和一个对称的扩展路径(向上采样或解码器),形成一个 “U” 形结构。
- 收缩路径通过卷积和池化操作捕获图像的上下文信息,而扩展路径则通过上采样和卷积操作进行精确的定位。
- 跳跃连接(Skip Connections):
- 在对称的编码器和解码器部分之间,存在跳跃连接,将编码器部分的特征图直接与解码器部分对应层的特征图进行连接。
- 这种连接方式帮助网络在上采样过程中恢复图像的详细信息,这对于实现高精度的图像分割至关重要。
- 数据效率:
- U-Net 以其对训练数据的高效利用而著名,这使得它即使在标注数据较少的情况下也能训练出性能良好的模型。
- 这一特性对于医学图像处理尤为重要,因为医学图像数据的获取和标注通常成本较高。
- 多尺度特征融合:
- 通过在解码器阶段融合来自编码器阶段的特征,U-Net 能够利用不同层次的特征信息,提高分割的准确性。
三、基于ResNet与U-net的图像分割网络
通过将ResNet50与U-Net结合,利用ResNet50强大的特征提取能力,还能通过U-Net的解码器结构恢复图像的详细空间信息,这种结构适合处理需要高精度像素级输出的图像分割任务。
1、为什么使用ResNet50与U-net进行结合
- 增强的特征提取能力:
- ResNet50作为一种预训练的深度卷积网络,其结构设计(残差块)能够有效地避免在训练深层网络时常见的梯度消失问题,使得网络可以从深层次学习复杂的特征,这对于图像分割尤其重要。
- 利用ResNet50的预训练权重,可以捕捉到丰富的图像特征,这些特征对于分割任务来说是一种强大的支持。
- 有效的空间信息恢复:
- U-Net的结构特点是其对称的编码器-解码器结构,其中解码器部分通过上采样层逐渐恢复图像的尺寸和细节,这对于进行像素级预测至关重要。
- 通过在解码器中融入与编码器对应层的跳跃连接,可以有效地恢复那些在卷积和池化过程中丢失的空间信息,这样可以提高分割的精度和质量。
2、结合后的作用
- ResNet50(编码器):
- 特征提取:作为网络的前半部分,ResNet50负责从输入图像中提取复杂的特征表示。每一层都逐渐捕捉更深层次的特征,这些特征包括了图像中的对象边界、纹理等重要信息。
- 层次化的特征学习:不同深度的层捕获不同尺度的特征,这些特征将被用于后续的上采样和融合过程。
- U-Net结构(解码器):
- 特征融合和上采样:解码器部分开始于ResNet50的最深层输出,然后通过上采样层逐步放大特征图。在每个上采样阶段,都会将先前的编码器层的输出通过跳跃连接并入到当前层,帮助恢复丢失的空间信息。
- 细节恢复:每一步的上采样都伴随着从对应编码器层传递来的特征的合并,这样可以确保在放大的特征图中保留尽可能多的原始图像细节和上下文信息,从而在最终输出层能生成精确的分割掩码。
3、代码
import os
import json
import cv2
import numpy as np
from skimage.transform import resize
import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Conv2D, UpSampling2D, Concatenate, Dropout
from tensorflow.keras.applications import ResNet50
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau
from tensorflow.keras.metrics import Precision, Recall, AUC
from sklearn.model_selection import train_test_split
def load_and_process_images(data_dir, target_size=(256, 256)):
images = []
masks = []
for filename in os.listdir(data_dir):
if filename.endswith('.jpg'):
image_path = os.path.join(data_dir, filename)
annotation_path = os.path.join(data_dir, filename.replace('.jpg', '.json'))
if not os.path.exists(annotation_path):
continue
image = cv2.imread(image_path)
if image is None:
continue
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image_resized = resize(image, target_size, mode='reflect', anti_aliasing=True)
scale_y = image_resized.shape[0] / image.shape[0]
scale_x = image_resized.shape[1] / image.shape[1]
mask_resized = np.zeros((target_size[0], target_size[1]), dtype=np.float32)
with open(annotation_path, 'r') as file:
data = json.load(file)
for region in data['ann']:
x1, y1, x2, y2, _ = [int(coord) for coord in region]
x1_resized = int(x1 * scale_x)
x2_resized = int(x2 * scale_x)
y1_resized = int(y1 * scale_y)
y2_resized = int(y2 * scale_y)
mask_resized[y1_resized:y2_resized, x1_resized:x2_resized] = 1
images.append(image_resized)
masks.append(mask_resized[..., np.newaxis])
return np.array(images, dtype=np.float32), np.array(masks, dtype=np.float32)
def build_resnet_unet(input_shape=(256, 256, 3)):
base_model = ResNet50(weights='imagenet', include_top=False, input_tensor=Input(shape=input_shape))
for layer in base_model.layers:
layer.trainable = False
skip_connections = ['conv1_relu', 'conv2_block3_out', 'conv3_block4_out', 'conv4_block6_out']
encoder_outputs = [base_model.get_layer(name).output for name in skip_connections]
bridge = base_model.get_layer('conv5_block3_out').output
x = bridge
num_filters = [512, 256, 128, 64, 32]
for i, f in enumerate(num_filters):
x = UpSampling2D((2, 2))(x)
if i < len(encoder_outputs):
x = Concatenate()([x, encoder_outputs[-(i + 1)]])
x = Conv2D(f, (3, 3), padding='same')(x)
x = Dropout(0.1)(x)
outputs = Conv2D(1, (1, 1), activation='sigmoid')(x)
model = Model(inputs=base_model.input, outputs=outputs)
return model
data_dir = 'C:\\Users\\11522\\Desktop\\qhk'
images, masks = load_and_process_images(data_dir)
X_train, X_val, y_train, y_val = train_test_split(images, masks, test_size=0.2, random_state=42)
model = build_resnet_unet()
model.compile(optimizer=Adam(learning_rate=1e-4), loss='binary_crossentropy',
metrics=['accuracy', Precision(), Recall(), AUC()])
callbacks = [
ModelCheckpoint('best_model.h5', save_best_only=True, monitor='val_loss'),
EarlyStopping(patience=10, monitor='val_loss'),
ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=5, min_lr=1e-6)
]
history = model.fit(X_train, y_train, validation_data=(X_val, y_val), epochs=50, batch_size=8, callbacks=callbacks)
scores = model.evaluate(X_val, y_val)
print(f'Loss: {scores[0]}, Accuracy: {scores[1]}, Precision: {scores[2]}, Recall: {scores[3]}, AUC: {scores[4]}')
-
图像和掩码加载处理 (
load_and_process_images
)- 功能: 加载指定目录下的图像及其对应的标注文件(JSON格式),并处理这些图像和标注生成适合模型输入的数据格式。
- 实现
- 遍历目录中的文件,筛选出以
.jpg
结尾的图像文件。 - 对每个图像文件,找到相应的JSON标注文件(将
.jpg
替换为.json
)。 - 读取图像并将其从BGR格式转换为RGB格式(深度学习中常用的格式)。
- 使用
resize
函数调整图像大小到指定的尺寸(256x256),同时应用反射模式和抗锯齿处理来优化图像质量。 - 根据标注文件中的坐标信息,生成相应的掩码(mask),掩码的大小也调整到与图像相同的尺寸。
- 遍历目录中的文件,筛选出以
- 输出: 处理后的图像数组和对应的掩码数组。
2. 构建ResNet-U-Net模型 (
build_resnet_unet
)- 功能: 利用预训练的ResNet50模型构建一个U-Net结构的网络,用于图像分割。
- 实现
- 加载预训练的ResNet50模型,不包括顶层,并设置所有层为不可训练。
- 选择四个关键的中间层作为跳跃连接的源。
- 在ResNet50的输出端添加上采样和卷积层来构建U-Net的解码器部分。在每次上采样后,将特征与对应的跳跃连接层的输出合并。
- 最终通过一个卷积层输出预测的分割掩码。
- 输出: 一个具有U-Net架构的模型,其输入为图像,输出为图像的分割掩码。
3. 训练和评估模型
- 功能: 使用加载的图像数据和掩码训练构建的ResNet-U-Net模型,并评估其性能。
- 实现
- 分割数据为训练集和验证集。
- 编译模型,设置优化器、损失函数和评估指标。
- 使用回调函数进行模型保存、早停和学习率调整。
- 训练模型,并在训练结束后对验证集进行评估。
- 输出: 输出训练过程中的损失和精度,以及验证集上的性能指标。
4、效果图
四、数据集地址
数据集以及更多代码请查看资源 资源中有完整的预处理代码 所需环境及甲骨文的图片及标记json文件(6000+照片)
数据集地址
五、完整代码及最佳训练文件地址
请私聊博主