Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[WIP] Reporting - Plotly #469

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Conversation

ryanhammonds
Copy link
Contributor

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

newplot

Anyone can directly push here as well. I'll likely be slow, so don't let me bottleneck things.

@dPys
Copy link
Owner

dPys commented Jan 6, 2021

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.

@dPys
Copy link
Owner

dPys commented Jan 6, 2021

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
Copy link

codecov bot commented Jan 6, 2021

Codecov Report

Merging #469 (57453d0) into master (f736491) will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           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.

@ryanhammonds
Copy link
Contributor Author

ryanhammonds commented Jan 8, 2021

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:
newplot

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.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants