前几年在做毕业设计时候曾用opencv1.0中defects做过简单的手势识别,这几天看OpenCV2.46中的轮廓函数,发现和以前差别挺大,函数调用完全不一样,重新实现了简单手势的代码。

1.首先用简单的肤色检测算法,得到手的区域。

Mat img = cv::imread("../hand2.jpg");
    namedWindow("image");
    imshow("image", img);

Mat hsvimg;

首先把图像转化到HSV颜色空间,利用肤色色度、饱和度和亮度的特殊范围,得到手的区域。最后对得到的二值手的区域进行开闭操作,去掉一些小的干扰点。
    //得到HSV颜色空间的图像
    cvtColor( img, hsvimg, CV_BGR2HSV );
    //初始化手的图像和原始图像一样大小
    Mat handimg1(img.rows,img.cols, CV_8UC1, Scalar(0));

//简单的肤色检测算法,基于HSV颜色空间和域值
    uchar* p;
    uchar* p1;
    uchar h, s, v;
    int i, j;
    for( i = 0; i < hsvimg.rows; ++i)
        {
        p = hsvimg.ptr<uchar>(i);
        p1= handimg1.ptr<uchar>(i);
        for ( j = 0; j < hsvimg.cols; ++j)
            {
            //printf (" %d %d %d",p[j*3], p[j*3+1],p[j*3+2]);
            h = p[j*3];
            s = p[j*3+1];
            v = p[j*3+2];
            if(h <= 19 && s >= 48 && v > 40)
                {
                p1[j] = 255;
                }
            else p1[j] = 0;
            }
        }
    //对得到手区域二值图做简单的开闭操作
    cv::Mat element = cv::getStructuringElement( cv::MORPH_RECT,
        cv::Size( 5,5 ),
        cv::Point( 2, 2 ) );
    morphologyEx( handimg1, handimg1, MORPH_OPEN, element );
    morphologyEx( handimg1, handimg1, MORPH_CLOSE, element );

简单肤色算法后的效果:

接着对得到的手区域二值图,查找轮廓,找出面积最大的轮廓区域,这个区域一般为手的区域,对该区域求凸包以及defect数目,根据defect的数目,可以判断伸出几个手指头。注意:在判断defect数目时候,我们根据defect点到凸包的距离长度,过滤了一些小的defect。

过滤的代码为:

if ( defectVector.val[3] <= 10000 ) { continue; }

下面是过滤前后的defect点(黄色点),可以看到过滤后,只有4个defect点,我们可以得出此时伸出了5个手指头。

而下面的图,只有1个defect点,则表示伸出2个手指头。

检测defect的代码如下:

