图像训练时的数据处理

当训练数据时,如果数据只是一列,而输出也是一列,那就对应着(train,label)输入模型即可。但是如果处理对象是图片时,应该如何将图片作为数据输入模型。我只记录到目前为止的理解作为参考,后续还会修改(应该会的吧!)

一、对图片的结构的了解

图片分成了彩色图和非彩色图:

彩色图,我们用python中任意一个可以读入图片的函数读入查看,可以得到彩色图的维度是(H,W,channel=3)

非彩色图,我认为可以分为灰度图和黑白图,非彩色图他们都只有二维,即(H,W),我原本以为会有一个channel=1,后来发现并木有。灰度图是(H,W)维的0~255的数值,而黑白图除了0就是255,没有别的其他值(哦,顺便提一句,H是height,W是width,channel是颜色通道)

在这里,我想要着重的对mask图(也就是image对应的label)进行分析,我们输入的image就是良民,没什么特殊的。但是我们输入的mask图却有的不太对劲:

如果是二分类问题,那么mask图应当是二值的,如果二值指的是0和255,那么画面就十分和谐了,就像我们常规认识的那样:

 

输入的image(灰度图)

 

对应的mask图(二值图)

 

如果是多分类问题,当我们输下左边图一的照片时,在我的印象中应该对应的mask是一个美美的就像图二有木有,

但是实际的图是个什么玩意,居然全是黑的图三!!(于是我一度认为这个数据集是坏的)

图1 输入的image(彩色图)
图2 理想中的mask图

 

 

 

 

 

 

 

 

图3 实际的mask图

 

 

 

 

 

 

 

 

 

实际上这个黑乎乎的图并没有坏!它就是这个样儿!因为多分类的mask图里每一个像素点的值其实是类别值,比如说我们要分成12类,那么这张图里面的像素点的值全都是在0-11的数字。而我们知道0是黑,255是白,在0-11之间的数字在我们人眼看来也和全黑差不多了。但是当你把这张图读入并查看它的每一个像素点时,你就会发现其实里面是有蹊跷的。

如果想要将这张黑图显示出来,我们可以使用一些涂色的手段,在这里贴一下我之前找到的函数。

(1)首先是创建一个颜色字典,留着为后面用

Sky = [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])
#首先创建一个颜色字典,可以把它想象成一个染色盘(小学美术课用的那种),然后我们按照序号每一类取一个
#颜色出来涂上,至于名字为什么取什么sky,buliding?哎想怎么叫怎么叫呗!

 (2)其次是调用染色函数(染好色以后上述的黑黑的mask图三就会变成美美的图二了)(问题1:除255如果有人看懂了可以留个言告告我哈哈。。我也没整明白,感jio不除才对,结果除了数值没变,不除反而报错,哎你说我这暴脾气

def labelVisualize(num_class,color_dict,img):
    
#img就是我们的黑黑的mask图,而img是(H,W)二维的,我们需要加上一维,也就是下面这行
    img_out = np.zeros(img.shape + (3,))
#img.shape返回的是(H,W)的元组,而(3,)也代表元组,只不过是一维的,二者相加后就是一个
#三维的元组(H,W,channel)
    for i in range(num_class):
        img_out[img == i,:] = color_dict[i]
#总共循环num_class次,比如numclass=11,那么我们在循环11次过程中,每一次我们都找到img上像素点等于i
#的位置为它涂上第i种颜色(因为我们的黑黑的mask图像素点都是0-11之间的值),涂完后就可以了
    return img_out / 255
#这儿为啥除以255呢???
#不除的话会报错ValueError: Images of type float must be between -1 and 1 具体原因希望有人可以tell me

此外,虽然我没有见过,但是如果某一天二分类问题时候的mask图也是黑色说不定里边就只有0和1,处理同上 。

 

二、训练过程中图片结构的变化了解

我们知道,我们在训练时候加入输入的image结构是s1,而训练时输入的mask是s2。那么,我们在训练好后测试时也应当输入结构为s1的图片,并且我们会得到结构为s2的结果。另外,有一点需要注意的是,s1和s2我们也不能随意规定,因为你的model的输入输出维度才可以决定你要放入的图片维度。那么,我们怎么去修改图片的结构呢?

为了具体,我在这里选择了一个模型(FCN)作为讲解的model。首先,我们将模型summary一下观察一下它的整体结构:

省略了中间部分,这里只截取了输入和输出模型时的维度:

输入部分的维度
输出部分的维度

首先分析输入,输入部分这里只放了第一层卷积后的层为(none,256,256,64),none指的是batchsize可以为任意值,256,256是指本次卷积后的feature-map的长和宽,最后一维64指的是有64个filter因此生成深度为64。从这些分析,我认为输入的image应该是(batchsize,H,W,channel),channel应该是可有可无的,因为它并没有什么作用(因为卷积过程会根据是否为彩色图对卷积核作相应的调整,因为我在本次实验中用到了flow_from_directory这样一个生成器函数,生成的返回值中是四维,也就是包括了channel所以我在这里也带上channel一起说)。

其次分析输出,输出的(none,65536,2)是指(none,H*W,num_class)。这里稍作解释,模型的输出是将一个(H,W)的mask图(上面包括了num_class种种类),转变成了一个(H*W,num_class)维度的one-hot编码形式。并且这里的模型由于加入了softmax层,输出的是one-hot编码下各种类的概率值。什么??没听懂怎么就是one-hot编码形式了??那么我手画个图给大家演示一波!

大致就是这样转换成了one-hot编码方式,具体转换的代码如下:

    new_mask = np.zeros(mask.shape+(num_class,))
    # new_mask的shape是(batchsize,H,W,num_class)
    for i in range(num_class):
        new_mask[mask==i,i]=1
        #new_mask的每一层都标记上该层上像素值等于i的位置
   
    new_mask=np.reshape(new_mask,new_mask.shape[0],new_mask.shape[1]*new_mask.shape[2],mask.shape[3]))
