aligning meshes with images
treder opened this issue · comments
I want to bring the meshes into image space but the results are still off a bit. As an example I will show images from m--20190828--1318--002645310--GHS/images/EXP_free_face
![meta_images2_only](https://private-user-images.githubusercontent.com/7069256/251184680-d18b2772-1deb-404d-bbfd-aee5f4ee0e3d.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjEzMTQ3ODQsIm5iZiI6MTcyMTMxNDQ4NCwicGF0aCI6Ii83MDY5MjU2LzI1MTE4NDY4MC1kMThiMjc3Mi0xZGViLTQwNGQtYmJmZC1hZWU1ZjRlZTBlM2QucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDcxOCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA3MThUMTQ1NDQ0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9MDFmZjRmMTE5NzllNTg1YzVlNzI3NGQxM2I5ZmExZGY2ZjVkZGQxNDU5ZDRlMTg0NDJkNmRkNGU5YWFhNzMzNiZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.OL2huB_OkJK47Bdbo2jlVAMG53z1O88meOEuVsdmA4M)
Now the following transformation are available:
- intrinsics matrix
K
and extrinsicsRT
in theKRT
file - mesh transform from the
[...]_transform.txt
files in thetracked_mesh
folder
The following figure shows the vertices
- first row: original vertices (only
x
andy
plotted,z
is omitted). The unit is in mm (as far as I understand) - second row: vertices after using the mesh transform in row 2. The mesh transform brings the meshes into an object-centric space and is not relevant so will be omitted for the rest.
![mesh12](https://private-user-images.githubusercontent.com/7069256/251187710-6a0dda87-c2cd-4cb7-8bcf-04bc31fd65aa.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjEzMTQ3ODQsIm5iZiI6MTcyMTMxNDQ4NCwicGF0aCI6Ii83MDY5MjU2LzI1MTE4NzcxMC02YTBkZGE4Ny1jMmNkLTRjYjctOGJjZi0wNGJjMzFmZDY1YWEucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDcxOCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA3MThUMTQ1NDQ0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9M2Q5ZmY5YTE4NmUzZmI4ZTA5YTE3MjcxOGYxMWY5NThmNDhmMzI1NTRjN2QwMjFlZWVkZjQ5YTk0NjQ0MjNmMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.qFLI8dxYwmsPsKpA3hHB8DSdtNu9rFl0stCfYvWj8wc)
Next figure shows the operations on vertices v
after applying intrinsics K
and extrinsics RT
. Note that the vertex coordinates are homogenous (by appending a column of 1's) and the matrices are extended/transposed accordingly
- row 1:
RT * v
. This brings the data into camera space, unit still mm. We can see that the head pose matches the input images. - row 2:
K * RT * v
(without correction by dividing by z-value) - row 3:
K * RT * v
then correction with z coordinate is performed (dividing x and y by z). Then the x and y coordinates are shifted byw//2
andh//2
. This seems to bring data into image space and align it.
![mesh345](https://private-user-images.githubusercontent.com/7069256/251188303-91a35cf7-98d8-4561-a00a-874f3fc6bc9e.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjEzMTQ3ODQsIm5iZiI6MTcyMTMxNDQ4NCwicGF0aCI6Ii83MDY5MjU2LzI1MTE4ODMwMy05MWEzNWNmNy05OGQ4LTQ1NjEtYTAwYS04NzRmM2ZjNmJjOWUucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDcxOCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA3MThUMTQ1NDQ0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9OWQ5MTc0ZTVlYjMyMDA0ZjZmNDFlYmI5NmU5ZTliNzM0Mzk2YmQxMmNkYmQyYjUwYjc4NzU4Y2Q0ZDY2ZTk1ZCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.9qzx6Ma6iZPvtzS11I0bPyASaJSjGjRi7sfhEbI8j4g)
Now we can take the meshes from the last row and superimpose them on the images to see the match. The match is noticeably off. Same for another example further down. So the following questions:
- Is the computation correct or am I missing something?
- Can you provide the correct formula?
- If you have a function that performs
[vertices_in_world_space, K, RT] -> [vertices_in_image_space]
do you mind sharing it?
Thanks!
![meta_images2](https://private-user-images.githubusercontent.com/7069256/251183790-c641e4b6-034e-47fa-ba03-b2c0ded00490.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjEzMTQ3ODQsIm5iZiI6MTcyMTMxNDQ4NCwicGF0aCI6Ii83MDY5MjU2LzI1MTE4Mzc5MC1jNjQxZTRiNi0wMzRlLTQ3ZmEtYmEwMy1iMmMwZGVkMDA0OTAucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDcxOCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA3MThUMTQ1NDQ0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NjNlOTVkOWE3NGMyYzhmYTY5NmIxMjViZmUzMTBjYTA1OWE0M2VlOGExYTdkZDRkOTA5NDAzZDU5OGZlYzIwMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.uNgfQgz5Bbh-Cwz2Nipy5cKhnbj6jJ60FvMKK0dYTMs)
![meta_mesh_and_images](https://private-user-images.githubusercontent.com/7069256/251182128-8dcbab6d-f51c-4422-8e2b-f8ea899c978c.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjEzMTQ3ODQsIm5iZiI6MTcyMTMxNDQ4NCwicGF0aCI6Ii83MDY5MjU2LzI1MTE4MjEyOC04ZGNiYWI2ZC1mNTFjLTQ0MjItOGUyYi1mOGVhODk5Yzk3OGMucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDcxOCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA3MThUMTQ1NDQ0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9ZDY4YjhjMzdiMGQxYzMxMzNmODI0ZmNjN2U4MTkwYzZiNzU2OTc4MTQzYjBkZjUwNGJkMWZhM2NiZDhjNTBiNSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.O935m-UQIKvwoHv-3VZX4ee7YUT_ZruYhkX3hSDh2Zw)
Hi, for your issue the problem might be in the shifting step. KRT already projects 3D points into 2D image space
you may refer to the code in data loader:
transf = np.genfromtxt(
"{}/{}/{}_transform.txt".format(self.meshpath, sentnum, frame)
)
R_f = transf[:3, :3]
t_f = transf[:3, 3]
campos = np.dot(R_f.T, self.campos[cam] - t_f).astype(np.float32)
view = campos / np.linalg.norm(campos)
extrin, intrin = self.krt[cam]["extrin"], self.krt[cam]["intrin"]
R_C = extrin[:3, :3]
t_C = extrin[:3, 3]
camrot = np.dot(R_C, R_f).astype(np.float32)
camt = np.dot(R_C, t_f) + t_C
camt = camt.astype(np.float32)
M = intrin @ np.hstack((camrot, camt[None].T))
and then use the matrix M
with the denormalized vertex coordinates in homogenous coordinates, e.g.,
projected = M @ vert_homo
projected_2d = projected / projected[:, -1]
This should give you 2d locations in the original image resolution (2048 x 1334) which looks like
Thanks for the quick reply. You are right, there must have been some error with the shift, your code works. I made a minimal reproducible example (for future reference) using your dataloader
import numpy as np
import matplotlib.pyplot as plt
from dataset import Dataset
# instantiate dataset and get sample
data_dir = 'PATH/TO/m--20190828--1318--002645310--GHS/'
multiface = Dataset(data_dir, krt_dir = data_dir + 'KRT', framelistpath = data_dir + 'frame_list_selected.txt')
sample = multiface[11]
where frame_list_selected.txt
is my selection of frames and the sampled frame is 003251
.
# get verts and denormalize
M = sample['M']
verts = sample['aligned_verts']
verts *= multiface.vertstd
verts += multiface.vertmean.reshape((-1, 3))
# homogenous coordinates and project
vert_homo = np.concatenate((verts, np.ones((verts.shape[0], 1))), axis=1)
projected = vert_homo @ M.T
projected_2d = projected / projected[:, -1:]
# plot
fig=plt.figure(figsize=(20,12), dpi= 100, facecolor='w', edgecolor='k')
plt.imshow(sample['photo'])
plt.plot(projected_2d[:,0], projected_2d[:,1], 'r.', markersize=1)
![image](https://private-user-images.githubusercontent.com/7069256/253903139-20a27915-6140-43e2-863e-aa75677d9bd3.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjEzMTQ3ODQsIm5iZiI6MTcyMTMxNDQ4NCwicGF0aCI6Ii83MDY5MjU2LzI1MzkwMzEzOS0yMGEyNzkxNS02MTQwLTQzZTItODYzZS1hYTc1Njc3ZDliZDMucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDcxOCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA3MThUMTQ1NDQ0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9ZGU2NTE3ZTQzMWY5ZDk2NjhmNmY1MjVhZjkyZGQ3MGNkNGEzNzE2Yjk4Y2Y4M2NjMjBkNWNiYjZiODM3YjBlOSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.UCIxjxEThsmXfp_kKi3GUcCy3qbA3W0d-UdvYaSRetE)