原文做者:aircrafthtml
原文连接:https://www.cnblogs.com/DOMLX/p/9780786.html前端
有没有大佬们的公司招c++开发/图像处理/opengl/opencv/halcon实习的啊,带上我一个呗QAQpython
最近秦老师叫我研究深度学习与指静脉结合,我就拿这篇来作敲门砖,而且成功将指静脉的纹理特征提取用u-net实现了,而眼球血管分割则给我提供了很大的帮助。如今就分享给你们吧。。虽然很想把指静脉也写一篇单独的博客分享,可是不容许啊hhhhhhh
linux
DRIVE数据集下载百度云连接:连接:https://pan.baidu.com/s/1C_1ikDwexB0hZvOwMSeDtw
提取码:关注最下面公众号添加小编微信 发送文章标题源码获取
c++
U-net+kears实现眼部血管分割源码python2.7版本的百度云连接:连接:https://pan.baidu.com/s/1C_1ikDwexB0hZvOwMSeDtw
提取码:关注最下面公众号添加小编微信 发送文章标题源码获取
git
U-net+kears实现眼部血管分割源码python3.6版本的百度连接:连接:https://pan.baidu.com/s/1rAf6wuWGCswuBfkDivxjyQ
提取码:关注最下面公众号添加小编微信 发送文章标题源码获取
github
大名鼎鼎的FCN就很少作介绍了,这里有一篇很好的博文 http://www.cnblogs.com/gujianhan/p/6030639.html。
不过仍是建议把论文读一下,这样才能加深理解。编程
医学图像分割主要有两种框架,一个是基于CNN的,另外一个就是基于FCN的。这里都是经过网络来进行语义分割。后端
那么什么是语义分割?可不是汉字分割句意,在图像处理中有本身的定义。api
图像语义分割的意思就是机器自动分割并识别出图像中的内容,好比给出一我的骑摩托车的照片,机器判断后应当可以生成右侧图,红色标注为人,绿色是车(黑色表示 back ground)。
因此图像分割对图像理解的意义,就比如读古书首先要断句同样。
在 Deeplearning 技术快速发展以前,就已经有了不少作图像分割的技术,其中比较著名的是一种叫作 “Normalized cut” 的图划分方法,简称 “N-cut”。
N-cut 的计算有一些链接权重的公式,这里就不提了,它的思想主要是经过像素和像素之间的关系权重来综合考虑,根据给出的阈值,将图像一分为二。
这个想法也很简单,就是对图像的每个像素点进行分类,在每个像素点上取一个patch,当作一幅图像,输入神经网络进行训练,举个例子:
这是一个二分类问题,把图像中全部label为0的点做为负样本,全部label为1的点做为正样本。
这种网络显然有两个缺点:
1. 冗余太大,因为每一个像素点都须要取一个以自己为中心patch,那么相邻的两个像素点的patch类似度是很是高的,这就致使了很是多的冗余,致使网络训练很慢。
2. 感觉野和定位精度不可兼得,当感觉野选取比较大的时候,后面对应的pooling层的降维倍数就会增大,这样就会致使定位精度下降,可是若是感觉野比较小,那么分类精度就会下降。
CNN 存在好久了,可是一直受限于过大的数据量和神经网络的规模,并无得到很大的成功,直至 Krizhevsky 才开始爆发。可是将 CNN 用于生物医学图像存在着两点困难,首先CNN经常使用于分类,可是生物医学每每关注的是分割之类的定位任务;其次医学图像很难得到那么大规模的数据
以往解决上面两点困难的方法是使用滑窗的方法,为每个待分类的像素点取周围的一部分邻域输入。这样的方法有两点好处,首先它完成了定位的工做,其次由于每次取一个像素点周围的邻域,因此大大增长了训练数据的数量。可是这样的方法也有两个缺点,首先经过滑窗所取的块之间具备较大的重叠,因此会致使速度变慢(由FCN的论文分析可知,前向传播和反向传播的速度都会变慢);其次是网络须要在局部准确性和获取上下文之间进行取舍。由于更大的块须要更多的池化层进而下降了定位的准确率,可是小的块使网络只看到很小的一部分上下文。如今一种常见的做法是将多个层放在一块儿进行考虑(好比说FCN)。
在医学图像处理领域,有一个应用很普遍的网络结构—-U-net ,网络结构以下:
它包含重复的2个3x3卷积,紧接着是一个RELU,一个max pooling(步长为2),用来降采样,每次降采样咱们都将feature channel减半。扩展路径包含一个上采样(2x2上卷积),这样会减半feature channel,接着是一个对应的收缩路径的feature map,而后是2个3x3卷积,每一个卷积后面跟一个RELU,由于每次卷积会丢失图像边缘,因此裁剪是有必要的,最后来一个1x1的卷积,用来将有64个元素的feature vector映射到一个类标签,整个网络一共有23个卷积层。
能够看出来,就是一个全卷积神经网络,输入和输出都是图像,没有全链接层。较浅的高分辨率层用来解决像素定位的问题,较深的层用来解决像素分类的问题。
好了理解完U-net网络,咱们就学习一下怎么用U-net网络来进行医学图像分割。
U-net+kears实现眼部血管分割
原做者的【英文说明】https://github.com/orobix/retina-unet#retina-blood-vessel-segmentation-with-a-convolution-neural-network-u-net
实现环境可直接看这篇博客下载:2018最新win10 安装tensorflow1.4(GPU/CPU)+cuda8.0+cudnn8.0-v6 + keras 安装CUDA失败 导入tensorflow失败报错问题解决
linux下就环境同样,配置就要本身去找了。
一、介绍
为了可以更好的对眼部血管等进行检测、分类等操做,咱们首先要作的就是对眼底图像中的血管进行分割,保证最大限度的分割出眼部的血管。从而方便后续对血管部分的操做。
这部分代码选用的数据集是DRIVE数据集,包括训练集和测试集两部分。眼底图像数据如图1所示。
图1 DRIVE数据集的训练集眼底图像
DRIVE数据集的优势是:不只有已经手工分好的的血管图像(在manual文件夹下,如图2所示),并且还包含有眼部轮廓的图像(在mask文件夹下,如图3所示)。
图2 DRIVE数据集的训练集手工标注血管图像
图3 DRIVE数据集的训练集眼部轮廓图像
DRIVE数据集的缺点是:显而易见,从上面的图片中能够看出,训练集只有20幅图片,可见数据量实在是少之又少。。。
因此,为了获得更好的分割效果,咱们须要对这20幅图像进行预处理从而增大其数据量
二、依赖的库
- numpy >= 1.11.1
- Keras >= 2.1.0
- PIL >=1.1.7
- opencv >=2.4.10
- h5py >=2.6.0
- configparser >=3.5.0b2
- scikit-learn >= 0.17.1
三、数据读取与保存
数据集中训练集和测试集各只有20幅眼底图像(tif格式)。首先要作的第一步就是对生成数据文件,方便后续的处理。因此这里咱们须要对数据集中的眼底图像、人工标注的血管图像、眼部轮廓生成数据文件。这里使用的是hdf5文件。有关hdf5文件的介绍,请参考CSDN博客(HDF5快速上手全攻略)。
四、网络解析
由于U-net网络能够针对不多的数据集来进行语义分割,好比咱们这个眼球血管分割就是用了20张图片来训练就能够达到很好的效果。并且咱们这种眼球血管,或者指静脉,指纹之类的提取特征或者血管静脉在U-net网络里就是一个二分类问题,你们一听,二分类对于目前的神经网络不是一件很简单的事情了吗?还有是什么能够说的。
的确目前二分类问题是没有什么难度了,只要给我足够的数据集作训练。而本文用的U-net网络来实现这个二分类就只须要二十张图片来做为数据集。你们能够看到优点所在了吧。
五、具体实现
首先咱们确定都是要对数据进行一些预处理的。
第一步
先将图像转为灰度图分别读入数组创建起一个符合咱们本身的tensor的格式才好传入神经网络,这里咱们是先将数据存入hdf5文件中,在开始运行的时候从文件中读入。
#将对应的图像数据存入对应图像数组 def get_datasets(imgs_dir,groundTruth_dir,borderMasks_dir,train_test="null"): imgs = np.empty((Nimgs,height,width,channels)) groundTruth = np.empty((Nimgs,height,width)) border_masks = np.empty((Nimgs,height,width)) for path, subdirs, files in os.walk(imgs_dir): #list all files, directories in the path for i in range(len(files)): #original print("original image: " +files[i]) img = Image.open(imgs_dir+files[i]) imgs[i] = np.asarray(img) #corresponding ground truth groundTruth_name = files[i][0:2] + "_manual1.gif" print("ground truth name: " + groundTruth_name) g_truth = Image.open(groundTruth_dir + groundTruth_name) groundTruth[i] = np.asarray(g_truth) #corresponding border masks border_masks_name = "" if train_test=="train": border_masks_name = files[i][0:2] + "_training_mask.gif" elif train_test=="test": border_masks_name = files[i][0:2] + "_test_mask.gif" else: print("specify if train or test!!") exit() print("border masks name: " + border_masks_name) b_mask = Image.open(borderMasks_dir + border_masks_name) border_masks[i] = np.asarray(b_mask) print("imgs max: " +str(np.max(imgs))) print("imgs min: " +str(np.min(imgs))) assert(np.max(groundTruth)==255 and np.max(border_masks)==255) assert(np.min(groundTruth)==0 and np.min(border_masks)==0) print("ground truth and border masks are correctly withih pixel value range 0-255 (black-white)") #reshaping for my standard tensors imgs = np.transpose(imgs,(0,3,1,2)) assert(imgs.shape == (Nimgs,channels,height,width)) groundTruth = np.reshape(groundTruth,(Nimgs,1,height,width)) border_masks = np.reshape(border_masks,(Nimgs,1,height,width)) assert(groundTruth.shape == (Nimgs,1,height,width)) assert(border_masks.shape == (Nimgs,1,height,width)) return imgs, groundTruth, border_masks if not os.path.exists(dataset_path): os.makedirs(dataset_path) #getting the training datasets imgs_train, groundTruth_train, border_masks_train = get_datasets(original_imgs_train,groundTruth_imgs_train,borderMasks_imgs_train,"train") print("saving train datasets") write_hdf5(imgs_train, dataset_path + "DRIVE_dataset_imgs_train.hdf5") write_hdf5(groundTruth_train, dataset_path + "DRIVE_dataset_groundTruth_train.hdf5") write_hdf5(border_masks_train,dataset_path + "DRIVE_dataset_borderMasks_train.hdf5") #getting the testing datasets imgs_test, groundTruth_test, border_masks_test = get_datasets(original_imgs_test,groundTruth_imgs_test,borderMasks_imgs_test,"test") print("saving test datasets") write_hdf5(imgs_test,dataset_path + "DRIVE_dataset_imgs_test.hdf5") write_hdf5(groundTruth_test, dataset_path + "DRIVE_dataset_groundTruth_test.hdf5") write_hdf5(border_masks_test,dataset_path + "DRIVE_dataset_borderMasks_test.hdf5")
第二步
是对读入内存准备开始训练的图像数据进行一些加强之类的处理,这里对其进行了,直方图均衡化,数据标准化,而且压缩像素值到0-1,将其的一个数据符合标准正态分布。固然啦咱们这个数据拿来训练仍是太少的,因此咱们对每张图片取patch时,除了正常的每一个patch每一个patch移动的取以外,咱们还在数据范围内进行随机取patch,这样虽然各个patch之间会有一部分数据是相同的,可是这对于网络而言,你传入的也是一个新的东西,网络能从中提取到的特征也更多了。这一步的目的其实就是在有限的数据集中进行一些数据扩充,这也是在神经网络训练中经常使用的手段了。
固然了在这个过程当中咱们也能够随机组合小的patch来看看。
随机原图:
mask图:
处理待训练数据的部分代码:
def get_data_training(DRIVE_train_imgs_original, DRIVE_train_groudTruth, patch_height, patch_width, N_subimgs, inside_FOV): train_imgs_original = load_hdf5(DRIVE_train_imgs_original) train_masks = load_hdf5(DRIVE_train_groudTruth) #masks always the same # visualize(group_images(train_imgs_original[0:20,:,:,:],5),'imgs_train')#.show() #check original imgs train train_imgs = my_PreProc(train_imgs_original) #直方图均衡化,数据标准化,压缩像素值到0-1 train_masks = train_masks/255. train_imgs = train_imgs[:,:,9:574,:] #cut bottom and top so now it is 565*565 train_masks = train_masks[:,:,9:574,:] #cut bottom and top so now it is 565*565 data_consistency_check(train_imgs,train_masks) #check masks are within 0-1 assert(np.min(train_masks)==0 and np.max(train_masks)==1) print("\ntrain images/masks shape:") print(train_imgs.shape) print("train images range (min-max): " +str(np.min(train_imgs)) +' - '+str(np.max(train_imgs))) print("train masks are within 0-1\n") #extract the TRAINING patches from the full images patches_imgs_train, patches_masks_train = extract_random(train_imgs,train_masks,patch_height,patch_width,N_subimgs,inside_FOV) data_consistency_check(patches_imgs_train, patches_masks_train) print("\ntrain PATCHES images/masks shape:") print(patches_imgs_train.shape) print("train PATCHES images range (min-max): " +str(np.min(patches_imgs_train)) +' - '+str(np.max(patches_imgs_train))) return patches_imgs_train, patches_masks_train#, patches_imgs_test, patches_masks_test
第三步
按照U-net的网络结构,使用keras来构造出网络。这里对keras函数语法不太理解的能够看这篇博客:深度学习(六)keras经常使用函数学习
def get_unet(n_ch,patch_height,patch_width): inputs = Input(shape=(n_ch,patch_height,patch_width)) conv1 = Conv2D(32, (3, 3), activation='relu', padding='same',data_format='channels_first')(inputs) conv1 = Dropout(0.2)(conv1) conv1 = Conv2D(32, (3, 3), activation='relu', padding='same',data_format='channels_first')(conv1) pool1 = MaxPooling2D((2, 2))(conv1) # conv2 = Conv2D(64, (3, 3), activation='relu', padding='same',data_format='channels_first')(pool1) conv2 = Dropout(0.2)(conv2) conv2 = Conv2D(64, (3, 3), activation='relu', padding='same',data_format='channels_first')(conv2) pool2 = MaxPooling2D((2, 2))(conv2) # conv3 = Conv2D(128, (3, 3), activation='relu', padding='same',data_format='channels_first')(pool2) conv3 = Dropout(0.2)(conv3) conv3 = Conv2D(128, (3, 3), activation='relu', padding='same',data_format='channels_first')(conv3) up1 = UpSampling2D(size=(2, 2))(conv3) up1 = concatenate([conv2,up1],axis=1) conv4 = Conv2D(64, (3, 3), activation='relu', padding='same',data_format='channels_first')(up1) conv4 = Dropout(0.2)(conv4) conv4 = Conv2D(64, (3, 3), activation='relu', padding='same',data_format='channels_first')(conv4) #上采样后横向拼接 up2 = UpSampling2D(size=(2, 2))(conv4) up2 = concatenate([conv1,up2], axis=1) conv5 = Conv2D(32, (3, 3), activation='relu', padding='same',data_format='channels_first')(up2) conv5 = Dropout(0.2)(conv5) conv5 = Conv2D(32, (3, 3), activation='relu', padding='same',data_format='channels_first')(conv5) # conv6 = Conv2D(2, (1, 1), activation='relu',padding='same',data_format='channels_first')(conv5) conv6 = core.Reshape((2,patch_height*patch_width))(conv6) conv6 = core.Permute((2,1))(conv6) ############ conv7 = core.Activation('softmax')(conv6) model = Model(inputs=inputs, outputs=conv7) # sgd = SGD(lr=0.01, decay=1e-6, momentum=0.3, nesterov=False) model.compile(optimizer='sgd', loss='categorical_crossentropy',metrics=['accuracy']) return model
第四步
这里就是将咱们处理好的数据传入到网络里训练了。得出结果图。
看的出来不少很细的纹理都被提取出来了,这个U-net网络也能够用于一些医学细胞的 边缘提取,指静脉,掌静脉之类的纹路提取均可以。后面在下可能还会出指静脉之类其余图像的语义分割提取,关注在下就能够看到啦hhhhhhh
由于keras内部能够直接将整个网络结构打印出来,因此咱们能够看到完整的网络结构图以下所示:
如有兴趣交流分享技术,可关注本人公众号,里面会不按期的分享各类编程教程,和共享源码,诸如研究分享关于c/c++,python,前端,后端,opencv,halcon,opengl,机器学习深度学习之类有关于基础编程,图像处理和机器视觉开发的知识
参考博客:https://blog.csdn.net/u013063099/article/details/79981097
参考博客:https://blog.csdn.net/qq_16900751/article/details/78251778