Skip to content

Speed up virtual fitting#374

Merged
ebrahimebrahim merged 5 commits into
mainfrom
vf-speedups
Sep 16, 2025
Merged

Speed up virtual fitting#374
ebrahimebrahim merged 5 commits into
mainfrom
vf-speedups

Conversation

@ebrahimebrahim

Copy link
Copy Markdown
Collaborator

This is work towards #367

The main change is to introduce embree CPU raytracing via embreex which is integrated with trimesh. Trimesh is already a dependency of openlifu so this came almost for free.

The raytracing speeds up both construction and use of the spherical interpolator. Here is some profiling:

previous timings

Conditions for all these:

  • Running virtual fit from SlicerOpenLIFU my ubuntu 22.04 machine.
  • I am using the example currently (as of 9420917) in the dvc data, specifically the session neuromod_2x_demo. This contains the VirtualFitOptions that were slow for Peter in Virtual Fit Slow #367
task time
skin interpolator construction 15s
pose search 7s

(About 8s of the 15s in skin interpolator construction are not spent tracing rays but rather just building the scipy LinearNDInterpolator, which uses the points to construct a triangulation.)

after just the thread cap introduced in OpenwaterHealth/SlicerOpenLIFU#474

task time
skin interpolator construction 15s
pose search 4s

It just shaved off a few seconds from the pose search.

after also including this PR

task time
both 1.5s

(and most of that 1.5s seems to be spent on Slicer updating model transforms, so the virtual fit algorithm part is quite fast. see gif here: OpenwaterHealth/SlicerOpenLIFU#474 (comment))

@ebrahimebrahim

Copy link
Copy Markdown
Collaborator Author

@sadhana-r This is most easily reviewed by reviewing the SlicerOpenLIFU companion PR: OpenwaterHealth/SlicerOpenLIFU#474

A cursory code review is sufficient, along with a test running this new virtual fit from SlicerOpenLIFU via the companion PR. I will add a couple of comments to provide some context

This greatly speeds up building the spherical interpolator for virtual
fitting, but only for intel architecture.
It's the same grid in the tangent space at each virtual fit search
point, so we don't need to rebuild it in every iteration.
import numpy as np
import skimage.filters
import skimage.measure
import trimesh

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trimesh is not a new dependency here -- we were already using it elsewhere

Comment on lines +332 to +337
# Pre-build plane fitting grid that will be used repeatedly in the search loop below.
# This grid lives in the spherical coordinate basis theta-phi plane at each point.
# Below we will project it onto the skin surface for each search point.
dtheta_sequence = np.arange(-planefit_dyaw_extent, planefit_dyaw_extent + planefit_dyaw_step, planefit_dyaw_step)
dphi_sequence = np.arange(-planefit_dpitch_extent, planefit_dpitch_extent + planefit_dpitch_step, planefit_dpitch_step)
dtheta_grid, dphi_grid = np.meshgrid(dtheta_sequence, dphi_sequence, indexing='ij')

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the construction of this point grid was previously inside the for i in range(num_search_points): just below, and I moved it out because it is the same grid each time.


return interpolator

def _spherical_interpolator_from_mesh_embree(surface_mesh : vtk.vtkPolyData) -> Callable:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function's implementation doesn't need to be reviewed closely because the unit test test_spherical_interpolator_from_mesh covers it well; we now run that test on both _spherical_interpolator_from_mesh_cell_locator (the old method) and _spherical_interpolator_from_mesh_embree (the new method)

@sadhana-r

sadhana-r commented Sep 16, 2025

Copy link
Copy Markdown
Contributor

I tested this out using the same example data. Before this PR, skin interpolator construction took 20s, and pose search took ~1min10s. With the changes in this PR, I don't experience any speed up to the skin interpolator (still 20s), but the pose search completes in ~6s.

Otherwise, the VF algorithm runs as expected.

@ebrahimebrahim

Copy link
Copy Markdown
Collaborator Author

Wow -- so that really sped up the pose search! The culprit was indeed the threading then! I didn't think that would actually work.

So the fix for that was not in this PR but rather in SlicerOpenLIFU where I limited it to a single thread.

Regarding the still-slow skin interpolator: I wonder if it skipped using embree for you because you do not have an x86_64 CPU. Is that right?

@sadhana-r

Copy link
Copy Markdown
Contributor

Regarding the still-slow skin interpolator: I wonder if it skipped using embree for you because you do not have an x86_64 CPU. Is that right?

I do have a x64-based CPU. I did some digging and you're right, it skipped using embree. It was not installing the embreex python dependency, and so trimesh.ray.has_embree was False. I tested out installing openlifu with platform_machine=='x86_64' removed from the pyproject.toml file. After doing that, the embreex dependency installs and the entire pipeline runs in like 2s!!

@ebrahimebrahim

Copy link
Copy Markdown
Collaborator Author

Oh huh... so the platform_machine=='x86_64' in the pyproject.toml is not working the way it should.

Can you try

import platform
print(platform.machine())

in a python console? Does it say 'x86_64' like it does for me, or does it say something like "amd64"?

The platform_machine on windows in not x86_64 but rather AMD64.
These are just different naming conventions for the same thing.
@ebrahimebrahim

Copy link
Copy Markdown
Collaborator Author

Yup that's what it was! In the CI's windows runners I see they now run the embree test after I added "AMD64" as a possible platform_machine; previously they were skipping that test due to embree not getting installed. I think this should be good now

@sadhana-r

Copy link
Copy Markdown
Contributor

Oh right, its amd64. This works!

@ebrahimebrahim

Copy link
Copy Markdown
Collaborator Author

x86_64 and amd64 are two names for the same thing; it just turns out that windows uses a different convention than linux when reporting on the architecture

@ebrahimebrahim ebrahimebrahim merged commit 2f65a38 into main Sep 16, 2025
9 checks passed
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

Successfully merging this pull request may close these issues.

2 participants