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

news2024/11/26 15:49:17

论文:《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 threshold
        points.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 parts
        Point2f 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://www.coloradmin.cn/o/649722.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AntDB数据库荣获2023年中国信创产业拳头奖“2023年中国信创数据库卓越品牌”

近日&#xff0c;由全球新经济产业第三方数据挖掘和分析机构iiMedia Research&#xff08;艾媒咨询&#xff09;主办的“2023年&#xff08;第2届&#xff09;中国信创产业大会”在广州圆满落幕。大会现场揭晓了“2023年中国信创产业拳头奖”。AntDB数据库从众多参评产品中脱颖…

研发为底、生态为径、AI为翼——全国一体化算力算网调度平台正式发布

GPU | COMPUTEX | 算力 | GPU服务器 英伟达 | GH200 | 一体化算力算网调度平台 近年来&#xff0c;人工智能硬件、软件算法以及应用场景的丰富度不断增加&#xff0c;算法模型参数也不断增加&#xff0c;这带动了对数据中心并行计算算力的需求。因此&#xff0c;AI、高性能计算…

升级后,配置文件被清空,导致无法开启WiFi

root cause&#xff1a; /data/vendor/wifi/wpa/wpa_supplicant.conf 是0字节&#xff0c;导致wpa_supplicant_init_iface缺少”p2p_disabled1“的配置就会在走错flow到p2p wpa_supplicant_init_iface 》wpas_p2p_init 从而在HidlManager::registerInterface 进入 if (isP2pIf…

EDLA协议授权,谷歌GMS认证,设备申请EDLA协议过认证,谷歌GMS EDLA协议

一、EDLA协议跟MADA协议以及GMS认证有什么关联&#xff1f;AER又是什么&#xff1f;车机使用什么协议申请GMS认证&#xff1f; GMS全称是Google Mobile Service(即谷歌移动服务)&#xff0c;GMS认证简单来说就是使用安卓系统的移动设备在经过谷歌的一系列测试后取得谷歌的认可&…

为什么学深度学习的同学都需要懂高性能计算?

深度学习作为人工智能领域的热门技术&#xff0c;正以惊人的速度推动着科学和工业的进步。然而&#xff0c;随着深度学习模型的不断复杂化和数据量的急剧增长&#xff0c;传统的计算资源已经无法满足其高效运算的需求。因此&#xff0c;对于学习深度学习的同学来说&#xff0c;…

Pod 的 phase 和 conditions 的区别

目录 前言 PodStatus对象 pod 的 phase 字段 phase 作用 有哪些 phase pod 的 conditions 字段 pod 有了 phase&#xff0c;为什么还要有 conditions pod 的 conditions 的作用 pod 的 conditions 分类 conditions设计原则 condition字段内容 前言 K…

高颜值测试报告- XTestRunner

基于unittest框架现代风格测试报告。 特点 漂亮测试报告让你更愿意编写测试。支持单元、Web UI、API 各种类型的测试。支持Selenium运行失败/错误自动截图。支持失败重跑。支持标签黑、白名单。支持发邮件功能。支持多语言en、zh-CN 等。支持HTML/XML不同格式的报告。 安装 …

ADG-rac备库的问题处理

第一次配置rac to rac的的adg&#xff0c;ADG过程就不叙述了&#xff0c;备库同步完成后&#xff0c;使用srvctl加入集群时未报错&#xff0c;但是使用crsctl status res -t查看状态时&#xff0c;发现了问题&#xff0c;如下图 ASM1:/home/gridhydb1> crsctl status res -…

达梦数据库DSC集群搭建:两节点与多节点(三节点)集群搭建

目录 说明... 4 两节点集群搭建... 5 一、创建共享磁盘... 6 1、DMDSC01添加硬盘... 6 2、DMDSC02添加共享磁盘... 8 二、搭建DMDSC集群... 12 1、安装完成数据库配置资源限制... 12 2、添加用户变量... 13 3、共享磁盘分区... 14 4、新建udev规则文件-裸设备绑定...…

如何将PDF转换为Excel?这4个方法请收好

