最近做一个关于kinect的东西,主要是在RGB图上提取指定的平面。对于kinect也是刚刚接触不是很熟悉,捣鼓了两天做了很粗糙的东西,但是也学到了一些东西,所以记录一下。

思路大概就是:

在RGB中选择一个感兴趣的平面,标定三个点来指定这个平面,由kinect深度图获取指定点的深度,也就是这个点在kinect坐标下的z值,再由kinect参数算出xy值,由此就可以算出这个平面在空间中的位置了,然后对RGB上的每一个点都使用同样的方法算出空间位置计算和指定平面的距离,去掉不再平面上的点即可。

这个思路最基本的就是如何把RGB上的点转换到空间中去。

本来最开始打算看看kinect有没有官方提供这样的功能,后来想了一下,不是很难算,就自己算了。

算法就是一个视椎:

知道了A点平面内的坐标和深度,计算B点位置。

可以直接使用投影矩阵。但是介于简便快速,还是直接上公式:

depth = 深度图取值
point.z = depth
point.y = 2*depth*tan(PI/6)*(RGB像素高度/2-pix.y)/RGB像素高度
point.x = (pix.x-RGB像素宽度/2)*point.y/(RGB像素高度/2-pix.y)

pix.x,pix.y就是RGB图上指定点的像素坐标。由于图像的原点在左上角所以做了个左边变换。

PI/6是kinect的垂直FOV的一般,也就是30度。
KinectV2的垂直fov是60度,水平是70.kinectV2输出的深度图分辨率是512*424,我根据这个视角和高度算过宽度,算出来结果基本就在424左右,所以这个深度图的确可以看成是视椎的一个截面。

另外,由于精度要求不是很高,所以红外摄像头和RGB摄像头之间的没有计算在内。

有了这些东西,就可以计算出RGB上指定坐标点在空间中的位置了。

使用下列公式可以计算出三个点指定的平面一般式ax+by+cz+d=0的参数adcd,用函数的形式给出:

void get_panel(Vec3f p1,Vec3f p2,Vec3f p3,int &a,int &b,int &c,int &d)
{ a = ( (p2.y-p1.y)*(p3.z-p1.z)-(p2.z-p1.z)*(p3.y-p1.y) ); b = ( (p2.z-p1.z)*(p3.x-p1.x)-(p2.x-p1.x)*(p3.z-p1.z) ); c = ( (p2.x-p1.x)*(p3.y-p1.y)-(p2.y-p1.y)*(p3.x-p1.x) ); d = -(a*p1.x+b*p1.y+c*p1.z); }

有了一般式,可以直接算出点到平面的距离:

float dis_pt2panel(Vec3f pt)
{
return abs(a*pt.x+b*pt.y+c*pt.z+d)/sqrt(a*a+b*b+c*c);
}

现在万事具备了,只要做一些简单的数学计算就可以算出结果。

途中遇到一些问题:

1、kinect返回的原始深度图是16位,前三位是player index,需要右移三位才能得到深度值

2、opengl纹理格式要使用GL_RGBA32F,把抽出的深度值换成小数放进去才正常。

下面是基于openframeworks写的一些散乱的代码:

