Czy moja perspektywa matematyki jest poprawna?

24

Mam zadanie domowe, w którym muszę obliczyć i wykreślić niektóre punkty za pomocą transformacji pespective, ale nie jestem pewien, czy moje wyniki są poprawne, ponieważ wykres 3d z wykorzystaniem współrzędnych kamery wygląda bardzo różnie od wykresu 2d przy użyciu współrzędnych obrazu . Czy możesz mi pomóc zrozumieć, co jest nie tak?

Oto, co podano: Kamera znajduje się w punkcie , określonym we współrzędnych świata (w metrach). Układ współrzędnych kamery jest obracany wokół osi Y światowego odniesienia o , więc jego macierz obrotu toWTC=[1,1,5]Tw R c = [ c o s ( θ ) 0 s i n ( θ ) 0 1 0 - s i n ( θ ) 0 c o s ( θ ) ]θ=160owRc=[cos(θ)0sin(θ)010sin(θ)0cos(θ)]

Parametry aparatu to: , , ,s x = s y = 0,01 m m / p x o x = 320 p x o y = 240 p xf=16mmsx=sy=0.01mm/pxox=320pxoy=240px

Przykładowe punkty (we współrzędnych świata):

WP1=[1,1,0.5]T

WP2=[1,1.5,0.5]T

WP3=[1.5,1.5,0.5]T

WP4=[1.5,1,0.5]T

Muszę obliczyć i wykreślić punkty we współrzędnych kamery i we współrzędnych obrazu, więc napisałem następujący kod w Octave:

%camera intrinsic parameters
f = 16
Sx = 0.01
Sy = 0.01
Ox = 320
Oy = 240

%given points, in world coordinate
wP1 = transpose([1, 1, 0.5])
wP2 = transpose([1, 1.5, 0.5])
wP3 = transpose([1.5, 1.5, 0.5])
wP4 = transpose([1.5, 1, 0.5])

% camera translation matrix
wTc = transpose([-1, 1, 5])

% rotation angle converted to rad
theta = 160 / 180 * pi

%camera rotation matrix
wRc = transpose([cos(theta), 0, sin(theta); 0, 1, 0; -sin(theta), 0, cos(theta)])

%transform the points to homogeneous coordinates
wP1h = [wP1; 1]
wP2h = [wP2; 1]
wP3h = [wP3; 1]
wP4h = [wP4; 1]

%separate each line of the rotation matrix
R1 = transpose(wRc(1 , :))
R2 = transpose(wRc(2 , :))
R3 = transpose(wRc(3 , :))

%generate the extrinsic parameters matrix
Mext = [wRc, [-transpose(R1) * wTc; -transpose(R2) * wTc; -transpose(R3) * wTc]]

%intrinsic parameters matrix
Mint = [-f/Sx, 0, Ox; 0, -f/Sy, Oy; 0, 0, 1]

% calculate coordinates in camera coordinates
cP1 = wRc * (wP1 - wTc)
cP2 = wRc * (wP2 - wTc)
cP3 = wRc * (wP3 - wTc)
cP4 = wRc * (wP4 - wTc)

% put coordinates in a list for plotting

x = [cP1(1), cP2(1), cP3(1), cP4(1), cP1(1)]
y = [cP1(2), cP2(2), cP3(2), cP4(2), cP1(2)]
z = [cP1(3), cP2(3), cP3(3), cP4(3), cP1(3)]

%plot the points in 3D using camera coordinates
plot3(x, y, z, "o-r")

pause()

% calculate the points in image coordinates
iP1 = Mint * (Mext * wP1h)
iP2 = Mint * (Mext * wP2h)
iP3 = Mint * (Mext * wP3h)
iP4 = Mint * (Mext * wP4h)

%generate a list of points for plotting
x = [iP1(1) / iP1(3), iP2(1) / iP2(3), iP3(1) / iP3(3), iP4(1) / iP4(3), iP1(1) / iP1(3)]
y = [iP1(2) / iP1(3), iP2(2) / iP2(3), iP3(2) / iP3(3), iP4(2) / iP4(3), iP1(2) / iP1(3)]

