3D手势识别(一)顺/逆时针画圈判断

2024-02-20 08:30

本文主要是介绍3D手势识别(一)顺/逆时针画圈判断,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

场景:前装摄像头

检测动作:单手指画圈,需要判断画圈方向和圈数。

     

步骤: (1)取得3D图像序列最前点;

            (2)将最前点投影在2D平面上;

            (3)中值滤波和平滑处理;

            (4)得到2D点集进行线性插值;

            (5)以重心为中心判断是否闭环;

            (6)2D点集进行椭圆拟合,判断是否椭圆、椭圆半径和椭圆度范围;

    (7)  2D点集进行圆拟合,计算半径均值和标准差;

            (8)判断2D点集方向:顺/逆时针。

其中手的识别使用深度学习进行,根据TensorFlow训练完的模型判断出图像中有手且为单手指,
去掉背景、挖出只含有手的图像;中值滤波用的OpenCV medianBlur函数,其他主要函数算法如下:


Table of Contents

1、2D点集线性插值算法

2、计算重心

3、闭环判断

4、椭圆拟合

5、圆拟合

6、顺/逆时针方向判断


1、2D点集线性插值算法

//************************************
// Description: 插值,根据两点间距离线性插值,两点间距离越大插值越多,距离小于min_dis则不插值返回原值
// Method:    InterpCurve
// FullName:  InterpCurve
// Access:    private
// Parameter: 插值前二维点集 std::vector<cv::Point2f> &data
// Parameter: 插值后二维点集 std::vector<cv::Point2f> &result
// Parameter: 最小插值距离 float min_dis
// Returns:
// Author:    
// Date:      2018/08/28
// History:
//************************************
void InterpCurve(std::vector<cv::Point2f> &data, std::vector<cv::Point2f> &result, float min_dis) {result.clear();result.reserve(data.size());result.push_back(data.at(0));for (size_t i = 1; i < data.size(); i++) {float dis = Distance(data.at(i - 1), data.at(i));float num = dis / min_dis + 1;float step = 1.0f / num;float s = 0;while (s <= 1.0) {result.emplace_back(cv::Point2f((1 - s) * data.at(i - 1).x + s * data.at(i).x,(1.0f - s) * data.at(i - 1).y + s * data.at(i).y));s += step;}}//  vecp::print(result, "result");
}

2、计算重心

3D点计算方法类似。

//************************************
// Description: 将输入的point2d点集求和平均求重心
// Method:    CalcCentre
// FullName:  CalcCentre
// Access:    private
// Parameter: 二维点集 const std::vector<cv::Point2f> &point2ds
// Returns:   重心坐标 cv::Point2f
// Author:    
// Date:      2018/08/28
// History:
//************************************
cv::Point2f CalcCentre(const std::vector<cv::Point2f> &point2ds) {float x = 0, y = 0;for (auto &pt: point2ds) {x += pt.x;y += pt.y;}return cv::Point2f(x / point2ds.size(), y / point2ds.size());
}

3、闭环判断

//************************************
// Description: 将输入的point2d点集判断是否闭环
// Method:    FindOneCircleFromUnformInCircle
// FullName:  FindOneCircleFromUnformInCircle
// Access:    private
// Parameter: 二维点集 const std::vector<cv::Point2f> &point2ds
// Parameter: 判断闭环坐标中心 const cv::Point2f &centre
// Parameter: 最大无点角度0-360 float max_angle
// Parameter: 划分区域数量 int part_count
// Returns:   bool
// Author:    
// Date:      2018/08/28
// History:
//************************************
bool FindOneCircleFromUnformInCircle(const std::vector<cv::Point2f> &point2ds,const cv::Point2f &centre,float max_angle, int part_count) {double each_path_dog = 2.0f * M_PI / part_count;//cout<<"each_path_dog:"<<each_path_dog<<"  ";std::vector<int> counts(part_count, 0);int full_part_counts = 0;for (auto &pt: point2ds) {float alpha = atan2Ex(pt.x - centre.x, pt.y - centre.y);auto idx = static_cast<size_t>(alpha / each_path_dog);counts.at(idx)++;}//每个区域大于5个点算作填满for (auto &count: counts) {if (count > 5)full_part_counts++;}double dao_each_path_dog = 1.0 / each_path_dog;//填满区域大于需要填满的区域则返回truereturn full_part_counts  >= int(DegToRad(360.0 - max_angle) * dao_each_path_dog);
}

4、椭圆拟合

使用了OpenCV的fitEllipseEx函数。

//************************************
// Description: 将输入的point2d点集做椭圆拟合
// Method:    IsCircleFromEllipse
// FullName:  IsCircleFromEllipse
// Access:    private
// Parameter: 二维点集 const std::vector<cv::Point2f> &point2ds
// Parameter: 最小半径 float min_radius
// Parameter: 最大椭圆度(两半径比值) float min_ovality
// Parameter: 最大非拟合点 float min_error
// Returns:   bool
// Author:    
// Date:      2018/08/28
// History:
//************************************
bool IsCircleFromEllipse(const std::vector<cv::Point2f> &point2ds,float min_radius, float min_ovality, float min_error,cv::RotatedRect &ellipse) {//OpenCV椭圆拟合,要求至少有六个点输入float error = fitEllipseEx(point2ds, ellipse);float a = ellipse.size.width;float b = ellipse.size.height;//printf("a %f, b %f, ovality %f, error %f\n", a, b, a/b, error);return !(a < min_radius || b < min_radius || a / b < min_ovality || a / b > 1.0 / min_ovality ||error > min_error);
}

5、圆拟合

以重心为圆心设置半径均值和标准差阈值:

 /************************************Description: 将输入的point2d点集做圆拟合,根据圆心计算半径均值和标准差Method:    IsCircelFromDevFullName:  IsCircelFromDevAccess:    privateParameter: 最小半径均值 float min_radiusParameter: 最大半径标准差 float max_devReturns:   boolAuthor:    Date:      2018/10/17History:
************************************/
bool CircleAction::IsCircelFromDev(const std::vector<cv::Point2f> &point2ds, float min_radius, float max_dev) {cv::Point2f centre = CalcCentre(point2ds);std::vector<float> radius(point2ds.size(), 0.0f);for (size_t i = 0; i < point2ds.size(); i++) {radius.at(i) = Distance(centre, point2ds.at(i));}cv::Mat mean, dev;cv::meanStdDev(radius, mean, dev);double mean_radius = mean.at<double>(0, 0), dev_radius = dev.at<double>(0, 0);//std::cout << "radius mean---dev: " <<mean_radius << "dev_radius: " << dev_radius << std::endl;return (mean_radius > min_radius && dev_radius < max_dev);
}

6、顺/逆时针方向判断

