跳转到内容

RGB+D 视频处理

25% developed
来自维基教科书,开放的书籍,开放的世界

什么是 RGB+D 视频?

[编辑 | 编辑源代码]
  • 2.5D 数据
  • 3D 感知(立体声、结构光、TOF、激光扫描仪,...)

向量、矩阵和标量

 * vector (lowercase Gothic alphabets): v, p, m = [m1, m2, ..., mk]', ...
 * matrix (uppercase Gothic alphabets): R, A, ...
 * scalar (Italic alphabets: s, t, X, Y, Z, ...

索引

   v(1)   // the 1st element of the vector v
   R(1,2) // the 1st row and 2nd col of the matrix R
   A(:,1) // the 1st vector _ from A = [v1, v2, ..., vN]

一些保留符号

hp: a homogeneous representation of a 2D point, hp = [u, v, 1]'
hx: a homogeneous representation of a 3D point, hx = [X, Y, Z, 1]'
p: a 2D point, p = [u, v]'
x: a 3D point, x = [X, Y, Z]'
H: (3x3) planar homography matrix
P: (3x4) projective projection matrix

坐标变换

o<W>: the origin of the world frame
o<C>: the origin of the camera frame
o<O>: the origin of the object frame
(e.g., x<C> represents a 3D point in the camera frame)
R: (3x3) rotation matrix
t: (3x1) translation vector
T<n, m>: transformation from m to n coordinate system (e.g., o<C> = T<C, W> o<W>)
  • 除非我们指定,否则我们使用(第一个)相机坐标系作为世界参考系。
  • 除了以上符号外,我们将遵循 MATLAB 的符号。

坐标变换

[编辑 | 编辑源代码]

当您取一个 3D 点,,该点的每个值 x(j) 都是相对于特定坐标系表示的(例如,传感器坐标系)。点 X 也可以在不同的坐标系中表示(例如,房间的一个角),。为了区分两组值,我们可以写出符号以及相应的坐标系:,其中 是相对于传感器坐标系的点,而 是相对于角落坐标系的相同点。两个坐标系之间存在一个刚性运动,该运动由一个 (3×3) 旋转矩阵 和一个 (3×1) 平移向量 表达:。在齐次坐标系中,刚性运动可以写成。请注意,我们放置了源坐标系(角落)和目标系统(传感器)的符号。一系列变换表示为,其中.

校准

[edit | edit source]

为什么要校准?

总体流程

 a) RGB camera ---- RGB image --(1)-- Rectified RGB image --(3)-- Registered RGB image (w.r.t. IR image)
 b) IR camera ---- IR image --(2)--  Rectified IR image
 c) projector --(internal calibration + IR image)-- Depth image --(4)-- Rectified Depth image --(5)-- 3D points                         
  • 问题 1: (1), (2), (4) - 径向畸变
  • 方法

http://www.ros.org/wiki/kinect_calibration/technical

http://nicolas.burrus.name/index.php/Research/KinectCalibration


  • 问题 2: (3) - RGB 图像到 IR 图像的配准
  • 方法:平面单应性


  • 问题 3: (5) - 3D 重建
  • 方法:三角测量
  • 全部合一

http://www.ee.oulu.fi/~dherrera/kinect/

几何基元和不确定性

[edit | edit source]

2D 空间中的点和线

[编辑 | 编辑源代码]

几何实体

[编辑 | 编辑源代码]
  • 当 x 和 s*x 代表相同的几何实体时,实体 x 是齐次的。
  • 点:欧几里得空间中的二维点 xx = [x1, x2]' 在二维投影空间中表示为 x = [x1, x2, 1]'。
  • 线:二维直线表示为 l = [a b c]'。

二维几何关系

[编辑 | 编辑源代码]
  • 直线和直线上一点的点积为 0:x' * l == 0。
二维欧几里得空间中的直线方程为 a*x + b*y + c == 0。我们可以将其改写为 [a b c] * [x y 1]' == 0,这就是点积。因此,在投影空间中,dot(x,l) == x' * l == 0。
  • 两条直线的叉积给出交点:x = cross(l,m)。
