为什么旋转3D点块后顶点法线会翻转?

时间:2019-04-29 17:29:36

标签: matlab graphics computer-vision visualization rgl

我有两个人脸3D点云样本。蓝点云表示目标面部,红点云表示模板。下图显示目标和模板面在不同方向上对齐(目标面大致沿x轴,模板面大致沿y轴)。

图1: enter image description here 鼻子周围的区域如图1所示。

我想以鼻尖为旋转中心旋转目标脸(蓝色脸)(我将目标平移到图1之前的模板上,以便鼻尖(即centerpt)用于两个面都叠加)以与模板面(红色面)大致对齐。我使用以下MATLAB代码旋转了目标面部:

% PCA for the target face
targetFaceptfmt = pointCloud(targetFace); % Convert to point cloud format
point = [templateFace(3522, 1), templateFace(3522, 2), templateFace(3522, 3)]; % The 3522th point in the templateFace is the nasal tip point used as center of rotation later on
radius = 20; % 20mm
[NNTarIndex, NNTarDist] = findNeighborsInRadius(Locationptfmt, point, radius); % Find all vertices within 20 of the nasal tip point on the target face
NNTar = select(Locationptfmt, NNTarIndex); % Select the identified points for PCA
[TarVec,TarSCORE,TarVal] = pca(NNTar.Location); % Do PCA for target face using vertices close to the nasal tip

% PCA for the template face
templateFaceptfmt = pointCloud(templateFace); % Convert to point cloud format
[NNTemIndex, NNTemDist] = findNeighborsInRadius( templateFaceptfmt, point, radius); % Find all vertices within 20 of the nasal tip point on the template
NNTem = select(templateFaceptfmt, NNTemIndex); % Select the identified points for PCA
[TemVec,TemSCORE,TemVal] = pca(NNTem.Location); % Do PCA for template face using vertices close to the nasal tip

% Rotate target face with nasal tip point as the center of rotation
targetFace_r = R * (targetFace-cenertpt)' + centerpt';
targetFace_new = targetFace_r';

其中targetFacetemplateFace分别包含未旋转目标面和模板面的坐标。 targetFace_r包含围绕鼻尖旋转后目标脸的坐标,R是通过PCA计算的旋转矩阵(有关旋转公式的来源,请参见here),{{1} }是鼻尖,用作旋转中心。然后,我绘制了转置的centerpt,即targetFace_r,并向每个顶点添加了法线:

图2: enter image description here

在旋转之前,目标面和模板面的法线通常指向相似的方向(图1)。旋转后,目标和模板面都沿y轴对齐(这是我想要的),但是,目标面和模板面的法线指向相反的方向。考虑到模板面未做任何更改,我意识到旋转后计算出的目标面的法线会翻转。但是我不知道为什么。我在R中使用了Rvcg包的targetFace_new函数来检查沿法线的扩展是否会增加质心的大小。模板面返回TRUE,目标面返回FALSE,这确认目标面的顶点法线已翻转。

顶点法线在MATLAB中的计算如下:

checkFaceOrientation

其中TR = triangulation(Faces, Vertices); % Triangulation based on face and vertex information VN = vertexNormal(TR); % Calculate vertext normal 包含面部信息,即连接列表,而Faces包含顶点的坐标。对于旋转前的目标面,旋转后的目标面以及模板面,顶点法线分别进行计算。在旋转目标面前后,我使用相同的Vertices数据来计算顶点法线。

顶点法线翻转会导致错误,需要进一步分析。结果,我必须手动翻转法线以使其指向与模板面法线相似的位置。

图3: enter image description here 图3显示,手动翻转法线后,目标和模板面的法线通常指向相似的方向。

我的问题是,旋转翻转后为什么要计算目标面部的法线?在什么情况下3D点云的旋转会导致顶点法线翻转?

可能有用的其他信息:我获得的旋转矩阵Faces如下:

R

