Commit 69f4ba48 authored by chenych's avatar chenych
Browse files

update readme

parent e0491117
......@@ -4,8 +4,9 @@
## 模型结构
CenterFace是一种人脸检测算法,采用了轻量级网络mobileNetV2作为主干网络,结合特征金字塔网络(FPN)实现anchor free的人脸检测。
![Architecture of the CenterFace](Architecture of the CenterFace.png)
<div align=center>
<img src="./Architecture of the CenterFace.png"/>
</div>
## 算法原理
CenterFace模型是一种基于单阶段人脸检测算法,作者借鉴了CenterNet的思想,将人脸检测转换为标准点问题,根据人脸中心点来回归人脸框的大小和五个标志点。
......@@ -53,7 +54,9 @@ pip3 install -r requirements.txt
WIDER_FACE:http://shuoyang1213.me/WIDERFACE/index.html
![datasets](datasets.png)
<div align=center>
<img src="./datasets.png"/>
</div>
下载图片红框中三个数据并解压,也可直接点击下面链接直接下载:
......@@ -99,25 +102,15 @@ WIDER_FACE:http://shuoyang1213.me/WIDERFACE/index.html
│ └── 61--Street_Battle
```
<<<<<<< HEAD
2. 如果是使用WIDER_train、WIDER_val数据, 可直接将./datasets/labels/下的train_wider_face.json重命名为train_face.json, val_wider_face.json重命名为val_face.json即可,无需进行标注文件格式转换;
反之,需要将训练图片/验证图片对应的人脸关键点标注信息文件train.txt/val.txt,放置于 ./datasets/annotations/下(train存放训练图片的标注文件,val存放验证图片的标注文件),存放目录结构如下:
=======
2. 如果是使用WIDER_train数据, 可直接将./datasets/labels/下的train_wider_face.json重命名为train_face.json即可,无需进行标注文件格式转换;反之,需要将训练图片对应的人脸关键点标注信息文件(xxxx.txt),放置于 ./datasets/annotations/下(train存放训练图片的标注文件,val存放验证图片的标注文件),存放目录结构如下:
>>>>>>> c9fac124caf657ec86fd6a58ca90e1da5f88f6cb
反之,需要将训练图片/验证图片对应的人脸标注信息文件train.txt/val.txt,放置于 ./datasets/annotations/下(train存放训练图片的标注文件,val存放验证图片的标注文件),存放目录结构如下:
```
├── annotations
│ ├── train
<<<<<<< HEAD
│ ├── train.txt
│ ├── val
│ ├── val.txt
=======
│ ├── xxx.txt
│ ├── val
│ ├── xxx.txt
>>>>>>> c9fac124caf657ec86fd6a58ca90e1da5f88f6cb
```
特别地,标注信息的格式为:
......@@ -128,7 +121,8 @@ x, y, w, h, left_eye_x, left_eye_y, flag, right_eye_x, right_eye_y, flag, nose_x
```
举个例子:
train_keypoints_widerface.txt是wider_face训练数据集的标注信息
./datasets/annotations/train/train.txt是wider_face训练数据集的标注信息
```
# 0--Parade/0_Parade_marchingband_1_849.jpg
449 330 122 149 488.906 373.643 0.0 542.089 376.442 0.0 515.031 412.83 0.0 485.174 425.893 0.0 538.357 431.491 0.0 0.82
......@@ -142,11 +136,7 @@ cd ./datasets
python gen_data.py
```
<<<<<<< HEAD
执行完成后会在./datasets/labels下生成训练数据的标注文件 train_face.json、val_face.json
=======
执行完成后会在./datasets/labels下生成训练数据的标注文件 train_face.json
>>>>>>> c9fac124caf657ec86fd6a58ca90e1da5f88f6cb
## 训练
......@@ -166,18 +156,23 @@ bash train_multi.sh
## 推理
#### 单卡推理
```
cd ./src
python test_wider_face.py
```
## result
![Result](draw_img.jpg)
<div align=center>
<img src="./draw_img.jpg"/>
</div>
### 精度
WIDER_FACE验证集上的测试结果如下
| Method | Easy(p) | Medium(p) | Hard(p)|
| Method | Easy(AP) | Medium(AP) | Hard(AP)|
|:--------:| :--------:| :---------:| :------:|
| ours(one scale) | 0.9264 | 0.9133 | 0.7479 |
| original | 0.922 | 0.911 | 0.782|
......
......@@ -2,13 +2,14 @@ from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import pycocotools.coco as coco
from pycocotools.cocoeval import COCOeval
import numpy as np
import json
import os
from pycocotools.cocoeval import COCOeval
import torch.utils.data as data
import numpy as np
import pycocotools.coco as coco
class FACEHP(data.Dataset):
......@@ -19,7 +20,8 @@ class FACEHP(data.Dataset):
dtype=np.float32).reshape(1, 1, 3)
std = np.array([0.28863828, 0.27408164, 0.27809835],
dtype=np.float32).reshape(1, 1, 3)
flip_idx = [[0, 1], [3, 4]] # 翻转的关键点在关键点矩阵中的索引
# 翻转的关键点在关键点矩阵中的索引
flip_idx = [[0, 1], [3, 4]]
def __init__(self, opt, split):
super(FACEHP, self).__init__()
......@@ -32,7 +34,8 @@ class FACEHP(data.Dataset):
self.acc_idxs = [1, 2, 3, 4]
self.data_dir = opt.data_dir
_ann_name = {'train': 'train', 'val': 'val'}
self.img_dir = os.path.join(self.data_dir, f'images/{_ann_name[split]}') # 训练图片所在地址
# 图片所在地址
self.img_dir = os.path.join(self.data_dir, f'images/{_ann_name[split]}')
print('===>', self.img_dir)
if split == 'val':
self.annot_path = os.path.join(
......
......@@ -103,8 +103,8 @@ def _topk_channel(scores, K=40):
def _topk(scores, K=40):
batch, cat, height, width = scores.size()
topk_scores, topk_inds = torch.topk(scores.view(batch, cat, -1), K) # 前100个点
# 前100个点
topk_scores, topk_inds = torch.topk(scores.view(batch, cat, -1), K)
topk_inds = topk_inds % (height * width)
topk_ys = (topk_inds / width).int().float()
......
......@@ -54,7 +54,8 @@ def _neg_loss(pred, gt):
loss = 0
pos_loss = torch.log(pred) * torch.pow(1 - pred, 2) * pos_inds
neg_loss = torch.log(1 - pred) * torch.pow(pred, 2) * neg_weights * neg_inds
neg_loss = torch.log(1 - pred) * torch.pow(pred, 2) * \
neg_weights * neg_inds
num_pos = pos_inds.float().sum()
pos_loss = pos_loss.sum()
......@@ -66,6 +67,7 @@ def _neg_loss(pred, gt):
loss = loss - (pos_loss + neg_loss) / num_pos
return loss
def _not_faster_neg_loss(pred, gt):
pos_inds = gt.eq(1).float()
neg_inds = gt.lt(1).float()
......@@ -83,6 +85,7 @@ def _not_faster_neg_loss(pred, gt):
loss -= all_loss
return loss
def _slow_reg_loss(regr, gt_regr, mask):
num = mask.float().sum()
mask = mask.unsqueeze(2).expand_as(gt_regr)
......@@ -94,6 +97,7 @@ def _slow_reg_loss(regr, gt_regr, mask):
regr_loss = regr_loss / (num + 1e-4)
return regr_loss
def _reg_loss(regr, gt_regr, mask, wight_=None):
''' L1 regression loss
Arguments:
......@@ -110,18 +114,22 @@ def _reg_loss(regr, gt_regr, mask, wight_=None):
if wight_ is not None:
wight_ = wight_.unsqueeze(2).expand_as(gt_regr).float()
# regr_loss = nn.functional.smooth_l1_loss(regr, gt_regr, reduce=False)
regr_loss = nn.functional.smooth_l1_loss(regr, gt_regr, reduction='none')
regr_loss = nn.functional.smooth_l1_loss(
regr, gt_regr, reduction='none')
regr_loss *= wight_
regr_loss = regr_loss.sum()
else:
regr_loss = nn.functional.smooth_l1_loss(regr, gt_regr, reduction='sum')
regr_loss = nn.functional.smooth_l1_loss(
regr, gt_regr, reduction='sum')
# regr_loss = nn.functional.smooth_l1_loss(regr, gt_regr, size_average=False)
regr_loss = regr_loss / (num + 1e-4)
return regr_loss
class FocalLoss(nn.Module):
'''nn.Module warpper for focal loss'''
def __init__(self):
super(FocalLoss, self).__init__()
self.neg_loss = _neg_loss
......@@ -129,6 +137,7 @@ class FocalLoss(nn.Module):
def forward(self, out, target):
return self.neg_loss(out, target)
class RegLoss(nn.Module):
'''Regression loss for an output tensor
Arguments:
......@@ -137,6 +146,7 @@ class RegLoss(nn.Module):
ind (batch x max_objects)
target (batch x max_objects x dim)
'''
def __init__(self):
super(RegLoss, self).__init__()
......@@ -145,6 +155,7 @@ class RegLoss(nn.Module):
loss = _reg_loss(pred, target, mask, wight_)
return loss
class RegL1Loss(nn.Module):
def __init__(self):
super(RegL1Loss, self).__init__()
......@@ -158,6 +169,7 @@ class RegL1Loss(nn.Module):
loss = loss / (mask.sum() + 1e-4)
return loss
class NormRegL1Loss(nn.Module):
def __init__(self):
super(NormRegL1Loss, self).__init__()
......@@ -174,6 +186,7 @@ class NormRegL1Loss(nn.Module):
loss = loss / (mask.sum() + 1e-4)
return loss
class RegWeightedL1Loss(nn.Module):
def __init__(self):
super(RegWeightedL1Loss, self).__init__()
......@@ -187,6 +200,7 @@ class RegWeightedL1Loss(nn.Module):
loss = loss / (mask.sum() + 1e-4)
return loss
class L1Loss(nn.Module):
def __init__(self):
super(L1Loss, self).__init__()
......@@ -194,9 +208,11 @@ class L1Loss(nn.Module):
def forward(self, output, mask, ind, target):
pred = _tranpose_and_gather_feat(output, ind)
mask = mask.unsqueeze(2).expand_as(pred).float()
loss = F.l1_loss(pred * mask, target * mask, reduction='elementwise_mean')
loss = F.l1_loss(pred * mask, target * mask,
reduction='elementwise_mean')
return loss
class BinRotLoss(nn.Module):
def __init__(self):
super(BinRotLoss, self).__init__()
......@@ -206,15 +222,19 @@ class BinRotLoss(nn.Module):
loss = compute_rot_loss(pred, rotbin, rotres, mask)
return loss
def compute_res_loss(output, target):
return F.smooth_l1_loss(output, target, reduction='elementwise_mean')
# TODO: weight
def compute_bin_loss(output, target, mask):
mask = mask.expand_as(output)
output = output * mask.float()
return F.cross_entropy(output, target, reduction='elementwise_mean')
def compute_rot_loss(output, target_bin, target_res, mask):
# output: (B, 128, 8) [bin1_cls[0], bin1_cls[1], bin1_sin, bin1_cos,
# bin2_cls[0], bin2_cls[1], bin2_sin, bin2_cos]
......
......@@ -47,6 +47,7 @@ class MultiPoseLoss(torch.nn.Module):
batch['hps'].detach().cpu().numpy(),
batch['ind'].detach().cpu().numpy(),
opt.output_res, opt.output_res)).to(opt.device)
if opt.eval_oracle_hp_offset:
output['hp_offset'] = torch.from_numpy(gen_oracle_map(
batch['hp_offset'].detach().cpu().numpy(),
......@@ -56,10 +57,13 @@ class MultiPoseLoss(torch.nn.Module):
# 1. focal loss,求目标的中心,
hm_loss += self.crit(output['hm'], batch['hm']) / opt.num_stacks
if opt.wh_weight > 0:
wh_loss += self.crit_reg(output['wh'], batch['reg_mask'], # 2. 人脸bbox高度和宽度的loss
# 2. 人脸bbox高度和宽度的loss
wh_loss += self.crit_reg(output['wh'], batch['reg_mask'],
batch['ind'], batch['wh'], batch['wight_mask']) / opt.num_stacks
if opt.reg_offset and opt.off_weight > 0:
off_loss += self.crit_reg(output['hm_offset'], batch['reg_mask'], # 3. 人脸bbox中心点下采样,所需要的偏差补偿
# 3. 人脸bbox中心点下采样,所需要的偏差补偿
off_loss += self.crit_reg(output['hm_offset'], batch['reg_mask'],
batch['ind'], batch['hm_offset'], batch['wight_mask']) / opt.num_stacks
if opt.dense_hp:
......@@ -68,14 +72,17 @@ class MultiPoseLoss(torch.nn.Module):
batch['dense_hps'] * batch['dense_hps_mask']) /
mask_weight) / opt.num_stacks
else:
lm_loss += self.crit_kp(output['landmarks'], batch['hps_mask'], # 4. 关节点的偏移
# 4. 关节点的偏移
lm_loss += self.crit_kp(output['landmarks'], batch['hps_mask'],
batch['ind'], batch['landmarks']) / opt.num_stacks
# if opt.reg_hp_offset and opt.off_weight > 0: # 关节点的中心偏移
# 关节点的中心偏移
# if opt.reg_hp_offset and opt.off_weight > 0:
# hp_offset_loss += self.crit_reg(
# output['hp_offset'], batch['hp_mask'],
# batch['hp_ind'], batch['hp_offset']) / opt.num_stacks
# if opt.hm_hp and opt.hm_hp_weight > 0: # 关节点的热力图
# 关节点的热力图
# if opt.hm_hp and opt.hm_hp_weight > 0:
# hm_hp_loss += self.crit_hm_hp(
# output['hm_hp'], batch['hm_hp']) / opt.num_stacks
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment