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

ENH: Refactor actor.odf_slicer for increased performances #374

Merged
merged 14 commits into from Feb 17, 2021

Conversation

CHrlS98
Copy link
Contributor

@CHrlS98 CHrlS98 commented Feb 10, 2021

Hi,
I did some refactoring of actor.odf_slicer with the objective to make it faster and less memory greedy.
Basically:

  • I replaced for loops by numpy array operations;
  • I added the option to supply ODFs in SH coefficients, when given a B matrix;
  • I added a method to change the sphere used for visualization when using SH coefficients;
  • I changed the numpy method called in utils.set_polydata_triangle for one that completes faster;
  • I applied the affine transform to the grid as well as to the ODFs themselves, as suggested here;
  • I moved the class OdfSlicerActor to the new module fury.actors.odf_slicer to make some space in fury.actor module;
  • I wrote tests.

If there is some ODF data available somewhere, I would gladly add an example too.

Below are some screenshots showing new functionalities.

Real-time ODF volume slicing
real-time-slicing

Changing sphere resolution during execution
update-sphere

Copy link
Contributor

@skoudoro skoudoro left a comment

Choose a reason for hiding this comment

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

Thank you for this nice update @CHrlS98.

We will look into this. For backward compatibility, we might need to keep the same order of all function parameters. I wonder why the order has been changed. We do not want to break some scripts so we will need to find a way to warn the users.

Concerning the move to a new module, we plan to do it for all actors as you can see in this issue: #64.

Concerning example, there is no ODF data available somewhere. Feel free to generate it, and create a PR in https://github.com/fury-gl/fury-data. All our example datas are in this place. Then, we create a specific fetcher.

What is the size of your volume in your demo gif?

Thank again for this good work

@CHrlS98
Copy link
Contributor Author

CHrlS98 commented Feb 11, 2021

Hi @skoudoro
The primary reason why I changed the order of arguments is because I feel like sphere should be mandatory, as it crashes when it is not supplied.

I will look into fury-data and add an ODF volume for examples.

The volume shown in the screenshots is a 111 x 105 x 138 order 8 fODF SH coefficients volume of 133.0 MB.

Thanks

@skoudoro
Copy link
Contributor

The primary reason why I changed the order of arguments is because I feel like sphere should be mandatory, as it crashes when it is not supplied.

I agree that the sphere should be defined. So maybe we can just provide a default value instead of None and keep the parameters in order

@CHrlS98
Copy link
Contributor Author

CHrlS98 commented Feb 11, 2021

I agree that the sphere should be defined. So maybe we can just provide a default value instead of None and keep the parameters in order

The thing is the method expects a DIPY Sphere, but DIPY is not a dependency of FURY. Therefore we can't set the default to some DIPY sphere. We would need some workaround to supply a sphere without requiring DIPY.

I'll try to think of something.

@skoudoro
Copy link
Contributor

skoudoro commented Feb 11, 2021

From DIPY SPHERE, you need only the vertices and faces. However, this is already present in FURY.

look at fury.primitive.prim_sphere, it will help.

@CHrlS98
Copy link
Contributor Author

CHrlS98 commented Feb 11, 2021

Nice, did not know that, adding a default will be easier than I thought.
I'll do it soon enough!

@CHrlS98
Copy link
Contributor Author

CHrlS98 commented Feb 12, 2021

Hi,
I switched the parameter order back to what it was and added a default sphere. I also made a PR here to make available an ODF volume I'll use for my example.

Copy link
Contributor

@skoudoro skoudoro left a comment

Choose a reason for hiding this comment

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

Hi @CHrlS98,

The code is really clean, Thank you for that. The tutorial is working well. I just have a small comment below but apart from that, I think it is ready to go.

After merging, I will tweet your gif to promote your nice work, I think it can be useful for many people and bring other contributors.

Thanks!

ps: We will fix the design of the combobox.

fury/utils.py Outdated
all_triangles = np.hstack(
np.c_[np.ones(len(triangles), dtype=req_dtype) * 3,
triangles.astype(req_dtype)])
all_triangles = np.insert(triangles, 0, 3, axis=1).flatten()
Copy link
Contributor

Choose a reason for hiding this comment

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

I would recommend using req_dtype just to be sure. We had some issues in the past.

np.insert(triangles, 0, 3, axis=1).astype(req_dtype).flatten()

Comment on lines +235 to +257
def _generate_color_for_vertices(self, sf):
"""
Get array of all vertices colors.
"""
if self.global_cm:
if self.colormap is None:
raise IOError("if global_cm=True, colormap must be defined.")
else:
all_colors = create_colormap(sf.ravel(), self.colormap) * 255
elif self.colormap is not None:
if isinstance(self.colormap, str):
# Map ODFs values [min, max] to [0, 1] for each ODF
range_sf = sf.max(axis=-1) - sf.min(axis=-1)
rescaled = sf - sf.min(axis=-1, keepdims=True)
rescaled[range_sf > 0] /= range_sf[range_sf > 0][..., None]
all_colors =\
create_colormap(rescaled.ravel(), self.colormap) * 255
else:
all_colors = np.tile(np.array(self.colormap).reshape(1, 3),
(sf.shape[0]*sf.shape[1], 1))
else:
all_colors = np.tile(np.abs(self.vertices)*255, (len(sf), 1))
return all_colors.astype(np.uint8)
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if this block can be an independent function. I feel like I saw something similar in our codebase. I just can not remember where....

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@skoudoro How about we move it to colormap.py so it is some colormap utility function for generating a color array given some values and a colormap?

Comment on lines +177 to +179
set_polydata_triangles(polydata, all_faces)
set_polydata_vertices(polydata, all_vertices)
set_polydata_colors(polydata, all_colors)
Copy link
Contributor

@skoudoro skoudoro Feb 16, 2021

Choose a reason for hiding this comment

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

there is a lot of deep copy here. I think this part can be easily optimized. There are some examples in the network modules.

For now, it is fine, but can you add a small note/TODO. It deserves its own PR

@CHrlS98
Copy link
Contributor Author

CHrlS98 commented Feb 16, 2021

@skoudoro Thanks for the nice comment,
I addressed your requests but didn't do anything about _generate_color_for_vertices yet. I think it could make sense to move it to colormap.py although it isn't clear to me what other methods could benefit from this.

On another note, I did a new GIF that I think you'll prefer. I removed the combo box and checkboxes and added transparency to the sagittal and axial slices. I also disabled the mouse capture.
better-slicing

@skoudoro
Copy link
Contributor

skoudoro commented Feb 17, 2021

Ok, I tested it and it is good to go.

Concerning _generate_color_for_vertices, I believe it was line or surface actors. I will create an issue for that. We have to review color and colormap in general. Sometimes the initialization is between [0-1] and sometimes [0-255]. There is a uniformization task to do.

Thank you for the updated gif. What is your Twitter handle?

Concerning checkboxes, you should look at dipy_horizon. we transform the X, Y, Z labels to flat buttons which avoid having checkboxes. The buttons hide/show the slices and sliders.

@skoudoro skoudoro merged commit 659f579 into fury-gl:master Feb 17, 2021
@CHrlS98
Copy link
Contributor Author

CHrlS98 commented Feb 17, 2021

@skoudoro Alrighty thanks,
I'll look into dipy_horizon for flat buttons 👍
My twitter: @CHrlS_98

See you around!

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

Successfully merging this pull request may close these issues.

None yet

2 participants