最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++ OpenCV實(shí)戰(zhàn)之手勢(shì)識(shí)別

 更新時(shí)間:2022年04月02日 14:29:43   作者:Zero___Chen  
這篇文章主要介紹了如何利用C++?OpenCV實(shí)現(xiàn)手勢(shì)識(shí)別,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)OpenCV有一定幫助,感興趣的小伙伴可以了解一下

前言

本文將使用OpenCV C++ 實(shí)現(xiàn)手勢(shì)識(shí)別效果。本案例主要可以分為以下幾個(gè)步驟:

1、手部關(guān)鍵點(diǎn)檢測(cè)

2、手勢(shì)識(shí)別

3、效果顯示

接下來(lái)就來(lái)看看本案例具體是怎么實(shí)現(xiàn)的吧?。?!

一、手部關(guān)鍵點(diǎn)檢測(cè)

如圖所示,為我們的手部關(guān)鍵點(diǎn)所在位置。第一步,我們需要檢測(cè)手部21個(gè)關(guān)鍵點(diǎn)。我們使用深度神經(jīng)網(wǎng)絡(luò)DNN模塊來(lái)完成這件事。通過(guò)使用DNN模塊可以檢測(cè)出手部21個(gè)關(guān)鍵點(diǎn)作為結(jié)果輸出,具體請(qǐng)看源碼。

1.1 功能源碼

//手部關(guān)鍵點(diǎn)檢測(cè)
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";  //網(wǎng)絡(luò)模型
	string model_weight = "pose_iter_102000.caffemodel";//網(wǎng)絡(luò)訓(xùn)練權(quán)重

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

	//將輸入圖像轉(zhuǎn)成blob形式
	Mat blob = blobFromImage(src, 1.0 / 255, Size(modelWidth, modelHeight), Scalar(0, 0, 0));

	//將圖像轉(zhuǎn)換的blob數(shù)據(jù)輸入到網(wǎng)絡(luò)的第一層“image”層,見(jiàn)deploy.protxt文件
	net.setInput(blob, "image");

	//結(jié)果輸出
	Mat output = net.forward();
	int H = output.size[2];
	int W = output.size[3];

	for (int i = 0; i < nPoints; i++)
	{
		//結(jié)果預(yù)測(cè)
		Mat probMap(H, W, CV_32F, output.ptr(0, i)); 

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

		Point keypoint; //最大可能性手部關(guān)鍵點(diǎn)位置
		double classProb;  //最大可能性概率值
		minMaxLoc(probMap, NULL, &classProb, NULL, &keypoint);

		HandKeypoints[i] = keypoint; //結(jié)果輸出,即手部關(guān)鍵點(diǎn)所在坐標(biāo)
	}

	return true;
}

1.2 功能效果

如圖所示,我們已經(jīng)通過(guò)DNN檢測(cè)出21個(gè)手部關(guān)鍵點(diǎn)所在位置。接下來(lái),我們需要使用這些關(guān)鍵點(diǎn)進(jìn)行簡(jiǎn)單的手勢(shì)識(shí)別。

二、手勢(shì)識(shí)別

2.1算法原理

本案例實(shí)現(xiàn)手勢(shì)識(shí)別是通過(guò)比較關(guān)鍵點(diǎn)位置確定的。首先拿出每個(gè)手指尖關(guān)鍵點(diǎn)索引(即4、8、12、16、20)。接下來(lái),對(duì)比每個(gè)手指其它關(guān)鍵點(diǎn)與其指尖所在位置。

例如我們想確定大拇指現(xiàn)在的狀態(tài)是張開的還是閉合的。如下圖所示,由于OpenCV是以左上角為起點(diǎn)建立坐標(biāo)系的。當(dāng)大拇指處于張開狀態(tài)時(shí)(掌心向內(nèi)),我們可以發(fā)現(xiàn),對(duì)比關(guān)鍵點(diǎn)4、關(guān)鍵點(diǎn)3所在位置。當(dāng)4的x坐標(biāo)大于3的x坐標(biāo)時(shí),拇指處于張開狀態(tài);當(dāng)4的x坐標(biāo)小于3的x坐標(biāo)時(shí),拇指處于閉合狀態(tài)。

