目标侦测(MTCNN)[人脸]

本文介绍了MTCNN目标侦测算法,适用于人脸检测。该算法利用小卷积核多次卷积替代大卷积核,通过图像金字塔处理实现多尺度目标检测。内容包括算法思路、数据处理、网络结构(P-R-O网络)、训练过程以及坐标反算和NMS去重策略。
摘要由CSDN通过智能技术生成

概念

精度高,最小可检测 12×12 的物体。
计算力需求低,大多数笔记本可以训练,手机可以运行。

算法思路

  • 用小卷积核卷多次代替大卷积核卷一次。
  • 把单目标侦测网络当成卷积核,通过图像金字塔缩放图片(最小边长不小于 12),多次扫描来侦测多个目标。
  • IOU:衡量框的相似度,交并(交叉的框),交小(嵌套的框)。
  • NMS(非极大值抑制):按照置信度给框从大到小排序,保留置信度最大的框,然后用这个框和其它框做 IOU,留下相似度小于阈值的框,再从剩下的框里找到置信度最大的保留,和其它框继续做 IOU 比较相似度。

数据处理

使用 CelebA 数据集,生成正样本、部分样本、负样本。(正样本 : 部分样本 : 负样本 = 1 : 1 : 3,因为图上大部分区域都是负样本)

设计标签
标签:地址 编号 x1偏移量 y1偏移量 x2偏移量 y2偏移量
positive/0.jpg 1 0.022935779816513763 0.4334862385321101 -0.013761467889908258 -0.0022935779816513763

增样
CelebA 数据集都是欧洲明星脸,要检测亚洲普通人脸,需要增样。

import os
from PIL import Image
import numpy as np

import utils


img_path = r"D:\class\CelebA_test\img"
label_path = r"D:\class\CelebA_test\label.txt"

save_path = r"D:\class\CelebA_"

# 3 个尺寸(12,24,48),3 种样本(positive-正样本,negative-负样本,part-部分样本)
for img_size in [12, 24, 48]:
    img_path_positive = os.path.join(save_path, str(img_size), "positive")
    img_path_part = os.path.join(save_path, str(img_size), "part")
    img_path_negative = os.path.join(save_path, str(img_size), "negative")
    for each in [img_path_positive, img_path_part, img_path_negative]:
        if not os.path.exists(each):
            os.makedirs(each)

    # 3 个标签(positive-正样本,negative-负样本,part-部分样本)
    label_path_positive = os.path.join(save_path, str(img_size), "positive.txt")
    label_path_part = os.path.join(save_path, str(img_size), "part.txt")
    label_path_negative = os.path.join(save_path, str(img_size), "negative.txt")
    label_positive = open(label_path_positive, "w")
    label_part = open(label_path_part, "w")
    label_negative = open(label_path_negative, "w")

    positive_count = 0
    part_count = 0
    negative_count = 0

    # 读取标签
    for i, info in enumerate(open(label_path).readlines()):
        if i < 2:
            continue
        # 000001.jpg    95  71 226 313
        info = info.strip().split(" ")
        info = list(filter(bool, info))

        # 打开图片
        with Image.open(os.path.join(img_path, info[0])) as img:
            x1 = float(info[1])
            y1 = float(info[2])
            w = float(info[3])
            h = float(info[4])
            # 排除奇异样本
            if min(w, h) < 40 or x1 < 0 or y1 < 0:
                continue
            x2 = x1 + w
            y2 = y1 + h
            r_box = np.array([[x1, y1, x2, y2]])
            # 标签中心点
            cx = x1 + w / 2
            cy = y1 + h / 2

            # 每个尺寸生成 5 个样本
            for _ in range(5):
                # 建议框中心点(标签框中心点偏移 0.2 倍)
                cx_ = cx + np.random.randint(-w * 0.2, w * 0.2)
                cy_ = cy + np.random.randint(-h * 0.2, h * 0.2)
                # 建议框边长(原图宽的 0.8 倍,原图长的 1.2 倍)
                side = np.random.randint(min(w, h) * 0.8, max(w, h) * 1.2)
                # 建议框坐标
                x1_ = cx_ - side / 2
                y1_ = cy_ - side / 2
                x2_ = x1_ + side
                y2_ = y1_ + side
                # 左上角坐标超出图片
                x1_ = np.maximum(0, cx_ - side / 2)
                y1_ = np.maximum(0, cy_ - side / 2)

                # 剪切建议框,设置尺寸
                s_box = np.array([x1_, y1_, x2_, y2_])
                s_img = img.crop(s_box)
                s_img = s_img.resize((img_size, img_size))

                # 新样本的标签:标签框相对于建议框的偏移量
                # (标签坐标 - 建议框坐标) / 建议框边长
                x1_offset = (x1 - x1_) / side
                y1_offset = (y1 - y1_) / side
                x2_offset = (x2 - x2_) / side
                y2_offset = (y2 - y2_) / side

                # 建议框和标签框做 IOU
                iou = utils.iou(s_box, r_box)
                if iou > 0.6:
                    # 正样本
                    label_positive.write("positive/{}.jpg 1 {} {} {} {}\n".format(positive_count, x1_offset, y1_offset, x2_offset, y2_offset))
                    s_img.save(os.path.join(img_path_positive, "{}.jpg".format(positive_count)))
                    positive_count += 1
                elif iou > 0.4:
                    # 部分样本
                    label_part.write("part/{}.jpg 2 {} {} {} {}\n".format(part_count, x1_offset, y1_offset, x2_offset, y2_offset))
                    s_img.save(os.path.join(img_path_part, "{}.jpg".format(part_count)))
                    part_count += 1
                elif iou < 0.3:
                    # 负样本
                    label_negative.write("negative/{}.jpg 0 0 0 0 0\n".format(negative_count))
                    s_img.save(os.path.join(img_path_negative, "{}.jpg".format(negative_count)))
                    negative_count += 1

            # 用中心点偏移生成的样本,几乎没有负样本,所以取标签框外的部分作为负样本
            img_w, img_h = img.size
            # 左
            if img_size < min(x1, y1):
                side = np.random.randint(img_size, min(x1, y1))
                x1_ = np.random.randint(0, x1 - side)
                y1_ = np.random.randint(0, img_h - side)
                x2_ = x1_ + side
                y2_ = y1_ + side
                cut_box = np.array([x1_, y1_, x2_, y2_])
                crop_img = img.crop(cut_box)
                crop_img = crop_img.resize((img_size, img_size))
                label_negative.write("negative/{}.jpg 0 0 0 0\n".format(negative_count))
                label_negative.flush()
                crop_img.save(os.path.join(img_path_negative, "{}.jpg".format(negative_count)))
                negative_count += 1

            # 上
            if img_size < min(x1, y1):
                side = np.random.randint(img_size, min(x1, y1))
                x1_ = np.random.randint(0, img_w - side)
                y1_ = np.random.randint(0, y1 - side)
                x2_ = x1_ + side
                y2_ = y1_ + side
                cut_box = np.array([x1_, y1_, x2_, y2_])
                crop_img = img.crop(cut_box)
                crop_img = crop_img.resize
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值