Skip to content

Commit

Permalink
add sample data and reader plugin (#18)
Browse files Browse the repository at this point in the history
* add sample data plugin

* add demo CLI

* add comments

* add obj file reader plugin

* add higher level import

* add higher level import
  • Loading branch information
kevinyamauchi authored Mar 13, 2024
1 parent 69a4d75 commit 982d28c
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 37 deletions.
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ install_requires =
magicgui
mrcfile
numpy
pooch
qtpy
rich
scikit-image
Expand Down
67 changes: 53 additions & 14 deletions src/surforama/_cli.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
from typing import Optional

import mrcfile
import napari
import typer
from typing_extensions import Annotated

from surforama.app import QtSurforama, read_obj_file_and_compute_normals

app = typer.Typer(
help="Surforama: wiew tomogram densities on a surface.",
no_args_is_help=True,
Expand All @@ -21,21 +17,64 @@ def launch_surforama(
mesh_path: Annotated[
Optional[str], typer.Option(help="Path to the mesh to load.")
] = None,
demo: Annotated[
bool, typer.Option("--demo", help="launch surforama with sample data")
] = False,
):
if demo and (image_path is not None or mesh_path is not None):
raise ValueError(
"Please do not specify an image/mesh path when launching in demo mode."
)

# delay imports
import mrcfile
import napari

from surforama.app import QtSurforama
from surforama.io.mesh import read_obj_file

viewer = napari.Viewer(ndisplay=3)
if image_path is not None:
# load the image if the path was passed
image = mrcfile.read(image_path)
volume_layer = viewer.add_image(image)
else:
volume_layer = None

if mesh_path is not None:
# load the mesh if a path was passed
mesh_data = read_obj_file_and_compute_normals(mesh_path)
if demo:
# set up the viewer in demo mode
from surforama.data._datasets import thylakoid_membrane

# fetch the data
tomogram, mesh_data = thylakoid_membrane()

# Add the data to the viewer
volume_layer = viewer.add_image(
tomogram, blending="translucent", depiction="plane"
)
surface_layer = viewer.add_surface(mesh_data)

# set up the slicing plane position
volume_layer.plane = {"normal": [1, 0, 0], "position": [66, 187, 195]}

# set up the camera
viewer.camera.center = (64.0, 124.0, 127.5)
viewer.camera.zoom = 3.87
viewer.camera.angles = (
-5.401480002668876,
-0.16832643131442776,
160.28901483338126,
)

else:
surface_layer = None
# set up the viewer with the user-requested images
if image_path is not None:
# load the image if the path was passed
image = mrcfile.read(image_path)
volume_layer = viewer.add_image(image)
else:
volume_layer = None

if mesh_path is not None:
# load the mesh if a path was passed
mesh_data = read_obj_file(mesh_path)
surface_layer = viewer.add_surface(mesh_data)
else:
surface_layer = None

# Instantiate the widget and add it to Napari
surforama_widget = QtSurforama(viewer, surface_layer, volume_layer)
Expand Down
25 changes: 2 additions & 23 deletions src/surforama/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,7 @@
QWidget,
)


def read_obj_file_and_compute_normals(file_path, scale_factor=1):
mesh = trimesh.load(file_path, file_type="obj", process=True)

# Subdivide
# verts, faces = trimesh.remesh.subdivide_to_size(
# mesh.vertices, mesh.faces, 1
# )

# Subdivide can introduce holes
# mesh = trimesh.Trimesh(vertices=verts, faces=faces)
# trimesh.repair.fill_holes(mesh)

verts = mesh.vertices
faces = mesh.faces

verts = verts[:, [2, 1, 0]]

values = np.ones((len(verts),))

return verts, faces, values

from surforama.io.mesh import read_obj_file

# column names for the starfile
STAR_X_COLUMN_NAME = "rlnCoordinateX"
Expand Down Expand Up @@ -401,7 +380,7 @@ def _write_star_file(self, output_path: Path):
mrc = mrcfile.open(tomo_path, permissive=True)
tomo_mrc = np.array(mrc.data)

vertices, faces, values = read_obj_file_and_compute_normals(obj_path)
vertices, faces, values = read_obj_file(obj_path)
surface = (vertices, faces, values)

viewer = napari.Viewer(ndisplay=3)
Expand Down
3 changes: 3 additions & 0 deletions src/surforama/data/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from surforama.data._datasets import thylakoid_membrane

__all__ = ("thylakoid_membrane",)
56 changes: 56 additions & 0 deletions src/surforama/data/_datasets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from typing import List, Tuple

import mrcfile
import numpy as np
import pooch
from napari.types import LayerDataTuple

from surforama.io.mesh import read_obj_file

_data_registry = pooch.create(
path=pooch.os_cache("mypackage"),
base_url="doi:10.5281/zenodo.10814409",
registry={
"S1_M3b_StII_grow2_1_mesh_data.mrc": "md5:a6e34bbf4edc767aa6c2c854c81c9c97",
"S1_M3b_StII_grow2_1_mesh_data.obj": "md5:63b7d681204d7d3a3937154a0f4d7fc1",
"S1_M3b_StII_grow2_1_mesh_data_seg.mrc": "md5:d88460eb3bdf3164be6053d281fc45be",
"S1_M3c_StOI_grow2_1_mesh_data.mrc": "md5:296fbc48917c2baab7784a5ede6aae70",
"S1_M3c_StOI_grow2_1_mesh_data.obj": "md5:076e6e8a825f67a24e28beba09edcf70",
"S1_M3c_StOI_grow2_1_mesh_data_seg.mrc": "md5:878d4b3fc076dfc01e788cc08f9c9201",
},
)


def thylakoid_membrane() -> (
Tuple[np.ndarray, Tuple[np.ndarray, np.ndarray, np.ndarray]]
):
"""Fetch the thylakoid membrane sample data.
Data originally from Wietrzynski and Schaffer et al., eLife, 2020.
https://doi.org/10.7554/eLife.53740
"""
# get the tomogram
tomogram_path = _data_registry.fetch(
"S1_M3b_StII_grow2_1_mesh_data.mrc", progressbar=True
)
tomogram = mrcfile.read(tomogram_path)

# get the mesh
mesh_path = _data_registry.fetch(
"S1_M3b_StII_grow2_1_mesh_data.obj", progressbar=True
)
mesh_data = read_obj_file(mesh_path)

return tomogram, mesh_data


def _thylakoid_membrane_sample_data_plugin() -> List[LayerDataTuple]:
"""napari sample data plugin for thylakoid membrane dataset."""

# get the data
tomogram, mesh_data = thylakoid_membrane()

return [
(tomogram, {"name": "tomogram"}, "image"),
(mesh_data, {"name": "mesh"}, "surface"),
]
3 changes: 3 additions & 0 deletions src/surforama/io/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from surforama.io.mesh import read_obj_file

__all__ = ("read_obj_file",)
18 changes: 18 additions & 0 deletions src/surforama/io/_reader_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from typing import Callable, List

from napari.types import LayerDataTuple

from surforama.io import read_obj_file


def mesh_reader_plugin(path: str) -> Callable[[str], LayerDataTuple]:
if isinstance(path, str) and path.endswith(".obj"):
return obj_reader

# format not recognized
return None


def obj_reader(path: str) -> List[LayerDataTuple]:
mesh_data = read_obj_file(path)
return [(mesh_data, {}, "surface")]
24 changes: 24 additions & 0 deletions src/surforama/io/mesh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import numpy as np
import trimesh


def read_obj_file(file_path):
mesh = trimesh.load(file_path, file_type="obj", process=True)

# Subdivide
# verts, faces = trimesh.remesh.subdivide_to_size(
# mesh.vertices, mesh.faces, 1
# )

# Subdivide can introduce holes
# mesh = trimesh.Trimesh(vertices=verts, faces=faces)
# trimesh.repair.fill_holes(mesh)

verts = mesh.vertices
faces = mesh.faces

verts = verts[:, [2, 1, 0]]

values = np.ones((len(verts),))

return verts, faces, values
15 changes: 15 additions & 0 deletions src/surforama/napari.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@ contributions:
- id: surforama.make_widget
python_name: surforama:QtSurforama
title: Make Surforama
- id: surforama.thylakoid
python_name: surforama.data._datasets:_thylakoid_membrane_sample_data_plugin
title: Thylakoid membrane
- id: surforama.mesh_reader
python_name: surforama.io._reader_plugin:mesh_reader_plugin
title: Mesh reader
sample_data:
- command: surforama.thylakoid
key: thylakoid
display_name: thylakoid membrane
readers:
- command: surforama.mesh_reader
filename_patterns:
- '*.obj'
accepts_directories: false
widgets:
- command: surforama.make_widget
display_name: Surforama

0 comments on commit 982d28c

Please sign in to comment.