OpenCV简单粗糙的指尖检测方法(FingerTips Detection)

分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net

在人机交互领域,如果可以比较好的检测指尖,对于交互的丰富度、灵活性来说是有很大提升的。目前指尖检测的方法也很多,我这里稍微尝试了下简单了两种。这两种方法都借助了手的几何特征,简单但比较粗糙,鲁棒性不够。

方法一:重心距离法

见下图,红色点是手的重心,那么手的边缘的所有点与重心点的距离按顺时针方向或者逆时针方向遍历,就会出现五个峰值,分别是五个手指,这样我们就可以简单找到了。如果你是只伸出一两个手指,那么就只有一两个峰值了。

简单的代码如下:

1、对图像做高斯模糊;

2、肤色分割(背景不要有类肤色,如果有,就需要加其他信息来排除干扰);

3、找到手轮廓;

4、计算矩,即重心;

5、寻找指尖。

// Simple FingerTips Detection
// Author : Zouxy
// Date   : 2013-3-23
// HomePage : http://blog.csdn.net/zouxy09
// Email  : zouxy09@qq.com  

#include "opencv2/opencv.hpp"  

using namespace cv;
using namespace std;  

void skinExtract(const Mat &frame, Mat &skinArea);  

int main(int argc, char* argv[])
{
    Mat frame, skinArea;
    VideoCapture capture;  

    capture.open(0);
    if (!capture.isOpened())
    {
        cout<<"No camera!\n"<<endl;
        return -1;
    }  

    while (1)
    {
        capture >> frame;
        //Mat frame = imread("fingertips(1).jpg");
        if (frame.empty())
            break;  

        skinArea.create(frame.rows, frame.cols, CV_8UC1);
        skinExtract(frame, skinArea);
        Mat show_img;
        frame.copyTo(show_img, skinArea);  

        vector<vector<Point> > contours;
        vector<Vec4i> hierarchy;  

        //寻找轮廓
        findContours(skinArea, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);  

        // 找到最大的轮廓
        int index;
        double area, maxArea(0);
        for (int i=0; i < contours.size(); i++)
        {
            area = contourArea(Mat(contours[i]));
            if (area > maxArea)
            {
                maxArea = area;
                index = i;
            }
        }  

        //drawContours(frame, contours, index, Scalar(0, 0, 255), 2, 8, hierarchy );  

        Moments moment = moments(skinArea, true);
        Point center(moment.m10/moment.m00, moment.m01/moment.m00);
        circle(show_img, center, 8 ,Scalar(0, 0, 255), CV_FILLED);  

        // 寻找指尖
        vector<Point> couPoint = contours[index];
        vector<Point> fingerTips;
        Point tmp;
        int max(0), count(0), notice(0);
        for (int i = 0; i < couPoint.size(); i++)
        {
            tmp = couPoint[i];
            int dist = (tmp.x - center.x) * (tmp.x - center.x) + (tmp.y - center.y) * (tmp.y - center.y);
            if (dist > max)
            {
                max = dist;
                notice = i;
            }  

            // 计算最大值保持的点数,如果大于40(这个值需要设置,本来想根据max值来设置,
            // 但是不成功,不知道为何),那么就认为这个是指尖
            if (dist != max)
            {
                count++;
                if (count > 40)
                {
                    count = 0;
                    max = 0;
                    bool flag = false;
                    // 低于手心的点不算
                    if (center.y < couPoint[notice].y )
                        continue;
                    // 离得太近的不算
                    for (int j = 0; j < fingerTips.size(); j++)
                    {
                        if (abs(couPoint[notice].x - fingerTips[j].x) < 20)
                        {
                            flag = true;
                            break;
                        }
                    }
                    if (flag) continue;
                    fingerTips.push_back(couPoint[notice]);
                    circle(show_img, couPoint[notice], 6 ,Scalar(0, 255, 0), CV_FILLED);
                    line(show_img, center, couPoint[notice], Scalar(255, 0, 0), 2);
                }
            }
        }  

        imshow("show_img", show_img);  

        if ( cvWaitKey(20) == ‘q‘ )
            break;
    }  

    return 0;
}  

//肤色提取,skinArea为二值化肤色图像
void skinExtract(const Mat &frame, Mat &skinArea)
{
    Mat YCbCr;
    vector<Mat> planes;  

    //转换为YCrCb颜色空间
    cvtColor(frame, YCbCr, CV_RGB2YCrCb);
    //将多通道图像分离为多个单通道图像
    split(YCbCr, planes);   

    //运用迭代器访问矩阵元素
    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;
    }  

    //膨胀和腐蚀,膨胀可以填补凹洞(将裂缝桥接),腐蚀可以消除细的凸起(“斑点”噪声)
    dilate(skinArea, skinArea, Mat(5, 5, CV_8UC1), Point(-1, -1));
    erode(skinArea, skinArea, Mat(5, 5, CV_8UC1), Point(-1, -1));
} 

方法二:曲率分析法

