C++ OpenCV实战之手势识别

目录
  • 前言
  • 一、手部关键点检测
    • 1.1 功能源码
    • 1.2 功能效果
  • 二、手势识别
    • 2.1算法原理
    • 2.2功能源码
  • 三、结果显示
    • 3.1功能源码
    • 3.2效果显示
  • 四、源码
  • 总结

前言

本文将使用OpenCV C++ 实现手势识别效果。本案例主要可以分为以下几个步骤:

1、手部关键点检测

2、手势识别

3、效果显示

接下来就来看看本案例具体是怎么实现的吧!!!

一、手部关键点检测

如图所示,为我们的手部关键点所在位置。第一步,我们需要检测手部21个关键点。我们使用深度神经网络DNN模块来完成这件事。通过使用DNN模块可以检测出手部21个关键点作为结果输出,具体请看源码。

1.1 功能源码

//手部关键点检测
bool HandKeypoints_Detect(Mat src, vector<Point>&HandKeypoints)
{
	//模型尺寸大小
	int width = src.cols;
	int height = src.rows;
	float ratio = width / (float)height;
	int modelHeight = 368;  //由模型输入维度决定
	int modelWidth = int(ratio*modelHeight);

	//模型文件
	string model_file = "pose_deploy.prototxt";  //网络模型
	string model_weight = "pose_iter_102000.caffemodel";//网络训练权重

	//加载caffe模型
	Net net = readNetFromCaffe(model_file, model_weight);

	//将输入图像转成blob形式
	Mat blob = blobFromImage(src, 1.0 / 255, Size(modelWidth, modelHeight), Scalar(0, 0, 0));

	//将图像转换的blob数据输入到网络的第一层“image”层,见deploy.protxt文件
	net.setInput(blob, "image");

	//结果输出
	Mat output = net.forward();
	int H = output.size[2];
	int W = output.size[3];

	for (int i = 0; i < nPoints; i++)
	{
		//结果预测
		Mat probMap(H, W, CV_32F, output.ptr(0, i)); 

		resize(probMap, probMap, Size(width, height));

		Point keypoint; //最大可能性手部关键点位置
		double classProb;  //最大可能性概率值
		minMaxLoc(probMap, NULL, &classProb, NULL, &keypoint);

		HandKeypoints[i] = keypoint; //结果输出,即手部关键点所在坐标
	}

	return true;
}

1.2 功能效果

如图所示,我们已经通过DNN检测出21个手部关键点所在位置。接下来,我们需要使用这些关键点进行简单的手势识别。

二、手势识别

2.1算法原理

本案例实现手势识别是通过比较关键点位置确定的。首先拿出每个手指尖关键点索引(即4、8、12、16、20)。接下来,对比每个手指其它关键点与其指尖所在位置。

例如我们想确定大拇指现在的状态是张开的还是闭合的。如下图所示,由于OpenCV是以左上角为起点建立坐标系的。当大拇指处于张开状态时(掌心向内),我们可以发现,对比关键点4、关键点3所在位置。当4的x坐标大于3的x坐标时,拇指处于张开状态;当4的x坐标小于3的x坐标时,拇指处于闭合状态。

同理,其余四个手指,以食指为例。当关键点8的y坐标小于关键点6的y坐标时,此时食指处于张开状态;当关键点8的y坐标大于关键点6的y坐标时,此时食指处于闭合状态。

当手指处于张开状态时,我们计数1。通过统计手指的张开数达到手势识别的目的。具体请看源码。

2.2功能源码

//手势识别
bool Handpose_Recognition(vector<Point>&HandKeypoints, int& count)
{
	vector<int>fingers;
	//拇指
	if (HandKeypoints[tipIds[0]].x > HandKeypoints[tipIds[0] - 1].x)
	{
		//如果关键点'4'的x坐标大于关键点'3'的x坐标,则说明大拇指是张开的。计数1
		fingers.push_back(1);
	}
	else
	{
		fingers.push_back(0);
	}
	//其余的4个手指
	for (int i = 1; i < 5; i++)
	{
		if (HandKeypoints[tipIds[i]].y < HandKeypoints[tipIds[i] - 2].y)
		{
			//例:如果关键点'8'的y坐标小于关键点'6'的y坐标,则说明食指是张开的。计数1
			fingers.push_back(1);
		}
		else
		{
			fingers.push_back(0);
		}
	}

	//结果统计
	for (int i = 0; i < fingers.size(); i++)
	{
		if (fingers[i] == 1)
		{
			count++;
		}
	}

	return true;
}

