在搭建完object detection环境之后(参考文章:) 我便开始着手参照文章做一个自己的小应用,目标是通过训练图片,让机器学习检测图片中是否含有武大靖这个人。
1、在网上收集武大靖的照片,一共100张,文件名1-100.jpg
在models\research\object_detection目录下面新建images文件夹,存放图片。新建连个子文件夹 train和test,分别存放训练集图片和测试集图片。
2、使用下载LabelImg软件,对图片进行标注:
自定义标签 wdj,每个图片保存之后会生成一个.xml文件,保存标签的位置信息。
3、将xml文件转换成csv文件:
# -*- coding: utf-8 -*-
"""
Created on Fri May 3 08:24:30 2019
@author: jack
"""
import os
import glob
import pandas as pd
import xml.etree.ElementTree as ET
def xml_to_csv(path):
xml_list = []
for xml_file in glob.glob(path + '\\*.xml'):
print(xml_file)
tree = ET.parse(xml_file)
root = tree.getroot()
for member in root.findall('object'):
value = (root.find('filename').text,
int(root.find('size')[0].text),
int(root.find('size')[1].text),
member[0].text,
int(member[4][0].text),
int(member[4][1].text),
int(member[4][2].text),
int(member[4][3].text)
)
xml_list.append(value)
column_name = ['filename', 'width', 'height', 'class', 'xmin', 'ymin', 'xmax', 'ymax']
xml_df = pd.DataFrame(xml_list, columns=column_name)
return xml_df
def main():
print(os.getcwd())
for folder in ['train','test']:
image_path = os.path.join('C:\\Users\\jack\\models\\research\\object_detection\\', ('images\\' + folder))
print(image_path)
#这里就是需要访问的.xml的存放地址
xml_df = xml_to_csv(image_path)
#object_detection/images/train or test
xml_df.to_csv(('C:\\Users\\jack\\models\\research\\object_detection\\images\\' + folder + '_labels.csv'), index=None)
print('Successfully converted xml to csv.')
main()
image_path这里容易出错,我索性改成了绝对路径。执行成功之后images文件夹下面就有两个新的csv文件了。文件内容如下:
4、将csv文件生成tfrecord格式:这一步把我折磨惨了。。。。一直报错。
程序如下:
"""
Usage:
# From tensorflow/models/
# Create train data:
python generate_tfrecord.py --csv_input=images/train_labels.csv --image_dir=images/train --output_path=train.record
python generate_tfrecord.py --csv_input=images/test_labels.csv --image_dir=images/test --output_path=test.record
"""
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import
import os
import io
import pandas as pd
import tensorflow as tf
from PIL import Image
from object_detection.utils import dataset_util
from collections import namedtuple, OrderedDict
flags = tf.app.flags
flags.DEFINE_string('csv_input', '', 'Path to the CSV input')
flags.DEFINE_string('image_dir', '', 'Path to the image directory')
flags.DEFINE_string('output_path', '', 'Path to output TFRecord')
FLAGS = flags.FLAGS
def class_text_to_int(row_label):
if row_label == 'wdj': #这里需要自己修改,有多少个类就该多少个!!!!
return 1
else:
None
def split(df, group):
data = namedtuple('data', ['filename', 'object'])
gb = df.groupby(group)
return [data(filename, gb.get_group(x)) for filename, x in zip(gb.groups.keys(), gb.groups)]
def create_tf_example(group, path):
with tf.gfile.GFile(os.path.join(path, '{}'.format(group.filename)), 'rb') as fid:
encoded_jpg = fid.read()
encoded_jpg_io = io.BytesIO(encoded_jpg)
image = Image.open(encoded_jpg_io)
width, height = image.size
filename = group.filename.encode('utf8')
image_format = b'jpg'
xmins = []
xmaxs = []
ymins = []
ymaxs = []
classes_text = []
classes = []
for index, row in group.object.iterrows():
xmins.append(row['xmin'] / width)
xmaxs.append(row['xmax'] / width)
ymins.append(row['ymin'] / height)
ymaxs.append(row['ymax'] / height)
classes_text.append(row['class'].encode('utf8'))
classes.append(class_text_to_int(row['class']))
tf_example = tf.train.Example(features=tf.train.Features(feature={
'image/height': dataset_util.int64_feature(height),
'image/width': dataset_util.int64_feature(width),
'image/filename': dataset_util.bytes_feature(filename),
'image/source_id': dataset_util.bytes_feature(filename),
'image/encoded': dataset_util.bytes_feature(encoded_jpg),
'image/format': dataset_util.bytes_feature(image_format),
'image/object/bbox/xmin': dataset_util.float_list_feature(xmins),
'image/object/bbox/xmax': dataset_util.float_list_feature(xmaxs),
'image/object/bbox/ymin': dataset_util.float_list_feature(ymins),
'image/object/bbox/ymax': dataset_util.float_list_feature(ymaxs),
'image/object/class/text': dataset_util.bytes_list_feature(classes_text),
'image/object/class/label': dataset_util.int64_list_feature(classes),
}))
return tf_example
def main(_):
writer = tf.python_io.TFRecordWriter(FLAGS.output_path)
path = os.path.join(os.getcwd(), FLAGS.image_dir)
print(path)
examples = pd.read_csv(FLAGS.csv_input)
grouped = split(examples, 'filename')
for group in grouped:
tf_example = create_tf_example(group, path)
writer.write(tf_example.SerializeToString())
writer.close()
output_path = os.path.join(os.getcwd(), FLAGS.output_path)
print('Successfully created the TFRecords: {}'.format(output_path))
if __name__ == '__main__':
tf.app.run()
将这个程序放在object_detection目录下,保存为generate_tfrecord.py文件。 cmd将目录切换到object_detection目录下,执行一下命令: python generate_tfrecord.py --csv_input=images/train_labels.csv --image_dir=images/train --output_path=train.record
刚开始报的错:
UnicodeEncodeError: ‘utf-8’ codec can’t encode character ‘\udcd5’ in position 84: surrogates not all
参考文档:
应该是斜线和反斜线的错误,于是我把相对路径全部改成了如上所示的绝对路径。继续报错:
看报错是找不到文件,但是我反复确认文件存在,路径没错!! 奔溃无数次之后,我想到我的文件名是90,但是不是需要加上后缀90.jpg 于是我在csv文件中filename那一列把文件名全部加上后缀,居然成功了!labelImg在生成xml的时候,filename这个标签不包含后缀,把我害惨了!!!!!!!! 既然它加不了,索性我在xml生成csv的时候动一下手脚:我把这一句
value = (root.find('filename').text,
改成了:
value = (root.find('filename').text+'.jpg',
重新生成csv文件之后,在运行转换成tfrecord的命令,成功了! 还有一个重要的地方是程序中定义类别那里一定要自己修改,不然也会报错。在这个离子中只有wdj一个标签。
def class_text_to_int(row_label):
一个小小的问题都可能让一个人精神奔溃。。。
万里长征走了一公里,我感觉走了一个世纪。
吐完血之后,开始训练。 5、在这里下载我们需要的模型:https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md
我选择的是第一个:ssd_mobilenet_v1_coco 这个下载挺快的,迅雷一分钟。 我将下载完的模型放在object_detection目录下ssd_mobilenet_v1_coco_2018_01_28
6、在object_detection文件夹下建立一个training文件夹,然后将上述模型对应的配置文件从C:\Users\jack\models\research\object_detection\samples\configs文件夹的 ssd_mobilenet_v1_coco.config拷贝到training文件夹下,并进行相应的修改。 文件的内容如下:
# SSD with Mobilenet v1 configuration for MSCOCO Dataset.
# Users should configure the fine_tune_checkpoint field in the train config as
# well as the label_map_path and input_path fields in the train_input_reader and
# eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that
# should be configured.
model {
ssd {
num_classes: 1 #需要修改的地方1:分类的类别数目,这里只有一个
box_coder {
faster_rcnn_box_coder {
y_scale: 10.0
x_scale: 10.0
height_scale: 5.0
width_scale: 5.0
}
}
matcher {
argmax_matcher {
matched_threshold: 0.5
unmatched_threshold: 0.5
ignore_thresholds: false
negatives_lower_than_unmatched: true
force_match_for_each_row: true
}
}
similarity_calculator {
iou_similarity {
}
}
anchor_generator {
ssd_anchor_generator {
num_layers: 6
min_scale: 0.2
max_scale: 0.95
aspect_ratios: 1.0
aspect_ratios: 2.0
aspect_ratios: 0.5
aspect_ratios: 3.0
aspect_ratios: 0.3333
}
}
image_resizer {
fixed_shape_resizer {
height: 300
width: 300
}
}
box_predictor {
convolutional_box_predictor {
min_depth: 0
max_depth: 0
num_layers_before_predictor: 0
use_dropout: false
dropout_keep_probability: 0.8
kernel_size: 1
box_code_size: 4
apply_sigmoid_to_scores: false
conv_hyperparams {
activation: RELU_6,
regularizer {
l2_regularizer {
weight: 0.00004
}
}
initializer {
truncated_normal_initializer {
stddev: 0.03
mean: 0.0
}
}
batch_norm {
train: true,
scale: true,
center: true,
decay: 0.9997,
epsilon: 0.001,
}
}
}
}
feature_extractor {
type: 'ssd_mobilenet_v1'
min_depth: 16
depth_multiplier: 1.0
conv_hyperparams {
activation: RELU_6,
regularizer {
l2_regularizer {
weight: 0.00004
}
}
initializer {
truncated_normal_initializer {
stddev: 0.03
mean: 0.0
}
}
batch_norm {
train: true,
scale: true,
center: true,
decay: 0.9997,
epsilon: 0.001,
}
}
}
loss {
classification_loss {
weighted_sigmoid {
}
}
localization_loss {
weighted_smooth_l1 {
}
}
hard_example_miner {
num_hard_examples: 3000
iou_threshold: 0.99
loss_type: CLASSIFICATION
max_negatives_per_positive: 3
min_negatives_per_image: 0
}
classification_weight: 1.0
localization_weight: 1.0
}
normalize_loss_by_num_matches: true
post_processing {
batch_non_max_suppression {
score_threshold: 1e-8
iou_threshold: 0.6
max_detections_per_class: 100
max_total_detections: 100
}
score_converter: SIGMOID
}
}
}
train_config: {
batch_size: 1 #可以修改的地方1:batch的大小,这里使用cpu版本的tensorflow可以改小一点
optimizer {
rms_prop_optimizer: {
learning_rate: {
exponential_decay_learning_rate {
initial_learning_rate: 0.004
decay_steps: 800720
decay_factor: 0.95
}
}
momentum_optimizer_value: 0.9
decay: 0.9
epsilon: 1.0
}
}
# Note: The below line limits the training process to 200K steps, which we
# empirically found to be sufficient enough to train the pets dataset. This
# effectively bypasses the learning rate schedule (the learning rate will
# never decay). Remove the below line to train indefinitely.
fine_tune_checkpoint: "C:\Users\jack\models\research\object_detection\ssd_mobilenet_v1_coco_2018_01_28\model.ckpt" #可以修改的地方:是否进行finetune,指定model的目录
from_detection_checkpoint: true #可以修改的地方:是否进行finetune
num_steps: 20000 #可以修改的地方,迭代次数,时间有限的话可以改小一点,但是效果差
data_augmentation_options {
random_horizontal_flip {
}
}
data_augmentation_options {
ssd_random_crop {
}
}
}
train_input_reader: {
tf_record_input_reader {
input_path: "C:\Users\jack\models\research\object_detection\train.record"
#需要修改的地方 训练集的地址
}
label_map_path: "C:\Users\jack\models\research\object_detection\trainning\wdj.pbtxt"
#需要修改的地方 映射关系文件的目录,后面讲如何生成
}
eval_config: {
num_examples: 8000 #需要修改的地方:测试集的样本个数
# Note: The below line limits the evaluation process to 10 evaluations.
# Remove the below line to evaluate indefinitely.
max_evals: 10
}
eval_input_reader: {
tf_record_input_reader {
input_path: "C:\Users\jack\models\research\object_detection\test.record" #需要修改的地方 测试集的地址
}
label_map_path: "C:\Users\jack\models\research\object_detection\trainning\wdj.pbtxt"
#需要修改的地方 映射关系文件的目录,后面讲如何生成
shuffle: false
num_readers: 1
}
需要修改的地方和可以修改的地方一共有几处,都已经表明了。 7、生成映射关系文件:wdj.pbtxt 随便找个txt文件,贴入:
item {
id: 1
name: 'wdj'
}
放在config文件中指定的目录中,这里是C:\Users\jack\models\research\object_detection\trainning\wdj.pbtxt
8、终于要开始训练了!但是我知道肯定没那么简单
C:\Users\jack\models\research\object_detection>python model_main.py --logtostderr --model_dir=trainning/ --pipeline_config_path=trainning/ssd_mobilenet_v1_coco.config
果然还是报错了: (1) “error:No modul named pycocotools” 因为之前的COCOAPI没有windows版本,不过在大神们的努力下github里面开源了能够在windows下执行的文件,具体的安装方式为:重新打开commond窗口,然后cd到models文件夹下,接着执行如下语句:
pip install git+https://github.com/philferriere/cocoapi.git#subdirectory=PythonAPI
然后将编译好的pycocotools文件夹拷贝到Tensorflow object detection API 的research文件夹下,就大功告成了。但是,为了使得上述编译过程可以顺利进行,电脑上面必须要有Visual C++ 14.0,没有的话建议大家下载visual c++ 2015 build tools进行安装。这一块内容是参考 (https://github.com/philferriere/cocoapi/blob/master/README.md)
但是我按照这个方法尝试了还是错。 最后我参考这篇文章: 除了上述命令之外,我采用了第二种方法: 在 https://github.com/philferriere/cocoapi 下载源码,并进行解压。 以管理员身份打开 CMD 终端,并切换到 *\cocoapi-master\PythonAPI 目录。 运行以下指令:
python setup.py build_ext install
运行以上指令时如果出现以下错误提示:
error: Microsoft Visual C++ 14.0 is required.
// 或者
error: Unable to find vcvarsall.bat
解决方法:此种安装方法需要使用 Microsoft Visual C++ 14.0 对 COCO 源码进行编译。如果本地不支持 Microsoft Visual C++ 14.0 或者版本低于 14.0,可以通过安装 Microsoft Visual Studio 2015 及以上版本。下载地址: https://visualstudio.microsoft.com/zh-hans/visual-cpp-build-tools/ 只要选择c++的那个选项就行了,其他的不用选。 用这种方法终于成功了 --------------------- 。 9、重新开始训练:
C:\Users\jack\models\research\object_detection>python model_main.py --logtostderr --model_dir=training/ --pipeline_config_path=training/ssd_mobilenet_v1_coco.config
发现说文件打不开。。。仔细检查,发现我的文件夹名字是trainning…
C:\Users\jack\models\research\object_detection>python model_main.py --logtostderr --model_dir=trainning/ --pipeline_config_path=trainning/ssd_mobilenet_v1_coco.config
真是细致活,来不得半点马虎。
然鹅,还是报错。。。
百度了一下,感觉不是普遍的错。仔细读了一下,感觉是路径的名字里有错,
参考这篇文章:
于是我把config文件中的路径都改成双斜线:例如
C:\\Users\\jack\\models\\research\\object_detection\\train.record
哈哈,真的不报这个错了,但是还是继续报错,我根本就不意外。
InvalidArgumentError : image_size must contain 3 elements[4]
查了一下,是因为我的图片不是RGB的。终于是一个普遍的问题了。。。 不是彩色的就是RGB三通道的吗,我吐了!!! 在网上找了个程序,把所有图片都扫描一遍,还好,只有两个图片是错的。
from PIL import Image
import os
path = 'C:\\Users\\jack\\models\\research\\object_detection\\images\\test\\' #图片目录
for file in os.listdir(path):
extension = file.split('.')[-1]
if extension == 'jpg':
fileLoc = path+file
img = Image.open(fileLoc)
if img.mode != 'RGB':
print(file+', '+img.mode)
把不是rgb的图片替换掉之后,或者转换成rgb格式的图片之后,从新生成csv、TFRecord文件,再次输入命令:
C:\Users\jack\models\research\object_detection>python model_main.py --logtostderr --model_dir=trainning/ --pipeline_config_path=trainning/ssd_mobilenet_v1_coco.config
跳出来好几个warning,然后好像没有报错,终于泡起来了!GOOD JOB~
还没开心多久,又报错了:
好在这个错误在我参考文章里提到了:
TypeError: can’t pickle dict_values objects”
解决方案为:我们进入到D:\tensorflow1\models\research\object_detection下,然后打开model_lib.py文件,接着找到下图中所标出的位置,最后将category_index.values()改为list(category_index.values())即可。
终于开始泡起来了,速度有点慢,cpu版本的破电脑。
10、此时,可以使用tensorboard查看运行的情况。另开一个cmd窗口,更改路径到object_detection:
tensorboard --logdir=trainning
按照提示在浏览器中输入地址:
http://jack-PC:6006 跳出tensorboard界面了:
我咋看到我的精度是在下降的呢, 瀑布。不过我是以跑通程序为主,后面再说精度把。
在模型跑的过程中可以任意时间中断,查看目前模型的效果如何。
在trainning目录下,发现多了很多文件,是checkpoint点生成的文件。
11、模型导出:
在object_detetion目录下,有个export_inference_graph.py文件。新建一个文件夹命名为wdj_detection用于存放导出的模型。
在cmd命令框中输入如下命令:
models\research\object_detection>python export_inference_graph.py --input_type image_tensor --pipeline_config_path trainning/ssd_mobilenet_v1_coco.config --trained_checkpoint_prefix trainning/model.ckpt-3582 --output_directory wdj_detection
其中 training/ssd_mobilenet_v1_coco.config是我们训练模型的配置文件 training/model.ckpt-3582是他trainning目录下挑选的checkpoint点文件。 wdj_detection是模型导出的文件夹名称。
这一次很顺利地导出了model,没有报错,撒花~~~
12、查看模型的泛化能力: 模型下载完毕,那个这个模型处理新的图片效果如何? 在test_images目录下载几张图片,可以包含武大靖的。 jupyter打开object_detection目录下的object_detection_tutorial.ipynb文件,在此程序下进行修改:
MODEL_NAME = 'ssd_mobilenet_v1_coco_2017_11_17'
此处修改成自己的model名称:
MODEL_NAME = 'wdj_detection'
下面两行因为不用下载模型了,可以删除:
MODEL_FILE = MODEL_NAME + '.tar.gz'
DOWNLOAD_BASE = 'http://download.tensorflow.org/models/object_detection/'
这里不用改: PATH_TO_FROZEN_GRAPH = MODEL_NAME + ‘/frozen_inference_graph.pb’
# List of the strings that is used to add correct label for each box.
这里改成自己的pbtxt文件:
PATH_TO_LABELS = os.path.join('trainning', 'wdj.pbtxt')
download程序块全部删除:
opener = urllib.request.URLopener()
opener.retrieve(DOWNLOAD_BASE + MODEL_FILE, MODEL_FILE)
tar_file = tarfile.open(MODEL_FILE)
for file in tar_file.getmembers():
file_name = os.path.basename(file.name)
if 'frozen_inference_graph.pb' in file_name:
tar_file.extract(file, os.getcwd())
检测程序块需要修改:
PATH_TO_TEST_IMAGES_DIR = 'test_images'
#TEST_IMAGE_PATHS = [ os.path.join(PATH_TO_TEST_IMAGES_DIR, 'image{}.jpg'.format(i)) for i in range(1, 3) ]
TEST_IMAGE_PATHS =os.listdir('C:\\Users\\jack\\models\\research\\object_detection\\test_images')
IMAGE_SIZE = (12, 8)
最后,在打开图片的时候修改一下:
for image_path in TEST_IMAGE_PATHS:
print(image_path)
image = Image.open('C:\\Users\\jack\\models\\research\\object_detection\\test_images\\'+image_path)
然后点击最上面Cell,run all运行全部程序。至此,整个过程全部结束!
由于训练时间不长,而且样本有点少,所以效果很差。后面希望改进吧。总算是圆满了。
后续:为了提高图片的识别成功率,我做了优化: 1、增加样本数,从50提高到100个 2、在配置文件中使用了 fine tune,在之前这两句是被注释掉的,现在去掉注释
fine_tune_checkpoint: "C:\Users\jack\models\research\object_detection\ssd_mobilenet_v1_coco_2018_01_28\model.ckpt"
from_detection_checkpoint: true
重新训练 20000次,再次之前需要把trainning中的文件删除。
结果如下:有了明显的提高
但是有几张图片还是没识别出来,感觉训练集还是不够。因为比较费时间,所以调试到此位置啦!成就感满满的