搜档网
当前位置:搜档网 › Kinect+OpenNI学习笔记之9(不需要骨骼跟踪的人体手部分割)

Kinect+OpenNI学习笔记之9(不需要骨骼跟踪的人体手部分割)

Kinect+OpenNI学习笔记之9(不需要骨骼跟踪的人体手部分割)
Kinect+OpenNI学习笔记之9(不需要骨骼跟踪的人体手部分割)

Kinect+OpenNI学习笔记之9(不需要骨骼跟踪的人体手部分割)

前言

手势识别非常重要的一个特点是要体验要好,即需要以用户为核心。而手势的定位一般在手势识别过程的前面,在上一篇博文Kinect+OpenNI学习笔记之8(Robert Walter手部提取代码的分析)中已经介绍过怎样获取手势区域,且取得了不错的效果,但是那个手势部位的提取有一个大的缺点,即需要人站立起来,当站立起来后才能够分隔出手。而手势在人之间的交流时,并不一定要处于站立状态,所以这不是一个好的HCI。因此本文介绍的手势部位的提取并不需要人处于站立状态,同样取得了不错的效果。

实验说明

其实,本实验实现的过程非常简单。首先通过手部的跟踪来获取手所在的坐标,手部跟踪可以参考本人前面的博文:Kinect+OpenNI学习笔记之7(OpenNI自带的类实现手部跟踪)。当定位到手所在的坐标后,因为该坐标是3D的,因此在该坐标领域的3维空间领域内提取出手的部位即可,整个过程的大概流程图如下:

OpenCV知识点总结:

调用Mat::copyTo()函数时,如果需要有mask操作,则不管源图像是多少通道的,其mask 矩阵都要定义为单通道,另外可以对一个mask矩阵画一个填充的矩形来达到使mask矩阵中对应ROI的位置的值为设定值,这样就不需要去一一扫描赋值了。

在使用OpenCV的Mat矩阵且需要对该矩阵进行扫描时,一定要注意其取值顺序,比如说列和行的顺序,如果弄反了,则经常会报内存错误。

实验结果

本实验并不要求人的手一定要放在人体的前面,且也不需要人一定是处在比较简单的背景环境中,本实验结果允许人处在复杂的背景环境下,且手可以到处随便移动。当然了,环境差时有时候效果就不太好。

下面是3张实验结果的截图,手势分隔图1:

手势分隔图2:

手势分隔图3:

实验主要部分代码即注释(附录有工程code下载链接):main.cpp:

#include

#include "opencv2/highgui/highgui.hpp"

#include "opencv2/imgproc/imgproc.hpp"

#include

#include "copenni.cpp"

#include

#define DEPTH_SCALE_FACTOR 255./4096.

#define ROI_HAND_WIDTH 140

#define ROI_HAND_HEIGHT 140

#define MEDIAN_BLUR_K 5

int XRES = 640;

int YRES = 480;

#define DEPTH_SEGMENT_THRESH 5

using namespace cv;

using namespace xn;

using namespace std;

int main (int argc, char **argv)

