]> Creatis software - clitk.git/blob - scripts/create_midP_masks-2.0.sh
Merge branch 'master' of git.creatis.insa-lyon.fr:clitk
[clitk.git] / scripts / create_midP_masks-2.0.sh
1 #! /bin/bash -x
2   
3 ###############################################################################
4 #
5 # FILE: create_midP-2.0.sh
6 # AUTHOR: RĂ´mulo Pinho 05/08/2011
7 #
8 # Version 2.0 of the create_midP_masks.sh script. The most relevant changes are:
9 #   * creation of bands around input and output image regions to try to improve 
10 #   the registration along lung boundaries (naturally, it depends on the quality
11 #   of motion mask generation).
12 #   * some steps are now in different modules, to facilitate re-use (see "includes" section).
13 #   * minor modifications on output file names.
14 #   * attempt to simplify the code a bit.
15 #
16 ###############################################################################
17
18 source `dirname $0`/midp_common.sh
19
20 extract_patient()
21 {
22   echo "$phase_file -> Extracting patient..."
23   clitkExtractPatient -i $phase_file -o $mask_dir_tmp/patient_mask_$phase_nb.mhd --noAutoCrop -a $afdb_file $ExtractPatientExtra
24 #   abort_on_error clitkExtractPatient $?
25
26   clitkSetBackground -i $phase_file -o $mask_dir_tmp/patient_$phase_nb.mhd --mask $mask_dir_tmp/patient_mask_$phase_nb.mhd --outsideValue -1000
27 #   abort_on_error clitkSetBackground $?
28 }
29
30 extract_bones()
31 {
32   if [ x = x$ExtractBonesLower1 ]; then
33     ExtractBonesLower1=120
34   fi
35   if [ x = x$ExtractBonesLower2 ]; then
36     ExtractBonesLower2=80
37   fi
38   echo "$phase_file -> Extracting bones..."
39   clitkImageConvert -i $phase_file -o $mask_dir_tmp/float_$phase_nb.mhd -t float
40   clitkExtractBones -i $mask_dir_tmp/float_$phase_nb.mhd -o $mask_dir_tmp/bones_$phase_nb.mhd -a $afdb_file --lower1 $ExtractBonesLower1 --upper1 2000 --lower2 $ExtractBonesLower2 --upper2 2000 --smooth --time 0.0625 --noAutoCrop
41 }
42
43 extract_lungs()
44 {
45   echo "$phase_file -> Extracting lungs..."  
46   clitkExtractLung -i $phase_file -o $mask_dir_tmp/lungs_$phase_nb.mhd -a $afdb_file --noAutoCrop --doNotSeparateLungs --type 1
47 }
48
49
50
51 resample()
52 {
53   echo "$phase_file -> Resampling..."
54   clitkResampleImage -i $mask_dir_tmp/patient_$phase_nb.mhd -o $mask_dir_tmp/patient_$phase_nb.mhd --spacing $resample_spacing --interp $resample_algo
55   clitkResampleImage -i $mask_dir_tmp/patient_mask_$phase_nb.mhd -o $mask_dir_tmp/patient_mask_$phase_nb.mhd --spacing $resample_spacing --interp $resample_algo
56   clitkResampleImage -i $mask_dir_tmp/lungs_$phase_nb.mhd -o $mask_dir_tmp/lungs_$phase_nb.mhd --like $mask_dir_tmp/patient_$phase_nb.mhd
57   clitkResampleImage -i $mask_dir_tmp/bones_$phase_nb.mhd -o $mask_dir_tmp/bones_$phase_nb.mhd --like $mask_dir_tmp/patient_$phase_nb.mhd
58 }
59
60 compute_motion_mask()
61 {
62   if [ x = x$MotionMaskOffsetDetect ]; then
63     MotionMaskOffsetDetect="0,-5,0"
64   fi
65   if [ x = x$FillingLevel ]; then
66     FillingLevel=94
67   fi
68
69   #clitkMotionMask -i $mask_dir_tmp/patient_$phase_nb.mhd -o $mask_dir_tmp/mm_$phase_nb.mhd --featureLungs $mask_dir_tmp/lungs_$phase_nb.mhd --upperThresholdLungs -400 --fillingLevel $FillingLevel --offsetDetect $MotionMaskOffsetDetect --pad --writeFeature=$mask_dir_tmp/feature_$phase_nb.mhd $MotionMaskExtra
70   clitkMotionMask -i $mask_dir_tmp/patient_$phase_nb.mhd -o $mask_dir_tmp/mm_$phase_nb.mhd --featureLungs $mask_dir_tmp/lungs_$phase_nb.mhd --upperThresholdLungs -400 --featureBones $mask_dir_tmp/bones_$phase_nb.mhd --fillingLevel $FillingLevel --offsetDetect $MotionMaskOffsetDetect --pad --writeFeature $mask_dir_tmp/feature_$phase_nb.mhd $MotionMaskExtra  
71   #--monitor=$mask_dir_tmp/monitor_$phase_nb.mhd
72 }
73
74 create_banded_mask()
75 {
76   input=$1
77   input_mask=$2
78   output=$3
79   output_mask=$4
80   radius=$5
81
82   input_dir=`dirname $input`
83   input_base=`basename $input`
84
85   # first band
86   clitkMorphoMath -i $input_mask -o $input_dir/extra1_$input_base --type 1 --radius $radius
87   clitkImageArithm -i $input_dir/extra1_$input_base -j $input_mask -o $input_dir/band1_$input_base -t 7
88   clitkBinarizeImage -i $input_dir/band1_$input_base -o $input_dir/band1_$input_base -l 1 -u 1 --fg 100 --mode both
89   clitkImageConvert -i $input_dir/band1_$input_base -o $input_dir/short_band1_$input_base -t short
90   
91   # second band
92   clitkMorphoMath -i $input_dir/extra1_$input_base -o $input_dir/extra2_$input_base --type 1 --radius $radius
93   clitkImageArithm -i $input_dir/extra2_$input_base -j $input_dir/extra1_$input_base -o $input_dir/band2_$input_base -t 7
94   clitkBinarizeImage -i $input_dir/band2_$input_base -o $input_dir/band2_$input_base -l 1 -u 1 --fg 200 --mode both
95   clitkImageConvert -i $input_dir/band2_$input_base -o $input_dir/short_band2_$input_base -t short
96   
97   # combine bands with masks
98   clitkImageArithm -i $input_mask -j $input_dir/band1_$input_base -o $output_mask -t 0
99   clitkImageArithm -i $output_mask -j $input_dir/band2_$input_base -o $output_mask -t 0
100   # combine bands with image
101   combine_image $input_dir/short_band1_$input_base $input $output $input_dir/band1_$input_base
102   combine_image $input_dir/short_band2_$input_base $output $output $input_dir/band2_$input_base
103
104   # clean-up
105   rm `echo $input_dir/extra?_$input_base | sed 's:.mhd:.*:g'`
106   rm `echo $input_dir/band?_$input_base | sed 's:.mhd:.*:g'`
107   rm `echo $input_dir/short_band?_$input_base | sed 's:.mhd:.*:g'`
108 }
109
110 create_registration_masks()
111 {
112   # extract inside and outside regions from the patient image, 
113   # using the motion mask computed previously
114   echo "$phase_file -> Setting Background..."
115   clitkSetBackground -i $mask_dir_tmp/patient_$phase_nb.mhd -o $mask_dir_tmp/inside_$phase_nb.mhd --mask $mask_dir_tmp/${mask_type}_$phase_nb.mhd --outsideValue -1200
116   clitkSetBackground -i $mask_dir_tmp/patient_$phase_nb.mhd -o $mask_dir_tmp/outside_$phase_nb.mhd --mask $mask_dir_tmp/${mask_type}_$phase_nb.mhd --outsideValue -1200 --fg
117
118   # the registration masks for inside (and outside) region correspond
119   # to the motion mask (and its complement) plus extra grey value bands,
120   # obtained with morphological dilations.
121   # 
122   echo "$phase_file -> Creating registration masks..."
123   # inside
124   clitkMorphoMath -i $mask_dir_tmp/${mask_type}_$phase_nb.mhd -o $mask_dir_tmp/mask_inside_$phase_nb.mhd --type 1 --radius 8
125   create_banded_mask $mask_dir_tmp/inside_$phase_nb.mhd $mask_dir_tmp/${mask_type}_$phase_nb.mhd $mask_dir_tmp/banded_inside_$phase_nb.mhd $mask_dir_tmp/banded_mask_inside_$phase_nb.mhd 4
126   # outside 
127   clitkBinarizeImage -i $mask_dir_tmp/outside_$phase_nb.mhd -o $mask_dir_tmp/${mask_type}_outside_$phase_nb.mhd -l -999 -u 4000 --mode both 
128   #clitkExtractPatient -i $mask_dir_tmp/outside_$phase_nb.mhd -o $mask_dir_tmp/${mask_type}_outside_$phase_nb.mhd --noAutoCrop
129   clitkMorphoMath -i $mask_dir_tmp/${mask_type}_outside_$phase_nb.mhd -o $mask_dir_tmp/mask_outside_$phase_nb.mhd --type 1 --radius 8
130   create_banded_mask $mask_dir_tmp/outside_$phase_nb.mhd $mask_dir_tmp/${mask_type}_outside_$phase_nb.mhd $mask_dir_tmp/banded_outside_$phase_nb.mhd $mask_dir_tmp/banded_mask_outside_$phase_nb.mhd 4
131 }
132
133 create_registration_motion_masks()
134 {
135   # extract inside and outside regions from the patient image, 
136   # using the motion mask computed previously
137   echo "$phase_file -> Setting Background..."
138   clitkSetBackground -i $mask_dir_tmp/patient_$phase_nb.mhd -o $mask_dir_tmp/inside_$phase_nb.mhd --mask $mask_dir_tmp/mm_$phase_nb.mhd --outsideValue -1200
139   clitkSetBackground -i $mask_dir_tmp/patient_$phase_nb.mhd -o $mask_dir_tmp/outside_$phase_nb.mhd --mask $mask_dir_tmp/mm_$phase_nb.mhd --outsideValue -1200 --fg
140
141   # the registration masks for inside (and outside) region correspond
142   # to the motion mask (and its complement) plus extra grey value bands,
143   # obtained with morphological dilations.
144   # 
145   echo "$phase_file -> Creating registration masks..."
146   # inside
147   clitkMorphoMath -i $mask_dir_tmp/mm_$phase_nb.mhd -o $mask_dir_tmp/mask_inside_$phase_nb.mhd --type 1 --radius 8
148   create_banded_mask $mask_dir_tmp/inside_$phase_nb.mhd $mask_dir_tmp/mm_$phase_nb.mhd $mask_dir_tmp/banded_inside_$phase_nb.mhd $mask_dir_tmp/banded_mask_inside_$phase_nb.mhd 4
149   # outside 
150   clitkExtractPatient -i $mask_dir_tmp/outside_$phase_nb.mhd -o $mask_dir_tmp/mm_outside_$phase_nb.mhd --noAutoCrop
151   clitkMorphoMath -i $mask_dir_tmp/mm_outside_$phase_nb.mhd -o $mask_dir_tmp/mask_outside_$phase_nb.mhd --type 1 --radius 8
152   create_banded_mask $mask_dir_tmp/outside_$phase_nb.mhd $mask_dir_tmp/mm_outside_$phase_nb.mhd $mask_dir_tmp/banded_outside_$phase_nb.mhd $mask_dir_tmp/banded_mask_outside_$phase_nb.mhd 4
153 }
154
155 create_registration_lung_masks()
156 {
157   # extract inside and outside lung regions from the patient image, 
158   # using the motion mask computed previously
159   echo "$phase_file -> Setting Background..."
160   clitkSetBackground -i $mask_dir_tmp/patient_$phase_nb.mhd -o $mask_dir_tmp/inside_$phase_nb.mhd --mask $mask_dir_tmp/lungs_$phase_nb.mhd --outsideValue -1200
161   clitkSetBackground -i $mask_dir_tmp/patient_$phase_nb.mhd -o $mask_dir_tmp/outside_$phase_nb.mhd --mask $mask_dir_tmp/lungs_$phase_nb.mhd --outsideValue -1200 --fg
162
163   # the registration masks for inside (and outside) region correspond
164   # to the motion mask (and its complement) plus extra grey value bands,
165   # obtained with morphological dilations.
166   # 
167   echo "$phase_file -> Creating registration masks..."
168   # inside
169   clitkMorphoMath -i $mask_dir_tmp/lungs_$phase_nb.mhd -o $mask_dir_tmp/mask_inside_$phase_nb.mhd --type 1 --radius 8
170   create_banded_mask $mask_dir_tmp/inside_$phase_nb.mhd $mask_dir_tmp/lungs_$phase_nb.mhd $mask_dir_tmp/banded_inside_$phase_nb.mhd $mask_dir_tmp/banded_mask_inside_$phase_nb.mhd 4
171   # outside 
172   clitkBinarizeImage -i $mask_dir_tmp/outside_$phase_nb.mhd -o $mask_dir_tmp/lungs_outside_$phase_nb.mhd -l -999 -u 4000 --mode both 
173   clitkMorphoMath -i $mask_dir_tmp/lungs_outside_$phase_nb.mhd -o $mask_dir_tmp/mask_outside_$phase_nb.mhd --type 1 --radius 8
174   create_banded_mask $mask_dir_tmp/outside_$phase_nb.mhd $mask_dir_tmp/lungs_outside_$phase_nb.mhd $mask_dir_tmp/banded_outside_$phase_nb.mhd $mask_dir_tmp/banded_mask_outside_$phase_nb.mhd 4
175 }
176
177 mm_preprocessing()
178 {
179   extract_patient
180   extract_bones
181   extract_lungs
182   # remove_tmp_masks 1
183   if [ $resample_spacing -ne 0 ] ; then 
184     resample
185   fi
186 }
187
188 mm_postprocessing()
189 {
190   # remove_tmp_masks 2
191   # remove_tmp_masks 3
192
193   create_registration_masks
194 #   if [ "$mask_type" == "mm" ]; then
195 #     create_registration_motion_masks
196 #   elif [ "$mask_type" == "lungs" ]; then
197 #     create_registration_lung_masks
198 #   fi
199 }
200
201 wait_mm_creation()
202 {
203   # the motion masks are first created in a tmp directory. this directory is 
204   # later going to be renamed to the final motion mask directory. concurrent
205   # executions trying to create the same set of masks will be blocked until
206   # the first execution finishes. naturally, these other executions will not
207   # recreate the masks. so first we try to create the tmp directory. 
208   # if the creation fails, it means that another execution is
209   # already creating the masks, so this execution will be blocked. the
210   # execution is unblocked only when the creation of masks is finished and
211   # the mask directory is renamed.
212   #
213   do_mm=1
214   if [ -e $mask_dir ]; then
215     # check that the final mask dir exists and that it contains all files it needs.
216     # the check assumes that the inside and outside masks are the key files to exist.
217     do_mm=0
218     nb_phases=${#phase_nbs[@]}
219     if [ "$mask_type" == "patient" ]; then
220       nb_masks=`ls $mask_dir/lungs_*.mhd | wc -l`
221       if [ $nb_masks != $nb_phases ]; then
222         # if the mask dir is invalid, remove it and recreate all masks, just in case.
223         rm -fr $mask_dir 2> /dev/null
224         do_mm=1
225       fi
226     else
227       nb_mm_masks=`ls $mask_dir/${mask_type}_outside*.mhd | wc -l`
228       nb_in_masks=`ls $mask_dir/mask_in*.mhd | wc -l`
229       nb_out_masks=`ls $mask_dir/mask_out*.mhd | wc -l`
230       if [ $nb_mm_masks != $nb_phases -o $nb_in_masks != $nb_phases -o $nb_out_masks != $nb_phases ]; then
231         # if the mask dir is invalid, remove it and recreate all masks, just in case.
232         rm -fr $mask_dir 2> /dev/null
233         do_mm=1
234       fi
235     fi
236 #     elif [ "$mask_type" == "lungs" ]; then
237 #       nb_mm_masks=`ls $mask_dir/lungs_outside*.mhd | wc -l`
238 #       nb_in_masks=`ls $mask_dir/mask_in*.mhd | wc -l`
239 #       nb_out_masks=`ls $mask_dir/mask_out*.mhd | wc -l`
240 #       if [ $nb_mm_masks != $nb_phases -o $nb_in_masks != $nb_phases -o $nb_out_masks != $nb_phases ]; then
241 #         # if the mask dir is invalid, remove it and recreate all masks, just in case.
242 #         rm -fr $mask_dir 2> /dev/null
243 #         do_mm=1
244 #       fi
245 #     elif [ "$mask_type" == "mm" ]; then
246 #       nb_mm_masks=`ls $mask_dir/mm_outside*.mhd | wc -l`
247 #       nb_in_masks=`ls $mask_dir/mask_in*.mhd | wc -l`
248 #       nb_out_masks=`ls $mask_dir/mask_out*.mhd | wc -l`
249 #       if [ $nb_mm_masks != $nb_phases -o $nb_in_masks != $nb_phases -o $nb_out_masks != $nb_phases ]; then
250 #         # if the mask dir is invalid, remove it and recreate all masks, just in case.
251 #         rm -fr $mask_dir 2> /dev/null
252 #         do_mm=1
253 #       fi
254 #     fi
255   fi
256   
257   if [ $do_mm = 1 ]; then
258     if ! mkdir $mask_dir_tmp 2> /dev/null; then
259       if [ ! -e $mask_dir_tmp ]; then
260         # if the temp dir couldn't be created, but it doesn't exist, abort
261         abort_on_error wait_mm_creation $? clean_up_masks
262       else
263         # assumes another process is creating the maks in the temp dir.
264         # now we need to wait until the masks are complete or until the
265         # time limit is reached. 
266         interval=10
267         sleeping=0
268         max_wait=3600 # one hour
269         nb_files0=`ls $mask_dir_tmp/* | wc -l`
270         while [ ! -e $mask_dir -a $sleeping -le $max_wait ]; do
271           echo "waiting creation of motion masks..."
272           sleep $interval
273           sleeping=$(( $sleeping + $interval ))
274           nb_files1=`ls $mask_dir_tmp/* | wc -l`
275           if [ $nb_files1 != $nb_files0 ]; then
276             nb_files0=$nb_files1
277             sleeping=0
278           fi  
279         done
280
281         if [ $sleeping -gt $max_wait ]; then
282           abort_on_error wait_mm_creation -1 clean_up_masks
283         else
284           echo "finished waiting"
285           do_mm=0
286         fi
287       fi
288     fi  
289   fi
290 }
291
292 motion_mask()
293 {
294   #set cmd line variables
295   local mhd4d=`basename $1`
296   mask_type=$2
297   if [ $# -eq 4 ] ; then
298     resample_spacing=$3
299     resample_algo=$4
300   else
301     resample_spacing=0
302     resample_algo=0
303   fi
304
305   dir=`dirname $1`
306   cd $dir
307     
308   # import variables specific to each patient
309   if test -e ./variables; then
310     source ./variables
311   fi
312
313   #set other global variables
314   if [ $resample_spacing -ne 0 ] ; then
315     mask_dir="MASK-${resample_spacing}mm-$resample_algo"
316   else
317     mask_dir="MASK"
318   fi
319   mask_dir_tmp="tmp.$mask_dir"
320   extract_4d_phases $mhd4d
321
322   echo
323   echo "------------ Motion mask from create_midP_masks.sh ------------"
324   start=`date`
325   echo "start: $start"
326   echo
327
328   wait_mm_creation
329
330 #   do_mm=1
331 #   mask_dir_tmp=$mask_dir
332   if [ $do_mm == 1 ]; then
333     mask_log_dir=$mask_dir_tmp/LOG
334     mkdir -p $mask_log_dir
335
336     # multi-threaded pre-processing for motion mask calcs
337     pids=( )
338     for i in $( seq 0 $((${#phase_nbs[@]} - 1))); do
339       phase_nb=${phase_nbs[$i]}
340       phase_file=${phase_files[$i]}
341       afdb_file=`echo $phase_file | sed 's/mhd/afdb/'`
342
343       check_threads $MAX_THREADS
344       mm_preprocessing &
345       pids=( "${pids[@]}" "$!" )
346     done
347
348     wait_pids ${pids[@]}
349     for ret in $ret_codes; do
350       abort_on_error mm_preprocessing $ret clean_up_masks
351     done
352
353     if [ "$mask_type" == "mm" ]; then
354       # single-threaded motion mask calc
355       for i in $( seq 0 $((${#phase_nbs[@]} - 1))); do
356         phase_nb=${phase_nbs[$i]}
357         phase_file=${phase_files[$i]}
358
359         check_threads 1
360         echo "$phase_file -> Computing motion mask..."
361         compute_motion_mask > $mask_log_dir/motion_mask_$phase_file.log
362         abort_on_error compute_motion_mask $? clean_up_masks
363       done
364     fi
365
366     # multi-threaded post-processing of motion mask calcs
367     if [ "$mask_type" != "patient" ]; then
368       pids=( )
369       for i in $( seq 0 $((${#phase_nbs[@]} - 1))); do
370         phase_nb=${phase_nbs[$i]}
371         phase_file=${phase_files[$i]}
372
373         check_threads $MAX_THREADS 
374         mm_postprocessing &
375         pids=( "${pids[@]}" "$!" )
376       done
377     
378       wait_pids ${pids[@]}
379       for ret in $ret_codes; do
380         abort_on_error mm_postprocessing $ret clean_up_masks
381       done
382     fi
383
384     # rename tmp mask directory after mask creation
385     check_threads 1
386     mv -f $mask_dir_tmp $mask_dir
387   fi
388
389   echo
390   echo "-------- Motion mask done ! ---------"
391   end=`date`
392   echo "start: $start"
393   echo "end: $end"
394   echo
395 }
396
397
398 #################
399 # main  #
400 #################
401
402 if [ $# -ne 4 -a $# -ne 2 -a $# -ne 1 ]; then
403   echo "Usage: $0 CT_4D TYPE [RESAMPLE_SPACING RESAMPLE_ALGORITHM]"
404   echo "  TYPE: \"motion\" (traditional motion masks); \"lungs\" (lung masks); \"patient\" (patient mask only)"
405   exit -1
406 fi
407
408 #
409 # variables exported in this scope
410 #
411 # mask_dir: directory where all masks are kept
412 #
413
414 if [ $1 != "using-as-lib" ]; then
415   if [ $# -eq 4 ] ; then
416     motion_mask $1 $2 $3 $4
417   elif [ $# -eq 2 ] ; then
418     motion_mask $1 $2
419   else
420     motion_mask $1 all
421   fi
422 fi