Skip to content

Commit 4b6a152

Browse files
committed
New AddNoise interface
Simple way to add gaussian (signal) and rayleigh (background) noise to images
1 parent 504858e commit 4b6a152

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed

nipype/algorithms/misc.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,3 +1205,91 @@ def calc_moments(timeseries_file, moment):
12051205
m3 = stats.moment(timeseries, moment, axis=0)
12061206
zero = (m2 == 0)
12071207
return np.where(zero, 0, m3 / m2**(moment/2.0))
1208+
1209+
class AddNoiseInputSpec(TraitedSpec):
1210+
in_file = File( exists=True, mandatory=True,
1211+
desc='input image that will be corrupted with noise')
1212+
1213+
in_mask = File( exists=True, desc='input mask, voxels outside this mask\
1214+
will be considered background and corrupted with noise\
1215+
with Rayleigh distribution' )
1216+
1217+
snr = traits.Float( 10.0, desc='desired output SNR in dB', usedefault=True )
1218+
1219+
dist = traits.Enum( 'normal', desc='desired noise distribution, currently\
1220+
only gaussian is implemented' )
1221+
out_file = File( desc='desired output filename' )
1222+
1223+
class AddNoiseOutputSpec(TraitedSpec):
1224+
out_file = File(exists=True, desc='corrupted image')
1225+
1226+
1227+
class AddNoise(BaseInterface):
1228+
"""
1229+
Corrupts with noise the input image
1230+
1231+
Example
1232+
-------
1233+
1234+
>>> from nipype.algorithms.misc import AddNoise
1235+
>>> noise = AddNoise()
1236+
>>> noise.inputs.in_file = 'T1.nii'
1237+
>>> noise.inputs.in_mask = 'mask.nii'
1238+
>>> noise.snr = 30.0
1239+
>>> noise.run() # doctest: +SKIP
1240+
"""
1241+
input_spec = AddNoiseInputSpec
1242+
output_spec = AddNoiseOutputSpec
1243+
1244+
def _run_interface(self, runtime):
1245+
in_image = nb.load( self.inputs.in_file )
1246+
in_data = in_image.get_data()
1247+
snr = self.inputs.snr
1248+
1249+
if isdefined( self.inputs.in_mask ):
1250+
in_mask = nb.load( self.inputs.in_mask ).get_data()
1251+
else:
1252+
in_mask = np.ones_like( in_data )
1253+
1254+
result = self.gen_noise( in_data, mask=in_mask, snr_db=snr )
1255+
res_im = nb.Nifti1Image( result, in_image.get_affine(), in_image.get_header() )
1256+
nb.save( res_im, self._gen_output_filename() )
1257+
return runtime
1258+
1259+
def _gen_output_filename( self ):
1260+
if not isdefined( self.inputs.out_file ):
1261+
_, base, _ = split_filename( self.inputs.in_file )
1262+
out_file = os.path.abspath( base + ('_SNR%03.2f' % self.inputs.snr) + '.nii.gz' )
1263+
else:
1264+
out_file = self.inputs.out_file
1265+
1266+
return out_file
1267+
1268+
def _list_outputs(self):
1269+
outputs = self.output_spec().get()
1270+
outputs['out_file'] = self._gen_output_filename()
1271+
return outputs
1272+
1273+
def gen_noise( self, image, mask=None, snr_db=10.0 ):
1274+
"""
1275+
Generates a copy of an image with a certain amount of
1276+
added gaussian noise (rayleigh for background in mask)
1277+
"""
1278+
from math import sqrt
1279+
snr = sqrt( np.power( 10.0, snr_db/10.0 ) )
1280+
noise = np.random.normal( size=image.shape )
1281+
1282+
if mask is None:
1283+
mask = np.ones_like( image )
1284+
1285+
1286+
S = np.mean(image[mask>0])
1287+
1288+
if np.any( mask==0 ):
1289+
S = S - np.mean( image[mask==0] )
1290+
bg_noise = np.random.rayleigh( size=image.shape )
1291+
noise[mask==0] = bg_noise[mask==0]
1292+
1293+
im_noise = image + noise * (S/snr)
1294+
1295+
return im_noise

0 commit comments

Comments
 (0)