最近做一个关于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. poj3040 发工资(贪心)

    题目传送门 题目大意:给一个人发工资,给出不同数量不同面额,(大面额一定是小面额的倍数),问最多能发几天,(每天实发工资>=应发工资). 思路:首先,将大于等于c的面额的钱直接每个星期给奶牛一张 ...

  2. 如何查看mysql执行的所有SQL

    在程序调试中,有时需要看到最终在DB执行的SQL文,而默认mysql此功能是关闭的,开启的方法如下: set global general_log='ON'; 然后用如下命令查看log文件所在路径即可 ...

  3. oracle截取字符串,定索引

    转载:https://www.cnblogs.com/qmfsun/p/4493918.html 使用Oracle中Instr()和substr()函数: 1 2 3 4 5 6 7 8 9 10 1 ...

  4. flink日记

    直接下载 解压, 运行 ./bin/start-cluster.sh 几个概念: 批处理: 大数据量,不要求实时. 输入源是有界的 流处理:实时要求高,通常在毫秒级, 数据量比较小,但是输入源是无界的 ...

  5. Java类名 方法名 常量 变量的命名习惯

    1.包 用于将完成不同功能的类分门别类,放在不同的目录(包)下,包的命名规则:将公司域名反转作为包名,对于包名,每个字母都需要小写. 如果定义类的时候没有使用package(包),那么java就认为我 ...

  6. 转 ogg issue

    1.http://www.dbdream.com.cn/2013/05/17/ogg-00446%E9%94%99%E8%AF%AF%E8%A7%A3%E5%86%B3/ OGG-00446错误解决 ...

  7. 标签li设为display inline-block后间距问题

    在对导航栏做水平排列的时候,我们往往对li元素设为display:inline-block 目的是为了,让所有li元素并排在一起,但是遇到个问题,我们的标签之间会产生空白边距 如图所示: 这样看上去, ...

  8. docker+ bind mount 部署复杂flask应用

    报错如下: [root@test-wenqiang flask-skeleton]# docker run -d -p 80:5000 -v $(pwd):/skeleton --name flask ...

  9. redis初步学习 0

    2.1 Redis是什么 REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统.Redis提供了一些丰富的数据 ...

  10. Python学习笔记--语音处理初步

    语音处理最基础的部分就是如何对音频文件进行处理. 声音的物理意义:声音是一种纵波,纵波是质点的振动方向与传播方向同轴的波.如敲锣时,锣的振动方向与波的传播方向就是一致的,所以声波是纵波.纵波是波动的一 ...