【问题标题】:Perspective projection and rotation in pythonpython中的透视投影和旋转
【发布时间】:2012-12-21 01:40:49
【问题描述】:

我尝试过搜索,但其他问题似乎都不像我的问题。我或多或少地在 python 中尝试透视投影和旋转,并且遇到了障碍。我确信我的投影方程和旋转方程都是准确的;但是,当我运行它时,旋转开始正常,但开始向内旋转,直到矢量与 Z 轴(我正在旋转的轴)处于同一位置。

''' Imports '''
from tkinter import Tk, Canvas, TclError
from threading import Thread
from math import cos, sin, radians, ceil
from time import sleep

''' Points class '''
class pPoint:
    def __init__(self, fPoint, wWC, wHC):
        self.X = 0
        self.Y = 0
        self.Z = 0
        self.xP = 0
        self.yP = 0
        self.fPoint = fPoint
        self.wWC = wWC
        self.wHC = wHC

    def pProject(self):
        self.xP = (self.fPoint * (self.X + self.wWC)) / (self.fPoint + self.Z)
        self.yP = (self.fPoint * (self.Y + self.wHC)) / (self.fPoint + self.Z)

''' Main class '''
class Main:
    def __init__(self):
        ''' Declarations '''
        self.wWidth = 640
        self.wHeight = 480

        self.fPoint = 256

        ''' Generated declarations '''
        self.wWC = self.wWidth / 2
        self.wHC = self.wHeight / 2

        ''' Misc declarations '''
        self.gWin = Tk()

        self.vPoint = pPoint(self.fPoint, self.wWC, self.wHC)

        self.vPoint.X = 50
        self.vPoint.Y = 60
        self.vPoint.Z = -25
        self.vPoint.pProject()

        self.ang = 0

    def initWindow(self):
        self.gWin.minsize(self.wWidth, self.wHeight)
        self.gWin.maxsize(self.wWidth, self.wHeight)

        ''' Create canvas '''
        self.gCan = Canvas(self.gWin, width = self.wWidth, height = self.wHeight, background = "black")
        self.gCan.pack()

    def setAxis(self):
        ''' Create axis points '''
        self.pXax = pPoint(self.fPoint, self.wWC, self.wHC)
        self.pXbx = pPoint(self.fPoint, self.wWC, self.wHC)
        self.pYax = pPoint(self.fPoint, self.wWC, self.wHC)
        self.pYbx = pPoint(self.fPoint, self.wWC, self.wHC)
        self.pZax = pPoint(self.fPoint, self.wWC, self.wHC)
        self.pZbx = pPoint(self.fPoint, self.wWC, self.wHC)

        ''' Set axis points '''
        self.pXax.X = -(self.wWC)
        self.pXax.Y = 0
        self.pXax.Z = 1
        self.pXbx.X = self.wWC
        self.pXbx.Y = 0
        self.pXbx.Z = 1
        self.pYax.X = 0
        self.pYax.Y = -(self.wHC)
        self.pYax.Z = 1
        self.pYbx.X = 0
        self.pYbx.Y = self.wHC
        self.pYbx.Z = 1
        self.pZax.X = 0
        self.pZax.Y = 0
        self.pZax.Z = -(self.fPoint) / 2
        self.pZbx.X = 0
        self.pZbx.Y = 0
        self.pZbx.Z = (self.fPoint * self.wWC) - self.fPoint

    def projAxis(self):
        ''' Project the axis '''
        self.pXax.pProject()
        self.pXbx.pProject()
        self.pYax.pProject()
        self.pYbx.pProject()
        self.pZax.pProject()
        self.pZbx.pProject()

    def drawAxis(self):
        ''' Draw the axis '''
        self.gCan.create_line(self.pXax.xP, self.pXax.yP, self.pXbx.xP, self.pXbx.yP, fill = "white")
        self.gCan.create_line(self.pYax.xP, self.pYax.yP, self.pYbx.xP, self.pYbx.yP, fill = "white")
        self.gCan.create_line(self.pZax.xP, self.pZax.yP, self.pZbx.xP, self.pZbx.yP, fill = "white")

    def prePaint(self):
        self.vA = self.gCan.create_line(self.wWC, self.wHC, self.vPoint.xP, self.vPoint.yP, fill = "red")

    def paintCanvas(self):
        try:
            while True:
                self.ang += 1
                if self.ang >= 361:
                    self.ang = 0

                self.vPoint.X = (self.vPoint.X * cos(radians(self.ang))) - (self.vPoint.Y * sin(radians(self.ang))) 
                self.vPoint.Y = (self.vPoint.X * sin(radians(self.ang))) + (self.vPoint.Y * cos(radians(self.ang)))
                self.vPoint.pProject()

                self.gCan.coords(self.vA, self.wWC, self.wHC, self.vPoint.xP, self.vPoint.yP)

                self.gWin.update_idletasks()
                self.gWin.update()

                sleep(0.1)
        except TclError:
            pass

