@@ -1205,3 +1205,91 @@ def calc_moments(timeseries_file, moment):
1205
1205
m3 = stats .moment (timeseries , moment , axis = 0 )
1206
1206
zero = (m2 == 0 )
1207
1207
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