三、结果显示

通过以上步骤,我们已经有了手部关键点所在坐标位置以及对应的手势结果,接下来就进行效果展示。

在这里,为了逼格高一点,我们将下面的手势模板图像作为输出结果放进我们的测试图中。具体操作请看源码。

3.1功能源码

//识别效果显示
bool ShowResult(Mat& src, vector<Point>&HandKeypoints, int& count)
{
	//画出关键点所在位置
	for (int i = 0; i < nPoints; i++)
	{
		circle(src, HandKeypoints[i], 3, Scalar(0, 0, 255), -1);
		putText(src, to_string(i), HandKeypoints[i], FONT_HERSHEY_COMPLEX, 0.8, Scalar(0, 255, 0), 2);
	}

	//为了显示骚操作,读取模板图片,作为识别结果
	vector<string>imageList;
	string filename = "images/";
	glob(filename, imageList);

	vector<Mat>Temp;
	for (int i = 0; i < imageList.size(); i++)
	{
		Mat temp = imread(imageList[i]);

		resize(temp, temp, Size(100, 100), 1, 1, INTER_AREA);

		Temp.push_back(temp);
	}

	//将识别结果显示在原图中
	Temp[count].copyTo(src(Rect(0, src.rows- Temp[count].rows, Temp[count].cols, Temp[count].rows)));
	putText(src, to_string(count), Point(20, 60), FONT_HERSHEY_COMPLEX, 2, Scalar(0, 0, 128), 3);

	return true;
}

3.2效果显示

除此之外,我们还可以将所有的图片整合成一张图,具体请看源码吧。

//将所有图片整合成一张图片
bool Stitching_Image(vector<Mat>images)
{
	Mat canvas = Mat::zeros(Size(1200, 1000), CV_8UC3);
	int width = 400;
	int height = 500;

	for (int i = 0; i < images.size(); i++)
	{
		resize(images[i], images[i], Size(width, height), 1, 1, INTER_LINEAR);
	}

	int col = canvas.cols / width;
	int row = canvas.rows / height;
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			int index = i * col + j;
			images[index].copyTo(canvas(Rect(j*width, i*height, width, height)));
		}
	}

	namedWindow("result", WINDOW_NORMAL);
	imshow("result", canvas);
	waitKey(0);
	return true;
}

最终结果如图所示。以上就是整个案例的流程啦。。。

四、源码

#include<iostream>
#include<opencv2/opencv.hpp>
#include<opencv2/dnn.hpp>
using namespace std;
using namespace cv;
using namespace cv::dnn;

//手部关键点数目
const int nPoints = 21;
//手指索引
const int tipIds[] = { 4,8,12,16,20 };

//手部关键点检测
bool HandKeypoints_Detect(Mat src, vector<Point>&HandKeypoints)
{
	//模型尺寸大小
	int width = src.cols;
	int height = src.rows;
	float ratio = width / (float)height;
	int modelHeight = 368;  //由模型输入维度决定
	int modelWidth = int(ratio*modelHeight);

	//模型文件
	string model_file = "pose_deploy.prototxt";  //网络模型
	string model_weight = "pose_iter_102000.caffemodel";//网络训练权重

	//加载caffe模型
	Net net = readNetFromCaffe(model_file, model_weight);

	//将输入图像转成blob形式
	Mat blob = blobFromImage(src, 1.0 / 255, Size(modelWidth, modelHeight), Scalar(0, 0, 0));

	//将图像转换的blob数据输入到网络的第一层“image”层,见deploy.protxt文件
	net.setInput(blob, "image");

	//结果输出
	Mat output = net.forward();
	int H = output.size[2];
	int W = output.size[3];

	for (int i = 0; i < nPoints; i++)
	{
		//结果预测
		Mat probMap(H, W, CV_32F, output.ptr(0, i)); 

		resize(probMap, probMap, Size(width, height));

		Point keypoint; //最大可能性手部关键点位置
		double classProb;  //最大可能性概率值
		minMaxLoc(probMap, NULL, &classProb, NULL, &keypoint);

		HandKeypoints[i] = keypoint; //结果输出,即手部关键点所在坐标
	}

	return true;
}