//.h
#pragma once #include "ofMain.h"
#include "..\..\addons\ofxGui\src\ofxGui.h"
#include "ofxKinectCommonBridge.h" class testApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
void keyPressed(int key);
void keyReleased(int key);
void mouseMoved(int x, int y);
void mouseDragged(int x, int y, int button);
void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void windowResized(int w, int h);
void dragEvent(ofDragInfo dragInfo);
void gotMessage(ofMessage msg); ofxKinectCommonBridge kinect; ofPixels px;
ofPixels pxg;
int w;
int h;
ofShortImage Image_Depth;
ofShortImage im;
ofFbo fbo; ofImage out;
ofImage ext; ofShader s; ofxPanel gui;
ofxFloatSlider addjustgui;
};
//.cpp
#include "testApp.h" /*
Readme
鼠标左中右按钮指定一个平面,不推荐超过RGB图的边界
console会打印选定点的空间坐标值
滑动滑块来调整平面距离精度,这个值就是每个点距离平面的阈值
这个值调大不会出现闪烁
测量计算单位精度为1cm 全局变量:a,b,c,d是指定平面参数
kinectV2参数:
水平FOV:70度
垂直FOV:60度
计算公式:
depth = 深度图取值
point.z = depth
point.y = 2*depth*tan(PI/6)*(RGB像素高度/2-pix.y)/RGB像素高度
point.x = (pix.x-RGB像素宽度/2)*point.y/(RGB像素高度/2-pix.y) 注意:kinect未检测到的点没有做处理
*/
ofVec2f po;
ofVec3f _3po;
ofVec2f po1;
ofVec3f _3po1;
ofVec2f po2;
ofVec3f _3po2;
float a,b,c,d; void get_panel(ofVec3f p1,ofVec3f p2,ofVec3f p3)
{ a = ( (p2.y-p1.y)*(p3.z-p1.z)-(p2.z-p1.z)*(p3.y-p1.y) ); b = ( (p2.z-p1.z)*(p3.x-p1.x)-(p2.x-p1.x)*(p3.z-p1.z) ); c = ( (p2.x-p1.x)*(p3.y-p1.y)-(p2.y-p1.y)*(p3.x-p1.x) ); d = -(a*p1.x+b*p1.y+c*p1.z); } float dis_pt2panel(ofVec3f pt)
{ return abs(a*pt.x+b*pt.y+c*pt.z+d)/sqrt(a*a+b*b+c*c); }
//--------------------------------------------------------------
void testApp::setup(){ ofSetWindowShape(,); ofDisableArbTex(); kinect.initSensor();
kinect.initColorStream(true);
kinect.initDepthStream(true);
kinect.initBodyIndexStream();
kinect.initSkeletonStream(true);
//kinect.initIRStream(640,480); //simple start
kinect.start();
ofDisableAlphaBlending(); //Kinect alpha channel is default 0; w = kinect.getDepthPixelsRef().getWidth();
h = kinect.getDepthPixelsRef().getHeight();
px.allocate(w,h,ofImageType::OF_IMAGE_COLOR); pxg.allocate(w,h,ofImageType::OF_IMAGE_GRAYSCALE); Image_Depth.allocate(,,ofImageType::OF_IMAGE_GRAYSCALE);
fbo.allocate(,,GL_RGBA32F);
out.allocate(,,ofImageType::OF_IMAGE_COLOR_ALPHA);
ext.allocate(,,ofImageType::OF_IMAGE_COLOR_ALPHA);
s.load("vertex.c","fragment.c"); gui.setup();
gui.add(addjustgui.setup("adjust",,,,,)); } //--------------------------------------------------------------
void testApp::update(){
kinect.update();
} //--------------------------------------------------------------
void testApp::draw()
{
ofBackground(, ); if(!kinect.isFrameNewDepth())
return; kinect.mapDepthToColor(kinect.getColorPixelsRef(),px); kinect.drawDepth(,,,); ofShortPixels& P = kinect.getRawDepthPixelsRef();
Image_Depth.setFromPixels(P);
Image_Depth.update();
int wd = P.getWidth();
int ht = P.getHeight();
//计算指定点的空间坐标
ofShortColor C1 = P.getColor(po.x,po.y);
_3po.z = C1.r>>;
_3po.y = _3po.z*0.57735f/.f*(-po.y);
_3po.x = (po.x-.f)*_3po.y/(-po.y+.f); _3po1.z = P.getColor(po1.x,po1.y).r>>;
_3po1.y = _3po1.z*0.57735f/.f*(-po1.y);
_3po1.x = (po1.x-.f)*_3po1.y/(-po1.y+.f); _3po2.z = P.getColor(po2.x,po2.y).r>>;
_3po2.y = _3po2.z*0.57735f/.f*(-po2.y);
_3po2.x = (po2.x-.f)*_3po2.y/(-po2.y+.f); printf("(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",(int)_3po.x,(int)_3po.y,(int)_3po.z,(int)_3po1.x,(int)_3po1.y,(int)_3po1.z,(int)_3po2.x,(int)_3po2.y,(int)_3po2.z); for(int i=;i<wd;i++)
{
for(int j=;j<ht;j++)
{
       //将深度数据处理好放入fbo
ofFloatColor CCC;
ofShortColor C = P.getColor(i,j);
CCC.r = (C.r>>)/.f;
CCC.g = (C.g>>)/.f;
CCC.b = (C.b>>)/.f;
CCC.a = ;
ext.setColor(i,j,CCC);
Image_Depth.setColor(i,j,C);
       
ofShortColor CC = P.getColor(i,j);
float depth = CC.r>>;
float ry = depth*0.57735/.f*(-j);
float rx = (i-)*ry/(-j);
float rz = depth; ofVec3f v(rx,ry,rz); get_panel(_3po,_3po1,_3po2); float dis = dis_pt2panel(v); if(dis<=addjustgui)
{
out.setColor(i,j,px.getColor(i,j));
}
else
{
out.setColor(i,j,ofColor::red);
}
}
} Image_Depth.update();
ext.update();
out.update(); ofImage i(px);
fbo.begin();
ext.draw(,);
fbo.end();
s.begin();
s.setUniform3f("iResolution",,,);
s.setUniform3f("pt1",_3po.x,_3po.y,_3po.z);
s.setUniform3f("pt2",_3po1.x,_3po1.y,_3po1.z);
s.setUniform3f("pt3",_3po2.x,_3po2.y,_3po2.z);
s.setUniformTexture("texture0",fbo.getTextureReference(),);
s.setUniformTexture("colortex",i.getTextureReference(),);
s.setUniform1f("adjust",addjustgui); ofRect(-,-,0.5,); s.end(); i.draw(,); kinect.drawDepth(,,,); ofPushStyle();
ofSetColor(,,);
ofRect(po.x,po.y,,,);
ofRect(po.x+,po.y,,,);
ofSetColor(,,);
ofRect(po1.x,po1.y,,,);
ofRect(po1.x+,po1.y,,,);
ofSetColor(,,);
ofRect(po2.x,po2.y,,,);
ofRect(po2.x+,po2.y,,,);
ofPopStyle(); out.draw(,,,);
Image_Depth.draw(,,,); gui.draw();
}
//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button)
{
printf("%d,%d\n\n",x,y);
if(button==)
{
po.x = x;
po.y = y;
}
else
if(button==)
{
po1.x = x;
po1.y = y;
}
else if(button==)
{
po2.x = x;
po2.y = y;
} }

