@TOC
最近参加2020年(第13届)中国大学生计算机设计大赛,选择了人工智能挑战赛的赛题二,基于 CT 影像的结直肠息肉检测。
赛题要求是,设计算法,判断图像中是否存在息肉,并实现息肉的准确检测,利用矩形方框将所检测出的息肉包含在检测框内。
乍一看,是一道标准的目标检测题,再看看官方给出的数据集标注,是YOLO格式的,那就直接用YOLOv3进行训练。最后再根据题目的需求,增加了一些功能。
我们这个版本,是在c语言的YOLOv3框架上进行修改的;网上还有很多基于pytorch、tensorflow构建的YOLOv3,以后有时间也写一下。
YOLOv3训练自己的数据集
标记数据
如果是训练自己的数据集,即还未对数据集进行标注,那么推荐使用LabelImg工具进行标注,可以得到适用于PascalVOC(xml)或者YOLO(txt)格式的标注。
因为官方给出的数据集已经是YOLO格式的标注,我们可以直接用。当然,考虑到后面某个功能使用了PascalVOC格式的标注,这里给出一个从YOLO(txt)格式转换成PascalVOC(xml)格式的代码txt2xml.py。
此时,原始图像放在JPEGImages目录下,YOLO(txt)格式标注放在labels目录下,PascalVOC(xml)格式标注放在Annotations目录下。
制作 yolo 需要的txt文件
这一步需要制作四个文件:train.txt、val.txt、object_train.txt、object_val.txt。
train.txt、val.txt这两个文件保存了用于训练、验证图片的名字,每行一个名字(不带后缀.jpg)。这里参考了一个别人的代码img2train.py。
object_train.txt、object_val.txt这两个文件保存了用于训练、验证图片的绝对路径,每行一个路径。这里参考了一个别人的代码voc_label.py。这个代码不仅可以划分文件,还可以将PascalVOC(xml)格式标注转换成YOLO(txt)格式标注。
制作 yolo 需要的配置文件
这一步需要制作三个文件:ct.names、ct.data、ct.cfg(ct是自己定义的,因为我用的是ct数据集,所以有此定义)。
ct.names包含数据集中的种类,每行一个。注意,这个顺序代表了之后预测时的种类顺序。
ct.data包含以下几个内容
classes= 1 # 类别数
train = data/object_train.txt # obj_train.txt 路径
valid = data/object_val.txt # obj_val.txt 路径
names = data/ct.names # ct.names 路径
backup = backup/ # 建一个 backup 文件夹用于存放 weights 结果
注意,这里放的是object_train.txt和object_val.txt,是写有绝对路径的txt文本。
ct.cfg包含的是与YOLOv3训练或测试相关的配置,有几个地方需要注意
1.注意文档开头training和testing的切换;
2.直接搜索‘classes’,修改三处对应位置:
[convolutional]
filters = 3*(classes + 5) #修改filters数量
[yolo]
classes=5 #修改类别数;
3.修改max_batches = 2000 * classes
训练
首先,下载预训练权重。
1 | wget https://pjreddie.com/media/files/darknet53.conv.74 |
然后,执行训练命令。
1 | ./darknet detector train ./cfg/ct.data ./cfg/ct.cfg darknet53.conv.74 |
增加功能
在图像上添加置信度
YOLOv3单张图像检测的结果,默认设定只包含目标类别。我们可以通过修改src/image.c文件draw_detections()函数,添加目标置信度。修改片段如下:
1 | for(i = 0; i < num; ++i) |
单张图像检测命令
1 | ./darknet detector test ./cfg/ct.data ./cfg/ct_test.cfg ct_final.weights test.jpg |
批量检测图像
首先,修改example/detector.c文件,在开头添加一个获取图片名字的函数:
1 | char *GetFilename(char *p) |
在这里,为了对不同长度的文件名能够兼容处理,设置name数组长度为50,可以根据需要修改。
然后,替换examples/detector.c 中的test_detector函数:
1 | void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh, char *outfile, int fullscreen) |
最后,在命令行输入make,更新文件。
批量检测命令,输入的路径为那些图片路径的txt。
1 | ./darknet detector test ./cfg/ct.data ./cfg/ct_test.cfg ct_final.weights |
保存批量检测结果为txt文件
YOLOv3有自带的命令进行这个操作。
1 | ./darknet detector valid ./cfg/ct.data ./cfg/ct_test.cfg ct_final.weights results |
计算recall
修改examples/detector.c的validate_detector_recall函数。
首先,将validate_detector_recall函数定义和调用修改如下:
void validate_detector_recall(char *datacfg, char *cfgfile, char *weightfile)
validate_detector_recall(datacfg, cfg, weights);
然后,将如下内容:
list *plist = get_paths(“data/coco_val_5k.list”);
char **paths = (char **)list_to_array(plist);
修改成
list *options = read_data_cfg(datacfg);
char *valid_images = option_find_str(options, “valid”, “data/train.list”);
list *plist = get_paths(valid_images);
char **paths = (char **)list_to_array(plist);
最后,记得make。
使用YOLOv3的命令调用方式。
1 | ./darknet detector recall ./cfg/ct.data ./cfg/ct_test.cfg ct_final.weights |
计算mAP
需要用到PascalVOC(xml)格式的注释,可以用文章开始提到的代码txt2xml.py进行转换。
可以先借助py-faster-rcnn下的voc_eval.py计算出单个类别的AP,然后求平均得到mAP。
新建一个all_map.py文件用于计算mAP,这边提供了别的博主的一个例子。
如果需要重复计算mAP,需要删除生成的annots.pkl。
参考
YOLOv3:训练自己的数据(附优化与问题总结)
How to train YOLOv3 model
YOLO-V3实战(darknet)