How to preprocess the input images containing human bodies when the datas and labels are mixed with noise ?

对于一个特定视觉识别或定位任务,使用一个已知神经网络模型进行训练,并不是一个无脑的“调参”工作

1.我们对于神经网络的拟合功能 还不够深入理解 我们对卷积操作对于数据提取的特征到底是什么还不够清晰 (比如不同尺度下,相同的卷积操作对于相同图像提取的特征是否存在本质的区别?)

2.我们对训练数据集与测试数据集中,数据分布与标签噪声对模型拟合产生的影响,还不能准确控制,(单纯的mini-batch真的就是好的?)

3.我们对于,将原始数据,该预处理成何种形式,才能让神经网络模型更加鲁棒地寻找数据中一致模式,并同时具备良好泛化能力的问题 ,缺少深入思考。

所以有时候我们期望通过盲目地调参,来使模型效果更好:

而以上这些环节,相比与模型本身什么结构来讲,同样重要,所以也不一定要一味地追求一个花里胡哨 、千篇一律的网络结构设计

在这里,我们从一个新颖的角度来考虑关于如何处理数据、设计模型。 我的数据处理与设计模型遵循了这样一个原则:尽可能同时消除数据集中数据和标签中的噪声信息对于训练模型、测试模型时的负面影响。 我们的具体目标为: 1.在训练模型时,尝试给模型制造一个在测试或泛化识别时所面对的相似的噪声干扰情景。(比如,训练时使输入的人体bbox尺寸尽可能一致) 2.在测试模型时,通过一些手段将输入数据转化为训练时模型所面对的常见一般形式的数据与标注,即将噪声限制在可控的范围内(测试时按与训练所用的同一准则对检测的bbox进行变换)3.在模型对一般人体形状模式具备一定的泛化识别能力后(比如150 epoches以后),我们不再限制的噪声的正常存在,并通过一些数据增强的手段来引入噪声,来使得模型具备较好的鲁棒性。 说明:以这种原则指导下,对数据处理的方式可以是多样的。并不是代表着只有某些确切的方法才会有效果。

尽管以往的研究声明,让神经网络具备良好的泛化能力,需要噪声的引入,来让模型对噪声的干扰具备一定的鲁棒性 但是近来的研究表明,神经网络的记忆能力会使得模型容易学习到噪声,而使模型对目标模式的识别出现偏差,因此,如何引入、处理、丢弃噪声,与网络结构设计同等重要,尤其在人体姿态识别任务中,人体的尺寸、姿态、衣物变化、复杂场景、拥挤遮挡情形,会产生大量的干扰。面对这样的难题,近年来,级联的、深度的、提取多级特征的卷积神经网络,如CPM,FPN,Stacked Hourglass等模型的出现,并采取自上而下或自下而上的方法,即使在给定2D关键点标注的局限信息的情形下,仍旧使得模型在人体2D骨架约束关系的表征拟合能力上有了飞跃式的突破,但目前仍然面临着一些瓶颈,在COCO keypoint挑战赛结果中,就侧面证明了,目前限制模型的性能提升的还是在于由尺寸、遮挡、拥挤带来的数据噪声干扰以及粗略标注上的误差与歧义。 即尽可能同时消除数据集中数据和标签中的噪声信息对于训练模型、测试模型时的负面影响,直觉上讲,此原则几乎覆盖了深度神经模型的设计理念中,如比如模型在ImageNet上预训练好后具备对低层特征的识别能力后再进一步作fine-tune,目的让模型可以“认识”一般的常见物体,再进一步识别人体的精细结构,再比如batch normalization让数据形式更一致,归一化量纲,还有一些旋转缩放的数据增强手段,但这些手段往往是粗略的、盲目使用的,并且是不全面的。有研究表明,我们在网络训练的初期与中期,应该让模型看到的是简单的一般的模式,在网络已经具备目标模式的良好识别能力后,噪声的加入才能更好的保持鲁棒性,也就是不同阶段下的噪声对于模型拟合有着不一样的作用。

Focal loss解决数据不平衡的问题、困难样本挖掘方法似乎与我们的原则有些违背,实则不然。可以说一致性与噪声是矛盾的两个方面,让数据形式的一致或者遵循某种特定的规律,这样一个抽象的原则是一个对抗噪声的准则。

样本更新权重重采样、小loss抛弃,一般面对的是分类问题,而对于人体关键点回归问题,同样试用