见下图,可以看到,在指尖和两指之间的凹槽的曲率是最大的。假设手的轮廓是通过一系列点{Pi}来保存的,那么我们可以通过 [Pi, Pi-k] 和 [Pi, Pi+k] 两个向量的内积(点乘)来寻找高曲率的点,如下图的Φ和β的计算结果,因为他们的两个向量的夹角趋向于90度,也就是它们的内积趋向于0,所以内积结果越小,曲率越大,我们只需要设置一个阈值,就可以把手的指尖和两指之间的凹槽都可以找到。

那么如何分辨指尖和两指之间的凹槽呢?我们可以通过两个向量的叉乘来计算,我们想象下手处于3D空间中,那么就还有一个z方向,β中两个向量的叉乘是大于0的(叉乘方向垂直纸面朝向你,z轴正方向),而Φ处的叉乘是小于0的(叉乘方向垂直纸面远离你,z轴负方向)。

综上,通过在每个点上构造两个向量,比较他们的点乘和叉乘就可以确定指尖了。

简单的代码如下:

1、对图像做高斯模糊;

2、肤色分割(背景不要有类肤色,如果有,就需要加其他信息来排除干扰);

3、找到手轮廓;

4、寻找指尖。

// Simple FingerTips Detection Using Curvature Determination
// Author : Zouxy
// Date   : 2013-3-23
// HomePage : http://blog.csdn.net/zouxy09
// Email  : zouxy09@qq.com  

#include "opencv2/opencv.hpp"  

using namespace cv;
using namespace std;  

void skinExtract(const Mat &frame, Mat &skinArea);  

int main(int argc, char* argv[])
{
    Mat frame, skinArea;
    VideoCapture capture;  

    capture.open(0);
    if (!capture.isOpened())
    {
        cout<<"No camera!\n"<<endl;
        return -1;
    }  

    while (1)
    {
        capture >> frame;
        //Mat frame = imread("fingertips(1).jpg");
        if (frame.empty())
            break;  

        GaussianBlur(frame, frame, Size(3, 3), 0);
        skinArea.create(frame.rows, frame.cols, CV_8UC1);
        skinExtract(frame, skinArea);
        Mat show_img;
        frame.copyTo(show_img, skinArea);  

        vector<vector<Point> > contours;
        vector<Vec4i> hierarchy;  

        //寻找轮廓
        findContours(skinArea, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);  

        // 找到最大的轮廓
        int index;
        double area, maxArea(0);
        for (int i=0; i < contours.size(); i++)
        {
            area = contourArea(Mat(contours[i]));
            if (area > maxArea)
            {
                maxArea = area;
                index = i;
            }
        }  

        //drawContours(frame, contours, index, Scalar(0, 0, 255), 2, 8, hierarchy );  

        Moments moment = moments(skinArea, true);
        Point center(moment.m10/moment.m00, moment.m01/moment.m00);
        circle(show_img, center, 8 ,Scalar(0, 0, 255), CV_FILLED);  

        // 寻找指尖
        vector<Point> couPoint = contours[index];
        int max(0), count(0), notice(0);
        vector<Point> fingerTips;
        Point p, q, r;
        for (int i = 5; (i < (couPoint.size() - 5)) && couPoint.size(); i++)
        {
            q = couPoint[i - 5];
            p = couPoint[i];
            r = couPoint[i + 5];
            int dot = (q.x - p.x ) * (q.y - p.y) + (r.x - p.x ) * (r.y - p.y);
            if (dot < 20 && dot > -20)
            {
                int cross = (q.x - p.x ) * (r.y - p.y) - (r.x - p.x ) * (q.y - p.y);
                if (cross > 0)
                {
                    fingerTips.push_back(p);
                    circle(show_img, p, 5 ,Scalar(255, 0, 0), CV_FILLED);
                    line(show_img, center, p, Scalar(255, 0, 0), 2);
                }
            }
        }  

        imshow("show_img", show_img);  

        if ( cvWaitKey(20) == ‘q‘ )
            break;
    }  

    return 0;
}  

//肤色提取,skinArea为二值化肤色图像
void skinExtract(const Mat &frame, Mat &skinArea)
{
    Mat YCbCr;
    vector<Mat> planes;  

    //转换为YCrCb颜色空间
    cvtColor(frame, YCbCr, CV_RGB2YCrCb);
    //将多通道图像分离为多个单通道图像
    split(YCbCr, planes);   

    //运用迭代器访问矩阵元素
    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;
    }  

    //膨胀和腐蚀,膨胀可以填补凹洞(将裂缝桥接),腐蚀可以消除细的凸起(“斑点”噪声)
    dilate(skinArea, skinArea, Mat(5, 5, CV_8UC1), Point(-1, -1));
    erode(skinArea, skinArea, Mat(5, 5, CV_8UC1), Point(-1, -1));
}  

效果:

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net

原文地址:https://www.cnblogs.com/sownchz/p/10500236.html

时间: 2024-11-05 13:44:19

OpenCV简单粗糙的指尖检测方法(FingerTips Detection)的相关文章

目标检测之指尖检测---指尖检测的新方法几种

