日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于OPENCV的手势识别技术

發布時間:2024/1/1 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于OPENCV的手势识别技术 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

基于OPENCV的手勢識別技術

?

前言:
??本篇博客主要介紹基于OPENCV的手勢識別程序,代碼為C++,OPENCV版本為OPENCV4會有較為詳細的實現流程和源碼,并且做到源碼盡量簡單,注釋也自認為較為清晰,希望能幫助到大家。(源碼將放在文章末尾的鏈接中,代碼較為粗糙,有錯誤歡迎大家指出。)

?
?
一、手勢識別流程圖

??首先是對于流程圖的簡單說明:兩條線是分開進行的,兩者在比對分類之前是不會互相影響的(當然有部分函數,例如提取特征的函數,是在兩條線中都是會使用到的),因此可以分別完成兩條線的工作。如果作為需要分工的項目,可以按照這兩部分進行分工,最后進行整合。
?
??而關于兩條線的順序,個人認為是優先進行神經網絡的訓練部分(也就是下方線路),原因是對于要識別的手勢,首先要有對應的樣本,優先去尋找樣本,以此確定能夠識別的手勢。(當然,要是已經找到了樣本,先做上方線路也沒啥太大問題)
?
本篇文章的順序:
先講對于圖片的處理:
??因為先明白圖片如何處理,并知道提取特征的方法,才能理解要把什么東西放到神經網絡里,得到的數據又是什么。
再講神經網路的搭建:
??神經網路的搭建其實就是一個模板性的東西,計算機并不知道你要識別的東西到底是什么,是數字還是手勢,對于計算機來說它只是一堆數據,它只負責給你找到——你輸入的數據在網絡里跟哪個數據最為匹配,然后就給你輸出。
?
?
二、讀取圖片、獲取皮膚部分及二值化
?
1)讀取圖片部分:
??并沒有過多好說的,直接使用imread函數對圖片進行讀入。(注意,讀入的圖片應該是彩色的而不是灰度圖,否則無法進行后面的皮膚區域獲取)
?
2)獲取皮膚部分及二值化:
??關于皮膚部分的獲取,這里列出幾種算法。由于圖片的光照等的不同,不同算法的優劣也很難對比,各位自行選擇算法。
?
①基于RGB顏色空間的簡單閾值膚色識別:
?
??根據他人的研究,我們可以知道有這樣的一條判別式來用于膚色檢測
?
R>95 && G>40 && B>20 && R>G && R>B && Max(R,G,B)-Min(R,G,B)>15 && Abs(R-G)>15
?
??有了條判別式,我們就能夠很容易地寫出代碼來實現膚色檢測。但該算法對于光線的抗干擾能力較弱,光線稍微不好就識別不出皮膚點。

Mat getSkin(Mat& ImageIn)//獲取皮膚的區域,返回二值化圖像 {vector<Mat> r_g_b;//用于存放RGB分量split(ImageIn,r_g_b);//分離RGB分量,順序為B,G,RMat Binary = Mat::zeros(ImageIn.size(),CV_8UC1);Mat R = r_g_b[2];Mat G = r_g_b[1];Mat B = r_g_b[0];for (int i = 0; i < ImageIn.rows; i++){for (int j = 0; j < ImageIn.cols; j++){if (R.at<uchar>(i, j) > 95 && G.at<uchar>(i, j) > 40 && B.at<uchar>(i, j) > 20 &&R.at<uchar>(i, j) > G.at<uchar>(i, j) && R.at<uchar>(i, j) > B.at<uchar>(i, j) &&MyMax(R.at<uchar>(i, j), G.at<uchar>(i, j), B.at<uchar>(i, j)) - MyMin(R.at<uchar>(i, j), G.at<uchar>(i, j), B.at<uchar>(i, j)) > 15&& abs(R.at<uchar>(i, j) - G.at<uchar>(i, j)) > 15){Binary.at<uchar>(i, j) = 255;}}}return Binary; }

??代碼說明:首先對彩色圖片分離開R、G、B分量,然后根據公式,將每一個點的R、G、B分量代入公式中進行判斷,符合條件的點我們可以認為它是皮膚中的一個點。(其中的MyMax和MyMin是自寫的判斷大小函數)將認為是皮膚的點的值置為255,就可以達到二值化的效果。