{

COpenNI openni;

int hand_depth;

Rect roi;

roi.x = XRES/2;

roi.y = YRES/2;

roi.width = ROI_HAND_WIDTH;

roi.height = ROI_HAND_HEIGHT;

if(!openni.Initial())

return1;

namedWindow("color image", CV_WINDOW_AUTOSIZE);

namedWindow("depth image", CV_WINDOW_AUTOSIZE);

namedWindow("hand_segment", CV_WINDOW_AUTOSIZE);//显示分割出来的手的区域

if(!openni.Start())

return1;

while(1) {

if(!openni.UpdateData()) {

return1;

}

/*获取并显示色彩图像*/

Mat color_image_src(openni.image_metadata.YRes(),

openni.image_metadata.XRes(),

CV_8UC3, (char *)openni.image_metadata.Data());

Mat color_image;

cvtColor(color_image_src, color_image, CV_RGB2BGR);

circle(color_image, Point(hand_point.X, hand_point.Y), 5, Scalar(255, 0, 0), 3, 8);

imshow("color image", color_image);

/*获取并显示深度图像*/

Mat depth_image_src(openni.depth_metadata.YRes(),

openni.depth_metadata.XRes(),

CV_16UC1, (char *)openni.depth_metadata.Data());//因为kinect获取到的深度图像实际上是无符号的16位数据

Mat depth_image;

depth_image_src.convertTo(depth_image, CV_8U, DEPTH_SCALE_FACTOR);

imshow("depth image", depth_image);

/*下面的代码是提取手的轮廓部分*/

hand_depth = hand_point.Z * DEPTH_SCALE_FACTOR;

roi.x = hand_point.X - ROI_HAND_WIDTH/2;

roi.y = hand_point.Y - ROI_HAND_HEIGHT/2;

if(roi.x <= 0)

roi.x = 0;

if(roi.x >= XRES)

roi.x = XRES;

if(roi.y <=0 )

roi.y = 0;

if(roi.y >= YRES)

roi.y = YRES;

//取出手的mask部分

//不管原图像时多少通道的,mask矩阵声明为单通道就ok

Mat hand_segment_mask(depth_image.size(), CV_8UC1, Scalar::all(0));

for(int i = roi.x; i < std::min(roi.x+roi.width, XRES); i++) for(int j = roi.y; j < std::min(roi.y+roi.height, YRES); j++) {

hand_segment_mask.at(j, i) =

((hand_depth-DEPTH_SEGMENT_THRESH) < depth_image.at(j, i))

&

((hand_depth+DEPTH_SEGMENT_THRESH) > depth_image.at(j,i));

}

medianBlur(hand_segment_mask, hand_segment_mask, MEDIAN_BLUR_K);

Mat hand_segment(color_image.size(), CV_8UC3);

color_image.copyTo(hand_segment, hand_segment_mask);

imshow("hand_segment", hand_segment);

waitKey(20);

}

}

copenni,cpp:

#ifndef COPENNI_CLASS

#define COPENNI_CLASS

#include

#include

#include

using namespace xn;

using namespace std;

static DepthGenerator depth_generator;

static HandsGenerator hands_generator;

static XnPoint3D hand_point;

static std::map> hands_track_points;

class COpenNI

