GCN图卷积神经网络入门讲解+实战结印识别–详细注释解析恩培作品4

感谢 恩培大佬对项目进行了完整的实现,并将代码进行开源,供大家交流学习。

一、项目简介

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

项目用 python实现,调用opencv,mediapipe,pytorch等库,由以下步骤组成:

1、使用OpenCV读取摄像头视频流;

2、识别手掌关键点像素坐标;

3、根据识别得到的手掌关键点信息,以图的方式构建数据结构;

4、用Pytorch提供的GCN图卷积神经网络训练数据并手势进行分类;

二、知识拆解

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

下面演示以下python版本,手指检测的使用方式。

安装库:

pip install mediapipe

调用示例:

import cv2

import mediapipe as mp

mp_drawing = mp.solutions.drawing_utils

mp_drawing_styles = mp.solutions.drawing_styles

mp_hands = mp.solutions.hands

For static images:

IMAGE_FILES = []

with mp_hands.Hands(

static_image_mode=True,

max_num_hands=2,

min_detection_confidence=0.5) as hands:

for idx, file in enumerate(IMAGE_FILES):

Read an image, flip it around y-axis for correct handedness output (see

above).

image = cv2.flip(cv2.imread(file), 1)

Convert the BGR image to RGB before processing.

results = hands.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))

Print handedness and draw hand landmarks on the image.

print('Handedness:', results.multi_handedness)

if not results.multi_hand_landmarks:

continue

image_height, image_width, _ = image.shape

annotated_image = image.copy()

for hand_landmarks in results.multi_hand_landmarks:

print('hand_landmarks:', hand_landmarks)

print(

f'Index finger tip coordinates: (',

f'{hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP].x * image_width}, '

f'{hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP].y * image_height})'

)

mp_drawing.draw_landmarks(

annotated_image,

hand_landmarks,

mp_hands.HAND_CONNECTIONS,

mp_drawing_styles.get_default_hand_landmarks_style(),

mp_drawing_styles.get_default_hand_connections_style())

cv2.imwrite(

'/tmp/annotated_image' + str(idx) + '.png', cv2.flip(annotated_image, 1))

Draw hand world landmarks.

if not results.multi_hand_world_landmarks:

continue

for hand_world_landmarks in results.multi_hand_world_landmarks:

mp_drawing.plot_landmarks(

hand_world_landmarks, mp_hands.HAND_CONNECTIONS, azimuth=5)

For webcam input:

cap = cv2.VideoCapture(0)

with mp_hands.Hands(

model_complexity=0,

min_detection_confidence=0.5,

min_tracking_confidence=0.5) as hands:

while cap.isOpened():

success, image = cap.read()

if not success:

print("Ignoring empty camera frame.")

If loading a video, use 'break' instead of 'continue'.

continue

To improve performance, optionally mark the image as not writeable to

pass by reference.

image.flags.writeable = False

image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

results = hands.process(image)

Draw the hand annotations on the image.

image.flags.writeable = True

image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

if results.multi_hand_landmarks:

for hand_landmarks in results.multi_hand_landmarks:

mp_drawing.draw_landmarks(

image,

hand_landmarks,

mp_hands.HAND_CONNECTIONS,

mp_drawing_styles.get_default_hand_landmarks_style(),

mp_drawing_styles.get_default_hand_connections_style())

Flip the image horizontally for a selfie-view display.

cv2.imshow('MediaPipe Hands', cv2.flip(image, 1))

if cv2.waitKey(5) & 0xFF == 27:

break

cap.release()

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

什么是图

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

计算机怎么表示图

在计算机表示中,vertex(边)、node(点)、graph(图)都由embedding向量表示。

这里我们可以看到,图神经网络的思想也是,将图的点、边、图用向量来表示。我们需要关心的就是这些向量在数据处理中如何运用,以及我们是否有办法通过数据来学到这些向量的值。

此外,在图的分类中,主要是有向图和无向图两种。

简单说来,你我都是朋友,这叫无向。你喜欢我,而我不喜欢你,这叫有向。

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

其它数据如何表示为图

引入图这种数据结构及其表示方法之后,我们自然有一个疑问,那就是如何将图片,文本等信息,用图来表示呢?

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

同理呀,其实分子、社交关系、文章引用等等,都很容易用图来表示。可见图的应用之广泛。

