marcomusy / vedo

A python module for scientific analysis of 3D data based on VTK and Numpy
https://vedo.embl.es
MIT License
2.05k stars 266 forks source link

Touching objects handling in isosurface_discrete #1180

Closed snownontrace closed 3 months ago

snownontrace commented 3 months ago

Thanks for making this wonderful library! I noticed that the mesh generated using isosurface_discrete from a 3D segmented image does not cover the touching objects. As a result, surface area calculation of each labeled object is underestimated. Is there a way to add a feature to enable watertight mesh around each object?

marcomusy commented 3 months ago

Thanks! I have just updated the isosurface2.py example, which should provide the functionality you asked for.

pip install -U git+https://github.com/marcomusy/vedo.git

Screenshot from 2024-08-19 22-08-45

snownontrace commented 3 months ago

Thank you so much for your rapid response! I am puzzled by that in each pair of the touching object, one of them get a flat surface that seems to be from the cap method, while the other one gets an indented concaved surface. For example, between blobs 3 and 10, blob 10 got a flat cap, while blob 3 got a concaved surface. It would be more natural to think the boundary here should be assigned to both blobs. This seems to originate from isosurface_discrete, since the label assignment was already there when I check the mesh with wireframe style (blob 10 had an opening there).

Screenshot 2024-08-19 171100
snownontrace commented 3 months ago

When internal_boundaries is set True, the iso_discrete.celldata['BoundaryLabels'] does contain sufficient information for my purpose. Thank you so much!

snownontrace commented 3 months ago

This dev version seems to introduce some bugs in plotter:

plotter = Plotter(bg=video_bg, size=video_size, offscreen=True)
plotter.add(msh)
plotter.show()  # important to set the camera at the right position
plotter.zoom(initial_zoom)
plotter.camera.Azimuth(90)
video = Video(video_file_name, duration=video_duration, fps=video_fps)
video.action(elevation=(0, 0), azimuth=(-90, 270))
video.close()  # merge all the recorded frames and write to disk

Gives the following error:

  File "C:\Users\wangs6\AppData\Local\miniforge3\envs\vedo\Lib\site-packages\vedo\file_io.py", line 2026, in action
    plt.render()
  File "C:\Users\wangs6\AppData\Local\miniforge3\envs\vedo\Lib\site-packages\vedo\plotter.py", line 1125, in render
    if self._cocoa_process_events and self.interactor.GetInitialized():
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'GetInitialized'

Reverting the installation to stable release fixed the issue.

marcomusy commented 3 months ago

That is strange..! Try with this latest dev.

snownontrace commented 3 months ago

Hi Marco, thanks so much for the quick update! With the latest dev it no longer errors out, but when the objects rotate, the closest and furthest parts were cut out. In other words, the default depth of view was shallower. See below for a comparison with plotter behavior with vedo-2024.5.2 (left) or vedo-2024.5.2-dev09 (right):

Screenshot 2024-08-20 at 3 42 00 PM
marcomusy commented 3 months ago

Oh, what if you call

plotter.reset_clipping_range()
snownontrace commented 3 months ago

I tried adding it right before calling Video, but it remains clipped.

plotter = Plotter(bg=video_bg, size=video_size, offscreen=True)
plotter.add(msh)
plotter.show()  # important to set the camera at the right position
plotter.zoom(initial_zoom)
plotter.camera.Azimuth(90)
plotter.reset_clipping_range()
video = Video(video_file_name, duration=video_duration, fps=video_fps)
video.action(elevation=(0, 0), azimuth=(-90, 270))
video.close()  # merge all the recorded frames and write to disk
marcomusy commented 3 months ago

Thanks Shaohe for reporting, I just pushed a change to the screenshot() method I hope it fixes the issue.

snownontrace commented 3 months ago

Problem solved! Thanks again for fixing this so quickly.