Skip to content

Commit 071e575

Browse files
authored
Merge pull request #3374 from 0rC0/cat12_sanlm
[ENH]: Add CAT12 SANLM denoising filter
2 parents 8c2330d + 2dfea7a commit 071e575

File tree

4 files changed

+227
-1
lines changed

4 files changed

+227
-1
lines changed

Diff for: .zenodo.json

+5
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,11 @@
835835
"affiliation": "MIT, HMS",
836836
"name": "Ghosh, Satrajit",
837837
"orcid": "0000-0002-5312-6729"
838+
},
839+
{
840+
"affiliation": "Charitè Universitätsmedizin Berlin, Germany",
841+
"name": "Dell\'Orco, Andrea",
842+
"orcid": "0000-0002-3964-8360"
838843
}
839844
],
840845
"keywords": [

Diff for: nipype/interfaces/cat12/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from .preprocess import CAT12Segment
1+
from .preprocess import CAT12Segment, CAT12SANLMDenoising
22
from .surface import (
33
ExtractAdditionalSurfaceParameters,
44
ExtractROIBasedSurfaceMeasures,

Diff for: nipype/interfaces/cat12/preprocess.py

+149
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,155 @@ def _list_outputs(self):
593593
return outputs
594594

595595

596+
class CAT12SANLMDenoisingInputSpec(SPMCommandInputSpec):
597+
598+
in_files = InputMultiPath(
599+
ImageFileSPM(exists=True),
600+
field="data",
601+
desc="Images for filtering.",
602+
mandatory=True,
603+
copyfile=False,
604+
)
605+
606+
spm_type = traits.Enum(
607+
"float32",
608+
"uint16",
609+
"uint8",
610+
"same",
611+
field="spm_type",
612+
usedefault=True,
613+
desc="Data type of the output images. 'same' matches the input image type.",
614+
)
615+
616+
intlim = traits.Int(
617+
field="intlim",
618+
default_value=100,
619+
usedefault=True,
620+
desc="intensity limitation (default = 100)",
621+
)
622+
623+
filename_prefix = traits.Str(
624+
field="prefix",
625+
default_value="sanlm_",
626+
usedefault=True,
627+
desc="Filename prefix. Specify the string to be prepended to the filenames of the filtered image file(s).",
628+
)
629+
630+
filename_suffix = traits.Str(
631+
field="suffix",
632+
default_value="",
633+
usedefault=True,
634+
desc="Filename suffix. Specify the string to be appended to the filenames of the filtered image file(s).",
635+
)
636+
637+
addnoise = traits.Float(
638+
default_value=0.5,
639+
usedefault=True,
640+
field="addnoise",
641+
desc="""Strength of additional noise in noise-free regions.
642+
Add minimal amount of noise in regions without any noise to avoid image segmentation problems.
643+
This parameter defines the strength of additional noise as percentage of the average signal intensity.""",
644+
)
645+
646+
rician = traits.Bool(
647+
True,
648+
field="rician",
649+
usedefault=True,
650+
desc="""Rician noise
651+
MRIs can have Gaussian or Rician distributed noise with uniform or nonuniform variance across the image.
652+
If SNR is high enough (>3) noise can be well approximated by Gaussian noise in the foreground. However, for
653+
SENSE reconstruction or DTI data a Rician distribution is expected. Please note that the Rician noise estimation
654+
is sensitive for large signals in the neighbourhood and can lead to artefacts, e.g. cortex can be affected by
655+
very high values in the scalp or in blood vessels.""",
656+
)
657+
658+
replace_nan_and_inf = traits.Bool(
659+
True,
660+
field="replaceNANandINF",
661+
usedefault=True,
662+
desc="Replace NAN by 0, -INF by the minimum and INF by the maximum of the image.",
663+
)
664+
665+
noisecorr_strength = traits.Enum(
666+
"-Inf",
667+
2,
668+
4,
669+
field="nlmfilter.optimized.NCstr",
670+
usedefault=True,
671+
desc="""Strength of Noise Corrections
672+
Strength of the (sub-resolution) spatial adaptive non local means (SANLM) noise correction. Please note
673+
that the filter strength is automatically estimated. Change this parameter only for specific conditions. The
674+
"light" option applies half of the filter strength of the adaptive "medium" cases, whereas the "strong"
675+
option uses the full filter strength, force sub-resolution filtering and applies an additional iteration.
676+
Sub-resolution filtering is only used in case of high image resolution below 0.8 mm or in case of the
677+
"strong" option. light = 2, medium = -Inf, strong = 4""",
678+
)
679+
680+
681+
class CAT12SANLMDenoisingOutputSpec(TraitedSpec):
682+
683+
out_file = File(desc="out file")
684+
685+
686+
class CAT12SANLMDenoising(SPMCommand):
687+
"""
688+
Spatially adaptive non-local means (SANLM) denoising filter
689+
690+
This function applies an spatial adaptive (sub-resolution) non-local means denoising filter
691+
to the data. This filter will remove noise while preserving edges. The filter strength is
692+
automatically estimated based on the standard deviation of the noise.
693+
694+
This filter is internally used in the segmentation procedure anyway. Thus, it is not
695+
necessary (and not recommended) to apply the filter before segmentation.
696+
______________________________________________________________________
697+
Christian Gaser, Robert Dahnke
698+
Structural Brain Mapping Group (http://www.neuro.uni-jena.de)
699+
Departments of Neurology and Psychiatry
700+
Jena University Hospital
701+
______________________________________________________________________
702+
703+
Examples
704+
--------
705+
>>> from nipype.interfaces import cat12
706+
>>> c = cat12.CAT12SANLMDenoising()
707+
>>> c.inputs.in_files = 'anatomical.nii'
708+
>>> c.run() # doctest: +SKIP
709+
"""
710+
711+
input_spec = CAT12SANLMDenoisingInputSpec
712+
output_spec = CAT12SANLMDenoisingOutputSpec
713+
714+
def __init__(self, **inputs):
715+
_local_version = SPMCommand().version
716+
if _local_version and "12." in _local_version:
717+
self._jobtype = "tools"
718+
self._jobname = "cat.tools.sanlm"
719+
720+
SPMCommand.__init__(self, **inputs)
721+
722+
def _format_arg(self, opt, spec, val):
723+
"""Convert input to appropriate format for spm"""
724+
if opt == "in_files":
725+
if isinstance(val, list):
726+
return scans_for_fnames(val)
727+
else:
728+
return scans_for_fname(val)
729+
if opt == "spm_type":
730+
type_map = {"same": 0, "uint8": 2, "uint16": 512, "float32": 16}
731+
val = type_map[val]
732+
return super(CAT12SANLMDenoising, self)._format_arg(opt, spec, val)
733+
734+
def _list_outputs(self):
735+
outputs = self._outputs().get()
736+
outputs["out_file"] = fname_presuffix(
737+
self.inputs.in_files[0],
738+
newpath=os.getcwd(),
739+
prefix=self.inputs.filename_prefix,
740+
suffix=self.inputs.filename_suffix,
741+
)
742+
return outputs
743+
744+
596745
class Cell2Str(Cell):
597746
def __str__(self):
598747
"""Convert input to appropriate format for cat12"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from ..preprocess import CAT12SANLMDenoising
3+
4+
5+
def test_CAT12SANLMDenoising_inputs():
6+
input_map = dict(
7+
addnoise=dict(
8+
field="addnoise",
9+
usedefault=True,
10+
),
11+
filename_prefix=dict(
12+
field="prefix",
13+
usedefault=True,
14+
),
15+
filename_suffix=dict(
16+
field="suffix",
17+
usedefault=True,
18+
),
19+
in_files=dict(
20+
copyfile=False,
21+
field="data",
22+
mandatory=True,
23+
),
24+
intlim=dict(
25+
field="intlim",
26+
usedefault=True,
27+
),
28+
matlab_cmd=dict(),
29+
mfile=dict(
30+
usedefault=True,
31+
),
32+
noisecorr_strength=dict(
33+
field="nlmfilter.optimized.NCstr",
34+
usedefault=True,
35+
),
36+
paths=dict(),
37+
replace_nan_and_inf=dict(
38+
field="replaceNANandINF",
39+
usedefault=True,
40+
),
41+
rician=dict(
42+
field="rician",
43+
usedefault=True,
44+
),
45+
spm_type=dict(
46+
field="spm_type",
47+
usedefault=True,
48+
),
49+
use_mcr=dict(),
50+
use_v8struct=dict(
51+
min_ver="8",
52+
usedefault=True,
53+
),
54+
)
55+
inputs = CAT12SANLMDenoising.input_spec()
56+
57+
for key, metadata in list(input_map.items()):
58+
for metakey, value in list(metadata.items()):
59+
assert getattr(inputs.traits()[key], metakey) == value
60+
61+
62+
def test_CAT12SANLMDenoising_outputs():
63+
output_map = dict(
64+
out_file=dict(
65+
extensions=None,
66+
),
67+
)
68+
outputs = CAT12SANLMDenoising.output_spec()
69+
70+
for key, metadata in list(output_map.items()):
71+
for metakey, value in list(metadata.items()):
72+
assert getattr(outputs.traits()[key], metakey) == value

0 commit comments

Comments
 (0)