ECCV2018的论文Simple baseline for human pose estiamtion以一个非常简单的网络的结构,在COCO test数据集上达到了74.3AP的结果,取得了2018 keypoint challange的第二名。如此优越的性能也侧面反映了,对于数据处理方式的重视与网络结构设计同样重要。 接下来我主要来讨论我关于人体姿态估计对于数据预处理的问题和自己的设计想法!

In [3]:
import cv2
from PIL import Image
import torch
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import json

针对人体姿态关键点回归问题 一般由两种方式: 对于自上而下的方式,将多人的姿态估计问题,通过目标检测手段,转换为单人姿态估计问题。 其主要方法就是,通过目标检测,得到图像中可能是人体的bbox,然后将该区域作为网络输入,进行训练(训练时采用GT_bbox)与测试!

拿在COCO_val2017数据上的 人体 bbox ( Average Precision : AP=0.56)的结果,来分析自上而下方法的网络输入应该怎么设计! :)

In [2]:
detection_bbox_file="COCO_val2017_detections_AP_H_56_person.json"
with open(detection_bbox_file,'r') as bbox_file:
    annot=json.loads(bbox_file.readline())
In [3]:
print(annot[0])

#统计图像id
img_list=[]
for i in annot:
    if i['image_id'] not in img_list:
        img_list.append(i['image_id'])

img_id=annot[0]['image_id']
print("img_id:",str(img_id))
print(img_list[0],img_list[1])
{'category_id': 1, 'bbox': [249.8199079291458, 175.21093805640606, 74.00419360691592, 55.626325589288854], 'score': 0.9992738366127014, 'image_id': 532481}
img_id: 532481
532481 66231
In [4]:
img_cv=cv2.imread(str(img_id).zfill(12)+'.jpg')
img=Image.open(str(img_id).zfill(12)+'.jpg')
pos=annot[0]['bbox']

a,b=max(0,pos[0]),max(0,pos[1])
c,d=min(img.size[1],pos[0]+pos[2]),min(img.size[0],pos[1]+pos[3])
                              
cv2.rectangle(img_cv, (int(a),int(b)), (int(c),int(d)), (226,43,138), 2)
#img=Image.fromarray(cv2.cvtColor(img_cv,cv2.COLOR_BGR2RGB))
#img.save(str(img_id)+'_x.jpg')
plt.imshow(img_cv)
plt.show()
print(pos)
[249.8199079291458, 175.21093805640606, 74.00419360691592, 55.626325589288854]
In [5]:
#crop(PIL.Image,y,x,h,w) !! note the order
img=transforms.functional.crop(img,pos[1],pos[0],pos[3],pos[2])  
plt.imshow(img)
plt.show()
print(img.size[0],img.size[1])
74 56
In [6]:
input_resolution=256
x_pad=256-img.size[0]
y_pad=256-img.size[1]
left_pad=int(x_pad/2)
up_pad=int(y_pad/2)
print("padding:{},{},{},{}".format(left_pad,up_pad,x_pad-left_pad,y_pad-up_pad))
Padding1=transforms.Compose([transforms.Pad((left_pad,up_pad,x_pad-left_pad,y_pad-up_pad))])
Padding2=transforms.Compose([transforms.Pad((0,0,x_pad,y_pad))])
img1=Padding1(img)
img2=Padding2(img)
fig=plt.figure(figsize=(10,10))
fig.add_subplot(1,2,1)

plt.imshow(img1)
fig.add_subplot(1,2,2)
plt.imshow(img2)

plt.show()
padding:91,100,91,100

如何确定网络的输入尺寸大小?这个需要参考数据集中人体大小的下限和上限

考虑到:COCO keypoints不考虑32x32大小以下的,那么32x32可以作为人体尺寸的下限

那么,我们接下来可以统计Val 2017数据集中人体bbox的尺寸来确定一个上限

In [7]:
import json
import numpy as np
with open('person_keypoints_val2017.json','r') as json_file:
            preds = json.loads(json_file.readline())