{

public:

~COpenNI() {

context.Release();//释放空间

}

bool Initial() {

//初始化

status = context.Init();

if(CheckError("Context initial failed!")) {

return false;

}

context.SetGlobalMirror(true);//设置镜像

xmode.nXRes = 640;

xmode.nYRes = 480;

xmode.nFPS = 30;

//产生颜色node

status = image_generator.Create(context);

if(CheckError("Create image generator error!")) {

return false;

}

//设置颜色图片输出模式

status = image_generator.SetMapOutputMode(xmode);

if(CheckError("SetMapOutputMdoe error!")) {

return false;

}

//产生深度node

status = depth_generator.Create(context);

if(CheckError("Create depth generator error!")) {

return false;

}

//设置深度图片输出模式

status = depth_generator.SetMapOutputMode(xmode);

if(CheckError("SetMapOutputMdoe error!")) {

return false;

}

//产生手势node

status = gesture_generator.Create(context);

if(CheckError("Create gesture generator error!")) {

return false;

}

/*添加手势识别的种类*/

gesture_generator.AddGesture("Wave", NULL);

gesture_generator.AddGesture("click", NULL);

gesture_generator.AddGesture("RaiseHand", NULL);

gesture_generator.AddGesture("MovingHand", NULL);

//产生手部的node

status = hands_generator.Create(context);

if(CheckError("Create hand generaotr error!")) {

return false;

}

//产生人体node

status = user_generator.Create(context);

if(CheckError("Create gesturen generator error!")) {

return false;

}

//视角校正

status =

depth_generator.GetAlternativeViewPointCap().SetViewPoint(image_generator);

if(CheckError("Can't set the alternative view point on depth generator!")) {

return false;

}

//设置与手势有关的回调函数

XnCallbackHandle gesture_cb;

gesture_generator.RegisterGestureCallbacks(CBGestureRecognized, CBGestureProgress, NULL, gesture_cb);

//设置于手部有关的回调函数

XnCallbackHandle hands_cb;

hands_generator.RegisterHandCallbacks(HandCreate, HandUpdate, HandDestroy, NULL, hands_cb);

//设置有人进入视野的回调函数

XnCallbackHandle new_user_handle;

user_generator.RegisterUserCallbacks(CBNewUser, NULL, NULL,

new_user_handle);

user_generator.GetSkeletonCap().SetSkeletonProfile(XN_SKEL_PROFILE_ALL);//设定

使用所有关节(共15个)

//设置骨骼校正完成的回调函数

XnCallbackHandle calibration_complete;

user_generator.GetSkeletonCap().RegisterToCalibrationComplete(CBCalibrationCom plete, NULL, calibration_complete);

return true;

}

bool Start() {

status = context.StartGeneratingAll();

if(CheckError("Start generating error!")) {

return false;

}

return true;

}

bool UpdateData() {

status = context.WaitNoneUpdateAll();

if(CheckError("Update date error!")) {

return false;

}

//获取数据

image_generator.GetMetaData(image_metadata);

depth_generator.GetMetaData(depth_metadata);

return true;

}

//得到色彩图像的node

ImageGenerator& getImageGenerator() {

return image_generator;

}

//得到深度图像的node

DepthGenerator& getDepthGenerator() {

return depth_generator;

}

//得到人体的node

UserGenerator& getUserGenerator() {

return user_generator;

}

//得到手势姿势node

GestureGenerator& getGestureGenerator() {

return gesture_generator;

}

public:

DepthMetaData depth_metadata;

ImageMetaData image_metadata;

// static std::map> hands_track_points;

private:

//该函数返回真代表出现了错误,返回假代表正确

bool CheckError(const char* error) {

if(status != XN_STATUS_OK ) {

//QMessageBox::critical(NULL, error, xnGetStatusString(status));

cerr << error << ": " << xnGetStatusString( status ) << endl;

return true;

}

return false;

}

//手势某个动作已经完成检测的回调函数

static void XN_CALLBACK_TYPE CBGestureRecognized(xn::GestureGenerator

&generator, const XnChar *strGesture, const XnPoint3D *pIDPosition,

const XnPoint3D *pEndPosition, void *pCookie) {

// COpenNI *openni = (COpenNI*)pCookie;

// openni->hands_generator.StartTracking(*pIDPosition);

hands_generator.StartTracking(*pIDPosition);

}

//手势开始检测的回调函数

static void XN_CALLBACK_TYPE CBGestureProgress(xn::GestureGenerator

&generator, const XnChar *strGesture, const XnPoint3D *pPosition,

XnFloat fProgress, void *pCookie) { // COpenNI *openni = (COpenNI*)pCookie;

// openni->hands_generator.StartTracking(*pPosition);

hands_generator.StartTracking(*pPosition);

}

//手部开始建立的回调函数

static void XN_CALLBACK_TYPE HandCreate(HandsGenerator& rHands, XnUserID xUID, const XnPoint3D* pPosition,

XnFloat fTime, void* pCookie) { // COpenNI *openni = (COpenNI*)pCookie;

XnPoint3D project_pos;

depth_generator.ConvertRealWorldToProjective(1, pPosition, &project_pos); // openni->hand_point = project_pos; //返回手部所在点的位置

hand_point = project_pos;

pair> hand_track_point(xUID,

vector());

hand_track_point.second.push_back(project_pos);

hands_track_points.insert(hand_track_point);

}

//手部开始更新的回调函数

static void XN_CALLBACK_TYPE HandUpdate(HandsGenerator& rHands, XnUserID xUID, const XnPoint3D* pPosition, XnFloat fTime,

void* pCookie) {

// COpenNI *openni = (COpenNI*)pCookie;

XnPoint3D project_pos;

depth_generator.ConvertRealWorldToProjective(1, pPosition, &project_pos);

// openni->hand_point = project_pos; //返回手部所在点的位置

hand_point = project_pos;

hands_track_points.find(xUID)->second.push_back(project_pos);

}

//销毁手部的回调函数

static void XN_CALLBACK_TYPE HandDestroy(HandsGenerator&rHands, XnUserID xUID, XnFloat fTime,

void* pCookie) {

// COpenNI *openni = (COpenNI*)pCookie;

//openni->hand_point.clear(); //返回手部所在点的位置

hands_track_points.erase(hands_track_points.find(xUID));

}

//有人进入视野时的回调函数

static void XN_CALLBACK_TYPE CBNewUser(UserGenerator &generator, XnUserID user, void *p_cookie) {

//得到skeleton的capability,并调用RequestCalibration函数设置对新检测到的人进行

骨骼校正

generator.GetSkeletonCap().RequestCalibration(user, true);

}

//完成骨骼校正的回调函数

static void XN_CALLBACK_TYPE CBCalibrationComplete(SkeletonCapability

&skeleton,

XnUserID user, XnCalibrationStatus calibration_error, void *p_cookie) {

if(calibration_error == XN_CALIBRATION_STATUS_OK) {

skeleton.StartTracking(user);//骨骼校正完成后就开始进行人体跟踪了

}

else {

UserGenerator *p_user = (UserGenerator*)p_cookie;

skeleton.RequestCalibration(user, true);//骨骼校正失败时重新设置对人体骨骼继续进行校正

}

}

private:

XnStatus status;

Context context;

ImageGenerator image_generator;

// DepthGenerator depth_generator;

UserGenerator user_generator;

GestureGenerator gesture_generator;

// HandsGenerator hands_generator;

// map> hands_track_points;

XnMapOutputMode xmode;

public:

// static XnPoint3D hand_point;

};

