yolov4-tiny从安装到训练再到python调用接口

发布时间:2022-09-12 16:00

(一)安装

在GitHub网址https://github.com/AlexeyAB/darknet下载最新版的darknetAB源码
解压后会生成名为darknet-master的文件夹
将解压的文件放到darknet的空文件夹下
下载 yolov4-tiny模型权重:yolov4-tiny模型权重文件,将下载好的权重放到darknet-master目录下
打开darknet-master目录下makefile文件,修改参数以适应于自己的计算机,GPU、CUDNN、OPENCV是主要修改的,其他参数根据自身需求修改

GPU=0     	# 使用GPU
CUDNN=0		# 使用GPU
CUDNN_HALF=0		# 混合精度训练,用于加速
OPENCV=1		# 使用opencv
AVX=0
OPENMP=0
LIBSO=1		# 生成libdarknet.so,便于python调用darknet模型
ZED_CAMERA=0
ZED_CAMERA_v2_8=0

注意: 如果要用python调用darknet模型接口的话 一定把 LIBSO 设为1

上述步骤完成后
在darknet目录下执行make进行编译

make

编译完成后
选择如下指令进行测试图片,视频以及摄像头实时检测

./darknet detector test cfg/coco.data cfg/yolov4-tiny.cfg yolov4-tiny.weights data/dog.jpg	# 图片测试
./darknet detector demo cfg/coco.data cfg/yolov4-tiny.cfg yolov4-tiny.weights -ext_output test.mp4		# 视频测试
./darknet detector demo cfg/coco.data cfg/yolov4-tiny.cfg yolov4-tiny.weights -c 0		# 摄像头测试

(二)训练自己的权重文件
用Yolov4-tiny来进行训练,则需要下载Yolov4-tiny的预训练权重:yolov4-tiny预训练权重
并放在darknet-master目录下

之后建立yolov4-tiny训练所需的目录结构

---darknet
  ---darknet-master
	---VOCdevkit
		---VOC2007
			---Annotations	# 存放xml文件
			---ImageSets
				---Main		# 存放训练集和测试集图片索引号的txt文件
				  ---test.txt # 存放测试集图片的路径
				  ---train.txt # 存放训练集图片的路径
			---JPEGImages	# 存放图片文件
			---labels
			  ---***.txt   #存放训练集和测试集的标注信息(如:0 0.002221 0.002221 0.002221 0.002221),***与照片名字相同
			  ---***.txt   #存放训练集和测试集的标注信息(如:0 0.002221 0.002221 0.002221 0.002221),***与照片名字相同
			  ---  #n多个,与图片数量相同

之后,修改训练所需文件(有**.names, **.cfg, **.data三个文件)
修改 **.names
在 darknet-master/data/目录下 建立 **.names
参考coco.names,更改自己的.names文件

# 存放自己的类别,这里的类别是“1 2 3 4 5 6 7 8 ”
1 
2
3
4
5
6
7
8

修改 **.names
在 darknet-master/cfg/目录下 建立 **.data
参考coco.data,更改自己的.names文件

