基于Opencv的手势识别(QT 多线程)

寒假闲来无事就捣腾下,先直接放效果图~~~~
这里写图片描述
主要用opencv写底层的算法(并没有用到机器学习),QT做的窗体。

算法部分:
用的是HSV识别肤色,这种方法在亮度足够的情况下对肤色识别还算OK,灯光的影响可以用白平衡解决(opencv的xphoto.hpp有具体的白平衡函数可以直接调用)。但是这种方法有个明显的缺点就是头部的干扰,这个我怎么解决的下面再讲。

for (int i = 0; i < Y.rows; i++) {
            uchar* currentCr = Cr.ptr<uchar>(i);
            uchar* currentCb = Cb.ptr<uchar>(i);
            uchar* current = temp_result.ptr<uchar>(i);
            for (int j = 0; j < Y.cols; j++) {
                if ((currentCr[j] > 140) && (currentCr[j] < 175) && (currentCb[j] > 100) && (currentCb[j] < 120)) {
                    m_mask[time][i][j] = 1;
                    current[j] = 255;
                }
                else current[j] = 0;
            }
        }

上面就是HSV肤色识别的关键代码,考虑到头部对手部轮廓提取的影响。我将程序分成两个部分。第一步是在手部还未出现在摄像头的情况下先用HSV把头部识别出来(直接HSV)然后把这些点作为MASK保存。第二步用MASK把之后输入的图像的对应点覆盖成黑色。这样就可以变相的解决自己头部干扰手部轮廓的提取。
这里写图片描述
就这个样子,在程序启动的时候先运行提取MASK,之后再进行手部的识别。

for (int i = 0; i < m_result.rows; i++) {
        uchar* data = m_result.ptr<uchar>(i);
        for (int j = 0; j < m_result.cols; j++) {
            if (m_mask[i][j] == 1) {
                data[j] = 0;
            }
        }
    }

直接覆盖掉MASK(就是头部以及其他类肤色的环境)对应的像素点。
之后就是正常的轮廓提取,获取最大包围圆,然后根据圆心的参数的变化来控制鼠标的滚轮,这样就可以控制网页的上下翻动了。或者做PPT的翻页也都可以。

至于多线程呢,是因为把数据处理和显示分成了两个线程来处理。因为这样在思路上更清晰一些,单线程的话因为有前面的MASK识别所以程序就各种绕来绕去很麻烦(各种goto)。

用QT是因为显示可以一直保持在一个窗口,直接imshow的话一个窗口无法保持一直打开的状态,因为有前面的MASK部分。

值得一提的是QT和多线程编程还是好麻烦的说。

class MyClass : public QMainWindow
{
    Q_OBJECT

public:
    MyClass(QWidget *parent = 0);
    ~MyClass();

private:
    Ui::MyClassClass ui;

    //Hard disk resources
    cv::Mat prompt_1, prompt_2, prompt_3;

    //Thread
    friend DWORD WINAPI data_threadfun(LPVOID _mes);//主数据处理线程
    friend DWORD WINAPI show_threadfun(LPVOID _mes);//主显示线程
    friend DWORD WINAPI show_loop_threadfun(LPVOID _mes);//主显示辅助线程

    //Lock
    CLock lock;

    //Thread public resource
    int sub_flag;//判断当前程序运行到第几步骤
    int sub_main_flag;//主显示线程辅助标志位(保护数据读写安全)
    cv::Mat sub_frame;//每帧数据
    cv::Mat sub_contour_frame;//每帧数据
    int sub_center_x, sub_center_y;//最终显示定位位置数据
    float sub_dwData;//滚轮位置数据
    cv::Mat sub_image;//最终显示图片数据Mat版本
    cv::Mat sub_contour_image;//最终显示轮廓数据Mat版本
    QImage Qt_image;//最终显示图片数据Mat版本浅Copy(与sub_image同内存地址)
    QImage Qt_contour_image;//最终显示轮廓数据Mat版本浅Copy(与sub_contourimage同内存地址)

    //Timer
    QTimer *frameTimer;

    //Camera
    cv::VideoCapture video;

    //Data
    int m_dwData_flag;
    std::vector<float> m_array_dwData;

    //Function
    void mousewheel(std::vector<float> array_dwData);//鼠标滚轮控制函数

private slots:
    void refresh();
};

这是QT主类各种成员,写多线程的是注意哇,QT对Ui进行操作只能在主线程进行,子线程是无法操作Ui的,会报错。

就写到这儿吧,第一次写Blog,各位看官海涵,如有错误或更好的idea欢迎指正。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值