Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set camera perspective so that projected ellipses on image plane fit on ellipsoids #1202

Open
ttsesm opened this issue Nov 22, 2024 · 8 comments

Comments

@ttsesm
Copy link

ttsesm commented Nov 22, 2024

Hi @marcomusy,

I am trying to set the camera perspective so that the projected ellipses on image plane fit on ellipsoids that I am manually creating. For example I have the following set of ellipsoids and ellipses (projected on a random image plane):

Screenshot_20241122_103127
The image plane:
Screenshot_20241122_102806
Screenshot_20241122_102952

and now I want to set my camera perspective to a fixed position so that I can get the contours of the projected ellipses
to fit the silhouettes of the ellipsoids as you can see bellow (tried to set it manually with the mouse, without great success):

Screenshot_20241122_102906

I have the camera settings, check the code snippet bellow:

# pip install 'git+https://gitlab.inria.fr/tangram/pyellcv.git'
from ellcv.types import Ellipsoid, Ellipse
from ellcv.algo.cpp import solveP3P_ransac
from ellcv.visu import draw_ellipse, draw_bbox, sample_ellipse_points
import ellcv
from ellcv.utils import (
    generate_random_scene,
    generate_random_camera,
    generate_K,
    bbox_from_ellipse,
)

from vedo import Points, Plotter
import vedo as vd
import matplotlib
import numpy as np
import cv2

# matplotlib.use("Qt5Agg")
np.set_printoptions(suppress=True)

# Create a scene of ellipsoids
N = 10
scene = generate_random_scene(N, axes_range=[0.3, 1.5], position_range=[-13.0, 13.0])

# Define the camera
W, H = 640, 480
o, p = generate_random_camera(dist_range=[20.0, 30.0], target_range=[-0.001, 0.001])
c2w = np.eye(4)
c2w[:3, :3] = o  # .transpose()
c2w[:3, -1] = p
w2c = np.linalg.inv(c2w)

fx = 500.0 / float(max(W, H))
# K = generate_K(fx, 0.0, 0.0)
K = np.array([[500.0, 0.0, float(W) / 2], [0.0, 500.0, float(H) / 2], [0.0, 0.0, 1.0]])
print(K)
P = K @ w2c[:3, :]

pose = c2w
axes = []
rot = []
center = []
psoid_pcds = []
ellipse_pcds = []
ellipses = []
img = np.full((H, W, 3), 180, dtype=np.uint8)

for i, psoid in enumerate(scene):
    axes_, rot_, center_ = psoid.decompose()
    axes.append(axes_)
    rot.append(rot_)
    center.append(center_)
    ellipse = psoid.project(P)
    ellipses.append(ellipse)
    psoid_pcds.append(
        vd.Points(ellcv.visu.draw_3d.generate_ellipsoid_pointcloud(psoid)).c(i)
    )
    ellipse_pnts = sample_ellipse_points(ellipse)
    ellipse_pnts[:, 0] *= 1
    ellipse_pcds.append(
        vd.Points(
            np.insert(ellipse_pnts, ellipse_pnts.shape[1], 0.5, axis=1), c=i
        ).apply_transform(pose)
    )
    #     bbox = bbox_from_ellipse(ellipse)
    #     draw_bbox(img, bbox, color=(255, 0, 0))
    draw_ellipse(img, ellipse, color=ellipse_pcds[-1].color() * 255)

import matplotlib.pyplot as plt

plt.imshow(img)
plt.draw()
plt.pause(0.001)
# cv2.imshow("viz", img)
# v2.waitKey(0)

pic = vd.image.Image(np.flip(img, axis=0)).scale(2 / W).apply_transform(pose).alpha(0.9) # image needs to be flipped, due to opengl coordinate system?? also scaled down (not sure if scale ration is correct though)
vd.show(psoid_pcds, pic, axes=1, interactive=True).close()
@marcomusy
Copy link
Owner

hi @ttsesm
unfortunately i cannot run pip install 'git+https://gitlab.inria.fr/tangram/pyellcv.git'
as it fails compiling...
Maybe you just miss the orthogonal view? Try:
vd.settings.use_parallel_projection = True

@ttsesm
Copy link
Author

ttsesm commented Nov 22, 2024

@marcomusy what python version are you using? maybe you could try with v3.10 (I think I had similar issues installing the lib with a newer python version)