??可以看到除了部分因為光線問題導致的陰影沒有被識別出來以外,幾乎所有皮膚點都被識別出來了,效果還過得去。
?
?
②基于橢圓皮膚模型的皮膚檢測
?
??研究發現,將皮膚映射到YCrCb空間,則在YCrCb空間中皮膚的像素點近似成一個橢圓的分布。因此如果我們得到了一個CrCb的橢圓,對于一個點的坐標(Cr, Cb),我們只需判斷它是否在橢圓內(包括邊界)就可以得知它是不是膚色點。
??該算法對于光線的敏感性沒有這么高,基本上該檢測到的皮膚都能夠檢測到,抗干擾能力相對較強。(原因大概是YCrCb中Y分量表示明亮度,而而“Cr”和“Cb” 表示的則是色度,作用是描述影像色彩及飽和度)

Mat getSkin2(Mat& ImageIn) {Mat Image = ImageIn.clone();//復制輸入的圖片//利用OPENCV自帶的ellipse函數生成一個橢圓的模型Mat skinCrCbHist = Mat::zeros(Size(256, 256), CV_8UC1);ellipse(skinCrCbHist, Point(113, 155.6), Size(23.4, 15.2), 43.0, 0.0, 360.0, Scalar(255, 255, 255), -1);Mat ycrcb_Image;cvtColor(Image, ycrcb_Image, COLOR_BGR2YCrCb);//用cvtColor函數將圖片轉換為YCrCb色彩空間的圖片Mat Binary = Mat::zeros(Image.size(), CV_8UC1);//輸出的二值化圖片vector<Mat>y_cr_cb;//用于存放分離開的YCrCb分量split(ycrcb_Image, y_cr_cb);//分離YCrCb分量,順序是Y、Cr、CbMat CR = y_cr_cb[1];Mat CB = y_cr_cb[2];for (int i = 0; i < Image.rows; i++){for (int j = 0; j < Image.cols; j++){if (skinCrCbHist.at<uchar>(CR.at<uchar>(i,j), CB.at<uchar>(i,j)) > 0)//在橢圓內的點置為255{Binary.at<uchar>(i, j) = 255;}}}return Binary; }

??代碼說明:首先用OPENCV自帶的函數生成一個橢圓的模型,然后將RGB圖片轉換為YCrCb圖片,分離開Y,Cr,Cb,對于原圖片里的每一個點,判斷其是否在橢圓內,在,則認為該點是一個皮膚點。

??可以看到幾乎所有的皮膚部分都被識別出來了,效果比上一個算法看上去要好不少。
?
?
③基于YCrCb顏色空間Cr,Cb范圍篩選法
?
??這種方法與第一種方法在原理上是一樣的,只不過這次將顏色空間變為了YCrCb空間。同樣的,我們有公式
?
Cr>133 && Cr<173 && Cb>77 && Cb<127
?
??將CrCb分量代入這條公式進行判斷,就能得到皮膚點,此處不進行過多的講述。

Mat getSkin3(Mat& ImageIn) {Mat Image = ImageIn.clone();Mat ycrcb_Image;cvtColor(Image, ycrcb_Image,COLOR_BGR2YCrCb);vector<Mat>y_cr_cb;split(ycrcb_Image, y_cr_cb);Mat CR = y_cr_cb[1];Mat CB = y_cr_cb[2];Mat ImageOut = Mat::zeros(Image.size(), CV_8UC1);for (int i = 0; i < Image.rows; i++){for (int j = 0; j < Image.cols; j++){if (CR.at<uchar>(i, j) > 133 && CR.at<uchar>(i, j) < 173 && CB.at<uchar>(i, j) > 77 && CB.at<uchar>(i, j) < 127){ImageOut.at<uchar>(i, j) = 255;}}}return ImageOut; }


??我們可以看到效果也是挺不錯的(相比第一種方法得到的),與第二種方法不分伯仲。
?
?
④YCrCb顏色空間Cr分量+Otsu法閾值分割
?
??首先我們要知道YCrCb色彩空間是什么:YCrCb即YUV,其中“Y”表示明亮度(Luminance或Luma),也就是灰階值;而“U”和“V” 表示的則是色度(Chrominance或Chroma),作用是描述影像色彩及飽和度,用于指定像素的顏色。其中,Cr反映了RGB輸入信號紅色部分與RGB信號亮度值之間的差異。(來自百度百科)
?
所以,該方法的原理也十分簡單:
?
1、將RGB圖像轉換到YCrCb顏色空間,提取Cr分量圖像

2、對Cr做自適應二值化閾值分割處理(Otsu法)

Mat getSkin4(Mat& ImageIn) {Mat Image = ImageIn.clone();Mat ycrcb_Image;cvtColor(Image, ycrcb_Image, COLOR_BGR2YCrCb);//轉換色彩空間vector<Mat>y_cr_cb;split(ycrcb_Image, y_cr_cb);//分離YCrCbMat CR = y_cr_cb[1];//圖片的CR分量Mat CR1;Mat Binary = Mat::zeros(Image.size(), CV_8UC1);GaussianBlur(CR, CR1, Size(3, 3), 0, 0);//對CR分量進行高斯濾波,得到CR1(注意這里一定要新建一張圖片存放結果)threshold(CR1, Binary, 0, 255, THRESH_OTSU);//用系統自帶的threshold函數,對CR分量進行二值化,算法為自適應閾值的OTSU算法return Binary; }

??代碼說明:前面的轉換色彩空間不再贅述,關鍵點在于系統的二值化函數threshold,使用了OTSU的算法對圖像前景和背景進行區分。(請不清楚threshold函數使用和OTSU算法的讀者自行查找資料,這里由于篇幅問題不展開解釋)

可以看到效果也不錯,基本也能識別出來。
?
?
⑤OPENCV自帶的膚色檢測類AdaptiveSkinDetector
(但是我沒用過,僅是放上來讓大家知道…)
?
?
總結:
??對于以上幾種方法,很難說到底哪一種會更加準確,根據環境不同,圖片不同,算法之間的優劣也有差別。因此有條件的可以每一種都嘗試,看看在自己的環境下哪種算法的效果最好。我在代碼中使用的是方法②,也僅供參考。
?
?
三、獲取輪廓及特征(特征為傅里葉描繪子)
?
原理:(請務必看看)
?
1)什么是圖像的特征?
??或許換個問題你就理解了,什么是人的特征?我們或許可以認為,會直立行走,能制造和使用工具,這些就是人的特征。然后,進一步,什么是手勢0的特征?是一根手指都沒有伸出來,這就是手勢0的特征。手勢1呢?只伸出了一根手指對吧?這就是特征。
?
2)為什么要獲取圖像特征?
??我們舉個比較簡單(但是并不真實)的例子:你輸入了一張二值化后的圖片(假設是手勢2,即我們前面的圖片里的手勢),我們通過一個獲取特征的函數,獲得了這個手勢的特征值,假設這一連串的值是[1,2,3,4,5,6]。
??而在神經網絡里,手勢0的特征值被認為是[4,4,3,3,2,2],手勢1被認為是[9,8,7,6,5,4,],手勢2被認為是[1,2,3,4,5,5]。那么你輸入了[1,2,3,4,5,6],你認為最匹配的是哪個手勢呢?顯然就是手勢2。
??而這就是神經網絡的工作原理。你輸入一串代表特征的數值,預測函數會在神經網絡里去找跟這串數值最相似的那個結果,把它告訴你:這就是我判斷的手勢結果。
??而圖像的特征,就是上面我們所說的[1,2,3,4,5,6]這串數字。我們要想辦法獲取這個特征向量,扔到神經網絡里去讓它識別。至于神經網絡怎么知道[1,2,3,4,5,5]代表手勢2,[4,4,3,3,2,2]代表手勢0,我們在后面的神經網絡部分會說到。
?
3)要獲取什么特征,以及怎么獲取?
??首先,對于手勢來說,我們獲取的特征應該是對旋轉和縮放都不敏感的。為什么呢?你總不希望你的手旋轉了45°系統就識別不出來了吧?你總不希望你離得遠一點系統就識別不出來了吧?所以,我們要找到一個方法來描述你的手勢,并且這個方法對于旋轉和縮放都不那么敏感。
??而在本項目中,我們使用的是傅里葉描繪子,這是一種邊界描繪子(就是專門用來描繪邊界的),它對于旋轉以及縮放并不十分敏感。這里簡單說一下傅里葉描繪子的原理。對于坐標系內的一個點,通常我們用(X,Y)來表示。但這就是兩個數值了,有沒有什么辦法用一個數值來表示?幸好我們有復數這個東西!對于點(X,Y)我們可以寫成X+iY,這不就變成一個數了嗎?這能將一個二維的問題轉變為一維的問題。
??而我們應該知道,任何一個周期函數都可以展開為傅里葉級數,所以我們可以通過傅里葉級數來描繪一個周期變換的函數。那么你可能會問,這個二值化后的手勢圖,跟周期變換函數有什么關系啊?還真有!沿邊界曲線上一個動點s[k] = [x(k),y(k)]就是一個以形狀邊界周長為周期的函數。
??為什么外輪廓是周期函數?假設有一個點在外輪廓上向著一個方向移動,轉了一圈,兩圈…,那這是不是周期函數?所以問題就解決了,我們可以用傅里葉描繪子來描繪手勢的外輪廓,從而描繪手勢。
??關于傅里葉描繪子更詳細的介紹以及推導建議各位去查閱相關資料,這里不打算過多說(畢竟估計也沒幾個人感興趣)
?
傅里葉描繪子原理
?
我這里直接給出計算公式:

