基于opencv的手势识别(HSV)控制鼠标
生活随笔
收集整理的這篇文章主要介紹了
基于opencv的手势识别(HSV)控制鼠标
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
基于opencv的手勢識別和鼠標(biāo)控制
opencv3.4.5和vs2015
新建工程->空項(xiàng)目->新建項(xiàng)
攝像頭使用
VideoCapture capture;capture.open(0);if (!capture.isOpened()){//打開失敗返回-1cout << "No camera!\n" << endl;return -1;while (1) {capture >> frame;//刷新//Mat frame = imread("1.jpg"); //工程測試if (frame.empty())//幀空跳出循環(huán)break;imshow("frame_", frame);}}分類器的使用
這里使用了自己做的分類器用作手勢識別
定義、讀取
CascadeClassifier open_palm_cascade;CascadeClassifier closed_palm_cascade;open_palm_cascade.load("palm.xml");closed_palm_cascade.load("closed_palm.xml");if (open_palm_cascade.empty()) {cout << "Could not load open_palm configuration file! ""Check directory! " << endl << "Press Q to Quit!" << endl;while (char(waitKey(0)) != 'q') {}return -2;}if (closed_palm_cascade.empty()) {cout << "Could not load closed_palm configuration file! ""Check directory! " << endl << "Press Q to Quit!" << endl;while (char(waitKey(0)) != 'q') {}return -2;}使用
open_palm_cascade.detectMultiScale(frame, open_palms, 1.3, 4, 0, Size(50, 50)); closed_palm_cascade.detectMultiScale(frame, closed_palms, 1.3, 4, 0, Size(50, 50));if (closed_palms.size() != 0) {for (int i = 0; i < closed_palms.size(); i++) {//cout << "=============Detected a closed_palm!=============" << endl;// Top left and bottom right points of rectangle.Point closed_palm_rect_p1(closed_palms[i].x, closed_palms[i].y);Point closed_palm_rect_p2(closed_palms[i].x + closed_palms[i].width, closed_palms[i].y + closed_palms[i].height);// Draw the rectangle in the image.rectangle(frame, closed_palm_rect_p1, closed_palm_rect_p2, Scalar(0, 255, 0));putText(frame, "Closed Palm", closed_palm_rect_p1, FONT_HERSHEY_SIMPLEX,1, Scalar(0, 255, 0), 1, 5, false);hand_mode[0] = 1;}}for (int i = 0; i < open_palms.size(); i++) {//cout << "=============Detected an open_palm!=============" << endl;// Top left and bottom right points of rectangle.Point open_palm_rect_p1(open_palms[i].x, open_palms[i].y);Point open_palm_rect_p2(open_palms[i].x + open_palms[i].width, open_palms[i].y + open_palms[i].height);// Draw the rectangle in the image.rectangle(frame, open_palm_rect_p1, open_palm_rect_p2, Scalar(255, 0, 0));putText(frame, "Open Palm", open_palm_rect_p1, FONT_HERSHEY_SIMPLEX,1, Scalar(255, 0, 0), 1, 5, false);hand_mode[0] = 2;}圖像處理、濾波
medianBlur(frame, frame, 5);cvtColor(frame, frame_hsv, CV_BGR2HSV);//轉(zhuǎn)HSV圖像split(frame_hsv, frameSplit);//三通道圖像分離//顯示HSV 3通道//imshow("WIN_H", frameSplit[0]);//imshow("WIN_S", frameSplit[1]);//imshow("WIN_V", frameSplit[2]);///Mat dstTemp1(frame.rows, frame.cols, CV_8UC1);Mat dstTemp2(frame.rows, frame.cols, CV_8UC1);// 對HSV空間進(jìn)行量化,得到2值圖像,亮的部分為手的形狀inRange(frame_hsv, Scalar(0, 50, 30), Scalar(20, 100, 256), dstTemp1);inRange(frame_hsv, Scalar(156, 30, 30), Scalar(180, 170, 256), dstTemp2);bitwise_or(dstTemp1, dstTemp2, mask);//按位或//imshow("mask", mask); // 形態(tài)學(xué)操作,去除噪聲,并使手的邊界更加清晰,提取邊緣Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));//返回指定形狀和尺寸的結(jié)構(gòu)元素,3*3的矩陣erode(mask, mask, element);//腐蝕morphologyEx(mask, mask, MORPH_OPEN, element);dilate(mask, mask, element);//膨脹morphologyEx(mask, mask, MORPH_CLOSE, element);//frame.copyTo(dst, mask);frame.copyTo(show_img, mask);輪廓提取、重心計(jì)算
//尋找最外層輪廓 //只保存拐點(diǎn)信息findContours(mask, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);// 去除偽輪廓for (size_t i = 0; i < contours.size(); i++) {if (fabs(contourArea(Mat(contours[i]))) > 30000) {//判斷手進(jìn)入?yún)^(qū)域的閾值filterContours.push_back(contours[i]);}}// 畫輪廓drawContours(show_img, filterContours, -1, Scalar(0, 0, 255), 3/*, 8, hierarchy*/);// 得到輪廓的凸包絡(luò)for (size_t j = 0; j<filterContours.size(); j++) {convexHull(Mat(filterContours[j]), hull, true);int hullcount = (int)hull.size();for (int i = 0; i<hullcount - 1; i++) {line(show_img, hull[i + 1], hull[i], Scalar(255, 0, 0), 2, 0);}line(show_img, hull[hullcount - 1], hull[0], Scalar(255, 0, 0), 2, 0);}//重心//Moments moment = moments(mask, true);Point center(moment.m10 / moment.m00, moment.m01 / moment.m00);circle(show_img, center, 8, Scalar(0, 0, 255), CV_FILLED);//畫紅色實(shí)心圓鼠標(biāo)控制
TEMP.x = center.x - LAST_CENTER.x;TEMP.y = center.y - LAST_CENTER.y;LAST_CENTER.x = center.x;LAST_CENTER.y = center.y;///if (hand_mode[0] == 1 && hand_mode[1] == 1){mouse_event(MOUSEEVENTF_LEFTDOWN, TEMP.x, TEMP.y, 0, 0);mouse_event(MOUSEEVENTF_MOVE, TEMP.x, TEMP.y, 0, 0);cout << "leftdown" << endl;}else{mouse_event(MOUSEEVENTF_LEFTUP, TEMP.x, TEMP.y, 0, 0);mouse_event(MOUSEEVENTF_MOVE, TEMP.x, TEMP.y, 0, 0);cout << "up" << endl;}hand_mode[1] = hand_mode[0];鼠標(biāo)取閾值
if (mode == "test"){//閾值測試while (1) {capture >> frame;//刷新if (frame.empty())//幀空跳出循環(huán)break;//// 中值濾波,去除椒鹽噪聲medianBlur(frame, frame, 5);cvtColor(frame, frame_hsv, CV_BGR2HSV);//轉(zhuǎn)HSV圖像setMouseCallback("frame_hsv", on_mouse, &frame_hsv);//鼠標(biāo)取閾值imshow("frame_hsv", frame);if (cvWaitKey(20) == 'q')break;}} void on_mouse(int EVENT, int x, int y, int flags, void* userdata) {Mat hh;hh = *(Mat*)userdata;Point p(x, y);switch (EVENT){case EVENT_LBUTTONDOWN:{//cout << "h=" << hh.at<Vec3b>(p)[0] << endl;//cout << "s=" << hh.at<Vec3b>(p)[1] << endl;//cout << "v=" << hh.at<Vec3b>(p)[2] << endl;printf("h=%d\t", hh.at<Vec3b>(p)[0]);printf("s=%d\t", hh.at<Vec3b>(p)[1]);printf("v=%d\n", hh.at<Vec3b>(p)[2]);circle(hh, p, 2, Scalar(255), 3);}break;} }整體程序
/* *日期:12月15日 *功能:手勢識別 *原理簡述:預(yù)處理->HSV三通道閾值分割圖像->提取輪廓->計(jì)算重心點(diǎn)->重心每幀偏差用來控制鼠標(biāo)移動*/ #include "opencv2/opencv.hpp" #include <windows.h>using namespace cv; using namespace std;POINT TEMP; POINT LAST_CENTER;vector<Rect> open_palms; vector<Rect> closed_palms;void skinExtract(const Mat &frame, Mat &skinArea); void on_mouse(int EVENT, int x, int y, int flags, void* userdata);int main(int argc, char* argv[]) {Mat frame, skinArea;Mat frame_hsv;//HSV圖像Mat mask(frame.rows, frame.cols, CV_8UC1); // 2值掩膜Mat frameSplit[4];//分離通道Mat show_img;//結(jié)果圖像vector<vector<Point> > contours;// 輪廓vector<Vec4i> hierarchy;//四維int向量// 輪廓的結(jié)構(gòu)信息vector< vector<Point> > filterContours; // 篩選后的輪廓vector< Point > hull; // 凸包絡(luò)的點(diǎn)集//攝像頭初始化、開啟VideoCapture capture;string mode;int hand_mode[3];capture.open(0);if (!capture.isOpened()){//打開失敗返回-1cout << "No camera!\n" << endl;return -1;}/CascadeClassifier open_palm_cascade;CascadeClassifier closed_palm_cascade;open_palm_cascade.load("palm.xml");closed_palm_cascade.load("closed_palm.xml");if (open_palm_cascade.empty()) {cout << "Could not load open_palm configuration file! ""Check directory! " << endl << "Press Q to Quit!" << endl;while (char(waitKey(0)) != 'q') {}return -2;}if (closed_palm_cascade.empty()) {cout << "Could not load closed_palm configuration file! ""Check directory! " << endl << "Press Q to Quit!" << endl;while (char(waitKey(0)) != 'q') {}return -2;}// Start the open_palm and eye detection phasecin >>mode;//輸入模式if (mode == "hsv") {while (1) {capture >> frame;//刷新//Mat frame = imread("1.jpg"); //工程測試if (frame.empty())//幀空跳出循環(huán)break;///skinArea.create(frame.rows, frame.cols, CV_8UC1);//和原圖像等行數(shù)/等列數(shù)的/8位無符號通/道數(shù)1//skinExtract(frame, skinArea);//找到皮膚// 中值濾波,去除椒鹽噪聲medianBlur(frame, frame, 5);cvtColor(frame, frame_hsv, CV_BGR2HSV);//轉(zhuǎn)HSV圖像split(frame_hsv, frameSplit);//三通道圖像分離//顯示HSV 3通道//imshow("WIN_H", frameSplit[0]);//imshow("WIN_S", frameSplit[1]);//imshow("WIN_V", frameSplit[2]);///Mat dstTemp1(frame.rows, frame.cols, CV_8UC1);Mat dstTemp2(frame.rows, frame.cols, CV_8UC1);// 對HSV空間進(jìn)行量化,得到2值圖像,亮的部分為手的形狀inRange(frame_hsv, Scalar(0, 30, 30), Scalar(40, 170, 256), dstTemp1);inRange(frame_hsv, Scalar(156, 30, 30), Scalar(180, 170, 256), dstTemp2);bitwise_or(dstTemp1, dstTemp2, mask);//按位或//imshow("mask", mask);// 形態(tài)學(xué)操作,去除噪聲,并使手的邊界更加清晰,提取邊緣Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));//返回指定形狀和尺寸的結(jié)構(gòu)元素,3*3的矩陣erode(mask, mask, element);//腐蝕morphologyEx(mask, mask, MORPH_OPEN, element);dilate(mask, mask, element);//膨脹morphologyEx(mask, mask, MORPH_CLOSE, element);//frame.copyTo(dst, mask);frame.copyTo(show_img, mask);//imshow("mask", mask);///清空contours.clear();hierarchy.clear();filterContours.clear();///尋找最外層輪廓 //只保存拐點(diǎn)信息findContours(mask, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);// 去除偽輪廓for (size_t i = 0; i < contours.size(); i++){if (fabs(contourArea(Mat(contours[i]))) > 30000){//判斷手進(jìn)入?yún)^(qū)域的閾值filterContours.push_back(contours[i]);}}// 畫輪廓drawContours(show_img, filterContours, -1, Scalar(0, 0, 255), 3/*, 8, hierarchy*/);// 得到輪廓的凸包絡(luò)for (size_t j = 0; j<filterContours.size(); j++){convexHull(Mat(filterContours[j]), hull, true);int hullcount = (int)hull.size();for (int i = 0; i<hullcount - 1; i++){line(show_img, hull[i + 1], hull[i], Scalar(255, 0, 0), 2, 0);}line(show_img, hull[hullcount - 1], hull[0], Scalar(255, 0, 0), 2, 0);}//重心//Moments moment = moments(mask, true);Point center(moment.m10 / moment.m00, moment.m01 / moment.m00);circle(show_img, center, 8, Scalar(0, 0, 255), CV_FILLED);//畫紅色實(shí)心圓TEMP.x = center.x - LAST_CENTER.x;TEMP.y = center.y - LAST_CENTER.y;LAST_CENTER.x = center.x;LAST_CENTER.y = center.y;////////cout << "TEMP.x=" << TEMP.x << "\tTEMP.y=" << TEMP.y << endl;//mouse_event(MOUSEEVENTF_MOVE, TEMP.x, TEMP.y, 0, 0);setMouseCallback("frame_hsv", on_mouse, &frame_hsv);//鼠標(biāo)取閾值imshow("frame_hsv", frame);imshow("result", show_img);show_img.release();if (cvWaitKey(20) == 'q')break;}}if (mode == "test"){//閾值測試while (1) {capture >> frame;//刷新if (frame.empty())//幀空跳出循環(huán)break;//// 中值濾波,去除椒鹽噪聲medianBlur(frame, frame, 5);cvtColor(frame, frame_hsv, CV_BGR2HSV);//轉(zhuǎn)HSV圖像setMouseCallback("frame_hsv", on_mouse, &frame_hsv);//鼠標(biāo)取閾值imshow("frame_hsv", frame);if (cvWaitKey(20) == 'q')break;}}if (mode == "1") {while (1){capture >> frame;//刷新if (frame.empty())//幀空跳出循環(huán)break;open_palm_cascade.detectMultiScale(frame, open_palms, 1.3, 4, 0, Size(50, 50));closed_palm_cascade.detectMultiScale(frame, closed_palms, 1.3, 4, 0, Size(50, 50));if (closed_palms.size() != 0) {for (int i = 0; i < closed_palms.size(); i++) {//cout << "=============Detected a closed_palm!=============" << endl;// Top left and bottom right points of rectangle.Point closed_palm_rect_p1(closed_palms[i].x, closed_palms[i].y);Point closed_palm_rect_p2(closed_palms[i].x + closed_palms[i].width, closed_palms[i].y + closed_palms[i].height);// Draw the rectangle in the image.rectangle(frame, closed_palm_rect_p1, closed_palm_rect_p2, Scalar(0, 255, 0));putText(frame, "Closed Palm", closed_palm_rect_p1, FONT_HERSHEY_SIMPLEX,1, Scalar(0, 255, 0), 1, 5, false);hand_mode[0] = 1;}}for (int i = 0; i < open_palms.size(); i++) {//cout << "=============Detected an open_palm!=============" << endl;// Top left and bottom right points of rectangle.Point open_palm_rect_p1(open_palms[i].x, open_palms[i].y);Point open_palm_rect_p2(open_palms[i].x + open_palms[i].width, open_palms[i].y + open_palms[i].height);// Draw the rectangle in the image.rectangle(frame, open_palm_rect_p1, open_palm_rect_p2, Scalar(255, 0, 0));putText(frame, "Open Palm", open_palm_rect_p1, FONT_HERSHEY_SIMPLEX,1, Scalar(255, 0, 0), 1, 5, false);hand_mode[0] = 2;}imshow("fram", frame);///skinArea.create(frame.rows, frame.cols, CV_8UC1);//和原圖像等行數(shù)/等列數(shù)的/8位無符號通/道數(shù)1//skinExtract(frame, skinArea);//找到皮膚// 中值濾波,去除椒鹽噪聲medianBlur(frame, frame, 5);cvtColor(frame, frame_hsv, CV_BGR2HSV);//轉(zhuǎn)HSV圖像split(frame_hsv, frameSplit);//三通道圖像分離//顯示HSV 3通道//imshow("WIN_H", frameSplit[0]);//imshow("WIN_S", frameSplit[1]);//imshow("WIN_V", frameSplit[2]);///Mat dstTemp1(frame.rows, frame.cols, CV_8UC1);Mat dstTemp2(frame.rows, frame.cols, CV_8UC1);// 對HSV空間進(jìn)行量化,得到2值圖像,亮的部分為手的形狀inRange(frame_hsv, Scalar(0, 50, 30), Scalar(20, 100, 256), dstTemp1);inRange(frame_hsv, Scalar(156, 30, 30), Scalar(180, 170, 256), dstTemp2);bitwise_or(dstTemp1, dstTemp2, mask);//按位或//imshow("mask", mask);// 形態(tài)學(xué)操作,去除噪聲,并使手的邊界更加清晰,提取邊緣Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));//返回指定形狀和尺寸的結(jié)構(gòu)元素,3*3的矩陣erode(mask, mask, element);//腐蝕morphologyEx(mask, mask, MORPH_OPEN, element);dilate(mask, mask, element);//膨脹morphologyEx(mask, mask, MORPH_CLOSE, element);//frame.copyTo(dst, mask);frame.copyTo(show_img, mask);//imshow("mask", mask);///清空contours.clear();hierarchy.clear();filterContours.clear();///尋找最外層輪廓 //只保存拐點(diǎn)信息findContours(mask, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);// 去除偽輪廓for (size_t i = 0; i < contours.size(); i++) {if (fabs(contourArea(Mat(contours[i]))) > 30000) {//判斷手進(jìn)入?yún)^(qū)域的閾值filterContours.push_back(contours[i]);}}// 畫輪廓drawContours(show_img, filterContours, -1, Scalar(0, 0, 255), 3/*, 8, hierarchy*/);// 得到輪廓的凸包絡(luò)for (size_t j = 0; j<filterContours.size(); j++) {convexHull(Mat(filterContours[j]), hull, true);int hullcount = (int)hull.size();for (int i = 0; i<hullcount - 1; i++) {line(show_img, hull[i + 1], hull[i], Scalar(255, 0, 0), 2, 0);}line(show_img, hull[hullcount - 1], hull[0], Scalar(255, 0, 0), 2, 0);}//重心//Moments moment = moments(mask, true);Point center(moment.m10 / moment.m00, moment.m01 / moment.m00);circle(show_img, center, 8, Scalar(0, 0, 255), CV_FILLED);//畫紅色實(shí)心圓TEMP.x = center.x - LAST_CENTER.x;TEMP.y = center.y - LAST_CENTER.y;LAST_CENTER.x = center.x;LAST_CENTER.y = center.y;///if (hand_mode[0] == 1 && hand_mode[1] == 1){mouse_event(MOUSEEVENTF_LEFTDOWN, TEMP.x, TEMP.y, 0, 0);mouse_event(MOUSEEVENTF_MOVE, TEMP.x, TEMP.y, 0, 0);cout << "leftdown" << endl;}else{mouse_event(MOUSEEVENTF_LEFTUP, TEMP.x, TEMP.y, 0, 0);mouse_event(MOUSEEVENTF_MOVE, TEMP.x, TEMP.y, 0, 0);cout << "up" << endl;}hand_mode[1] = hand_mode[0];/////cout << "TEMP.x=" << TEMP.x << "\tTEMP.y=" << TEMP.y << endl;imshow("result", show_img);show_img.release();if (cvWaitKey(20) == 'q')break;}}return 0; } //膚色提取,skinArea為二值化膚色圖像 void skinExtract(const Mat &frame, Mat &skinArea) {Mat YCbCr;vector<Mat> planes;//轉(zhuǎn)換為YCrCb顏色空間 cvtColor(frame, YCbCr, CV_RGB2YCrCb);//將多通道圖像分離為多個單通道圖像 split(YCbCr, planes);//運(yùn)用迭代器訪問矩陣元素 MatIterator_<uchar> it_Cb = planes[1].begin<uchar>(), it_Cb_end = planes[1].end<uchar>();MatIterator_<uchar> it_Cr = planes[2].begin<uchar>();MatIterator_<uchar> it_skin = skinArea.begin<uchar>();//人的皮膚顏色在YCbCr色度空間的分布范圍:100<=Cb<=127, 138<=Cr<=170 for (; it_Cb != it_Cb_end; ++it_Cr, ++it_Cb, ++it_skin){if (138 <= *it_Cr && *it_Cr <= 170 && 100 <= *it_Cb && *it_Cb <= 127)*it_skin = 255;else*it_skin = 0;}//膨脹和腐蝕,膨脹可以填補(bǔ)凹洞(將裂縫橋接),腐蝕可以消除細(xì)的凸起(“斑點(diǎn)”噪聲) dilate(skinArea, skinArea, Mat(5, 5, CV_8UC1), Point(-1, -1));erode(skinArea, skinArea, Mat(5, 5, CV_8UC1), Point(-1, -1)); }void on_mouse(int EVENT, int x, int y, int flags, void* userdata) {Mat hh;hh = *(Mat*)userdata;Point p(x, y);switch (EVENT){case EVENT_LBUTTONDOWN:{//cout << "h=" << hh.at<Vec3b>(p)[0] << endl;//cout << "s=" << hh.at<Vec3b>(p)[1] << endl;//cout << "v=" << hh.at<Vec3b>(p)[2] << endl;printf("h=%d\t", hh.at<Vec3b>(p)[0]);printf("s=%d\t", hh.at<Vec3b>(p)[1]);printf("v=%d\n", hh.at<Vec3b>(p)[2]);circle(hh, p, 2, Scalar(255), 3);}break;} }總結(jié)
以上是生活随笔為你收集整理的基于opencv的手势识别(HSV)控制鼠标的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql的genelog_小白实战课堂
- 下一篇: 小红书java算法难吗_Java面试系列