yolov3模型识别不出训练图片_YOLO_V3从训练到部署

本文介绍了如何使用darknet框架进行目标检测,包括数据标注、数据增强、数据处理、模型训练、以及使用OpenCV部署YOLO_v3模型。详细讲解了数据标注工具LabelImg的使用,提供了一个MATLAB脚本实现数据增强,数据转换为darknet格式,并给出了训练模型和在OpenCV中应用的步骤。
摘要由CSDN通过智能技术生成

darknet是一个C语言实现的深度学习框架,几乎不依赖任何库,安装编译都很方便,训练好的模型可以直接在opencv上部署,堪称业界良心。这篇博客主要包含目标检测数据标注和预处理、yolo_v3代码编译、模型训练、在opencv上部署,都是简要的笔记。

〇、参考链接

这些博客写的比较详细,博主也是参照这些博客一步步走下来的,在这些博客基础上总结扩充的:

一、数据标注

使用labelimg,有python版本,也有打包好的二进制文件(window/linux)直接用:下载地址    本站暂存的windows版本

我们使用Pascal标注格式,将标注文件xml和图像存放在一起,放到同一目录下。标注的时候可以在软件里设置自动保存(View->AutoSaving),就不需要一直弹窗确认了。下面是软件的快捷键,可以提高标注效率。

Ctrl + u

Load all of the images from a directory

Ctrl + r

Change the default annotation target dir

Ctrl + s

Save

Ctrl + d

Copy the current label and rect box

Space

Flag the current image as verified

w

Create a rect box

d

Next image

a

Previous image

del

Delete the selected rect box

Ctrl++

Zoom in

Ctrl–

Zoom out

↑→↓←

Keyboard arrows to move selected rect box

二、数据增强

标注好图像之后,为了取得好的训练效果,常常需要对数据集进行扩充,在变化图像的时候谁带也把标注文件一起处理好,这样同源的图像就不用重复标注了。

有一些Python的脚本比较好用,博主根据自己需要写了一个matlab的(这里没哟该标注框的位置,如果你用的变换有目标位置发生改变的,要换算一下矩形框位置进行修改)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

clear

FOLDER='D:/data/drone/';

EXT='.jpg';

output_path='1';

mkdir(output_path);

files=dir([FOLDER'*'EXT]);

%calculatetransformposition

fori=1:length(files)

file=files(i);

img=imread(fullfile(file.folder,file.name));

% read xml

xmlfile=fullfile(file.folder,[file.name(1:end-4)'.xml']);

fp=fopen(xmlfile,'r');

A=fscanf(fp,'%c');

fclose(fp);

%output_path = file.folder;

% transform image

parfortrans=1:10

img_new_name=fullfile(output_path,...

[file.name(1:end-4)'.'sprintf('%02d',trans)'.JPG']);

xml_new_name=fullfile(output_path,...

[file.name(1:end-4)'.'sprintf('%02d',trans)'.xml']);

% transform image

img_new=data_enhance(img,trans);

imwrite(img_new,img_new_name);

% trnasform xml

A_new=strrep(A,file.name,...

[file.name(1:end-4)'.'sprintf('%02d',trans)'.JPG']);

fp=fopen(xml_new_name,'w');

fprintf(fp,'%s',A_new);

fclose(fp);

end

end

里面用到的图像变换函数(后面几个变换会改变标注框的位置,如果要用后面的变换,上面那个文件中还需要修改矩形的位置):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

function[img]=data_enhance(img_src,type)

img=img_src;

iftype==0

img=img_src;

elseiftype==1

img=imsharpen(img_src);

elseiftype==2

img=imnoise(img_src,'gaussian');

elseiftype==3

img=imnoise(img_src,'poisson');

elseiftype==4

img=img_src*0.8;

elseiftype==5

img=img_src*0.9;

elseiftype==6

img=img_src*1.1;

elseiftype==7

img=img_src*1.2;

elseiftype==8

img=imguidedfilter(img_src);

elseiftype==9

img=imgaussfilt(img_src,2);

elseiftype==10

img=rgb2gray(img_src);

img=cat(3,img,img,img);

elseiftype==31

img=rot90(img_src,180);

elseiftype==32

img=fliplr(img_src);

elseiftype==33

img=fliplr(img_src);

img=rot90(img,180);

elseiftype==41

img=imrotate(img_src,2);

elseiftype==42

img=imratate(img_src,3);

elseiftype==43

img=imresize(img_src,0.8);

elseiftype==44

img=imresize(img_src,1.2);

end

img=uint8(img);

end

三、数据处理

我们已经有了训练需要的数据,但是它与darknet规定的格式不同,darknet并不认识它,这一步就是我们在训练之前需要把数据和标注的格式处理成darknet需要的格式。

先新建一个VOCdevkit目录,在其下新建一个VOC2007目录,再在VOC2007下新建Annotations、ImageSets、JPEGImages三个目录,再在ImageSets下面新建Main目录

最终目录结构如下(先不用管里面的txt文件,txt文件都是脚本生成的):