两条直线(l 和 m)的交点是一个点。交点 x 位于直线 l 上:dot(x,l) = 0。此外,点 x 位于直线 m 上:dot(x,m) = 0。因此,l 和 m 的叉积给出交点 x。
  • 两点的叉积给出直线:l = cross(x, y)。
相似性
[编辑 | 编辑源代码]
  • 两点:|| x - y ||
  • 两条直线:|| l x m ||
  • 两条线段

三维空间中的点、线、平面和圆锥曲线

[编辑 | 编辑源代码]
  • 三维直线:两点 X、Y 构成一条直线。
普吕克矩阵定义为 U(4x4) = X Y' - Y X'。从 U 中,直线定义为 L := [ U(4,1) U(4,2) U(4,3) U(2,3) U(3,1) U(1,2) ]'。
直线 L 由 [方向向量 U1 | 法向量 U2] 部分组成。U1 代表向量 Y - X。U2 代表 X 和 Y 的叉积。显然,dot(U1,U2) = 0。
  • 两条直线的交点是一个点。如果两条直线 L、M 相交,dot(L,M) = 0。
  • 两平面的交点形成一条直线。
  • 经过平面 K 的直线 L 形成一个点。
  • ...

刚体运动

[编辑 | 编辑源代码]
欧拉角、轴角、沙西定理
关于自由形状精确配准的刚体变换表示
  • 三维坐标变换
  • 对齐(参见:ICP)

三维投影变换

[编辑 | 编辑源代码]
TP:三维空间的投影变换(15dof),TP = [TA t; v' s]
TA:三维空间的仿射变换(12dof),TP = [TA t; 0' 1]
TS:三维空间的相似变换(7dof),TP = [s*R t; 0' 1]
TE:三维空间的欧几里得变换(6dof),TP = [R t; 0' 1]


不确定性和几何约束

[编辑 | 编辑源代码]

几何实体的不确定性

[编辑 | 编辑源代码]
  • 点的 uncertainty?
  • 直线的 uncertainty?
  • 方向的 uncertainty?
  • 平面的 uncertainty?

示例:三角形

[编辑 | 编辑源代码]
三角形的三个角

假设你用量角器测量三角形的三个角。从第一次试验中,你得到了值 [30.5, 60.0, 90.1] 度。这些值并不完美,因为它们的总和不等于 180 度!因此,你决定测量 10 次并将它们取平均值。但是,你仍然不确定平均值的总和是否会等于 180 度。

图 XXX 显示了三角形中三个角之间的关系。

方法
1) Define a manifold M (e.g. Figure XXX)
2) Measure a set of values p = [a(i), b(i), c(i)] for N times
3) Compute the mean m and the covariance matrix S
4) Project p and S onto the manifold M: q, H
output:
  angles <-- q
  variance <-- f(H)

对多个观测值取平均值

[编辑 | 编辑源代码]

当你在 N 个传感器的外部标定中观察到多个刚体变换时,你可以使用“对偶四元数”来计算“平均”刚体变换。

算法
* input: {R(i), t(i)} % a set of R, t
* output: R*, t*      % optimal R, t
Procedure:
* convert all R(i), t(i) to DQ(i) % Dual Quaternions
* find the mean DQ* from {DQ(i)}
* convert DQ* to R*, t*
对偶四元数
[编辑 | 编辑源代码]

"类似于单位长度四元数可以表示三维空间中的旋转,单位长度对偶四元数可以表示三维空间中的刚体运动。这个事实被用于理论运动学以及在三维计算机图形学、机器人技术和计算机视觉中的应用。"维基百科

L. Kavan, S. Collins, C. O'Sullivan, J. Zara (2006) 对偶四元数用于刚体变换混合,技术报告,都柏林三一学院。
带有良好文档的 Matlab 工具箱
在流形上工作
[编辑 | 编辑源代码]

http://stat.fsu.edu/~anuj/CVPR_Tutorial/ShortCourse.htm 形状分析和活动识别的微分几何方法

