Skip to content

Commit 2ca3326

Browse files
authored
Merge pull request #2340 from MRtrix3/denoising_outputrank
Denoising updates for 3.1.0
2 parents 442e89e + 2fd87e0 commit 2ca3326

2 files changed

Lines changed: 39 additions & 10 deletions

File tree

cmd/dwidenoise.cpp

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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

211222
private:
@@ -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

257269
template <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

docs/reference/commands/dwidenoise.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ Options
3636

3737
- **-noise level** The output noise map, i.e., the estimated noise level 'sigma' in the data. Note that on complex input data, this will be the total noise level across real and imaginary channels, so a scale factor sqrt(2) applies.
3838

39+
- **-rank cutoff** The selected signal rank of the output denoised image.
40+
3941
- **-datatype float32/float64** Datatype for the eigenvalue decomposition (single or double precision). For complex input data, this will select complex float32 or complex float64 datatypes.
4042

4143
- **-estimator Exp1/Exp2** Select the noise level estimator (default = Exp2), either: |br|

0 commit comments

Comments
 (0)