PDF&#xff08;便携式文档格式&#xff09;因其安全且易于访问而广泛用于在线共享和存档文档。但是&#xff0c;很多时候您可能需要从 PDF 文档中提取数据以在 Excel 中使用它。这是将 PDF 转换为 Excel 发挥作用的地方。在本文中&#xff0c;我们将探讨将 PDF 文件转换为 Exc…

HarmonyOS学习路之开发篇—Java UI框架(自定义组件与布局 一)

HarmonyOS提供了一套复杂且强大的Java UI框架&#xff0c;其中Component提供内容显示&#xff0c;是界面中所有组件的基类。ComponentContainer作为容器容纳Component或ComponentContainer对象&#xff0c;并对它们进行布局。 Java UI框架也提供了一部分Component和ComponentC…

linux系统总结

linux系统总结&#xff1a; linux系统总结 一、网络&#xff1a;1.七层模型&#xff1a;2.三次握手&#xff0c;四次挥手&#xff1a;3.网络层&#xff1a;ARP&#xff0c;icmp4.总结tcp和udp的不同&#xff1a;5.端口号的作用&#xff1a;6.VRRP的原理&#xff1a;SNAT DNAT7…

论文解读 | 超越人类智慧!类脑多模态混合神经网络助力机器人精准定位

原创 | 文 BFT机器人 01 研究内容 这篇论文的研究内容是基于大脑启发的多模态混合神经网络&#xff0c;用于机器人地点识别。研究人员设计了一个名为NeuroGPR的系统&#xff0c;该系统可以模拟大脑的多模态感知机制&#xff0c;从传统和神经形态传感器中编码和整合多模态线索&a…

消息队列的基本概念

每种消息队列都有自己的一套消息模型&#xff0c;像队列&#xff08;Queue&#xff09;、主题&#xff08;Topic&#xff09;或是分区&#xff08;Partition&#xff09;这些名词概念&#xff0c;在每个消息队列模型中都会涉及一些&#xff0c;含义还不太一样。 为什么出现这种…

1.8C++流提取运算符重载

C流提取运算符重载 在 C中&#xff0c;流提取运算符&#xff08;>>&#xff09;是用于从流中提取数据的运算符。 C中的流提取运算符可以被重载&#xff0c;使得程序员可以自定义输入对象的方式&#xff0c;更方便地输入自定义的数据类型&#xff0c;也可以使得输入更加…

阿里云轻量应用服务器和云服务器ECS区别(终于懂了)

阿里云服务器ECS和轻量应用服务器有什么区别&#xff1f;云服务器ECS是明星级云服务器&#xff0c;轻量应用服务器可以理解为简化版的云服务器ECS&#xff0c;轻量适用于单机应用&#xff0c;云服务器ECS适用于集群类高可用高容灾应用&#xff0c;阿里云百科来详细说下阿里云轻…

dbGet 快速学习教程

dbGet是innovus/encounter工具自带的"database access command"命令中的一部分&#xff0c;它几乎可以用来获取设计相关的一切信息。 输入dbGet 按[Tab]键&#xff0c;能看到三个选项&#xff0c;分别是head / top /selected。这三个选项所代表的意义如下: head --…

Vue3对于一个前端来讲意味着什么?

最近很多技术网站&#xff0c;讨论的最多的无非就是Vue3了&#xff0c;大多数都是Composition API和基于Proxy的原理分析。但是今天想着跟大家聊聊&#xff0c;Vue3对于一个低代码平台的前端更深层次意味着什么&#xff1f; 首先&#xff0c;Vue是前端三大主流框架之一&#xf…

python,如何设置定时执行python代码-windows本地

最近写了一个python小程序&#xff0c;希望能每天定时执行&#xff0c;但是又不想用jenkins这样的工具&#xff0c;后来发现windows本地就可以设置&#xff0c;而且很好用&#xff0c;具体步骤如下&#xff1a; 首先&#xff0c;需确保本地python代码已编写好&#xff0c;环境…

c++创建对象常见的坑

c创建对象常见的坑 创建对象的时候不要在对象名后面加空的圆括号&#xff0c;编译器误认为是声明函数。&#xff08;如果没有构造函数、构造函数没有参数、构造函数的参数都有默认参数&#xff09;在构造函数名后面加括号和参数不是调用构造函数&#xff0c;是创建匿名对象。以…