多个视图的配准

[编辑 | 编辑源代码]

仅点云

[编辑 | 编辑源代码]

点配准问题 (1) 查找点到点的对应关系,(2) 估计它们之间的变换。以前的方法可以分为三类:ICP(迭代最近点)、软分配方法和概率方法。

ICP 可能会陷入局部最小值,并且对初始化和接受匹配的阈值敏感。因此,最近点策略被连续优化框架内的软分配取代。但是,在存在异常值的情况下,收敛性不能得到保证。点到点的分配问题可以被转化为估计(高斯)混合模型的参数。--> EM-ICP

  • ICP、EM-ICP
  • 张量
  • 初始化

源代码

[编辑 | 编辑源代码]

RGB 2D 特征 + 深度值

[编辑 | 编辑源代码]

由于我们同时拥有 RGB 和深度图,因此可以通过简单的 3D 特征匹配来完成两个视图的配准。给定两组对应的 3D 特征描述符,X = [x1, x2, ..., xn] 和 Y = [y1, y2, ..., yn],X 和 Y 之间的刚体运动,Y = T X,可以直接计算。在存在异常值的情况下,鲁棒估计方法(如 RANSAC)可以提供精确的结果。

伪代码:计算刚性运动
* input: X, Y
* output: R, t

Solution = [];
For i=1:N
  randomly select 5 corresponding points from X, Y: s1 = {X(j), X(k), X(l)} and s2 = {Y(j), Y(k), Y(l)}
  estimate T' = (R', t') using s1, s2
  compute Y^ = T' X
  store the number of inliers, m(i), to Solution: S(i) = (R'(i), t'(i), m(i))
Endfor
Answer = arg max(i) (S(i).m(i))

2D、2.5D 和 3D 特征

[编辑 | 编辑源代码]

曲率估计:HK 分割

[编辑 | 编辑源代码]
  • 参见 HK 分割
  • 存在噪声深度图时的曲率估计

2.5D 特征

[编辑 | 编辑源代码]
  • 在深度图上使用 2D 图像特征

我们可以在 2.5D 深度图上使用 2D 图像特征(例如,SIFT、ORB、SURF、LBP)。关键区别之一是图像的尺度。由于我们知道范围图中的绝对尺度,因此可以将搜索空间限制为仅检测物理上有意义的特征。

3D 特征

[编辑 | 编辑源代码]
  • 最大值、自旋图像
  • 使用 LKT 在深度视频中跟踪特征?

如何获得精确的配准?

[编辑 | 编辑源代码]
  • 深度传感器应经过校准。
  • 我们对 2D 特征的位置和深度值的误差进行建模。
  • 如果我们有一个已知的相机矩阵 K,最小化图像空间中的重新投影误差可以提供一个准确的估计。
  • SIM(表面穿透度量)[Queirolo 2010]
  • RGB 值和深度信息可以融合。

多传感器系统

[编辑 | 编辑源代码]
  • 人脸检测
  • 人脸识别(OpenNI2 中间件)[1]
  • 人脸建模(OpenNI2 应用程序)[2]
  • 面部特征检测:眼睛、鼻尖、嘴巴、耳朵
  • 3D 人脸姿态跟踪
  • 微软发布的人脸跟踪 SDK Kinect Windows[3]

等等。

[编辑 | 编辑源代码]
[编辑 | 编辑源代码]

网格表示

[编辑 | 编辑源代码]
  • 从 2.5D
  • 从 3D

3D 物体识别

[编辑 | 编辑源代码]

[物体识别 - M. Bennamoun 和 G.J. Mamic] 中的特征匹配方法

1. 假设和检验

2. 匹配关系结构

3. 姿态聚类

4. 几何哈希

5. 解释树

6. 配准和距离变换

用于识别的索引

[编辑 | 编辑源代码]
  • 几何哈希

参数模型拟合

[编辑 | 编辑源代码]

线性代数

[编辑 | 编辑源代码]

