├── 3722c4492eae4705b2ecb37bb74bee5c.png ├── 37942c082b94762ff4a9086a17e81c31.webp ├── README.md ├── b0d3214d94e24cb9af0259a7879b1a8b.png ├── bfc2f44429df66b50c42a46417120316.webp ├── c2ce0c9d3c4433103df2b221fb770e14.webp ├── e4c50d44ab5668cbc7a59e2126664888.webp ├── predict.py ├── train.py ├── ui.py └── 副本.py /3722c4492eae4705b2ecb37bb74bee5c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/unet-enhanced-covid-lung-lesion-segmentation/0b303dcae8576ca5ba8cdd4e2708f3a599d41cae/3722c4492eae4705b2ecb37bb74bee5c.png -------------------------------------------------------------------------------- /37942c082b94762ff4a9086a17e81c31.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/unet-enhanced-covid-lung-lesion-segmentation/0b303dcae8576ca5ba8cdd4e2708f3a599d41cae/37942c082b94762ff4a9086a17e81c31.webp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |  2 | 3 | ## 1.研究背景与意义 4 | 5 | 新冠肺炎是一种由严重急性呼吸综合征冠状病毒引起的传染病,其在全球范围内造成了严重的公共卫生危机。随着疫情的蔓延,CT扫描成为了一种重要的诊断工具,可以帮助医生快速准确地检测和定位肺部病变。然而,由于新冠肺炎的特殊性,CT影像中的病灶分割任务变得尤为重要。 6 | 7 | 传统的病灶分割方法通常基于手工设计的特征提取器和分类器,这些方法往往需要大量的人工干预和经验调整,且对于不同的病灶类型和数据集可能不具有泛化能力。因此,研究人员开始探索基于深度学习的方法来解决这个问题。 8 | 9 | Unet是一种经典的深度学习架构,被广泛应用于医学图像分割任务。然而,对于新冠肺炎CT影像的病灶分割,传统的Unet存在一些问题。首先,新冠肺炎CT影像中的病灶边界模糊不清,而传统的Unet往往难以准确地捕捉到这些细节信息。其次,新冠肺炎CT影像中的病灶形状和大小变化较大,传统的Unet可能无法很好地适应这种变化。 10 | 11 | 因此,本研究旨在改进Unet的性能,提高新冠肺炎CT影像病灶分割的准确性和鲁棒性。具体来说,我们将尝试以下几个方面的改进: 12 | 13 | 1. 引入注意力机制:通过引入注意力机制,我们可以使网络更加关注病灶区域,从而提高分割的准确性。注意力机制可以根据不同的病灶形状和大小自适应地调整网络的注意力分布。 14 | 15 | 2. 使用深度监督:传统的Unet通常只使用最后一层的输出进行分割,而忽略了中间层的特征。我们将尝试在不同层级上添加监督信号,以引导网络更好地学习特征表示,从而提高分割的准确性。 16 | 17 | 3. 数据增强:由于新冠肺炎CT影像的数据量有限,我们将尝试使用数据增强的方法来扩充数据集,从而提高网络的泛化能力。 18 | 19 | 改进Unet的新冠肺炎CT影像病灶分割系统的意义在于提供了一种高效准确的工具,可以帮助医生更好地诊断和治疗新冠肺炎患者。通过准确地分割出病灶区域,医生可以更好地评估病情的严重程度,制定更合理的治疗方案。此外,该系统还可以用于疫情监测和研究,帮助研究人员更好地理解新冠肺炎的病理过程和传播机制。 20 | 21 | 总之,改进Unet的新冠肺炎CT影像病灶分割系统具有重要的临床应用价值和科学研究意义,有望为新冠肺炎的诊断和治疗提供有力支持。 22 | 23 | 24 | # 2.图片演示 25 | ![在这里插入图片描述](b0d3214d94e24cb9af0259a7879b1a8b.png#pic_center) 26 | ![在这里插入图片描述](3722c4492eae4705b2ecb37bb74bee5c.png#pic_center) 27 | 28 | ![2.png](37942c082b94762ff4a9086a17e81c31.webp) 29 | 30 | # 3.视频演示 31 | 32 | [改进Unet的新冠肺炎CT影像病灶分割系统(部署教程和源码)_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1FN411z7du/?vd_source=ff015de2d29cbe2a9cdbfa7064407a08) 33 | 34 | 35 | # 4.UNet 模型结构特点 36 | 在深度学习领域,卷积神经网络主要被用于图像上的自动特征提取,虽然目前对卷积神经网络所提取的特征还不能进行完全准确的理论解释,但目前主流思想认为:在卷积神经网络的浅层,其提取的特征主要是语义信息较低级的特征,例如边缘、颜色、亮度等,而在卷积神经网络的深层,其提取的特征主要是由低级语义特征组合得到的高级语义特征,例如形状、纹理、深度等,但由于需要更大的特征感受野以及在计算量和显存上的限制,深层特征一般都经过多次池化操作,其分辨率较低。 37 | 卷积神经网络一开始被用于图像分类领域,输入到卷积神经网络的图像在不断进行卷积和池化的过程中,提取到的特征逐渐积累聚合成为高级的语义特征,最终获取到关于整幅图像的唯一语义特征向量,并将该特征向量用于图像的分类工作,但在图像分割领域,最终要求输出每个像素点的分类信息,因此设计模型结构时,需要在最终的输出上保持特征图的分辨率和输入图像接近,同时还要求高分辨率的特征具备较高级的语义信息以提高分割效果。 38 | 在RonnebergerO等人的工作中针对上述问题设计了独特的特征提取结构:UNet模型,该模型被提出之后在后续的代码实现上有一些改进,略微不同于原论文的描述,但目前对UNet的具体实现一般如图所示,其中C表示对应特征图的通道个数,Conv表示卷积层,CBR表示卷积层、Batch Normalization操作以及ReLU激活函数的组合,Conv和CBR前面的3x3或者1×1均表示其中卷积层的卷积核大小。 39 | 总体来说,UNet模型主要包括U型的编解码结构以及中间的横向连接结构,这种结构包含了两种特征传递路径,第一个路径是将深层的特征逐层上采样,第二个路径是将浅层的特征逐层和上采样的深层特征进行融合,这种结构设计最终可以输出高分辨率的特征图,并且该特征图上的每个像素特征都包含高分辨率的低级语义信息以及低分辨率的高级语义信息,这种特征对于语义边界较为模糊的分割任务有较大的帮助,另外相对于其他分割模型,UNet模型的结构简单,参数量较少,且由于横向连接的存在,训练过程中不容易出现梯度消失的情况,收敛速度更快,因此对标注数据数量的要求不高。在医学图像分割领域,一般存在语义边界较为模糊、标注数据量较少两个问题,UNet对于这两种问题都有较好的缓解,因此UNet 结构成为了目前深度学习医学图像分割领域的最常见模型结构。 40 | ![image.png](bfc2f44429df66b50c42a46417120316.webp) 41 | 42 | 43 | 44 | ## 5.核心代码讲解 45 | 46 | #### 5.1 predict.py 47 | 48 | 根据代码,可以将核心部分封装为一个名为"ImageSegmentation"的类。该类包含以下方法: 49 | 50 | 1. `__init__(self, image_path, model_path)`: 初始化方法,接收图片路径和模型路径作为参数,并加载模型。 51 | 52 | 2. `check_version(self)`: 检查主要环境版本的方法。 53 | 54 | 3. `load_dataset(self)`: 加载数据集的方法。 55 | 56 | 4. `read_jpg(self, path)`: 预处理jpg图像的方法。 57 | 58 | 5. `read_png(self, path)`: 预处理png图像的方法。 59 | 60 | 6. `normal_img(self, input_image, input_anno)`: 归一化处理图像的方法。 61 | 62 | 7. `load_images(self, input_images_path, input_anno_path)`: 加载图像和annotations的方法。 63 | 64 | 8. `set_config(self, data_train)`: 设置配置的方法。 65 | 66 | 9. `predict(self)`: 进行预测并绘制图像的方法。 67 | 68 | 69 | 70 | ```python 71 | 72 | class ImageSegmentation: 73 | def __init__(self, image_path, model_path): 74 | self.image_path = image_path 75 | self.model_path = model_path 76 | self.new_model = tf.keras.models.load_model(model_path) 77 | 78 | def check_version(self): 79 | if tf.__version__ != '2.3.0': 80 | print('警告,为了防止出现环境冲突,tensorflow(tensorflow-gpu)版本建议为2.3.0。') 81 | if matplotlib.__version__ != '3.5.1': 82 | print('警告,为了防止出现环境冲突,matplotlib版本建议为3.5.1。') 83 | if np.__version__ != '1.21.5': 84 | print('警告,为了防止出现环境冲突,numpy版本建议为1.21.5。') 85 | 86 | def load_dataset(self): 87 | images = glob.glob(self.image_path) 88 | anno = images 89 | dataset = tf.data.Dataset.from_tensor_slices((images, anno)) 90 | train_count = len(images) 91 | data_train = dataset.take(train_count) 92 | return data_train 93 | 94 | def read_jpg(self, path): 95 | img = tf.io.read_file(path) 96 | img = tf.image.decode_jpeg(img, channels=3) 97 | return img 98 | 99 | def read_png(self, path): 100 | img = tf.io.read_file(path) 101 | img = tf.image.decode_png(img, channels=1) 102 | return img 103 | 104 | def normal_img(self, input_image, input_anno): 105 | input_image = tf.cast(input_image, tf.float32) 106 | input_image = input_image / 127.5 - 1 107 | input_anno -= 1 108 | return input_image, input_anno 109 | 110 | def load_images(self, input_images_path, input_anno_path): 111 | input_image = self.read_jpg(input_images_path) 112 | input_anno = self.read_png(input_anno_path) 113 | input_image = tf.image.resize(input_image, (224, 224)) 114 | input_anno = tf.image.resize(input_anno, (224, 224)) 115 | return self.normal_img(input_image, input_anno) 116 | 117 | def set_config(self, data_train): 118 | data_train = data_train.map(self.load_images, num_parallel_calls=tf.data.experimental.AUTOTUNE) 119 | BATCH_SIZE = 32 120 | data_train = data_train.repeat().shuffle(100).batch(BATCH_SIZE) 121 | return data_train 122 | 123 | def predict(self): 124 | data_train = self.load_dataset() 125 | data_train = self.set_config(data_train) 126 | 127 | for image, mask in data_train.take(1): 128 | pred_mask = self.new_model.predict(image) 129 | pred_mask = tf.argmax(pred_mask, axis=-1) 130 | pred_mask = pred_mask[..., tf.newaxis] 131 | 132 | print(np.unique(pred_mask[0].numpy())) 133 | plt.figure(figsize=(10, 10)) 134 | plt.subplot(1, 3, 1) 135 | plt.imshow(tf.keras.preprocessing.image.array_to_img(image[0])) 136 | plt.subplot(1, 3, 2) 137 | plt.imshow(tf.keras.preprocessing.image.array_to_img(mask[0])) 138 | plt.subplot(1, 3, 3) 139 | plt.imshow(tf.keras.preprocessing.image.array_to_img(pred_mask[0])) 140 | plt.show() 141 | ``` 142 | 143 | 144 | 该程序文件名为predict.py,主要功能是使用训练好的模型对输入的图片进行预测并绘制预测结果。 145 | 146 | 程序首先导入了必要的库,包括tensorflow、matplotlib和numpy等。然后定义了一系列函数,包括加载数据集、检查版本、读取jpg和png图像、图像归一化处理、加载图像和annotations、设置配置等。 147 | 148 | 在主函数中,首先调用check_version()函数检查主要环境版本。然后指定了要预测的图片路径image_path,并调用load_dataset()函数加载数据集。接着调用set_config()函数设置配置。 149 | 150 | 接下来,使用tf.keras.models.load_model()函数加载训练好的模型。然后使用data_train.take(1)获取数据集中的一组图片和对应的mask。使用加载的模型对图片进行预测,得到预测的mask。然后使用matplotlib绘制了原始图片、真实mask和预测mask的对比图。 151 | 152 | 最后调用plt.show()显示绘制的图像。 153 | 154 | 总体来说,该程序文件实现了使用训练好的模型对输入图片进行预测并绘制预测结果的功能。 155 | 156 | #### 5.2 train.py 157 | 158 | ```python 159 | 160 | 161 | # 压缩激活模块 (Squeeze-and-Excitation Module) 162 | class SEBlock(tf.keras.layers.Layer): 163 | def __init__(self, channels, reduction=16): 164 | super(SEBlock, self).__init__() 165 | self.avg_pool = tf.keras.layers.GlobalAveragePooling2D() 166 | self.fc1 = tf.keras.layers.Dense(channels // reduction, activation='relu') 167 | self.fc2 = tf.keras.layers.Dense(channels, activation='sigmoid') 168 | 169 | def call(self, x): 170 | batch, height, width, channels = x.shape 171 | se = self.avg_pool(x) 172 | se = self.fc1(se) 173 | se = self.fc2(se) 174 | se = tf.reshape(se, (-1, 1, 1, channels)) 175 | return x * se 176 | 177 | 178 | # 空洞空间金字塔池化模块 (ASPP) 179 | class ASPP(tf.keras.layers.Layer): 180 | def __init__(self, filters): 181 | super(ASPP, self).__init__() 182 | self.conv1 = tf.keras.layers.Conv2D(filters, 1, activation='relu') 183 | self.conv2 = tf.keras.layers.Conv2D(filters, 3, dilation_rate=3, padding='same', activation='relu') 184 | self.conv3 = tf.keras.layers.Conv2D(filters, 3, dilation_rate=5, padding='same', activation='relu') 185 | self.conv4 = tf.keras.layers.Conv2D(filters, 3, dilation_rate=7, padding='same', activation='relu') 186 | 187 | def call(self, x): 188 | feat1 = self.conv1(x) 189 | feat2 = self.conv2(x) 190 | feat3 = self.conv3(x) 191 | feat4 = self.conv4(x) 192 | combine = tf.concat([feat1, feat2, feat3, feat4], axis=3) 193 | return combine 194 | 195 | 196 | # 空间注意力模块 (Spatial Attention) 197 | class SpatialAttention(tf.keras.layers.Layer): 198 | def __init__(self): 199 | super(SpatialAttention, self).__init__() 200 | self.conv = tf.keras.layers.Conv2D(1, 7, padding='same', activation='sigmoid') 201 | 202 | def call(self, x): 203 | avg_feat = tf.reduce_mean(x, axis=3) 204 | max_feat = tf.reduce_max(x, axis=3) 205 | concat = tf.concat([avg_feat, max_feat], axis=3) 206 | sa = self.conv(concat) 207 | return x * sa 208 | 209 | 210 | # 卷积块注意力模块 (CBAM) 211 | class CBAM(tf.keras.layers.Layer): 212 | def __init__(self, channels): 213 | super(CBAM, self).__init__() 214 | self.se = SEBlock(channels) 215 | self.sa = SpatialAttention() 216 | 217 | def call(self, x): 218 | x = self.se(x) 219 | x = self.sa(x) 220 | return x 221 | 222 | 223 | # 完整的 MA-Unet 模型 224 | class MAUnet(tf.keras.Model): 225 | def __init__(self): 226 | super(MAUnet, self).__init__() 227 | # 编码器 228 | self.encoder_conv1 = tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu') 229 | self.encoder_conv2 = tf.keras.layers.Conv2D(128, 3, padding='same', activation='relu') 230 | self.encoder_conv3 = tf.keras.layers.Conv2D(256, 3, padding='same', activation='relu') 231 | self.encoder_conv4 = tf.keras.layers.Conv2D(512, 3, padding='same', activation='relu') 232 | self.encoder_conv5 = tf.keras.layers.Conv2D(1024, 3, padding='same', activation='relu') 233 | 234 | self.cbam1 = CBAM(64) 235 | self.cbam2 = CBAM(128) 236 | self.cbam3 = CBAM(256) 237 | self.cbam4 = CBAM(512) 238 | 239 | self.aspp = ASPP(1024) 240 | 241 | # 解码器 242 | self.decoder_conv1 = tf.keras.layers.Conv2D(512, 3, padding='same', activation='relu') 243 | self.decoder_conv2 = tf.keras.layers.Conv2D(256, 3, padding='same', activation='relu') 244 | self.decoder_conv3 = tf.keras.layers.Conv2D(128, 3, padding='same', activation='relu') 245 | self.decoder_conv4 = tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu') 246 | 247 | self.output_conv = tf.keras.layers.Conv2D(1, 1, activation='sigmoid') 248 | 249 | def call(self, x): 250 | # 编码器 251 | x1 = self.encoder_conv1(x) 252 | x1 = self.cbam1(x1) 253 | 254 | x2 = self.encoder_conv2(x1) 255 | x2 = self.cbam2(x2) 256 | 257 | x3 = self.encoder_conv3(x2) 258 | x3 = self.cbam3(x3) 259 | 260 | x4 = self.encoder_conv4(x3) 261 | x4 = self.cbam4(x4) 262 | 263 | x5 = self.encoder_conv5(x4) 264 | x5 = self.aspp(x5) 265 | 266 | # 解码器 267 | x6 = self.decoder_conv1(x5) 268 | x7 = self.decoder_conv2(x6) 269 | x8 = self.decoder_conv3(x7) 270 | x9 = self.decoder_conv4(x8) 271 | 272 | out = self.output_conv(x9) 273 | 274 | return out 275 | ``` 276 | 277 | 该程序文件名为train.py,是一个用于训练语义分割模型的代码。该模型使用了MA-Unet架构,包含了压缩激活模块(SEBlock)、空洞空间金字塔池化模块(ASPP)、空间注意力模块(Spatial Attention)和卷积块注意力模块(CBAM)。该模型通过编码器和解码器来提取特征并生成预测结果。在训练过程中,并设置了回调函数来保存训练中的最优权重文件。训练过程中使用了Adam优化器,损失函数为稀疏分类交叉熵,评估指标为准确率。训练完成后,将完整模型保存到本地。 278 | 279 | #### 5.3 ui - 副本.py 280 | 281 | ```python 282 | import tensorflow as tf 283 | import cv2 284 | import numpy as np 285 | import glob 286 | 287 | class Segmentation: 288 | def __init__(self, model_path): 289 | self.model = tf.keras.models.load_model(model_path) 290 | 291 | def read_jpg(self, path): 292 | img = tf.io.read_file(path) 293 | img = tf.image.decode_jpeg(img, channels=3) 294 | return img 295 | 296 | def read_png(self, path): 297 | img = tf.io.read_file(path) 298 | img = tf.image.decode_png(img, channels=1) 299 | return img 300 | 301 | def normal_img(self, input_image, input_anno): 302 | input_image = tf.cast(input_image, tf.float32) 303 | input_image = input_image / 127.5 - 1 304 | input_anno -= 1 305 | return input_image, input_anno 306 | 307 | def load_images(self, input_images_path, input_anno_path): 308 | input_image = self.read_jpg(input_images_path) 309 | input_anno = self.read_png(input_anno_path) 310 | input_image = tf.image.resize(input_image, (224, 224)) 311 | input_anno = tf.image.resize(input_anno, (224, 224)) 312 | return self.normal_img(input_image, input_anno) 313 | 314 | def segment(self, image_path): 315 | image = self.load_images(image_path, image_path) 316 | pred_mask = self.model.predict(image) 317 | pred_mask = tf.argmax(pred_mask, axis=-1) 318 | pred_mask = pred_mask[..., tf.newaxis] 319 | return pred_mask.numpy()[0] 320 | ``` 321 | 322 | 这是一个使用PyQt5库创建的用户界面程序。该程序包含一个主窗口,窗口中有一个标签、三个标签框和两个按钮。标签用于显示图像,标签框用于显示文本信息,按钮用于触发相应的操作。 323 | 324 | 程序的主要功能是加载图像数据集并进行预测。它使用TensorFlow库加载图像数据集,并使用预训练的模型对图像进行分割。分割结果会显示在第三个标签框中。然后,程序会对分割结果进行后处理,计算病灶的面积,并根据面积比例判断病灶的严重程度。最后,程序会将结果显示在文本框中。 325 | 326 | 程序还包含一个线程类Thread_1,用于在后台运行分割和后处理的操作。当用户点击第一个按钮时,程序会创建一个Thread_1的实例,并将图像数据集的路径作为参数传递给它。然后,线程会在后台运行分割和后处理的操作,并将结果显示在界面上。 327 | 328 | 总之,这个程序是一个基于PyQt5和TensorFlow的图像分割应用程序,可以加载图像数据集并进行预测,然后根据预测结果进行后处理和分析。 329 | 330 | #### 5.4 ui.py 331 | 332 | ```import tensorflow as tf 333 | 334 | # 压缩激活模块 (Squeeze-and-Excitation Module) 335 | class SEBlock(tf.keras.layers.Layer): 336 | def __init__(self, channels, reduction=16): 337 | super(SEBlock, self).__init__() 338 | self.avg_pool = tf.keras.layers.GlobalAveragePooling2D() 339 | self.fc1 = tf.keras.layers.Dense(channels // reduction, activation='relu') 340 | self.fc2 = tf.keras.layers.Dense(channels, activation='sigmoid') 341 | 342 | def call(self, x): 343 | batch, height, width, channels = x.shape 344 | se = self.avg_pool(x) 345 | se = self.fc1(se) 346 | se = self.fc2(se) 347 | se = tf.reshape(se, (-1, 1, 1, channels)) 348 | return x * se 349 | 350 | 351 | # 空洞空间金字塔池化模块 (ASPP) 352 | class ASPP(tf.keras.layers.Layer): 353 | def __init__(self, filters): 354 | super(ASPP, self).__init__() 355 | self.conv1 = tf.keras.layers.Conv2D(filters, 1, activation='relu') 356 | self.conv2 = tf.keras.layers.Conv2D(filters, 3, dilation_rate=3, padding='same', activation='relu') 357 | self.conv3 = tf.keras.layers.Conv2D(filters, 3, dilation_rate=5, padding='same', activation='relu') 358 | self.conv4 = tf.keras.layers.Conv2D(filters, 3, dilation_rate=7, padding='same', activation='relu') 359 | 360 | def call(self, x): 361 | feat1 = self.conv1(x) 362 | feat2 = self.conv2(x) 363 | feat3 = self.conv3(x) 364 | feat4 = self.conv4(x) 365 | combine = tf.concat([feat1, feat2, feat3, feat4], axis=3) 366 | return combine 367 | 368 | 369 | # 空间注意力模块 (Spatial Attention) 370 | class SpatialAttention(tf.keras.layers.Layer): 371 | def __init__(self): 372 | super(SpatialAttention, self).__init__() 373 | self.conv = tf.keras.layers.Conv2D(1, 7, padding='same', activation='sigmoid') 374 | 375 | def call(self, x): 376 | avg_feat = tf.reduce_mean(x, axis=3) 377 | max_feat = tf.reduce_max(x, axis=3) 378 | concat = tf.concat([avg_feat, max_feat], axis=3) 379 | sa = self.conv(concat) 380 | return x * sa 381 | 382 | 383 | # 卷积块注意力模块 (CBAM) 384 | class CBAM(tf.keras.layers.Layer): 385 | def __init__(self, channels): 386 | super(CBAM, self).__init__() 387 | self.se = SEBlock(channels) 388 | self.sa = SpatialAttention() 389 | 390 | def call(self, x): 391 | x = self.se(x) 392 | x = self.sa(x) 393 | return x 394 | 395 | 396 | # 完整的 MA-Unet 模型 397 | class MAUnet(tf.keras.Model): 398 | def __init__(self): 399 | super(MAUnet, self).__init__() 400 | # 编码器 401 | self.encoder_conv1 = tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu') 402 | self.encoder_conv2 = tf.keras.layers.Conv2D(128, 3, padding='same', activation='relu') 403 | self.encoder_conv3 = tf.keras.layers.Conv2D(256, 3, padding='same', activation='relu') 404 | self.encoder_conv4 = tf.keras.layers.Conv2D(512, 3, padding='same', activation='relu') 405 | self.encoder_conv5 = tf.keras.layers.Conv2D(1024, 3, padding='same', activation='relu') 406 | 407 | self.cbam1 = CBAM(64) 408 | self.cbam2 = CBAM(128) 409 | self.cbam3 = CBAM(256) 410 | self.cbam4 = CBAM(512) 411 | 412 | self.aspp = ASPP(1024) 413 | 414 | # 解码器 415 | self.decoder_conv1 = tf.keras.layers.Conv2D(512, 3, padding='same', activation='relu') 416 | self.decoder_conv2 = tf.keras.layers.Conv2D(256, 3, padding='same', activation='relu') 417 | self.decoder_conv3 = tf.keras.layers.Conv2D(128, 3, padding='same', activation='relu') 418 | self.decoder_conv4 = tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu') 419 | 420 | self.output_conv = tf.keras.layers.Conv2D(1, 1, activation='sigmoid') 421 | 422 | def call(self, x): 423 | # 编码器 424 | x1 = self.encoder_conv1(x) 425 | x1 = self.cbam1(x1) 426 | 427 | x2 = self.encoder_conv2(x1) 428 | x2 = self.cbam2(x2) 429 | 430 | x3 = self.encoder_conv3(x2) 431 | x3 = self.cbam3(x3) 432 | 433 | x4 = self.encoder_conv4(x3) 434 | x4 = self.cbam4(x4) 435 | 436 | x5 = self.encoder_conv5(x4) 437 | x5 = self.aspp(x5) 438 | 439 | # 解码器 440 | x6 = self.decoder_conv1(x5) 441 | x7 = self.decoder_conv2(x6) 442 | x8 = self.decoder_conv3(x7) 443 | x9 = self.decoder_conv4(x8) 444 | 445 | out = self.output_conv(x9) 446 | 447 | return out 448 | ``` 449 | 450 | 这是一个使用TensorFlow和PyQt5实现的图像分割程序。程序文件名为ui.py。程序主要包括以下几个部分: 451 | 452 | 1. 导入所需的库和模块:包括tensorflow、matplotlib、numpy、glob、os、sys、cv2等。 453 | 454 | 2. 定义了几个自定义的层和模型:包括SEBlock(压缩激活模块)、ASPP(空洞空间金字塔池化模块)、SpatialAttention(空间注意力模块)、CBAM(卷积块注意力模块)和MAUnet(完整的MA-Unet模型)。 455 | 456 | 3. 定义了一些辅助函数:包括load_dataset(加载数据集)、check_version(检查版本)、read_jpg(读取jpg图像)、read_png(读取png图像)、normal_img(归一化处理图像)、load_images(加载图像并进行预处理)、set_config(设置数据集配置)、bingzao_seg(病灶分割函数)和fei_seg(肺分割函数)。 457 | 458 | 4. 程序的主要功能是进行图像分割:通过调用bingzao_seg函数和fei_seg函数实现对病灶和肺的分割,并将分割结果显示在界面上。 459 | 460 | 总体来说,这个程序是一个基于MA-Unet模型的图像分割应用,可以对病灶和肺进行分割,并提供了图像预处理、数据加载和显示等功能。 461 | 462 | ## 6.系统整体结构 463 | 464 | 整体功能和构架概括: 465 | 该工程是一个改进Unet的新冠肺炎CT影像病灶分割系统,包含了预测、训练和用户界面三个主要部分。predict.py文件用于加载训练好的模型并对输入图片进行预测。train.py文件用于训练语义分割模型。ui.py和ui - 副本.py文件是用户界面程序,使用PyQt5库创建了一个界面,可以加载图像数据集并进行预测和分割。 466 | 467 | 下表整理了每个文件的功能: 468 | 469 | | 文件名 | 功能 | 470 | |-------|------| 471 | | predict.py | 加载训练好的模型并对输入图片进行预测 | 472 | | train.py | 训练语义分割模型 | 473 | | ui - 副本.py | 用户界面程序,加载图像数据集并进行预测和分割 | 474 | | ui.py | 用户界面程序,加载图像数据集并进行预测和分割 | 475 | 476 | # 7.CT 图像上的 COVID-19 病灶特征 477 | 一般情况下,COVID-19病灶区域首先表现为肺泡中积聚较多液体,由于液体密度比空气大,因此在CT图像中呈现为半透明糊状的灰色阴影,也称为“磨玻璃影”,如图(a)红框区域所示。另外一些较严重的肺部感染病例中,由于越来越多的液体积聚在肺叶内,密度逐渐增大,导致在CT图像中磨玻璃影逐渐进展为实心白色样的“肺实变”,如图(b)红框区域所示。在更为严重的一些病例中,由于肺小叶壁间质肿胀,肺泡壁增厚,其较大的密度导致在CT图像中看起来就像一条条白线,这些白线将肺实变或者模糊的磨玻璃影分隔开,整体看上去犹如碎石铺成的道路,也因此称为“碎石路征”,如图(c)所示。在COVID-19感染者的CT图像中,从时间范围来看,磨玻璃影一般是首发影像特征,另外两种特征随后单个出现或者同时出现,从空间范围来看,这些影像特征一般遍布双肺多个肺叶,更常见于双肺外围,对于轻症患者或者正在痊愈的患者,这些特征也可能仅累及单个肺叶,COVID-19患者的患病严重程度和双肺的改变程度成正比。从图中的三种病灶表征图像来看,仅从灰度上不能将病灶区域和其他肺部组织进行区分,而且不同的病变在纹理、位置上均有较大区别,手工构造特征难以应对这些情况,因此COVID-19病灶分割任务更适合采用基于 478 | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MO9Xa5Dg-1692944776319)(https://upload-images.jianshu.io/upload_images/29260926-e102444c6ddef0e5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)] 479 | 需要说明的是,虽然CT图像中出现的以上病灶表征对于COVID-19的敏感性很高,但上述影像特征:磨玻璃影、肺实变、碎石路征,也可能在由其他病毒(如流感病毒、腺病毒)引起的肺炎中看到,另外在一些非感染性疾病中也可能出现此类影像。CT中的病灶区域表征对COVID-19敏感性虽然很高,但其特异性较低,如果直接将CT应用于COVID-19患者筛查,那么对发现有这些影像表现的患者应进一步进行全面的检查才能确认诊断结果。但在通过实验室检查明确病因之前,首先隔离这些有典型CT表现的患者,可以有效阻止潜在病毒的进一步传播,另外由于COVID-19患者的患病严重程度和双肺的改变程度成正比,在COVID-19患者的临床监测过程中,对CT图像中的磨玻璃影、肺实变、碎石路征进行分割,可以有效的量化患者的病情严重程度,同时为医生提供更好的可视化结果。 480 | 481 | # 8.改进的MA-Unet结构 482 | 参考该博客给出的改进方案,MA-Unet分为两个部分,分别为编码器与解码器。由于U-Net在跳跃连接时存在无效的语义信息,故在编码器中每一层的特征图在经过卷积层之后加入CBAM。在CBAM的通道注意力模块中,通过通道注意力机制来加强特征,抑制其他无效特征,从而提高网络精度;在CBAM的空间注意力模块中,空间注意力机制可从已进行上采样的特征图中恢复空间信息。针对U-Net对尺度信息提取不足的问题,在编码器最后一层通道数为1024的特征图后加入改进的ASPP模块。通过ASPP模块可以实现对不同尺度特征的提取,在低层阶段获得精细的目标像素分割定位,在高层阶段提取更为有效的特征,从而提高分割精度。在解码器中,为了解决图像存在的不同尺寸的物体的多尺度问题,对每一个尺度的特征图进行串联,在最后进行提取时,特征图包含了各个尺度的信息特征。通过跨尺度信息聚合的方式可以解决“同物异谱,异物同谱”的问题。 483 | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5uGdJOPd-1692944776319)(https://upload-images.jianshu.io/upload_images/29260926-d61c52ac86b85f77.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)] 484 | 485 | # 9.空洞空间金字塔池化模块 486 | 在CT图像中,在进行语义分割的过程中小型对象易被忽略,这种空间信息的损失通常是在网络模型中持续的池化操作导致的。在进行池化时,固定的池化核会让提取到的特征具有一定的局限性。通过提取不同尺度的特征,可以提升对小型病灶的提取效果。针对以上问题,引入ASPP模块[[14](http://www.opticsjournal.net/Articles/OJc5508ed47c4caafd/FullText#Ref_14)]来进行病灶提取。 487 | 其中也在该模块中使用空洞卷积,它可以在不增加网络参数量以及不降低特征维度的情况下增大感受野,从而提升分割效果。针对CT图像中存在的不同尺度物体的分割精度问题,本文将改进的ASPP模块加入到网络中。ASPP模块内经过改进的空洞卷积的空洞率为3、5、7,特征图经不同空洞率的卷积层处理后再进行上采样操作,之后堆叠为特征金字塔,然后对输出结果进行融合。改进的ASPP模块通过不同空洞率的并行滤波器提取到了不同尺度的特征,解决了CT图像分割中病灶大小各不相同的问题。 488 | 489 | ![image.png](e4c50d44ab5668cbc7a59e2126664888.webp) 490 | 491 | # 10.卷积块注意力模块 492 | 卷积块注意力模块结合了通道注意力机制与空间注意力机制,相较于只关注通道域的SE模块,CBAM在通道域和空间域两个维度推算注意力图,可筛选出更重要的语义特征,因此可以取得更好的效果,具体结构如图所示。 493 | ![image.png](c2ce0c9d3c4433103df2b221fb770e14.webp) 494 | 首先将特征图输入到通道注意力模块中得到对应通道的权重,得到加权结果后将加权后的特征图输入空间注意力模块中得到对应权重,加权后输出特征图,计算方法表示为: 495 | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sdfSHmIy-1692944776320)(https://upload-images.jianshu.io/upload_images/29260926-52e090e242d8dc85.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)] 496 | 497 | # 11.系统整合 498 | 下图完整源码&环境部署视频教程&数据集&自定义UI界面 499 | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nrht4BXe-1692944776320)(https://upload-images.jianshu.io/upload_images/29260926-c7afd2a9268bdafc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)] 500 | 501 | 参考博客[《改进Unet的新冠肺炎CT影像病灶分割系统(部署教程和源码)》](https://mbd.pub/o/qunshan/work) 502 | 503 | 504 | 505 | # 12.参考文献 506 | --- 507 | [1]佚名.[Sensitivity of Chest CT for COVID-19: Comparison to RT-PCR](https://d.wanfangdata.com.cn/periodical/331645e3c3ec23ab8434d56f6116601b)[J].Radiology.2020,296(2).E115-E117.DOI:10.1148/radiol.2020200432. 508 | 509 | [2][Singhal Tanu](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Singhal%20Tanu%22).[A Review of Coronavirus Disease-2019 (COVID-19)](https://d.wanfangdata.com.cn/periodical/qk3e_000024556279)[J].The Indian Journal of Pediatrics.2020,87(4).281-286.DOI:10.1007/s12098-020-03263-6. 510 | 511 | [3][Wang, Wenling](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Wang%2C%20Wenling%22),[Xu, Yanli](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Xu%2C%20Yanli%22),[Gao, Ruqin](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Gao%2C%20Ruqin%22),等.[Detection of SARS-CoV-2 in Different Types of Clinical Specimens](https://d.wanfangdata.com.cn/periodical/650281f48024c8673b19fcbe874a222e)[J].JAMA: the Journal of the American Medical Association.2020,323(18).1843-1844.DOI:10.1001/jama.2020.3786. 512 | 513 | [4][Mohamed Abd Elaziz](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Mohamed%20Abd%20Elaziz%22),[Ahmed A. Ewees](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Ahmed%20A.%20Ewees%22),[Dalia Yousri](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Dalia%20Yousri%22),等.[An Improved Marine Predators Algorithm With Fuzzy Entropy for Multi-Level Thresholding: Real World Example of COVID-19 CT Image Segmentation](https://d.wanfangdata.com.cn/periodical/IEEE000001730370)[J].IEEE Access.2020.8DOI:10.1109/ACCESS.2020.3007928. 514 | 515 | [5][Youxin,Wang](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Youxin%2CWang%22),[Haifeng,Hou](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Haifeng%2CHou%22),[Wenrui,Wang](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Wenrui%2CWang%22),等.[Combination of CT and RT-PCR in the screening or diagnosis of COVID-19.](https://d.wanfangdata.com.cn/periodical/Pubmed20202020052019153721)[J].Journal of global health.2020,10(1).010347.DOI:10.7189/jogh.10.010347. 516 | 517 | [6][Tao,Ai](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Tao%2CAi%22),[Zhenlu,Yang](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Zhenlu%2CYang%22),[Hongyan,Hou](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Hongyan%2CHou%22),等.[Correlation of Chest CT and RT-PCR Testing in Coronavirus Disease 2019 (COVID-19) in China: A Report of 1014 Cases.](https://d.wanfangdata.com.cn/periodical/Pubmed20202020052018879639)[J].Radiology.2020.200642.DOI:10.1148/radiol.2020200642. 518 | 519 | [7][Pedro M, Gordaliza](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Pedro%20M%2C%20Gordaliza%22),[Arrate, Mu?oz-Barrutia](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Arrate%2C%20Mu%3Foz-Barrutia%22),[Mónica, Abella](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22M%C3%B3nica%2C%20Abella%22),等.Unsupervised CT Lung Image Segmentation of a Mycobacterium Tuberculosis Infection Model.[J].[Scientific reports](https://sns.wanfangdata.com.cn/perio/EnJour00074220).2018,8(1).9802.DOI:10.1038/s41598-018-28100-x. 520 | 521 | [8][Arnaud Arindra Adiyoso Setio](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Arnaud%20Arindra%20Adiyoso%20Setio%22),[Thijs Kooi](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Thijs%20Kooi%22),[Babak Ehteshami Bejnordi](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Babak%20Ehteshami%20Bejnordi%22),等.[A survey on deep learning in medical image analysis](https://d.wanfangdata.com.cn/periodical/7480d15ce76949ca06f33b73de990a75)[J].Medical image analysis.2017.4260-88. 522 | 523 | [9][Havaei, Mohammad](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Havaei%2C%20Mohammad%22),[Davy, Axel](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Davy%2C%20Axel%22),[Warde-Farley, David](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Warde-Farley%2C%20David%22),等.[Brain tumor segmentation with Deep Neural Networks](https://d.wanfangdata.com.cn/periodical/a7f6ecdf94f76ca778e728552611aec0)[J].Medical image analysis.2017.3518-31.DOI:10.1016/j.media.2016.05.004. 524 | 525 | [10][Wang, Shuo](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Wang%2C%20Shuo%22),[Zhou, Mu](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Zhou%2C%20Mu%22),[Liu, Zaiyi](https://s.wanfangdata.com.cn/paper?q=%E4%BD%9C%E8%80%85:%22Liu%2C%20Zaiyi%22),等.[Central focused convolutional neural networks: Developing a data-driven model for lung nodule segmentation](https://d.wanfangdata.com.cn/periodical/aa68fcf536d6a7ed4e0cf356a63c3143)[J].Medical image analysis.2017.40172-183.DOI:10.1016/j.media.2017.06.014. 526 | 527 | 528 | --- 529 | #### 如果您需要更详细的【源码和环境部署教程】,除了通过【系统整合】小节的链接获取之外,还可以通过邮箱以下途径获取: 530 | #### 1.请先在GitHub上为该项目点赞(Star),编辑一封邮件,附上点赞的截图、项目的中文描述概述(About)以及您的用途需求,发送到我们的邮箱 531 | #### sharecode@yeah.net 532 | #### 2.我们收到邮件后会定期根据邮件的接收顺序将【完整源码和环境部署教程】发送到您的邮箱。 533 | #### 【免责声明】本文来源于用户投稿,如果侵犯任何第三方的合法权益,可通过邮箱联系删除。 -------------------------------------------------------------------------------- /b0d3214d94e24cb9af0259a7879b1a8b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/unet-enhanced-covid-lung-lesion-segmentation/0b303dcae8576ca5ba8cdd4e2708f3a599d41cae/b0d3214d94e24cb9af0259a7879b1a8b.png -------------------------------------------------------------------------------- /bfc2f44429df66b50c42a46417120316.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/unet-enhanced-covid-lung-lesion-segmentation/0b303dcae8576ca5ba8cdd4e2708f3a599d41cae/bfc2f44429df66b50c42a46417120316.webp -------------------------------------------------------------------------------- /c2ce0c9d3c4433103df2b221fb770e14.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/unet-enhanced-covid-lung-lesion-segmentation/0b303dcae8576ca5ba8cdd4e2708f3a599d41cae/c2ce0c9d3c4433103df2b221fb770e14.webp -------------------------------------------------------------------------------- /e4c50d44ab5668cbc7a59e2126664888.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/unet-enhanced-covid-lung-lesion-segmentation/0b303dcae8576ca5ba8cdd4e2708f3a599d41cae/e4c50d44ab5668cbc7a59e2126664888.webp -------------------------------------------------------------------------------- /predict.py: -------------------------------------------------------------------------------- 1 | python 2 | 3 | class ImageSegmentation: 4 | def __init__(self, image_path, model_path): 5 | self.image_path = image_path 6 | self.model_path = model_path 7 | self.new_model = tf.keras.models.load_model(model_path) 8 | 9 | def check_version(self): 10 | if tf.__version__ != '2.3.0': 11 | print('警告,为了防止出现环境冲突,tensorflow(tensorflow-gpu)版本建议为2.3.0。') 12 | if matplotlib.__version__ != '3.5.1': 13 | print('警告,为了防止出现环境冲突,matplotlib版本建议为3.5.1。') 14 | if np.__version__ != '1.21.5': 15 | print('警告,为了防止出现环境冲突,numpy版本建议为1.21.5。') 16 | 17 | def load_dataset(self): 18 | images = glob.glob(self.image_path) 19 | anno = images 20 | dataset = tf.data.Dataset.from_tensor_slices((images, anno)) 21 | train_count = len(images) 22 | data_train = dataset.take(train_count) 23 | return data_train 24 | 25 | def read_jpg(self, path): 26 | img = tf.io.read_file(path) 27 | img = tf.image.decode_jpeg(img, channels=3) 28 | return img 29 | 30 | def read_png(self, path): 31 | img = tf.io.read_file(path) 32 | img = tf.image.decode_png(img, channels=1) 33 | return img 34 | 35 | def normal_img(self, input_image, input_anno): 36 | input_image = tf.cast(input_image, tf.float32) 37 | input_image = input_image / 127.5 - 1 38 | input_anno -= 1 39 | return input_image, input_anno 40 | 41 | def load_images(self, input_images_path, input_anno_path): 42 | input_image = self.read_jpg(input_images_path) 43 | input_anno = self.read_png(input_anno_path) 44 | input_image = tf.image.resize(input_image, (224, 224)) 45 | input_anno = tf.image.resize(input_anno, (224, 224)) 46 | return self.normal_img(input_image, input_anno) 47 | 48 | def set_config(self, data_train): 49 | data_train = data_train.map(self.load_images, num_parallel_calls=tf.data.experimental.AUTOTUNE) 50 | BATCH_SIZE = 32 51 | data_train = data_train.repeat().shuffle(100).batch(BATCH_SIZE) 52 | return data_train 53 | 54 | def predict(self): 55 | data_train = self.load_dataset() 56 | data_train = self.set_config(data_train) 57 | 58 | for image, mask in data_train.take(1): 59 | pred_mask = self.new_model.predict(image) 60 | pred_mask = tf.argmax(pred_mask, axis=-1) 61 | pred_mask = pred_mask[..., tf.newaxis] 62 | 63 | print(np.unique(pred_mask[0].numpy())) 64 | plt.figure(figsize=(10, 10)) 65 | plt.subplot(1, 3, 1) 66 | plt.imshow(tf.keras.preprocessing.image.array_to_img(image[0])) 67 | plt.subplot(1, 3, 2) 68 | plt.imshow(tf.keras.preprocessing.image.array_to_img(mask[0])) 69 | plt.subplot(1, 3, 3) 70 | plt.imshow(tf.keras.preprocessing.image.array_to_img(pred_mask[0])) 71 | plt.show() 72 | -------------------------------------------------------------------------------- /train.py: -------------------------------------------------------------------------------- 1 | python 2 | 3 | 4 | # 压缩激活模块 (Squeeze-and-Excitation Module) 5 | class SEBlock(tf.keras.layers.Layer): 6 | def __init__(self, channels, reduction=16): 7 | super(SEBlock, self).__init__() 8 | self.avg_pool = tf.keras.layers.GlobalAveragePooling2D() 9 | self.fc1 = tf.keras.layers.Dense(channels // reduction, activation='relu') 10 | self.fc2 = tf.keras.layers.Dense(channels, activation='sigmoid') 11 | 12 | def call(self, x): 13 | batch, height, width, channels = x.shape 14 | se = self.avg_pool(x) 15 | se = self.fc1(se) 16 | se = self.fc2(se) 17 | se = tf.reshape(se, (-1, 1, 1, channels)) 18 | return x * se 19 | 20 | 21 | # 空洞空间金字塔池化模块 (ASPP) 22 | class ASPP(tf.keras.layers.Layer): 23 | def __init__(self, filters): 24 | super(ASPP, self).__init__() 25 | self.conv1 = tf.keras.layers.Conv2D(filters, 1, activation='relu') 26 | self.conv2 = tf.keras.layers.Conv2D(filters, 3, dilation_rate=3, padding='same', activation='relu') 27 | self.conv3 = tf.keras.layers.Conv2D(filters, 3, dilation_rate=5, padding='same', activation='relu') 28 | self.conv4 = tf.keras.layers.Conv2D(filters, 3, dilation_rate=7, padding='same', activation='relu') 29 | 30 | def call(self, x): 31 | feat1 = self.conv1(x) 32 | feat2 = self.conv2(x) 33 | feat3 = self.conv3(x) 34 | feat4 = self.conv4(x) 35 | combine = tf.concat([feat1, feat2, feat3, feat4], axis=3) 36 | return combine 37 | 38 | 39 | # 空间注意力模块 (Spatial Attention) 40 | class SpatialAttention(tf.keras.layers.Layer): 41 | def __init__(self): 42 | super(SpatialAttention, self).__init__() 43 | self.conv = tf.keras.layers.Conv2D(1, 7, padding='same', activation='sigmoid') 44 | 45 | def call(self, x): 46 | avg_feat = tf.reduce_mean(x, axis=3) 47 | max_feat = tf.reduce_max(x, axis=3) 48 | concat = tf.concat([avg_feat, max_feat], axis=3) 49 | sa = self.conv(concat) 50 | return x * sa 51 | 52 | 53 | # 卷积块注意力模块 (CBAM) 54 | class CBAM(tf.keras.layers.Layer): 55 | def __init__(self, channels): 56 | super(CBAM, self).__init__() 57 | self.se = SEBlock(channels) 58 | self.sa = SpatialAttention() 59 | 60 | def call(self, x): 61 | x = self.se(x) 62 | x = self.sa(x) 63 | return x 64 | 65 | 66 | # 完整的 MA-Unet 模型 67 | class MAUnet(tf.keras.Model): 68 | def __init__(self): 69 | super(MAUnet, self).__init__() 70 | # 编码器 71 | self.encoder_conv1 = tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu') 72 | self.encoder_conv2 = tf.keras.layers.Conv2D(128, 3, padding='same', activation='relu') 73 | self.encoder_conv3 = tf.keras.layers.Conv2D(256, 3, padding='same', activation='relu') 74 | self.encoder_conv4 = tf.keras.layers.Conv2D(512, 3, padding='same', activation='relu') 75 | self.encoder_conv5 = tf.keras.layers.Conv2D(1024, 3, padding='same', activation='relu') 76 | 77 | self.cbam1 = CBAM(64) 78 | self.cbam2 = CBAM(128) 79 | self.cbam3 = CBAM(256) 80 | self.cbam4 = CBAM(512) 81 | 82 | self.aspp = ASPP(1024) 83 | 84 | # 解码器 85 | self.decoder_conv1 = tf.keras.layers.Conv2D(512, 3, padding='same', activation='relu') 86 | self.decoder_conv2 = tf.keras.layers.Conv2D(256, 3, padding='same', activation='relu') 87 | self.decoder_conv3 = tf.keras.layers.Conv2D(128, 3, padding='same', activation='relu') 88 | self.decoder_conv4 = tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu') 89 | 90 | self.output_conv = tf.keras.layers.Conv2D(1, 1, activation='sigmoid') 91 | 92 | def call(self, x): 93 | # 编码器 94 | x1 = self.encoder_conv1(x) 95 | x1 = self.cbam1(x1) 96 | 97 | x2 = self.encoder_conv2(x1) 98 | x2 = self.cbam2(x2) 99 | 100 | x3 = self.encoder_conv3(x2) 101 | x3 = self.cbam3(x3) 102 | 103 | x4 = self.encoder_conv4(x3) 104 | x4 = self.cbam4(x4) 105 | 106 | x5 = self.encoder_conv5(x4) 107 | x5 = self.aspp(x5) 108 | 109 | # 解码器 110 | x6 = self.decoder_conv1(x5) 111 | x7 = self.decoder_conv2(x6) 112 | x8 = self.decoder_conv3(x7) 113 | x9 = self.decoder_conv4(x8) 114 | 115 | out = self.output_conv(x9) 116 | 117 | return out 118 | -------------------------------------------------------------------------------- /ui.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | # 压缩激活模块 (Squeeze-and-Excitation Module) 4 | class SEBlock(tf.keras.layers.Layer): 5 | def __init__(self, channels, reduction=16): 6 | super(SEBlock, self).__init__() 7 | self.avg_pool = tf.keras.layers.GlobalAveragePooling2D() 8 | self.fc1 = tf.keras.layers.Dense(channels // reduction, activation='relu') 9 | self.fc2 = tf.keras.layers.Dense(channels, activation='sigmoid') 10 | 11 | def call(self, x): 12 | batch, height, width, channels = x.shape 13 | se = self.avg_pool(x) 14 | se = self.fc1(se) 15 | se = self.fc2(se) 16 | se = tf.reshape(se, (-1, 1, 1, channels)) 17 | return x * se 18 | 19 | 20 | # 空洞空间金字塔池化模块 (ASPP) 21 | class ASPP(tf.keras.layers.Layer): 22 | def __init__(self, filters): 23 | super(ASPP, self).__init__() 24 | self.conv1 = tf.keras.layers.Conv2D(filters, 1, activation='relu') 25 | self.conv2 = tf.keras.layers.Conv2D(filters, 3, dilation_rate=3, padding='same', activation='relu') 26 | self.conv3 = tf.keras.layers.Conv2D(filters, 3, dilation_rate=5, padding='same', activation='relu') 27 | self.conv4 = tf.keras.layers.Conv2D(filters, 3, dilation_rate=7, padding='same', activation='relu') 28 | 29 | def call(self, x): 30 | feat1 = self.conv1(x) 31 | feat2 = self.conv2(x) 32 | feat3 = self.conv3(x) 33 | feat4 = self.conv4(x) 34 | combine = tf.concat([feat1, feat2, feat3, feat4], axis=3) 35 | return combine 36 | 37 | 38 | # 空间注意力模块 (Spatial Attention) 39 | class SpatialAttention(tf.keras.layers.Layer): 40 | def __init__(self): 41 | super(SpatialAttention, self).__init__() 42 | self.conv = tf.keras.layers.Conv2D(1, 7, padding='same', activation='sigmoid') 43 | 44 | def call(self, x): 45 | avg_feat = tf.reduce_mean(x, axis=3) 46 | max_feat = tf.reduce_max(x, axis=3) 47 | concat = tf.concat([avg_feat, max_feat], axis=3) 48 | sa = self.conv(concat) 49 | return x * sa 50 | 51 | 52 | # 卷积块注意力模块 (CBAM) 53 | class CBAM(tf.keras.layers.Layer): 54 | def __init__(self, channels): 55 | super(CBAM, self).__init__() 56 | self.se = SEBlock(channels) 57 | self.sa = SpatialAttention() 58 | 59 | def call(self, x): 60 | x = self.se(x) 61 | x = self.sa(x) 62 | return x 63 | 64 | 65 | # 完整的 MA-Unet 模型 66 | class MAUnet(tf.keras.Model): 67 | def __init__(self): 68 | super(MAUnet, self).__init__() 69 | # 编码器 70 | self.encoder_conv1 = tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu') 71 | self.encoder_conv2 = tf.keras.layers.Conv2D(128, 3, padding='same', activation='relu') 72 | self.encoder_conv3 = tf.keras.layers.Conv2D(256, 3, padding='same', activation='relu') 73 | self.encoder_conv4 = tf.keras.layers.Conv2D(512, 3, padding='same', activation='relu') 74 | self.encoder_conv5 = tf.keras.layers.Conv2D(1024, 3, padding='same', activation='relu') 75 | 76 | self.cbam1 = CBAM(64) 77 | self.cbam2 = CBAM(128) 78 | self.cbam3 = CBAM(256) 79 | self.cbam4 = CBAM(512) 80 | 81 | self.aspp = ASPP(1024) 82 | 83 | # 解码器 84 | self.decoder_conv1 = tf.keras.layers.Conv2D(512, 3, padding='same', activation='relu') 85 | self.decoder_conv2 = tf.keras.layers.Conv2D(256, 3, padding='same', activation='relu') 86 | self.decoder_conv3 = tf.keras.layers.Conv2D(128, 3, padding='same', activation='relu') 87 | self.decoder_conv4 = tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu') 88 | 89 | self.output_conv = tf.keras.layers.Conv2D(1, 1, activation='sigmoid') 90 | 91 | def call(self, x): 92 | # 编码器 93 | x1 = self.encoder_conv1(x) 94 | x1 = self.cbam1(x1) 95 | 96 | x2 = self.encoder_conv2(x1) 97 | x2 = self.cbam2(x2) 98 | 99 | x3 = self.encoder_conv3(x2) 100 | x3 = self.cbam3(x3) 101 | 102 | x4 = self.encoder_conv4(x3) 103 | x4 = self.cbam4(x4) 104 | 105 | x5 = self.encoder_conv5(x4) 106 | x5 = self.aspp(x5) 107 | 108 | # 解码器 109 | x6 = self.decoder_conv1(x5) 110 | x7 = self.decoder_conv2(x6) 111 | x8 = self.decoder_conv3(x7) 112 | x9 = self.decoder_conv4(x8) 113 | 114 | out = self.output_conv(x9) 115 | 116 | return out 117 | -------------------------------------------------------------------------------- /副本.py: -------------------------------------------------------------------------------- 1 | python 2 | import tensorflow as tf 3 | import cv2 4 | import numpy as np 5 | import glob 6 | 7 | class Segmentation: 8 | def __init__(self, model_path): 9 | self.model = tf.keras.models.load_model(model_path) 10 | 11 | def read_jpg(self, path): 12 | img = tf.io.read_file(path) 13 | img = tf.image.decode_jpeg(img, channels=3) 14 | return img 15 | 16 | def read_png(self, path): 17 | img = tf.io.read_file(path) 18 | img = tf.image.decode_png(img, channels=1) 19 | return img 20 | 21 | def normal_img(self, input_image, input_anno): 22 | input_image = tf.cast(input_image, tf.float32) 23 | input_image = input_image / 127.5 - 1 24 | input_anno -= 1 25 | return input_image, input_anno 26 | 27 | def load_images(self, input_images_path, input_anno_path): 28 | input_image = self.read_jpg(input_images_path) 29 | input_anno = self.read_png(input_anno_path) 30 | input_image = tf.image.resize(input_image, (224, 224)) 31 | input_anno = tf.image.resize(input_anno, (224, 224)) 32 | return self.normal_img(input_image, input_anno) 33 | 34 | def segment(self, image_path): 35 | image = self.load_images(image_path, image_path) 36 | pred_mask = self.model.predict(image) 37 | pred_mask = tf.argmax(pred_mask, axis=-1) 38 | pred_mask = pred_mask[..., tf.newaxis] 39 | return pred_mask.numpy()[0] 40 | --------------------------------------------------------------------------------