医学图像分割模型U-Net介绍和Kaggle的Top1解决方案源码解析

简介: 医学图像分割模型U-Net介绍和Kaggle的Top1解决方案源码解析

介绍

计算机视觉是人工智能的一个领域,训练计算机解释和理解视觉世界。利用来自相机、视频和深度学习模型的数字图像,机器可以准确地识别和分类物体,然后对它们看到的东西做出反应。

在过去几年里,深度学习使得计算机视觉领域迅速发展。在这篇文章中,我想讨论计算机视觉中一个叫做分割的特殊任务。尽管研究人员已经提出了许多方法来解决这个问题,但我将讨论一种特殊的架构,即UNET,它使用一个完全卷积的网络模型来完成这项任务。

我们将利用UNET构建Kaggle SCIENCE BOWL 2018 挑战赛的第一解决方案。

先决条件

这篇文章是假设读者已经熟悉机器学习和卷积网络的基本概念。同时,他/她也有一些使用Python和Keras库的ConvNets的工作知识。

什么是市场细分?

分割的目的是将图像的不同部分分割成可感知的相干部分。细分有两种类型:

  • 语义分割(基于标记类的像素级预测)
  • 实例分割(目标检测和目标识别)

在这篇文章中,我们将主要关注语义分割。

U-NET是什么?

U-Net创建于2015年,是一款专为生物医学图像分割而开发的CNN。目前,U-Net已经成为一种非常流行的用于语义分割的端到端编解码器网络。它有一个独特的上下结构,有一个收缩路径和一个扩展路径。

640.png

U-NET 结构

640.png

U-Net下采样路径由4个block组成,其层数如下:

3x3 CONV (ReLU +批次标准化和Dropout使用)

3x3 CONV (ReLU +批次标准化和Dropout使用)

2x2 最大池化

当我们沿着这些块往下走时,特征图会翻倍,从64开始,然后是128、256和512。

瓶颈层由2个CONV层、BN和Dropout组成

与下采样相似上采样路径由4个块组成,层数如下:

反卷积层

从特征图中拼接出相应的收缩路径

3x3 CONV (ReLU +BN和Dropout)

3x3 CONV (ReLU +BN和Dropout)

KAGGLE DATA SCIENCE BOWL 2018 CHALLENGE

这项挑战的主要任务是在图像中检测原子核。通过自动化核检测,你可以帮助更快的解锁治疗。识别细胞核是大多数分析的起点,因为人体30万亿个细胞中的大多数都包含一个充满DNA的细胞核,而DNA是给每个细胞编程的遗传密码。识别细胞核使研究人员能够识别样本中的每个细胞,并通过测量细胞对各种治疗的反应,研究人员可以了解潜在的生物学过程。

640.jpg

样本图像,目标和方法

我们将使用U-Net这个专门为分割任务而设计的CNN自动生成图像遮罩

640.png

导入所有必要的包和模块

importosimportsysimportrandomimportwarningsimportnumpyasnpimportpandasaspdimportmatplotlib.pyplotaspltfromtqdmimporttqdmfromitertoolsimportchainfromskimage.ioimportimread, imshow, imread_collection, concatenate_imagesfromskimage.transformimportresizefromskimage.morphologyimportlabelfromkeras.modelsimportModel, load_modelfromkeras.layersimportInputfromkeras.layers.coreimportDropout, Lambdafromkeras.layers.convolutionalimportConv2D, Conv2DTransposefromkeras.layers.poolingimportMaxPooling2Dfromkeras.layers.mergeimportconcatenatefromkeras.callbacksimportEarlyStopping, ModelCheckpointfromkerasimportbackendasKimporttensorflowastfIMG_WIDTH=128IMG_HEIGHT=128IMG_CHANNELS=3TRAIN_PATH='./U_NET/train/'TEST_PATH='./U_NET/validation/'warnings.filterwarnings('ignore', category=UserWarning, module='skimage')
seed=42random.seed=seednp.random.seed=seed