向量和矩阵操作、伪逆、秩、特征值分解、奇异值分解、最小二乘法、齐次系统

梯度下降法、牛顿法、列文伯格-马夸特法 (http://www.ics.forth.gr/~lourakis/levmar/)、单纯形法

概率与统计

[编辑 | 编辑源代码]

协方差矩阵、贝叶斯概率

  • 卡尔曼滤波、扩展卡尔曼滤波
  • 粒子滤波

计算几何

[编辑 | 编辑源代码]

表面切线、法线、面积、三角形网格、沃罗诺伊图、表面曲率、法线和主曲率

安装:Primesense、Kinect 和华硕相机及库

[编辑 | 编辑源代码]
  • OpenNI 和 Prime Sensor

http://www.openni.org/Downloads/OpenNIModules.aspx,选择与您的操作系统匹配的版本。

尝试此操作:http://structure.io/openni

您需要

  1. OpenNI 二进制文件
  2. 适用于 Prime Sensor 模块的 OpenNI 兼容硬件二进制文件稳定版本

下载并安装二进制文件后,您现在可以使用 OpenNI 库与 OpenCV 结合编写精彩的代码。

  • 在您的工作环境(Visual Studio)中进行配置

在项目的“属性”中,您需要添加

  1. DIRECTORY_OF_OPENNI\Include 到您的“附加包含目录”
  2. DIRECTORY_OF_OPENNI\Lib 到您的“附加库目录”
  3. 在链接器部分,在输入节点下,选择附加依赖项并添加 OpenNI2.lib。(应该在 DIRECTORY_OF_OPENNI\Lib\OpenNI2.lib 中)
  4. 确保您将附加包含目录和库目录添加到您的发布和调试配置中。根据您的 OpenNI2 版本选择 win32 或 x64。
  5. 您的代码文件应包含 OpenNI.h

供您参考,请查看 http://www.openni.org/openni-programmers-guide/

简单的 Openni

[edit | edit source]

https://code.google.com/p/simple-openni/wiki/Installation#Windows

如何从深度传感器读取单个 3D 点?(OpenNI 2)

[edit | edit source]

成功安装 OpenNI2 后,您应该能够运行 DIRECTORY_OF_OPENNI\Samples/Bin 中的示例程序。

以下代码是获取单个点的深度信息的关键代码(对于初始化,您可以参考“SimpleViewer”示例代码)

openni::VideoFrameRef depthFrame;
const openni::DepthPixel* pDepth = (const openni::DepthPixel*) depthFrame.getData;
int width = depthFrame.getWidth();
int height = depthFrame.getHeight();
for (int i = 0; i < height; ++i)
    for (int j = 0; j < width; ++j, ++pDepth)
    if(i == height/2 && j == width/2 && *pDepth != 0 )//The if sentence depends on which point you want, this is the center point for example
    {} //assign the value to a 3D point structure

在 OpenCV 图像中显示来自 OpenNI 输入的深度图 (C++) (OpenNI 1)

[edit | edit source]
/* Display depth map */
// Matthias Hernandez: 07/27/2011 - University of Southern California
// [email protected]
// display the depthMap from openNI input in the openCV image ‘out’
void displayDepthMap(IplImage *out, const XnDepthPixel* pDepthMap, XnDepthPixel max_depth, XnDepthPixel min_depth) {
    uchar *data = (uchar *)out->imageData;
    int step = out->widthStep;
    int channels = out->nChannels;
    float normalize = (float)(max_depth-min_depth)/255.0f;

    int index=0;
    for (int i=0; i<MAX_I; i++) {
   	 if (pDepthMap[i] < min_depth || pDepthMap[i] > max_depth) {
   		 for (int k=0; k<channels; k++)
   			 data[index++] = 0;
   	 } else {
   		 if (normalize != 0) {
   			 for (int k=0; k<channels; k++)
   				 data[index++] = (int)(255-(float)(pDepthMap[i]-min_depth)/normalize);
   		 }
   		 else
   			 for (int k=0; k<channels; k++)
   				 data[index++] = 255;
   	 }    
    }
    
}

显示来自 OpenNI 输入的 RGB 图像 (C) (OpenNI 1)

[edit | edit source]
/* Display image map */
// Matthias Hernandez: 07/27/2011 - University of Southern California
// [email protected]
// display the image map from openNI input in the openCV image ‘out’
void displayImageMap(IplImage *out, const XnUInt8* pImageMap) {
    uchar *data = (uchar *)out->imageData;
    int step = out->widthStep;
    int channels = out->nChannels;    

    for (int i=0; i<MAX3_HR; i+=3) {
   	 data[i+2]    = pImageMap[i];
   	 data[i+1]    = pImageMap[i+1];
   	 data[i]   	 = pImageMap[i+2];
    }
}

从投影到真实世界的转换 (C) (OpenNI 2)

[edit | edit source]
#define XtoZ 1.114880018171494f
#define YtoZ 0.836160013628620f
#define MIN_DEPTH 450
#define MAX_DEPTH 800

void convertP2RW(float *pDepth, float *pReal, int x, int y, int w, int h) {
        int max_i = w * h;

	int i1 = (y * w + x),
		i2 = i1 + max_i,
		i3 = i2 + max_i;

	float Z = pDepth[i1];

    if (Z > MIN_DEPTH && Z < MAX_DEPTH){
        float X_rw = ( (float)x /(float)w -0.5f)*Z*XtoZ;
        float Y_rw = (0.5f-(float)y / (float)h)*Z*YtoZ;

		pReal[i1] = X_rw;
		pReal[i2] = Y_rw;
		pReal[i3] = Z;
	} else {
		pReal[i1] = 0.0f;
		pReal[i2] = 0.0f;
		pReal[i3] = 0.0f;
	}
}

// Of Course, the inverse function is 

void convertRW2P(float *pReal, float *pDepth,int x, int y, int w, int h) {
	 int max_i = w * h;

	int i1 = (y * w + x),
	i2 = i1 + max_i,
	i3 = i2 + max_i;

	float xR = pReal[i1];
	float yR = pReal[i2];
	float zR = pReal[i3];

	int ixi = (xR/zR/XtoZ + 0.5f)*(float)w + 0.5f;  // x
	int iyi = (-yR/zR/YtoZ + 0.5f)*(float)h + 0.5f; // y

	pDepth[i1] = zR;
}

校准的理念(使用绝对圆锥的图像)

[edit | edit source]
  x = P (H inv(H)) X
  H: a projective transformation

无穷远平面和绝对圆锥

PAI:在 H 下固定

AC:在 H 下固定

ADQ:在 H 下固定(单个方程)

 Q = H Q H’
 PAI is the null-vector of ADQ (Q a = 0)

DIAC(绝对圆锥的双重图像)= ADQ 的图像(参见 IAC(绝对圆锥的图像))

W* = K K'(K:校准矩阵)通过 Cholesky 因式分解。

如何从深度相机读取红外流

[edit | edit source]

http://stomatobot.com/primesense-3dsensor-ir-stream/?goback=.gde_2642596_member_242724626

参考资料

[edit | edit source]

深度传感器

[edit | edit source]

PrimeSense www.primesense.com

Kinect - Xbox.com www.xbox.com/KINECT

Xtion PRO LIVE http://www.asus.com/Multimedia/Motion_Sensor/Xtion_PRO_LIVE/

softkinetic http://www.softkinetic.com/

velodynelidar.com http://velodynelidar.com/lidar/lidar.aspx

Leap 简介 http://www.youtube.com/watch?v=_d6KuiuteIA

Mesa Imaging http://mesa-imaging.ch/

工具

[edit | edit source]

http://www.danielgm.net/cc/ CloudCompare - 3D 点云和网格处理软件

http://www.cs.unc.edu/~isenburg/lastools/ LAStools:用于快速转换、过滤、查看、网格化和压缩 LiDAR 的屡获殊荣的软件

问答

[edit | edit source]
  • {在此处提出您的问题...}
华夏公益教科书