//************************************
// Description: 将输入的point2d点集判断顺/逆时针方向
// Method:    IsClockwiseFromCross
// FullName:  IsClockwiseFromCross
// Access:    private
// Parameter: 二维点集 const std::vector<cv::Point2f> &point2ds
// Returns:   bool
// Author:    
// Date:      2018/08/28
// History:
//************************************
bool IsClockwiseFromCross(const std::vector<cv::Point2f> &point2ds) {int positive = 0, negative = 0;for (size_t i = 1; i < point2ds.size() - 1; i++) {//由i,i-1,i+1得到局部方向int status = WhichClockWise(point2ds.at(i - 1), point2ds.at(i), point2ds.at(i + 1));if (status == -1)negative++;else if (status == 1)positive++;}//  if (abs(positive - negative) < 5)//      std::cout << "[warning]: the clockwise may be wrong" << std::endl;//按正、反方向数量判断返回多的反向return (positive >= negative);
}
//************************************
// Description: 将输入的point2d点判断局部方向
// Method:    WhichClockWise
// FullName:  WhichClockWise
// Access:    private
// Parameter: 二维点a cv::Point2f a
// Parameter: 二维点b cv::Point2f b
// Parameter: 二维点c cv::Point2f c
// Returns:   int
// Author:    
// Date:      2018/08/28
// History:
//************************************
int WhichClockWise(cv::Point2f a, cv::Point2f b, cv::Point2f c) {double triangle_area = a.x * b.y - a.y * b.x + a.y * c.x - a.x * c.y + b.x * c.y - c.x * b.y;if (triangle_area < 0) return -1;else if (triangle_area > 0) return 1;return 0;
}

 

这篇关于3D手势识别(一)顺/逆时针画圈判断的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/727600

相关文章

Python中判断对象是否为空的方法

《Python中判断对象是否为空的方法》在Python开发中,判断对象是否为“空”是高频操作,但看似简单的需求却暗藏玄机,从None到空容器,从零值到自定义对象的“假值”状态,不同场景下的“空”需要精... 目录一、python中的“空”值体系二、精准判定方法对比三、常见误区解析四、进阶处理技巧五、性能优化

使用PyTorch实现手写数字识别功能

《使用PyTorch实现手写数字识别功能》在人工智能的世界里,计算机视觉是最具魅力的领域之一,通过PyTorch这一强大的深度学习框架,我们将在经典的MNIST数据集上,见证一个神经网络从零开始学会识... 目录当计算机学会“看”数字搭建开发环境MNIST数据集解析1. 认识手写数字数据库2. 数据预处理的

Pytorch微调BERT实现命名实体识别

《Pytorch微调BERT实现命名实体识别》命名实体识别(NER)是自然语言处理(NLP)中的一项关键任务,它涉及识别和分类文本中的关键实体,BERT是一种强大的语言表示模型,在各种NLP任务中显著... 目录环境准备加载预训练BERT模型准备数据集标记与对齐微调 BERT最后总结环境准备在继续之前,确

讯飞webapi语音识别接口调用示例代码(python)

《讯飞webapi语音识别接口调用示例代码(python)》:本文主要介绍如何使用Python3调用讯飞WebAPI语音识别接口,重点解决了在处理语音识别结果时判断是否为最后一帧的问题,通过运行代... 目录前言一、环境二、引入库三、代码实例四、运行结果五、总结前言基于python3 讯飞webAPI语音

使用Python开发一个图像标注与OCR识别工具

《使用Python开发一个图像标注与OCR识别工具》:本文主要介绍一个使用Python开发的工具,允许用户在图像上进行矩形标注,使用OCR对标注区域进行文本识别,并将结果保存为Excel文件,感兴... 目录项目简介1. 图像加载与显示2. 矩形标注3. OCR识别4. 标注的保存与加载5. 裁剪与重置图像

C++实现回文串判断的两种高效方法

《C++实现回文串判断的两种高效方法》文章介绍了两种判断回文串的方法:解法一通过创建新字符串来处理,解法二在原字符串上直接筛选判断,两种方法都使用了双指针法,文中通过代码示例讲解的非常详细,需要的朋友... 目录一、问题描述示例二、解法一:将字母数字连接到新的 string思路代码实现代码解释复杂度分析三、

Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)

《Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)》本文介绍了如何使用Python和Selenium结合ddddocr库实现图片验证码的识别和点击功能,感兴趣的朋友一起看... 目录1.获取图片2.目标识别3.背景坐标识别3.1 ddddocr3.2 打码平台4.坐标点击5.图

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

Python判断for循环最后一次的6种方法

《Python判断for循环最后一次的6种方法》在Python中,通常我们不会直接判断for循环是否正在执行最后一次迭代,因为Python的for循环是基于可迭代对象的,它不知道也不关心迭代的内部状态... 目录1.使用enuhttp://www.chinasem.cnmerate()和len()来判断for