用图来解决什么问题

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

二是点层面的问题。例子,如下的图中,按照某种标准,将所有的点分为两类。

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

深度学习中运用图,有什么难题

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

其实这个问题,我们稍作分析便不难发现,邻接关系描述的信息本质就是,哪两个点连接了。

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

进入正题,什么是图神经网络

下定义也许不容易理解,只需要有个概念,它是一个神经网络,并具备以下两个特点。

1、输入是一张图,输出也是一张图。

2、对边和顶点向量做一系列变换,但是连接关系不会改变。

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

现在来看一个具体的问题。例如我要对以上图中的某个点做分类,假设这些点的embedding向量已知,那问题便简单了,直接输入一个神经网络做分类问题即可。这和传统的神经网络没有啥区别。

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

有了汇聚的操作,我们就能够通过神经网络来处理点、边的信息,并通过汇聚补全了。但细心的朋友已经发现,这样做并没有完全利用到一个图的信息。那我们如何能做到输入一张图的顶点、边的所有信息呢。

GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4

此时还有一个问题,那就是当图很大的时候,要将远方的点的信息传过来,岂不是需要消耗很长的时间。正因为如此,才提出了一个全局向量U,表示整张图的平均属性。

至此,图神经网络的基本原理便介绍清楚了。

那么GCN呢,讲的其实是在汇聚的过程中,设计K层汇聚网络,每次汇聚都往外看N步,那么每个点的视野范围便是K*N。其实等价于拿出邻接矩阵做乘法。

可以看到,图神经网络的灵活性非常高,基本上所有的数据都可以表示成图。但同时也带来了它的问题,想在这种稀疏的架构上做优化是非常困难的,况且图还是一个动态的架构。

三、实战演练

用mediapipe得到手部框,再用InterHand进行手部关节点的精确识别

InterHand的权重链接:Release InterHand2.6M release · facebookresearch/InterHand2.6M · GitHub Official PyTorch implementation of “InterHand2.6M: A Dataset and Baseline for 3D Interacting Hand Pose Estimation from a Single RGB Image”, ECCV 2020 – Release InterHand2.6M release · facebookresearch/InterHand2.6M GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4https://github.com/facebookresearch/InterHand2.6M/releases/tag/v1.0 ;

更多疑问,欢迎私信交流。thujiang000

输入一个手部图片,返回3D坐标

class HandPose:

def __init__(self):

cfg.set_args('0')

cudnn.benchmark = True

joint set information is in annotations/skeleton.txt

self.joint_num = 21 # single hand

self.joint_type = {'right': np.arange(0,self.joint_num), 'left': np.arange(self.joint_num,self.joint_num*2)}

snapshot load

model_path = './snapshot_19.pth.tar'

assert osp.exists(model_path), 'Cannot find self.hand_pose_model at ' + model_path

print('Load checkpoint from {}'.format(model_path))

self.hand_pose_model = get_model('test', self.joint_num)

self.hand_pose_model = DataParallel(self.hand_pose_model).cuda()

ckpt = torch.load(model_path)

self.hand_pose_model.load_state_dict(ckpt['network'], strict=False)

self.hand_pose_model.eval()

prepare input image

self.transform = transforms.ToTensor()

def get3Dpoint(self,x_t_l, y_t_l, cam_w, cam_h,original_img):

bbox = [x_t_l, y_t_l, cam_w, cam_h] # xmin, ymin, width, height

original_img_height, original_img_width = original_img.shape[:2]

bbox = process_bbox(bbox, (original_img_height, original_img_width, original_img_height))

img, trans, inv_trans = generate_patch_image(original_img, bbox, False, 1.0, 0.0, cfg.input_img_shape)

img = self.transform(img.astype(np.float32))/255

img = img.cuda()[None,:,:,:]

forward

inputs = {'img': img}

targets = {}

meta_info = {}

with torch.no_grad():

out = self.hand_pose_model(inputs, targets, meta_info, 'test')

img = img[0].cpu().numpy().transpose(1,2,0) # cfg.input_img_shape[1], cfg.input_img_shape[0], 3

joint_coord = out['joint_coord'][0].cpu().numpy() # x,y pixel, z root-relative discretized depth

rel_root_depth = out['rel_root_depth'][0].cpu().numpy() # discretized depth

hand_type = out['hand_type'][0].cpu().numpy() # handedness probability

restore joint coord to original image space and continuous depth space

joint_coord[:,0] = joint_coord[:,0] / cfg.output_hm_shape[2] * cfg.input_img_shape[1]

joint_coord[:,1] = joint_coord[:,1] / cfg.output_hm_shape[1] * cfg.input_img_shape[0]

joint_coord[:,:2] = np.dot(inv_trans, np.concatenate((joint_coord[:,:2], np.ones_like(joint_coord[:,:1])),1).transpose(1,0)).transpose(1,0)

joint_coord[:,2] = (joint_coord[:,2]/cfg.output_hm_shape[0] * 2 - 1) * (cfg.bbox_3d_size/2)

restore right hand-relative left hand depth to continuous depth space

rel_root_depth = (rel_root_depth/cfg.output_root_hm_shape * 2 - 1) * (cfg.bbox_3d_size_root/2)

right hand root depth == 0, left hand root depth == rel_root_depth

joint_coord[self.joint_type['left'],2] += rel_root_depth

3D节点信息

return joint_coord

经过三维关节点提取后,我们便可以构建手部的图了。思考一下,我们的任务是对不同的图做分类,边的指向和长度是图的特征。基于特征的特点,我们可以尝试用边来构造这个图。注意手指之间是无向的,要做处理。

首先构造一个两层的图卷积神经网络

class GCN(nn.Module):

def __init__(self, in_feats, h_feats, num_classes):

super(GCN, self).__init__()

self.conv1 = GraphConv(in_feats, h_feats)

self.conv2 = GraphConv(h_feats, num_classes)

def forward(self, g, in_feat):

h = self.conv1(g, in_feat)

h = F.relu(h)

h = self.conv2(g, h)

g.ndata['h'] = h

return dgl.mean_nodes(g, 'h')

u、v分别为图的起点和终点。

然后按照每个点对于手掌的中心坐标的相对坐标为特征,输入原始图中。

构造图以及特征

u,v = torch.tensor([[0,0,0,0,0,4,3,2,8,7,6,12,11,10,16,15,14,20,19,18,0,21,21,21,21,21,25,24,23,29,28,27,33,32,31,37,36,35,41,40,39],

[4,8,12,16,20,3,2,1,7,6,5,11,10,9,15,14,13,19,18,17,21,25,29,33,37,41,24,23,22,28,27,26,32,31,30,36,35,34,40,39,38]])

g = dgl.graph((u,v))

无向处理

bg = dgl.to_bidirected(g)

计算相对坐标

x_y_z_column = self.relativeMiddleCor(x_list, y_list,z_list)

添加特征

bg.ndata['feat'] =torch.tensor( x_y_z_column ) # x,y,z坐标

测试模型

device = torch.device("cuda:0")

device = torch.device("cpu")

bg = bg.to(device)

self.modelGCN = self.modelGCN.to(device)

pred = self.modelGCN(bg, bg.ndata['feat'].float())

pred_type =pred.argmax(1).item()

完整项目代码地址:

CVprojects/codes at main · enpeizhao/CVprojects · GitHub GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4https://github.com/enpeizhao/CVprojects/tree/main/codes ;

运行时注意:

1、采集训练数据时,运行demo.py文件中的handRecognize.getTrainningData(task_type = ‘6’)。参数task_type取值为0~6,最多可识别七种手势。

2、运行时,假设采集了5种手势,要将对应的手部分类的输出层改为5。

self.modelGCN = GCN(3, 16, 5)

self.modelGCN.load_state_dict(torch.load('./saveModel/handsModel.pth'))

self.modelGCN.eval()

self.handPose = HandPose()

self.mp_hands = mp.solutions.hands

3、若需要gpu加速,dgl需要安装gpu版本。nvcc –version查看自己的cuda版本,然后去Deep Graph Library GCN图卷积神经网络入门讲解+实战结印识别--详细注释解析恩培作品4https://www.dgl.ai/pages/start.html ;查找对应的安装指令。

若运行遇到苦难,欢迎留言交流。

Original: https://blog.csdn.net/thujiang000/article/details/122788564
Author: 清华江同学
Title: GCN图卷积神经网络入门讲解+实战结印识别–详细注释解析恩培作品4

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/704536/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球