Skip to content

Commit 0c2c80e

Browse files
author
Robert D. Vincent
committed
Fix BIC-MNI/Register#20 - scan range of label volume before saving.
1 parent 159b340 commit 0c2c80e

File tree

4 files changed

+192
-9
lines changed

4 files changed

+192
-9
lines changed

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ SET(C_SRC
205205
main/main.c
206206
main/display.c
207207
main/event_loop.c
208-
main/graphics.c
208+
main/graphics.c
209+
main/multidim_x.c
209210
main/three_d.c
210211
main/transforms.c
211212
atlas/atlas.c

Include/multidim_x.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/** \file multidim_x.h
2+
*/
3+
void multidim_scan_range( const VIO_multidim_array *array,
4+
VIO_Real *min_value_p,
5+
VIO_Real *max_value_p );

callbacks/segmenting.c

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#endif
2020

2121
#include <display.h>
22+
#include <multidim_x.h>
2223

2324
static void set_slice_labels(
2425
display_struct *display,
@@ -399,7 +400,44 @@ DEF_MENU_UPDATE(load_label_data )
399400
return( get_n_volumes(display) > 0 );
400401
}
401402

402-
/* ARGSUSED */
403+
/**
404+
* \brief Scan a volume and set the real and voxel ranges to their
405+
* true values.
406+
*
407+
* Normally the label volume is set to have the a voxel minimum of
408+
* zero, and a voxel maximum of the currently set largest permitted
409+
* label value (255 by default). However, when saving the volume, this
410+
* is awkward or even incorrect because label volumes might have only
411+
* a few label values set, or in the extreme case of a mask volume,
412+
* might just have two possible values.
413+
*
414+
* To work around this, we save the original settings for the ranges, then
415+
* scan the volume to determine the "true" minimum and maximum. We then
416+
* temporarily set the voxel and real ranges to the true scanned values.
417+
* We will restore it to normal after the save completes.
418+
*
419+
* \param label_volume The label volume to scan.
420+
* \param real_range Will receive the current real range of the volume.
421+
* \param voxel_range Will receive the current voxel range of the volume.
422+
*/
423+
424+
static void
425+
temporarily_fix_range( VIO_Volume label_volume,
426+
VIO_Real real_range[],
427+
VIO_Real voxel_range[] )
428+
{
429+
VIO_Real voxel_min;
430+
VIO_Real voxel_max;
431+
432+
get_volume_real_range( label_volume, &real_range[0], &real_range[1] );
433+
get_volume_voxel_range( label_volume, &voxel_range[0], &voxel_range[1] );
434+
435+
multidim_scan_range( &label_volume->array, &voxel_min, &voxel_max );
436+
437+
/* Temporarily reconfigure the volume for saving. */
438+
set_volume_voxel_range( label_volume, voxel_min, voxel_max );
439+
set_volume_real_range( label_volume, voxel_min, voxel_max );
440+
}
403441

404442
/**
405443
* Menu command to save labels to a volumetric (e.g. MINC) file.
@@ -410,6 +448,9 @@ DEF_MENU_FUNCTION(save_label_data)
410448
VIO_STR filename, backup_filename;
411449
display_struct *slice_window;
412450
VIO_Real crop_threshold;
451+
VIO_Real old_real_range[2];
452+
VIO_Real old_voxel_range[2];
453+
VIO_Volume label_volume;
413454

414455
status = VIO_OK;
415456

@@ -442,6 +483,10 @@ DEF_MENU_FUNCTION(save_label_data)
442483
else
443484
crop_threshold = Crop_label_volumes_threshold;
444485

486+
label_volume = get_label_volume( slice_window );
487+
488+
temporarily_fix_range( label_volume, old_real_range, old_voxel_range );
489+
445490
status = make_backup_file( filename, &backup_filename );
446491
if( status == VIO_OK )
447492
{
@@ -458,16 +503,25 @@ DEF_MENU_FUNCTION(save_label_data)
458503

459504
status = save_label_volume( filename,
460505
backup_filename,
461-
get_label_volume(slice_window),
506+
label_volume,
462507
crop_threshold );
463508
if ( made_backup )
464509
{
465510
/* We made a backup, so clean it up now.
466511
*/
467512
cleanup_backup_file( filename, backup_filename, status );
513+
514+
delete_string( backup_filename );
468515
}
469516
}
470517

518+
/* Restore the original voxel and real ranges.
519+
*/
520+
set_volume_voxel_range( label_volume,
521+
old_voxel_range[0], old_voxel_range[1] );
522+
set_volume_real_range( label_volume,
523+
old_real_range[0], old_real_range[1] );
524+
471525
if( status == VIO_OK )
472526
print( "Label saved to %s\n", filename );
473527
else
@@ -908,9 +962,9 @@ typedef struct
908962
int volume_index;
909963
} callback_data;
910964

