从二月到五月的毕设答辩,期间发生了很多没有结果的事情,但做毕设这件事情是从一而终的贯彻到底哒,前期遇到了许多技术上的问题,幸运的是在网络中遇到了乐于助人的人们,大家的无私分享才使得我能顺利毕业,这个过程让我深深感受到了互联网的力量以及更加深刻的理解了网络世界里的共享思想。这里重点感谢@会思考的猴子、Robert Pinedo López等网友~
从目前来看,互动装置相关的技术在中国并未受到重视,学习资源的匮乏使得众多爱好者望而却步。从这个角度出发,我希望将学习到的内容分享出来,使更多的人学习、进步!(哈哈哈,好官方,但真是我初衷)
项目的目标群体为老年人,所以采取Kinect身体识别这一功能作为主要的交互方式,简单易操作。画面默认为随机生成的粒子向下坠落,当Kinect检测到有人时,绘制对象的身形剪影,并可以与粒子产生物理碰撞的效果。每次识别到人时,则会生成有关戏曲的角色图片和对应的戏曲,人物的进入动作可以对图片和戏曲进行切换。(戏曲元素的设计是为了更加符合老年群体的兴趣特点)
Toxiclibs是一个独立的开源库集合,用于Java和Processing计算设计任务,由Karsten开发。该库可用于生成设计、动画、交互/界面设计、数据可视化、架构和数字制作等方面。在该项目中,该库用于对人形的绘制。通过该库,将Kinect中识别到的用户数据转化为形状的模式,并绘制在画面中,该操作为后续的形状检测奠定了基础。
BlobDetection库旨在通过在图像上找到“斑点”来进行计算机视觉的开发,它检测的是画面中亮度高于或低于特定值的区域,允许计算斑点区域的边界,但是该库不可以进行斑点区域的追踪既位置信息的计算。主要应用在Processing中,但也可以在任何java程序中使用。该库在本项目中用于检测由Toxiclibs库生成的人体图形,并对图形的边缘进行检测。
Box2D是一个较为又名的2d物理引擎,有C++、flash和Java等版本。我们所采用的为Java版本,可支持在Processing中开发。Box2D的功能较为多样,包括连续碰撞检测、联系回调、凸多边形和圆形、每个身体多个形状、一次性接触歧管、动态树宽相;可以实现的物理效果有连续物理与冲击求解器的时间、持久的身体关节接触图、岛屿解决方案和睡眠管理、接触,摩擦和恢复原状、旋转,棱柱,距离,齿轮,鼠标接头等关节类型、关节限制,电击和摩擦等。在此项目中我们所采用的是碰撞与摩擦的功能,通过该功能实现粒子图案与人物角色的物理碰撞效果。
Kinect识别人体这部分在Processing中实现的方法主要运用KinectV2 for Processing库,通过该库调用用户的身体数据,在本项目中需要获取去除背景的人物身形的图像,Kinect.enableBodyTrackImg()函数即可实现。此时,当识别区内有用户时,可以在Processing中看到没有背景的白色填充的用户,画面中的用户可以实时复制用户的动作,可以简单理解Kinect为充当的是去除背景,白色填充的摄像机。
import Processing.opengl.*;
import KinectPV2.*;
KinectPV2 Kinect;
Kinect = new KinectPV2(this);
Kinect.enableBodyTrackImg(true);
Kinect.init();
cam = Kinect.getBodyTrackImage();
blobs.copy(cam, 0, 0, cam.width, cam.height, 0, 0, blobs.width,blobs.height); //将图像复制到较小的blob图像中
blobs.filter(BLUR, 1); //模糊斑点图像
theBlobDetection.computeBlobs(blobs.pixels); //检测斑点
粒子生成部分将指定粒子在特定的范围内生成,从而搭配基础界面的设计。粒子将采用外部图片做替换,将外部导入的图片作为一个一个的粒子进行生成。图片的设计为戏曲角色,这部分将在Photoshop中进行。粒子生成的书写不需要引用库,Processing中自带的函数即可实现。
void makeBody(float x, float y) {
//在box2d世界坐标中定义一个位于xy的动态体,创建它并设置此box2d体的速度和角度的初始值
BodyDef bd = new BodyDef();
bd.type = BodyType.DYNAMIC;
bd.position.set(box2d.coordPixelsToWorld(new Vec2(x, y)));
body = box2d.createBody(bd);
body.setLinearVelocity(new Vec2(random(-5, 5), random(2, 6)));
body.setAngularVelocity(random(-5, 5));
//设置box2d半径为r的圆形
CircleShape cs = new CircleShape();
cs.m_radius = box2d.scalarPixelsToWorld(r);
//稍微调整一下圆圈的夹具
FixtureDef fd = new FixtureDef();
fd.shape = cs;
fd.density = 1;
fd.friction = 0.01;
fd.restitution = 0.3;
Kinect传进来的数据生成的图像没有经过处理只能具备视觉效果,若是想要与画面发生交互,需要对图像进行处理,使这些数据生成图形。具备图形的属性,这部分操作需要调用Toxiclibs库进行实现。
//将轮廓添加到多边形
ArrayList c = contours.get(i); //ArrayList c =contours.get(i); //没绘制出的人形转化为c,c的属性为PVector
PVector fp = c.get(0); //fp为识别到每个人形的id
PVector lp = c.get(c.size()-1); //lp用来区分每个人形
if (fp.y > KinectHeight-5 && fp.x < closestPoint.x) {
closestPoint = fp;
selectedContour = i;
selectedPoint = 0;
}
if (lp.y > KinectHeight-5 && lp.x < closestPoint.y) {
closestPoint = lp;
selectedContour = i;
selectedPoint = 1; //区分每个人物数据
}
}
}
ArrayList contour = contours.get(selectedContour);
if (selectedPoint > 0) { // 添加的每个人形只能为形状
Collections.reverse(contour);
}
for (PVector p : contour) {
add(new Vec2D(p.x, p.y));
}
contours.remove(selectedContour);
}
}
数据成为图形之后,需要对图形的边缘进行检测,对边缘的检测这步操作为之后的碰撞检测奠定了基础。实现该部分功能所引用的是BlobDetection形状检测库。
void createPolygon() {
ArrayList contours = new ArrayList();
int selectedContour = 0;
int selectedPoint = 0;
for (int n=0; n 100) { //当有数据并且数据完整时生成坐标信息
ArrayList contour = new
ArrayList();
for (int m=0; m 15 || dp > 15) { //对数据信息的匹配进行优化,并且实时处理Kinect
if (contour.size() > 0) { 识别到的人物信息并进行更新
contour.add(new PVector(eB.x*KinectWidth,
eB.y*KinectHeight));
contours.add(contour);
contour = new ArrayList();
} else {
contour.add(new PVector(eA.x*KinectWidth,
eA.y*KinectHeight));
}
} else {
contour.add(new PVector(eA.x*KinectWidth,
eA.y*KinectHeight));}}}}
最后的碰撞检测部分则调用的是Box2D碰撞检测库进行实现,该库是针对于2D图形进行物理化碰撞效果实现的库,这部分内容的实现是整个交互效果呈现的核心重要阶段,通过该库极大增加了项目最终的互动效果。