//手势识别
bool Handpose_Recognition(vector<Point>&HandKeypoints, int& count)
{
	vector<int>fingers;
	//拇指
	if (HandKeypoints[tipIds[0]].x > HandKeypoints[tipIds[0] - 1].x)
	{
		//如果关键点'4'的x坐标大于关键点'3'的x坐标,则说明大拇指是张开的。计数1
		fingers.push_back(1);
	}
	else
	{
		fingers.push_back(0);
	}
	//其余的4个手指
	for (int i = 1; i < 5; i++)
	{
		if (HandKeypoints[tipIds[i]].y < HandKeypoints[tipIds[i] - 2].y)
		{
			//例:如果关键点'8'的y坐标小于关键点'6'的y坐标,则说明食指是张开的。计数1
			fingers.push_back(1);
		}
		else
		{
			fingers.push_back(0);
		}
	}

	//结果统计
	for (int i = 0; i < fingers.size(); i++)
	{
		if (fingers[i] == 1)
		{
			count++;
		}
	}

	return true;
}

//识别效果显示
bool ShowResult(Mat& src, vector<Point>&HandKeypoints, int& count)
{
	//画出关键点所在位置
	for (int i = 0; i < nPoints; i++)
	{
		circle(src, HandKeypoints[i], 3, Scalar(0, 0, 255), -1);
		putText(src, to_string(i), HandKeypoints[i], FONT_HERSHEY_COMPLEX, 0.8, Scalar(0, 255, 0), 2);
	}

	//为了显示骚操作,读取模板图片,作为识别结果
	vector<string>imageList;
	string filename = "images/";
	glob(filename, imageList);

	vector<Mat>Temp;
	for (int i = 0; i < imageList.size(); i++)
	{
		Mat temp = imread(imageList[i]);

		resize(temp, temp, Size(100, 100), 1, 1, INTER_AREA);

		Temp.push_back(temp);
	}

	//将识别结果显示在原图中
	Temp[count].copyTo(src(Rect(0, src.rows- Temp[count].rows, Temp[count].cols, Temp[count].rows)));
	putText(src, to_string(count), Point(20, 60), FONT_HERSHEY_COMPLEX, 2, Scalar(0, 0, 128), 3);

	return true;
}

//将所有图片整合成一张图片
bool Stitching_Image(vector<Mat>images)
{
	Mat canvas = Mat::zeros(Size(1200, 1000), CV_8UC3);
	int width = 400;
	int height = 500;

	for (int i = 0; i < images.size(); i++)
	{
		resize(images[i], images[i], Size(width, height), 1, 1, INTER_LINEAR);
	}

	int col = canvas.cols / width;
	int row = canvas.rows / height;
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			int index = i * col + j;
			images[index].copyTo(canvas(Rect(j*width, i*height, width, height)));
		}
	}

	namedWindow("result", WINDOW_NORMAL);
	imshow("result", canvas);
	waitKey(0);
	return true;
}

int main()
{
	vector<string>imageList;
	string filename = "test/";
	glob(filename, imageList);

	vector<Mat>images;
	for (int i = 0; i < imageList.size(); i++)
	{
		Mat src = imread(imageList[i]);

		vector<Point>HandKeypoints(nPoints);
		HandKeypoints_Detect(src, HandKeypoints);

		int count = 0;
		Handpose_Recognition(HandKeypoints, count);

		ShowResult(src, HandKeypoints, count);
		images.push_back(src);

		imshow("Demo", src);
		waitKey(0);
	}

	Stitching_Image(images);

	system("pause");
	return 0;
}

总结

本文使用OpenCV C++实现一些简单的手势识别,在这里仅为了提供一个算法思想,理解了算法思想自己想实现什么功能都会很简单。主要操作有以下几点。

1、使用DNN模块实现手部关键点检测

2、利用各关键点所在位置来判定手指的张合状态。

3、效果显示(仅为了实现效果演示,可以省略)

以上就是C++ OpenCV实战之手势识别的详细内容,更多关于OpenCV手势识别的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++基于OpenCV实现手势识别的源码

    先给大家上效果图: 源码在下面 使用 RGB 值分割手部区域,即手部的 GB 值将与背景不同 或者使用边缘检测 或者 背景减法. 我这里使用了背景减法模型.OpenCV为我们提供了不同的背景减法模型,codebook   它的作用是对某些帧进行一段时间的精确校准.其中对于它获取的所有图像:它计算每个像素的平均值和偏差,并相应地指定框. 在前景中它就像一个黑白图像,只有手是白色的 用 Convex Hull 来找到指尖.Convex hull 基本上是包围手部区域的凸集. 包围手的红线是凸包.基本

  • OpenCV利用手势识别实现虚拟拖放效果

    目录 第一步 第二步 第三步 完整代码 本文将实现一些通过手拖放一些框,我们可以使用这个技术实现一些游戏,控制机械臂等很多有趣的事情. 第一步 通过opencv设置显示框和调用摄像头显示当前画面 import cv2 cap = cv2.VideoCapture(0) cap.set(3,1280) cap.set(4,720) while True:     succes, img = cap.read()     cv2.imshow("Image", img)     cv2.w

  • OpenCV+Python3.5 简易手势识别的实现

    检测剪刀石头布三种手势,通过摄像头输入,方法如下: 选用合适颜色空间及阈值提取皮肤部分 使用滤波腐蚀膨胀等方法去噪 边缘检测 寻用合适方法分类 OpenCV用摄像头捕获视频 采用方法:调用OpenCV--cv2.VideoCapture() def video_capture(): cap = cv2.VideoCapture(0) while True: # capture frame-by-frame ret, frame = cap.read() # our operation on th

  • opencv实现静态手势识别 opencv实现剪刀石头布游戏

    本文实例为大家分享了opencv实现静态手势识别的具体代码,供大家参考,具体内容如下 要想运行该代码,请确保安装了:python 2.7,opencv 2.4.9 效果如下: 算法如下: 把图片先进行处理,处理过程: 1.用膨胀图像与腐蚀图像相减的方法获得轮廓. 2.用二值化获得图像 3. 反色 经过如上的处理之后,图片为: 这之后就简单了,设计一个办法把三种图像区分开来即可. 代码如下: # -*- coding: cp936 -*- import cv2 import numpy impor

  • OpenCV+python手势识别框架和实例讲解

    基于OpenCV2.4.8和 python 2.7实现简单的手势识别. 以下为基本步骤 1.去除背景,提取手的轮廓 2. RGB->YUV,同时计算直方图 3.进行形态学滤波,提取感兴趣的区域 4.找到二值化的图像轮廓 5.找到最大的手型轮廓 6.找到手型轮廓的凸包 7.标记手指和手掌 8.把提取的特征点和手势字典中的进行比对,然后判断手势和形状 提取手的轮廓 cv2.findContours() 找到最大凸包cv2.convexHull(),然后找到手掌和手指的相对位置,定位手型的轮廓和关键点

  • Python如何使用opencv进行手势识别详解

    目录 前言 原理 程序部分 附另一个手势识别实例 总结 前言 本项目是使用了谷歌开源的框架mediapipe,里面有非常多的模型提供给我们使用,例如面部检测,身体检测,手部检测等. 原理 首先先进行手部的检测,找到之后会做Hand Landmarks. 将手掌的21个点找到,然后我们就可以通过手掌的21个点的坐标推测出来手势,或者在干什么. 程序部分 第一安装Opencv pip install opencv-python 第二安装mediapipe pip install mediapipe

  • C++ OpenCV实战之手势识别

    目录 前言 一.手部关键点检测 1.1 功能源码 1.2 功能效果 二.手势识别 2.1算法原理 2.2功能源码 三.结果显示 3.1功能源码 3.2效果显示 四.源码 总结 前言 本文将使用OpenCV C++ 实现手势识别效果.本案例主要可以分为以下几个步骤: 1.手部关键点检测 2.手势识别 3.效果显示 接下来就来看看本案例具体是怎么实现的吧!!! 一.手部关键点检测 如图所示,为我们的手部关键点所在位置.第一步,我们需要检测手部21个关键点.我们使用深度神经网络DNN模块来完成这件事.

  • Python+Opencv实战之人脸追踪详解

    目录 前言 人脸追踪技术简介 使用基于 dlib DCF 的跟踪器进行人脸跟踪 使用基于 dlib DCF 的跟踪器进行对象跟踪 小结 前言 人脸处理是人工智能中的一个热门话题,人脸处理可以使用计算机视觉算法从人脸中自动提取大量信息,例如身份.意图和情感:而目标跟踪试图估计目标在整个视频序列中的轨迹,其中只有目标的初始位置是已知的,将这两者进行结合将产生许多有趣的应用.由于外观变化.遮挡.快速运动.运动模糊和比例变化等多种因素,人脸追踪非常具有挑战性. 人脸追踪技术简介 基于判别相关滤波器 (d

  • Python OpenCV实战之与机器学习的碰撞

    目录 0. 前言 1. 机器学习简介 1.1 监督学习 1.2 无监督学习 1.3 半监督学习 2. K均值 (K-Means) 聚类 2.1 K-Means 聚类示例 3. K最近邻 3.1 K最近邻示例 4. 支持向量机 4.1 支持向量机示例 小结 0. 前言 机器学习是人工智能的子集,它为计算机以及其它具有计算能力的系统提供自动预测或决策的能力,诸如虚拟助理.车牌识别系统.智能推荐系统等机器学习应用程序给我们的日常生活带来了便捷的体验.机器学习的蓬勃发展,得益于以下三个关键因素:1) 海

  • Python+OpenCV实战之利用 K-Means 聚类进行色彩量化

    目录 前言 利用 K-Means 聚类进行色彩量化 完整代码 显示色彩量化后的色彩分布 前言 K-Means 聚类算法的目标是将 n 个样本划分(聚类)为 K 个簇,在博文<OpenCV与机器学习的碰撞>中,我们已经学习利用 OpenCV 提供了 cv2.kmeans() 函数实现 K-Means 聚类算法,该算法通过找到簇的中心并将输入样本分组到簇周围,同时通过简单的示例了解了 K-Means 算法的用法.在本文中,我们将学习如何利用 K-Means 聚类进行色彩量化,以减少图像中颜色数量.

  • C++ OpenCV实战之图像全景拼接

    目录 前言 一.OpenCV Stitcher 1.功能源码 2.效果 二.图像全景拼接 1.特征检测 2.计算单应性矩阵 3.透视变换 4.图像拼接 5.功能源码 6.效果 三.源码 总结 前言 本文将使用OpenCV C++ 进行图像全景拼接.目前使用OpenCV对两幅图像进行拼接大致可以分为两类. 一.使用OpenCV内置API Stitcher 进行拼接. 二.使用特征检测算法匹配两幅图中相似的点.计算变换矩阵.最后对其进行透视变换就可以了. 一.OpenCV Stitcher imag

  • OpenCV实战之AI照片背景替换

    目录 导语 正文 1)附主程序 2)展示其他 总结 导语 不少人在生活中都有抠人像图换背景的需求.那怎么抠图呢? 相信不少人第一时间就想到了 PS 抠图大法,为了学会 PS 抠图很多人还花费不少精力,而且学会后大家想必都有共同感触:PS 抠图在制作抠图选区这个步骤太耗费时间!!就跟我减肥似的! 今天木木子就手把手教大家编写一款抠图人像技术—— 这款小程序实现一键智能抠取人像图的功能,非常强大! 比 PS 慢慢抠图效率可提升了太多了,而且还能让不会 PS 的群体也能轻松学会抠人像图. 吹了这么多,

  • C++ OpenCV实战之图像透视矫正

    目录 前言 一.图像预处理 二.轮廓提取 1.提取最外轮廓 2.提取矩形四个角点 3.将矩形角点排序 三.透视矫正 四.源码 前言 本文将使用OpenCV C++ 进行图像透视矫正. 一.图像预处理 原图如图所示.首先进行图像预处理.将图像进行灰度.滤波.二值化.形态学等操作,目的是为了下面的轮廓提取.在这里我还使用了形态学开.闭操作,目的是使整个二值图像连在一起.大家在做图像预处理时,可以根据图像特征自行处理. Mat gray; cvtColor(src, gray, COLOR_BGR2G

  • C++ OpenCV实战之制作九宫格图像

    目录 前言 一.九宫格图像 二.源码 三.效果显示 总结 前言 本文将使用OpenCV C++ 制作九宫格图像.其实原理很简单,就是将一张图像均等分成九份.然后将这九个小块按一定间隔(九宫格效果)拷贝到新画布上就可以啦. 一.九宫格图像 原图如图所示.本案例的需求是希望将图像均等分成九份,制作九宫格图像.首先得将原图均等分成九份. 如图所示.将原图均等分成九份,然后将这每一个小方块按照一定的间隔(九宫格效果)拷贝到新图像就可以了.具体算法思想请看源码注释. 二.源码 #include<iostr

  • OpenCV实战之基于Hu矩实现轮廓匹配

    目录 前言 一.查找轮廓 二.计算Hu矩 三.显示效果 四.源码 总结 前言 本文将使用OpenCV C++ 基于Hu矩进行轮廓匹配. 一.查找轮廓 原图 测试图 vector<vector<Point>>findContour(Mat Image) {     Mat gray;     cvtColor(Image, gray, COLOR_BGR2GRAY);     Mat thresh;     threshold(gray, thresh, 0, 255, THRESH

随机推荐