invisible_num_keypoints=0
total_keypoints=0
total_labeled_people=0
total_people=0
half_labeled_people=0
bbox_w_max=0
bbox_h_max=0
bbox_w_max_id=0
bbox_h_max_id=0
max_area=0
for i in preds['annotations']:

    total_people += 1
    if  i['num_keypoints'] != 0 and i['area'] > max_area:
        max_area = i['area']
        max_area_id = i['image_id']
        max_area_w=i['bbox'][2]
        max_area_h = i['bbox'][3]
        kp = i['keypoints']
    if i['num_keypoints'] != 0 and i['bbox'][2] > bbox_w_max:
        bbox_w_max = i['bbox'][2]
        bbox_w_max_id = i['image_id']
        w_max__area=i['area']
        h = i['bbox'][3]
    if i['num_keypoints'] != 0 and  i['bbox'][3]>bbox_h_max:
        bbox_h_max=i['bbox'][3]
        bbox_h_max_id=i['image_id']
        h_max__area=i['area']
        w = i['bbox'][2]
    if i['num_keypoints'] != 0:

        total_labeled_people += 1
        total_keypoints = total_keypoints + i['num_keypoints']

        if i['num_keypoints'] <= 8:
            half_labeled_people += 1

        keypoints=(i['keypoints'])
        keypoints=np.array(keypoints).reshape(-1,3)
        for p in keypoints:
            if p[2]==1:
                
                invisible_num_keypoints += 1
                #print(p)
                
                #print('image_id:{},id:{}'.format(i["image_id"],i['id']))

print("总人数 = {}".format(total_people))
print("有标记总人数 = {}".format(total_labeled_people))
print("部分标记(<8)的点 = {}".format(half_labeled_people))
print("总标记点 = {}".format(total_keypoints))
print("不可见的标记点 = {}".format(invisible_num_keypoints))
print("有标记人数 / 总人数 = {} ".format(float(total_labeled_people)/float(total_people)))
print("部分标记人数 / 标记人数 = {}".format(float(half_labeled_people)/float(total_labeled_people)))
print("不可见点 / 总标记点 = {}".format((float(invisible_num_keypoints)/float(total_keypoints))))

print("bbox_w_max = {}, id={}, area={},h={}".format(bbox_w_max,bbox_w_max_id,w_max__area,h))
print("bbox_h_max = {}, id={}, area={},w={}".format(bbox_h_max,bbox_h_max_id,h_max__area,w))
print("max_area = {}, id = {}, w={}, h={}".format(max_area,max_area_id,max_area_w,max_area_h))
总人数 = 11004
有标记总人数 = 6352
部分标记(<8)的点 = 1861
总标记点 = 68215
不可见的标记点 = 8365
有标记人数 / 总人数 = 0.5772446383133406 
部分标记人数 / 标记人数 = 0.2929785894206549
不可见点 / 总标记点 = 0.12262698819907646
bbox_w_max = 640, id=214869, area=235886.27775,h=471.37
bbox_h_max = 640, id=276018, area=36036.7234,w=130.88
max_area = 266844.89135, id = 343706, w=640, h=487.09
In [8]:
imgs=[bbox_w_max_id,bbox_h_max_id,max_area_id]
#imgs=[39914,64718,463542]
### draw bboxes  and keypoints in imgs
part_labels = ['nose','eye_l','eye_r','ear_l','ear_r',
               'sho_l','sho_r','elb_l','elb_r','wri_l','wri_r',
               'hip_l','hip_r','kne_l','kne_r','ank_l','ank_r']
part_idx = {keypoint_name:idx for idx, keypoint_name in enumerate(part_labels)}  

def draw_limbs(inp, pred,BGR):  #pred=[x1,y1,1,x2,y2,1,...,x17,y17,1]
    def link(a, b, color):
        if part_idx[a] < pred.shape[0] and part_idx[b] < pred.shape[0]:
            
            a = pred[part_idx[a]]     #[x,y,1] 
            b = pred[part_idx[b]]     #[x,y,1]
            
            if a[2]>0.07 and b[2]>0.07:
                cv2.line(inp, (int(a[0]), int(a[1])), (int(b[0]), int(b[1])), color, 2,cv2.LINE_AA)

    pred = np.array(pred).reshape(-1, 3)   #array([[x1,y1,1],[],[x17,y17,1]]]
    bbox = pred[pred[:,2]>0]
    a, b, c, d = bbox[:,0].min(), bbox[:,1].min(), bbox[:,0].max(), bbox[:,1].max()
    
    #cv2.rectangle(inp, (int(a), int(b)), (int(c), int(d)), (255, 255, 255), 1)
    (B,G,R) = BGR
    link('nose', 'eye_l', (B,G,R))
    link('eye_l', 'eye_r', (B,G,R))
    link('eye_r', 'nose', (B,G,R))

    link('eye_l', 'ear_l', (B,G,R))
    link('eye_r', 'ear_r', (B,G,R))

    link('ear_l', 'sho_l', (B,G,R))
    link('ear_r', 'sho_r', (B,G,R))
    link('sho_l', 'sho_r', (B,G,R))
    link('sho_l', 'hip_l', (B,G,R))
    link('sho_r', 'hip_r', (B,G,R))
    link('hip_l', 'hip_r', (B,G,R))

    link('sho_l', 'elb_l', (B,G,R))
    link('elb_l', 'wri_l', (B,G,R))

    link('sho_r', 'elb_r', (B,G,R))
    link('elb_r', 'wri_r', (B,G,R))

    link('hip_l', 'kne_l', (B,G,R))
    link('kne_l', 'ank_l', (B,G,R))

    link('hip_r', 'kne_r', (B,G,R))
    link('kne_r', 'ank_r', (B,G,R))
    
