PyNets
PyNets copied to clipboard
[WIP] Reporting - Plotly
There is still a lot to-do on this, but it's a start. This uses plotly to create figures, which can then easily be embedded into html reports using to to_html
method.
from pynets.reports.plotting import plot_t1w
# Load the data from register_node
cwd = os.getcwd()
reg_dir = os.path.join(cwd, 'data/register_node')
t1w_path = os.path.join(reg_dir, 'sub-OAS31172_ses-d0407_run-01_T1w_reor-RAS_res-2mm_tmp.nii.gz')
# Plot
fig = plot_t1w(t1w_path)
fig.show()
# Create an html page
html = fig.to_html()
with open('test.html', 'w') as f:
f.write(html)
This produces simple axial slice plots similar to fmriprep. Plotly is super cool and can also do things like animations - which could be useful for checking registration - with a relatively small amount of code.
To-Do List:
- Create general report templates to drop figures into
- Extend plotting funcs to fMRI, dMRI, segmentation overlays, graphs, etc
- Testing
Anyone can directly push here as well. I'll likely be slow, so don't let me bottleneck things.
Yeah I really like this and I think it would work nicely as an interface in core/interfaces because nearly all of the images to be plotted live in the workflow metadata.
So with wm + csf segmentation overlays, it could be something like:
def plot_t1w_with_segs(t1w, wm, csf):
"""Plot t1w images using plotly.
Parameters
----------
t1w : str
Path to a t1w image.
wm : str
Path to wm image.
csf : str
Path to csf image.
Returns
-------
fig : plotly.graph_objs.Figure
A plotly figure that is ready-to-embed using the to_html method.
"""
# Load data from file
t1w_arr = nib.load(t1w).get_fdata()
wm_arr = nib.load(wm).get_fdata()
csf_arr = nib.load(csf).get_fdata()
# Space out z-slices
z_max_t1w = np.shape(t1w_arr)[2]
pad_t1w = 30
z_slices_t1w = np.linspace(pad_t1w, z_max_t1w-pad_t1w, num=21, dtype=int)
z_max_wm = np.shape(wm_arr)[2]
pad_wm = 30
z_slices_wm = np.linspace(pad_wm, z_max_wm-pad_wm, num=21, dtype=int)
z_max_csf = np.shape(csf_arr)[2]
pad_csf = 30
z_slices_csf = np.linspace(pad_csf, z_max_csf-pad_csf, num=21, dtype=int)
# Init figure
nrows = 3
ncols = 7
fig = make_subplots(nrows, ncols, vertical_spacing=0.005, horizontal_spacing=0.005)
# Get subplot positions
fig_idxs = [(row, col) for row in range(1, nrows+1)
for col in range(1, ncols+1)]
for idx, z_slice_tup in enumerate([z_slices_t1w, z_slices_wm, z_slices_csf]):
# Get subplot coords
x_coord, y_coord = fig_idxs[idx]
# Slice and rotate the t1w array
fig.add_trace(go.Heatmap(z=np.rot90(t1w_arr[:, :, z_slice_tup[0]], k=3), showscale=False, colorscale="gray"),
x_coord, y_coord)
fig.add_trace(go.Heatmap(z=np.rot90(wm_arr[:, :, z_slice_tup[1]], k=3), showscale=False, colorscale="greys"),
x_coord, y_coord, opacity=0.5)
fig.add_trace(go.Heatmap(z=np.rot90(csf_arr[:, :, z_slice_tup[2]], k=3), showscale=False, colorscale="ice"),
x_coord, y_coord, opacity=0.5)
# Update axes
fig.update_xaxes(showticklabels=False, row=x_coord, col=y_coord)
fig.update_yaxes(showticklabels=False, row=x_coord, col=y_coord)
fig.update_layout(width=800, height=500)
return fig
For whatever reason though, the .html snapshot it returns (with both the original and my modified version of the function) looks way different than yours?
Codecov Report
Merging #469 (57453d0) into master (f736491) will not change coverage. The diff coverage is
n/a
.
@@ Coverage Diff @@
## master #469 +/- ##
=======================================
Coverage 71.74% 71.74%
=======================================
Files 12 12
Lines 5270 5270
=======================================
Hits 3781 3781
Misses 1489 1489
Continue to review full report at Codecov.
Legend - Click here to learn more
Δ = absolute <relative> (impact)
,ø = not affected
,? = missing data
Powered by Codecov. Last update f736491...e7d579f. Read the comment docs.
I pushed an update fixing the plot_t1w_with_segs
. It looks like we needed a nested loop to accomplish this. I ended splitting out a sub function (_add_overlay
) that has the slice loop, allowing it to be called for each seg. Adding csf from t1w_csf.nii.gz
looks a bit messy. Using just t1w and wm, I got this plot:
It would be cool if the layers could be toggled with a button. The funcs would need to be reworked for this. I'll have to read more on the plotly docs.