mMain = Main()

mMain.initWindow()
mMain.setAxis()
mMain.projAxis()
mMain.drawAxis()
mMain.prePaint()
mMain.paintCanvas()

感谢您的任何意见:)

编辑:对不起,我刚刚意识到我忘了提出我的问题。我只想知道为什么它向内吸引,而不仅仅是“正常”旋转?

【问题讨论】:

  • 您看到了浮点错误累积的影响,因为self.vPoint 是从它的旧值派生的,但使用了不精确的浮点数。您需要经常正交化矩阵以防止这些错误失控。
  • 啊,好的。所以我应该说,每次 theta 返回到 0 度时,将 X 和 Y 重置回它们的原始大小?
  • 或者,根本不根据旧向量计算向量。只有从旧值计算出的一两个角度,浮点误差的累积无关紧要。然后从这些角度重新计算所有向量。

标签: python rotation projection perspective


【解决方案1】:

这一段是错的:

self.ang += 1
if self.ang >= 361:
    self.ang = 0

self.vPoint.X = (self.vPoint.X * cos(radians(self.ang))
               - self.vPoint.Y * sin(radians(self.ang))) 
self.vPoint.Y = (self.vPoint.X * sin(radians(self.ang))
               + self.vPoint.Y * cos(radians(self.ang)))
self.vPoint.pProject()

有两个原因:

  1. self.ang 将采用开放范围 [0 - 360] 内的整数,这意味着角度 360 (== 0) 重复。
  2. 在每次迭代中,您将上一次迭代中的点旋转一定角度。结果,您的第一帧为 1 度,第二帧为 1+2 = 3,第三帧为 1 + 2 + 3... 您应该是:
    • 每次将上一次迭代中的点旋转一个恒定角度 (1°)。这受到我评论中提到的问题的影响
    • 每次将初始点旋转当前的旋转角度

【讨论】:

  • 选项 2.1(旋转初始点)是正确的。如果您的对象没有改变,但角度也发生了变化,则始终在“基线位置”对象上执行旋转,而不是增量旋转它(它会累积积分错误)。计算成本是一样的。
【解决方案2】:

实际上与您的问题无关,但我强烈建议您使用 Numpy 执行几何变换,特别是涉及 3D 点时。

下面,我贴一个示例sn-p,希望对你有帮助:

import numpy
from math import radians, cos, sin

## suppose you have a Nx3 cloudpoint (it might even be a single row of x,y,z coordinates)
cloudpoint = give_me_a_cloudpoint()

## this will be a rotation around Y azis:
yrot = radians(some_angle_in_degrees)

## let's create a rotation matrix using a numpy array
yrotmatrix = numpy.array([[cos(yrot), 0, -sin(yrot)],
                          [0,         1,          0],
                          [sin(yrot), 0,  cos(yrot)]], dtype=float)


## apply the rotation via dot multiplication
rotatedcloud = numpy.dot(yrotmatrix, pointcloud.T).T   # .T means transposition

【讨论】:

  • 如何有效地将其扩展到更大的点云?我想过简单地增加yrotmatrix 的尺寸,但我不确定如何将角值缩放到中间的零。
  • @DexterMorgan 我不确定我是否理解您的疑问。通常“放大”只会意味着你有一个更大的pointcloud(一个 Nx3 矩阵,N 更小),并且人们预计操作需要更长的时间,但我不相信它会花费更长的时间,除了非常大点云(例如,数百万个点)。另外,我不明白您所说的“将角值缩放到中间的零”是什么意思。无论如何,您永远不需要更大的yrotmatrix,因为它必须是一个 3x3 矩阵定义。
  • 我没有任何关于性能的意思,而是:如果点云是 10x10,旋转矩阵会是什么样子?
  • 嗯,这是一个有趣的问题。我的答案假设 3D 空间中的点云是一个 Nx3 矩阵,其中 N 是点数,3 是维度 (x,y,z)。当写一个 10x10 的点云时,这可能意味着两件事:一组 10 个点,每个点有 10 个维度(坐标),我认为这不太可能,或者一个 10x10 的 3D 点数组。在后一种情况下,您将使用 numpy.reshape 将其制成 100x3 数组。在前一种情况下,您需要一个 10x10 的旋转矩阵。但我想知道 10 维点空间的实际意义是什么。
  • 我有兴趣通过这种方法旋转一些图像,所以我有例如(100, 100, 3) RGB 阵列。重塑、旋转和回形是否有意义? ...关于你的最后一点:也许涉及弦理论的东西可以用于这么多维度:)
猜你喜欢
  • 2018-07-15
  • 1970-01-01
  • 2014-07-06
  • 1970-01-01
  • 2013-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多