《Hand Keypoint Detection in Single Images using Multiview Bootstrapping》及模型推理

article/2025/10/22 17:42:23

论文:《Hand Keypoint Detection in Single Images using Multiview Bootstrapping》2017

链接:1704.07809.pdf (arxiv.org)

code:Hand Keypoint Detection using Deep Learning and OpenCV | LearnOpenCV 

论文略读

1.Introduction

        In this paper, we present an approach to boost the performance of a given keypoint detector using a multi-camera setup.在本文中,我们提出了一种使用多摄像机装置提高给定关键点探测器性能的方法。这种方法,我们称之为多视图引导,基于以下观察:即使手的特定图像有明显的遮挡,总有也不存在遮挡的视图。如下,单视图下由于存在遮挡,标注也是很困难的。

2. Multiview Bootstrapped Training

        多视图自举。(a)多视图系统提供其中容易进行关键点检测的手的视图,其用于对(B)关键点的3D位置进行三角测量。具有(c)失败检测的困难视图可以(d)使用重新投影的3D关键点来注释,并且用于重新训练(e)现在在困难视图上工作的改进的检测器。

        从一小组标记的手部图像开始,并使用神经网络( Convolutional Pose Machines -类似于身体姿势)来粗略估计手部关键点。论文中有一个巨大的多视角手部采集系统,可以从不同的视点或角度拍摄图像,该系统包括31个高清摄像头。

        将这些图像通过检测器detector 来获得许多粗略的关键点预测。从不同视图获取同一只手的检测到的关键点后,将执行Keypoint triangulation以获取关键点的三维位置。关键点的3D位置用于通过从3D到2D的重投影来鲁棒地预测关键点。这对于难以预测关键点的图像尤其重要。这样,他们在几次迭代中得到了一个性能大大改进的检测器detector。该模型产生22个关键点,手有21个点,而第22个点表示背景。如下:

Code test

1.python推理