911-
/**
965+
/**
912966
* This callback is used to actually set the label voxel during 3D dilation
913-
* and fill operations. It therefore has to have the standard parameter
967+
* and fill operations. It therefore has to have the standard parameter
914968
* types.
915969
* \param volume The label volume to modify.
916970
* \param x The X voxel coordinate to set.
@@ -948,7 +1002,7 @@ do_fill_connected_3d( display_struct *display, VIO_BOOL is_erase )
9481002
int label_under_mouse, desired_label, volume_index;
9491003
display_struct *slice_window;
9501004
callback_data data;
951-
1005+
9521006
if( get_slice_window( display, &slice_window ) &&
9531007
get_voxel_under_mouse( slice_window, &volume_index, &view_index, voxel))
9541008
{
@@ -970,7 +1024,7 @@ do_fill_connected_3d( display_struct *display, VIO_BOOL is_erase )
9701024

9711025
data.slice_window = slice_window;
9721026
data.volume_index = volume_index;
973-
1027+
9741028
fill_connected_voxels_callback( get_nth_volume(slice_window,volume_index),
9751029
get_nth_label_volume(slice_window,volume_index),
9761030
slice_window->slice.segmenting.connectivity,
@@ -1063,10 +1117,10 @@ dilation_or_erosion_command(display_struct *display, VIO_BOOL do_erosion )
10631117
min_outside_label = min_user_label;
10641118
max_outside_label = max_user_label;
10651119
}
1066-
1120+
10671121
data.slice_window = slice_window;
10681122
data.volume_index = volume_index;
1069-
1123+
10701124
undo_start( slice_window, volume_index );
10711125
dilate_voxels_callback( get_volume( display ),
10721126
get_label_volume( display ),

main/multidim_x.c

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* \file multidim_x.c
3+
* \brief Extended functionality for multi-dimensional arrays.
4+
*
5+
* Functions for operations on multidimensional arrays that seemed
6+
* fairly generic. These probably belong in volume IO.
7+
*/
8+
9+
#include <volume_io.h>
10+
#include <float.h>
11+
#include <multidim_x.h>
12+
13+
/**
14+
* Actually scan through a multidimensional array and determine the
15+
* true minimum and maximum values.
16+
* \param array The array to scan.
17+
* \param min_value_p Where to store the minimum value.
18+
* \param max_value_p Where to store the maximum value.
19+
*/
20+
void multidim_scan_range( const VIO_multidim_array *array,
21+
VIO_Real *min_value_p,
22+
VIO_Real *max_value_p )
23+
{
24+
int x, y, z, t, v;
25+
VIO_Real value;
26+
VIO_Real min_value = DBL_MAX;
27+
VIO_Real max_value = -DBL_MAX;
28+
29+
/* Scan for the true minimum and maximum.
30+
*/
31+
switch ( array->n_dimensions )
32+
{
33+
case 5:
34+
for_less ( x, 0, array->sizes[VIO_X] )
35+
{
36+
for_less ( y, 0, array->sizes[VIO_Y] )
37+
{
38+
for_less ( z, 0, array->sizes[VIO_Z] )
39+
{
40+
for_less ( t, 0, array->sizes[3] )
41+
{
42+
for_less (v, 0, array->sizes[4] )
43+
{
44+
GET_MULTIDIM_5D( value, (VIO_Real), *array, x, y, z, t, v );
45+
if ( value < min_value )
46+
min_value = value;
47+
if ( value > max_value )
48+
max_value = value;
49+
}
50+
}
51+
}
52+
}
53+
}
54+
break;
55+
56+
case 4:
57+
for_less ( x, 0, array->sizes[VIO_X] )
58+
{
59+
for_less ( y, 0, array->sizes[VIO_Y] )
60+
{
61+
for_less ( z, 0, array->sizes[VIO_Z] )
62+
{
63+
for_less ( t, 0, array->sizes[3] )
64+
{
65+
GET_MULTIDIM_4D( value, (VIO_Real), *array, x, y, z, t );
66+
if ( value < min_value )
67+
min_value = value;
68+
if ( value > max_value )
69+
max_value = value;
70+
}
71+
}
72+
}
73+
}
74+
break;
75+
76+
case 3:
77+
for_less ( x, 0, array->sizes[VIO_X] )
78+
{
79+
for_less ( y, 0, array->sizes[VIO_Y] )
80+
{
81+
for_less ( z, 0, array->sizes[VIO_Z] )
82+
{
83+
GET_MULTIDIM_3D( value, (VIO_Real), *array, x, y, z );
84+
if ( value < min_value )
85+
min_value = value;
86+
if ( value > max_value )
87+
max_value = value;
88+
}
89+
}
90+
}
91+
break;
92+
93+
case 2:
94+
for_less ( x, 0, array->sizes[VIO_X] )
95+
{
96+
for_less ( y, 0, array->sizes[VIO_Y] )
97+
{
98+
GET_MULTIDIM_2D( value, (VIO_Real), *array, x, y );
99+
if ( value < min_value )
100+
min_value = value;
101+
if ( value > max_value )
102+
max_value = value;
103+
}
104+
}
105+
break;
106+
107+
case 1:
108+
for_less (x, 0, array->sizes[VIO_X] )
109+
{
110+
GET_MULTIDIM_1D( value, (VIO_Real), *array, x );
111+
if ( value < min_value )
112+
min_value = value;
113+
if ( value > max_value )
114+
max_value = value;
115+
}
116+
break;
117+
118+
default:
119+
break;
120+
}
121+
*min_value_p = min_value;
122+
*max_value_p = max_value;
123+
}

0 commit comments

Comments
 (0)