上面的代码实现,使用了for迭代和shader两种计算方案。由于是逐像素处理,fragment shader GPU计算起来会比CPU快得多。

shader代如下:

#version
#extension GL_ARB_texture_rectangle : enable
uniform vec3 iResolution;
uniform vec4 cgd;
uniform float iGlobalTime;
uniform float ps;
uniform float factor;
uniform sampler2D texture0;
uniform sampler2D colortex;
uniform vec3 pt1;
uniform vec3 pt2;
uniform vec3 pt3;
uniform float adjust; float a,b,c,d; void get_panel(vec3 p1,vec3 p2,vec3 p3)
{ a = ( (p2.y-p1.y)*(p3.z-p1.z)-(p2.z-p1.z)*(p3.y-p1.y) ); b = ( (p2.z-p1.z)*(p3.x-p1.x)-(p2.x-p1.x)*(p3.z-p1.z) ); c = ( (p2.x-p1.x)*(p3.y-p1.y)-(p2.y-p1.y)*(p3.x-p1.x) ); d = ( -(a*p1.x+b*p1.y+c*p1.z) ); } float dis_pt2panel(vec3 pt)
{ return abs(a*pt.x+b*pt.y+c*pt.z+d)/sqrt(a*a+b*b+c*c); } void main()
{ vec2 pos = gl_FragCoord.xy/iResolution.xy;
vec4 c0 = texture2D(texture0, pos);
vec4 ccc = texture2D(colortex,pos);
vec4 blackc = vec4(,,,); float depth = c0.r*; float ry = depth*0.57735/.f*(-pos.y*);
float rx = (pos.x*-)*ry/(-pos.y*);
float rz = depth; vec3 pt = {rx,ry,rz}; get_panel(pt1,pt2,pt3); float dis = dis_pt2panel(pt); if(dis<=adjust)
{
gl_FragColor = ccc;
}
else
{
gl_FragColor = blackc;
} }

方法都是一样的,由于GLSL类C,所以代码都差不多。

运行结果:

可以看出来,基本功能还是实现了,但是在准确性上依然有很多问题需要解决。

kinectV2平面检测的更多相关文章

  1. 深度估计&平面检测小结

    https://yq.aliyun.com/ziliao/582885 最近一段时间已知忙着赶图像分析与理解的项目,在三个星期内强行接触了CNN,MRF,Caffe,openCV在内的很多东西.现在项 ...

  2. 目标检测---搬砖一个ALPR自动车牌识别的环境

    目标检测---搬砖一个ALPR自动车牌识别的环境 参考License Plate Detection and Recognition in Unconstrained Scenarios@https: ...

  3. 《Note --- Unreal 4 --- Sample analyze --- StrategyGame(continue...)》

    ---------------------------------------------------------------------------------------------------- ...

  4. ARKit 初体验

    ARKIT是苹果公司在今年发布的一个AR开发包,用于现有的IOS设备,是的,就是用在手机或者平板上,类似于pokemon go的效果.看了下演示视屏,嗯,看起来很厉害. 对于一个资深软粉,居然被要求研 ...

  5. 爆款AR游戏如何打造?网易杨鹏以《悠梦》为例详解前沿技术

    本文来自网易云社区. 7月31日,2018云创大会游戏论坛在杭州国际博览中心103B圆满举行.本场游戏论坛聚焦探讨了可能对游戏行业发展有重大推动的新技术.新实践,如AR.区块链.安全.大数据等. 网易 ...

  6. ARKit入门

    ARKit介绍 ARKit是iOS11引入的一个全新的框架,使用Visual Inertial Odometry(VIO,视觉惯性里程计)来精确跟踪现实世界中的真实场景.相比其它设备平台,ARKit中 ...

  7. 深度解析丨秒懂nova3手机上超酷炫的AR应用及开发

    此前在HUAWEI nova3发布会中,相信大家都已经感受到了AR能力带来的惊喜: 现实场景召唤圣斗士,随时随地交流合影: 点击观看视频:https://v.qq.com/x/page/m1344f6 ...

  8. 基于ORB-SLAM2的图片识别

    基于ORB-SLAM2的图片识别,其功能是首先运行ORB-SLAM2,在运行过程中调起另一个线程进行图像识别,识别成功后在图片上渲染AR中的立方体模型. 识别过程主要基于ORB-SLAM2中的BoW算 ...

  9. 车牌识别1:License Plate Detection and Recognition in Unconstrained Scenarios阅读笔记

    一.WHAT 论文下载地址:License Plate Detection and Recognition in Unconstrained Scenarios [pdf] github 的项目地址: ...

随机推荐

  1. P2264 情书

    传送门 正常会想到字典树 然鹅数据怎么小直接map也能过 然后就写map暴力匹配了 毫无思维难度,毫无代码难度 注意逗号算单词分隔符,如果有句号就算另一句 同一句的单词重复出现只计算一次贡献 再开个m ...

  2. TortoiseGit安装简单介绍和使用

    首先,你必须有会装软件的技能和一个看得懂英语的眼睛.然后保证Git也装好了 他提供了中文版的安装包哦 安装过程尽量选择默认就行,先装上面那个啊,语言包最后装. 语言配置 因为以前装过,所以...路径是 ...

  3. 权限知识中的AIX ACL

    Aix ACL是对标准权限位的扩展.通过修改分配给个人或组的标准权限,对每个文件或目录进行更精细的控制. 对每个组或用户,有3种权限分配情况: PERMIT : 准许对文件或目录的特定权限. DEMY ...

  4. 利用RELK进行日志收集

    利用RELK进行日志收集 发布时间:April 3, 2018 // 分类:运维工作,开发笔记,python // No Comments 前不久在做应急的总是遇到要求对日志进行分析溯源,当时就想到如 ...

  5. Qt中的布局管理器

    1. 布局管理器提供相关的类对界面组件进行布局管理,能够自动排列窗口中的界面组件,窗口变化后能自动更新界面组件的大小. 2. QLayout是Qt布局管理器的抽象基类,通过继承QLayout实现了功能 ...

  6. thinkPHP 模板操作

    1.assign赋值 $this->assign('title','模板操作'); $this->assign('bests',$bests);//$bests是二维数组 2.变量的输出 ...

  7. my12_xtrabackup压缩备份与恢复

    xtrabackup版本 # rpm -qa | grep xtrabackup percona-xtrabackup-24-2.4.12-1.el7.x86_64 此版本为目前比较新的版本(2018 ...

  8. JS图片加载失败用默认图片代替

    1.onerror 事件会在文档或图像加载过程中发生错误时被触发. 当图片不存在时,将触发onerror,onerror 中img为 指定的默认图片. 图片存在则显示正常图片,图片不存在将显示默认. ...

  9. PlayMaker Destroy Self 和 Destroy Object 和 Set Visibility

    1. 这个销毁是销毁状态机所在的游戏物体,不能销毁父物体. 2. 这个销毁只要把想销毁的游戏物体拖进去就可以. 3. 这个其实不是真正的销毁游戏对象,只是把它的 MeshRenderer 组件关上了, ...

  10. 性能测试工具LoadRunner18-LR之Controller 集合点

    含义 当通过controller虚拟多个用户执行该脚本时.用户的启动或运行步骤不一定是同步的.集合点是在脚本的某处设置一个标记.当有虚拟用户运行到这个标记时,停下等待,直到所有用户都达到这个标记时,再 ...