Skip to content

Commit

Permalink
Create reformatted BrainSwipes figures for HBCD QC (PennLINC#1091)
Browse files Browse the repository at this point in the history
Co-authored-by: Barry Tikalsky <[email protected]>
  • Loading branch information
tsalo and BarryTik authored Mar 21, 2024
1 parent 5e18b16 commit e368aed
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ jobs:
paths:
- .coverage.pytests
- store_artifacts:
path: /src/xcp_d/.circleci/out/
path: /src/xcp_d/.circleci/out

merge_coverage:
<<: *dockersetup
Expand Down
75 changes: 75 additions & 0 deletions xcp_d/interfaces/execsummary.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,18 @@
from bs4 import BeautifulSoup
from jinja2 import Environment, FileSystemLoader
from markupsafe import Markup
from nipype.interfaces.base import (
BaseInterfaceInputSpec,
File,
InputMultiPath,
SimpleInterface,
TraitedSpec,
)
from PIL import Image
from pkg_resources import resource_filename as pkgrf

from xcp_d.utils.filemanip import fname_presuffix


class ExecutiveSummary(object):
"""A class to build an executive summary.
Expand Down Expand Up @@ -327,3 +337,68 @@ def include_file(name):
)

self.write_html(html, out_file)


class _FormatForBrainSwipesInputSpec(BaseInterfaceInputSpec):
in_files = InputMultiPath(
File(exists=True),
desc=(
"Figure files. Must be the derivative's filename, "
"not the file from the working directory."
),
)


class _FormatForBrainSwipesOutputSpec(TraitedSpec):
out_file = File(exists=True, desc="Reformatted png file.")


class FormatForBrainSwipes(SimpleInterface):
"""Reformat figure for Brain Swipes.
From https://github.com/DCAN-Labs/BrainSwipes/blob/cb2ce964bcae93c9a234e4421c07b88bcadf2908/\
tools/images/ingest_brainswipes_data.py#L113
Credit to @BarryTik.
"""

input_spec = _FormatForBrainSwipesInputSpec
output_spec = _FormatForBrainSwipesOutputSpec

def _run_interface(self, runtime):
input_files = self.inputs.in_files
assert len(input_files) == 9, "There must be 9 input files."
idx = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
widths, rows = [], []
for i_row in range(3):
row_idx = idx[i_row]
row_files = [input_files[j_col] for j_col in row_idx]
row = [np.asarray(Image.open(row_file)) for row_file in row_files]
row = np.concatenate(row, axis=1)
widths.append(row.shape[1])
rows.append(row)

max_width = np.max(widths)

for i_row, row in enumerate(rows):
width = widths[i_row]
if width < max_width:
pad = max_width - width
prepad = pad // 2
postpad = pad - prepad
rows[i_row] = np.pad(row, ((0, 0), (prepad, postpad), (0, 0)), mode="constant")

x = np.concatenate(rows, axis=0)
new_x = ((x - x.min()) * (1 / (x.max() - x.min()) * 255)).astype("uint8")
new_im = Image.fromarray(np.uint8(new_x))

output_file = fname_presuffix(
input_files[0],
newpath=runtime.cwd,
suffix="_reformatted.png",
use_ext=False,
)
# all images should have the a .png extension
new_im.save(output_file)
self._results["out_file"] = output_file

return runtime
15 changes: 14 additions & 1 deletion xcp_d/interfaces/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,8 @@ class _SlicesDirInputSpec(FSLCommandInputSpec):

class _SlicesDirOutputSpec(TraitedSpec):
out_dir = Directory(exists=True, desc="Output directory.")
out_files = OutputMultiPath(File(exists=True), desc="List of generated PNG files.")
out_files = OutputMultiPath(File(exists=True), desc="Concatenated PNG files.")
slicewise_files = OutputMultiPath(File(exists=True), desc="List of generated PNG files.")


class SlicesDir(FSLCommand):
Expand Down Expand Up @@ -775,6 +776,18 @@ def _list_outputs(self):
)
for f in self.inputs.in_files
]
temp_files = [
"grota.png",
"grotb.png",
"grotc.png",
"grotd.png",
"grote.png",
"grotf.png",
"grotg.png",
"groth.png",
"groti.png",
]
outputs["slicewise_files"] = [os.path.join(out_dir, f) for f in temp_files]
return outputs

def _gen_filename(self, name):
Expand Down
1 change: 1 addition & 0 deletions xcp_d/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,3 +479,4 @@ def _run_and_generate(
check_generated_files(out_dir, output_list_file)

check_affines(data_dir, out_dir, input_type=input_type)
LOGGER.warning(f"Test passed in {out_dir}")
31 changes: 25 additions & 6 deletions xcp_d/workflows/execsummary.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from pkg_resources import resource_filename as pkgrf

from xcp_d.interfaces.bids import DerivativesDataSink
from xcp_d.interfaces.execsummary import FormatForBrainSwipes
from xcp_d.interfaces.nilearn import BinaryMath, ResampleToImage
from xcp_d.interfaces.plotting import AnatomicalPlot, PNGAppend
from xcp_d.interfaces.workbench import ShowScene
Expand Down Expand Up @@ -761,14 +762,12 @@ def init_plot_overlay_wf(
name="plot_overlay_figure",
)

# fmt:off
workflow.connect([
(inputnode, plot_overlay_figure, [
("underlay_file", "in_files"),
("overlay_file", "outline_image"),
]),
])
# fmt:on
]) # fmt:skip

ds_overlay_figure = pe.Node(
DerivativesDataSink(
Expand All @@ -782,11 +781,31 @@ def init_plot_overlay_wf(
run_without_submitting=True,
)

# fmt:off
workflow.connect([
(inputnode, ds_overlay_figure, [("name_source", "source_file")]),
(plot_overlay_figure, ds_overlay_figure, [("out_files", "in_file")]),
])
# fmt:on
]) # fmt:skip

reformat_for_brain_swipes = pe.Node(FormatForBrainSwipes(), name="reformat_for_brain_swipes")
workflow.connect([
(plot_overlay_figure, reformat_for_brain_swipes, [("slicewise_files", "in_files")]),
]) # fmt:skip

ds_reformatted_figure = pe.Node(
DerivativesDataSink(
base_directory=output_dir,
dismiss_entities=["den"],
datatype="figures",
desc=f"{desc}BrainSwipes",
extension=".png",
),
name="ds_reformatted_figure",
run_without_submitting=True,
)

workflow.connect([
(inputnode, ds_reformatted_figure, [("name_source", "source_file")]),
(reformat_for_brain_swipes, ds_reformatted_figure, [("out_file", "in_file")]),
]) # fmt:skip

return workflow

0 comments on commit e368aed

Please sign in to comment.