1. 系统环境
- Ubuntu 18.04
- OpenCV 3.4.6
- RTX 3070
2. 下载并测试 DarkNet
- DarkNet YOLO 官方网站:https://pjreddie.com/darknet/yolo/
git clone https://github.com/pjreddie/darknet # 克隆项目 cd darknet # 进入文件夹 make # 编译 wget https://pjreddie.com/media/files/yolov3.weights # 下载权重 ./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg # 预测 data/dog.jpg 这张图片中的目标 ./darknet detect cfg/yolov3.cfg yolov3.weights # 运行后输入图片相对路径,测试多张图片
2.1 测试 yolov3-tiny
因为我的 3070 显存只有 8 GB ,所以只能测试 yolov3-tiny。
# 进入 darknet 文件夹
wget https://pjreddie.com/media/files/yolov3-tiny.weights # 下载权重文件
./darknet detect cfg/yolov3-tiny.cfg yolov3-tiny.weights data/dog.jpg # 执行
2.2 连接网络摄像头进行目标检测
-
修改Makefile:
-
修改代码:
- 手机上安装软件,参考:https://zhangtianhao.blog.csdn.net/article/details/115279932
- 执行命令(这个部分参考 github 上给出的提示,但我没有成功):
# 修改为自己 ip 地址,两台设备处于同一局域网下使用 ./darknet detector demo cfg/coco.data cfg/yolov3.cfg yolov3.weights rtsp://login:pass@192.168.0.228:554 -i 0
3. 训练数据集
3.1 VOC 数据集
3.1.1 获取数据集
cd darknet/scripts/ # 进入文件夹
# 下载数据集
wget https://pjreddie.com/media/files/VOCtrainval_11-May-2012.tar
wget https://pjreddie.com/media/files/VOCtrainval_06-Nov-2007.tar
wget https://pjreddie.com/media/files/VOCtest_06-Nov-2007.tar
# 解压数据集,解压后的数据集均位于子目录 VOCdevkit 下。
tar xf VOCtrainval_11-May-2012.tar
tar xf VOCtrainval_06-Nov-2007.tar
tar xf VOCtest_06-Nov-2007.tar
数据集很大,你忍一下。忍不了可以用百度网盘下载。
Linux 百度网盘官方下载链接:https://pan.baidu.com/download
VOC 数据集百度网盘链接:https://pan.baidu.com/s/18MwwjNUi0Z5wkTmSzStFqA
密码: m4vh
3.1.2 为数据集生成标签
cd ~/darknet/scripts/ # 进入文件夹
python voc_label.py # 生成标签文件
cat 2007_train.txt 2007_val.txt 2012_*.txt > train.txt # 将包含图片路径的文本文件合并为一个
3.1.3 下载预训练权重
# 进入 darknet 文件夹(建议使用迅雷下载,该命令下载速度很慢)
wget https://pjreddie.com/media/files/darknet53.conv.74
3.1.4 开始训练
./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74
如果出现:
fatal error: cudnn_version.h
这个问题,这是cudnn没有配置好,解决方法参考【Ubuntu|cuDNN】训练 DarkNet 时解决 fatal error: cudnn_version.h如果出现:
error: ‘CUDNN_CONVOLUTION_FWD_SPECIFY_WORKSPACE_LIMIT’ undeclared (first use in this function)
这个问题,是因为 DarkNet 没有适配 cudnn8.x,解决方法参考如何解决pjreddie版darknet不能使用cudnn8编译的问题。
- 跑起来后查看显卡状态:
watch -n 0.1 -d nvidia-smi #每隔 0.1 秒刷新一次
3.2 自己的数据集
3.2.1 数据集标注
工具:labelImg,全平台支持。
建议使用 conda 虚拟环境安装。
# 下载源码,解压,进入其根目录
conda activate # 激活 conda 环境
conda install pyqt=5 # 安装 pyqt5
pyrcc5 -o resources.py resources.qrc
unset PYTHONPATH # 在当前终端移除 python2 环境变量
python3 python labelImg.py # 打开 labelImg 工具
3.2.2 生成一些文件
在训练自己的数据集时需要生成一些文件和文件夹,网上很多博客提供了步骤,但对于想尽快把代码的跑起来的搞应用的同学不是很友好,这里我编写了一个脚本文件,运行即可生成需要的文件和文件夹。
首先需要将图片和对应的标注文件分别放在 img
和 label_xml
文件夹内:
├── img # 存放图片的文件夹
├── label_txt # 存放标签文件的文件夹(标签文件名除了后缀,与图片名相同)
└── generate.py # 用于生成文件和文件夹的预处理脚本
2 directories, 1 file
运行 generate.py
脚本后的项目目录为:
├── backup # 存放训练的中间结果
├── cornell.data # 存放类数量和一些文件(夹)的路径
├── cornell.names # 存放类名称
├── img # 存放图片的文件夹
├── label_txt # 存放标签文件的文件夹(标签文件名除了后缀,与图片名相同)
├── train.txt # 训练集路径
├── valid.txt # 验证集路径
└── generate.py # 用于生成文件和文件夹的预处理脚本
3 directories, 5 files
generate.py
脚本的内容如下:
# 脚本名:generate.py 用于生成文件和文件夹的预处理脚本
import os
import glob
import xml.etree.ElementTree as ET
# 图片的相对路径
img_path = 'img/'
# xml 文件路径
path = 'label_xml/'
# txt 保存路径
txt_save_path = 'label_txt/'
class_file_names_path = 'cornell.names'
file_data_path = 'cornell.data'
train_txt_path = 'train.txt'
valid_txt_path = 'valid.txt'
backup_dir_path = 'backup'
# 类名
class_names = []
# 类名集合
class_set = set() # 创建空集合
# 查找一个 xml 文件中的 class_name 标签
def find_class_name(filename):
tree = ET.parse(filename)
root = tree.getroot()
for member in root.findall('object'):
class_name = member[0].text
class_set.add(class_name) # 将种类名称加入集合
def find_class_names(path):
for xml_file in glob.glob(path + '*.xml'): # 遍历文件夹下所有的 xml 文件
find_class_name(xml_file)
# print(class_set) # 打印所有的标签名称
# 转换一个 xml 文件为 txt
def xml2txt(filename):
tree = ET.parse(filename)
root = tree.getroot()
# 保存的 txt 文件路径
filename0 = filename.split('.')[0]
txt_file = txt_save_path + filename0.split('/')[1] + '.txt'
# print(txt_file)
with open(txt_file, 'w') as txt_file:
for member in root.findall('object'):
picture_width = int(root.find('size')[0].text)
picture_height = int(root.find('size')[1].text)
class_name = member[0].text
# print(class_name)
# 类名对应的 index
class_num = class_names.index(class_name)
box_x_min = int(member[4][0].text) # 左上角横坐标
box_y_min = int(member[4][1].text) # 左上角纵坐标
box_x_max = int(member[4][2].text) # 右下角横坐标
box_y_max = int(member[4][3].text) # 右下角纵坐标
# 转成相对位置和宽高
x_center = (box_x_min + box_x_max) / (2 * picture_width)
y_center = (box_y_min + box_y_max) / (2 * picture_height)
width = (box_x_max - box_x_min) / picture_width
height = (box_y_max - box_y_min) / picture_height
# print(class_num, x_center, y_center, width, height)
txt_file.write(
str(class_num) + ' ' + str(x_center) + ' ' + str(y_center) +
' ' + str(width) + ' ' + str(height) + '\n')
# 转换文件夹下所有的 xml 文件为 txt 文件
def dir_xml2txt(path):
if not os.path.exists('label_txt'):
os.mkdir('label_txt')
print('[INFO] Folder label_txt was created...')
for xml_file in glob.glob(path + '*.xml'):
xml2txt(xml_file)
print('[INFO] xml has been transformed to txt...')
# 生成类名文件 .names
def generate_class_name_file(file_name_, class_names_):
with open(file_name_, 'w') as tf:
for class_name_ in class_names_:
tf.write(class_name_ + '\n')
# print(class_name_)
print('[INFO] File ' + file_name_ + ' was created...')
# 创建 backup 文件夹
def mk_backup_dir(dir_name):
if not os.path.exists(dir_name): # 若不存在 backup 文件夹
os.mkdir(dir_name) # 创建 backup 文件夹
print('[INFO] Folder ' + dir_name + ' was created...')
# 生成用于存放输入路径的 .data 文件
def generate_data_file(data_file, class_size):
# 相对路径转化为绝对路径
train_txt_abs_path = os.path.abspath(train_txt_path)
valid_txt_abs_path = os.path.abspath(valid_txt_path)
names_abs_path = os.path.abspath(class_file_names_path)
backup_dir_abs_path = os.path.abspath(backup_dir_path)
# 写入 .data 文件
with open(data_file, 'w') as tf:
tf.write('class = ' + str(class_size) + '\n')
tf.write('train = ' + str(train_txt_abs_path) + '\n')
tf.write('valid = ' + str(valid_txt_abs_path) + '\n')
tf.write('names = ' + str(names_abs_path) + '\n')
tf.write('backup = ' + str(backup_dir_abs_path) + '\n')
print('[INFO] File ' + data_file + ' was created...')
def generate_train_and_val(path, txt_file):
img_abs_path = os.path.abspath(img_path) + '/' # 相对路径转化为绝对路径
with open(txt_file, 'w') as tf:
for img_file in glob.glob(path + '*.png'): # 读取文件夹下所有的图片路径
# print(img_file)
tf.write(img_file + '\n') # 图片的绝对路径写入 txt
print('[INFO] File ' + txt_file + ' was created...')
if __name__ == '__main__':
find_class_names(path)
class_names = list(class_set.intersection(class_set)) # 将集合转化为列表
dir_xml2txt(path) # 转换文件夹下所有的 xml 文件为 txt 文件
generate_train_and_val(img_path, 'train.txt') # 生成存储训练集路径的 txt 文件
generate_train_and_val(img_path, 'valid.txt') # 生成存储验证集路径的 txt 文件
generate_class_name_file(class_file_names_path,
class_names) # 生成类名文件 .names
mk_backup_dir(backup_dir_path) # 创建 backup 文件夹
generate_data_file(file_data_path,
len(class_names)) # 生成用于存放输入路径的 .data 文件
将 yolov3-voc.cfg
复制到自己新建的文件夹 ~/darknet/cornell_detect/
下,然后修改:
<img src="https://gitee.com/zhang_ma_nong/img_bed/raw/master/img/20210826224238.png" alt="QQ20210826-224217<a href=" https:="" github.com="" 2x"="" title="@2x" class="at-link">@2x">
<img src="https://gitee.com/zhang_ma_nong/img_bed/raw/master/img/20210826224424.png" alt="QQ20210826-224404<a href=" https:="" github.com="" 2x"="" title="@2x" class="at-link">@2x">
<img src="https://gitee.com/zhang_ma_nong/img_bed/raw/master/img/20210826224659.png" alt="QQ20210826-224646<a href=" https:="" github.com="" 2x"="" title="@2x" class="at-link">@2x">
3.2.3 开始训练
cd ~/darknet/ # 进入克隆下来的 darknet 根目录
./darknet detector train cornell_detect/cornell.data cornell_detect/yolov3-voc.cfg darknet53.conv.74
3.2.4 测试模型
训练过程中,中间结果权重会保存在 ~/darknet/cornell_detect/backup/
中。
cd ~/darknet/ # 进入克隆下来的 darknet 根目录
./darknet detector test cornell_detect/cornell.data cornell_detect/yolov3-voc.cfg cornell_detect/backup/yolov3-voc_30000.weights ~/桌面/dataset/img/pcd0145r.png # 参数依次是 .data 文件,网络模型,权重文件,图片路径
评论(1)
您还未登录,请登录后发表或查看评论