大家好,我是小P,今天在此给大家分享一下基于DarknetAB版本的YOLOV3如何生成和显示中文标签的方法,效果如下图所示,希望大家支持和喜欢。此外,对“目标检测/模型压缩/语义分割”感兴趣的小伙伴,欢迎加入QQ群 813221712 讨论交流,进群请看群公告!(可以点击如下连接直接加入!)
点击链接加入群聊【Object Detection】:https://jq.qq.com/?_wv=1027&k=5kXCXF8
首先,本次教程所使用的模型为DarknetAB版本,其相对于官方原版作出了比较多的修改,最大的便利在于能方便地在Win10操作系统上运行,有兴趣地小伙伴可以研究研究,下载地址为:
https://github.com/AlexeyAB/darknet 在此,强烈推荐大家使用AB版本
在此之前,先普及一点源码画标签的知识。假如,现在要往检测图片的某一位置绘制标签Person,则程序操作流程为:
如上图所示,源码的 darknet-master/data/labels 路径(有两个labels文件夹,Win10环境使用图中所示的labels文件夹,Ubuntu使用 darknet-master/data/labels 文件夹)下有对应大小不同的字母图片,其命名规则为:ASCII_大小 ,其中ASCII表示字母对应的ASCII值,这样处理是为了计算方便,大小是根据检测框大小计算得到的,假定现在Person标签的大小为5号,则程序会将Person标签分解为6个字母:‘P’ ‘e’ ‘r’ ‘s’ ‘o’ ‘n’ ,首先找到‘P’字母的5号图片,然后找到‘e’字母的5号图片将其拼接在’P’字母的右边…其它字母类似,如下图所示:
?是不是很艰辛!明白了画标签的原理,要改就简单多了。现在,来生成中文标签所需的图片,这儿与原代码不同的是中文标签基于词语生成图片,如“行人”,则不拆分成“行”和“人”,主要是因为字母可以根据ASCII简便计算,但中文不行,当然,你也可以拆开来做,只是稍微复杂点,但这儿为了简单起见,就不拆开。生成标签的图片代码如下:
import os
import string
import pipes
font = 'futura-normal'
fileName=open("my.names",'r')
l=[label.lstrip().rstrip() for label in fileName.readlines()]
for i in range(8):
index=0
for word in l:
os.system(
"convert -fill black -background white -bordercolor white -border 4 -font /usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf -pointsize %d label:\"%s\" \"%d_%s.png\"" % ((i+1)*12, word, i,index))
index+=1
警告:本段代码只能在Ubuntu操作系统下运行,没有Ubuntu的小伙伴可以让别人替你生成好后拷贝过来
注意:你只需要将此段代码粘贴进任何一个.py文件,放到labels文件夹下面,在该文件夹下面运行即可。此外:你需要修改代码中的“my.names”为你的标签文件,运行的结果是:在labels文件夹下面生成了一系列标签图片,如下图所示(请确认你的结果正确):
此处:第一个数字表示大小,分为8个等级,0-7表示,第二个数字表示标签编号,和**.names文件对应。
查找 get_label_v3函数的声明处,在其下面添加函数 get_label_v3_my ,采用添加不采用修改的方式主要是避免破坏源码,其它地方的修改类似,函数代码为:
image get_label_v3_my(image **characters, char *labelindex, int size)
{
size = size / 10;
if (size > 7) size = 7;
image label = make_empty_image(0, 0, 0);
int class,i=0,nlabels=1;
int len=strlen(labelindex);
for(i=0;i
添加代码过后,你的文件应该如下图所示,其中具体意义这里不进行讲解,感兴趣的小伙伴自行研究和修改
找到 image.c 中的 draw_detections_v3函数。然后找到 if ( alphabet ) { … } 代码块,将其改为:
if (alphabet) {
char labelindex[100]= { 0 };
char class[10]={0};
sprintf(class,"%d",selected_detections[i].best_class);
strcat(labelindex, class);
int j;
for (j = 0; j < classes; ++j) {
if (selected_detections[i].det.prob[j] > thresh && j != selected_detections[i].best_class) {
strcat(labelindex,", ");
sprintf(class,"%d",selected_detections[i].best_class);
strcat(labelindex, class);
}
}
image label = get_label_v3_my(alphabet, labelindex, (im.h*.03));
draw_label(im, top + width, left, label, rgb);
free_image(label);
}
当然,这儿你也可以新建函数,然后改函数调用,从而避免破坏源码。此处修改是为了调用上面定义的函数
image **load_labels(int classes)
{
int i, j;
const int nsize = 8;
image** alphabets = (image**)calloc(nsize, sizeof(image*));
for(j = 0; j < nsize; ++j){
alphabets[j] = (image*)calloc(classes, sizeof(image));
for(i = 0; i < classes; ++i){
char buff[256];
sprintf(buff, "data/labels/%d_%d.png", j, i);
alphabets[j][i] = load_image_color(buff, 0, 0);
}
}
return alphabets;
}
本函数的作用为将前面我们生成的标签图片加载进内存,保存在二维结构体数组中
image **load_labels(int);
此行代码的作用为声明方法。
找到 test_detector 函数的定义,将 image **alphabet =load_alphabet(); 修改为:
image **alphabet = load_labels(names_size);
到此,教程完毕,其它诸如调节字体大小和框粗细的问题都可以在image.c函数中进行修改。本人对Darknet框架作了精细的研究,并进行了大量的注释,有感兴趣的小伙伴可以一起学习。