同理,其余四個(gè)手指,以食指為例。當(dāng)關(guān)鍵點(diǎn)8的y坐標(biāo)小于關(guān)鍵點(diǎn)6的y坐標(biāo)時(shí),此時(shí)食指處于張開狀態(tài);當(dāng)關(guān)鍵點(diǎn)8的y坐標(biāo)大于關(guān)鍵點(diǎn)6的y坐標(biāo)時(shí),此時(shí)食指處于閉合狀態(tài)。

當(dāng)手指處于張開狀態(tài)時(shí),我們計(jì)數(shù)1。通過(guò)統(tǒng)計(jì)手指的張開數(shù)達(dá)到手勢(shì)識(shí)別的目的。具體請(qǐng)看源碼。

2.2功能源碼

//手勢(shì)識(shí)別
bool Handpose_Recognition(vector<Point>&HandKeypoints, int& count)
{
	vector<int>fingers;
	//拇指
	if (HandKeypoints[tipIds[0]].x > HandKeypoints[tipIds[0] - 1].x)
	{	 
		//如果關(guān)鍵點(diǎn)'4'的x坐標(biāo)大于關(guān)鍵點(diǎn)'3'的x坐標(biāo),則說(shuō)明大拇指是張開的。計(jì)數(shù)1
		fingers.push_back(1);
	}
	else
	{
		fingers.push_back(0);
	}
	//其余的4個(gè)手指
	for (int i = 1; i < 5; i++)
	{
		if (HandKeypoints[tipIds[i]].y < HandKeypoints[tipIds[i] - 2].y)
		{
			//例:如果關(guān)鍵點(diǎn)'8'的y坐標(biāo)小于關(guān)鍵點(diǎn)'6'的y坐標(biāo),則說(shuō)明食指是張開的。計(jì)數(shù)1
			fingers.push_back(1);
		}
		else
		{
			fingers.push_back(0);
		}
	}

	//結(jié)果統(tǒng)計(jì)
	for (int i = 0; i < fingers.size(); i++)
	{
		if (fingers[i] == 1)
		{
			count++;
		}
	}

	return true;
}

三、結(jié)果顯示

通過(guò)以上步驟,我們已經(jīng)有了手部關(guān)鍵點(diǎn)所在坐標(biāo)位置以及對(duì)應(yīng)的手勢(shì)結(jié)果,接下來(lái)就進(jìn)行效果展示。

在這里,為了逼格高一點(diǎn),我們將下面的手勢(shì)模板圖像作為輸出結(jié)果放進(jìn)我們的測(cè)試圖中。具體操作請(qǐng)看源碼。

3.1功能源碼

//識(shí)別效果顯示
bool ShowResult(Mat& src, vector<Point>&HandKeypoints, int& count)
{
	//畫出關(guān)鍵點(diǎn)所在位置
	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);
	}

	//為了顯示騷操作,讀取模板圖片,作為識(shí)別結(jié)果
	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);
	}

	//將識(shí)別結(jié)果顯示在原圖中
	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效果顯示

除此之外,我們還可以將所有的圖片整合成一張圖,具體請(qǐng)看源碼吧。

//將所有圖片整合成一張圖片
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;
}

最終結(jié)果如圖所示。以上就是整個(gè)案例的流程啦。。。

四、源碼

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

//手部關(guān)鍵點(diǎn)數(shù)目
const int nPoints = 21; 
//手指索引
const int tipIds[] = { 4,8,12,16,20 };