classes= 8
train  = /darknet/VOCdevkit/VOC2020/ImageSets/Main/number_train.txt
valid  = /darknet/VOCdevkit/VOC2020/ImageSets/Main/number_text.txt
names = /darknet/darknet-master/data/**.names
backup = /darknet/darknet-master/backup/ #训练时生成的权重文件

修改 **.cfg
在 darknet-master/cfg/目录下 建立 **.cfg
参考yolov4-tiny.cfg,更改自己的.cfg文件

1)yolov4-tiny.cfg文件第1-7行

[net]
#Testing
#batch=1
#subdivisions=1
#Training
batch=64
subdivisions=16
# 注意:由于是进行训练,这里不需要修改。训练过程中可能出现
# CUDA out of memory的提示,可将这里的subdivisions增
# 大,如32或64,但是数值越大耗时越长,因此需要权衡一下;

(2)yolov4-tiny.cfg文件第8-9行

width=224
height=224
# 可以写别的大小,比例是1:1 
# 但是这里的数值必须是32的倍数,
# 这里也是数值越大耗时越长;

(3)第20行的参数max_batches

max_batches = 16000  #max_batches = classes*2000 也有写 max_batches = classes*1000,这里写的是8*2000
policy=steps
steps=12800,14400   # steps=max_batches*0.8, max_batches*0.9
scales=.1,.1
# 更改max_batches, steps两处

(4)继续修改yolov4-tiny.cfg文件,按Ctrl+F键,搜索“classes”,将classes=80改为classes=2,并将classes前面最近的filters修改为39,计算由来(classes+5)*3=39;

注意:把所有的都改了

[convolutional]
size=1
stride=1
pad=1
filters=39  #(classes+5)*3=21 这里是(8+5)*3 = 39
activation=linear

[yolo]
mask = 3,4,5
anchors = 10,14,  23,27,  37,58,  81,82,  135,169,  344,319
classes=8  # 自己的来类别数量
num=6
# 更改filters, classes两处

注意:把所有的都改了

最后:打开终端,切换到darknet目录下
使用

./darknet detector train cfg/***.data cfg/***.cfg yolov4-tiny.conv.29 -cpu 
# 或者 -gpu 训练模型

回车 开始训练

训练的过程中,生成的权重文件会存放在/darknet/backup文件夹下

(三)python调用darknet 接口
有两种方法
法一
调用之前,必须,在make编译前把将makefile文件里的LIBSO设为1
如果没有设置

make clean #清除编译
make       #再编译
# 也可以直接 make,为了保险,make clean一下 ,再make

之后在darknet目录下,建立darknet_me.py

import os
import cv2
import numpy as np
import darknet
import time
 
class Detect:
    def __init__(self, metaPath, configPath, weightPath, gpu_id=2, batch=1):
        '''
        :param metaPath:   ***.data 存储各种参数
        :param configPath: ***.cfg  网络结构文件
        :param weightPath: ***.weights yolo的权重
        :param batch:      ########此类只支持batch=1############
        '''
        assert batch == 1, "batch必须为1"
        # 设置gpu_id
        darknet.set_gpu(0)
        # 网络
        network, class_names, class_colors = darknet.load_network(
            configPath,
            metaPath,
            weightPath,
            batch_size=batch
        )
        self.network = network
        self.class_names = class_names
        self.class_colors = class_colors
 
    def bbox2point(self, bbox):
        x, y, w, h = bbox
        xmin = x - (w / 2)
        xmax = x + (w / 2)
        ymin = y - (h / 2)
        ymax = y + (h / 2)
        return (xmin, ymin, xmax, ymax)
 
    def point2bbox(self, point):
        x1, y1, x2, y2 = point
        x = (x1 + x2) / 2
        y = (y1 + y2) / 2
        w = (x2 - x1)
        h = (y2 - y1)
        return (x, y, w, h)
 
    def image_detection(self, image_bgr, network, class_names, class_colors, thresh=0.5):
        # 判断输入图像是否为3通道
        if len(image_bgr.shape) == 2:
            image_bgr = np.stack([image_bgr] * 3, axis=-1)
        # 获取原始图像大小
        orig_h, orig_w = image_bgr.shape[:2]
 
        width = darknet.network_width(network)
        height = darknet.network_height(network)
        darknet_image = darknet.make_image(width, height, 3)
 
        # image = cv2.imread(image_path)
        image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
        image_resized = cv2.resize(image_rgb, (width, height), interpolation=cv2.INTER_LINEAR)
 
        darknet.copy_image_from_bytes(darknet_image, image_resized.tobytes())
        detections = darknet.detect_image(network, class_names, darknet_image, thresh=thresh)
        darknet.free_image(darknet_image)
        new_detections = []
        for detection in detections:
            pred_label, pred_conf, (x, y, w, h) = detection
            new_x = x / width * orig_w
            new_y = y / height * orig_h
            new_w = w / width * orig_w
            new_h = h / height * orig_h
 
            # 可以约束一下
            (x1, y1, x2, y2) = self.bbox2point((new_x, new_y, new_w, new_h))
            x1 = x1 if x1 > 0 else 0
            x2 = x2 if x2 < orig_w else orig_w
            y1 = y1 if y1 > 0 else 0
            y2 = y2 if y2 < orig_h else orig_h
 
            (new_x, new_y, new_w, new_h) = self.point2bbox((x1, y1, x2, y2))
 
            new_detections.append((pred_label, pred_conf, (new_x, new_y, new_w, new_h)))
 
        image = darknet.draw_boxes(new_detections, image_rgb, class_colors)
        return cv2.cvtColor(image, cv2.COLOR_RGB2BGR), new_detections
 
    def predict_image(self, image_bgr, thresh=0.5, is_show=True, save_path=''):
        '''
        :param image_bgr:  输入图像
        :param thresh:     置信度阈值
        :param is_show:   是否将画框之后的原始图像返回
        :param save_path: 画框后的保存路径, eg='/home/aaa.jpg'
        :return:
        '''
        draw_bbox_image, detections = self.image_detection(image_bgr, self.network, self.class_names, self.class_colors,
                                                           thresh)
        # detections = [('helmet', '99.76', (271.18813918187067, 162.8237687624418, 88.92447724709143, 112.84086117377649))]
        if is_show:
            if save_path:
                cv2.imwrite(save_path, draw_bbox_image)
            return draw_bbox_image
        return detections
 
  
detect = Detect(metaPath=r'cfg/***.data',
                    configPath=r'cfg/***.cfg',
                    weightPath=r'***.weights',# 生成的权重文件
                    gpu_id=1)
if __name__ == '__main__':
    image_root = r'/home/xipaoer/yolov4_tiny/darknet-master/data/78_2.jpg'
    image = cv2.imread(image_root)
#    print(image)
    save_root = r'/home/xipaoer/Desktop/number/'
    draw_bbox_image = detect.predict_image(image, save_path=os.path.join(save_root, "12.jpg"))

然后根据自己的需求更改就可以了

法二:
使用opencv调用yolov4-tiny(需要opencv版本4.4.0及以上)
注意:jetson nano 不要随意改变opencv版本,可能会造成“核心已转储”的问题,jetson nano不建议使用本方法

import numpy as np
import cv2
import os
import random
 
weights_path = '***.weights'     #模型权重文件
cfg_path = 'cfg/***.cfg'         #模型配置文件
labels_path = 'data/***.names'#模型类别标签文件

#初始化一些参数
LABELS = open(labels_path).read().strip().split("\n")
boxes = []
confidences = []
classIDs = []
color_list=[]
for i in range(len(LABELS)):
    color_list.append([random.randint(0,255),random.randint(0,255),random.randint(0,255)])
 
#加载网络配置与训练的权重文件 构建网络
net = cv2.dnn.readNetFromDarknet(cfg_path, weights_path)
 
#读入待检测的图像
image = cv2.imread(os.path.join("img","1.jpg"))
#得到图像的高和宽
(H,W) = image.shape[0: 2]
 
 
#得到YOLO需要的输出层
ln = net.getLayerNames()
out = net.getUnconnectedOutLayers() #得到未连接层得序号  [[200] /n [267]  /n [400] ]
x = []
for i in out:   # 1=[200]
    x.append(ln[i[0]-1])    # i[0]-1    取out中的数字  [200][0]=200  ln(199)= 'yolo_82'
ln=x
# ln  =  ['yolo_82', 'yolo_94', 'yolo_106']  得到 YOLO需要的输出层
 
 
 
#从输入图像构造一个blob,然后通过加载的模型,给我们提供边界框和相关概率
#blobFromImage(image, scalefactor=None, size=None, mean=None, swapRB=None, crop=None, ddepth=None)
blob = cv2.dnn.blobFromImage(image, 1 / 255.0, (416, 416),swapRB=True, crop=False)
#构造了一个blob图像,对原图像进行了图像的归一化,缩放了尺寸 ,对应训练模型
 
net.setInput(blob)
layerOutputs = net.forward(ln)  #ln此时为输出层名称  ,向前传播  得到检测结果
 
for output in layerOutputs:  #对三个输出层 循环
    for detection in output:  #对每个输出层中的每个检测框循环
        scores=detection[5:]  #detection=[x,y,h,w,c,class1,class2] scores取第6位至最后
        classID = np.argmax(scores)#np.argmax反馈最大值的索引
        confidence = scores[classID]
        if confidence >0.5:#过滤掉那些置信度较小的检测结果
            box = detection[0:4] * np.array([W, H, W, H])
            #print(box)
            (centerX, centerY, width, height)= box.astype("int")
            # 边框的左上角
            x = int(centerX - (width / 2))
            y = int(centerY - (height / 2))
            # 更新检测出来的框
            boxes.append([x, y, int(width), int(height)])
            confidences.append(float(confidence))
            classIDs.append(classID)
 
idxs=cv2.dnn.NMSBoxes(boxes, confidences, 0.2,0.3)
box_seq = idxs.flatten()#[ 2  9  7 10  6  5  4]
if len(idxs)>0:
    for seq in box_seq:
        (x, y) = (boxes[seq][0], boxes[seq][1])  # 框左上角
        (w, h) = (boxes[seq][2], boxes[seq][3])  # 框宽高
        # if classIDs[seq]==0: #根据类别设定框的颜色
        #     color = [0,0,255]
        # else:
        #     color = [0,255,0]
        cv2.rectangle(image, (x, y), (x + w, y + h), color_list[classIDs[seq]], 2)  # 画框
        text = "{}: {:.4f}".format(LABELS[classIDs[seq]], confidences[seq])
        cv2.putText(image, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color_list[classIDs[seq]],2)  # 写字
cv2.namedWindow('Image', cv2.WINDOW_AUTOSIZE)
cv2.imshow("Image", image)
cv2.waitKey(0)

然后根据自己的需求更改就可以了

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号