C++ OpenCV手势识别(一)
2020 年 04 月 27 日 1303 353 字 暂无评论

最近研究了一下基于opencv的手势识别。

目前了解到的一种手势识别算法为:
先滤波去噪
-->转换到HSV空间
-->根据皮肤在HSV空间的分布做出阈值判断
-->进行一下形态学的操作,去除噪声干扰,是手的边界更加清晰平滑
-->得到2值图像
-->得到的2值图像后用findContours找出手的轮廓,去除伪轮廓后,再用convexHull函数得到凸包络
-->通过凸包的数量来确定手势的数字,通过计算向量夹角判断特殊数字。

目前只能获取手部的凸包络,代码如下:

#include <iostream>    // for standard I/O
#include <string>   // for strings
#include <iomanip>  // for controlling float print precision
#include <sstream>  // string to number conversion

#include <opencv2/imgproc/imgproc.hpp>  // Gaussian Blur
#include <opencv2/core/core.hpp>        // Basic OpenCV structures (cv::Mat, Scalar)
#include <opencv2/highgui/highgui.hpp>  // OpenCV window I/O
using namespace cv;
using namespace std;

int main(int argc, char* argv[])
{
    int delay = 1;

    char c;
    int frameNum = -1;            // Frame counter

    VideoCapture captRefrnc(0);

    if (!captRefrnc.isOpened())
    {
        cout  << "Could not open reference " << endl;
        return -1;
    }

    Size refS = Size((int)captRefrnc.get(CV_CAP_PROP_FRAME_WIDTH),
        (int)captRefrnc.get(CV_CAP_PROP_FRAME_HEIGHT));

    bool bHandFlag = false;

    const char* WIN_SRC = "Source";
    const char* WIN_RESULT = "Result";

    // Windows
    namedWindow(WIN_SRC, CV_WINDOW_AUTOSIZE);
    namedWindow(WIN_RESULT, CV_WINDOW_AUTOSIZE);

    Mat frame;    // 输入视频帧序列
    Mat frameHSV;    // hsv空间
    Mat mask(frame.rows, frame.cols, CV_8UC1);    // 2值掩膜
    Mat dst(frame);    // 输出图像
    //     Mat frameSplit[4];
    vector< vector<Point> > contours;    // 轮廓
    vector< vector<Point> > filterContours;    // 筛选后的轮廓
    vector< Vec4i > hierarchy;    // 轮廓的结构信息
    vector< Point > hull;    // 凸包络的点集

    while (true) //Show the image captured in the window and repeat
    {
        captRefrnc >> frame;

        if (frame.empty())
        {
            cout << " < < <  Game over!  > > > ";
            break;
        }
        imshow(WIN_SRC, frame);

        // Begin

        // 中值滤波,去除椒盐噪声
        medianBlur(frame, frame, 5);
        //         GaussianBlur( frame, frameHSV, Size(9, 9), 2, 2 );
        //         imshow("blur2", frameHSV);
        //        pyrMeanShiftFiltering(frame, frameHSV, 10, 10);
        //         imshow(WIN_BLUR, frameHSV);
                // 转换到HSV颜色空间,更容易处理
        cvtColor(frame, frameHSV, CV_BGR2HSV);

        //         split(frameHSV, frameSplit);
        //         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空间进行量化,得到2值图像,亮的部分为手的形状
        inRange(frameHSV, Scalar(0, 30, 30), Scalar(40, 170, 256), dstTemp1);
        inRange(frameHSV, Scalar(156, 30, 30), Scalar(180, 170, 256), dstTemp2);
        bitwise_or(dstTemp1, dstTemp2, mask);
        //         inRange(frameHSV, Scalar(0,30,30), Scalar(180,170,256), dst);        

                // 形态学操作,去除噪声,并使手的边界更加清晰
        Mat element = getStructuringElement(MORPH_RECT, Size(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);

        contours.clear();
        hierarchy.clear();
        filterContours.clear();
        // 得到手的轮廓
        findContours(mask, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
        // 去除伪轮廓
        for (size_t i = 0; i < contours.size(); i++)
        {
            //             approxPolyDP(Mat(contours[i]), Mat(approxContours[i]), arcLength(Mat(contours[i]), true)*0.02, true);
            if (fabs(contourArea(Mat(contours[i]))) > 30000)    //判断手进入区域的阈值
            {
                filterContours.push_back(contours[i]);
            }
        }
        // 画轮廓
        drawContours(dst, filterContours, -1, Scalar(0, 0, 255), 3/*, 8, hierarchy*/);
        // 得到轮廓的凸包络
        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(dst, hull[i + 1], hull[i], Scalar(255, 0, 0), 2, CV_AA);
            }
            line(dst, hull[hullcount - 1], hull[0], Scalar(255, 0, 0), 2, CV_AA);
        }

        imshow(WIN_RESULT, dst);
        dst.release();
        // End

        c = cvWaitKey(delay);
        if (c == 27) break;
    }
}

请输入图片描述

注意:

  • 目前可以得到凸包络,但背景和光线影响大。
  • 由于是通过颜色来提取手的,所以最好在光线均匀,背景为白色的情况下进行试验,

版权属于:zfh

本文链接:http://zfhblog.com/index.php/archives/22/



评论已关闭