# 深度学习还没开始就想放弃系列 #

环境: win7+python 3.5+ tensorflow 1.9.0 + keras 2.2.4

参考博客: U-net源码讲解(Keras)源码解析很棒很细!

全卷积神经网络图像分割(U-net)-keras实现代码根本来源于该博主的Github

深度学习数据增强(data_augmentation):Keras ImageDataGenerator : keras的图像增强讲解,方便理解源码函数;

Keras中文文档——图片预处理 : 无意中翻到的,学习keras可以留存。

目的 : 利用U-Net (keras)实现自己数据集的分割任务,二类。

数据集准备: 

样本: size: 256*256 位深为8,.png格式;

标签: size: 256*256 ,.png格式,使用photoshop软件标注控制位深为8,图片名于样本名字相同。

存储位置: 同一个根目录下的两个文件夹中,文件名为:image, label

测试图片:位深为8,

主要代码:model.py:存放U-Net网络模型,keras写起来真的很简单!!

# model.py
import numpy as np
import os
import skimage.io as io
import skimage.transform as trans
import numpy as np
from keras.models import *
from keras.layers import *
from keras.optimizers import *
from keras.callbacks import ModelCheckpoint, LearningRateScheduler
from keras import backend as kerasdef unet(pretrained_weights = None,input_size = (256,256,1)):inputs = Input(input_size)conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(inputs)conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv1)pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool1)conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv2)pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool2)conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv3)pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool3)conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv4)drop4 = Dropout(0.5)(conv4)pool4 = MaxPooling2D(pool_size=(2, 2))(drop4)conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool4)conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv5)drop5 = Dropout(0.5)(conv5)up6 = Conv2D(512, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(drop5))merge6 = concatenate([drop4,up6], axis = 3)conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge6)conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv6)up7 = Conv2D(256, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv6))merge7 = concatenate([conv3,up7], axis = 3)conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge7)conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv7)up8 = Conv2D(128, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv7))merge8 = concatenate([conv2,up8], axis = 3)conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge8)conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv8)up9 = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv8))merge9 = concatenate([conv1,up9], axis = 3)conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge9)conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)conv9 = Conv2D(2, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)conv10 = Conv2D(1, 1, activation = 'sigmoid')(conv9)model = Model(input = inputs, output = conv10)model.compile(optimizer = Adam(lr = 1e-4), loss = 'binary_crossentropy', metrics = ['accuracy'])#model.summary()if(pretrained_weights):model.load_weights(pretrained_weights)return model

data.py:

# data.py 对图片的相关处理
from __future__ import print_function
from keras.preprocessing.image import ImageDataGenerator
import numpy as np
import os
import glob
import skimage.io as io
import skimage.transform as transSky = [128,128,128]
Building = [128,0,0]
Pole = [192,192,128]
Road = [128,64,128]
Pavement = [60,40,222]
Tree = [128,128,0]
SignSymbol = [192,128,128]
Fence = [64,64,128]
Car = [64,0,128]
Pedestrian = [64,64,0]
Bicyclist = [0,128,192]
Unlabelled = [0,0,0]COLOR_DICT = np.array([Sky, Building, Pole, Road, Pavement,Tree, SignSymbol, Fence, Car, Pedestrian, Bicyclist, Unlabelled])#adjustData()函数主要是对训练集的数据和标签的像素值进行归一化
def adjustData(img,mask,flag_multi_class,num_class):if(flag_multi_class):#此程序中不是多类情况,所以不考虑这个img = img / 255.0mask = mask[:,:,:,0] if(len(mask.shape) == 4) else mask[:,:,0]# if else的简洁写法,一行表达式,为真时放在前面,不明白mask.shape=4的情况是什么,# 由于有batch_size,所以mask就有3维[batch_size,wigth,heigh],估计mask[:,:,0]是写错了,应该写成[0,:,:],这样可以得到一片图片,new_mask = np.zeros(mask.shape + (num_class,))# np.zeros里面是shape元组,此目的是将数据厚度扩展到num_class层,以在层的方向实现one-hot结构for i in range(num_class):#for one pixel in the image, find the class in mask and convert it into one-hot vector#index = np.where(mask == i)#index_mask = (index[0],index[1],index[2],np.zeros(len(index[0]),dtype = np.int64) + i) if (len(mask.shape) == 4) else (index[0],index[1],np.zeros(len(index[0]),dtype = np.int64) + i)#new_mask[index_mask] = 1new_mask[mask == i,i] = 1#将平面的mask的每类,都单独变成一层,new_mask = np.reshape(new_mask,(new_mask.shape[0],new_mask.shape[1]*new_mask.shape[2],new_mask.shape[3])) if flag_multi_class else np.reshape(new_mask,(new_mask.shape[0]*new_mask.shape[1],new_mask.shape[2]))mask = new_maskelif(np.max(img) > 1):img = img / 255.0mask = mask /255.0mask[mask > 0.5] = 1mask[mask <= 0.5] = 0return (img,mask)# trainGenerator()函数主要是产生一个数据增强的图片生成器,方便后面使用这个生成器不断生成图片
def trainGenerator(batch_size,train_path,image_folder,mask_folder,aug_dict,image_color_mode = "grayscale",mask_color_mode = "grayscale",image_save_prefix  = "image",mask_save_prefix  = "mask",flag_multi_class = False,num_class = 2,save_to_dir = None,target_size = (256,256),seed = 1):'''can generate image and mask at the same timeuse the same seed for image_datagen and mask_datagen to ensure the transformation for image and mask is the sameif you want to visualize the results of generator, set save_to_dir = "your path"'''image_datagen = ImageDataGenerator(**aug_dict)mask_datagen = ImageDataGenerator(**aug_dict)image_generator = image_datagen.flow_from_directory(train_path,#训练数据文件夹路径classes = [image_folder],#类别文件夹,对哪一个类进行增强class_mode = None,#不返回标签color_mode = image_color_mode,#灰度,单通道模式target_size = target_size,#转换后的目标图片大小batch_size = batch_size,#每次产生的(进行转换的)图片张数save_to_dir = save_to_dir,#保存的图片路径save_prefix  = image_save_prefix,#生成图片的前缀,仅当提供save_to_dir时有效seed = seed)mask_generator = mask_datagen.flow_from_directory(train_path,classes = [mask_folder],class_mode = None,color_mode = mask_color_mode,target_size = target_size,batch_size = batch_size,save_to_dir = save_to_dir,save_prefix  = mask_save_prefix,seed = seed)train_generator = zip(image_generator, mask_generator)#组合成一个生成器for (img,mask) in train_generator:#由于batch是2,所以一次返回两张,即img是一个2张灰度图片的数组,[2,256,256]img,mask = adjustData(img,mask,flag_multi_class,num_class)#返回的img依旧是[2,256,256]yield (img,mask)#每次分别产出两张图片和标签,不懂yield的请看https://blog.csdn.net/mieleizhi0522/article/details/82142856# testGenerator()函数主要是对测试图片进行规范,使其尺寸和维度上和训练图片保持一致
def testGenerator(test_path,num_image = 30,target_size = (256,256),flag_multi_class = False,as_gray = True):for i in range(num_image):img = io.imread(os.path.join(test_path,"%d.png"%i),as_gray = as_gray)img = img / 255.0img = trans.resize(img,target_size)img = np.reshape(img,img.shape+(1,)) if (not flag_multi_class) else imgimg = np.reshape(img,(1,)+img.shape)#将测试图片扩展一个维度,与训练时的输入[2,256,256]保持一致yield img# geneTrainNpy()函数主要是分别在训练集文件夹下和标签文件夹下搜索图片,
# 然后扩展一个维度后以array的形式返回,是为了在没用数据增强时的读取文件夹内自带的数据
def geneTrainNpy(image_path,mask_path,flag_multi_class = False,num_class = 2,image_prefix = "image",mask_prefix = "mask",image_as_gray = True,mask_as_gray = True):image_name_arr = glob.glob(os.path.join(image_path,"%s*.png"%image_prefix))#相当于文件搜索,搜索某路径下与字符匹配的文件image_arr = []mask_arr = []for index,item in enumerate(image_name_arr):#enumerate是枚举,输出[(0,item0),(1,item1),(2,item2)]img = io.imread(item,as_gray = image_as_gray)img = np.reshape(img,img.shape + (1,)) if image_as_gray else imgmask = io.imread(item.replace(image_path,mask_path).replace(image_prefix,mask_prefix),as_gray = mask_as_gray)#重新在mask_path文件夹下搜索带有mask字符的图片(标签图片)mask = np.reshape(mask,mask.shape + (1,)) if mask_as_gray else maskimg,mask = adjustData(img,mask,flag_multi_class,num_class)image_arr.append(img)mask_arr.append(mask)image_arr = np.array(image_arr)mask_arr = np.array(mask_arr)#转换成arrayreturn image_arr,mask_arr# labelVisualize()函数是给出测试后的输出之后,为输出涂上不同的颜色,多类情况下才起作用,两类的话无用
def labelVisualize(num_class,color_dict,img):img = img[:,:,0] if len(img.shape) == 3 else imgimg_out = np.zeros(img.shape + (3,))#变成RGB空间,因为其他颜色只能再RGB空间才会显示for i in range(num_class):img_out[img == i,:] = color_dict[i]#为不同类别涂上不同的颜色,color_dict[i]是与类别数有关的颜色,img_out[img == i,:]是img_out在img中等于i类的位置上的点return img_out / 255.0# # 直接将在0-1的浮点数直接保存成图片,生成的是灰度图
# def saveResult(save_path,npyfile,flag_multi_class = False,num_class = 2):# for i,item in enumerate(npyfile):# img = labelVisualize(num_class,COLOR_DICT,item) if flag_multi_class else item[:,:,0]# io.imsave(os.path.join(save_path,"%d_predict.png"%i),img)# 生成二值图片
def saveResult(save_path,npyfile,flag_multi_class = False,num_class = 2):for i,item in enumerate(npyfile):if flag_multi_class:img = labelVisualize(num_class,COLOR_DICT,item)#多类的话就图成彩色,非多类(两类)的话就是黑白色else:img=item[:,:,0]print(np.max(img),np.min(img))img[img>=0.5]=1#此时1是浮点数,下面的0也是img[img<0.5]=0print(np.max(img),np.min(img))io.imsave(os.path.join(save_path,"%d_predict.png"%i),img)

接下来是训练,此处将训练和测试分开在两个.py文件中,好调用!


# main_train.py
from model import *
from data import *# #os.environ["CUDA_VISIBLE_DEVICES"] = "0"
# data_gen_args = dict() : 为keras自带的图像增强方法
data_gen_args = dict(rotation_range=0.2, #整数。随机旋转的度数范围。width_shift_range=0.05, #浮点数、一维数组或整数height_shift_range=0.05, #浮点数。剪切强度(以弧度逆时针方向剪切角度)。shear_range=0.05, zoom_range=0.05, #浮点数 或 [lower, upper]。随机缩放范围horizontal_flip=True, fill_mode='nearest') # {"constant", "nearest", "reflect" or "wrap"} 之一。默认为 'nearest'。输入边界以外的点根据给定的模式填充:
# 建立测试集,样本和标签分别放在同一个目录下的两个文件夹中,文件夹名字为:'image','label'
#得到一个生成器,以batch=2的速率无限生成增强后的数据
myGene = trainGenerator(2,'E:\\数据集目录\\Pic','image','label',data_gen_args,save_to_dir = None) # data# 调用模型,默认模型输入图像size=(256,256,1),样本位深为8位
model = unet() # model
# 保存训练的模型参数到指定的文件夹,格式为.hdf5; 检测的值是'loss'使其更小。
model_checkpoint = ModelCheckpoint('E:/模型参数存储目录/unet_membrane.hdf5', monitor='loss',verbose=1, save_best_only=True) # keras
# 开始训练,steps_per_epoch为迭代次数,epochs:
model.fit_generator(myGene,steps_per_epoch=600,epochs=1,callbacks=[model_checkpoint]) # keras

 接下来就是愉快的测试了:main_test.py

from model import *
from data import *# python main_test.py
"""
注:A: target_size()为图片尺寸,要求测试集图像尺寸设置和model输入图像尺寸保持一致,如果不设置图片尺寸,会对输入图片做resize为处理,输入网络和输出图像尺寸默认均为(256,256),B: 且要求图片位深为8位,24/32的会报错!!C: 测试集数据名称需要设置为:0.png……D:model.predict_generator( ,n, ):n为测试集中样本数量,需要手动设置,不然会报错!!
"""
# 输入测试数据集,
testGene = testGenerator("E:\\测试集目录\\crackTestimg",target_size = (256,256)) # data# 导入模型
model = unet(input_size = (256,256,1)) # model# 导入训练好的模型
model.load_weights("E:/模型参数存放的目录/unet_membrane.hdf5")# 预测数据
results = model.predict_generator(testGene,20,verbose=1) # keras
print(results)
saveResult("E:\\测试集目录\\crackTestimg",results) # data
print("over")

测试结果部分展示:(裂缝检测)

问题 :

A:图片要求位深是8位的,24/32测试会报错,实在不知如何修改!

B:对测试数据命名需要时0.png,在源码data.py可以做相应的修改,改成按图片名字输入,,具体需要进一步探寻

C:修改部分:针对输出图片全灰,全黑,全白的修改

(1)将data.py中所以/255的变成/255.0, 经验证没啥太大用,不过还是修改了;

(2)将saveResult()函数进行了修改,使得结果变成二值图像

(3)正确导入保存的模型参数: model.load_weights("E:/模型参数存放的目录/unet_membrane.hdf5");做了此处修改后,代码正常输出。其实还是没找到具体输出全灰或全黑/白的问题在哪!!不过问题却意外解决了!!欣喜。。。

U-Net源码上实现自己数据集的分割任务相关推荐

  1. 全卷积神经网路【U-net项目实战】U-Net源码上实现自己数据集的分割任务

    文章目录 环境: 数据集准备: 主要代码: 接下来是训练,此处将训练和测试分开在两个.py文件中,好调用! 接下来就是愉快的测试了:main_test.py 测试结果部分展示:(裂缝检测) 问题 : ...

  2. ROS探索总结(七)(八)(九)——smartcar源码上传 键盘控制 操作杆控制

    ROS探索总结(七)--smartcar源码上传 看到前面写的博客还是帮助了很多ROS的学习者,我感到非常荣幸.其实我也是一名ROS的新手,ROS的相关资料少,上手难度大,我现在也在摸索着学习,还希望 ...

  3. c# MODBUS协议源码 上/下位机源码烧写Flash工具

    c# MODBUS协议源码 上/下位机源码烧写Flash工具 包含: 1.C#上位机源码 2.上位机源码包含MODBUS协议源码 3.下位机源码 下位机源码采用STM32F10x芯片 的uC/OS-I ...

  4. gradle打包并将源码上传到私服

    gradle打包并将源码上传到私服 主要依赖Maven Publish Plugin插件,Maven Publish Plugin 在build.gradle: apply plugin: 'java ...

  5. python教程之打包python源码上传的PyPI官网

    为什么打包源码 如果你想让你的实现的python代码,通过pip install 方式供所有人进行下载:那就需要将代码上传到PyPi上,这样才能让所有人使用: 如何打包源码上传: 前提条件: 1.有一 ...

  6. 手机QQ侧滑菜单_从源码上一步步解析效果的实现

    本文思想来自洪洋大哥,本来写的原创的,有些朋友看到标题后认为是照搬翔哥的例子,仔细看看,会有不同,不过其中的主要思想还是翔哥的,滑动方面的算法还真是有些区别的,看完了就知道不一样,而且我这人比较啰嗦, ...

  7. HiAGM模型源码测试【原始数据集+中文数据集】

    论文链接:Hierarchy-Aware Global Model for Hierarchical Text Classification github代码链接:HiAGM HiAGM模型源码测试 ...

  8. php文件上传到虚拟主机,php源码上传到虚拟主机(php源码上传到服务器)

    php网站的源码在上传到虚拟主机之前,需要做什么修改本人小白,只知道需. 这个啊,倒是简单,你下载个ftp软件,登陆上传即可,不过要注意传对目录,一般的虚拟主机都有好几目录的,要传合适,具体的可以咨询 ...

  9. c++ 字符串拼接_源码上看 .NET 中 StringBuilder 拼接字符串的实现

    专注分享C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协 ...

  10. 前端小游戏2048(一步步详解附带源代码,源码上传到csdn,可以免费下载)

    2048小游戏 2048是前端开发必经的一个小游戏,2048小游戏包含了HTML,CSS和JavaScript. 简介 <2048>,是一款益智小游戏,这款游戏是由年仅19岁的意大利程序员 ...

最新文章

  1. PHP Shell生成工具Weevely
  2. 转-OpenJDK源码阅读导航跟编译
  3. cdh5.12.1 service monitor监控状态_来,我们在重新说下,线程状态?
  4. 编译原理之正则表达式
  5. sql同时向两个表插入数据_SQL入门-数据库和客户端的安装,表的创建和数据插入...
  6. 复旦计算机考研复试要口试吗,2017复旦大学考研复试:英语口语面试常见问题汇总...
  7. RabbitMq(七) Topic模式介绍及代码示例
  8. python敏感词过滤replace_Serverless 实战:3 分钟实现文本敏感词过滤
  9. ES6个人小手册,走过路过不要错过。。。。。
  10. UDK游戏开发基础命令
  11. 2016年回顾2017年目标之流水账
  12. bzoj1088 [SCOI2005]扫雷Mine
  13. 2009年南京辞职当日
  14. HTML简易自适应布局
  15. 小米9SE CC9小米8小米6X小米mix2s红米note7Pro小米9红米note8Pro 红米note8等移除ID 解账户锁教程
  16. ZooKeeper管理工具一览
  17. html 页面新窗口打开,HTML 在新窗口打开全站链接
  18. win10总是很快自动休眠,设置休眠时间也无效?
  19. 数据库(笔记)——候选码、主码、外码以及关系的完整性
  20. 基于COF智能屏的桌面式3D打印机方案

热门文章

  1. opencv+ArcFace人脸识别
  2. pcb天线和纯铜天线_PCB天线是什么
  3. Centos7+搜狗拼音输入法 安装不踩雷
  4. 微信小程序开发全流程记录(从前台到后台,到发布)
  5. Oracle中针对中文进行排序
  6. 伟哥大数据入门教程一
  7. html斜杠单元格,用斜杠拆分左侧单元格的html表
  8. Origin修改安装位数
  9. Matlab图像、矩阵旋转、翻转函数 rot90、flipud、fliplr、imrotate、flipdim、flip详解
  10. arcgis公里坐标转经纬度_利用arcgis实现经纬度和平面坐标互转