指尖检测根据应用可以分为单指尖检测和多指尖检测. 下面是我在工作中想到的方法,希望对你有用或提供点儿灵感. 单指尖检测新方法:重心距离法 找到手的区域,我一般用肤色检测 计算手的区域的重心 在手的区域的边缘点集中寻找距离重心最远的点,该点即为指尖候选位置 判断找到的点是否为指尖.判据:候选点到重心的距离大于边缘到重心平均距离的1.6倍,即为指尖:否则不是指尖,也就是说没有手指伸出 下面是我在随便挑的几张图片上做的试验 下面是我在应用中的试验截图: 说明:红色圆是平均距离:蓝色为重心:绿色表示最远

【opencv入门】角点检测之Harris角点检测

一.引言:关于兴趣点(interest points) 在图像处理和与计算机视觉领域,兴趣点(interest points),或称作关键点(keypoints).特征点(feature points) 被大量用于解决物体识别,图像识别.图像匹配.视觉跟踪.三维重建等一系列的问题.我们不再观察整幅图,而是选择某些特殊的点,然后对他们进行局部有的放矢的分析.如果能检测到足够多的这种点,同时他们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就有使用价值. 图像特征类型可以被分为如下三种: <

33、【opencv入门】角点检测之Harris角点检测

一.引言:关于兴趣点(interest points) 在图像处理和与计算机视觉领域,兴趣点(interest points),或称作关键点(keypoints).特征点(feature points) 被大量用于解决物体识别,图像识别.图像匹配.视觉跟踪.三维重建等一系列的问题.我们不再观察整幅图,而是选择某些特殊的点,然后对他们进行局部有的放矢的分析.如果能检测到足够多的这种点,同时他们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就有使用价值. 图像特征类型可以被分为如下三种: <

百度云离线下载含有违规内容检测方法分析

最近国家开始一轮净网行动,清除网上的淫秽***信息.各大互联网厂家纷纷开始行动,比如当年很好用的百度云离线下载就一度关闭.后来再次开启后,就出现了这句经典词,因含有违规内容被屏蔽无法下载. 其实被屏蔽的不一定都是不健康视频,有些仅仅是因为文件名含有一些字眼而已,比如一些美国大片的枪版就几乎都不能通过百度云的离线下载检测.据说这种方法还在迅雷等地方都有检测,因此我们来分析一下,这个检测到底是如何进行的. 首先上传了一个BT文件,BT文件里面的内容为大闹天宫的电影,但是我把文件名改成了含有敏感词汇的

R-CNN,SPP-NET, Fast-R-CNN,Faster-R-CNN, YOLO, SSD系列深度学习检测方法梳理

1. R-CNN:Rich feature hierarchies for accurate object detection and semantic segmentation 技术路线:selective search + CNN + SVMs Step1:候选框提取(selective search) 训练:给定一张图片,利用seletive search方法从中提取出2000个候选框.由于候选框大小不一,考虑到后续CNN要求输入的图片大小统一,将2000个候选框全部resize到227*

浅谈webshell检测方法 寒龙博客转载

一  什么是webshell"web"的含义是显然需要服务器开放web服务,"shell"的含义是取得对服务器某种程度上操作权限.webshell常常被称为匿名用户(入侵者)通过网站端口对网站服务器的某种程度上操作的权限.简单理解:webshell就是一个web的页面,但是它的功能非常强大可以获得一些管理员不希望你获得的权限,比如执行系统命令.删除web页面.修改主页等.webshell中由于需要完成一些特殊的功能就不可避免的用到一些特殊的函数,我们也就可以对着特征

基于OpenCV读取摄像头进行人脸检测和人脸识别

前段时间使用OpenCV的库函数实现了人脸检测和人脸识别,笔者的实验环境为VS2010+OpenCV2.4.4,opencv的环境配置网上有很多,不再赘述.检测的代码网上很多,记不清楚从哪儿copy的了,识别的代码是从OpenCV官网上找到的:http://docs.opencv.org/trunk/modules/contrib/doc/facerec/facerec_api.html 需要注意的是,opencv的FaceRecogizer目前有三个类实现了它,特征脸和fisherface方法

对这个运动目标检测方法实现的结果A Hybrid Algorithm for Moving Object Detection

最近在做烟火检测,需要用到运动检测,看到论文A System for Video Surveillance and Monitoring中的A Hybrid Algorithm for Moving Object Detection这个方法,我用opencv将其实现,代码下面会贴出,但是其的到的结果很差,不知道代码哪里出了问题,请高手给予指点,谢谢! 左图为原图,有图为用上面论文中的方法得到前景图像,[自己的代码可能写错了,请大神们给予指点谢谢] 代码如下: #include <stdio.h>

【火炉炼AI】机器学习046-图像边缘的检测方法

[火炉炼AI]机器学习046-图像边缘的检测方法 (本文所使用的Python库和版本号: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 ) 图像中各种形状的检测时计算机视觉领域中非常常见的技术之一,特别是图像中直线的检测,圆的检测,图像边缘的检测等,下面我们来研究一下如何快速检测图像边缘. 边缘是不同区域的分界线,是周围(局部)像素有显著变化的像素的集合,有幅值与方向两个属性.这个不是绝对的定义,主要记住边缘是局部特征以及周围