千家信息网

C++中怎么用OpenCV实现手势识别

发表于:2025-02-07 作者:千家信息网编辑
千家信息网最后更新 2025年02月07日,本篇内容介绍了"C++中怎么用OpenCV实现手势识别"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
千家信息网最后更新 2025年02月07日C++中怎么用OpenCV实现手势识别

本篇内容介绍了"C++中怎么用OpenCV实现手势识别"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一、手部关键点检测

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

1.1 功能源码

//手部关键点检测bool HandKeypoints_Detect(Mat src, vector&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&HandKeypoints, int& count){        vectorfingers;        //拇指        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&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);        }        //为了显示骚操作,读取模板图片,作为识别结果        vectorimageList;        string filename = "images/";        glob(filename, imageList);        vectorTemp;        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(vectorimages){        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#include#includeusing 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&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&HandKeypoints, int& count){        vectorfingers;        //拇指        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&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);        }        //为了显示骚操作,读取模板图片,作为识别结果        vectorimageList;        string filename = "images/";        glob(filename, imageList);        vectorTemp;        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(vectorimages){        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(){        vectorimageList;        string filename = "test/";        glob(filename, imageList);        vectorimages;        for (int i = 0; i < imageList.size(); i++)        {                Mat src = imread(imageList[i]);                vectorHandKeypoints(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;}

"C++中怎么用OpenCV实现手势识别"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

0