fig=plt.figure(figsize=(50,50))
for idx,img_id in enumerate(imgs):
    img_xxx=cv2.imread('E:/yangsen/ysstronger/pose/val2017/'+str(img_id).zfill(12)+'.jpg')
    for i in preds['annotations']:
        if img_id == i['image_id'] and i['num_keypoints'] >=2 :
            keypoints=i['keypoints']
            pos=i['bbox']
            print(pos)
            a,b=max(0,pos[0]),max(0,pos[1])
            c,d=min(img_xxx.shape[1],pos[0]+pos[2]),min(img_xxx.shape[0],pos[1]+pos[3])
            B,G,R=int(a*255/img_xxx.shape[1]),int(b*255/img_xxx.shape[0]),int(d*165/img_xxx.shape[0])
            draw_limbs(img_xxx,keypoints,(B,G,R))
            cv2.rectangle(img_xxx, (int(a),int(b)), (int(c),int(d)), (B,G,R), 3)
            #cv2.putText(img_xxx, 'score:{}'.format(1), (int(a),int(b)+20), cv2.FONT_HERSHEY_TRIPLEX, 1, (255, 255, 0), 1, False)
    cv2.imwrite(str(img_id)+'.jpg',img_xxx)
    fig.add_subplot(1,len(imgs),idx+1)
    img_xxx=cv2.cvtColor(img_xxx, cv2.COLOR_BGR2RGB)
    plt.imshow(img_xxx)
plt.show()
#img=Image.fromarray(cv2.cvtColor(img_cv,cv2.COLOR_BGR2RGB))
#img.save(str(img_id)+'_x.jpg')
[0, 3.31, 640, 471.37]
[147.55, 22.12, 121.36, 256.7]
[120.78, 34.73, 163.38, 171.11]
[256, 17.26, 122.25, 240.18]
[66.16, 115.06, 70.47, 165.39]
[377, 17.31, 39, 147.35]
[153.96, 125.1, 262.04, 426.91]
[109, 190.23, 110.19, 83.14]
[23.31, 251.96, 180.36, 388.04]
[142.56, 254.04, 116.24, 381.07]

网络输入 尺寸处理与bbox剪裁

  1. 根据结果可以看出val2017 大尺度的人体在600x600附近 像素区域较大 一般的3x3卷积或者5x5卷积提取低层特征 难度较大 。
  2. 根据以往的经验,模型在96x96以上的人体尺寸表现较良好

那么, 我们可以先设置 256x256 大小的输入,对于大尺寸的人体采取缩小的操作(信息损失较少),小尺寸的人体采取上采样(是直接插值法还是,弥补细节插值法?)

接下来,我们要做的就是将图像中较大和较小尺寸的人体bbox区域 尽可能地合适地填充到256x256像素区域的正中心(如果放在左上角好吗?) 对图像的操作主要涉及到:平移、缩小或放大、旋转(数据增强手段),这一系列都可以通过:

图像像素齐次坐标的仿射变换

仿射:$M=\begin{bmatrix} a & b & tx\\ c & d & ty \\ 0 & 0 & 1 \end{bmatrix}$ 平移: $M=\begin{bmatrix}1 & 0 & tx\\ 0 & 1 & ty \\ 0 & 0 & 1\end{bmatrix}$ 缩放: $M=\begin{bmatrix}sx & 0 & 0\\ 0 & sy & 0 \\ 0 & 0 & 1\end{bmatrix}$ 旋转: $M=\begin{bmatrix}\cos \theta & -\sin \theta & 0\\ \sin \theta & \cos \theta & 0 \\ 0 & 0 & 1\end{bmatrix}$

opencv里面有一个 cv2.wrapAffine(Image,AffineMatrix,(rows,cols))可以实现这个操作

比如拿上面第二幅图片来做实验,我们的目标就是,将一副图中,以某个bbox的中心位置为图像中心,将bbox区域放在256x256的图像中心,并进行一定的缩放与旋转操作,同时我们还要将原始图像的keypoints坐标进行相同的仿射变换。我们的操作必须针对所有数据具有一般性。接下来的工作就是来构造 变换矩阵

