http://pan.baidu.com/s/1gd5nthL
密码: h46g
基于视觉的动作识别,一直以来的最大问题是精度问题。 - -!
刚刚做了一套手势识别的算法,在此做下简单总结
先说下我做的效果:
2m内手掌识别率在90%以上(强光干扰下效果烂成渣渣了。。so。。 扣掉10%)
处理速度,15fps
cpu 30% (arm 4核)
以上效果为平板测试结果
1> 目的: opencv的haar特征库用来训练各种手势其实是很强势,很好用的东西,唯一的缺点是: Haar版权问题。。,
Haar训练需要的样本数也是一个蛮头疼的问题,识别准确性完全取决于样本数量和质量,没有几十k的样本,效果只能呵呵了
还好,条条大路通罗马,
2>外围设备: 摄像头,这是基本的
深度传感器 ,这是一直想要的,可惜到现在也没搞到
红外传感器 ,同上。。。。。
3> 检测方式: 利用手势的特征点:
手掌的特征点还是挺多的,我使用的是手轮廓的5个内凹陷,再加上相对位置,再加上人体肤色的特征
基本一个完整的手就检测出来了
4> 缺点: 精度缺失 ,没办法识别3d空间位置,单camera。。。。
--------------------------------code 分割线--------------------------------------------------------
肤色过滤部分
#include "utils.h"
cv::Mat edgeFilter();
void areaFilter(cv::Mat srcMask);
void skinFilter();
cv::Scalar YUV_SKIN_BEGIN = cv::Scalar(0,133,77); // 论文肤色(0,133,77)->(256,173,127)
cv::Scalar YUV_SKIN_END = cv::Scalar(256,173,127);
cv::Scalar COLOR_RED = cv::Scalar(0,0,255);
cv::Scalar COLOR_GREEN = cv::Scalar(0,255,0);
cv::Scalar COLOR_BLUE = cv::Scalar(255,0,0);
#ifdef USE_SHARP
cv::Mat sharpKernal = (cv::Mat_<float>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
#endif
cv::Mat morphKernal = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3),cv::Point(1,1) );
cv::Size pSize = cv::Size(320,240);
// 边缘轮廓
cv::Mat edgeFilter(){
#ifdef USE_EDGE
#ifdef DEBUG
long begT = getTime(true);
#endif
cv::Mat g = gray.clone();
if(withRsize)
resize(g,g,pSize);
equalizeHist(g,g);
Canny(g,g,100,300,3);
g = g > 1;
#ifdef USE_SHARP
long usec = getTime(true);
filter2D(gray, gray, gray.depth(), sharpKernal); //卷积
LOGD("filter2D(%d*%d): %d us",gray.cols,gray.rows,(int)(getTime(true)-usec));
#endif
#ifdef USE_ERODE
dilate(gray,gray,morphKernal);
#endif
if(withRsize)
resize(g,g,src.size());
#ifdef DEBUG
int w = withRsize? 320 : gray.cols;
int h = withRsize? 240 : gray.rows;
cv::Mat can;
cvtColor(g,can,CV_GRAY2BGR);
//src.copyTo(can,g);
//addWeighted(src,1.0,can,1.0,1.0,src);
src = src + can;
LOGD("edgeFilter(%d*%d): %d us",w,h,(int)(getTime(true)-begT));
#endif
return g;
#endif //USE_EDGE
}
// 小面积过滤
void areaFilter(cv::Mat srcMask){
#ifdef USE_SF_AREA
#ifdef DEBUG
long usec = getTime(true);
#endif
cv::Mat temp = srcMask.clone(); // 二值化图像
std::vector<std::vector<cv::Point> > points;
std::vector<cv::Vec4i> vecs;
findContours(temp, points, vecs,
CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE );
if(points.size()<=0 || vecs.size()<=0) {
skin.setTo(0);
return;
}
std::vector<std::vector<cv::Point> > points_poly(points.size());
std::vector<cv::Rect> bound(points.size());
std::vector<int> usefulList;
std::vector<float> areaList;
float max=0;
for(int i=0;i>=0;i=vecs[i][0]){
bound[i] = boundingRect(points[i]);
//噪声过滤(微小面积过滤)
if( points[i].size()<6
|| bound[i].width < MIN_IMAGE_PIX || bound[i].height < MIN_IMAGE_PIX
) {
srcMask(bound[i]).setTo(0);
continue;
}else{ // 多边形逼近
approxPolyDP(points[i], points_poly[i],10,true);
float area = fabs(contourArea(points_poly[i]));
// 有用Rect
usefulList.push_back(i); // 索引
areaList.push_back(area); // 面积
if(max<area) max = area; // 最大面积
}
}
max *= 0.01;
int j=usefulList.size()-1;
for(int i=0;j>=0;j--){
i = usefulList.at(j);
float area = areaList.at(j);
//过滤
if(area<=max) {
srcMask(bound[i]).setTo(0);
continue;
}
#ifdef USE_ERODE
std::vector<int> hull;
std::vector<cv::Point> pos = points_poly[i];
convexHull(cv::Mat(pos), hull, true);
try{
std::vector<cv::Vec4i> defects; // 起始点,终止点,最凹点,凹点深度
convexityDefec