Skip to content
New issue

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

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

Already on GitHub? Sign in 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.

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

Successfully merging this pull request may close these issues.

None yet

2 participants