plot(x, y, "o-r")

pause()

A oto wątki, które otrzymałem ze skryptu: spodziewałem się, że są nieco podobne, ale nie wyglądają tak.

Działka 3D

Rysuj we współrzędnych kamery

Działka 2D

Wykreśl współrzędne obrazu

Vitor
źródło
8
+1 za pokazanie, że pytania domowe mogą być pytaniami wysokiej jakości. :)
Martin Ender
2
Jak wskazano na meta, to pytanie zasługuje na dobrą odpowiedź. Sam go nie mam, ale cieszę się, że mogę przekazać moją reputację komuś, kto je posiada.
trichoplax
@trichoplax problem polega na tym, że zrobiono to w Matlabie.
joojaa
@joojaa ah good point. Jeśli w okresie nagrody nie pojawią się eksperci Matlab, rozważę nauczenie się Octave, aby sprawdzić, czy to wystarczająco blisko, aby znaleźć rozwiązanie.
trichoplax
1
Nie jest dla mnie jasne, co powinien oznaczać pierwszy obraz. Drugi jest z punktu widzenia kamery, a po oszacowaniu koperty wydaje mi się, że wygląda poprawnie.
Julien Guertault

Odpowiedzi:

8

Identyfikacja osi na obu figurach i dodanie położenia kamery do pierwszej figury pomoże ci zrozumieć, co się dzieje.

Można też pojedynczych zmiennych dla wszystkich punktów, tworząc matrycę 2D z wierszy jak każdego punktu i kolumn jako elementy , i . W ten sposób można obsługiwać projekcję za pomocą prostego mnożenia macierzy zamiast obsługi każdego wiersza osobno.y zxyz

W opisie problemu ciekawie byłoby poznać kierunek obrotu, a co ważniejsze, oryginalny kierunek kamery i jej wektor do góry. Domyślam się, że kamera jest obrócona o 160 ° w lewo, pierwotny kierunek kamery to a jej wektor w górę to . Jeśli którekolwiek z tych założeń jest błędne, reszta odpowiedzi będzie błędna.[ 0 , 1 , 0 ][0,0,1][0,1,0]

W kodzie nie ma śladu konwersji mm na m. Albo twoja ogniskowa powinna wynosić a lub współrzędne punktu należy pomnożyć przez .S x = S y = 0,0001 0,000010.016Sx=Sy=0.00010.00001

Zastanówmy się, gdzie powinny kończyć się punkty na twoim obrazie. Na przykład środek kamery bez obrotu wskazuje na linię . Ponieważ wszystkie punkty znajdują się na płaszczyźnie , możemy wykonać następującą analizę: jeśli skupimy się na osi , możemy zobaczyć, że , więc środek kamery skończy się nieco na lewo od punktów (ponieważ kamera ma ), więc środek skończy się na , co oznacza, że ​​punkty pojawią się w prawej części obrazu . Ponadto kamera ma te same współrzędne co dwa punkty i od czasuz = 0,5 x t a n ( 160 ° ) ( 5 - 0,5 ) = 1,64 ... x = - 1 0,64 y y[1,1,x]z=0.5xtan(160°)(50.5)=1.64...x=10.64yy współrzędne nie są zmieniane przez obrót, nadal powinny kończyć się na tych samych współrzędnych po transformacji, czyli w środkowym rzędzie obrazu.

Dobrym sposobem na sprawdzenie odpowiedzi jest użycie istniejącego modelera 3D, takiego jak Blender: Scena 3D w Blenderze bądź ostrożny z układem współrzędnych Blendera, na przykład domyślnym wektorem kamery [0, 0, -1]. Oto rendering: Renderuj w Blenderze Focal ustawiono na inną wartość, aby kula była bardziej widoczna. Widzimy więc, że dwa dolne punkty znajdują się w środkowym rzędzie obrazu, a punkty znajdują się nieco po prawej stronie obrazu.

Zaimplementowałem twoje zadanie domowe w Pythonie:

import numpy as np

from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import axes3d, Axes3D