其中a(u)就是傅里葉級數的第n項,k就是圖像中第k個點。
?
??當然,這個公式可以使用歐拉公式進行展開,對于編程實現來說會更加簡單。但是僅僅是這個公式是不夠的,因為這樣計算出來的傅里葉描繪子與形狀尺度,方向和曲線起始點S0都有關系,顯然不是我們想要的。所以,我們還要以a(1)為基準,進行歸一化,最終得到:

??由于形狀的能量大多集中在低頻部分,高頻部分一般很小且容易受到干擾,所以我們只取前12位就足夠了。
?
?
具體實現:
?
??首先,要描繪手勢的外輪廓,我們就需要先得到手勢的外輪廓。幸好,OPENCV有對應的函數讓我們使用,就是findContours函數。

Mat ImageBinary = ImageIn;//二值化的圖片(傳入時應該就已經是二值化的圖片了)vector<vector<Point>> contours;//定義輪廓向量findContours(ImageBinary, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);//尋找輪廓int max_size = 0;//最大的輪廓的size(通常來說就是我們的手勢輪廓)int contour_num = 0;//最大的輪廓是圖片中的第contour_num個輪廓for (int i = 0; i < contours.size(); i++)//找到最大的輪廓,記錄在contour_num中{if (contours[i].size() > max_size){max_size = contours[i].size();contour_num = i;}}