#endif

实验总结:

本次实验简单的利用OpenNI的手部跟踪功能提实时分隔出了人体手所在的部位。但是该分隔效果并不是特别好,以后可以改进手利用色彩信息来分隔出手的区域,或者计算出自适应手部位的区域。另外,本程序只是暂时分隔出一个手,以后可以扩展到分隔出多个手的部位.

参考资料:

Kinect+OpenNI学习笔记之8(Robert Walter手部提取代码的分析)

https://www.sodocs.net/doc/a717887947.html,/u/5505209/FingertipTuio3d.zip

附录:实验工程code下载。

作者:tornadomeet 出处:https://www.sodocs.net/doc/a717887947.html,/tornadomeet 欢迎转载或分享,但请务必声明文章出处。

手绘古装服装设计图

手绘是从事建筑,服饰陈列设计、橱窗设计、家居软装设计、空间花艺设计、美术、园林、环艺、摄影,工业设计视觉传达等专业学习的学生一门重要的专业必修课程。 手绘是从事建筑,服饰陈列设计、橱窗设计、家居软装设计、空间花艺设计、美术、园林、环艺、摄影、工业设计、视觉传达等专业学习的学生一门重要的专业必修课程。在效果图的学习过程中,临摹是一个非常重要的内容与环节。它是衡量大学生手绘能力的重要指标。同时对大学生毕业,就业都具有很大的影响。与其相对应的是电脑效果图。手绘与我们的现代生活密不可分,建筑、服装、插画、动漫……手绘的形式分门别类,各具专业性,对建筑师、研究学者、设计人员等设计绘图相关职业的人来说,手绘设计的学习是一个贯穿职业生涯的过程。手绘培训是一种以手绘技能需求为对象的教育训练,对现代社会设计美学的传承有着不可取代的现实意义。 手绘是应用于各个行业手工绘制图案的技术手法,设计类手绘主要是前期构思设计方案的研究型手绘和设计成果部分的表现型手绘,前期部分被称为草图,成果部分被称为表现图或者效果图。手绘内容很广阔,所以言语无法尽善表达。 "图画是设计师的语言"。从家居设计到空间花艺设计,从陈列设计到橱窗设计,可以肯定的是,没有一个设计师是不会画图的。虽然随着科技的发展,很多平面设计图和3D效果图都

被用来体现产品设计,但是电脑制图所需的时间和硬件设备上都具有一定的局限性,所以作为一个设计师,如何把自己的创意和灵感记录和描绘出来,如何用画笔及时的与客户交流沟通,成为衡量设计师们专业度的重要标准。 手绘不仅仅只有手绘设计图还要包含手绘壁画、手绘墙纸、手绘装饰画、手绘服饰、手绘墙等更多的内容。作为一名设计师应该具备的基础素质:职业规划、设计文化、美学基础、设计法则、手绘技巧、必备软件…… 绘画步骤及基本技法: 1、准备 2、草图 3、正稿 4、上色 5、调整 6、出稿

服装设计手绘 教程 人体结构

