京天华盛口碑怎么样的这个机箱怎么拆

DL开源框架_caffe(8)
深度学习(27)
一 全卷积神经网络
深度学习图像分割(FCN)训练自己的模型大致可以以下三步:
1.为自己的数据制作label;
2.将自己的数据分为train,val和test集;
3.仿照voc_lyaers.py编写自己的输入数据层。
其中主要是如何制作自己的数据label困扰着大家。
补充:由于图像大小的限制,这里给几个图像Resize的脚本:
(1)单张图片的resize
import Image
convert(width,height):
im = Image.open("C:\\xxx\\test.jpg")
out = im.resize((width, height),Image.ANTIALIAS)
out.save("C:\\xxx\\test.jpg")
if __name__ == '__main__':
convert(256,256)
(2)resize整个文件夹里的图片
import Image
def convert(dir,width,height):
file_list = os.listdir(dir)
print(file_list)
for filename in file_list:
path = dir+filename
im = Image.open(path)
out = im.resize((256,256),Image.ANTIALIAS)
print "%s has been resized!"%filename
out.save(path)
if __name__ == '__main__':
dir = raw_input('please input the operate dir:')
convert(dir,256,256)
(3)按比例resize
import Image
convert(width,height):
im = Image.open("C:\\workspace\\PythonLearn1\\test_1.jpg")
(x, y)= im.size
x_s = width
y_s = y * x_s / x
out = im.resize((x_s, y_s), Image.ANTIALIAS)
out.save("C:\\workspace\\PythonLearn1\\test_1_out.jpg")
if __name__ == '__main__':
convert(256,256)
二 图像标签制作
第一步:使用github开源软件进行标注
第二步:为标注出来的label.png进行着色
首先需要对照VOC分割的颜色进行着色,一定要保证颜色的准确性。Matlab代码:
function cmap = labelcolormap(N)
if nargin==0
cmap = zeros(N,3);
id = i-1; r=0;g=0;b=0;
r = bitor(r, bitshift(bitget(id,1),7 - j));
g = bitor(g, bitshift(bitget(id,2),7 - j));
b = bitor(b, bitshift(bitget(id,3),7 - j));
id = bitshift(id,-3);
cmap(i,1)=r; cmap(i,2)=g; cmap(i,3)=b;
cmap = cmap / 255;
对应的颜色类别:
类别名称 R G B
background 0 0 0 背景
aeroplane 128 0 0 飞机
bicycle 0 128 0
bird 128 128 0
boat 0 0 128
bottle 128 0 128 瓶子
bus 0 128 128 大巴
car 128 128 128
cat 64 0 0 猫
chair 192 0 0
cow 64 128 0
diningtable 192 128 0 餐桌
dog 64 0 128
horse 192 0 128
motorbike 64 128 128
person 192 128 128
pottedplant 0 64 0 盆栽
sheep 128 64 0
sofa 0 192 0
train 128 192 0
tvmonitor 0 64 128 显示器
然后使用python 的skimage库进行颜色填充,具体函数是skimage.color.label2rgb(),这部分代码以及颜色调整我已经完成了,由于代码太长就不贴出来了,有需要的可以私信我。
第三步:最关键的一步
需要注意的是,label文件要是gray格式,不然会出错:scores层输出与label的数据尺寸不一致,通道问题导致的,看下面的输出是否与VOC输出一致。
In [23]: img = PIL.Image.open('F:/DL/000001_json/test/dstfcn.png')
In [24]: np.unique(img)
Out[24]: array([0, 1, 2], dtype=uint8)
其中涉及到如何把24位png图转换为8位png图,直接上代码:
dirs=dir('F:/xxx/*.png');
for n=1:numel(dirs)
strname=strcat('F:/xxx/',dirs(n).name);
img=imread(strname);
[x,map]=rgb2ind(img,256);
newname=strcat('F:/xxx/',dirs(n).name);
imwrite(x,map,newname,'png');
三 FCN模型训练
推荐博客:
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:201003次
积分:2823
积分:2823
排名:第11782名
原创:67篇
转载:11篇
评论:69条
(1)(2)(1)(1)(3)(1)(2)(4)(1)(3)(9)(17)(5)(1)(6)(2)(3)(6)(4)(3)(1)(2)(1)全卷积网络 FCN 详解
全卷积网络 FCN 详解。CNN能够对图片进行分类,可是怎么样才能识别图片中特定部分的物体,在2015年之前还是一个世界难题。
CNN能够对图片进行分类,可是怎么样才能识别图片中特定部分的物体,在2015年之前还是一个世界难题。神经网络大神Jonathan Long发表了《Fully Convolutional Networks for Semantic Segmentation》在图像语义分割挖了一个坑,于是无穷无尽的人往坑里面跳。
全卷积网络 Fully Convolutional Networks
CNN 与 FCN
通常CNN网络在卷积层之后会接上若干个全连接层, 将卷积层产生的特征图(feature map)映射成一个固定长度的特征向量。以AlexNet为代表的经典CNN结构适合于图像级的分类和回归任务,因为它们最后都期望得到整个输入图像的一个数值描述(概率),比如AlexNet的ImageNet模型输出一个1000维的向量表示输入图像属于每一类的概率(softmax归一化)。
栗子:下图中的猫, 输入AlexNet, 得到一个长为1000的输出向量, 表示输入图像属于每一类的概率, 其中在“tabby cat”这一类统计概率最高。
FCN对图像进行像素级的分类,从而解决了语义级别的图像分割(semantic segmentation)问题。与经典的CNN在卷积层之后使用全连接层得到固定长度的特征向量进行分类(全联接层+softmax输出)不同,FCN可以接受任意尺寸的输入图像,采用反卷积层对最后一个卷积层的feature map进行上采样, 使它恢复到输入图像相同的尺寸,从而可以对每个像素都产生了一个预测, 同时保留了原始输入图像中的空间信息, 最后在上采样的特征图上进行逐像素分类。
最后逐个像素计算softmax分类的损失, 相当于每一个像素对应一个训练样本。下图是Longjon用于语义分割所采用的全卷积网络(FCN)的结构示意图:
简单的来说,FCN与CNN的区域在把于CNN最后的全连接层换成卷积层,输出的是一张已经Label好的图片。vcD4NCjxwPjxpbWcgYWx0PQ=="这里写图片描述" src="/uploadfile/Collfiles/158.png" title="\" />
其实,CNN的强大之处在于它的多层结构能自动学习特征,并且可以学习到多个层次的特征:较浅的卷积层感知域较小,学习到一些局部区域的特征;较深的卷积层具有较大的感知域,能够学习到更加抽象一些的特征。这些抽象特征对物体的大小、位置和方向等敏感性更低,从而有助于识别性能的提高。下图CNN分类网络的示意图:
这些抽象的特征对分类很有帮助,可以很好地判断出一幅图像中包含什么类别的物体,但是因为丢失了一些物体的细节,不能很好地给出物体的具体轮廓、指出每个像素具体属于哪个物体,因此做到精确的分割就很有难度。
传统的基于CNN的分割方法:为了对一个像素分类,使用该像素周围的一个图像块作为CNN的输入用于训练和预测。这种方法有几个缺点:一是存储开销很大。例如对每个像素使用的图像块的大小为15x15,然后不断滑动窗口,每次滑动的窗口给CNN进行判别分类,因此则所需的存储空间根据滑动窗口的次数和大小急剧上升。二是计算效率低下。相邻的像素块基本上是重复的,针对每个像素块逐个计算卷积,这种计算也有很大程度上的重复。三是像素块大小的限制了感知区域的大小。通常像素块的大小比整幅图像的大小小很多,只能提取一些局部的特征,从而导致分类的性能受到限制。
而全卷积网络(FCN)则是从抽象的特征中恢复出每个像素所属的类别。即从图像级别的分类进一步延伸到像素级别的分类。
全连接层 -& 成卷积层
全连接层和卷积层之间唯一的不同就是卷积层中的神经元只与输入数据中的一个局部区域连接,并且在卷积列中的神经元共享参数。然而在两类层中,神经元都是计算点积,所以它们的函数形式是一样的。因此,将此两者相互转化是可能的:
对于任一个卷积层,都存在一个能实现和它一样的前向传播函数的全连接层。权重矩阵是一个巨大的矩阵,除了某些特定块,其余部分都是零。而在其中大部分块中,元素都是相等的。
相反,任何全连接层都可以被转化为卷积层。比如,一个 K=4096 的全连接层,输入数据体的尺寸是 7?7?512,这个全连接层可以被等效地看做一个 F=7,P=0,S=1,K=4096 的卷积层。换句话说,就是将滤波器的尺寸设置为和输入数据体的尺寸一致了。因为只有一个单独的深度列覆盖并滑过输入数据体,所以输出将变成 1?1?4096,这个结果就和使用初始的那个全连接层一样了。
全连接层转化为卷积层:在两种变换中,将全连接层转化为卷积层在实际运用中更加有用。假设一个卷积神经网络的输入是 224x224x3 的图像,一系列的卷积层和下采样层将图像数据变为尺寸为 7x7x512 的激活数据体。AlexNet使用了两个尺寸为4096的全连接层,最后一个有1000个神经元的全连接层用于计算分类评分。我们可以将这3个全连接层中的任意一个转化为卷积层:
针对第一个连接区域是[7x7x512]的全连接层,令其滤波器尺寸为F=7,这样输出数据体就为[1x1x4096]了。针对第二个全连接层,令其滤波器尺寸为F=1,这样输出数据体为[1x1x4096]。对最后一个全连接层也做类似的,令其F=1,最终输出为[1x1x1000]
实际操作中,每次这样的变换都需要把全连接层的权重W重塑成卷积层的滤波器。那么这样的转化有什么作用呢?它在下面的情况下可以更高效:让卷积网络在一张更大的输入图片上滑动,得到多个输出,这样的转化可以让我们在单个向前传播的过程中完成上述的操作。
举个栗子:如果我们想让224&224尺寸的浮窗,以步长为32在384&384的图片上滑动,把每个经停的位置都带入卷积网络,最后得到6&6个位置的类别得分。上述的把全连接层转换成卷积层的做法会更简便。如果224&224的输入图片经过卷积层和下采样层之后得到了[7x7x512]的数组,那么,384&384的大图片直接经过同样的卷积层和下采样层之后会得到[12x12x512]的数组。然后再经过上面由3个全连接层转化得到的3个卷积层,最终得到[6x6x1000]的输出((12 & 7)/1 + 1 = 6)。这个结果正是浮窗在原图经停的6&6个位置的得分!
面对384&384的图像,让(含全连接层)的初始卷积神经网络以32像素的步长独立对图像中的224&224块进行多次评价,其效果和使用把全连接层变换为卷积层后的卷积神经网络进行一次前向传播是一样的。
Evaluating the original ConvNet (with FC layers) independently across 224x224 crops of the 384x384 image in strides of 32 pixels gives an identical result to forwarding the converted ConvNet one time.
如下图所示,FCN将传统CNN中的全连接层转化成卷积层,对应CNN网络FCN把最后三层全连接层转换成为三层卷积层。在传统的CNN结构中,前5层是卷积层,第6层和第7层分别是一个长度为4096的一维向量,第8层是长度为1000的一维向量,分别对应1000个不同类别的概率。FCN将这3层表示为卷积层,卷积核的大小 (通道数,宽,高) 分别为 ()、()、()。看上去数字上并没有什么差别,但是卷积跟全连接是不一样的概念和计算过程,使用的是之前CNN已经训练好的权值和偏置,但是不一样的在于权值和偏置是有自己的范围,属于自己的一个卷积核。因此FCN网络中所有的层都是卷积层,故称为全卷积网络。
下图是一个全卷积层,与上图不一样的是图像对应的大小下标,CNN中输入的图像大小是同意固定resize成 227x227 大小的图像,第一层pooling后为55x55,第二层pooling后图像大小为27x27,第五层pooling后的图像大小为13*13。而FCN输入的图像是H*W大小,第一层pooling后变为原图大小的1/4,第二层变为原图大小的1/8,第五层变为原图大小的1/16,第八层变为原图大小的1/32(勘误:其实真正代码当中第一层是1/2,以此类推)。
经过多次卷积和pooling以后,得到的图像越来越小,分辨率越来越低。其中图像到 H/32?W/32 的时候图片是最小的一层时,所产生图叫做heatmap热图,热图就是我们最重要的高维特诊图,得到高维特征的heatmap之后就是最重要的一步也是最后的一步对原图像进行upsampling,把图像进行放大、放大、放大,到原图像的大小。
最后的输出是1000张heatmap经过upsampling变为原图大小的图片,为了对每个像素进行分类预测label成最后已经进行语义分割的图像,这里有一个小trick,就是最后通过逐个像素地求其在1000张图像该像素位置的最大数值描述(概率)作为该像素的分类。因此产生了一张已经分类好的图片,如下图右侧有狗狗和猫猫的图。
upsampling
相较于使用被转化前的原始卷积神经网络对所有36个位置进行迭代计算,使用转化后的卷积神经网络进行一次前向传播计算要高效得多,因为36次计算都在共享计算资源。这一技巧在实践中经常使用,一次来获得更好的结果。比如,通常将一张图像尺寸变得更大,然后使用变换后的卷积神经网络来对空间上很多不同位置进行评价得到分类评分,然后在求这些分值的平均值。
最后,如果我们想用步长小于32的浮窗怎么办?用多次的向前传播就可以解决。比如我们想用步长为16的浮窗。那么先使用原图在转化后的卷积网络执行向前传播,然后分别沿宽度,沿高度,最后同时沿宽度和高度,把原始图片分别平移16个像素,然后把这些平移之后的图分别带入卷积网络。
如下图所示,当图片在网络中经过处理后变成越小的图片,其特征也越明显,就像图像中颜色所示,当然啦,最后一层的图片不再是一个1个像素的图片,而是原图像 H/32xW/32 大小的图,这里为了简化而画成一个像素而已。
如下图所示,对原图像进行卷积conv1、pool1后原图像缩小为1/2;之后对图像进行第二次conv2、pool2后图像缩小为1/4;接着继续对图像进行第三次卷积操作conv3、pool3缩小为原图像的1/8,此时保留pool3的featureMap;接着继续对图像进行第四次卷积操作conv4、pool4,缩小为原图像的1/16,保留pool4的featureMap;最后对图像进行第五次卷积操作conv5、pool5,缩小为原图像的1/32,然后把原来CNN操作中的全连接变成卷积操作conv6、conv7,图像的featureMap数量改变但是图像大小依然为原图的1/32,此时图像不再叫featureMap而是叫heatMap。
现在我们有1/32尺寸的heatMap,1/16尺寸的featureMap和1/8尺寸的featureMap,1/32尺寸的heatMap进行upsampling操作之后,因为这样的操作还原的图片仅仅是conv5中的卷积核中的特征,限于精度问题不能够很好地还原图像当中的特征,因此在这里向前迭代。把conv4中的卷积核对上一次upsampling之后的图进行反卷积补充细节(相当于一个差值过程),最后把conv3中的卷积核对刚才upsampling之后的图像进行再次反卷积补充细节,最后就完成了整个图像的还原。
在这里我们要注意的是FCN的缺点:
是得到的结果还是不够精细。进行8倍上采样虽然比32倍的效果好了很多,但是上采样的结果还是比较模糊和平滑,对图像中的细节不敏感。
是对各个像素进行分类,没有充分考虑像素与像素之间的关系。忽略了在通常的基于像素分类的分割方法中使用的空间规整(spatial regularization)步骤,缺乏空间一致性。Fast RCNN 训练自己数据集 (2修改数据读取接口) - 推酷
Fast RCNN 训练自己数据集 (2修改数据读取接口)
Fast RCNN训练自己的数据集 (2修改读写接口)
这里楼主讲解了如何修改Fast RCNN训练自己的数据集,首先请确保你已经安装好了Fast RCNN的环境,具体的编配编制操作请参考我的上一篇文章。首先可以看到fast rcnn的工程目录下有个Lib目录这里下面存在3个目录分别是:
roi_data_layer
在这里修改读写数据的接口主要是datasets目录下,fast_rcnn下面主要存放的是python的训练和测试脚本,以及训练的配置文件,roi_data_layer下面存放的主要是一些ROI处理操作,utils下面存放的是一些通用操作比如非极大值nms,以及计算bounding box的重叠率等常用功能
1.构建自己的IMDB子类
1.1文件概述
可有看到datasets目录下主要有三个文件,分别是
factory.py
pascal_voc.py
factory.py 学过设计模式的应该知道这是个工厂类,用类生成imdb类并且返回数据库共网络训练和测试使用
imdb.py 这里是数据库读写类的基类,分装了许多db的操作,但是具体的一些文件读写需要继承继续读写
pascal_voc.py Ross在这里用pascal_voc.py这个类来操作
1.2 读取文件函数分析
接下来我来介绍一下pasca_voc.py这个文件,我们主要是基于这个文件进行修改,里面有几个重要的函数需要修改
def init (self, image_set, year, devkit_path=None)
这个是初始化函数,它对应着的是pascal_voc的数据集访问格式,其实我们将其接口修改的更简单一点
def image_path_at(self, i)
根据第i个图像样本返回其对应的path,其调用了image_path_from_index(self, index)作为其具体实现
def image_path_from_index(self, index)
实现了 image_path的具体功能
def load image_set_index(self)
加载了样本的list文件
def get default_path(self)
获得数据集地址
def gt_roidb(self)
读取并返回ground_truth的db
def selective_search_roidb
读取并返回ROI的db
def load selective_search_roidb(self, gt_roidb)
加载预选框的文件
def selective_search_IJCV_roidb(self)
在这里调用读取Ground_truth和ROI db并将db合并
def load selective_search_IJCV_roidb(self, gt_roidb)
这里是专门读取作者在IJCV上用的dataset
def load pascal_annotation(self, index)
这个函数是读取gt的具体实现
def write voc_results_file(self, all_boxes)
voc的检测结果写入到文件
def do matlab_eval(self, comp_id, output_dir='output')
根据matlab的evluation接口来做结果的分析
def evaluate_detections
其调用了_do_matlab_eval
def competition_mode
设置competitoin_mode,加了一些噪点
1.3训练数据集格式
在我的检测任务里,我主要是从道路卡口数据中检测车,因此我这里只有background 和car两类物体,为了操作方便,我不像pascal_voc数据集里面一样每个图像用一个xml来标注多类,先说一下我的数据格式
这里是所有样本的图像列表
我的GroundTruth数据的格式,第一个为图像路径,之后1代表目标物的个数, 后面的坐标代表左上右下的坐标,坐标的位置从1开始
这里我要特别提醒一下大家,一定要注意坐标格式,一定要注意坐标格式,一定要注意坐标格式,重要的事情说三遍!!!,要不然你会范很多错误都会是因为坐标不一致引起的报错
1.4修改读取接口
这里是原始的pascal_voc的init函数,在这里,由于我们自己的数据集往往比voc的数据集要更简单的一些,在作者额代码里面用了很多的路径拼接,我们不用去迎合他的格式,将这些操作简单化即可,在这里我会一一列举每个我修改过的函数。这里按照文件中的顺序排列。原始初始化函数:
def __init__(self, image_set, year, devkit_path=None):
datasets.imdb.__init__(self, 'voc_' + year + '_' + image_set)
self._year = year
self._image_set = image_set
self._devkit_path = self._get_default_path() if devkit_path is None \
else devkit_path
self._data_path = os.path.join(self._devkit_path, 'VOC' + self._year)
self._classes = ('__background__', # always index 0
'aeroplane', 'bicycle', 'bird', 'boat',
'bottle', 'bus', 'car', 'cat', 'chair',
'cow', 'diningtable', 'dog', 'horse',
'motorbike', 'person', 'pottedplant',
'sheep', 'sofa', 'train', 'tvmonitor')
self._class_to_ind = dict(zip(self.classes, xrange(self.num_classes)))
self._image_ext = '.jpg'
self._image_index = self._load_image_set_index()
# Default to roidb handler
self._roidb_handler = self.selective_search_roidb
# PASCAL specific config options
self.config = {'cleanup'
'use_salt' : True,
assert os.path.exists(self._devkit_path), \
'VOCdevkit path does not exist: {}'.format(self._devkit_path)
assert os.path.exists(self._data_path), \
'Path does not exist: {}'.format(self._data_path)
修改后的初始化函数:
def __init__(self, image_set, devkit_path=None):
datasets.imdb.__init__(self, image_set)#imageset 为train
self._image_set = image_set
self._devkit_path = devkit_path
self._data_path = os.path.join(self._devkit_path)
self._classes = ('__background__','car')#包含的类
self._class_to_ind = dict(zip(self.classes, xrange(self.num_classes)))#构成字典{'__background__':'0','car':'1'}
self._image_index = self._load_image_set_index('ImageList_Version_S_AddData.txt')#添加文件列表
# Default to roidb handler
self._roidb_handler = self.selective_search_roidb
# PASCAL specific config options
self.config = {'cleanup'
'use_salt' : True,
assert os.path.exists(self._devkit_path), \
'VOCdevkit path does not exist: {}'.format(self._devkit_path)
assert os.path.exists(self._data_path), \
'Path does not exist: {}'.format(self._data_path)
原始的image_path_from_index:
def image_path_from_index(self, index):
Construct an image path from the image's &index& identifier.
image_path = os.path.join(self._data_path, 'JPEGImages',
index + self._image_ext)
assert os.path.exists(image_path), \
'Path does not exist: {}'.format(image_path)
return image_path
修改后的image_path_from_index:
def image_path_from_index(self, index):#根据_image_index获取图像路径
Construct an image path from the image's &index& identifier.
image_path = os.path.join(self._data_path, index)
assert os.path.exists(image_path), \
'Path does not exist: {}'.format(image_path)
return image_path
原始的 load image_set_index:
def _load_image_set_index(self):
Load the indexes listed in this dataset's image set file.
# Example path to image set file:
# self._devkit_path + /VOCdevkit2007/VOC2007/ImageSets/Main/val.txt
image_set_file = os.path.join(self._data_path, 'ImageSets', 'Main',
self._image_set + '.txt')
assert os.path.exists(image_set_file), \
'Path does not exist: {}'.format(image_set_file)
with open(image_set_file) as f:
image_index = [x.strip() for x in f.readlines()]
return image_index
修改后的 load image_set_index:
def _load_image_set_index(self, imagelist):#已经修改
Load the indexes listed in this dataset's image set file.
# Example path to image set file:
# self._devkit_path + /VOCdevkit2007/VOC2007/ImageSets/Main/val.txt
#/home/chenjie/KakouTrainForFRCNN_1/DataSet/KakouTrainFRCNN_ImageList.txt
image_set_file = os.path.join(self._data_path, imagelist)# load ImageList that only contain ImageFileName
assert os.path.exists(image_set_file), \
'Path does not exist: {}'.format(image_set_file)
with open(image_set_file) as f:
image_index = [x.strip() for x in f.readlines()]
return image_index
函数 get default_path,我直接删除了
原始的gt_roidb:
def gt_roidb(self):
Return the database of ground-truth regions of interest.
This function loads/saves from/to a cache file to speed up future calls.
cache_file = os.path.join(self.cache_path, self.name + '_gt_roidb.pkl')
if os.path.exists(cache_file):
with open(cache_file, 'rb') as fid:
roidb = cPickle.load(fid)
print '{} gt roidb loaded from {}'.format(self.name, cache_file)
return roidb
gt_roidb = [self._load_pascal_annotation(index)
for index in self.image_index]
with open(cache_file, 'wb') as fid:
cPickle.dump(gt_roidb, fid, cPickle.HIGHEST_PROTOCOL)
print 'wrote gt roidb to {}'.format(cache_file)
return gt_roidb
修改后的gt_roidb:
def gt_roidb(self):
Return the database of ground-truth regions of interest.
This function loads/saves from/to a cache file to speed up future calls.
cache_file = os.path.join(self.cache_path, self.name + '_gt_roidb.pkl')
if os.path.exists(cache_file):#若存在cache file则直接从cache file中读取
with open(cache_file, 'rb') as fid:
roidb = cPickle.load(fid)
print '{} gt roidb loaded from {}'.format(self.name, cache_file)
return roidb
gt_roidb = self._load_annotation()
#已经修改,直接读入整个GT文件
with open(cache_file, 'wb') as fid:
cPickle.dump(gt_roidb, fid, cPickle.HIGHEST_PROTOCOL)
print 'wrote gt roidb to {}'.format(cache_file)
return gt_roidb
原始的selective_search_roidb(self):
def selective_search_roidb(self):
Return the database of selective search regions of interest.
Ground-truth ROIs are also included.
This function loads/saves from/to a cache file to speed up future calls.
cache_file = os.path.join(self.cache_path,
self.name + '_selective_search_roidb.pkl')
if os.path.exists(cache_file):
with open(cache_file, 'rb') as fid:
roidb = cPickle.load(fid)
print '{} ss roidb loaded from {}'.format(self.name, cache_file)
return roidb
if int(self._year) == 2007 or self._image_set != 'test':
gt_roidb = self.gt_roidb()
ss_roidb = self._load_selective_search_roidb(gt_roidb)
roidb = datasets.imdb.merge_roidbs(gt_roidb, ss_roidb)
roidb = self._load_selective_search_roidb(None)
with open(cache_file, 'wb') as fid:
cPickle.dump(roidb, fid, cPickle.HIGHEST_PROTOCOL)
print 'wrote ss roidb to {}'.format(cache_file)
return roidb
修改后的selective_search_roidb(self):这里有个pkl文件我需要特别说明一下,如果你再次训练的时候修改了数据库,比如添加或者删除了一些样本,但是你的数据库名字函数原来那个,比如我这里训练的数据库叫KakouTrain,必须要在data/cache/目录下把数据库的缓存文件.pkl给删除掉,否则其不会重新读取相应的数据库,而是直接从之前读入然后缓存的pkl文件中读取进来,这样修改的数据库并没有进入网络,而是加载了老版本的数据。
def selective_search_roidb(self):#已经修改
Return the database of selective search regions of interest.
Ground-truth ROIs are also included.
This function loads/saves from/to a cache file to speed up future calls.
cache_file = os.path.join(self.cache_path,self.name + '_selective_search_roidb.pkl')
if os.path.exists(cache_file): #若存在cache_file则读取相对应的.pkl文件
with open(cache_file, 'rb') as fid:
roidb = cPickle.load(fid)
print '{} ss roidb loaded from {}'.format(self.name, cache_file)
return roidb
if self._image_set !='KakouTest':
gt_roidb = self.gt_roidb()
ss_roidb = self._load_selective_search_roidb(gt_roidb)
roidb = datasets.imdb.merge_roidbs(gt_roidb, ss_roidb)
roidb = self._load_selective_search_roidb(None)
with open(cache_file, 'wb') as fid:
cPickle.dump(roidb, fid, cPickle.HIGHEST_PROTOCOL)
print 'wrote ss roidb to {}'.format(cache_file)
return roidb
原始的_load_selective_search_roidb(self, gt_roidb):
def _load_selective_search_roidb(self, gt_roidb):
filename = os.path.abspath(os.path.join(self.cache_path, '..',
'selective_search_data',
self.name + '.mat'))
assert os.path.exists(filename), \
'Selective search data not found at: {}'.format(filename)
raw_data = sio.loadmat(filename)['boxes'].ravel()
box_list = []
for i in xrange(raw_data.shape[0]):
box_list.append(raw_data[i][:, (1, 0, 3, 2)] - 1)
return self.create_roidb_from_box_list(box_list, gt_roidb)
修改后的_load_selective_search_roidb(self, gt_roidb):
这里原作者用的是Selective_search,但是我用的是EdgeBox的方法来提取Mat,我没有修改函数名,只是把输入的Mat文件给替换了,Edgebox实际的效果比selective_search要好,速度也要更快,具体的EdgeBox代码大家可以在Ross的tutorial中看到地址。
注意,这里非常关键!!!!!,由于Selective_Search中的OP返回的坐标顺序需要调整,并不是左上右下的顺序,可以看到在下面box_list.append()中有一个(1,0,3,2)的操作,不管你用哪种OP方法,输入的坐标都应该是x1 y1 x2 y2,不要弄成w h 那种格式,也不要调换顺序。坐标-1,默认坐标从0开始,楼主提醒各位,一定要非常注意坐标顺序,大小,边界,格式问题,否则你会被错误折腾死的!!!
def _load_selective_search_roidb(self, gt_roidb):#已经修改
#filename = os.path.abspath(os.path.join(self.cache_path, '..','selective_search_data',self.name + '.mat'))
filename = os.path.join(self._data_path, 'EdgeBox_Version_S_AddData.mat')#这里输入相对应的预选框文件路径
assert os.path.exists(filename), \
'Selective search data not found at: {}'.format(filename)
raw_data = sio.loadmat(filename)['boxes'].ravel()
box_list = []
for i in xrange(raw_data.shape[0]):
#box_list.append(raw_data[i][:,(1, 0, 3, 2)] - 1)#原来的Psacalvoc调换了列,我这里box的顺序是x1 ,y1,x2,y2 由EdgeBox格式为x1,y1,w,h经过修改
box_list.append(raw_data[i][:,:] -1)
return self.create_roidb_from_box_list(box_list, gt_roidb)
原始的_load_selective_search_IJCV_roidb,我没用这个数据集,因此不修改这个函数
原始的_load_pascal_annotation(self, index):
def _load_pascal_annotation(self, index):
Load image and bounding boxes info from XML file in the PASCAL VOC
filename = os.path.join(self._data_path, 'Annotations', index + '.xml')
# print 'Loading: {}'.format(filename)
def get_data_from_tag(node, tag):
return node.getElementsByTagName(tag)[0].childNodes[0].data
with open(filename) as f:
data = minidom.parseString(f.read())
objs = data.getElementsByTagName('object')
num_objs = len(objs)
boxes = np.zeros((num_objs, 4), dtype=np.uint16)
gt_classes = np.zeros((num_objs), dtype=np.int32)
overlaps = np.zeros((num_objs, self.num_classes), dtype=np.float32)
# Load object bounding boxes into a data frame.
for ix, obj in enumerate(objs):
# Make pixel indexes 0-based
x1 = float(get_data_from_tag(obj, 'xmin')) - 1
y1 = float(get_data_from_tag(obj, 'ymin')) - 1
x2 = float(get_data_from_tag(obj, 'xmax')) - 1
y2 = float(get_data_from_tag(obj, 'ymax')) - 1
cls = self._class_to_ind[
str(get_data_from_tag(obj, &name&)).lower().strip()]
boxes[ix, :] = [x1, y1, x2, y2]
gt_classes[ix] = cls
overlaps[ix, cls] = 1.0
overlaps = scipy.sparse.csr_matrix(overlaps)
return {'boxes' : boxes,
'gt_classes': gt_classes,
'gt_overlaps' : overlaps,
'flipped' : False}
修改后的_load_pascal_annotation(self, index):
def _load_annotation(self):
Load image and bounding boxes info from annotation
#,此函数作用读入GT文件,我的文件的格式 CarTrainingDataForFRCNN_1\Images\131.jpg 1 147 65 443 361
gt_roidb = []
annotationfile = os.path.join(self._data_path, 'ImageList_Version_S_GT_AddData.txt')
f = open(annotationfile)
split_line = f.readline().strip().split()
while(split_line):
num_objs = int(split_line[1])
boxes = np.zeros((num_objs, 4), dtype=np.uint16)
gt_classes = np.zeros((num_objs), dtype=np.int32)
overlaps = np.zeros((num_objs, self.num_classes), dtype=np.float32)
for i in range(num_objs):
x1 = float( split_line[2 + i * 4])
y1 = float (split_line[3 + i * 4])
x2 = float (split_line[4 + i * 4])
y2 = float (split_line[5 + i * 4])
cls = self._class_to_ind['car']
boxes[i,:] = [x1, y1, x2, y2]
gt_classes[i] = cls
overlaps[i,cls] = 1.0
overlaps = scipy.sparse.csr_matrix(overlaps)
gt_roidb.append({'boxes' : boxes, 'gt_classes': gt_classes, 'gt_overlaps' : overlaps, 'flipped' : False})
split_line = f.readline().strip().split()
return gt_roidb
之后的这几个函数我都没有修改,检测结果,我是修改了demo.py这个文件,直接生成txt文件,然后用python opencv直接可视化,没有用着里面的接口,感觉太麻烦了,先怎么方便怎么来
write voc_results_file(self, all_boxes)
do matlab_eval(self, comp_id, output_dir='output')
evaluate_detections(self, all_boxes, output_dir)
competition_mode(self, on)
记得在最后的__main__下面也修改相应的路径
d = datasets.pascal_voc('trainval', '2007')
d = datasets.kakou('KakouTrain', '/home/chenjie/KakouTrainForFRCNN_1')
并且同时在文件的开头import 里面也做修改
import datasets.pascal_voc
import datasets.kakou
OK,在这里我们已经完成了整个的读取接口的改写,主要是将GT和预选框Mat文件读取并返回
2.修改factory.py
当网络训练时会调用factory里面的get方法获得相应的imdb,
首先在文件头import 把pascal_voc改成kakou
在这个文件作者生成了多个数据库的路径,我们自己数据库只要给定根路径即可,修改主要有以下4个
因此将里面的def selective search_IJCV_top_k函数整个注释掉
函数之后有两个多级的for循环,也将其注释
直接定义imageset和devkit
修改get_imdb函数
原始的factory.py:
__sets = {}
import datasets.pascal_voc
import numpy as np
def _selective_search_IJCV_top_k(split, year, top_k):
&&&Return an imdb that uses the top k proposals from the selective search
IJCV code.
imdb = datasets.pascal_voc(split, year)
imdb.roidb_handler = imdb.selective_search_IJCV_roidb
imdb.config['top_k'] = top_k
return imdb
# Set up voc_&year&_&split& using selective search &fast& mode
for year in ['2007', '2012']:
for split in ['train', 'val', 'trainval', 'test']:
name = 'voc_{}_{}'.format(year, split)
__sets[name] = (lambda split=split, year=year:
datasets.pascal_voc(split, year))
# Set up voc_&year&_&split&_top_&k& using selective search &quality& mode
# but only returning the first k boxes
for top_k in np.arange(, 1000):
for year in ['2007', '2012']:
for split in ['train', 'val', 'trainval', 'test']:
name = 'voc_{}_{}_top_{:d}'.format(year, split, top_k)
__sets[name] = (lambda split=split, year=year, top_k=top_k:
_selective_search_IJCV_top_k(split, year, top_k))
def get_imdb(name):
&&&Get an imdb (image database) by name.&&&
if not __sets.has_key(name):
raise KeyError('Unknown dataset: {}'.format(name))
return __sets[name]()
def list_imdbs():
&&&List all registered imdbs.&&&
return __sets.keys()
修改后的factory.py
#import datasets.pascal_voc
import datasets.kakou
import numpy as np
__sets = {}
imageset = 'KakouTrain'
devkit = '/home/chenjie/DataSet/CarTrainingDataForFRCNN_1/Images_Version_S_AddData'
#def _selective_search_IJCV_top_k(split, year, top_k):
# &&&Return an imdb that uses the top k proposals from the selective search
# IJCV code.
# imdb = datasets.pascal_voc(split, year)
# imdb.roidb_handler = imdb.selective_search_IJCV_roidb
# imdb.config['top_k'] = top_k
# return imdb
### Set up voc_&year&_&split& using selective search &fast& mode
##for year in ['2007', '2012']:
## for split in ['train', 'val', 'trainval', 'test']:
name = 'voc_{}_{}'.format(year, split)
__sets[name] = (lambda split=split, year=year:
datasets.pascal_voc(split, year))
# Set up voc_&year&_&split&_top_&k& using selective search &quality& mode
# but only returning the first k boxes
##for top_k in np.arange(, 1000):
## for year in ['2007', '2012']:
for split in ['train', 'val', 'trainval', 'test']:
name = 'voc_{}_{}_top_{:d}'.format(year, split, top_k)
__sets[name] = (lambda split=split, year=year, top_k=top_k:
_selective_search_IJCV_top_k(split, year, top_k))
def get_imdb(name):
&&&Get an imdb (image database) by name.&&&
__sets['KakouTrain'] = (lambda imageset = imageset, devkit = devkit: datasets.kakou(imageset,devkit))
if not __sets.has_key(name):
raise KeyError('Unknown dataset: {}'.format(name))
return __sets[name]()
def list_imdbs():
&&&List all registered imdbs.&&&
return __sets.keys()
3.修改 __init__.py在行首添加上 from .kakou import kakou
在这里终于改完了读取接口的所有内容,主要步骤是
复制pascal_voc,改名字,修改GroundTruth和OP预选框的读取方式
修改factory.py,修改数据库路径和获得方式
__init__.py添加上改完的py文件
下面列出一些需要注意的地方
读取方式怎么方便怎么来,并不一定要按照里面xml的格式,因为大家自己应用到工程中去往往不会是非常多的类别,单个对象的直接用txt就可以
坐标的顺序我再说一次,要左上右下,并且x1必须要小于x2,这个是基本,反了会在坐标水平变换的时候会出错,坐标从0开始,如果已经是0,则不需要再-1
GT的路径最好用相对,别用绝对,然后路径拼接的时候要注意,然后如果是txt是windows下生成的,注意斜杠的方向和编码的格式,中文路径编码必须用UTF-8无BOM格式,不能用windows自带的记事本直接换一种编码存储,相关数据集的编码问题参见我的另一篇文章,linux传输乱码
关于Mat文件,在训练时是将所有图像的OP都合在了一起,是一个很大的Mat文件,注意其中图像list的顺序千万不能错,并且坐标格式要修改为x1 y1 x2 y2,每种OP生成的坐标顺序要小心,从0开始还是从1开始也要小心
训练图像的大小不要太大,否则生成的OP也会太多,速度太慢,图像样本大小最好调整到500,600左右,然后再提取OP
如果读取并生成pkl文件之后,实际数据内容或者顺序还有问题,记得要把data/cache/下面的pkl文件给删掉
关于下部训练和检测网络,我将在下一篇文章中说明
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致

我要回帖

更多关于 京天华盛怎么样 的文章

 

随机推荐