《Learning to Estimate 3D Hand Pose from Single RGB Images》代码运行阅读笔记
读懂这个文章的代码,并能在自己的照片上测试
环境搭建
环境是很久以前搭的,已经忘了。不过按照github中的readme一步一步来基本没问题。
数据集
根据文章中所讲目前可用的带关键点位置的RGB数据集有:Steror和Dexter。NYU数据集主要用于深度图不作考虑。不过这两个数据集太小,而且人工标注不够精确,所以用模型生成的数据做一个补充(工具:Mixamo、Blender、背景)。一共有320*320大小的41258张训练图像和2728张测试图像。
值得注意的是文章中说有深度图的数据和分割标签。
网络训练
训练分三个阶段,HandSegNet对应程序中的training_handsegnet.py,PoseNet对应程序中的training_posenet.py,PosePrior、ViewPoint对应程序中的training_lifting.py
-
training_handsegnet.py
1:训练数据读取
创建一个dataset对象,其中存放了读取数据需要的信息,比较重要的有:batch大小、噪音设置、图片大小320*320、样本数量41258、数据集路径rhd、shuffle是否打乱顺序等信息。
用dataset.get()读取需要的数据。
2:搭建网络
搭建网络的各种层的具体操作封装在NetworkOps类里面。
网络输入:图像8*256*256*3和权重(true or false)是否更改
网络输出:8*256*256*2的特征,与标签mask一致
数据读取操作和根据图片都是用tensorflow的计算图进行的,分别封装在dataset类和ColorHandPose3DNetwork类里面
# build network
evaluation = tf.placeholder_with_default(True, shape=())
net = ColorHandPose3DNetwork()
hand_mask_pred = net.inference_detection(data['image'], train=True)
3:开启session
# Start TF
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.8)
sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))
tf.train.start_queue_runners(sess=sess)
4:逐点计算loss,计算梯度,设置学习率
# Loss
loss = 0.0
s = data['hand_mask'].get_shape().as_list()
for i, pred_item in enumerate(hand_mask_pred):
gt = tf.reshape(data['hand_mask'], [s[0]*s[1]*s[2], -1])
pred = tf.reshape(hand_mask_pred, [s[0]*s[1]*s[2], -1])
loss += tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=gt))
# Solver
global_step = tf.Variable(0, trainable=False, name="global_step")
lr_scheduler = LearningRateScheduler(values=train_para['lr'], steps=train_para['lr_iter'])
lr = lr_scheduler.get_lr(global_step)
opt = tf.train.AdamOptimizer(lr)
train_op = opt.minimize(loss)
5:初始化权重,加载参数
# init weights
sess.run(tf.global_variables_initializer())
saver = tf.train.Saver(max_to_keep=1, keep_checkpoint_every_n_hours=4.0)
rename_dict = {'CPM/PersonNet': 'HandSegNet',
'_CPM': ''}
load_weights_from_snapshot(sess, './weights/cpm-model-mpii', ['PoseNet', 'Mconv', 'conv6'], rename_dict)
-
training_posenet.py和training_lifting.py也是类似的操作顺序
网络测试
修改run.py,把输入数据换成自己的摄像头获取的数据,把显示改成连续显示。连接自己的摄像头实验效果如下:
1:相机使用
把相机插上,然后运行如下代码,应该就可以获取了。
import cv2
cap = cv2.VideoCapture(0)
_,image_raw = cap.read()
2:图像整形
将获取的图像缩放到240**320大小,并归一化到0~1范围。
image_raw = scipy.misc.imresize(image_raw, (240, 320))
image_v = np.expand_dims((image_raw.astype('float') / 255.0) - 0.5, 0)
3:网络计算
整个流程: HandSegNet + PoseNet + PosePrior. Inputs: image: [B, H, W, 3] tf.float32 tensor, Image with mean subtracted hand_side: [B, 2] tf.float32 tensor, one hot encoding 需要限制为左手 evaluation: [] tf.bool tensor, True while evaluation false during training (controls dropout) Outputs: hand_scoremap: [B, H, W, 2] 像素点是否属于手部的得分 image_crop: [B, 256, 256, 3] 切出来的手部图像,并进行缩放 scale_crop: [B, 1] 切分出来的手部相对于原图的大小 center: [B, 1] 切出来的手部中心位置 keypoints_scoremap: [B, 256, 256, 21] 每个手部关节点的概率图 keypoint_coord3d: [B, 21, 3] 3D关节点位置
注意,最后2D和3D之间,不仅仅是相机矩阵转换和预测3D点位置而已,3D的似乎更符合运动学规律。