0.0473096146726546 0.867593376108813 -0.495018720950670 0.987013081649028 0.0355601323276586 0.156654567895508 -0.153515396665006 0.496001220483328 0.854643675613313 起,我通过trace(R) = 1 + 2cos(alpha)计算了alpha值,相对于鼻尖点,旋转角度为91.7904。

2 个答案:

答案 0 :(得分:3)

如果我正确理解了所有内容,则看来您的旋转矩阵实际上是对旋转加反射进行编码。如果您的矩阵大约是:

 0.04  0.86  -0.49
 0.98  0.03   0.15
-0.15  0.49   0.85

则沿正轴指向的每个单位矢量的图像为:

x = [ 0.04 0.98 -0.15]
y = [ 0.86 0.03  0.49]
z = [-0.49 0.15  0.85]

但是,如果取xycross(x, y))的叉积,您将得到大约[0.49 -0.15 -0.85],这是{{1}的否定},表示矩阵同时编码旋转和反射。自然地,将网格的顶点乘以反射矩阵会反转其多边形的缠绕顺序,从而产生倒置的法线。

在您引用的幻灯片中,它指出生成旋转矩阵的PCA方法在3D情况下应仅考虑轴的四个不同组合,以确保输出矩阵遵循右手法则。如果检查了所有轴组合,则PCA在搜索最佳匹配时将考虑旋转空间和反射空间。如果是这种情况,并且如果数据中存在一些噪声,使得模板的左半部分与目标的右半部分稍有匹配,反之亦然,则PCA方法可能会生成一个像你观察。也许您可能想重新审视如何从PCA结果中生成z的逻辑?

答案 1 :(得分:2)

in the comments所指,顶点法线的方向将取决于您如何对Faces矩阵中的三角形小平面进行排序。这将遵循right-hand rule,其中您的手指遵循三角形周围的顶点顺序,而您的拇指指示表面法线方向。这是一个简单的示例来帮助说明:

Vertices = [0 0; 0 1; 1 1; 1 0];  % Points clockwise around a unit square in x-y plane
Faces = [1 2 3; 1 3 4];           % Two triangular facets, clockwise vertex ordering
TR = triangulation(Faces, Vertices);
VN = vertexNormal(TR)

VN =

     0     0    -1
     0     0    -1
     0     0    -1
     0     0    -1

在此示例中,Vertices包含x-y平面中单位正方形的4个顶点,如果从正z向下看则按顺时针方向排列。 Faces中定义了两个三角形小平面,并且每行中索引的顺序也沿顺时针方向沿着顶点跟踪。这会导致指向负z方向的每个面的表面法线。计算 vertex 法线时,它们也指向负z方向。

当我们翻转一个三角形的阶以使其点为逆时针时会发生什么?...

Faces = [1 2 3; 1 4 3];  % Second facet is 1 4 3 instead of 1 3 4
TR = triangulation(Faces, Vertices);
VN = vertexNormal(TR)

VN =

     0     0     0
     0     0    -1
     0     0     0
     0     0     1
现在,第二个三角形的

表面法线将指向z的正方向。仅由一个三角形(第2行和第4行)使用的顶点将具有与曲面法线匹配的顶点法线,而每个三角形(行1和3)共享的顶点将具有0的顶点法线(两个曲面法线抵消)

这将如何帮助您解决问题?好吧,很难说,因为我不知道您是如何定义FacesVertices的。但是,如果您确定网格中的每个顶点法线都指向错误的方向,则可以通过在计算法线之前交换Faces矩阵中的两列来轻松地将它们全部翻转:

Faces = [1 2 3; 1 3 4];  % Clockwise-ordered vertices
TR = triangulation(Faces(:, [1 3 2]), Vertices);  % Change to counter-clockwise
VN = vertexNormal(TR)

VN =

     0     0     1  % Normals are now pointing in positive z
     0     0     1
     0     0     1
     0     0     1