我们先跟据原图bbox的大小来决定缩放尺度scale,然后将缩放后的图像种某个bbox的中心平移到256x256的图像中心

$resolution=(256,256), bbox=[x,y,w,h], tx=res/2-s*(x+w/2), ty=res/2-s*(y+h/2),$

$\begin{bmatrix}{x}'\\ {y}'\\ 1 \end{bmatrix}=\begin{bmatrix}1 & 0 & tx\\ 0 & 1 & ty \\ 0 & 0 & 1\end{bmatrix}\cdot \begin{bmatrix}s&0&0\\ 0&s&0\\ 0&0& 1\end{bmatrix}\cdot \begin{bmatrix}x\\ y\\ 1\end{bmatrix}=\begin{bmatrix}s&0&tx\\ 0&s&ty\\ 0&0&1\end{bmatrix}\cdot \begin{bmatrix}x\\ y\\ 1\end{bmatrix}$

In [9]:
import json
import numpy as np
with open('person_keypoints_val2017.json','r') as json_file:
            preds = json.loads(json_file.readline())
        
def Affine_transform(img,bbox,keypoints_orig,scale=1,size=(256,256)):
    # bbox should > 32*32
    w,h=size[0],size[1]              
    if bbox[2]/h >=1 or bbox[3]/h>=1:
        scale=h*0.75/float(max(bbox[2],bbox[3]))
        print("reduce {}x from size larger than 256x256".format(scale))
        
    if bbox[2]<32 and bbox[3]<32:
        assert "bbox cannot be smaller than 32x32"
    if bbox[2]<96 and bbox[3]<96:
        scale=2.5
        print("zoom 2.5x from size unedr 96x96")
    t = np.zeros((3, 3)) 
    offset_X=int(w/2)-int(scale*(bbox[0]+bbox[2]/2))
    offset_Y=int(h/2)-int(scale*(bbox[1]+bbox[3]/2))
    t[0, 0] = scale
    t[1, 1] = scale
    t[0, 2] = offset_X
    t[1, 2] = offset_Y
    t[2, 2] = 1
    
    keypoints = np.array(keypoints_orig).reshape(-1, 3)
    for i in range(len(keypoints)):
        if keypoints_orig[i*3+2]==2:
            keypoints[i][2]=1
    keypoints_affine=[np.dot(t,i) for i in keypoints]
    keypoints_affine=np.array(keypoints_affine).reshape(17*3)
    img=cv2.warpAffine(img,t[[0,1],:],size)
    #print(img.dtype)
    
    if img.dtype == np.float32:
        img=img*255
        img=img.astype(np.int32)
    draw_limbs(img, keypoints_affine,(255,0,0))
    for i in range(len(keypoints)):
        if keypoints_orig[i*3+2]==2:
            keypoints_affine[i*3+2]=2
    
    return img,keypoints_affine   

def bbox_resize(img,bbox,keypoints,margin=0):
    kps = np.array(keypoints).reshape(-1, 3)   #array([[x1,y1,1],[],[x17,y17,1]]]
    border = kps[kps[:,2]==2] #标注点kps[kps[:,2]>0],或者直接考虑可见点的位置,那么kps[kps[:,2]==2] 
    a, b = min(border[:,0].min(),bbox[0]), min(border[:,1].min(), bbox[1])
    c, d = max(border[:,0].max(),bbox[0]+bbox[2]), max(border[:,1].max(),bbox[1]+bbox[3])
    assert abs(margin)<20 ,"margin is too large"
    a,b,c,d=max(0,a-margin),max(0,b-margin),min(img.shape[1],c+margin),min(img.shape[0],d+margin)
    ###因为原来bbox只覆盖了人体可见区域,有些关键点不可见但标注的是否该按照这样的点扩大,会导致噪声特征产生,需要记录一下吧##
    return [a,b,c-a,d-b]
    
#orig_img_id=39914
#orig_img_id=1000
orig_img_id=276018
orig_img=cv2.imread('E:/yangsen/ysstronger/pose/val2017/'+str(orig_img_id).zfill(12)+'.jpg')
orig_img = cv2.cvtColor(orig_img, cv2.COLOR_BGR2RGB)