import cv2
import time
import numpy as np# 模型文件、关键点数量、骨架连接方式
protoFile = "./model/pose_deploy.prototxt"
weightsFile = "./model/pose_iter_102000.caffemodel"
nPoints = 22
POSE_PAIRS = [[0, 1], [1, 2], [2, 3], [3, 4], [0, 5], [5, 6], [6, 7], [7, 8], [0, 9], [9, 10], [10, 11], [11, 12],[0, 13], [13, 14], [14, 15], [15, 16], [0, 17], [17, 18], [18, 19], [19, 20]]# 读取图片和模型加载
frame = cv2.imread("./image/1402.jpg")
net = cv2.dnn.readNetFromCaffe(protoFile, weightsFile)# 准备推理图片
frameCopy = np.copy(frame)
frameWidth = frame.shape[1]
frameHeight = frame.shape[0]
aspect_ratio = frameWidth / frameHeight
threshold = 0.1# 推理并计算推理时间
t = time.time()
# input image dimensions for the network
inHeight = 368
inWidth = int(((aspect_ratio * inHeight) * 8) // 8)
inpBlob = cv2.dnn.blobFromImage(frame, 1.0 / 255, (inWidth, inHeight), (0, 0, 0), swapRB=False, crop=False)
net.setInput(inpBlob)
output = net.forward()
print("time taken by network : {:.3f}".format(time.time() - t))# Empty list to store the detected keypoints
points = []
# 画出关键点并标记编号
for i in range(nPoints):# confidence map of corresponding body's part.probMap = output[0, i, :, :]probMap = cv2.resize(probMap, (frameWidth, frameHeight))# Find global maxima of the probMap.minVal, prob, minLoc, point = cv2.minMaxLoc(probMap)if prob > threshold:cv2.circle(frameCopy, (int(point[0]), int(point[1])), 4, (0, 255, 255), thickness=-1,lineType=cv2.FILLED)cv2.putText(frameCopy, "{}".format(i), (int(point[0]), int(point[1])), cv2.FONT_HERSHEY_SIMPLEX, 0.5,(0, 0, 255),1, lineType=cv2.LINE_AA)# Add the point to the list if the probability is greater than the thresholdpoints.append((int(point[0]), int(point[1])))else:points.append(None)
# Draw Skeleton
# 根据骨架数组,画出骨架(即连接点)
for pair in POSE_PAIRS:partA = pair[0]partB = pair[1]if points[partA] and points[partB]:cv2.line(frame, points[partA], points[partB], (0, 255, 255), 2)cv2.circle(frame, points[partA], 4, (0, 0, 255), thickness=-1, lineType=cv2.FILLED)cv2.circle(frame, points[partB], 4, (0, 0, 255), thickness=-1, lineType=cv2.FILLED)# 显示推理结果
cv2.imshow('Output-Keypoints', frameCopy)
cv2.waitKey(0)
cv2.imshow('Output-Skeleton', frame)
cv2.waitKey(0)

2.C++推理

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/dnn/dnn.hpp>using namespace std;
using namespace cv;
using namespace cv::dnn;//各个部位连接线坐标,比如(0,1)表示第0特征点和第1特征点连接线为拇指
const int POSE_PAIRS[20][2] ={{0,1}, {1,2}, {2,3}, {3,4},         // thumb{0,5}, {5,6}, {6,7}, {7,8},         // index{0,9}, {9,10}, {10,11}, {11,12},    // middle{0,13}, {13,14}, {14,15}, {15,16},  // ring{0,17}, {17,18}, {18,19}, {19,20}   // small};int nPoints = 22;int main()
{//模型文件位置string protoFile = "../model/pose_deploy.prototxt";string weightsFile = "../model/pose_iter_102000.caffemodel";// read image 读取图像string imageFile = "../image/1402.jpg";Mat frame = imread(imageFile);if (frame.empty()){cout << "check image" << endl;return 0;}//复制图像Mat frameCopy = frame.clone();//读取图像长宽int frameWidth = frame.cols;int frameHeight = frame.rows;float thresh = 0.01;//原图宽高比float aspect_ratio = frameWidth / (float)frameHeight;int inHeight = 368;//缩放图像int inWidth = (int(aspect_ratio*inHeight) * 8) / 8;cout << "inWidth = " << frameWidth << " ; inHeight = " << frameHeight << endl;double t = (double)cv::getTickCount();//调用caffe模型cv::dnn::Net net = readNetFromCaffe(protoFile, weightsFile);Mat inpBlob = blobFromImage(frame, 1.0 / 255, Size(inWidth, inHeight), Scalar(0, 0, 0), false, false);net.setInput(inpBlob);Mat output = net.forward();int H = output.size[2];int W = output.size[3];// find the position of the body parts 找到各点的位置vector<Point> points(nPoints);for (int n = 0; n < nPoints; n++){// Probability map of corresponding body's part. 第一个特征点的预测矩阵Mat probMap(H, W, CV_32F, output.ptr(0, n));//放大预测矩阵resize(probMap, probMap, Size(frameWidth, frameHeight));Point maxLoc;double prob;//寻找预测矩阵,最大值概率以及最大值的坐标位置minMaxLoc(probMap, 0, &prob, 0, &maxLoc);if (prob > thresh){//画图circle(frameCopy, cv::Point((int)maxLoc.x, (int)maxLoc.y), 4, Scalar(0, 255, 255), -1);cv::putText(frameCopy, cv::format("%d", n), cv::Point((int)maxLoc.x, (int)maxLoc.y), cv::FONT_HERSHEY_COMPLEX, 0.4, cv::Scalar(0, 0, 255), 1);}//保存特征点的坐标points[n] = maxLoc;}//获取要画的骨架线个数int nPairs = sizeof(POSE_PAIRS) / sizeof(POSE_PAIRS[0]);//连接点,画骨架for (int n = 0; n < nPairs; n++){// lookup 2 connected body/hand partsPoint2f partA = points[POSE_PAIRS[n][0]];Point2f partB = points[POSE_PAIRS[n][1]];if (partA.x <= 0 || partA.y <= 0 || partB.x <= 0 || partB.y <= 0)continue;//画骨条线line(frame, partA, partB, Scalar(0, 255, 255), 2);circle(frame, partA, 4, Scalar(0, 0, 255), -1);circle(frame, partB, 4, Scalar(0, 0, 255), -1);}//计算运行时间t = ((double)cv::getTickCount() - t) / cv::getTickFrequency();cout << "Time Taken = " << t << endl;imshow("Output-Keypoints", frameCopy);imshow("Output-Skeleton", frame);imwrite("../image/result/Output-Skeleton.jpg", frame);imwrite("../image/result/Output-Keypoints.jpg", frameCopy);waitKey();return 0;
}

        模型文件:可在我的资源中下载:openpose的手部关键点估计预训练模型资源

        也可前往以下链接下载:https://github.com/CMU-Perceptual-Computing-Lab/openpose


http://chatgpt.dhexx.cn/article/WA52Kh6b.shtml

相关文章

bootstraping

之前一位同学问及bootstrap&#xff0c;由此我查阅了几篇文献&#xff0c;初步知晓个皮毛&#xff1a;它是一种非参检验方法&#xff0c;利用重复抽样理论&#xff0c;来减少偏差、控制方差、得到有效置信区间等统计方法。国内bootstrap研究比较少&#xff0c;这里摘录了国外研…

CKKS自举笔记(CKKS Bootstrapping)

文章目录 CKKS Bootstrapping流程流程的框架如何做同态取模操作直接泰勒展开&#xff08;naive idea&#xff09;采用二倍角公式来拟合&#xff08;欧密2018&#xff09; 如何做同态编码或解码CKKS的编码和解码基础知识&#xff08;明文下面怎么做&#xff09;同态的旋转、共轭…

解决:‘config.status: error: Something went wrong bootstrapping makefile fragments......’问题

解决&#xff1a;‘config.status: error: Something went wrong bootstrapping makefile fragments......’问题 一、问题二、解决方法 一、问题 首先我们来看安装sqlite时报的这个错误&#xff1a; config.status: error: in ‘/home/dengyonghao/Downloads/sqlite-autoconf…

Bootstrapping的意义

一、原理解释 Bootstrapping 方法是种集成方法&#xff0c;通俗解释就是盲人摸象&#xff0c;很多盲人摸一头象&#xff0c;各自摸到的都不一样&#xff0c;但是都比较片面&#xff0c;当他们在一起讨论时&#xff0c;就得到了象的整体。 Bootstrap的过程&#xff0c;类似于重…

Bootstrapping method

Bootsrapping指的就是利用有限的样本资料经由多次重复抽样&#xff0c;重新建立起足以代表母体样本分布的新样本。 统计中我们常常需要做参数估计&#xff0c;具体问题可以描述为&#xff1a;给定一系列数据 假设它们是从分布F中采样得到的&#xff0c;参数估计就是希望估计分…

【强化学习】n步Bootstrapping

目录 n步TD 预测 n-step Sarsa n步off - policy学习 Per-reward Off - policy 方法 n步Tree Backup算法 BootStrapping原是推论统计学里的概念。所谓推论统计学&#xff0c;就是根据样本统计量来推算总体的统计量。n部方法通常会被用作eligibility trace思想的一个例子&am…

Bootstrapping

Bootstrapping从字面意思翻译是拔靴法&#xff0c;从其内容翻译又叫自助法&#xff0c;是一种再抽样的统计方法。自助法的名称来源于英文短语“to pull oneself up by one’s bootstrap”&#xff0c;表示完成一件不能自然完成的事情。1977年美国Standford大学统计学教授Efron提…

Bootstrapping?

一、什么是Bootstrapping&#xff1f; 中文翻译也叫“自助法(自举法)”。 类似于给鞋子穿鞋带&#xff0c;把鞋带穿进去在穿出来再穿进去。 举个例子&#xff0c;一个总体有五十人&#xff0c;没有办法直接测量总体的情况&#xff0c;我们就从总体中抽取一些样本&#xff0c;用…

华为机试题整理

1、整数反转后求和 #include <iostream> using namespace std; int reversenum(int x) {int a0;while (x>0) {aa*10x%10;x/10;}return a; } int reverseAdd(int a,int b) {if(a<1||a>70000||b<1||b>70000){return -1;}int num1reversenum(a);int num2re…

2021.华为机试某题

问题描述&#xff1a; 有M*N的节点矩阵&#xff0c;每个节点可以向8个方向&#xff08;上、下、左、右及四个斜线方向&#xff09;转发数据包&#xff0c;每个节点转发时会消耗固定时延&#xff0c;连续两个相同时延可以减少一个时延值&#xff08;即当有K个相同时延的节点连续…

牛客网华为机试题训练汇总(JavaScript)

牛客网华为机试题训练&#xff08;JavaScript Node环境&#xff09; 文章目录 牛客网华为机试题训练&#xff08;JavaScript Node环境&#xff09;前言一、题目1. HJ11 数字颠倒2. HJ22 汽水瓶3. HJ53 杨辉三角的变形4. HJ2 计算某字母出现次数5. HJ8 合并表记录6. HJ17 坐标移…

1、华为机试题记录

1、小型机通常采用RISC和unix操作系统。 RISC&#xff1a;精简指令集计算机&#xff0c;指令少&#xff0c;运行效率更高。 unix&#xff1a;商用UNIX现在主要是三大分支IBM的AIX,SUN的solaris&#xff0c;HP的hp-ux&#xff0c;运行在小型机上。金融电信等行业应用广泛&#x…

华为机试练习题汇总

华为机试练习广场&#xff1a; [华为机试练习题]1.周期串问题 - Yoona - 博客频道 - CSDN.NET[华为机试练习题]2.大数求和 - Yoona - 博客频道 - CSDN.NET[华为机试练习题]3.分解字符串 - Yoona - 博客频道 - CSDN.NET[华为机试练习题]4.简单密码破解 - Yoona - 博客频道 - CSD…

华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典

文章目录 2023 年用 Python 语言解华为 OD 机试题&#xff0c;一篇博客找全。华为 OD 机试题清单&#xff08;机试题库还在逐日更新&#xff09; 2023 年用 Python 语言解华为 OD 机试题&#xff0c;一篇博客找全。 在 2023 年&#xff0c;Python 已成为广泛使用的编程语言之一…

华为OD机试真题2022Q4 A + 2023 B卷(JavaJavaScript)

大家好&#xff0c;我是哪吒。 五月份之前&#xff0c;如果你参加华为OD机试&#xff0c;收到的应该是2022Q4或2023Q1&#xff0c;这两个都是A卷题。 5月10日之后&#xff0c;很多小伙伴收到的是B卷&#xff0c;那么恭喜你看到本文了&#xff0c;抓紧刷题吧。B卷新题库正在更…

EntityWrapper的in用法

EntityWrapper<UserLife> wrapper new EntityWrapper<>(); wrapper.eq("is_valid", 1); wrapper.in("life_name", "ge,edu,career"); List<UserLife> userLabelList userLabelService.selectList(wrapper); in的第二个参数…

QueryWrapper

官方文档&#xff1a;https://mp.baomidou.com/guide/wrapper.html#querywrapper select("id", "name", "age") select(i -> i.getProperty().startsWith("test")) controller中使用的例子

wrapper.and

多条件查询时 如果使用这种的话&#xff0c;会出现只要这个条件成功了&#xff0c;不管你后面或者前面有没有and条件&#xff0c;它都成功&#xff0c; 可以看出来整个条件都在一个括号里面 //创建查询对象LambdaQueryWrapper<PublishWorksRemit> wrapper new Lambd…

MyBatis-Plus使用条件构造器Wrapper

Wrapper &#xff1a;条件构造抽象类&#xff0c;最顶端父类。AbstractWrapper类比较重要。 AbstractWrapper类是 QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类。用于生成 sql 的 where 条件,&#xff0c;entity 属性也用于生成 sql 的 whe…

MybatisPlus使用Wrapper实现增删改查功能

条件构造器的格式说明 导入maven依赖 <dependency><groupId>com.github.jeffreyning</groupId><artifactId>mybatisplus-plus</artifactId><version>1.5.1-RELEASE</version><scope>compile</scope></dependency>…