這樣,我們就找到了圖片的全部輪廓,并且存在了二維向量contours中。然后我們就可以計算傅里葉描繪子了。

/***計算圖像的傅里葉描繪子***//***傅里葉變換后的系數儲存在f[d]中***/vector<float>f;vector<float>fd;//最終傅里葉描繪子前14位Point p;for (int i = 0; i < max_size; i++)//主要的計算部分{float x, y, sumx = 0, sumy = 0;for (int j = 0; j < max_size; j++){p = contours[contour_num].at(j);x = p.x;y = p.y;sumx += (float)(x * cos(2 * CV_PI * i * j / max_size) + y * sin(2 * CV_PI * i * j / max_size));sumy += (float)(y * cos(2 * CV_PI * i * j / max_size) - x * sin(2 * CV_PI * i * j / max_size));}f.push_back(sqrt((sumx * sumx) + (sumy * sumy)));}fd.push_back(0);//放入了標志位‘0’,并不影響最終結果for (int k = 2; k < 16; k++)//進行歸一化,然后放入最終結果中{f[k] = f[k] / f[1];fd.push_back(f[k]);}out = Mat::zeros(1, fd.size(), CV_32F);//out是用于輸出的手勢特征for (int i = 0; i < fd.size(); i++){out.at<float>(i) = fd[i];}

計算完了傅里葉描繪子,我們就算是拿到了手勢的特征了。
?
?
四:找到訓練樣本
?
??首先,我們要明確找到訓練樣本是很重要的,因為訓練樣本決定了你能識別到的是什么。要是你找到了身份證數字的訓練樣本,那么你就能識別身份證的數字;要是你找到了車牌號碼的數字樣本,那么你就能識別車牌;而我們在這個項目要找到手勢的樣本。
??不過在這里我可以將我找到的樣本分享給大家,我將其一并打包在了項目文件中,大家有需要的可以下載我的工程文件。其中有10個手勢,從0到9,希望能幫助到大家。
?
?
五:樣本的特征提取
?
??對于樣本的特征提取,其實跟前面我們說到的特征提取沒有什么不同。只不過我們這里需要的是對大量的樣本進行特征提取,并且將其存放在一個文件中,方便我們后續進行的神經網絡訓練。