为训练和测试数据收集我们的文件名

train_ids=next(os.walk(TRAIN_PATH))[1]
test_ids=next(os.walk(TEST_PATH))[1]

创建尺寸为128 x 128的图像遮罩(黑色图像)

print('Getting and resizing training images ... ')
X_train=np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS), dtype=np.uint8)
Y_train=np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.bool)#Re-sizingourtrainingimagesto128x128#Notesys.stdoutprintsinfothatcanbeclearedunlikeprint.
#UsingTQDMallowsustocreateprogressbarssys.stdout.flush()
forn, id_intqdm(enumerate(train_ids), total=len(train_ids)):
path=TRAIN_PATH+id_img=imread(path+'/images/'+id_+'.png')[:,:,:IMG_CHANNELS]
img=resize(img, (IMG_HEIGHT, IMG_WIDTH), mode='constant', preserve_range=True)
X_train[n] =imgmask=np.zeros((IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.bool)
#Nowwetakeallmasksassociatedwiththatimageandcombinethemintoonesinglemaskformask_fileinnext(os.walk(path+'/masks/'))[2]:
mask_=imread(path+'/masks/'+mask_file)
mask_=np.expand_dims(resize(mask_, (IMG_HEIGHT, IMG_WIDTH), mode='constant',
preserve_range=True), axis=-1)
mask=np.maximum(mask, mask_)
#Y_trainisnowoursinglemaskassociatedwithourimageY_train[n] =mask#GetandresizetestimagesX_test=np.zeros((len(test_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS), dtype=np.uint8)
sizes_test= []
print('Getting and resizing test images ... ')
sys.stdout.flush()
#Hereweresizeourtestimagesforn, id_intqdm(enumerate(test_ids), total=len(test_ids)):
path=TEST_PATH+id_img=imread(path+'/images/'+id_+'.png')[:,:,:IMG_CHANNELS]
sizes_test.append([img.shape[0], img.shape[1]])
img=resize(img, (IMG_HEIGHT, IMG_WIDTH), mode='constant', preserve_range=True)
X_test[n] =imgprint('Done!')

建立U-Net模型

defmy_iou_metric(label, pred):     metric_value=tf.py_func(iou_metric_batch, [label, pred], tf.float32)    
returnmetric_value#BuildU-Netmodel#Notewemakeourlayersvaraiblessothatwecanconcatenateorstack#Thisisrequiredsothatwecanre-createourU-NetModelinputs=Input((IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS))
s=Lambda(lambdax: x/255) (inputs)c1=Conv2D(16, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (s)
c1=Dropout(0.1) (c1)
c1=Conv2D(16, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c1)
p1=MaxPooling2D((2, 2)) (c1)c2=Conv2D(32, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (p1)
c2=Dropout(0.1) (c2)
c2=Conv2D(32, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c2)
p2=MaxPooling2D((2, 2)) (c2)c3=Conv2D(64, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (p2)
c3=Dropout(0.2) (c3)
c3=Conv2D(64, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c3)
p3=MaxPooling2D((2, 2)) (c3)c4=Conv2D(128, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (p3)
c4=Dropout(0.2) (c4)
c4=Conv2D(128, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c4)
p4=MaxPooling2D(pool_size=(2, 2)) (c4)c5=Conv2D(256, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (p4)
c5=Dropout(0.3) (c5)
c5=Conv2D(256, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c5)u6=Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same') (c5)
u6=concatenate([u6, c4])
c6=Conv2D(128, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (u6)
c6=Dropout(0.2) (c6)
c6=Conv2D(128, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c6)u7=Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same') (c6)
u7=concatenate([u7, c3])
c7=Conv2D(64, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (u7)
c7=Dropout(0.2) (c7)
c7=Conv2D(64, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c7)u8=Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same') (c7)
u8=concatenate([u8, c2])
c8=Conv2D(32, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (u8)
c8=Dropout(0.1) (c8)
c8=Conv2D(32, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c8)u9=Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same') (c8)
u9=concatenate([u9, c1], axis=3)
c9=Conv2D(16, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (u9)
c9=Dropout(0.1) (c9)
c9=Conv2D(16, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c9)#Noteouroutputiseffectivelyamaskof128x128outputs=Conv2D(1, (1, 1), activation='sigmoid') (c9)model=Model(inputs=[inputs], outputs=[outputs])
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=[my_iou_metric])
model.summary()

训练我们的模型

model_path="./nuclei_finder_unet_1.h5"checkpoint=ModelCheckpoint(model_path,
monitor="val_loss",
mode="min",
save_best_only=True,
verbose=1)
earlystop=EarlyStopping(monitor='val_loss',
min_delta=0,
patience=5,
verbose=1,
restore_best_weights=True)
#Fitourmodelresults=model.fit(X_train, Y_train, validation_split=0.1,
batch_size=16, epochs=10,
callbacks=[earlystop, checkpoint])

生成验证数据的预测

#Predictontrainingandvalidationdata#Noteouruseofmean_ioumetrimodel=load_model('./nuclei_finder_unet_1.h5',
custom_objects={'my_iou_metric': my_iou_metric})
#thefirst90%wasusedfortrainingpreds_train=model.predict(X_train[:int(X_train.shape[0]*0.9)], verbose=1)
#thelast10%usedasvalidationpreds_val=model.predict(X_train[int(X_train.shape[0]*0.9):], verbose=1)
#preds_test=model.predict(X_test, verbose=1)
#Thresholdpredictionspreds_train_t= (preds_train>0.5).astype(np.uint8)
preds_val_t= (preds_val>0.5).astype(np.uint8)

在我们的训练数据上显示我们预测的遮罩

ix=random.randint(0, 602)
plt.figure(figsize=(20,20))
#Ouroriginaltrainingimageplt.subplot(131)
imshow(X_train[ix])
plt.title("Image")
#Ouroriginalcombinedmaskplt.subplot(132)
imshow(np.squeeze(Y_train[ix]))
plt.title("Mask")
#ThemaskourU-Netmodelpredictsplt.subplot(133)
imshow(np.squeeze(preds_train_t[ix] >0.5))
plt.title("Predictions")
plt.show()

最后这里是完整的代码:

数据集:https://www.kaggle.com/c/data-science-bowl-2018

本文代码:https://github.com/bhaveshgoyal27/mediumblogs/blob/master/U-Net.ipynb

目录
相关文章
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
1180 29
|
12月前
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
494 4
|
算法 前端开发 定位技术
地铁站内导航系统解决方案:技术架构与核心功能设计解析
本文旨在分享一套地铁站内导航系统技术方案,通过蓝牙Beacon技术与AI算法的结合,解决传统导航定位不准确、路径规划不合理等问题,提升乘客出行体验,同时为地铁运营商提供数据支持与增值服务。 如需获取校地铁站内智能导航系统方案文档可前往文章最下方获取,如有项目合作及技术交流欢迎私信我们哦~
1025 1
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。
|
存储 前端开发 JavaScript
在线教育网课系统源码开发指南:功能设计与技术实现深度解析
在线教育网课系统是近年来发展迅猛的教育形式的核心载体,具备用户管理、课程管理、教学互动、学习评估等功能。本文从功能和技术两方面解析其源码开发,涵盖前端(HTML5、CSS3、JavaScript等)、后端(Java、Python等)、流媒体及云计算技术,并强调安全性、稳定性和用户体验的重要性。
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
监控 前端开发 API
一款基于 .NET MVC 框架开发、功能全面的MES系统
一款基于 .NET MVC 框架开发、功能全面的MES系统
560 5
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
330 7

热门文章

最新文章

推荐镜像

更多
  • DNS