• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

neurospin-deepinsight / brainprep / 23547419711

25 Mar 2026 02:52PM UTC coverage: 74.094% (-1.0%) from 75.06%
23547419711

push

github

AGrigis
brainprep: fix CI.

1 of 1 new or added line in 1 file covered. (100.0%)

358 existing lines in 17 files now uncovered.

1410 of 1903 relevant lines covered (74.09%)

0.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

40.82
/brainprep/interfaces/plotting.py
1
##########################################################################
2
# NSAp - Copyright (C) CEA, 2021 - 2025
3
# Distributed under the terms of the CeCILL-B license, as published by
4
# the CEA-CNRS-INRIA. Refer to the LICENSE file or to
5
# http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
6
# for details.
7
##########################################################################
8

9
"""
10
Plotting functions.
11
"""
12

13
import itertools
1✔
14
import warnings
1✔
15

16
import matplotlib.pyplot as plt
1✔
17
import nibabel
1✔
18
import numpy as np
1✔
19
import pandas as pd
1✔
20
import seaborn as sns
1✔
21

22
with warnings.catch_warnings():
1✔
23
    warnings.filterwarnings(
1✔
24
        "ignore",
25
        message=".*'agg' matplotlib backend.*",
26
        category=UserWarning
27
    )
28
    from nilearn import plotting
1✔
29

30
from ..decorators import (
1✔
31
    CoerceparamsHook,
32
    LogRuntimeHook,
33
    OutputdirHook,
34
    PythonWrapperHook,
35
    step,
36
)
37
from ..typing import (
1✔
38
    Directory,
39
    File,
40
)
41

42

43
@step(
1✔
44
    hooks=[
45
        CoerceparamsHook(),
46
        OutputdirHook(
47
            plotting=True
48
        ),
49
        LogRuntimeHook(
50
            bunched=False
51
        ),
52
        PythonWrapperHook(),
53
    ]
54
)
55
def plot_network(
1✔
56
        network_file: File,
57
        output_dir: Directory,
58
        entities: dict,
59
        dryrun: bool = False) -> tuple[File]:
60
    """
61
    Plot the given network using nilearn.
62

63
    The generated image will have the same name as the input network file.
64

65
    Parameters
66
    ----------
67
    network_file : File
68
        Path to a TSV file containing a square connectivity matrix.
69
        The first column and the first row are interpreted as labels and are
70
        used to annotate the plotted matrix.
71
    output_dir : Directory
72
        Directory where the image will be saved.
73
    entities : dict
74
        A dictionary of parsed BIDS entities including modality.
75
    dryrun : bool
76
        If True, skip actual computation and file writing. Default False.
77

78
    Returns
79
    -------
80
    network_image_file : File
81
        Path to the saved network image.
82
    """
83
    basename = network_file.stem
1✔
84
    network_image_file = output_dir / f"{basename}.png"
1✔
85

86
    if dryrun:
1✔
87
        return (network_image_file, )
1✔
88

UNCOV
89
    df = pd.read_csv(network_file, sep="\t", index_col=0)
×
UNCOV
90
    labels = df.index.tolist()
×
91

UNCOV
92
    display = plotting.plot_matrix(
×
93
        df.values,
94
        figure=(10, 8),
95
        labels=labels,
96
        reorder=True,
97
    )
UNCOV
98
    display.figure.savefig(network_image_file)
×
99

UNCOV
100
    return (network_image_file, )
×
101

102

103
@step(
1✔
104
    hooks=[
105
        CoerceparamsHook(),
106
        OutputdirHook(
107
            plotting=True
108
        ),
109
        LogRuntimeHook(
110
            bunched=False
111
        ),
112
        PythonWrapperHook(),
113
    ]
114
)
115
def plot_defacing_mosaic(
1✔
116
        im_file: File,
117
        mask_file: File,
118
        output_dir: Directory,
119
        entities: dict,
120
        dryrun: bool = False) -> tuple[File]:
121
    """
122
    Generates a defacing mosaic image by overlaying a mask on an anatomical
123
    image.
124

125
    Parameters
126
    ----------
127
    im_file : File
128
        Path to the anatomical image.
129
    mask_file : File
130
        Path to the defacing mask.
131
    output_dir : Directory
132
        Directory where the mosaic image will be saved.
133
    entities : dict
134
        A dictionary of parsed BIDS entities including modality.
135
    dryrun : bool
136
        If True, skip actual computation and file writing. Default False.
137

138
    Returns
139
    -------
140
    mosaic_file : File
141
        Path to the saved mosaic image.
142
    """
143
    basename = "sub-{sub}_ses-{ses}_run-{run}_mod-T1w_deface".format(
1✔
144
        **entities)
