1
1
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2
2
# vi: set ft=python sts=4 ts=4 sw=4 et:
3
3
"""Helper tools for visualization purposes"""
4
- import os . path as op
4
+ from pathlib import Path
5
5
from shutil import which
6
6
import subprocess
7
7
import base64
8
8
import re
9
- from sys import version_info
10
9
from uuid import uuid4
11
10
from io import StringIO
12
11
13
12
import numpy as np
14
13
import nibabel as nb
15
14
16
- from lxml import etree
17
15
from nilearn import image as nlimage
18
16
from nilearn .plotting import plot_anat
19
- from svgutils .transform import SVGFigure
17
+ from svgutils .transform import fromstring , SVGFigure , GroupElement
20
18
from seaborn import color_palette
21
19
22
20
from nipype .utils import filemanip
23
21
from .. import NIWORKFLOWS_LOG
24
22
25
23
26
24
SVGNS = "http://www.w3.org/2000/svg"
27
- PY3 = version_info [0 ] > 2
28
25
29
26
30
27
def robust_set_limits (data , plot_params , percentiles = (15 , 99.8 )):
@@ -229,19 +226,9 @@ def plot_segs(image_nii, seg_niis, out_file, bbox_nii=None, masked=False,
229
226
plot_params ['cut_coords' ] = cuts [d ]
230
227
svg = _plot_anat_with_contours (image_nii , segs = seg_niis , compress = compress ,
231
228
** plot_params )
232
-
233
229
# Find and replace the figure_1 id.
234
- try :
235
- xml_data = etree .fromstring (svg )
236
- except etree .XMLSyntaxError as e :
237
- NIWORKFLOWS_LOG .info (e )
238
- return
239
- find_text = etree .ETXPath ("//{%s}g[@id='figure_1']" % SVGNS )
240
- find_text (xml_data )[0 ].set ('id' , 'segmentation-%s-%s' % (d , uuid4 ()))
241
-
242
- svg_fig = SVGFigure ()
243
- svg_fig .root = xml_data
244
- out_files .append (svg_fig )
230
+ svg = svg .replace ('figure_1' , 'segmentation-%s-%s' % (d , uuid4 ()), 1 )
231
+ out_files .append (fromstring (svg ))
245
232
246
233
return out_files
247
234
@@ -333,17 +320,8 @@ def plot_registration(anat_nii, div_id, plot_params=None,
333
320
display .close ()
334
321
335
322
# Find and replace the figure_1 id.
336
- try :
337
- xml_data = etree .fromstring (svg )
338
- except etree .XMLSyntaxError as e :
339
- NIWORKFLOWS_LOG .info (e )
340
- return
341
- find_text = etree .ETXPath ("//{%s}g[@id='figure_1']" % SVGNS )
342
- find_text (xml_data )[0 ].set ('id' , '%s-%s-%s' % (div_id , mode , uuid4 ()))
343
-
344
- svg_fig = SVGFigure ()
345
- svg_fig .root = xml_data
346
- out_files .append (svg_fig )
323
+ svg = svg .replace ('figure_1' , '%s-%s-%s' % (div_id , mode , uuid4 ()), 1 )
324
+ out_files .append (fromstring (svg ))
347
325
348
326
return out_files
349
327
@@ -353,7 +331,6 @@ def compose_view(bg_svgs, fg_svgs, ref=0, out_file='report.svg'):
353
331
Composes the input svgs into one standalone svg and inserts
354
332
the CSS code for the flickering animation
355
333
"""
356
- import svgutils .transform as svgt
357
334
358
335
if fg_svgs is None :
359
336
fg_svgs = []
@@ -380,7 +357,7 @@ def compose_view(bg_svgs, fg_svgs, ref=0, out_file='report.svg'):
380
357
381
358
# Compose the views panel: total size is the width of
382
359
# any element (used the first here) and the sum of heights
383
- fig = svgt . SVGFigure (width , heights [:nsvgs ].sum ())
360
+ fig = SVGFigure (width , heights [:nsvgs ].sum ())
384
361
385
362
yoffset = 0
386
363
for i , r in enumerate (roots ):
@@ -393,21 +370,20 @@ def compose_view(bg_svgs, fg_svgs, ref=0, out_file='report.svg'):
393
370
# Group background and foreground panels in two groups
394
371
if fg_svgs :
395
372
newroots = [
396
- svgt . GroupElement (roots [:nsvgs ], {'class' : 'background-svg' }),
397
- svgt . GroupElement (roots [nsvgs :], {'class' : 'foreground-svg' })
373
+ GroupElement (roots [:nsvgs ], {'class' : 'background-svg' }),
374
+ GroupElement (roots [nsvgs :], {'class' : 'foreground-svg' })
398
375
]
399
376
else :
400
377
newroots = roots
401
378
fig .append (newroots )
402
379
fig .root .attrib .pop ("width" )
403
380
fig .root .attrib .pop ("height" )
404
381
fig .root .set ("preserveAspectRatio" , "xMidYMid meet" )
405
- out_file = op . abspath (out_file )
406
- fig .save (out_file )
382
+ out_file = Path (out_file ). absolute ( )
383
+ fig .save (str ( out_file ) )
407
384
408
385
# Post processing
409
- with open (out_file , 'r' if PY3 else 'rb' ) as f :
410
- svg = f .read ().split ('\n ' )
386
+ svg = out_file .read_text ().splitlines ()
411
387
412
388
# Remove <?xml... line
413
389
if svg [0 ].startswith ("<?xml" ):
@@ -422,9 +398,8 @@ def compose_view(bg_svgs, fg_svgs, ref=0, out_file='report.svg'):
422
398
.foreground-svg:hover { animation-play-state: running;}
423
399
</style>""" % tuple ([uuid4 ()] * 2 ))
424
400
425
- with open (out_file , 'w' if PY3 else 'wb' ) as f :
426
- f .write ('\n ' .join (svg ))
427
- return out_file
401
+ out_file .write_text ("\n " .join (svg ))
402
+ return str (out_file )
428
403
429
404
430
405
def transform_to_2d (data , max_axis ):
@@ -459,32 +434,31 @@ def plot_melodic_components(melodic_dir, in_file, tr=None,
459
434
from functional MRI data.
460
435
461
436
Parameters
462
-
463
- melodic_dir : str
464
- Path pointing to the outputs of MELODIC
465
- in_file : str
466
- Path pointing to the reference fMRI dataset. This file
467
- will be used to extract the TR value, if the ``tr`` argument
468
- is not set. This file will be used to calculate a mask
469
- if ``report_mask`` is not provided.
470
- tr : float
471
- Repetition time in seconds
472
- out_file : str
473
- Path where the resulting SVG file will be stored
474
- compress : ``'auto'`` or bool
475
- Whether SVG should be compressed. If ``'auto'``, compression
476
- will be executed if dependencies are installed (SVGO)
477
- report_mask : str
478
- Path to a brain mask corresponding to ``in_file``
479
- noise_components_file : str
480
- A CSV file listing the indexes of components classified as noise
481
- by some manual or automated (e.g. ICA-AROMA) procedure. If a
482
- ``noise_components_file`` is provided, then components will be
483
- plotted with red/green colors (correspondingly to whether they
484
- are in the file -noise components, red-, or not -signal, green-).
485
- When all or none of the components are in the file, a warning
486
- is printed at the top.
487
-
437
+ ----------
438
+ melodic_dir : str
439
+ Path pointing to the outputs of MELODIC
440
+ in_file : str
441
+ Path pointing to the reference fMRI dataset. This file
442
+ will be used to extract the TR value, if the ``tr`` argument
443
+ is not set. This file will be used to calculate a mask
444
+ if ``report_mask`` is not provided.
445
+ tr : float
446
+ Repetition time in seconds
447
+ out_file : str
448
+ Path where the resulting SVG file will be stored
449
+ compress : ``'auto'`` or bool
450
+ Whether SVG should be compressed. If ``'auto'``, compression
451
+ will be executed if dependencies are installed (SVGO)
452
+ report_mask : str
453
+ Path to a brain mask corresponding to ``in_file``
454
+ noise_components_file : str
455
+ A CSV file listing the indexes of components classified as noise
456
+ by some manual or automated (e.g. ICA-AROMA) procedure. If a
457
+ ``noise_components_file`` is provided, then components will be
458
+ plotted with red/green colors (correspondingly to whether they
459
+ are in the file -noise components, red-, or not -signal, green-).
460
+ When all or none of the components are in the file, a warning
461
+ is printed at the top.
488
462
489
463
"""
490
464
from nilearn .image import index_img , iter_img
0 commit comments