vector<vector<Point> > contours;
vector<Vec4i> hierarchy; //查找轮廓
findContours( handimg1, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
printf("轮廓数目:%d\n", contours.size());
vector<vector<Point> >hull( contours.size() );
for( int i = 0; i < contours.size(); i++ )
{ convexHull( Mat(contours[i]), hull[i], false ); } Mat drawing = Mat::zeros( handimg1.size(), CV_8UC3 );
int area = 0; //轮廓索引
int k = 0;
for(i = 0; i< contours.size(); i++ )
{
Scalar color1 = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours( drawing, contours, i, color1, 1, 8, vector<Vec4i>(), 0, Point() );
Scalar color2 = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours( drawing, hull, i, color2, 1, 8, vector<Vec4i>(), 0, Point() );
int tt = contourArea(contours[i]);
printf("轮廓面积%d = %d\n", i, tt);
if( tt > area)
{
area = contourArea(contours[i]);
k = i;
} } if (!isContourConvex(contours[k]) &&contours[k].size() > 3)
{
vector<int> convexHull_IntIdx;
vector<Vec4i> defects;
convexHull(contours[k], convexHull_IntIdx, true);
convexityDefects(contours[k], convexHull_IntIdx, defects); int defectcount = 0;
for(i=0;i < defects.size();++i)
{
Matx<int,4,1> defectVector = defects[i];
vector<Point> contours1 =contours[k];
Point point1 = contours1[defectVector.val[0]];//开始点
Point point2 = contours1[defectVector.val[1]];//结束点
Point point3 = contours1[defectVector.val[2]];//深度点
float dist = defectVector.val[3];
printf("dist: %f \n", dist);
if ( defectVector.val[3] <= 10000 ) { continue; } // skip defects that are shorter than 100 pixel
circle(drawing, point1, 3, Scalar(255,255,0), 2, CV_AA);
circle(drawing, point2, 8, Scalar(0,255,0), 2, CV_AA);
circle(drawing, point3, 3, Scalar(0,255,255), 2, CV_AA);
defectcount++;
}
printf("指缝数目:%d\n", defectcount); }

程序源码:工程FirstOpenCV26

OpenCV学习(31) 基于defects的简单手势的更多相关文章

  1. 【opencv学习笔记五】一个简单程序:图像读取与显示

    今天我们来学习一个最简单的程序,即从文件读取图像并且创建窗口显示该图像. 目录 [imread]图像读取 [namedWindow]创建window窗口 [imshow]图像显示 [imwrite]图 ...

  2. OpenCV学习(30) 轮廓defects

    上一篇教程中,我们学习了如何计算轮廓的凸包,其实对一个轮廓而言,可能它的凸包和它本身是重合的,也有可能不是重合的.比如下面左边图像的轮廓本身就是凸包,而右边图像的轮廓则不是.我们可以通过函数bool ...

  3. Netty学习——基于netty实现简单的客户端聊天小程序

    Netty学习——基于netty实现简单的客户端聊天小程序 效果图,聊天程序展示 (TCP编程实现) 后端代码: package com.dawa.netty.chatexample; import ...

  4. OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波

    http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 201 ...

  5. OpenCV 学习笔记03 boundingRect、minAreaRect、minEnclosingCircle、boxPoints、int0、circle、rectangle函数的用法

    函数中的代码是部分代码,详细代码在最后 1 cv2.boundingRect 作用:矩形边框(boundingRect),用于计算图像一系列点的外部矩形边界. cv2.boundingRect(arr ...

  6. OpenCV 学习笔记03 findContours函数

    opencv-python   4.0.1 1 函数释义 词义:发现轮廓! 从二进制图像中查找轮廓(Finds contours in a binary image):轮廓是形状分析和物体检测和识别的 ...

  7. OpenCV学习系列(一) Mac下OpenCV + xcode人脸检测实现

    # OpenCV学习系列(一) Mac下OpenCV + xcode人脸检测实现 [-= 博客目录 =-] 1-学习目标 1.1-本章介绍 1.2-实践内容 1.3-相关说明 2-学习过程 2.1-环 ...

  8. opencv学习笔记D01

    目录 opencv学习笔记D01 一.图片读取 二.图片保存 三.图片展示 四.图片缩放 五.四种常用插值方式的比较 1.最近邻插值 2.双线性插值 3.区域插值 4.三次样条插值 我是尾巴: ope ...

  9. 【转载】JMeter学习(一)工具简单介绍

    JMeter学习(一)工具简单介绍 一.JMeter 介绍 Apache JMeter是100%纯JAVA桌面应用程序,被设计为用于测试客户端/服务端结构的软件(例如web应用程序).它可以用来测试静 ...

随机推荐

  1. 8-4 奖品的价值 uva11491(贪心)

    题意:给出一个n位整数   请删除d个数字 使得剩余数字最大 一开始写成窗口扫描  非常的麻烦易错 只要保证前d+1 降序即可  如果非降序   则删除 很巧妙的贪心 每次读入和已经保存的数组相比较 ...

  2. Android中Xposed框架篇-微信实现本地视频发布到朋友圈功能

    微信非常庞大,还好有一些强大的工具,下面就来总结收获的知识. 一.使用adb shell dumpsys activity top命令快速定位页面 二.使用Jadx进行方法跟踪时候如果发现没有结果,可 ...

  3. Python并发编程-IO模型-非阻塞IO实现SocketServer

    Server.py import socket sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.setblocking(False) #把soc ...

  4. 标准c库函数与Linux下系统函数库 区别 (即带不带缓冲区的学习)

    我们都知道,C语言在UNIX/Linux系统下有一套系统调用(系统函数),比如文件操作open().close().write().read()等,而标准C语言的库函数中也有一套对文件的操作函数fop ...

  5. 利用python实现新浪微博爬虫

    第一个模块,模拟登陆sina微博,创建weiboLogin.py文件,输入以下代码: #! /usr/bin/env python # -*- coding: utf-8 -*- import sys ...

  6. 深入理解ajax系列第三篇

    前面的话 我们接收到的响应主体类型可以是多种形式的,包括字符串String.ArrayBuffer对象.二进制Blob对象.JSON对象.javascirpt文件及表示XML文档的Document对象 ...

  7. luogu P2439 [SDOI2005]阶梯教室设备利用

    题目链接 luogu P2439 [SDOI2005]阶梯教室设备利用 题解 dp 代码 #include<vector> #include<cstdio> #include& ...

  8. pkuwc2019自闭记

    窝自闭了... 所以这篇\(blog\)咕咕咕了.

  9. 【漏洞预警】Intel爆CPU设计问题,导致win和Linux内核重设计(附测试poc)

    目前研究人员正抓紧检查 Linux 内核的安全问题,与此同时,微软也预计将在本月补丁日公开介绍 Windows 操作系统的相关变更. 而 Linux 和 Windows 系统的这些更新势必会对 Int ...

  10. 可变参数模拟printf()函数实现一个my_print()函数以及调用可变参数需注意的陷阱

    入栈规则 可变参数函数的实现与函数调用的栈帧结构是密切相关的.所以在我们实现可变参数之前,先得搞清楚 栈是怎样传参的. 正常情况下,C的函数参数入栈遵照__stdcall规则, 它是从右到左的,即函数 ...