# Parameters
f_mm = 0.016
f_px = f_mm / 0.00001
t_cam = np.array([[-1., 1., 5.]]).T
t_cam_homogeneous = np.vstack((t_cam, np.array([[0]])))
theta = 160. * np.pi / 180.
ox = 320
oy = 240
# Rotation and points are in homogeneous coordinates
rot_cam = np.array([[np.cos(theta), 0, np.sin(theta)],
                    [0, 1, 0],
                    [-np.sin(theta), 0, np.cos(theta)]])
points = np.array([[1, 1, 0.5, 1],
                   [1, 1.5, 0.5, 1],
                   [1.5, 1.5, 0.5, 1],
                   [1.5, 1, 0.5, 1]]).T

# Compute projection matrix using intrinsics and extrinsics
intrinsics = np.array([[f_px, 0, ox],
                       [0, f_px, oy],
                       [0, 0, 1]])
extrinsics = np.hstack((rot_cam, rot_cam.dot(-t_cam)))

rot_cam2 = np.identity(4); rot_cam2[:3,:3] = rot_cam
camera_coordinates = rot_cam2.dot(points - t_cam_homogeneous)
camera_coordinates = camera_coordinates[:3,:] / camera_coordinates[3,:]

# Perform the projection
projected_points = intrinsics.dot(camera_coordinates)
projected_points = projected_points[:2,:] / projected_points[2,:]
projected_points[0,:] = -projected_points[0,:] # Inverted x-axis because camera is pointing toward [0, 0, 1]

fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(points[0,:], points[1,:], points[2,:], label="Points")
ax.scatter(t_cam[0], t_cam[1], t_cam[2], c="red", label="Camera")
ax.set_xlabel("X axis"); ax.set_ylabel("Y axis"); ax.set_zlabel("Z axis")
plt.title("World coordinates")
plt.legend()
plt.savefig('world_coordinates.png', dpi=300, bbox_inches="tight")

fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(camera_coordinates[0,:], camera_coordinates[1,:], camera_coordinates[2,:], label="Points")
ax.scatter(0, 0, 0, c="red", label="Camera")
ax.set_xlabel("X axis"); ax.set_ylabel("Y axis"); ax.set_zlabel("Z axis")
plt.title("Camera coordinates")
plt.legend()
plt.savefig('camera_coordinates.png', dpi=300, bbox_inches="tight")

plt.figure()
plt.scatter(projected_points[0,:], projected_points[1,:])
plt.xlabel("X axis"); plt.ylabel("Y axis")
plt.title("Image coordinates")
plt.savefig('image_coordinates.png', dpi=300, bbox_inches="tight")

plt.show()

Dają mi te liczby: odpowiednio: współrzędne świata, współrzędne kamery, współrzędne kamery obrócone, aby lekko dopasować orientację kamery (zauważ, że tutaj wektor kamery idzie w kierunku punktu widzenia figury, nie „wchodzi” na figurę) i współrzędne obrazu.Współrzędne świata Współrzędne kamery Obrócono współrzędne kamery Współrzędne obrazu

Widzimy więc, że współrzędne pionowe dolnych punktów są poprawnie w środkowym rzędzie (240), a punkty znajdują się po prawej stronie obrazu (wartość pozioma> 320).

Uważam, że jeden błąd, który miałeś, polega na tym, że znalazłeś ujemne wartości X, więc zrekompensowałeś ogniskowe ( -f/Sxy) w macierzy wewnętrznej. Problem polega na tym, że przyjęliśmy, że kamera początkowo wskazywała na (w przeciwnym razie obrót o 160 ° nie wskazywałby na punkty). Jeśli spojrzycie na to w ten sposób, oś wzrasta, gdy idziemy w lewo , należy wziąć odwrotność tej osi.x[0,0,1]x

Oba nasze wyniki wydają się podobne do mnie, tyle że założyłeś wektor o górę dla kamery (w rzeczywistości obie osie były dublowane, ponieważ zanegowałeś oba ogniskowania) i wykonałeś obliczenia w mm zamiast w metrach.[0,1,0]

Soravux
źródło