void getAnnXML() {FileStorage fs("ann_xml.xml", FileStorage::WRITE);if (!fs.isOpened()){cout << "failed to open " << "/n";}Mat trainData;//用于存放樣本的特征數據Mat classes = Mat::zeros(200, 1, CV_8UC1);//用于標記是第幾類手勢char path[60];//樣本路徑Mat Image_read;//讀入的樣本for (int i = 0; i < 4; i++)//第i類手勢 比如手勢1、手勢2{for (int j = 1; j < 51; j++)//每個手勢設置50個樣本{sprintf_s(path, "D:\\數字圖像處理\\Gesture_Picture\\%d_ (%d).png", i, j);Image_read = imread(path, 1);Mat Binary = OTSU_Binary(Image_read, 1);//對輸入的圖片進行二值化Mat dst_feature;//該樣本對應的特征值getFeatures(Binary,dst_feature);trainData.push_back(dst_feature);classes.at<uchar>(i * 50 + j - 1) = i;}}fs << "TrainingData" << trainData;fs << "classes" << classes;fs.release();cout << "訓練矩陣和標簽矩陣搞定了!" << endl;

??在該段代碼運行完之后,在文件夾下會生成一個ann_xml.xml文件,里面存放著所有樣本的特征值和一個標簽矩陣(標簽矩陣如下圖)

??其中的getFeatures函數與前面用到的特征提取函數是完全一致的。但是在這部分我們是可以省去找到皮膚部分的函數,因為樣本給到我們的就已經是只有皮膚部分的圖片了。所以我們只需要進行二值化并找到最大的輪廓,提取特征。
?
?
六:訓練神經網絡
?
??一些想法:神經網絡的訓練看起來像是比較難的一個部分,但其實并不是,看起來難的原因也許是之前并沒有接觸過神經網絡,覺得無從下手。(所以在寫這部分代碼之前應該先去了解一下什么是神經網絡,不需要過于深入,知道基本的工作原理即可)但其實只要寫過一次,就會發現這是一個模板性的東西,寫過了就是從0到1的變化。當然在寫該部分的代碼時會遇到很多的小問題,比如OPENCV版本的不同,有部分網上的代碼使用的是OPENCV2,對于OPENCV3就不適用了。
?
廢話不多說了直接開始吧,首先是訓練部分的函數代碼:

void ann_train(Ptr<ANN_MLP>& ann, int numCharacters, int nlayers)//神經網絡訓練函數,numCharacters設置為4,nlayers設置為24 {Mat trainData, classes;FileStorage fs;fs.open("ann_xml.xml", FileStorage::READ);fs["TrainingData"] >> trainData;fs["classes"] >> classes;Mat layerSizes(1, 3, CV_32SC1); //3層神經網絡layerSizes.at<int>(0) = trainData.cols; //輸入層的神經元結點數,設置為15layerSizes.at<int>(1) = nlayers; //1個隱藏層的神經元結點數,設置為24layerSizes.at<int>(2) = numCharacters; //輸出層的神經元結點數為:4ann->setLayerSizes(layerSizes);ann->setTrainMethod(ANN_MLP::BACKPROP, 0.1, 0.1);//后兩個參數: 權梯度項的強度(一般設置為0.1) 動量項的強度(一般設置為0.1)ann->setActivationFunction(ANN_MLP::SIGMOID_SYM);ann->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 5000, 0.01));//后兩項參數為迭代次數和誤差最小值Mat trainClasses;//用于告訴神經網絡該特征對應的是什么手勢trainClasses.create(trainData.rows, numCharacters, CV_32FC1);for (int i = 0; i < trainData.rows; i++){for (int k = 0; k < trainClasses.cols; k++){if (k == (int)classes.at<uchar>(i)){trainClasses.at<float>(i, k) = 1;}elsetrainClasses.at<float>(i, k) = 0;}}//Mat weights(1 , trainData.rows , CV_32FC1 ,Scalar::all(1) );ann->train(trainData, ml::ROW_SAMPLE, trainClasses);cout << " 訓練完了! " << endl; }

代碼說明:
?
1、首先我們要將ann_xml.xml文件中的數據讀入到程序中,包括樣本的特征還有標簽矩陣。
?
2、然后設置神經網絡的基本參數。首先,設置3層的神經網絡,即1層輸入層,1層隱藏層,1層輸出層。
?
輸入層的結點數?這卻決于我們一張圖片獲得的特征值的數量。(根據第三大點中說到的,我們取傅里葉描繪子的前12位)
?
隱藏層的結點數?我們在該項目中將隱藏層設置為24。
?
輸出層的結點數?我們最后想讓神經網絡識別幾個手勢,就設置為多少。比如該項目最后要識別四個手勢,就設置為4。
?
setTrainMethod(訓練方法)?使用ANN_MLP::BACKPROP,即反向傳播神經網絡,這個較為常用。
?
setActivationFunction(激活函數)?一般常用的就是Sigmoid 函數,即下圖的函數

setTermCriteria為迭代終止準則的設置
?
trainClasses變量 ?這個變量建議大家畫個圖理解一下。到底和class的區別是什么?雖然都是作為一個標簽矩陣,但還是有一些不同。
?
train函數?這個就是神經網絡的訓練函數,把相應的參數代入進去,就能夠訓練出一個神經網絡。
?
??需要注意的是,這個函數不需要每一次都識別都運行,(而且運行該函數需要花費較多的時間)只要我們運行一次,把神經神經網絡保存下來,下次識別的時候直接讀取神經網絡,就可以進行識別了。具體如下:

Ptr<ANN_MLP> ann = ANN_MLP::create();ann_train(ann, 4, 24);ann->save("ann_param");

這樣就能保存一個ann_param的文件了,下次使用只需要

Ptr<ANN_MLP> ann = ANN_MLP::load("ann_param");//讀取神經網絡

?
?
七:比對分類,預測結果
?
預測函數的作用就是幫助我們對比圖片的特征與神經網絡里哪個最像,然后把這個作為結果輸出。所以在這里我們要獲取要預測的圖片的特征,然后使用預測函數predict進行預測。最后找到邏輯最大值,作為結果。

int classify(Ptr<ANN_MLP>& ann, Mat& Gesture)//預測函數,找到最符合的一個手勢(輸入的圖片是二值化的圖片) {int result = -1;Mat output(1, 4, CV_32FC1); //1*4矩陣Mat Gesture_feature;getFeatures(Gesture, Gesture_feature);ann->predict(Gesture_feature, output);Point maxLoc;double maxVal;minMaxLoc(output, 0, &maxVal, 0, &maxLoc);//對比值,看哪個是最符合的。result = maxLoc.x;return result; }

output變量存儲的是“該手勢對應每個手勢的可能性”,找到最大的那個可能,作為結果輸出。至此,我們獲得了我們想要的那個結果。
?
?
結尾:
?
我們最后把main函數放上來,這樣就能更清楚地看清流程:

int main(void) {Mat ImageIn = imread("D://VSprogram/DistinguishGesture/MyTestPicture/test.jpg", 1);//輸入彩色圖片Mat Binary = skinMask(ImageIn);//獲取二值化后的手勢圖Mat Binary2 = OPENorCLOSE_Operation(Binary, 0, 3, 3);//對手勢圖進行閉運算,使一些小的點連起來//只需要執行一次,獲取了標簽矩陣和神經網絡后就不再需要執行//getAnnXML();//Ptr<ANN_MLP> ann = ANN_MLP::create();//ann_train(ann, 4, 24);//ann->save("ann_param");//cout << "搞定" << endl;//Mat contour = showContour(Binary2);//獲得手勢的輪廓圖//imshow("Contour", contour);//畫出手勢的輪廓圖Ptr<ANN_MLP> ann = ANN_MLP::load("ann_param");//讀取神經網絡int result = classify(ann, Binary2);//預測最終結果cout << "手勢是:"<<result << endl;//輸出waitKey(0);return 0; }

然后我再把源代碼鏈接放在這里,需要的可以下載來看看(我盡量把積分調低,讓大家即使覺得沒啥用也不會虧多少積分)
?
https://download.csdn.net/download/qq_42884797/13658137
?
參考文獻及鏈接:
?
趙三琴,丁為民,劉徳營.基于傅里葉描述子的稻飛虱形狀識別[J].農業機械學報,2009,40(8):181~184
https://blog.csdn.net/qq_41562704/article/details/88975569
https://blog.csdn.net/javastart/article/details/97615918

總結

以上是生活随笔為你收集整理的基于OPENCV的手势识别技术的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。