Kevin 服装设计讲座一:服装公司需要的人体 现在的各大服装院校,以画出夸张,抽象的,装饰性,不合实际的时装画,还引以自豪,以为这个就是服装设计课程的最好重要的东西,我觉的这个是学院和老师引导的一个很大错误,从而导致了很多大学毕业生,画出的设计稿,居然是画而不实,连面试都会受阻,这个太让人气愤了。 在现如今的公司里,用到效果图的人体,大多数都是简易型人体,有时就是简化到,只剩下几根线条了!在公司里,是讲究实效的,尤其随着现如今服装快速发展,就更讲究效率了,很多时候都追求精炼,简洁,,高效!人体在设计师眼里其实就是一个衣架,能够摆出衣服的穿着动态就可以了,更主要的还是对服装设计构思的表达。 现在很多人学习服装设计,把重心盲目的放着画人体上,花费了过多的精力,这也是一个很大的错误引导。你在学画人体的时候要先弄清楚,在公司里用的人体的要求,因为你最终是要到公司里工作的(学的时候,老师就要讲的很清楚的,让学生一目了然,不至于学到最后,都不知道公司里认不认可,那才是悲哀的),所以必须清楚的,针对性去训练学习服装设计(这在我的教学里作为教学纲领),这样你才能明明白白的去学习,才不会迷茫! 接下来,我就说说,如何来画公司用的人体 在公司里人体很多时候都是可以重复去用的,不需要你每个款式都要配一个人体姿势的,只要你选择几个标准的人体姿势,就可以作为你设计中长期应用的人体了,用的时候,就直接描摹一下,就可以用了。而在学校学画人体和时装画,那是为了了解人体的比例和画设计稿的一些画线技法,比如,褶皱,褶裥,荷叶边,木耳边,车橡筋等一些衣纹褶皱,这样会使你画的设计稿更加准确生动。 我把我教学中用的人体和大家分享一下,这个人体我是在cd软件中画出的,让人体线条更加清楚流畅!

人体动态表现——拼贴画手法

人体动态表现——拼贴画手法 【教学内容】用拼贴画的手法表现人体动态 【课时安排】1课时(45分钟) 【设计理念】 让学生成为课堂的主人,加强学生的动手操作能力;以任务引领的方式,注重课堂的理实结合,培养学生的创新创造和合作学习的能力。 【教材简析】 本章节内容选自省编教材<<服装设计基础>>第三章第二节服装设计绘画人物造型的方法与技巧这一章节内容。作为一名设计师,必须探索所有能用的绘画和时装画表现方法。其中最洒脱的方法之一就是拼贴画。作为时装画表现技法的一种,用拼贴画来表现人体的动态,使时装画表现技法的教学更加直观化和多样化,也对下节新课手绘服装人体动态表现二横线、重心等内容起到了的承上启下的作用。 【学情简析】 本节课的授课对象为高一的学生,已有一定的绘画基础和小学初中劳技课积累的动手基础,但长期都是以临摹为主,缺乏创新精神,表现技法的思维比较局限对人体动态变化要素也不够了解。并还针对学生,理解和造型能力不强,注意力容易分散,但对新鲜事物特别感兴趣这一特点,确定了本节课的教学模式以任务引领、学生动手实践操作为主,并通过团队合作和教师引导来完成本节课的学习。 【教学目标】 1、通过本堂教学锻炼学生的快速思维,运用剪报、撕纸等探索新的方法在纸上表现理念,让学生打破传统的服装画绘制方法和工具; 2、让学生用所学的拼贴画手法表现人体的动态 【教学重点】 1、打破思维,敢于探索创新服装画的材料和手法; 2、人体动态拼贴画制作过程中二横线的变化和重心位置; 【教学难点】 1、如何启发学生敢于突破传统思维,善于发现拼贴画的各类材料及手法; 2、如何用拼贴画的手法进行人体动态的表现(如何撕剪和拼贴) 【教法学法】 1、教法:任务引领教学法 2、学法:实践探究法 【课前准备】 1、醒目的彩纸剪图、彩报、杂志彩页、贺卡、海报等等 2、分组:共35人,5人一组,分成7组 3、摄像头一个

服装人体动态——教案

