欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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、效果顯示

接下來就來看看本案例具體是怎么實(shí)現(xiàn)的吧?。。?/p>

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

如圖所示,為我們的手部關(guān)鍵點(diǎn)所在位置。第一步,我們需要檢測(cè)手部21個(gè)關(guān)鍵點(diǎn)。我們使用深度神經(jīng)網(wǎng)絡(luò)DNN模塊來完成這件事。通過使用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”層,見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)通過DNN檢測(cè)出21個(gè)手部關(guān)鍵點(diǎn)所在位置。接下來,我們需要使用這些關(guān)鍵點(diǎn)進(jìn)行簡(jiǎn)單的手勢(shì)識(shí)別。

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

2.1算法原理

本案例實(shí)現(xiàn)手勢(shì)識(shí)別是通過比較關(guān)鍵點(diǎn)位置確定的。首先拿出每個(gè)手指尖關(guān)鍵點(diǎn)索引(即4、8、12、16、20)。接下來,對(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。通過統(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),則說明大拇指是張開的。計(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),則說明食指是張開的。計(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é)果顯示

通過以上步驟,我們已經(jīng)有了手部關(guān)鍵點(diǎn)所在坐標(biāo)位置以及對(duì)應(yīng)的手勢(shì)結(jié)果,接下來就進(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”層,見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),則說明大拇指是張開的。計(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),則說明食指是張開的。計(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)所在位置來判定手指的張合狀態(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)文章

最新評(píng)論