将平面上的3D坐标转换为新的2D坐标系。

3
我一直在做一个个人项目,目的是生成满足方程x^2 + y^2 + z^2 = S的整数解的图像,其中'S'是任意整数。
换句话说,我正在寻找所有3D点[x,y,z],其中x,y和z都是完全平方整数,且x + y + z = S。
例如,S = 2809将具有以下解:
[144,1296,1369], [144,729,1936], [0,0,2809] ...以及上述所有排列(即144 + 729 + 1936 = 1936 + 729 + 144)
在我提出问题之前,这里有一个小的离题讨论,以便更好地理解:
通用方程x + y + z = S的所有解将位于由以下2D平面定义的区域中:
A = [S,0,0] B = [0,S,0] C = [0,0,S]
这是一个关于 x + y + z = 50 的所有解(不仅是正方形点)的图表,以说明此方程的所有解都位于上述定义的 ABC 所界定的同一平面上。请注意,下面三角形的顶点为:[50, 0, 0]、[0, 50, 0] 和 [0, 0, 50]。

ABC plane

回到我的问题: 在找到正方形解决方案点之后,我希望将3D解决方案点转换为基于ABC平面的2D坐标,其中A为(0,0),B为最大的“x”值,C为最大的“y”值。然后,我希望将这些解决方案输出到图像文件中。
我的线性代数知识很少,我一直无法找到一种方法来将3D坐标转换为基于3个非共线点的2D平面坐标。
我的代码目前是用Python编写的,但算法/数学答案同样好!
非常感谢任何帮助!

这个问题至少有三个不同的问题。请将您的问题集中在“我的输入是”和“期望输出”的形式上,以便我们更好地帮助您。 - Gulzar
@Gulzar 不确定你看到了哪些其他问题。输入:一组3D点。期望输出:在ABC平面上以A为原点的一组2D点。 - Matt
我认为你在使用“转置”一词时实际上是指“变换”(或者可能是投影)—— 转置基本上意味着“交换”。此外,任何三个不同的点都可以定义一个平面,因此说“非共线点”是没有意义的。 - martineau
@martineau,P1 =(1,2,3),P2 =(2,4,6),P3 =(3,6,9)是三个不同的点,它们不能定义一个平面,而是一条直线,因为P2和P3都是P1的线性组合。因此,“非共线”部分并不毫无意义。 - Matt
2个回答

6
据我所见,您已经找到了您的 (x, y, z) 点,您的问题是如何将它们投影到一个平面上。
请参考 投影矩阵 以学习如何将3D世界投影到您选择的图像平面上。
具体来说,您需要将您的 (x, y, z) 坐标表示为 齐次坐标,通过将它们称为 (x, y, z, 1) 并将它们乘以与您需要投射的平面正交的相关相机矩阵。
这将得出形式为 (x', y', f) 的2D齐次坐标,通过 (x_projected, y_projected) = (x'/f, y'/f) 您将能够获得投影坐标。 OpenCV 是您的好朋友。
总结:
1. 输入:n个(x, y, z)点 2. 使用opencv获取投影(相机)大小为(4, 3)的矩阵M或使用其他工具进行计算。 3. 给所有点添加最后一个维度1,将它们作为3D齐次坐标获取:n个点(x, y, z, 1) 4. 将所有点乘以矩阵以获取投影点作为2D齐次坐标:M * (x, y, z, 1)^T = (x', y', f) 5. 获取n个实际的2D投影坐标(相对于由M矩阵定义的相机中心):(x, y) = (x'/f, y'/f)
额外提示:您可以将所有的 (x, y, z, 1) 点堆叠成一个 (4, n) 的矩阵P,并且整个乘法过程将是 R = M * P,一个形状为 (3, n) 的结果矩阵R,其列是生成的齐次坐标。

1

我认为Gulzar的答案是正确的,但更侧重于渲染(即相机和齐次坐标)。不过,我确实找到了如何做我想要的事情。

import ast
import math
import matplotlib.pyplot as plt

def dot_3d(a, b):
    return (a[0]*b[0])+ (a[1]*b[1]) + (a[2]*b[2])

def minus_3d(a, b):
    return [a[0] - b[0], a[1] - b[1], a[2] - b[2]]

def midpoint_3d(a, b):
    return [(a[0] + b[0])/2, (a[1] + b[1])/2, (a[2] + b[2])/2]

def normalize_3d(vec):
    magnitude = math.sqrt(vec[0]**2 + vec[1]**2 + vec[2]**2)
    return [vec[0]/magnitude, vec[1]/magnitude, vec[2]/magnitude]

X = 2809

A = [X, 0, 0]
B = [0, X, 0]
C = [0, 0, X]

S = set([])
for a in range(X+1):
    if int(math.sqrt(a))**2 == a:
        for b in range(X+1):
            if int(math.sqrt(b))**2 == b:
                for c in range(X+1):
                    if int(math.sqrt(c))**2 == c and a + b + c == X:
                        S.add(str([a, b, c]))
S = list(S)

origin = A
normal = normalize_3d([X/3, X/3, X/3])
ax1 = normalize_3d(minus_3d(B, A))
ax2 = normalize_3d(minus_3d(C, midpoint_3d(A, B)))

answers = []

for point_str in S:
    point = ast.literal_eval(point_str)
    x = dot_3d(ax1, minus_3d(point, origin))
    y = dot_3d(ax2, minus_3d(point, origin))
    answers.append([x, y])

plt.scatter([p[0] for p in answers], [p[1] for p in answers])
plt.xlabel('x')
plt.ylabel('y')
plt.show()

开始绘制3D坐标图: 输入图像描述

在ABC平面上的“投影”坐标: 输入图像描述


介意从几何角度解释一下你所做的吗? - Gulzar
此外,请注意函数 dot_3dminus_3dmidpoint_3d 可以使用(标准)numpy库通过给定两个 np.array,分别用 a * ba - b(a + b) / 2 表示。同时,normalize_3d 可以使用 normalized_v = v / np.sqrt(np.sum(v**2)) 实现。 我不太理解您代码中的投影部分。 - Gulzar
更好的是,normalized_v = v / np.sqrt(np.sum(v**2)) 可以写成 normalized_v = v / np.linalg.norm(v) - Gulzar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接