@@ -87,6 +87,9 @@ void usage ()
8787 " real and imaginary channels, so a scale factor sqrt(2) applies." )
8888 + Argument (" level" ).type_image_out ()
8989
90+ + Option (" rank" , " The selected signal rank of the output denoised image." )
91+ + Argument (" cutoff" ).type_image_out ()
92+
9093 + Option (" datatype" , " Datatype for the eigenvalue decomposition (single or double precision). "
9194 " For complex input data, this will select complex float32 or complex float64 datatypes." )
9295 + Argument (" float32/float64" ).type_choice (dtypes)
@@ -130,7 +133,8 @@ class DenoisingFunctor {
130133 using SValsType = Eigen::VectorXd;
131134
132135 DenoisingFunctor (int ndwi, const vector<uint32_t >& extent,
133- Image<bool >& mask, Image<real_type>& noise, bool exp1)
136+ Image<bool >& mask, Image<real_type>& noise,
137+ Image<uint16_t >& rank, bool exp1)
134138 : extent {{extent[0 ]/2 , extent[1 ]/2 , extent[2 ]/2 }},
135139 m (ndwi),
136140 n (extent[0 ]*extent[1 ]*extent[2 ]),
@@ -140,10 +144,11 @@ class DenoisingFunctor {
140144 X (m,n),
141145 XtX (r, r),
142146 eig (r),
143- s (SValsType() ),
147+ s (r ),
144148 pos {{0 , 0 , 0 }},
145149 mask (mask),
146- noise (noise)
150+ noise (noise),
151+ rankmap (rank)
147152 { }
148153
149154 template <typename ImageType>
@@ -206,6 +211,12 @@ class DenoisingFunctor {
206211 assign_pos_of (dwi, 0 , 3 ).to (noise);
207212 noise.value () = real_type (std::sqrt (sigma2));
208213 }
214+ // store rank map if requested:
215+ if (rankmap.valid ()) {
216+ assign_pos_of (dwi, 0 , 3 ).to (rankmap);
217+ rankmap.value () = uint16_t (r - cutoff_p);
218+ }
219+
209220 }
210221
211222private:
@@ -220,6 +231,7 @@ class DenoisingFunctor {
220231 double sigma2;
221232 Image<bool > mask;
222233 Image<real_type> noise;
234+ Image<uint16_t > rankmap;
223235
224236 template <typename ImageType>
225237 void load_data (ImageType& dwi) {
@@ -255,7 +267,7 @@ class DenoisingFunctor {
255267
256268
257269template <typename T>
258- void process_image (Header& data, Image<bool >& mask, Image<real_type> noise,
270+ void process_image (Header& data, Image<bool >& mask, Image<real_type>& noise, Image< uint16_t >& rank ,
259271 const std::string& output_name, const vector<uint32_t >& extent, bool exp1)
260272 {
261273 auto input = data.get_image <T>().with_direct_io (3 );
@@ -264,7 +276,7 @@ void process_image (Header& data, Image<bool>& mask, Image<real_type> noise,
264276 header.datatype () = DataType::from<T>();
265277 auto output = Image<T>::create (output_name, header);
266278 // run
267- DenoisingFunctor<T> func (data.size (3 ), extent, mask, noise, exp1);
279+ DenoisingFunctor<T> func (data.size (3 ), extent, mask, noise, rank, exp1);
268280 ThreadedLoop (" running MP-PCA denoising" , data, 0 , 3 ).run (func, input, output);
269281 }
270282
@@ -296,7 +308,7 @@ void run ()
296308 if (!(extent[i] & 1 ))
297309 throw Exception (" -extent must be a (list of) odd numbers" );
298310 if (extent[i] > dwi.size (i))
299- throw Exception (" -extent must nott exceed the image dimensions" );
311+ throw Exception (" -extent must not exceed the image dimensions" );
300312 }
301313 } else {
302314 uint32_t e = 1 ;
@@ -310,6 +322,11 @@ void run ()
310322
311323 bool exp1 = get_option_value (" estimator" , 1 ) == 0 ; // default: Exp2 (unbiased estimator)
312324
325+ if (std::min<uint32_t >(dwi.size (3 ), extent[0 ]*extent[1 ]*extent[2 ]) < 15 ) {
326+ WARN (" The number of volumes or the patch size is small. This may lead to discretisation effects "
327+ " in the noise level and cause inconsistent denoising between adjacent voxels." );
328+ }
329+
313330 Image<real_type> noise;
314331 opt = get_options (" noise" );
315332 if (opt.size ()) {
@@ -319,24 +336,34 @@ void run ()
319336 noise = Image<real_type>::create (opt[0 ][0 ], header);
320337 }
321338
339+ Image<uint16_t > rank;
340+ opt = get_options (" rank" );
341+ if (opt.size ()) {
342+ Header header (dwi);
343+ header.ndim () = 3 ;
344+ header.datatype () = DataType::UInt16;
345+ header.reset_intensity_scaling ();
346+ rank = Image<uint16_t >::create (opt[0 ][0 ], header);
347+ }
348+
322349 int prec = get_option_value (" datatype" , 0 ); // default: single precision
323350 if (dwi.datatype ().is_complex ()) prec += 2 ; // support complex input data
324351 switch (prec) {
325352 case 0 :
326353 INFO (" select real float32 for processing" );
327- process_image<float >(dwi, mask, noise, argument[1 ], extent, exp1);
354+ process_image<float >(dwi, mask, noise, rank, argument[1 ], extent, exp1);
328355 break ;
329356 case 1 :
330357 INFO (" select real float64 for processing" );
331- process_image<double >(dwi, mask, noise, argument[1 ], extent, exp1);
358+ process_image<double >(dwi, mask, noise, rank, argument[1 ], extent, exp1);
332359 break ;
333360 case 2 :
334361 INFO (" select complex float32 for processing" );
335- process_image<cfloat>(dwi, mask, noise, argument[1 ], extent, exp1);
362+ process_image<cfloat>(dwi, mask, noise, rank, argument[1 ], extent, exp1);
336363 break ;
337364 case 3 :
338365 INFO (" select complex float64 for processing" );
339- process_image<cdouble>(dwi, mask, noise, argument[1 ], extent, exp1);
366+ process_image<cdouble>(dwi, mask, noise, rank, argument[1 ], extent, exp1);
340367 break ;
341368 }
342369
0 commit comments