//手部關(guān)鍵點(diǎn)檢測(cè)
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";  //網(wǎng)絡(luò)模型
	string model_weight = "pose_iter_102000.caffemodel";//網(wǎng)絡(luò)訓(xùn)練權(quán)重

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

	//將輸入圖像轉(zhuǎn)成blob形式
	Mat blob = blobFromImage(src, 1.0 / 255, Size(modelWidth, modelHeight), Scalar(0, 0, 0));

	//將圖像轉(zhuǎn)換的blob數(shù)據(jù)輸入到網(wǎng)絡(luò)的第一層“image”層,見(jiàn)deploy.protxt文件
	net.setInput(blob, "image");

	//結(jié)果輸出
	Mat output = net.forward();
	int H = output.size[2];
	int W = output.size[3];

	for (int i = 0; i < nPoints; i++)
	{
		//結(jié)果預(yù)測(cè)
		Mat probMap(H, W, CV_32F, output.ptr(0, i)); 

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

		Point keypoint; //最大可能性手部關(guān)鍵點(diǎn)位置
		double classProb;  //最大可能性概率值
		minMaxLoc(probMap, NULL, &classProb, NULL, &keypoint);

		HandKeypoints[i] = keypoint; //結(jié)果輸出,即手部關(guān)鍵點(diǎn)所在坐標(biāo)
	}

	return true;
}

//手勢(shì)識(shí)別
bool Handpose_Recognition(vector<Point>&HandKeypoints, int& count)
{
	vector<int>fingers;
	//拇指
	if (HandKeypoints[tipIds[0]].x > HandKeypoints[tipIds[0] - 1].x)
	{	 
		//如果關(guān)鍵點(diǎn)'4'的x坐標(biāo)大于關(guān)鍵點(diǎn)'3'的x坐標(biāo),則說(shuō)明大拇指是張開的。計(jì)數(shù)1
		fingers.push_back(1);
	}
	else
	{
		fingers.push_back(0);
	}
	//其余的4個(gè)手指
	for (int i = 1; i < 5; i++)
	{
		if (HandKeypoints[tipIds[i]].y < HandKeypoints[tipIds[i] - 2].y)
		{
			//例:如果關(guān)鍵點(diǎn)'8'的y坐標(biāo)小于關(guān)鍵點(diǎn)'6'的y坐標(biāo),則說(shuō)明食指是張開的。計(jì)數(shù)1
			fingers.push_back(1);
		}
		else
		{
			fingers.push_back(0);
		}
	}

	//結(jié)果統(tǒng)計(jì)
	for (int i = 0; i < fingers.size(); i++)
	{
		if (fingers[i] == 1)
		{
			count++;
		}
	}

	return true;
}

//識(shí)別效果顯示
bool ShowResult(Mat& src, vector<Point>&HandKeypoints, int& count)
{
	//畫出關(guān)鍵點(diǎn)所在位置
	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);
	}

	//為了顯示騷操作,讀取模板圖片,作為識(shí)別結(jié)果
	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);
	}

	//將識(shí)別結(jié)果顯示在原圖中
	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;
}

總結(jié)

本文使用OpenCV C++實(shí)現(xiàn)一些簡(jiǎn)單的手勢(shì)識(shí)別,在這里僅為了提供一個(gè)算法思想,理解了算法思想自己想實(shí)現(xiàn)什么功能都會(huì)很簡(jiǎn)單。主要操作有以下幾點(diǎn)。

1、使用DNN模塊實(shí)現(xiàn)手部關(guān)鍵點(diǎn)檢測(cè)

2、利用各關(guān)鍵點(diǎn)所在位置來(lái)判定手指的張合狀態(tài)。

3、效果顯示(僅為了實(shí)現(xiàn)效果演示,可以省略)