《服装人体动态——正面动态表现》教案设计 【课题】服装人体动态——正面动态表现(高等教育出版社《服装设计基础》第二章第三节) 【课时】1课时(45分钟) 【授课班级】07服装设计中专 【授课时间】08年10月9日 【教材分析】 本节教材是《服装设计基础》中第二章第三节的内容。服装设计教学注重学生专业基础知识和专业基本能力的培养,注重学生的创新能力和实践能力的培养。服装人体动态是服装设计基础教学的重要内容,它不仅贯穿整个服装画知识教学,而且是学生顺利、快捷勾画出服装人体动态的关键一环。由此可见,服装人体动态在服装设计基础教学中占有重要地位,它是对前面知识的巩固,也是后继课的基础,具有承上启下的重要作用。 教材处理:为充分体现课程的实用性和生活性以及结合学生的学情,在教学设计中对教材内容做了如下的二点处理:第一点、将正面人体动态的表现作为服装人体动态变化的基础原理来进行分析讲解,符合学生有易到难的认识规律;第二点,按本节课的教学内容和要求,拓展了人体动态表现的相关知识。 【学情分析】教学对象:高二年级学生 基本情况:针对职高学生的特点,学生基础相对较差,对理论学习普遍缺乏兴趣,但是他们善于观察,对动手操作实务,尤其是当课本上的知识与实际生活相联系时,他们会产生浓厚的兴趣。因此,本堂课是以学生为本,在生活实例与课本理论知识之间找到契合点,让学生积极主动地做到理论与实践相结合,从而激发学生学习的兴趣。 【教学方法】1、创设情景法 2、直观演示法 3、分析比较法 4、举例法 【学法指导】1、观察探究法 2、实践操作法 3、小组讨论法 【设计理念】 服装设计基础教学是一种理论与实践相结合的教学活动,在教学实践中应尽量体现服装设计的生活性和实用性。因此根据服装设计教学的特点,结合本节知识内容,设计了“创设情境→分析感知→理解掌握→综合运用”的教学模式。本节课将生活中的真实人体为服装设计表现的基础和依据作为主线,通过构建时装模特走秀,引出人体简化,由人体的动态变化的原因来组织教学。教师始终是学生学习的组织者、指导者。同时,结合多种形式的活动和交流,培养学生的自主探究、分析比较的能力。 【教学目标】知识目标: 1、了解服装人体的简化法; 2、理解人体动态表现表现规律; 3、理解人体的重心线和重心点的概念;

人体动态与着装技法

教案( 2012—2013学年第二学期 ) 课程名称:人体动态与着装技法 授课学时:30学时 授课班级:11服装工学 任课教师:尹佳 怀化职业技术学院 2013年4月整理

怀化职业技术学院 2012-2013 学年第 2学期课程授课计NO 1 教师尹佳课程名称服装人体动态和着装表现技法授课班级11服装工学教研室主任:系(部)主任: 总时数30 理论 课时 26 实验实训课时 4 电教 考试 考核 考查 周次顺 序 章节教学内容摘要学时 教学形式 备注 理论实践 1 学生报到 2 1 人体结构和动态规律 2 1 1 3 2 女装动态 2 1 1 4 3 男装动态 2 1 1 5 4 童装动态 2 1 1 6 5 徒手表现 2 1 1 7 6 女装着装图一 2 1 1 8 7 女装着装图二 2 1 1 9 8 男装着装图一 2 1 1 10 9 男装着装图二 2 1 1 11 10 童装着装图 2 1 1 12 11 着装表现技法一 2 1 1 13 12 着装表现技法二 2 1 1 14 其他科目实习 15 其他科目实习 16 其他科目实习 17 其他科目实习 18 13 着装表现技法练习 2 1 1 19 14 考试 4 教材及 参考书目 服装设计图人体动态与着装表现技法胡晓东著湖北美术出版社注:本计划表一式四份,教师、系部、督评办、教务处各一份,于开学第一周由系部汇总查。

怀化职业技术学院课时授课计划NO: 1 教师:尹佳教研室主任:2013 年 3 月3日 授课日期3月4日月日月日月日教学检查:2周星期1 周星期周星期周星期 授课 班级 11服装工学 章节课题人体结构和 动态规律 授课方式多媒体 授课时间90分钟 目标要求1 简单了解人体结构才能帮助我们画好人体的动态造型 2 掌握人体的动态规律 重点难点了解人体结构和比例掌握人体的动态规律 教学要点1 人体的主要肌肉和骨骼 2 服装人体的比例 3 人体的动态规律 作业画服装人体动态和比例图 执行 情况 良好

相关主题