然后我们把标注好的图像放到JPEGImages目录下,然后把标注好的xml文件放到Annotations目录下,把test.py脚本放到VOC2007目录下,把voc_label.py脚本。

test.py脚本的作用是生成训练检测文件列表。test.py最开头的参数可以调整用于训练和验证的图像比例。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

importos

importrandom

trainval_percent=0.1

train_percent=0.9

xmlfilepath='Annotations'

txtsavepath='ImageSets\Main'

total_xml=os.listdir(xmlfilepath)

num=len(total_xml)

list=range(num)

tv=int(num*trainval_percent)

tr=int(tv*train_percent)

trainval=random.sample(list,tv)

train=random.sample(trainval,tr)

ftrainval=open('ImageSets/Main/trainval.txt','w')

ftest=open('ImageSets/Main/test.txt','w')

ftrain=open('ImageSets/Main/train.txt','w')

fval=open('ImageSets/Main/val.txt','w')

foriinlist:

name=total_xml[i][:-4]+'\n'

ifiintrainval:

ftrainval.write(name)

ifiintrain:

ftest.write(name)

else:

fval.write(name)

else:

ftrain.write(name)

ftrainval.close()

ftrain.close()

fval.close()

ftest.close()

voc_label.py脚本的作用是按照上一步生成的文件列表,将标注也转化成darknet认识的txt格式,也就是目录最外层的txt,这几个txt是真正最后传递给darknet的。该脚本使用有两点要注意:

下面的脚本里默认图片是jpg格式,如果你的图片是其它格式,那么需要在脚本中修改

修改目录 Sets 和类别 classes,脚本下载链接:voc_label.py

1

wgethttps://pjreddie.com/media/files/voc_label.py

首先运行test.py脚本,再运行voc_label.py脚本,看到对应txt文件生成,就大功告成了

四、darknet代码编译

1. 下载代码

1

gitclonehttps://github.com/pjreddie/darknet

2. 编译代码

如果是linux,直接修改MakeFile,是使用GPU还是CPU选择下就可以了,纯c写的,没啥依赖,有装opencv的话添加下路径,没有opencv也可以运行。

如果是windows,需要读github首页的readme,来确认自己的环境,cuda版本不一样的改下配置文件,该装cudnn的装cudnn,该重新cmake的重新cmake,该把cuda.procs放到指定位置就按照readme给的环境来配置就好,一般多折腾会儿都没什么问题。

3. 下载模型

也是在官网已经有了预训练好的模型,这里再贴两个模型链接,都提前下下来:

待会儿测试的时候会跑一个识别,用到的预训练模型:

1

wgethttps://pjreddie.com/media/files/yolov3.weights

预训练的时候可能会用到darknet53网络的权重:

1

wgethttps://pjreddie.com/media/files/darknet53.conv.74

这两个文件都放到和darknet.exe同一个目录下。

4. 运行测试demo

测试下效果:

1

darknetdetectcfg/yolov3.cfgyolov3.weightsdata/dog.jpg

五、训练模型

1.修改cfg/voc.data

这里指定了训练时使用的数据,我们选择之前脚本生成的txt就好

2.修改data/voc.names和coco.names

修改成自己标注时候的类别就可以

3.修改参数文件cfg/yolov3-voc.cfg

ctrl+f搜 yolo, 总共会搜出3个含有yolo的地方。

每个地方都必须要改2处, filters:3*(5+len(classes));因为输出是矩形4个location+1个置信度+1个类别

其中:classes: len(classes) = 1,这里以单个类dog为例

filters = 18

classes = 1

可修改:random = 1:原来是1,显存小改为0。(是否要多尺度输出。)

参数文件开头的地方可以选训练的batchsize,要注意,训练时要改batchsize,测试时要强制成1,。博主使用的显卡是8G现存,每次只能算4张或者5张图,所以选了64/16=4张来算,你需要根据自己显卡的情况进行配置,免得显存爆掉。

4.训练模型

我们在先前下载的darknet53权重基础上微调

1

darknetdetectortraincfg/voc.datacfg/yolov3-voc.cfgdarknet53.conv.74

博主使用大概700张图片,训练到200次的时候结果已经可以看了,一晚上大概跑了3000轮,默认没100次迭代后模型备份到backup目录下

六、应用部署

opencv3.4.2之后dnn模块就支持yolov3了,因为ipp和openmp并行,opencv对cpu的利用效率很高,测试过opencv的实现比darknet本来的实现要快九倍,博主也测了下,也差不多是这么快,所以把模型通过opencv部署可以有比较快的运行速度,当然,如果你需要更快的速度,能配置一块儿比较好的显卡就另当别论了。

OS

Framework

CPU/GPU

Time(ms)/Frame

Linux 16.04

Darknet

12x Intel Core i7-6850K CPU @ 3.60GHz

9370

Linux 16.04

Darknet + OpenMP

12x Intel Core i7-6850K CPU @ 3.60GHz

1942

Linux 16.04

OpenCV [CPU]

12x Intel Core i7-6850K CPU @ 3.60GHz

220

Linux 16.04

Darknet

NVIDIA GeForce 1080 Ti GPU

23

macOS

DarkNet

2.5 GHz Intel Core i7 CPU

7260

macOS

OpenCV [CPU]

2.5 GHz Intel Core i7 CPU

400

直接上博主的代码吧,只有一个cpp文件,也可以在github直接下载下来用:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

#include

floatconfThreshold=0.5f;

floatnmsThreshold=0.4f;

intinpWidth=416;

intinpHeight=416;

std::vector<:string>classes;

std::vector<:string>getOutputsNames(constcv::dnn::Net&net){

staticstd::vector<:string>names;

if(names.empty()){

std::vectoroutLayers=

net.getUnconnectedOutLayers();

std::vector<:string>layersNames=

net.getLayerNames();

names.resize(outLayers.size());

for(size_ti=0;i

names[i]=layersNames[outLayers[i]-1];

}

returnnames;

}

// Draw the predicted bounding box

voiddrawPred(intclassId,floatconf,intleft,inttop,intright,intbottom,cv::Mat&frame){

//Draw a rectangle displaying the bounding box

cv::rectangle(frame,cv::Point(left,top),cv::Point(right,bottom),cv::Scalar(0,0,255));

//Get the label for the class name and its confidence

std::stringlabel=cv::format("%.2f",conf);

if(!classes.empty()){

CV_Assert(classId

label=classes[classId]+":"+label;

}

//Display the label at the top of the bounding box

intbaseLine;

cv::SizelabelSize=cv::getTextSize(label,cv::FONT_HERSHEY_SIMPLEX,0.5,1,&baseLine);

top=std::max(top,labelSize.height);

putText(frame,label,cv::Point(left,top),cv::FONT_HERSHEY_SIMPLEX,0.5,cv::Scalar(255,255,255));

}

voidpostprocess(cv::Mat&frame,conststd::vector<:mat>&outs){

std::vectorclassIds;

std::vectorconfidences;

std::vector<:rect>boxes;

for(size_ti=0;i

float*data=(float*)outs[i].data;

for(intj=0;j

cv::Matscores=outs[i].row(j).colRange(5,outs[i].cols);

cv::PointclassIdPoint;

doubleconfidence;

cv::minMaxLoc(scores,0,&confidence,0,&classIdPoint);

if(confidence>confThreshold){

intcenterX=(int)(data[0]*frame.cols);

intcenterY=(int)(data[1]*frame.rows);

intwidth=(int)(data[2]*frame.cols);

intheight=(int)(data[3]*frame.rows);

intleft=centerX-width/2;

inttop=centerY-height/2;

classIds.push_back(classIdPoint.x);

confidences.push_back((float)confidence);

boxes.push_back(cv::Rect(left,top,width,height));

}

}

}

// Perform non maximum suppression to eliminate redundant overlapping boxes with

// lower confidences

std::vectorindices;

cv::dnn::NMSBoxes(boxes,confidences,confThreshold,nmsThreshold,indices);

for(size_ti=0;i

intidx=indices[i];

cv::Rectbox=boxes[idx];

drawPred(classIds[idx],confidences[idx],box.x,box.y,

box.x+box.width,box.y+box.height,frame);

}

return;

}

intdetect(cv::Mat&image){

// Load names of classes

std::stringclassesFile="coco.names";

std::stringline;

std::ifstreamifs(classesFile.c_str());

while(std::getline(ifs,line))

classes.push_back(line);

std::stringmodelConfiguration="yolov3.cfg";

std::stringmodelWeights="yolov3.weights";

// Load the network

cv::dnn::Netnet=cv::dnn::readNetFromDarknet(

modelConfiguration,modelWeights);

net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);

net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);

if(image.empty()){

std::cout<

return1;

}

cv::Matblob;

cv::dnn::blobFromImage(image,blob,1/255.0,

cv::Size(inpWidth,inpHeight),cv::Scalar(0,0,0),true,false);

net.setInput(blob);

std::vector<:mat>outs;

net.forward(outs,getOutputsNames(net));

// Remove the bounding boxes with low confidence

postprocess(image,outs);

// Profile

std::vectorlayersTimes;

doublefreq=cv::getTickFrequency()/1000;

doublet=net.getPerfProfile(layersTimes)/freq;

std::stringlabel=cv::format("Inference time for a frame: %.2f ms",t);

cv::putText(image,label,cv::Point(0,15),

cv::FONT_HERSHEY_SIMPLEX,0.5,cv::Scalar(0,0,255));

cv::MatdetectedFrame;

image.convertTo(detectedFrame,CV_8U);

cv::imwrite("predict.png",detectedFrame);

return0;

}

intmain(intargc,char*argv[]){

cv::Matimage=cv::imread("D:/workspace/cvyolo/data/dark.jpg");

cv::imshow("image",image);

detect(image);

cv::waitKey();

return0;

}

OK,希望能有帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值