以上就是C++ OpenCV實(shí)戰(zhàn)之手勢(shì)識(shí)別的詳細(xì)內(nèi)容,更多關(guān)于OpenCV手勢(shì)識(shí)別的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用C語(yǔ)言構(gòu)建基本的二叉樹數(shù)據(jù)結(jié)構(gòu)

    使用C語(yǔ)言構(gòu)建基本的二叉樹數(shù)據(jù)結(jié)構(gòu)

    這篇文章主要介紹了使用C語(yǔ)言使用C語(yǔ)言構(gòu)建基本的二叉樹數(shù)據(jù)結(jié)構(gòu),包括根據(jù)前序序列和中序序列構(gòu)建二叉樹的方法,需要的朋友可以參考下
    2015-08-08
  • 對(duì)比C語(yǔ)言中g(shù)etc()函數(shù)和ungetc()函數(shù)的使用

    對(duì)比C語(yǔ)言中g(shù)etc()函數(shù)和ungetc()函數(shù)的使用

    這篇文章主要介紹了對(duì)比C語(yǔ)言中g(shù)etc()函數(shù)和ungetc()函數(shù)的使用,是C語(yǔ)言入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-08-08
  • C++ Boost MultiIndex使用詳細(xì)介紹

    C++ Boost MultiIndex使用詳細(xì)介紹

    Boost是為C++語(yǔ)言標(biāo)準(zhǔn)庫(kù)提供擴(kuò)展的一些C++程序庫(kù)的總稱。Boost庫(kù)是一個(gè)可移植、提供源代碼的C++庫(kù),作為標(biāo)準(zhǔn)庫(kù)的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開發(fā)引擎之一,是為C++語(yǔ)言標(biāo)準(zhǔn)庫(kù)提供擴(kuò)展的一些C++程序庫(kù)的總稱
    2022-11-11
  • 詳解C語(yǔ)言中的memset()函數(shù)

    詳解C語(yǔ)言中的memset()函數(shù)

    這篇文章主要介紹了C語(yǔ)言中的memset()函數(shù),包括其與memcpy()函數(shù)的區(qū)別,需要的朋友可以參考下
    2015-08-08
  • C++之友元:友元函數(shù)和友元類詳解

    C++之友元:友元函數(shù)和友元類詳解

    友元是一種允許非類成員函數(shù)訪問(wèn)類的非公有成員的一種機(jī)制??梢园岩粋€(gè)函數(shù)指定為類的友元,也可以把整個(gè)類指定為另一個(gè)類的友元
    2013-09-09
  • 使用C語(yǔ)言詳解霍夫曼樹數(shù)據(jù)結(jié)構(gòu)

    使用C語(yǔ)言詳解霍夫曼樹數(shù)據(jù)結(jié)構(gòu)

    這篇文章主要介紹了使用C語(yǔ)言詳解霍夫曼樹數(shù)據(jù)結(jié)構(gòu),包括一道AMC相關(guān)的例題演示需要的朋友可以參考下
    2015-08-08
  • C++執(zhí)行Linux Bash命令的方法

    C++執(zhí)行Linux Bash命令的方法

    今天小編就為大家分享一篇C++執(zhí)行Linux Bash命令的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-07-07
  • Cocos2d-x UI開發(fā)之菜單類使用實(shí)例

    Cocos2d-x UI開發(fā)之菜單類使用實(shí)例

    這篇文章主要介紹了Cocos2d-x UI開發(fā)之菜單類使用實(shí)例,本文的代碼中含有詳細(xì)注釋,需要的朋友可以參考下
    2014-09-09
  • Qt控件點(diǎn)擊消息獲取的方法詳解

    Qt控件點(diǎn)擊消息獲取的方法詳解

    本文將利用Qt中的QLabel、QPushButton這兩個(gè)控件,為大家詳細(xì)介紹一下Qt控件點(diǎn)擊消息獲取的方法,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-06-06
  • Vscode搭建遠(yuǎn)程c開發(fā)環(huán)境的圖文教程

    Vscode搭建遠(yuǎn)程c開發(fā)環(huán)境的圖文教程

    很久沒(méi)有寫C語(yǔ)言了,今天抽空學(xué)習(xí)下C語(yǔ)言知識(shí),接下來(lái)通過(guò)本文給大家介紹Vscode搭建遠(yuǎn)程c開發(fā)環(huán)境的詳細(xì)步驟,本文通過(guò)圖文實(shí)例代碼相結(jié)合給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-11-11

最新評(píng)論

玉屏| 常山县| 宜宾市| 嘉荫县| 来凤县| 东丰县| 乐山市| 桃源县| 贡山| 米脂县| 柳州市| 辛集市| 三门县| 海门市| 贡山| 甘肃省| 镇雄县| 新泰市| 米脂县| 浦城县| 会理县| 梅河口市| 旺苍县| 东港市| 开封市| 贞丰县| 武夷山市| 白沙| 弥勒县| 金塔县| 登封市| 辉县市| 雷州市| 荃湾区| 湟源县| 建宁县| 上林县| 巢湖市| 京山县| 三明市| 邢台县|