for i in preds['annotations']:
    if orig_img_id == i['image_id'] and i['num_keypoints'] !=0 :
        bbox=i['bbox']
        keypoints=i['keypoints']
        print(keypoints)
        ###  根据关键点位置重新确定bbox边缘,防止某些关键点漏在外面
        bbox=bbox_resize(orig_img,bbox,keypoints,0)
        ###  稍微扩大bbox区域,引入更多信息
        
        roi_mask=np.zeros(shape=(orig_img.shape[0],orig_img.shape[1],3),dtype=np.float32)
        roi_mask[int(bbox[1]):int(bbox[1]+bbox[3]),int(bbox[0]):int(bbox[0]+bbox[2]),:]=1
        print(roi_mask.shape)
        orig_img_x=orig_img.astype(np.float32)
        
        orig_img_mask=orig_img_x*roi_mask
        
        xx=orig_img_mask/255
        #print(orig_img_mask.dtype)
        
        print(bbox)
        affine_img,keypoints=Affine_transform(xx,bbox,keypoints,size=(196,256))               
        print(keypoints)
        #plt.figure(figsize=(2.56,2.56))

        plt.imshow(affine_img)
        plt.show()
[177, 72, 1, 183, 67, 2, 165, 67, 1, 203, 73, 2, 149, 71, 1, 230, 119, 2, 139, 121, 1, 281, 173, 1, 0, 0, 0, 195, 177, 1, 0, 0, 0, 228, 244, 1, 142, 244, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(640, 416, 3)
[147.55, 22.12, 121.36000000000001, 256.7]
reduce 0.7479548110634983x from size larger than 256x256
[ 75.38800156  69.8527464    1.          79.87573042  66.11297234
   2.          66.41254383  66.11297234   1.          94.83482665
  70.60070121   2.          54.44526685  69.10479159   1.
 115.02960654 105.00662252   2.          46.96571874 106.50253214
   1.         153.17530191 145.39618231   1.           0.
   0.           0.          88.85118816 148.38800156   1.
   0.           0.           0.         113.53369692 198.5009739
   1.          49.20958317 198.5009739    1.           0.
   0.           0.           0.           0.           0.
   0.           0.           0.           0.           0.
   0.        ]
[177, 71, 2, 184, 66, 1, 165, 67, 2, 0, 0, 0, 0, 0, 0, 225, 126, 1, 138, 127, 2, 269, 177, 2, 0, 0, 0, 218, 176, 2, 0, 0, 0, 197, 237, 1, 139, 236, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(640, 416, 3)
[120.78, 34.73, 163.37999999999997, 171.11]
[ 73.  79.   2.  80.  74.   1.  61.  75.   2.   0.   0.   0.   0.   0.
   0. 121. 134.   1.  34. 135.   2. 165. 185.   2.   0.   0.   0. 114.
 184.   2.   0.   0.   0.  93. 245.   1.  35. 244.   1.   0.   0.   0.
   0.   0.   0.   0.   0.   0.   0.   0.   0.]
[346, 67, 1, 352, 57, 2, 336, 57, 2, 363, 59, 2, 0, 0, 0, 378, 100, 1, 310, 106, 2, 0, 0, 0, 274, 161, 1, 0, 0, 0, 295, 103, 2, 0, 0, 0, 324, 234, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(640, 416, 3)
[256, 17.26, 122.25, 240.18]
[127.  58.   1. 133.  48.   2. 117.  48.   2. 144.  50.   2.   0.   0.
   0. 159.  91.   1.  91.  97.   2.   0.   0.   0.  55. 152.   1.   0.
   0.   0.  76.  94.   2.   0.   0.   0. 105. 225.   2.   0.   0.   0.
   0.   0.   0.   0.   0.   0.   0.   0.   0.]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 111, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(640, 416, 3)
[0, 0, 130.88, 640]
reduce 0.3x from size larger than 256x256
[ 0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.
  0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.
  0.   0.   0.   0.   0.  81.1 65.3  2.   0.   0.   0.   0.   0.   0.
  0.   0.   0.   0.   0.   0.   0.   0.   0. ]
[100, 161, 2, 110, 155, 2, 91, 155, 2, 125, 166, 2, 78, 169, 1, 150, 223, 1, 57, 224, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(640, 416, 3)
[66.16, 115.06, 70.47, 165.39]
[ 97.  92.   2. 107.  86.   2.  88.  86.   2. 122.  97.   2.  75. 100.
   1. 147. 154.   1.  54. 155.   1.   0.   0.   0.   0.   0.   0.   0.
   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.
   0.   0.   0.   0.   0.   0.   0.   0.   0.]
[405, 65, 2, 413, 63, 2, 399, 60, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(640, 416, 3)
[377, 17.31, 39, 147.35]
[107. 103.   2. 115. 101.   2. 101.  98.   2.   0.   0.   0.   0.   0.
   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.
   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.   0.
   0.   0.   0.   0.   0.   0.   0.   0.   0.]
[374, 171, 2, 385, 164, 2, 363, 162, 2, 400, 174, 2, 354, 167, 1, 0, 0, 0, 338, 220, 2, 402, 281, 2, 0, 0, 0, 365, 266, 2, 0, 0, 0, 397, 351, 2, 346, 344, 2, 0, 0, 0, 336, 435, 2, 0, 0, 0, 346, 528, 2]
(640, 416, 3)
[153.96, 125.1, 262.03999999999996, 426.90999999999997]
reduce 0.44974350565693005x from size larger than 256x256
[138.20407112  52.90613947   2.         143.15124968  49.75793493
   2.         133.25689255  48.85844792   2.         149.89740226
  54.25536998   2.         129.209201    51.10716544   1.
   0.           0.           0.         122.01330491  74.94357124
   2.         150.79688927 102.37792509   2.           0.
   0.           0.         134.15637956  95.6317725    2.
   0.           0.           0.         148.54817175 133.85997049
   2.         125.61125296 130.71176595   2.           0.
   0.           0.         121.1138179  171.63842496   2.
   0.           0.           0.         125.61125296 213.46457099
   2.        ]
[166, 174, 1, 180, 168, 1, 160, 161, 1, 205, 176, 1, 0, 0, 0, 214, 223, 1, 161, 210, 2, 226, 285, 1, 0, 0, 0, 193, 253, 1, 131, 238, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(640, 416, 3)
[109, 190.23, 110.19, 83.14000000000001]
[100.  71.   1. 114.  65.   1.  94.  58.   1. 139.  73.   1.   0.   0.
   0. 148. 120.   1.  95. 107.   2. 160. 182.   1.   0.   0.   0. 127.
 150.   1.  65. 135.   1.   0.   0.   0.   0.   0.   0.   0.   0.   0.
   0.   0.   0.   0.   0.   0.   0.   0.   0.]
[104, 320, 2, 117, 309, 2, 92, 304, 2, 146, 312, 2, 84, 311, 1, 151, 358, 2, 85, 359, 2, 179, 430, 2, 33, 420, 1, 132, 429, 2, 67, 438, 2, 132, 519, 2, 80, 520, 2, 138, 618, 2, 76, 626, 2, 0, 0, 0, 0, 0, 0]
(640, 416, 3)
[23.31, 251.96, 180.36, 388.03999999999996]
reduce 0.494794351097825x from size larger than 256x256
[ 93.45861251  66.33419235   2.          99.89093908  60.89145449
   2.          87.5210803   58.41748273   2.         114.23997526
  62.37583754   2.          83.56272549  61.88104319   1.
 116.71394702  85.13637769   2.          84.05751984  85.63117204
   2.         130.56818885 120.76157097   2.          58.32821359
 115.81362746   1.         107.31285434 120.26677662   2.
  75.15122152 124.71992578   2.         107.31285434 164.79826822
   2.          81.58354809 165.29306257   2.         110.28162045
 213.78290898   2.          79.60437068 217.74126379   2.
   0.           0.           0.           0.           0.
   0.        ]
[174, 306, 2, 184, 298, 2, 166, 298, 2, 204, 300, 2, 0, 0, 0, 215, 340, 2, 149, 341, 2, 250, 391, 2, 138, 391, 1, 212, 389, 2, 178, 395, 1, 206, 446, 2, 163, 449, 1, 204, 527, 2, 167, 525, 2, 212, 587, 2, 169, 595, 2]
(640, 416, 3)
[142.56, 254.04, 116.24000000000001, 381.07000000000005]
reduce 0.5038444380297582x from size larger than 256x256
[ 84.66893222  59.17639804   2.          89.7073766   55.14564253
   2.          80.63817671  55.14564253   2.          99.78426536
  56.15333141   2.           0.           0.           0.
 105.32655418  76.30710893   2.          72.07282127  76.81095337
   2.         122.96110951 102.00317527   2.          66.53053245
 102.00317527   1.         103.81502086 100.99548639   2.
  86.68430997 104.01855302   1.         100.79195423 129.71461936
   2.          79.1266434  131.22615268   1.          99.78426536
 170.52601884   2.          81.14202115 169.51832997   2.
 103.81502086 200.75668512   2.          82.14971003 204.78744063
   2.        ]

从截出的bbox我们可以看到有部分的bbox边界没有很好的贴到人体边缘,甚至出现大面积包含了其他人体的bbox,还有一些bbox漏掉了属于该bbox的人体关键点(如果该点可见则噪声产生,标注但不可见则噪声没有产生),那么针对这两种标注中产生的噪声干扰情况,我们是否可以利用keypoints的坐标信息来调整bbox的位置,来降低干扰?我们设计了这样的函数bbox_resize()来重新调整bbox(考虑到训练时,利用给定keypoint的heatmap来训练,其实这个bbox的不准情况可以忽略掉,我们甚至可以采取,抛弃小loss的方式来减少这类噪声干扰)

我想到一种方式,因为训练时我们把bbox放在正中心,那么我们可以在中心位置产生一个高斯权重系数,对于边缘位置出现的干扰点,降低其影响。

我们大体上已经把一副图像人体bbox的尺寸调整到合适的位置和大小,另外需要处理的就是像素数据了,以何种数值与形式喂给模型,可能是一件需要认真考虑的事情

In [10]:
def preprocess(data):
        # random hue and saturation
        data = cv2.cvtColor(data, cv2.COLOR_RGB2HSV);
        delta = (np.random.random() * 2 - 1) * 0.2
        data[:, :, 0] = np.mod(data[:,:,0] + (delta * 360 + 360.), 360.)

        delta_sature = np.random.random() + 0.5
        data[:, :, 1] *= delta_sature
        data[:,:, 1] = np.maximum( np.minimum(data[:,:,1], 1), 0 )
        data = cv2.cvtColor(data, cv2.COLOR_HSV2RGB)

        # adjust brightness
        delta = (np.random.random() * 2 - 1) * 0.3
        data += delta

        # adjust contrast
        mean = data.mean(axis=2, keepdims=True)
        data = (data - mean) * (np.random.random() + 0.5) + mean
        data = np.minimum(np.maximum(data, 0), 1)
        #cv2.imwrite('x.jpg', (data*255).astype(np.uint8))
        return data
imgs=[39914,64718,463542]
img_x=cv2.imread('E:/yangsen/ysstronger/pose/val2017/'+str(64718).zfill(12)+'.jpg')
img2x=preprocess(img_x.astype(np.float32)/255)
img_x = cv2.cvtColor(img_x, cv2.COLOR_BGR2RGB)
plt.figure('z')
plt.imshow(img2x)
plt.figure('x')
plt.imshow(img_x)
plt.show()

我们所做的一切可以归纳为:采取手段来对抗数据本身所携带的噪声信息和标注时产生的噪声。具体目标为: 1.在训练模型时,尝试给模型制造一个在测试或泛化识别时所面对的噪声干扰情景。 2.在测试模型时,通过一些手段将输入数据转化为训练时模型所面对的常见一般形式的数据与标注,即将噪声限制在可控的范围内。

另外,噪声的定义可以是十分灵活的,跟我们识别目标无关的信息都可被定义为噪声,但噪声教会模型的是如何学会去除噪声,如何在无序的噪声中寻找一致性的模式。可以说噪声与模式同时存在于数值特征中,任何模式识别的主要任务就是从无序中寻找有序的信息

我们希望模型对人体姿态的一般模式具备泛化识别能力,并且面对一些未知的噪声干扰,模型依旧可以从一些比较强的人体肢体信息,找到最有可能存在的人体位置。以上可以总结为,我们期望深度神经网络模型对于一般的人体姿态和少量干扰下的姿态具备鲁棒的拟合能力。

而有些时候,一些极端并且大量存在的情况,是以鲁棒拟合为目的模型所不能处理的,比如靠推理才能解决的严重遮挡,这种都被数据集评价指标所避免的东西,所以当我们在COCO,MPII数据集上达到一个较好的性能时,这意味着研究者所使用的模型具备了一般人体姿态模式的识别能力,模型本身或者可以采取一些方法能够对较好地面对测试时的干扰噪声,但并不代表着,人类视觉推理功能的具备,比如,我们可以根据部分肢体与场景,来推理遮挡的点,而场景信息是模型训练完全抛弃的内容。

这种人类视觉的识别是必须需要假设推理,我们事先推出一个置信度信息,然后从人体骨架的先验分布中,结合场景知识,推断出不可见的肢体,其中不可忽略的一点是,我们的先验知识是建立在3维空间中的,很多种遮挡情况,在1个3维空间中就可以别良好地应对,我们还可以利用一些场景中的道具进行推理,比如领带、鞋子、帽子等等。信息是多重利用的,而神经网络的本质在于忽略噪声信息,保留一致性的信息,它仅仅提供的是一个先验知识的表达拟合能力,而我们的智能识别就不能够单纯依赖神经网络的拟合能力。