145
    mosaic_file = output_dir / f"{basename}mosaic.png"
1✔
146

147
    if dryrun:
1✔
148
        return (mosaic_file, )
1✔
149

150
    plotting.plot_roi(
×
151
        mask_file,
152
        bg_img=im_file,
153
        display_mode="z",
154
        cut_coords=25,
155
        black_bg=True,
156
        alpha=0.6,
157
        colorbar=False,
158
        output_file=mosaic_file
159
    )
UNCOV
160
    arr = plt.imread(mosaic_file)
×
UNCOV
161
    cut = int(arr.shape[1] / 5)
×
UNCOV
162
    plt.figure()
×
UNCOV
163
    arr = np.concatenate(
×
164
        [arr[:, idx * cut: (idx + 1) * cut] for idx in range(5)], axis=0)
UNCOV
165
    plt.imshow(arr)
×
UNCOV
166
    plt.axis("off")
×
UNCOV
167
    plt.savefig(mosaic_file)
×
168

UNCOV
169
    return (mosaic_file, )
×
170

171

172
@step(
1✔
173
    hooks=[
174
        CoerceparamsHook(),
175
        OutputdirHook(
176
            plotting=True
177
        ),
178
        LogRuntimeHook(
179
            bunched=False
180
        ),
181
        PythonWrapperHook(),
182
    ]
183
)
184
def plot_histogram(
1✔
185
        table_file: File,
186
        col_name: str,
187
        output_dir: Directory,
188
        bar_coords: list[float] | None = None,
189
        dryrun: bool = False) -> tuple[File]:
190
    """
191
    Generates a histogram image with optional vertical bars.
192

193
    Parameters
194
    ----------
195
    table_file : File
196
        TSV table containing the data to be displayed.
197
    col_name : str
198
        Name of the column containing the histogram data.
199
    output_dir : Directory
200
        Directory where the image with the histogram will be saved.
201
    bar_coords: list[float] | None
202
        Coordianates of vertical lines to be displayed in red. Default None.
203
    dryrun : bool
204
        If True, skip actual computation and file writing. Default False.
205

206
    Returns
207
    -------
208
    histogram_file : File
209
        Generated image with the histogram.
210
    """
211
    histogram_file = output_dir / f"histogram_{col_name}.png"
1✔
212

213
    if dryrun:
1✔
214
        return (histogram_file, )
1✔
215

216
    data = pd.read_csv(
×
217
        table_file,
218
        sep="\t",
219
    )
UNCOV
220
    arr = data[col_name].astype(float)
×
221
    arr = arr[~np.isnan(arr)]
×
UNCOV
222
    arr = arr[~np.isinf(arr)]
×
223

UNCOV
224
    _, ax = plt.subplots()
×
UNCOV
225
    sns.histplot(
×
226
        arr,
227
        color="gray",
228
        alpha=0.6,
229
        ax=ax,
230
        kde=True,
231
        stat="density",
232
        label=col_name,
233
    )
UNCOV
234
    for x_coord in bar_coords or []:
×
UNCOV
235
        ax.axvline(x=x_coord, color="red")
×
UNCOV
236
    ax.spines["right"].set_visible(False)
×
UNCOV
237
    ax.spines["top"].set_visible(False)
×
UNCOV
238
    ax.legend()
×
239

UNCOV
240
    plt.savefig(histogram_file)
×
241

UNCOV
242
    return (histogram_file, )
×
243

244

245
@step(
1✔
246
    hooks=[
247
        CoerceparamsHook(),
248
        OutputdirHook(
249
            plotting=True
250
        ),
251
        LogRuntimeHook(
252
            bunched=False
253
        ),
254
        PythonWrapperHook(),
255
    ]
256
)
257
def plot_brainparc(
1✔
258
        wm_mask_file: File,
259
        gm_mask_file: File,
260
        csf_mask_file: File,
261
        brain_mask_file: File,
262
        output_dir: Directory,
263
        entities: dict,
264
        dryrun: bool = False) -> tuple[File]:
265
    """
266

267
    Parameters
268
    ----------
269
    wm_mask_file : File
270
        Binary mask of white matter regions.
271
    gm_mask_file : File
272
        Binary mask of gray matter regions.
273
    csf_mask_file : File
274
        Binary mask of cerebrospinal fluid regions.
275
    brain_mask_file : File
276
        Binary brain mask file.
277
    output_dir : Directory
278
        FreeSurfer working directory containing all the subjects.
279
    entities : dict
280
        A dictionary of parsed BIDS entities including modality.
281
    dryrun : bool
282
        If True, skip actual computation and file writing. Default False.
283

284
    Returns
285
    -------
286
    brainparc_image_file : File
287
        Image of the GM mask and GM, WM, CSF tissues histograms.
288
    """
