yolov5-face的github库:
deepcam-cn/yolov5-face: YOLO5Face: Why Reinventing a Face Detector (https://arxiv.org/abs/2105.12931) ECCV Workshops 2022)yolov5-face的论文:
[2105.12931] YOLO5Face: Why Reinventing a Face Detector

0前言

我的平台是linux,cuda11.8下部署yolov5-face,自己之前配置过mmyolo的环境,所以conda环境直接用的mmyolo的codna环境,conda环境的配置不多赘述。

1.下载yolov5-face的工程和数据集

复制yolov5-face的github工程到当前文件夹

git clone https://github.com/deepcam-cn/yolov5-face.git

下载训练和验证集数据集地址为
WIDER FACE: A Face Detection Benchmark

 labels文件下载地址为:

https://drive.google.com/file/d/1tU_IjyOwGQfGNUvZGwWWM4SwxKp2PUQ8/view

 下载完大概类似如图放置,train和val文件夹下都是这个布局

2.转换数据集

因为数据集需要改成yolo适合读取的格式,所以需要转换 。
工程下的yolov5-face/data/train2yolo.py和yolov5-face/data/val2yolo.py为转换数据集所用文件
先新建一个新数据的结构

train部分:

 然后先修改train2yolo.py,将主函数部分改成如下:

if __name__ == '__main__':
    original_path = '/home/ubuntu/workdir/qita/insightface/data/retinaface/train'
    save_path = '/home/ubuntu/workdir/mmyolo/widerface_yolo/train'
    save_path_img = os.path.join(save_path, 'images')
    save_path_labels = os.path.join(save_path, 'labels')
    aa = WiderFaceDetection(os.path.join(original_path, 'label.txt'))
    for i in range(len(aa.imgs_path)):
        print(i, aa.imgs_path[i])
        img = cv2.imread(aa.imgs_path[i])
        base_img = os.path.basename(aa.imgs_path[i])
        base_txt = os.path.basename(aa.imgs_path[i])[:-4] + ".txt"
        save_img_path = os.path.join(save_path_img, base_img)
        save_txt_path = os.path.join(save_path_labels, base_txt)
        with open(save_txt_path, "w") as f:
            height, width, _ = img.shape
            labels = aa.words[i]
            annotations = np.zeros((0, 14))
            if len(labels) == 0:
                continue
            for idx, label in enumerate(labels):
                annotation = np.zeros((1, 14))
                # bbox
                label[0] = max(0, label[0])
                label[1] = max(0, label[1])
                label[2] = min(width - 1, label[2])
                label[3] = min(height - 1, label[3])
                annotation[0, 0] = (label[0] + label[2] / 2) / width  # cx
                annotation[0, 1] = (label[1] + label[3] / 2) / height  # cy
                annotation[0, 2] = label[2] / width  # w
                annotation[0, 3] = label[3] / height  # h
                #if (label[2] -label[0]) < 8 or (label[3] - label[1]) < 8:
                #    img[int(label[1]):int(label[3]), int(label[0]):int(label[2])] = 127
                #    continue
                # landmarks
                annotation[0, 4] = label[4] / width  # l0_x
                annotation[0, 5] = label[5] / height  # l0_y
                annotation[0, 6] = label[7] / width  # l1_x
                annotation[0, 7] = label[8] / height  # l1_y
                annotation[0, 8] = label[10] / width  # l2_x
                annotation[0, 9] = label[11] / height  # l2_y
                annotation[0, 10] = label[13] / width  # l3_x
                annotation[0, 11] = label[14] / height  # l3_y
                annotation[0, 12] = label[16] / width  # l4_x
                annotation[0, 13] = label[17] / height  # l4_y
                str_label = "0 "
                for i in range(len(annotation[0])):
                    str_label = str_label + " " + str(annotation[0][i])
                str_label = str_label.replace('[', '').replace(']', '')
                str_label = str_label.replace(',', '') + '\n'
                f.write(str_label)
        cv2.imwrite(save_img_path, img)

针对自己的目录修改original_path和save_path的路径然后运行就可以了

val部分:

然后再修改val2yolo.py,将主函数部分改成如下:

if __name__ == '__main__':
    save_path = '/home/ubuntu/workdir/mmyolo/widerface_yolo/val'
    save_path_img = os.path.join(save_path, 'images')
    save_path_labels = os.path.join(save_path, 'labels')
    datas = wider2face(root='/home/ubuntu/workdir/qita/insightface/data/retinaface', phase='val')
    for idx, data in enumerate(datas.keys()):
        pict_name = os.path.basename(data)
        out_img = f'{save_path_img}/{idx}.jpg'
        out_txt = f'{save_path_labels}/{idx}.txt'
        shutil.copyfile(data, out_img)
        labels = datas[data]
        f = open(out_txt, 'w')
        for label in labels:
            f.write(label + '\n')
        f.close()

针对自己的目录修改datas和save_path的路径然后运行就可以了
 

3.下载权重

后面我尝试训练的时候,发现报错
FileNotFoundError: [Errno 2] No such file or directory: 'weights/yolov5s.pt'

官方有给下载权重的办法:

cd mmyolo/yolov5-face #在工程路径下运行
bash weights/download_weights.sh

注:如果官方方法不可以,可以直接下载

wget https://github.com/ultralytics/yolov5/releases/download/v7.0/yolov5s.pt


运行后下载了yolov5s.pt,将其移动到weights文件夹下

4.训练

修改yolov5-face/data/widerface.yaml文件

# download command/URL (optional)
#download: bash data/scripts/get_voc.sh

# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
train: /home/ubuntu/workdir/mmyolo/widerface_yolo/train  # 16551 images
val: /home/ubuntu/workdir/mmyolo/widerface_yolo/val  # 16551 images
#val: /ssd_1t/derron/yolov5-face/data/widerface/train/  # 4952 images

# number of classes
nc: 1

# class names
names: [ 'face']

运行程序

python train.py

遇到的问题1:
问题:AttributeError: module 'numpy' has no attribute 'int'.
原因:numpy版本高导致的
解决方案::将错误的地方的np.int改成np.int32

    #cum_counts = np.cumsum(np.greater(counts, 0, dtype=np.int32))
    cum_counts = np.cumsum(np.greater(counts, 0).astype(np.int32))

遇到的问题2:
问题:TypeError: No loop matching the specified signature and casting was found for ufunc greater
原因:NumPy 和 TensorBoard 的兼容性问题
解决方案::

#tb_writer.add_histogram('classes', c, 0)
tb_writer.add_histogram('classes', c.cpu().numpy().astype(float), 0) 

遇到的问题3:
问题:TypeError: No loop matching the specified signature and casting was found for ufunc greater
原因:TensorBoard的add_histogram()函数中,NumPy的greater运算与数据类型不兼容。
解决方案::

    #cum_counts = np.cumsum(np.greater(counts, 0, dtype=np.int32))
    cum_counts = np.cumsum(np.greater(counts, 0).astype(np.int32))


遇到的问题4:
问题:RuntimeError: result type Float can't be cast to the desired output type long int
原因:build_targets() 函数中尝试将浮点数转换为整数时出现的类型不匹配问题
解决方案::参考文章一步真实解决RuntimeError: result type Float can‘t be cast to the desired output type long int_runtimeerror: result type float can't be cast to t-CSDN博客

# anchors = det.anchors[i]
anchors, shape = det.anchors[i], p[i].shape 

#indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1)))  
indices.append((b, a, gj.clamp_(0, shape[2] - 1), gi.clamp_(0, shape[3] - 1))) 

然后成功开始跑了

具体代码可以是

CUDA_VISIBLE_DEVICES="0,1,2,3" python3 train.py --data data/widerface.yaml --cfg models/yolov5s.yaml --weights 'pretrained models'

5.测试和评估

yolov5-face官方给的测试和评估方案是,也是widerface这个人脸库官方给的测评方案

python3 test_widerface.py --weights 'your test model' --img-size 640

cd widerface_evaluate
python3 evaluation.py

注:后面的测试和评估都是用的yolo变换前的数据集


5.1生成wider_val.txt

遇到的问题1:运行代码python test_widerface.py --weights 'mmyolo/yolov5-face/runs/train/exp/weights/best.pt' --img-size 640时出现报错:
[Errno 2] No such file or directory: '/yolov5-face/data/widerface/val/wider_val.txt'
原因:缺少wider_val.txt'文件
解决方案:后评论区查阅到有人说该文章有解决方法
【人脸检测】复现Pytorch_Retinaface(Pytorch版本)_retinaface要gpu吗-CSDN博客
新建一个文件命名为widerValFile.py,内容为:

import os
import argparse

parser = argparse.ArgumentParser(description='Retinaface')
parser.add_argument('--dataset_folder', default='./data/widerface/val/images/', type=str, help='dataset path')
args = parser.parse_args()

if __name__ == '__main__':
    # testing dataset
    testset_folder = args.dataset_folder
    testset_list = args.dataset_folder[:-7] + "label.txt"

    with open(testset_list, 'r') as fr:
        test_dataset = fr.read().split()
    num_images = len(test_dataset)


    for i, img_name in enumerate(test_dataset):
        print("line i :{}".format(i))
        if img_name.endswith('.jpg'):
            print("     img_name :{}".format(img_name))
            f = open(args.dataset_folder[:-7] + 'wider_val.txt', 'a')
            f.write(img_name+'\n')
    f.close()

再运行 ,把后面的文件夹路径换成自己的(数据集用的是未转换成yolo格式的那个数据集)

python widerValFile.py --dataset_folder /home/ubuntu/workdir/qita/insightface/data/retinaface/val/images/

转换成功后,会出现一个 wider_val.txt文件,就是我们缺少的wider_val.txt文件

5.2测试


修改test_widerface.py文件的主函数,内容如下

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--weights', nargs='+', type=str, default='/home/ubuntu/workdir/mmyolo/yolov5-face/runs/train/exp/weights/best.pt', help='model.pt path(s)')
    parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
    parser.add_argument('--conf-thres', type=float, default=0.02, help='object confidence threshold')
    parser.add_argument('--iou-thres', type=float, default=0.5, help='IOU threshold for NMS')
    parser.add_argument('--device', default='0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
    parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
    parser.add_argument('--augment', action='store_true', help='augmented inference')
    parser.add_argument('--update', action='store_true', help='update all models')
    parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')
    parser.add_argument('--project', default='runs/detect', help='save results to project/name')
    parser.add_argument('--name', default='exp', help='save results to project/name')
    parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
    parser.add_argument('--save_folder', default='./widerface_evaluate/widerface_txt/', type=str, help='Dir to save txt results')
    parser.add_argument('--dataset_folder', default='/home/ubuntu/workdir/qita/insightface/data/retinaface/val/images', type=str, help='dataset path')
    parser.add_argument('--folder_pict', default='/home/ubuntu/workdir/qita/insightface/data/retinaface/val/wider_val.txt', type=str, help='folder_pict')
    opt = parser.parse_args()
    print(opt)

    # Load model
    device = select_device(opt.device)
    model = attempt_load(opt.weights, map_location=device)  # load FP32 model
    
    # Read folder_pict to map image names to their subfolders
    pict_folder = {}
    with open(opt.folder_pict, 'r') as f:
        for line in f.readlines():
            line = line.strip()
            if line:  # Skip empty lines
                parts = line.split('/')
                if len(parts) >= 2:  # Ensure valid format: "subfolder/image.jpg"
                    pict_folder[parts[-1]] = parts[-2]

    # Prepare testing dataset
    testset_folder = opt.dataset_folder
    image_extensions = ('.jpg', '.jpeg', '.png')
    
    # Recursively find all image files in subdirectories
    image_paths = []
    for root, _, files in os.walk(testset_folder):
        for file in files:
            if file.lower().endswith(image_extensions):
                image_paths.append(os.path.join(root, file))

    # Process images
    with torch.no_grad():
        for image_path in tqdm(image_paths, desc='Processing images'):
            img0 = cv2.imread(image_path)  # BGR
            if img0 is None:
                print(f'Warning: Could not read image {image_path}')
                continue
                
            # Detect faces
            boxes = detect(model, img0)
            
            # Prepare save path
            image_name = os.path.basename(image_path)
            subfolder = pict_folder.get(image_name, 'unknown')  # Default to 'unknown' if not found
            txt_name = os.path.splitext(image_name)[0] + ".txt"
            save_name = os.path.join(opt.save_folder, subfolder, txt_name)
            
            # Ensure directory exists
            os.makedirs(os.path.dirname(save_name), exist_ok=True)
            
            # Save results
            with open(save_name, "w") as fd:
                fd.write(f"{os.path.basename(save_name)[:-4]}\n")
                fd.write(f"{len(boxes)}\n")
                for box in boxes:
                    x, y, w, h, conf = box
                    fd.write(f"{x} {y} {w} {h} {min(conf, 1):.3f}\n")

    print('Evaluation completed.')

并修改test_widerface.py文件的最后两个parser,将default后面修改成自己数据集的路径

parser.add_argument('--dataset_folder', default='/home/ubuntu/workdir/qita/insightface/data/retinaface/val/images', type=str, help='dataset path')
parser.add_argument('--folder_pict', default='/home/ubuntu/workdir/qita/insightface/data/retinaface/val/wider_val.txt', type=str, help='folder_pict')

再运行 ,weights后记得换成自己的路径

python test_widerface.py --weights runs/train/exp/weights/best.pt --img-size 640

成功运行了 

5.3评估

 后面再运行官方给的

cd widerface_evaluate
python evaluation.py

结果又出现了问题2:AttributeError: module 'numpy' has no attribute 'float'.
原因:还是numpy版本的问题
解决方案:

python setup.py build_ext --inplace

再尝试运行

 python evaluation.py

成功运行 

可以看到结果是

 和论文的结果Our YOLOv5s那行结果94.33 92.61 83.15类似

参考博客:

❀YOLOv5学习❀yolo5-face论文里代码复现,实现运行_yolov5-face train怎么显示过程-CSDN博客yolov5-face模型训练以及测试_yolov5face训练-CSDN博客

更多推荐