#在reshape后的new_mask的维度成了(batchsize,H*W,num_class)也就是onehot编码了

那么,既然我们训练时候的mask是one-hot形式,那么我想,当我们输入训练集的时候,输出应该也是一个onehot编码方式,并且因为加入了softmax层后输出的是概率值,应该不只是在对应的类别那一列标1,应该是每一类的概率。那么如果我们想要把每一个像素究竟属于哪一类找出来,应该找出每一列中概率值最大的作为该像素点的类别,之后再reshape成正常的(H,W)图就可以了。

具体的实现可以这样做:

        mask=np.argmax(item,axis=1)#横着一行一行看,记录下来每一行的最大值概率所在的位置也就是该像素的类别
        mask = np.reshape(mask,(H,W))

这样我们得到的就是和训练时候一样的(H,W)的mask图了,如果是多分类可以用上面的涂色的函数显示,如果是二分类可以直接乘以255。然后保存即可!

三、一个bug,but我还没有想好,但是有可能是你出错的地方

我们知道,在训练的时候,我们会使用数据增强来增加数据,比如说我使用的方法如下:

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,
                    horizontal_flip=True,
                    fill_mode='nearest')
#用于数据增强的选项
mask_datagen = ImageDataGenerator(**aug_dict)
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
)
#通过flow_from_directory可以无限的生成增强后的图片,是一个棒棒的生成器函数

但是如果使用生成器的话,就会产生如下的问题:

我输入的mask是二值图(只有0和255)

当我归一化后应当只有0和1,然后采用上述转化方法转换为one-hot编码是没有问题的,但是如果数据增强的话,其中就会不只是0和255,而是产生了一些浮动的float类型的数字,如下:

当前的mask图状态呢如下

可见,这里的image和mask都变成float类型了,而且我发现mask中除了0和255还多出来不少别的数,怪不得我用newmask[mask==i,i]发现白色(也就是类别1)全是0,因为这个图上根本就找不到1啊(归一化后)!!

并且如果是多分类问题的话,那么原本输入的mask图中可能是0-numclass的一些整数,使用newmask[mask==i,i]就可以转为one-hot编码,但是若是数据增强后便会有浮动而非是0-numclass的整数,这样的话,想要转为onehot编码采用我上述的手段就不行了,bug源头出在这里,如果是二分类我们可以直接除以255后根据结果是否小于0.5来赋值0还是1,但是如果是多分类问题该如何转换呢?暂时还没想出来,这是我的第二个问题。

 

先记录至此,未完待续。。。。。希望有人可以告我如何解决(#^.^#)!

 

 

 

 

 

 

 

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值