289
    basename = "sub-{sub}_ses-{ses}_run-{run}_brainparc".format(
1✔
290
        **entities)
291
    brainparc_image_file = output_dir / f"{basename}.png"
1✔
292

293
    if dryrun:
1✔
294
        return (brainparc_image_file, )
1✔
295

UNCOV
296
    subject = f"run-{entities['run']}"
×
UNCOV
297
    anat_file = output_dir.parent / subject / "mri" / "norm.mgz"
×
298

UNCOV
299
    fig, axs = plt.subplots(2)
×
UNCOV
300
    plotting.plot_roi(
×
301
        roi_img=gm_mask_file,
302
        bg_img=anat_file,
303
        alpha=0.3,
304
        figure=fig,
305
        axes=axs[0],
306
    )
307

308
    anat_arr = nibabel.load(anat_file).get_fdata()
×
309
    mask_arr = nibabel.load(brain_mask_file).get_fdata()
×
UNCOV
310
    bins = np.histogram_bin_edges(
×
311
        anat_arr[mask_arr.astype(bool)],
312
        bins="auto",
313
    )
UNCOV
314
    palette = itertools.cycle(sns.color_palette("Set1"))
×
UNCOV
315
    for name, path in [("WM", wm_mask_file),
×
316
                       ("GM", gm_mask_file),
317
                       ("CSF", csf_mask_file)]:
UNCOV
318
        mask = nibabel.load(path).get_fdata()
×
UNCOV
319
        sns.histplot(
×
320
            anat_arr[mask.astype(bool)],
321
            bins=bins,
322
            color=next(palette),
323
            alpha=0.6,
324
            ax=axs[1],
325
            kde=True,
326
            stat="density",
327
            label=name,
328
        )
UNCOV
329
    axs[1].spines["right"].set_visible(False)
×
UNCOV
330
    axs[1].spines["top"].set_visible(False)
×
UNCOV
331
    axs[1].legend()
×
332

UNCOV
333
    plt.subplots_adjust(wspace=0, hspace=0, top=0.9, bottom=0.1)
×
UNCOV
334
    plt.savefig(brainparc_image_file)
×
335

UNCOV
336
    return (brainparc_image_file, )
×
337

338

339
@step(
1✔
340
    hooks=[
341
        CoerceparamsHook(),
342
        OutputdirHook(
343
            plotting=True
344
        ),
345
        LogRuntimeHook(
346
            bunched=False
347
        ),
348
        PythonWrapperHook(),
349
    ]
350
)
351
def plot_pca(
1✔
352
        pca_file: File,
353
        output_dir: Directory,
354
        dryrun: bool = False) -> tuple[File]:
355
    """
356
    Plot the two first PCA components.
357

358
    Parameters
359
    ----------
360
    pca_file : File
361
        TSV file containing PCA two first components as two columns named
362
        ``pc1`` and ``pc2``, as well as BIDS ``participant_id``, ``session``,
363
        and ``run``.
364
    output_dir : Directory
365
        Directory where the result image will be saved.
366
    dryrun : bool
367
        If True, skip actual computation and file writing. Default False.
368

369
    Returns
370
    -------
371
    pca_image_file : File
372
        Generated image with the two first PCA components.
373
    """
374
    pca_image_file = output_dir / f"pca.png"
1✔
375

376
    if dryrun:
1✔
377
        return (pca_image_file, )
1✔
378

UNCOV
379
    df = pd.read_csv(pca_file, sep="\t")
×
380

UNCOV
381
    fig, ax = plt.subplots(figsize=(20, 10))
×
UNCOV
382
    ax.scatter(df.pc1, df.pc2)
×
UNCOV
383
    for idx in range(len(df)):
×
UNCOV
384
        ax.annotate(
×
385
            f"{df.participant_id[idx]}-{df.session[idx]}-{df.run[idx]}",
386
            xy=(df.pc1[idx], df.pc2[idx]),
387
            xytext=(4, 4),
388
            textcoords="offset pixels"
389
        )
UNCOV
390
    plt.xlabel(f"PC1 (var={df.explained_variance_ratio_pc1[0]:.2f})")
×
UNCOV
391
    plt.ylabel(f"PC2 (var={df.explained_variance_ratio_pc2[1]:.2f})")
×
UNCOV
392
    plt.axis("equal")
×
UNCOV
393
    ax.spines["right"].set_visible(False)
×
UNCOV
394
    ax.spines["top"].set_visible(False)
×
UNCOV
395
    plt.tight_layout()
×
UNCOV
396
    plt.savefig(pca_image_file)
×
UNCOV
397
    plt.close(fig)
×
398

UNCOV
399
    return (pca_image_file, )
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc