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

neurospin-deepinsight / brainprep / 22225420118

20 Feb 2026 01:10PM UTC coverage: 75.32% (-2.1%) from 77.455%
22225420118

push

github

AGrigis
brainprep: fix CI.

9 of 11 new or added lines in 2 files covered. (81.82%)

168 existing lines in 9 files now uncovered.

1471 of 1953 relevant lines covered (75.32%)

0.75 hits per line

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

49.57
/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 ..reporting import log_runtime
1✔
31
from ..typing import (
1✔
32
    Directory,
33
    File,
34
)
35
from ..utils import (
1✔
36
    coerceparams,
37
    outputdir,
38
)
39
from ..wrappers import pywrapper
1✔
40

41

42
@coerceparams
1✔
43
@outputdir(
1✔
44
    plotting=True)
45
@log_runtime(
1✔
46
    bunched=False)
47
@pywrapper
1✔
48
def plot_network(
1✔
49
        network_file: File,
50
        output_dir: Directory,
51
        entities: dict,
52
        dryrun: bool = False) -> tuple[File]:
53
    """
54
    Plot the given network using nilearn.
55

56
    The generated image will have the same name as the input network file.
57

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

71
    Returns
72
    -------
73
    network_image_file : File
74
        Path to the saved network image.
75
    """
76
    basename = network_file.stem
1✔
77
    network_image_file = output_dir / f"{basename}.png"
1✔
78

79
    if dryrun:
1✔
80
        return (network_image_file, )
1✔
81

UNCOV
82
    df = pd.read_csv(network_file, sep="\t", index_col=0)
×
83
    labels = df.index.tolist()
×
84

85
    display = plotting.plot_matrix(
×
86
        df.values,
87
        figure=(10, 8),
88
        labels=labels,
89
        reorder=True,
90
    )
UNCOV
91
    display.figure.savefig(network_image_file)
×
92

UNCOV
93
    return (network_image_file, )
×
94

95

96
@coerceparams
1✔
97
@outputdir(
1✔
98
    plotting=True)
99
@log_runtime(
1✔
100
    bunched=False)
101
@pywrapper
1✔
102
def plot_defacing_mosaic(
1✔
103
        im_file: File,
104
        mask_file: File,
105
        output_dir: Directory,
106
        entities: dict,
107
        dryrun: bool = False) -> tuple[File]:
108
    """
109
    Generates a defacing mosaic image by overlaying a mask on an anatomical
110
    image.
111

112
    Parameters
113
    ----------
114
    im_file : File
115
        Path to the anatomical image.
116
    mask_file : File
117
        Path to the defacing mask.
118
    output_dir : Directory
119
        Directory where the mosaic image will be saved.
120
    entities : dict
121
        A dictionary of parsed BIDS entities including modality.
122
    dryrun : bool
123
        If True, skip actual computation and file writing. Default False.
124

125
    Returns
126
    -------
127
    mosaic_file : File
128
        Path to the saved mosaic image.
129
    """
130
    basename = "sub-{sub}_ses-{ses}_run-{run}_mod-T1w_deface".format(
1✔
131
        **entities)
132
    mosaic_file = output_dir / f"{basename}mosaic.png"
1✔
133

134
    if dryrun:
1✔
135
        return (mosaic_file, )
1✔
136

137
    plotting.plot_roi(
×
138
        mask_file,
139
        bg_img=im_file,
140
        display_mode="z",
141
        cut_coords=25,
142
        black_bg=True,
143
        alpha=0.6,
144
        colorbar=False,
145
        output_file=mosaic_file
146
    )
UNCOV
147
    arr = plt.imread(mosaic_file)
×
UNCOV
148
    cut = int(arr.shape[1] / 5)
×
UNCOV
149
    plt.figure()
×
150
    arr = np.concatenate(
×
151
        [arr[:, idx * cut: (idx + 1) * cut] for idx in range(5)], axis=0)
152
    plt.imshow(arr)
×
153
    plt.axis("off")
×
154
    plt.savefig(mosaic_file)
×
155

156
    return (mosaic_file, )
×
157

158

159
@coerceparams
1✔
160
@outputdir(
1✔
161
    plotting=True)
162
@log_runtime(
1✔
163
    bunched=False)
164
@pywrapper
1✔
165
def plot_histogram(
1✔
166
        table_file: File,
167
        col_name: str,
168
        output_dir: Directory,
169
        bar_coords: list[float] | None = None,
170
        dryrun: bool = False) -> tuple[File]:
171
    """
172
    Generates a histogram image with optional vertical bars.
173

174
    Parameters
175
    ----------
176
    table_file : File
177
        TSV table containing the data to be displayed.
178
    col_name : str
179
        Name of the column containing the histogram data.
180
    output_dir : Directory
181
        Directory where the image with the histogram will be saved.
182
    bar_coords: list[float] | None
183
        Coordianates of vertical lines to be displayed in red. Default None.
184
    dryrun : bool
185
        If True, skip actual computation and file writing. Default False.
186

187
    Returns
188
    -------
189
    histogram_file : File
190
        Generated image with the histogram.
191
    """
192
    histogram_file = output_dir / f"histogram_{col_name}.png"
1✔
193

194
    if dryrun:
1✔
195
        return (histogram_file, )
1✔
196

UNCOV
197
    data = pd.read_csv(
×
198
        table_file,
199
        sep="\t",
200
    )
UNCOV
201
    arr = data[col_name].astype(float)
×
UNCOV
202
    arr = arr[~np.isnan(arr)]
×
UNCOV
203
    arr = arr[~np.isinf(arr)]
×
204

205
    _, ax = plt.subplots()
×
206
    sns.histplot(
×
207
        arr,
208
        color="gray",
209
        alpha=0.6,
210
        ax=ax,
211
        kde=True,
212
        stat="density",
213
        label=col_name,
214
    )
UNCOV
215
    for x_coord in bar_coords or []:
×
UNCOV
216
        ax.axvline(x=x_coord, color="red")
×
217
    ax.spines["right"].set_visible(False)
×
218
    ax.spines["top"].set_visible(False)
×
219
    ax.legend()
×
220

UNCOV
221
    plt.savefig(histogram_file)
×
222

223
    return (histogram_file, )
×
224

225

226
@coerceparams
1✔
227
@outputdir(
1✔
228
    plotting=True)
229
@log_runtime(
1✔
230
    bunched=False)
231
@pywrapper
1✔
232
def plot_brainparc(
1✔
233
        wm_mask_file: File,
234
        gm_mask_file: File,
235
        csf_mask_file: File,
236
        brain_mask_file: File,
237
        output_dir: Directory,
238
        entities: dict,
239
        dryrun: bool = False) -> tuple[File]:
240
    """
241

242
    Parameters
243
    ----------
244
    wm_mask_file : File
245
        Binary mask of white matter regions.
246
    gm_mask_file : File
247
        Binary mask of gray matter regions.
248
    csf_mask_file : File
249
        Binary mask of cerebrospinal fluid regions.
250
    brain_mask_file : File
251
        Binary brain mask file.
252
    output_dir : Directory
253
        FreeSurfer working directory containing all the subjects.
254
    entities : dict
255
        A dictionary of parsed BIDS entities including modality.
256
    dryrun : bool
257
        If True, skip actual computation and file writing. Default False.
258

259
    Returns
260
    -------
261
    brainparc_image_file : File
262
        Image of the GM mask and GM, WM, CSF tissues histograms.
263
    """
264
    basename = "sub-{sub}_ses-{ses}_run-{run}_brainparc".format(
1✔
265
        **entities)
266
    brainparc_image_file = output_dir / f"{basename}.png"
1✔
267

268
    if dryrun:
1✔
269
        return (brainparc_image_file, )
1✔
270

UNCOV
271
    subject = f"run-{entities['run']}"
×
UNCOV
272
    anat_file = output_dir.parent / subject / "mri" / "norm.mgz"
×
273

UNCOV
274
    fig, axs = plt.subplots(2)
×
UNCOV
275
    plotting.plot_roi(
×
276
        roi_img=gm_mask_file,
277
        bg_img=anat_file,
278
        alpha=0.3,
279
        figure=fig,
280
        axes=axs[0],
281
    )
282

283
    anat_arr = nibabel.load(anat_file).get_fdata()
×
284
    mask_arr = nibabel.load(brain_mask_file).get_fdata()
×
285
    bins = np.histogram_bin_edges(
×
286
        anat_arr[mask_arr.astype(bool)],
287
        bins="auto",
288
    )
UNCOV
289
    palette = itertools.cycle(sns.color_palette("Set1"))
×
UNCOV
290
    for name, path in [("WM", wm_mask_file),
×
291
                       ("GM", gm_mask_file),
292
                       ("CSF", csf_mask_file)]:
293
        mask = nibabel.load(path).get_fdata()
×
294
        sns.histplot(
×
295
            anat_arr[mask.astype(bool)],
296
            bins=bins,
297
            color=next(palette),
298
            alpha=0.6,
299
            ax=axs[1],
300
            kde=True,
301
            stat="density",
302
            label=name,
303
        )
UNCOV
304
    axs[1].spines["right"].set_visible(False)
×
UNCOV
305
    axs[1].spines["top"].set_visible(False)
×
UNCOV
306
    axs[1].legend()
×
307

UNCOV
308
    plt.subplots_adjust(wspace=0, hspace=0, top=0.9, bottom=0.1)
×
UNCOV
309
    plt.savefig(brainparc_image_file)
×
310

UNCOV
311
    return (brainparc_image_file, )
×
312

313

314
@coerceparams
1✔
315
@outputdir(
1✔
316
    plotting=True)
317
@log_runtime(
1✔
318
    bunched=False)
319
@pywrapper
1✔
320
def plot_pca(
1✔
321
        pca_file: File,
322
        output_dir: Directory,
323
        dryrun: bool = False) -> tuple[File]:
324
    """
325
    Plot the two first PCA components.
326

327
    Parameters
328
    ----------
329
    pca_file : File
330
        TSV file containing PCA two first components as two columns named
331
        ``pc1`` and ``pc2``, as well as BIDS ``participant_id``, ``session``,
332
        and ``run``.
333
    output_dir : Directory
334
        Directory where the result image will be saved.
335
    dryrun : bool
336
        If True, skip actual computation and file writing. Default False.
337

338
    Returns
339
    -------
340
    pca_image_file : File
341
        Generated image with the two first PCA components.
342
    """
343
    pca_image_file = output_dir / f"pca.png"
1✔
344

345
    if dryrun:
1✔
346
        return (pca_image_file, )
1✔
347

UNCOV
348
    df = pd.read_csv(pca_file, sep="\t")
×
349

UNCOV
350
    fig, ax = plt.subplots(figsize=(20, 10))
×
UNCOV
351
    ax.scatter(df.pc1, df.pc2)
×
UNCOV
352
    for idx in range(len(df)):
×
UNCOV
353
        ax.annotate(
×
354
            f"{df.participant_id[idx]}-{df.session[idx]}-{df.run[idx]}",
355
            xy=(df.pc1[idx], df.pc2[idx]),
356
            xytext=(4, 4),
357
            textcoords="offset pixels"
358
        )
UNCOV
359
    plt.xlabel(f"PC1 (var={df.explained_variance_ratio_pc1[0]:.2f})")
×
UNCOV
360
    plt.ylabel(f"PC2 (var={df.explained_variance_ratio_pc2[1]:.2f})")
×
UNCOV
361
    plt.axis("equal")
×
UNCOV
362
    ax.spines["right"].set_visible(False)
×
UNCOV
363
    ax.spines["top"].set_visible(False)
×
UNCOV
364
    plt.tight_layout()
×
UNCOV
365
    plt.savefig(pca_image_file)
×
UNCOV
366
    plt.close(fig)
×
367

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

© 2026 Coveralls, Inc