The vd.settings.use_parallel_projection = True will not work (and actually it didn't), since my camera is each time randomly set and in practice I need to be always behind the camera plane in order to be able to see the ellipsoids through the projected plane (I do not know if I explain it well).

@marcomusy
Copy link
Owner

..it still fails compiling..
anyways i'm not sure I get what you are trying to do ... maybe you ca use the vedo equivalent pca_ellipsoid()?

@ttsesm
Copy link
Author

ttsesm commented Nov 26, 2024

Hhhmmm... interesting...

How I can save the ellipsoids and ellipses points... 🤔
I am trying the following command vd.show(ellipsoid_pcds, ellipses_pcds, axes=1, interactive=True).export("scene.npz").close() but when I am loading it with plt = vd.load("scene.npz") I am getting the following error: [vedo.file_io:268] ERROR: in load(), cannot load scene.npz both ellipsoid_pcds and ellipses_pcds are lists of vd.Points() objects.

I've tried also with pickle, based on the recent support of the vtkmodules but this didn't work as well.

Also can I use somehow the pca_ellipsoid() and pca_ellipse() functions to create my ellipsoids and ellipses from my mean and covariance matrices?

@marcomusy
Copy link
Owner

thanks for the link! if you have many such ellipsoids you can merge and save them normally with

my_list_of_ellis
...
ellis = vedo.merge(my_list_of_ellis, flag=1)
ellis.write("merged_ellis.vtk")

another option is to save an Assembly, see examples/basic/slider_browser.py

@ttsesm
Copy link
Author

ttsesm commented Dec 2, 2024

The vedo.merge() is not a good solution since doesn't maintain the colors of the individual meshes/pcds which is importance in this case scenario.

The vedo.Assembly() seems to be fine, it saves the objects, but again when I am trying to load them (vedo.load("data_scene.npz")) I am getting the error I've pointed earlier above: [vedo.file_io:268] ERROR: in load(), cannot load data_scene.npz though the file seems to be valid.

I am saving the Assembly object with the following command: vd.show(vd.Assembly(ellipsoid_pcds, ellipses_pcds), axes=1, interactive=True).export("data_scene.npz").close() if I use vedo.write(vd.Assembly(ellipsoid_pcds, ellipses_pcds), "data_scene.npz") I get the error {AttributeError}AttributeError("'Assembly' object has no attribute 'dataset'").

marcomusy added a commit that referenced this issue Dec 2, 2024
@marcomusy
Copy link
Owner

I just pushed a fix.. try:

from vedo import *

# test write
ellis = []
for i in range(200):
    elli = Ellipsoid().pos(5*np.random.randn(3)).rotate_z(i)
    elli.c(i).alpha(0.1)
    elli.metadata['my_name'] = f'ellipsoid_{i}'
    ellis.append(elli)
asse = Assembly(ellis).shift([100,0,0])
asse.write("ellipses.npy")

# test read
asse2 = Assembly("ellipses.npy")
meshes = asse2.unpack()
meshes[0].alpha(1).print()
show(asse2, axes=1)

image

@ttsesm
Copy link
Author

ttsesm commented Dec 3, 2024

Thanks Marco, indeed now everything seems to work fine.
Please find the script bellow to load the scene.
I am also giving you the means and covariance matrices of both the 2D (mt, ct) and 3D (Ms, Cs) gaussians in case that there is a way to create the ellipses and ellipsoids respectively in vedo from such data (though checking on the pca_ellipsoid() and pca_ellipse() it doesn't seem so).

import vedo as vd
import numpy as np
import pickle

asse = vd.Assembly("gaussians.npy")
meshes = asse.unpack()
ellipsoids = meshes[:100]
ellipses = meshes[100:]

with open('data.pkl', 'rb') as inp:
    img = pickle.load(inp)
    pose = pickle.load(inp)
    mt = pickle.load(inp)
    ct = pickle.load(inp)
    Ms = pickle.load(inp)
    Cs = pickle.load(inp)

import matplotlib.pyplot as plt
# image plane where ellipsoids are projected as ellipses
plt.imshow(img)
plt.draw()
plt.pause(0.001)

W, H = 640, 480
K = np.array([[500.0, 0.0, float(W) / 2], [0.0, 500.0, float(H) / 2], [0.0, 0.0, 1.0]]) # camera intrinsics
pic = vd.image.Image(np.flip(img, axis=0)).scale(2 / max(W,H)).apply_transform(pose).alpha(0.9) # image needs to be flipped, due to opengl coordinate system?? also scaled down to be compatible with the NDC coordinates (not sure if scale ratio is correct though, but it should be fine)
vd.show(ellipsoids, ellipses, pic, axes=1, interactive=True).close()

So, now the initial question is how I can set the camera (if possible) position in 3D so that I look from behind and overlay the projected ellipses on the image plane to the ellipsoids 🤔

btw, the projected ellipsoids that are outside the plane are obviously not visible that's why you also have some ellipses being cut out fully or partially on the image plane since not all of them fit on the image.

data.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants