Skip to content
This repository was archived by the owner on Nov 17, 2025. It is now read-only.

Commit 4543790

Browse files
authored
Merge pull request #33 from AllenCellModeling/julie_dev_variance_data
Julie dev variance data
2 parents 023ff91 + 41c523c commit 4543790

File tree

13 files changed

+956
-621
lines changed

13 files changed

+956
-621
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/local_staging
33
/data
44
/dask-worker-space
5+
/.dask_logs
56

67
# notebooks bcz eww (force add them if you must?)
78
*.ipynb

mti_nma/bin/all.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from datetime import datetime
1212
from pathlib import Path
1313

14+
import dask
1415
from dask_jobqueue import SLURMCluster
1516
from distributed import LocalCluster
1617
from prefect import Flow
@@ -33,22 +34,22 @@ def __init__(self):
3334
"""
3435
step_list = [
3536
[
36-
steps.Singlecell(step_name=f"single_{name}"),
37+
steps.LoadData(),
3738
steps.Shparam(step_name=f"shparam_{name}"),
3839
steps.Avgshape(step_name=f"avgshape_{name}"),
3940
steps.Nma(step_name=f"nma_{name}")
4041
]
41-
for name in ["nuc", "cell"]
42+
for name in ["nuc"]
4243
]
4344
self.step_list = [step for sublist in step_list for step in sublist]
4445
self.step_list.append(steps.CompareNucCell())
4546

4647
def run(
4748
self,
48-
distributed: bool = True,
49+
distributed: bool = False,
4950
clean: bool = False,
5051
debug: bool = False,
51-
structs: list = ["Nuc", "Cell"],
52+
structs: list = ["Nuc"],
5253
flow_viz: bool = False,
5354
**kwargs,
5455
):
@@ -83,7 +84,7 @@ def run(
8384

8485
# Initalize steps
8586
if "Nuc" in structs:
86-
single_nuc = steps.Singlecell(step_name="single_nuc")
87+
loaddata_nuc = steps.LoadData()
8788
shparam_nuc = steps.Shparam(step_name="shparam_nuc")
8889
avgshape_nuc = steps.Avgshape(step_name="avgshape_nuc")
8990
nma_nuc = steps.Nma(step_name="nma_nuc")
@@ -165,15 +166,15 @@ def run(
165166
if "Nuc" in structs:
166167
struct = "Nuc"
167168

168-
sc_nuc_df = single_nuc(
169+
ld_nuc_df = loaddata_nuc(
169170
distributed_executor_address=distributed_executor_address,
170171
clean=clean,
171172
debug=debug,
172173
struct=struct,
173174
**kwargs
174175
)
175176
sh_nuc_df = shparam_nuc(
176-
sc_df=sc_nuc_df,
177+
sc_df=ld_nuc_df,
177178
distributed_executor_address=distributed_executor_address,
178179
clean=clean,
179180
debug=debug,
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import imageio
2+
import os
3+
4+
# Steps to generate GIF of first nine modes
5+
# Edit filepaths in mti_nma/bin/view_modes.pvsm to match your own filepaths
6+
# In Paraview: File> Load State
7+
# Select view_modes.pvsm located in mti_nma/bin/
8+
# At Load State Data File Options "Use file names from state"
9+
# You should now see the 9-grid of modes
10+
# To save pngs of frames: File> Save Animation
11+
# Save in whatever directory you'd like, and use this as "pngdir" below
12+
# In the base mti_nma directory for this repo, run "python make_9grid_mode_video.png"
13+
# The gif should now be saved in your specified save_file_path below
14+
15+
16+
def create_gif_from_png_dir(
17+
basedir="../../local_staging/nma/nma_Nuc/nma_data/9grid_frames/",
18+
save_file_path="../../local_staging/nma/nma_Nuc/nma_data/9grid_videos/"):
19+
20+
# Create empty array
21+
for dirname in os.listdir(basedir):
22+
images = []
23+
pngdir = basedir + dirname
24+
for filename in os.listdir(pngdir):
25+
images.append(imageio.imread(pngdir + "/" + filename))
26+
imageio.mimsave(save_file_path + f"9grid_{dirname}_video.gif" , images)
27+
28+
29+
create_gif_from_png_dir()

mti_nma/bin/view_modes.pvsm

Lines changed: 585 additions & 585 deletions
Large diffs are not rendered by default.

mti_nma/steps/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
from .avgshape import Avgshape
66
from .nma import Nma
77
from .compare_nuc_cell import CompareNucCell
8+
from .load_data import LoadData
89

910

1011
__all__ = [
1112
"Singlecell",
1213
"Shparam",
1314
"Avgshape",
1415
"Nma",
15-
"CompareNucCell"]
16+
"CompareNucCell",
17+
"LoadData"]

mti_nma/steps/avgshape/avgshape.py

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
import numpy as np
66
import pandas as pd
7+
import os
78
from aicsshparam import shtools
89

910
from datastep import Step, log_run_params
1011
from .avgshape_utils import run_shcoeffs_analysis, save_mesh_as_stl
12+
from .avgshape_utils import get_smooth_and_coarse_mesh_from_voxelization
13+
from .avgshape_utils import save_mesh_as_obj, save_voxelization, save_displacement_map
1114

1215
###############################################################################
1316

@@ -50,13 +53,10 @@ def run(self, sh_df=None, struct="Nuc", **kwargs):
5053
# If no dataframe is passed in, load manifest from previous step
5154
if sh_df is None:
5255
sh_df = pd.read_csv(
53-
self.step_local_staging_dir.parent / "shparam_"
54-
f"{struct}" / "manifest.csv"
56+
self.step_local_staging_dir.parent / "shparam" /
57+
f"shparam_{struct}" / "manifest.csv", index_col="CellId"
5558
)
5659

57-
# Fix filepaths and use cell id as dataframe index
58-
sh_df = sh_df.set_index("CellId", drop=True)
59-
6060
# Load sh coefficients of all samples in manifest
6161
coeffs_df = pd.DataFrame([])
6262
for CellId in sh_df.index:
@@ -66,9 +66,20 @@ def run(self, sh_df=None, struct="Nuc", **kwargs):
6666
)
6767

6868
# Create directory to hold results from this step
69-
avg_data_dir = self.step_local_staging_dir / "avgshape_data"
69+
struct_dir = self.step_local_staging_dir / f"avgshape_{struct}"
70+
struct_dir.mkdir(parents=True, exist_ok=True)
71+
72+
avg_data_dir = struct_dir / f"avgshape_data"
7073
avg_data_dir.mkdir(parents=True, exist_ok=True)
7174

75+
# Move init and run parameters to structure dir to avoid overwriting
76+
for filetype in ["init", "run"]:
77+
filename = f"{filetype}_parameters.json"
78+
os.rename(
79+
self.step_local_staging_dir / filename,
80+
struct_dir / filename
81+
)
82+
7283
# Perform some per-cell analysis
7384
run_shcoeffs_analysis(df=coeffs_df, savedir=avg_data_dir, struct=struct)
7485

@@ -82,18 +93,42 @@ def run(self, sh_df=None, struct="Nuc", **kwargs):
8293
coeffs_avg = coeffs_avg.reshape(-2, lmax, lmax)
8394

8495
# Here we use the new meshing implementation for a more evenly distributed mesh
96+
'''
8597
mesh_avg, _ = shtools.get_even_reconstruction_from_coeffs(
8698
coeffs=coeffs_avg,
8799
npoints=1024
88100
)
101+
'''
102+
103+
mesh_avg, grid_avg = shtools.get_reconstruction_from_coeffs(
104+
coeffs=coeffs_avg,
105+
)
89106

90107
shtools.save_polydata(
91108
mesh=mesh_avg,
92109
filename=str(avg_data_dir / f"avgshape_{struct}.ply")
93110
)
94111

112+
# Save mesh as obj
113+
save_mesh_as_obj(mesh_avg, avg_data_dir / f"avgshape_{struct}.obj")
114+
115+
# Save displacement map
116+
save_displacement_map(grid_avg, avg_data_dir / f"avgshape_dmap_{struct}.tif")
117+
118+
# Save mesh as image
119+
domain = save_voxelization(mesh_avg, avg_data_dir / f"avgshape_{struct}.tif")
120+
95121
# Save mesh as stl file for blender import
96-
save_mesh_as_stl(mesh_avg, str(avg_data_dir / f"avgshape_{struct}.stl"))
122+
save_mesh_as_stl(mesh_avg, avg_data_dir / f"avgshape_{struct}.stl")
123+
124+
# Remesh voxelization
125+
remesh_avg = get_smooth_and_coarse_mesh_from_voxelization(domain, sigma=3, npoints=2000)
126+
127+
# Save remesh as PLY
128+
shtools.save_polydata(
129+
mesh=remesh_avg,
130+
filename=str(avg_data_dir / f"avgshape_remesh_{struct}.ply")
131+
)
97132

98133
# Save avg coeffs to csv file
99134
coeffs_df_avg.to_csv(
@@ -104,12 +139,16 @@ def run(self, sh_df=None, struct="Nuc", **kwargs):
104139
self.manifest = pd.DataFrame({
105140
"Label": "Average_mesh",
106141
"AvgShapeFilePath": avg_data_dir / f"avgshape_{struct}.ply",
142+
"AvgShapeRemeshFilePath": avg_data_dir / f"avgshape_remesh_{struct}.ply",
107143
"AvgShapeFilePathStl": avg_data_dir / f"avgshape_{struct}.stl",
144+
"AvgShapeFilePathObj": avg_data_dir / f"avgshape_{struct}.obj",
145+
"AvgShapeFilePathTif": avg_data_dir / f"avgshape_{struct}.tif",
146+
"AvgShapeDMapFilePathTif": avg_data_dir / f"avgshape_dmap_{struct}.tif",
108147
"Structure": struct,
109148
}, index=[0])
110149

111150
# Save manifest as csv
112151
self.manifest.to_csv(
113-
self.step_local_staging_dir / Path(f"manifest.csv"), index=False
152+
struct_dir / Path(f"manifest.csv"), index=False
114153
)
115154
return self.manifest

mti_nma/steps/avgshape/avgshape_utils.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import logging
22
from pathlib import Path
33

4+
import vtk
45
import matplotlib.pyplot as plt
56
import numpy as np
67
from stl import mesh
8+
from aicscytoparam import cytoparam
9+
from aicsimageio import writers
10+
from skimage import filters as skfilters
11+
from vtk.util import numpy_support as vtknp
12+
import pyvista as pv
13+
import pyacvd
714

815
###############################################################################
916

@@ -131,3 +138,89 @@ def save_mesh_as_stl(polydata, fname):
131138

132139
# Write the mesh to file"
133140
nuc_mesh.save(fname)
141+
142+
143+
def save_mesh_as_obj(polydata, fname):
144+
145+
writer = vtk.vtkOBJWriter()
146+
writer.SetInputData(polydata)
147+
writer.SetFileName(str(fname))
148+
writer.Write()
149+
150+
151+
def save_voxelization(polydata, fname):
152+
153+
domain, _ = cytoparam.voxelize_meshes([polydata])
154+
with writers.ome_tiff_writer.OmeTiffWriter(fname, overwrite_file=True) as writer:
155+
writer.save(
156+
255 * domain,
157+
dimension_order='ZYX',
158+
image_name=fname.stem
159+
)
160+
return domain
161+
162+
163+
def save_displacement_map(grid, fname):
164+
165+
grid = grid.reshape(1, *grid.shape).astype(np.float32)
166+
167+
with writers.ome_tiff_writer.OmeTiffWriter(fname, overwrite_file=True) as writer:
168+
writer.save(
169+
grid,
170+
dimension_order='ZYX',
171+
image_name=fname.stem
172+
)
173+
174+
175+
def get_smooth_and_coarse_mesh_from_voxelization(img, sigma, npoints):
176+
"""
177+
Converts an image into a triangle mesh with even distributed points.
178+
First we use a Gaussian kernel with size (sigma**3) to smooth the
179+
input image. Next we apply marching cubes (vtkContourFilter) to obtain
180+
a first mesh, which is used as input to a Voronoi-based clustering
181+
that is responsible for remeshing. Details can be found here:
182+
https://github.com/pyvista/pyacvd
183+
184+
Parameters
185+
----------
186+
img: np.array
187+
Input image corresponding to the voxelized version of the original
188+
average mesh.
189+
sigma: float
190+
Gaussian kernel size.
191+
npoints: int
192+
Number of points used to create the Voronoi clustering. The larger
193+
this value the more points the final mesh will have.
194+
Returns
195+
-------
196+
remesh_vtk: vtkPolyData
197+
Triangle with even distirbuted points.
198+
"""
199+
200+
201+
rad = 5
202+
img = np.pad(img, ((rad, rad), (rad, rad), (rad, rad)))
203+
d, h, w = img.shape
204+
img = skfilters.gaussian(img > 0, sigma=sigma, preserve_range=True)
205+
imagedata = vtk.vtkImageData()
206+
imagedata.SetDimensions([w, h, d])
207+
imagedata.SetExtent(0, w - 1, 0, h - 1, 0, d - 1)
208+
imagedata.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1)
209+
values = (255 * img).ravel().astype(np.uint8)
210+
values = vtknp.numpy_to_vtk(values, deep=True, array_type=vtk.VTK_UNSIGNED_CHAR)
211+
imagedata.GetPointData().SetScalars(values)
212+
cf = vtk.vtkContourFilter()
213+
cf.SetInputData(imagedata)
214+
cf.SetValue(0, 255.0 / np.exp(1.0))
215+
cf.Update()
216+
mesh = cf.GetOutput()
217+
218+
pv_temp = pv.PolyData(mesh)
219+
cluster = pyacvd.Clustering(pv_temp)
220+
cluster.cluster(npoints)
221+
remesh = cluster.create_mesh()
222+
remesh_vtk = vtk.vtkPolyData()
223+
remesh_vtk.SetPoints(remesh.GetPoints())
224+
remesh_vtk.SetVerts(remesh.GetVerts())
225+
remesh_vtk.SetPolys(remesh.GetPolys())
226+
return remesh_vtk
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from .load_data import LoadData # noqa: F401
4+
5+
__all__ = ["LoadData"]

0 commit comments

Comments
 (0)