--- a
+++ b/MASHelperFunctions.sh
@@ -0,0 +1,2906 @@
+#!/bin/bash
+source $HOME/.bashrc
+
+##########################################################
+# Multi-Atlas-Segmentation, parcellation, and label fusion
+# To report any bugs/request, please contact: Da Ma (da_ma@sfu.ca,d.ma.11@ucl.ac.uk)
+##########################################################
+
+# -------------------------------------------------
+# define some global variables, (if not predefined)
+# -------------------------------------------------
+mas_script_path="$BASH_SOURCE"
+mas_script_dir=${mas_script_path%/*}
+mas_script_file="$(basename $mas_script_path)"
+# Alternatively: mas_script_name=${mas_script_path##*/}
+mas_script_name=$(echo $mas_script_file | cut -d. -f1 )
+
+# define some default global variable value
+AtlasListFileName=template_list.cfg
+
+# define FSLDIR explicitely
+# export FSLDIR="$HOME/Tools/fsl"
+# source ${FSLDIR}/etc/fslconf/fsl.sh
+
+echo """
+=======================
+[ $mas_script_name ]
+mas_script_dir  = $mas_script_dir
+mas_script_file = $mas_script_file
+
+[ Base functions ]
+# - check_image_file
+# - check_atlas_file
+# - check_mapping_file
+# - check_label_fusion_file
+
+# - get_orientation
+
+[ Single processing functions ]
+# - mas_masking (prerequisite: NiftyReg)
+# - mas_masking_fusion (prerequisite: NiftyReg)
+# - mas_mapping (prerequisite: NiftyReg)
+# - mas_fusion (prerequisite: NiftySeg)
+# - mas_quickcheck (prerequisite: FSL)
+# - mas_label_volume (prerequisite: NiftySeg)
+# - mas_template_function (template functions for developer)
+
+[ Batch processing functions ]:
+# - mas_masking_batch
+# - mas_mask_dilate_batch
+# - mas_mapping_batch
+# - mas_fusion_batch
+# - mas_parcellation_batch
+# - mas_quickcheck_batch
+
+[ Pre-processing functions ]:
+# - mas_fix_header_info
+# - mas_smooth_batch
+# - mas_N4_batch (prerequisite: ANT)
+
+[ Post-processing functions ]:
+# - mas_extract_label
+# - mas_extract_label_batch
+# - mas_extract_volume
+# - mas_extract_volume_batch
+# - mas_quickcheck_panorama
+=======================
+"""
+
+
+# ---------------------------------
+#  function: get_orientation
+# ---------------------------------
+function get_orientation(){
+    local function_name=${FUNCNAME[0]}
+    if [ "$#" -lt 1 ]; then
+        echo "Get nifti file header"
+        echo "Usage: $function_name [input_img]"
+        return 1
+    else
+        local IMG=$1
+    fi
+
+    ORIENT=`mri_info $IMG | grep Orientation`
+    ORIENT=${ORIENT##*\ }
+    echo $ORIENT
+}
+
+function get_orientation_batch(){
+    local function_name=${FUNCNAME[0]}
+    if [ "$#" -lt 2 ]; then
+        echo "Get nifti file header"
+        echo "Usage: $function_name [input_dir] [targetlist]"
+        return 1
+    else
+        local input_dir=$1
+        local targetlist=$2
+    fi
+
+    echo "input_dir=$input_dir"
+    echo "targetlist=$targetlist"
+
+    local target_id
+    for target_id in $(cat $targetlist); do
+        echo $target_id $(get_orientation $input_dir/$target_id.nii.gz)
+    done
+}
+
+# ---------------------------------
+#  function: check_image_file
+# ---------------------------------
+function check_image_file(){
+	local exist_flag=0
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 1 ]]; then
+		echo "[$function_name] please specify image file to check the existence"
+		return 1
+	fi
+	local file_path=$1 # with or without image extension
+	local function_name=${FUNCNAME[0]}
+	
+	# check existence with any valid extension
+  if [[ -f $file_path ]]; then
+    exist_flag=1
+    return 0
+    fi
+  ext_array=("" ".nii" ".nii.gz" ".img" ".hdr")
+  for ext in ${ext_array[@]}; do
+		if [[ -f $file_path$ext ]]; then
+			exist_flag=1
+			break
+		fi
+	done
+	# if find no file with any extension:
+	if [[ $exist_flag -eq 0 ]]; then
+		echo "[$function_name] cannot find file ($file_path)"
+		return 1
+	fi
+	return 0
+}
+
+# ---------------------------------
+#  function: check_atlas_file
+# ---------------------------------
+function check_atlas_file(){
+	local return_flag=0
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 2 ]]; then
+		echo "[$function_name] <atlas_dir> <atlas_id>"
+		return_flag=1
+		return $return_flag
+	fi
+
+	local atlas_dir=$1
+	local atlas_id=$2
+
+	local seg_type
+
+	for seg_type in template label mask;do
+		check_image_file "$atlas_dir/$seg_type/$atlas_id"
+		if [[ $? -ne 0 ]]; then
+			echo "[$function_name] ($atlas_id) atlas $seg_type missing "
+			return_flag=1
+			return $return_flag
+		fi
+	done
+
+	return $return_flag
+}
+
+# functions to be implemented and used for input/output file checking with accurate control
+function check_mapping_input(){
+	local return_flag=0
+	local function_name=${FUNCNAME[0]}
+}
+function check_mapping_output(){
+	local return_flag=0
+	local function_name=${FUNCNAME[0]}
+}
+function check_fusion_input(){
+	local return_flag=0
+	local function_name=${FUNCNAME[0]}
+}
+function check_fusion_output(){
+	local return_flag=0
+	local function_name=${FUNCNAME[0]}
+}
+
+# ---------------------------------
+#  function: check_mapping_file
+# ---------------------------------
+function check_mapping_file(){
+	local return_flag=0
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 4 ]]; then
+		echo "[$function_name] <target_dir> <target_id> <atlas_dir> <atlas_list> <(Optional)result_dir> <(Optional)targetmask_dir> (Optional)targetmask_suffix>"
+		return_flag=1
+		return $return_flag
+	fi
+
+	local target_dir=$1
+	local target_id=$2
+	local atlas_dir=$3
+	local atlas_list=$4
+	local result_dir=$5
+	local targetmask_dir=$6
+	local targetmask_suffix=$7
+
+	local target_id
+	local atlas_name=$(basename $atlas_dir)
+	local atlas_id
+
+	# check if output file already exist
+	local mapped_file_all_exist=1
+	for atlas_id in $(cat $atlas_list);do
+		if [[ ! -f "$result_dir/label/$atlas_name/$target_id.label.$atlas_id.nii.gz" ]] || \
+		   [[ ! -f "$result_dir/mask/$atlas_name/$target_id.mask.$atlas_id.nii.gz" ]] || \
+		   [[ ! -f "$result_dir/mapping/$atlas_name/$atlas_id.$target_id.f3d.nii.gz" ]]; then
+		   # found missing output files
+		   mapped_file_all_exist=0
+		   continue
+		fi
+	done
+	# if all file exist (no missing files found)
+	if [[ $mapped_file_all_exist -eq 1 ]]; then
+	   echo "[$function_name] all mapping output files exist"
+	   return_flag=2
+	   return $return_flag
+	fi
+
+	# check if input target file missing
+	check_image_file $target_dir/$target_id
+	if [[ $? -ne 0 ]]; then
+		echo "[$function_name] cannot find target ($target_dir/$target_id)"
+		return_flag=3
+		return $return_flag
+	fi
+	# check if specified target mask file exist
+	if [[ ! -z $targetmask_dir ]]; then
+		local targetmask_name="$targetmask_dir/$target_id$targetmask_suffix"
+		check_image_file $targetmask_name
+		if [[ $? -ne 0 ]]; then
+			echo "[$function_name] cannot find target mask $targetmask_name"
+			return_flag=4
+			return $return_flag
+		fi
+	fi
+	
+
+	return $return_flag
+}
+
+# ---------------------------------
+#  function: check_label_fusion_file
+# ---------------------------------
+function check_label_fusion_file(){
+	local return_flag=0
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 5 ]]; then
+		echo "[$function_name] <target_dir> <target_id> <atlas_name> <atlas_list> <result_dir>"
+		return_flag=1
+		return $return_flag
+	fi
+
+	local target_dir=$1
+	local target_id=$2
+	local atlas_name=$3
+	local atlas_list=$4
+	local result_dir=$5
+
+	# check output file
+	if [[ -f $result_dir/label/$target_id.label.$atlas_name.nii.gz ]]; then
+		echo "[$function_name] fusion output exist for target: $target_id, skipping ..."
+		return_flag=2
+		return $return_flag
+	fi
+	# check if input target file missing
+	check_image_file $target_dir/$target_id
+	local target_exist=$?
+	if [[ $target_exist -ne 0 ]]; then
+		echo "[$function_name] cannot find target ($target_dir/$target_id)"
+		return_flag=3
+		return $return_flag
+	fi
+	# check if the input 4D file has already been created
+	local seg_file
+	local seg_file_flag=1
+	for seg_file in mapping label; do
+		local merged_4d_file="$result_dir/$seg_file/$atlas_name/$target_id.4D.nii.gz"
+		if [[ ! -f $merged_4d_file ]]; then
+			seg_file_flag=0
+			break			
+		fi
+	done
+	if [[ $seg_file_flag -eq 1 ]]; then
+		echo "[$function_name] ($target_id) both 4D mapping and label file, ready for fusion ..."
+		return_flag=0
+		return $return_flag
+	fi
+
+
+	# check if input mapped atlas file missing
+	local atlas_flag=0
+	local atlas_seg_flag=0
+	local atlas_id
+	for atlas_id in $(cat $atlas_list);do
+		if [[ ! -f "$result_dir/mapping/$atlas_name/$atlas_id.$target_id.f3d.nii.gz" ]]; then
+			# echo "[$function_name] ($target_id) Mapping file for atlas ($atlas_id) not pre-exist"
+			#location:  $result_dir/mapping/$atlas_name/$atlas_id.$target_id.f3d.nii.gz"
+			atlas_flag=1
+			break
+		fi
+		local seg_file
+		for seg_file in label; do # no need to check mapped mask
+			if [[ ! -f "$result_dir/$seg_file/$atlas_name/$target_id.$seg_file.$atlas_id.nii.gz" ]]; then
+				echo "[$function_name] ($target_id) Cannot find mapped $seg_file of atlas ($atlas_id): $result_dir/$seg_file/$atlas_name/$target_id.$seg_file.$atlas_id.nii.gz"
+				atlas_seg_flag=1
+				break 2
+			fi
+		done
+	done
+	# skip current $target_id if input file missing
+	if [[ $atlas_flag -eq 1 ]]; then
+		# echo "[$function_name] cannot fine input mapped atlas template file"
+		return_flag=4
+		return $return_flag
+	elif [[ $atlas_seg_flag -eq 1 ]]; then
+		# echo "[$function_name] cannot fine input mapped $seg_file file"
+		return_flag=5
+		return $return_flag
+	fi
+	return $return_flag
+}
+
+# ------------------------------
+#  function: lable2mask
+# ------------------------------
+function label2masks(){
+	local function_name=${FUNCNAME[0]}
+	usage() {
+		echo """binarize label 2 mask
+		[Usage]: $function_name \$label_dir \$mask_dir [Optional]
+		"""
+	}
+	if [[ $# -lt 2 ]]; then
+		usage; return 1;
+	fi
+
+	local label_dir=$1
+	local mask_dir=$2
+
+	mkdir -p $mask_dir
+	for id in $(ls $label_dir); do
+		echo "create binarized mask for: $id"
+		seg_maths $label_dir/$id -bin $mask_dir/$id
+	done
+}
+
+# ------------------------------
+#  function: dilate_mask
+# ------------------------------
+function dilate_masks(){
+  local function_name=${FUNCNAME[0]}
+  usage() {
+    echo """dilate mask by [dil]
+    [Usage]: $function_name \$mask_dir \$mask_dil_dir \$dil
+    """
+  }
+  if [[ $# -lt 3 ]]; then
+    usage; return 1;
+  fi
+
+  local mask_dir=$(readlink -f $1)
+  local mask_dil_dir=$(readlink -f $2)
+  local dil=$3
+
+  mkdir -p $mask_dil_dir
+  for id in $(ls $mask_dir); do
+    echo "dilate [$dil] for mask: $id"
+    seg_maths $mask_dir/$id -dil $dil $mask_dil_dir/$id
+  done
+}
+
+# ------------------------------
+#  function: mas_label_volume
+# ------------------------------
+function mas_label_volume(){
+	local function_name=${FUNCNAME[0]}
+	usage() {
+		echo """
+Multi Atlas Segmentation - Part 4: volume extraction
+[$function_name] [-l target_list] [-s seg_dir] [-v vol_csv (file path)]
+       (optional file name suffix) [-t seg_type] [-a atlas_name]
+
+example usage 1: To extract the label volume for the parcellated results:
+	mas_label_volume -l target_list -s result_dir/\"label\" -v vol_csv -t \"label\" -a atlas_name
+example usage 2: To extract the mask volume for the parcellated results:
+	mas_label_volume -l target_list -s result_dir/\"mask\" -v vol_csv -t \"mask\" -a atlas_name
+		 """
+		return 1
+	}
+	
+	local OPTIND
+	local options
+	# print a seperate line
+	echo ""
+
+	while getopts ":l:a:s:t:v:c:h" options; do
+		case $options in
+			l ) echo "Target list:       $OPTARG"
+				local target_list=$OPTARG;;
+			a ) echo "Atlas name:        $OPTARG"
+				local atlas_name=$OPTARG;;
+			s ) echo "Result directory:  $OPTARG"
+				local seg_dir=$OPTARG;;
+			t ) echo "Segmentation type: $OPTARG"
+				local seg_type=$OPTARG;;
+			v ) echo "Volume file:       $OPTARG"
+				local vol_csv=$OPTARG;;
+			c ) echo "Cleanup flag:      $OPTARG"
+				local cleanup_flag=$OPTARG;;
+			\?) echo "Unknown option"
+				usage; return 1;;
+			h ) usage; return 1;;
+			: ) usage; return 1;;
+		esac
+	done
+
+	if [[ $OPTIND -eq 1 ]]; then
+		echo "[$function_name] no option specified"
+		usage; return 1
+	fi 
+
+	echo "[$function_name] begin volume extraction... "
+	local error_flag=0
+	local target_id
+	local seg_file
+
+	# check existance of $target_list
+	if [[ ! -f $target_list ]]; then
+		echo "[$function_name] cannot find target_list file: $starget_list"
+		error_flag=1
+		return $error_flag
+	fi
+
+	# check existance of $target_id
+	for target_id in $(cat $target_list); do
+		seg_file="$seg_dir/$target_id"
+		if [[ ! -z $seg_type ]]; then
+			seg_file="$seg_file.$seg_type"
+			if [[ ! -z $atlas_name ]]; then
+				seg_file="$seg_file.$atlas_name"
+			fi
+		fi
+		check_image_file $seg_file
+		if [[ $? -ne 0 ]]; then
+			echo "[$function_name] cannot find $seg_type file: $seg_file"
+			error_flag=1
+			return $error_flag
+		fi
+	done
+
+	# In case output file exist, warn user before overwrite
+	if [[ -f $vol_csv ]]; then
+		echo "Output file: <$vol_csv> exist, overwrite?"
+		local yn
+		select yn in "select 1 for Yes" "select 2 for No"; do
+			case $yn in
+				"select 1 for Yes" ) rm -f $vol_csv; break;;
+				"select 2 for No" ) echo "no overwrite, exiting ..."; return 1;;
+			esac
+		done
+	fi
+
+	local tmp_dir="$seg_dir/tmp_$RANDOM"
+	mkdir -p $tmp_dir
+
+	for target_id in $(cat $target_list); do
+		# input image file
+		seg_file="$seg_dir/$target_id"
+		if [[ ! -z $seg_type ]]; then
+			seg_file="$seg_file.$seg_type"
+			if [[ ! -z $atlas_name ]]; then
+				seg_file="$seg_file.$atlas_name"
+			fi
+		fi
+		# output csv file
+		local target_csv="$tmp_dir/$target_id.$seg_type.$atlas_name.csv"
+		seg_stats $seg_file -Vl $target_csv
+		# exclude the first colume, which is the background volume
+		echo "$target_id,$(cat $target_csv | cut -d, -f2-)" >> $vol_csv
+	done
+
+	# remove tmp
+	rm -rf $tmp_dir
+}
+
+# -------------------------
+# function mas_quickcheck
+# reference: 
+# [1] Generating Quality Assurance Images using FSL overlay and slicer
+#    https://faculty.washington.edu/madhyt/2016/12/10/180/
+# -------------------------
+function mas_quickcheck(){
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 3 ]]; then
+		echo "Multi Atlas Segmentation - Part 3: quickcheck generation"
+		echo "Usage: $function_name [bg_img] [(optional) overlay_img] [qc_dir] [qc_filename]"
+		return 1
+	fi
+	
+	local bg_img=$1
+	local overlay_img=$2
+	local qc_dir=$3
+	local qc_filename=$4
+
+	echo "bg_img      = $bg_img"
+	echo "overlay_img = $overlay_img"
+	echo "qc_dir      = $qc_dir"
+	echo "qc_filename = $qc_filename"
+
+	# check if FSL is installed (by checking variable $FSLDIR)
+	if [[ -z $FSLDIR ]]; then
+		echo "[$function_name] variable \$FSLDIR not set, cannot determine FSL installed location, exitng ..."
+		return 1
+	fi
+
+	local tmp_dir=$qc_dir/tmp_$RANDOM
+	mkdir -p $tmp_dir
+
+	local bg_name=$(basename $bg_img | cut -d. -f1)
+	local slicer_cmd
+
+	if [[ ! -z $overlay_img ]]; then
+		local overlay_name=$(basename $overlay_img | cut -d. -f1)
+		local overlay_nan=$tmp_dir/masknan.$bg_name.$overlay_name.nii.gz
+		local overlay_tmp=$tmp_dir/overlay.$bg_name.$overlay_name.nii.gz
+
+		# determine label range
+		seg_maths $overlay_img -masknan $overlay_img $overlay_nan
+		local label_range=$(seg_stats $overlay_nan -r)
+		echo "label_range = $label_range"
+		# generate overlay nifti file using FSL's overlay
+		overlay 1 0 $bg_img -a $overlay_nan $label_range $overlay_tmp
+
+		# local label_min=$(( $(echo $label_range | cut -d' ' -f1) +1 ))
+		# local label_max=$(echo $label_range | cut -d' ' -f2)
+		# echo "label_range = $label_min $label_max"
+		# # generate overlay nifti file using FSL's overlay
+		# overlay 1 0 $bg_img -a $overlay_img $label_min $label_max $overlay_tmp
+
+		# specify overlay transparency and colormap
+		local LUT_file="$FSLDIR/etc/luts/renderjet.lut"
+		if [[ ! -f $LUT_file ]]; then
+			echo "[$function_name] cannot find color Look-up-table file: $LUT_file"
+			return 1
+		fi
+		slicer_cmd="slicer -t -n -l $LUT_file"
+	else # no overlay_img
+		local overlay_tmp=$tmp_dir/$bg_name.nii.gz
+		ln -s $bg_img $overlay_tmp
+		slicer_cmd="slicer"
+	fi
+
+	# create png
+	slicer_cmd="$slicer_cmd $overlay_tmp \
+	  -x 0.10 $tmp_dir/x_11.png -x 0.20 $tmp_dir/x_12.png -x 0.30 $tmp_dir/x_13.png \
+	  -x 0.34 $tmp_dir/x_21.png -x 0.38 $tmp_dir/x_22.png -x 0.42 $tmp_dir/x_23.png \
+	  -x 0.46 $tmp_dir/x_31.png -x 0.50 $tmp_dir/x_32.png -x 0.54 $tmp_dir/x_33.png \
+	  -x 0.58 $tmp_dir/x_41.png -x 0.62 $tmp_dir/x_42.png -x 0.66 $tmp_dir/x_43.png \
+	  -x 0.70 $tmp_dir/x_51.png -x 0.74 $tmp_dir/x_52.png -x 0.78 $tmp_dir/x_53.png \
+	  -x 0.82 $tmp_dir/x_61.png -x 0.86 $tmp_dir/x_62.png -x 0.90 $tmp_dir/x_63.png \
+	  -y 0.15 $tmp_dir/y_11.png -y 0.25 $tmp_dir/y_12.png -y 0.34 $tmp_dir/y_13.png -y 0.42 $tmp_dir/y_14.png \
+	  -y 0.50 $tmp_dir/y_21.png -y 0.55 $tmp_dir/y_22.png -y 0.62 $tmp_dir/y_23.png -y 0.65 $tmp_dir/y_24.png \
+	  -y 0.70 $tmp_dir/y_31.png -y 0.73 $tmp_dir/y_32.png -y 0.78 $tmp_dir/y_33.png -y 0.82 $tmp_dir/y_34.png \
+	  -z 0.15 $tmp_dir/z_11.png -z 0.25 $tmp_dir/z_12.png -z 0.34 $tmp_dir/z_13.png -z 0.42 $tmp_dir/z_14.png \
+	  -z 0.50 $tmp_dir/z_21.png -z 0.55 $tmp_dir/z_22.png -z 0.62 $tmp_dir/z_23.png -z 0.65 $tmp_dir/z_24.png \
+	  -z 0.70 $tmp_dir/z_31.png -z 0.73 $tmp_dir/z_32.png -z 0.78 $tmp_dir/z_33.png -z 0.82 $tmp_dir/z_34.png "
+
+	 eval $slicer_cmd
+	  
+	# append png files -x
+	  pngappend $tmp_dir/x_11.png + 2 $tmp_dir/x_12.png + 2 $tmp_dir/x_13.png $tmp_dir/x_1.png
+	  pngappend $tmp_dir/x_21.png + 2 $tmp_dir/x_22.png + 2 $tmp_dir/x_23.png $tmp_dir/x_2.png
+	  pngappend $tmp_dir/x_31.png + 2 $tmp_dir/x_32.png + 2 $tmp_dir/x_33.png $tmp_dir/x_3.png
+	  pngappend $tmp_dir/x_41.png + 2 $tmp_dir/x_42.png + 2 $tmp_dir/x_43.png $tmp_dir/x_4.png
+	  pngappend $tmp_dir/x_51.png + 2 $tmp_dir/x_52.png + 2 $tmp_dir/x_53.png $tmp_dir/x_5.png
+	  pngappend $tmp_dir/x_61.png + 2 $tmp_dir/x_62.png + 2 $tmp_dir/x_63.png $tmp_dir/x_6.png
+
+	  pngappend $tmp_dir/x_1.png + 1 $tmp_dir/x_2.png $tmp_dir/x1.png
+	  pngappend $tmp_dir/x_3.png + 1 $tmp_dir/x_4.png $tmp_dir/x2.png
+	  pngappend $tmp_dir/x_5.png + 1 $tmp_dir/x_6.png $tmp_dir/x3.png
+
+	  pngappend $tmp_dir/x1.png - 2 $tmp_dir/x2.png - 2 $tmp_dir/x3.png $tmp_dir/x.png
+
+	# append png files -y
+	  pngappend $tmp_dir/y_11.png + 2 $tmp_dir/y_12.png + 2 $tmp_dir/y_13.png + 2 $tmp_dir/y_14.png $tmp_dir/y_1.png
+	  pngappend $tmp_dir/y_21.png + 2 $tmp_dir/y_22.png + 2 $tmp_dir/y_23.png + 2 $tmp_dir/y_24.png $tmp_dir/y_2.png
+	  pngappend $tmp_dir/y_31.png + 2 $tmp_dir/y_32.png + 2 $tmp_dir/y_33.png + 2 $tmp_dir/y_34.png $tmp_dir/y_3.png
+
+	  pngappend $tmp_dir/y_1.png - 2 $tmp_dir/y_2.png - 2 $tmp_dir/y_3.png  $tmp_dir/y.png
+
+	# append png files -z
+	  pngappend $tmp_dir/z_11.png + 2 $tmp_dir/z_12.png + 2 $tmp_dir/z_13.png + 2 $tmp_dir/z_14.png $tmp_dir/z_1.png
+	  pngappend $tmp_dir/z_21.png + 2 $tmp_dir/z_22.png + 2 $tmp_dir/z_23.png + 2 $tmp_dir/z_24.png $tmp_dir/z_2.png
+	  pngappend $tmp_dir/z_31.png + 2 $tmp_dir/z_32.png + 2 $tmp_dir/z_33.png + 2 $tmp_dir/z_34.png $tmp_dir/z_3.png
+
+	  pngappend $tmp_dir/z_1.png - 2 $tmp_dir/z_2.png - 2 $tmp_dir/z_3.png $tmp_dir/z.png
+	
+	# append png files -xyz
+	  pngappend $tmp_dir/x.png - 2 $tmp_dir/y.png + 2 $tmp_dir/z.png $qc_dir/$qc_filename.png
+
+	rm -rf $tmp_dir
+
+	# To display the output image, type
+	# xdg-open foo.png 
+}
+
+#-----------------------------------
+# function: mas_masking
+# ----------------------------------
+function mas_masking(){
+	local function_name=${FUNCNAME[0]}
+	usage() {
+		echo ""
+		echo "Multi Atlas Segmentation - Preprocssing: Brain masking (brain extraction). Only affine transformation is used"
+		echo "Usage: $function_name [-T target_dir] [-t target_id] [-A atlas_dir] [-a atlas_id] [-r result_dir] [-p parameter_cfg]"
+		echo ""
+		return 1
+	}
+	
+	local OPTIND
+	local options
+	while getopts ":T:t:A:a:f:r:p:c:h" options; do
+		case $options in
+			T ) echo "Target directory: $OPTARG"
+				local target_dir=$OPTARG;;
+			t ) echo "Target ID: $OPTARG"
+				local target_id=$OPTARG;;
+			A ) echo "Atlas directory: $OPTARG"
+				local atlas_dir=$OPTARG;;
+			a ) echo "Atlas ID: $OPTARG"
+				local atlas_id=$OPTARG;;
+			f ) echo "Affine parameter: $OPTARG"
+				local affine_param=$OPTARG;;
+			r ) echo "Result directory: $OPTARG"
+				local result_dir=$OPTARG;;
+			p ) echo "Parameter config file: $OPTARG"
+				local parameter_cfg=$OPTARG;;
+			c ) echo "Cleanup flag: $OPTARG"
+				local cleanup_flag=$OPTARG;;
+			h ) usage; return 1;;
+			\?) echo "Unknown option"
+				usage; return 1;;
+			: ) usage; return 1;;
+		esac
+	done
+
+	if [[ $OPTIND -eq 1 ]]; then
+		echo "[$function_name] no option specified"
+		usage; return 1
+	fi 
+
+	# preload parameter_cfg file
+	if [[ ! -z $parameter_cfg ]]; then
+		source $parameter_cfg
+	fi
+
+	echo "[$function_name] begin mapping template $atlas_id to $target_id"
+	local error_flag=0
+
+	# check mapping input/output file existance 
+	# check_mapping_file $target_dir $target_id $atlas_dir $atlas_list $result_dir
+	# local file_tag=$?
+	# if [[ $file_tag -eq 2 ]]; then
+	# 	echo "[$function_name] Mapping file already exist: $target_dir/$target_id "
+	# 	return 0
+	# elif [[ $file_tag -ne 0 ]]; then
+	# 	echo "[$function_name] cannot find target ($target_id)"
+	# 	return 1
+	# fi
+
+	# checking atlas_file existance
+	check_atlas_file $atlas_dir $atlas_id
+	if [[ $? -ne 0 ]]; then
+		echo "[$function_name] cannot find atlas $atlas_file_type: $atlas_dir atlas_id: atlas_id"
+		return 1
+	fi
+
+	# eliminate .ext from id
+	# local target_id=$(echo $target_id | cut -d. -f1) # to avoid cases where there are dots ('.') in the filename
+	local atlas_id=$(echo $atlas_id | cut -d. -f1)
+
+	# creat folders
+	local atlas_name=$(basename $atlas_dir)
+	for subdir in tmp mapping mask label quickcheck; do
+		local subdir_path="$result_dir/$subdir/$atlas_name"
+		mkdir -p $subdir_path
+	done
+
+	local mapping_dir="$result_dir/mapping/$atlas_name"
+	local mask_dir="$result_dir/mask/$atlas_name"
+	local label_dir="$result_dir/label/$atlas_name"
+	local tmp_dir="$result_dir/tmp/$atlas_name"
+	mkdir -p $mapping_dir
+	mkdir -p $mask_dir
+	mkdir -p $label_dir
+	mkdir -p $tmp_dir
+
+	# generate affine matrix (atlas >> target), if no affine matrix found
+	if [[ -f $tmp_dir/$atlas_id.$target_id.aff ]]; then
+		echo "[$function_name] affine matrix already exist: $tmp_dir/$atlas_id.$target_id.aff, skipping affine step ..."
+	else
+		if [[ -z $affine_param ]]; then
+			local affine_param="" # "-rigOnly"
+		fi
+		affine_param="$affine_param -speeeeed" #  -ln 4 -lp 4
+		affine_param="$affine_param -flo $atlas_dir/template/$atlas_id"
+		affine_param="$affine_param -fmask $atlas_dir/mask/$atlas_id"
+		affine_param="$affine_param -ref $target_dir/$target_id"
+		affine_param="$affine_param -res $tmp_dir/$atlas_id.$target_id.aff.nii.gz"
+		affine_param="$affine_param -aff $tmp_dir/$atlas_id.$target_id.aff"
+
+		reg_aladin $affine_param
+	fi
+
+	# check if affine matrix file generated successfully
+	if [[ ! -f $tmp_dir/$atlas_id.$target_id.aff ]]; then
+		echo "[$function_name] failed to generate affine matrix file"
+		return 1
+	fi
+	# resample the label, as well as mask
+	local seg_file
+	for seg_file in mask; do
+		local result_file="$result_dir/$seg_file/$atlas_name/$target_id.$seg_file.$atlas_id.affine.nii.gz"
+		if [[ -f $result_file ]]; then
+			echo "[$function_name] ($target_id) $seg_file file already exist: $result_file, skipping ..."
+			continue
+		fi
+		local resamp_param=""
+		resamp_param="$resamp_param -flo $atlas_dir/$seg_file/$atlas_id"
+		resamp_param="$resamp_param -ref $target_dir/$target_id"
+		resamp_param="$resamp_param -trans $tmp_dir/$atlas_id.$target_id.aff"
+		resamp_param="$resamp_param -inter 0"
+		resamp_param="$resamp_param -res $result_file"
+		reg_resample $resamp_param 
+
+		# generating quickcheck for mask and label
+		mas_quickcheck $target_dir/$target_id $result_file $result_dir/quickcheck/$atlas_name $target_id.$seg_file.affine.$atlas_id
+	done
+
+	# remove tmp files
+	if [[ $cleanup_flag -eq 1 ]]; then
+		rm -rf $tmp_dir
+		error_flag=$?
+	fi
+
+	# unset parameters
+	unset affine_param
+
+	return $error_flag
+}
+
+#-----------------------------------
+# function: mas_mapping
+# ----------------------------------
+function mas_mapping(){
+	local function_name=${FUNCNAME[0]}
+	usage() {
+		echo ""
+		echo "Multi Atlas Segmentation - Part 1: Mapping"
+		echo "Usage: $function_name [-T target_dir] [-t target_id] [-m target_mask] [-A atlas_dir] [-a atlas_id] [-r result_dir] [-p parameter_cfg]"
+		echo ""
+		return 1
+	}
+	
+	local OPTIND
+	local options
+	while getopts ":T:t:m:A:a:r:p:c:h" options; do
+		case $options in
+			T ) echo "Target directory: $OPTARG"
+				local target_dir=$OPTARG;;
+			t ) echo "Target ID: $OPTARG"
+				local target_id=$OPTARG;;
+			m ) echo "Target mask: $OPTARG"
+				local target_mask=$OPTARG;;
+			A ) echo "Atlas directory: $OPTARG"
+				local atlas_dir=$OPTARG;;
+			a ) echo "Atlas ID: $OPTARG"
+				local atlas_id=$OPTARG;;
+			r ) echo "Result directory: $OPTARG"
+				local result_dir=$OPTARG;;
+			p ) echo "Parameter config file: $OPTARG"
+				local parameter_cfg=$OPTARG;;
+			c ) echo "Cleanup flag: $OPTARG"
+				local cleanup_flag=$OPTARG;;
+			h ) usage; return 1;;
+			\?) echo "Unknown option"
+				usage; return 1;;
+			: ) usage; return 1;;
+		esac
+	done
+
+	if [[ $OPTIND -eq 1 ]]; then
+		echo "[$function_name] no option specified"
+		usage; return 1
+	fi 
+
+	# preload parameter_cfg file
+	if [[ ! -z $parameter_cfg ]]; then
+		source $parameter_cfg
+	fi
+
+	echo "[mas_mapping] begin mapping template $atlas_id to $target_id"
+	local error_flag=0
+
+	# check mapping input/output file existance 
+	# check_mapping_file $target_dir $target_id $atlas_dir $atlas_list $result_dir
+	# local file_tag=$?
+	# if [[ $file_tag -eq 2 ]]; then
+	# 	echo "[mas_mapping] Mapping file already exist: $target_dir/$target_id "
+	# 	return 0
+	# elif [[ $file_tag -ne 0 ]]; then
+	# 	echo "[mas_mapping] cannot find target ($target_id)"
+	# 	return 1
+	# fi
+
+	# check target_mask existance
+	if [[ ! -z $target_mask ]]; then
+		check_image_file $target_mask
+		if [[ $? -ne 0 ]]; then
+			echo "[mas_mapping] cannot find target mask for ($target_id): $target_mask"
+			return 1
+		fi
+	# else # need to create target_mask first
+	# 	mas_masking -T $target_dir -t $target_id -A $atlas_dir -a $atlas_id -r $result_dir -p $parameter_cfg
+	# 	local target_mask = $result_dir/mask/$atlas_name/$target_id.mask.$atlas_id.affine.nii.gz
+	fi
+	# checking atlas_file existance
+	check_atlas_file $atlas_dir $atlas_id
+	if [[ $? -ne 0 ]]; then
+		echo "[mas_mapping] cannot find atlas $atlas_file_type: $atlas_dir atlas_id: $atlas_id"
+		return 1
+	fi
+
+	# eliminate .ext from id
+	# local target_id=$(echo $target_id | cut -d. -f1)
+	local atlas_id=$(echo $atlas_id | cut -d. -f1)
+
+	# creat folders
+	local atlas_name=$(basename $atlas_dir)
+	for subdir in tmp mapping mask label quickcheck; do
+		local subdir_path="$result_dir/$subdir/$atlas_name"
+		mkdir -p $subdir_path
+	done
+
+	local mapping_dir="$result_dir/mapping/$atlas_name"
+	local mask_dir="$result_dir/mask/$atlas_name"
+	local label_dir="$result_dir/label/$atlas_name"
+	local tmp_dir="$result_dir/tmp/$atlas_name"
+	mkdir -p $mapping_dir
+	mkdir -p $mask_dir
+	mkdir -p $label_dir
+	mkdir -p $tmp_dir
+
+	# generate affine matrix (atlas >> target), if no affine matrix found
+	if [[ -f $tmp_dir/$atlas_id.$target_id.aff ]]; then
+		echo "[$function_name] affine matrix already exist: $tmp_dir/$atlas_id.$target_id.aff, skipping affine step ..."
+	else
+		# load affine_param from parameter_cfg if specified
+		if [[ -z $affine_param ]]; then
+			local affine_param="" # "-rigOnly"
+		fi
+		affine_param="$affine_param -ln 4 -lp 4 -speeeeed" #
+		affine_param="$affine_param -flo $atlas_dir/template/$atlas_id"
+		affine_param="$affine_param -fmask $atlas_dir/mask/$atlas_id"
+		affine_param="$affine_param -ref $target_dir/$target_id"
+		affine_param="$affine_param -res $tmp_dir/$atlas_id.$target_id.aff.nii.gz"
+		affine_param="$affine_param -aff $tmp_dir/$atlas_id.$target_id.aff"
+		# use target mask if specified
+		if [[ ! -z $target_mask ]]; then
+			affine_param="$affine_param -rmask $target_mask -cog"
+		else
+			affine_param="$affine_param -rmask $target_mask -nac"
+		fi
+		reg_aladin $affine_param
+	fi
+
+	# generate nrr cpp (atlas >> target), if no cpp found
+	if [[ -f $tmp_dir/$atlas_id.$target_id.cpp.nii.gz ]]; then
+		echo "[$function_name] non-rigid control point already exist: $tmp_dir/$atlas_id.$target_id.cpp.nii.gz, skipping non-rigid step ..."
+	else
+		# check if affine matrix successfully generated
+		if [[ ! -f $tmp_dir/$atlas_id.$target_id.aff ]]; then
+			echo "[mas_mapping] failed to generate affine matrix"
+			return 1
+		fi
+		# load nrr_param from parameter_cfg if specified
+		if [[ -z $nrr_param ]]; then
+			local nrr_param="$nrr_param -vel -nogce -vel -smooF 0.04 -smooR 0.04 -ln 4 -lp 4" # -smooR 0.04 -ln 4 -lp 4
+		fi
+		nrr_param="$nrr_param -flo $atlas_dir/template/$atlas_id"
+		nrr_param="$nrr_param -fmask $atlas_dir/mask/$atlas_id"
+		nrr_param="$nrr_param -ref $target_dir/$target_id"
+		# use affine transform matrix to initialize non-rigid registration
+		nrr_param="$nrr_param -aff $tmp_dir/$atlas_id.$target_id.aff"
+		nrr_param="$nrr_param -res $mapping_dir/$atlas_id.$target_id.f3d.nii.gz"
+		nrr_param="$nrr_param -cpp $tmp_dir/$atlas_id.$target_id.cpp.nii.gz"
+
+		if [[ ! -z $target_mask ]] ; then
+			# use target mask if specified and exist
+			nrr_param="$nrr_param -rmask $target_mask"
+		fi
+		reg_f3d $nrr_param
+	fi
+
+	# check if cpp file generated successfully
+	if [[ ! -f $tmp_dir/$atlas_id.$target_id.cpp.nii.gz ]]; then
+		echo "[mas_mapping] failed to generate control point cpp file"
+		return 1
+	fi
+	# resample the label, as well as mask
+	local seg_file
+	for seg_file in label mask; do
+		local result_file="$result_dir/$seg_file/$atlas_name/$target_id.$seg_file.$atlas_id.nii.gz"
+		if [[ -f $result_file ]]; then
+			echo "[$function_name] ($target_id) $seg_file file already exist: $result_file, skipping ..."
+			continue
+		fi
+		local resamp_param=""
+		resamp_param="$resamp_param -flo $atlas_dir/$seg_file/$atlas_id"
+		resamp_param="$resamp_param -ref $target_dir/$target_id"
+		resamp_param="$resamp_param -cpp $tmp_dir/$atlas_id.$target_id.cpp.nii.gz"
+		resamp_param="$resamp_param -inter 0"
+		resamp_param="$resamp_param -res $result_file"
+		reg_resample $resamp_param
+
+		# generating quickcheck for mask and label
+		mas_quickcheck $target_dir/$target_id $result_file $result_dir/quickcheck/$atlas_name $target_id.$seg_file.$atlas_id
+	done
+
+	# remove tmp files
+	if [[ $cleanup_flag -eq 1 ]]; then
+		rm -rf $tmp_dir
+		error_flag=$?
+	fi
+
+	return $error_flag
+}
+
+#-----------------------------------
+# function: mas_fusion
+# ----------------------------------
+function mas_fusion(){
+	local function_name=${FUNCNAME[0]}
+	usage() {
+		echo ""
+		echo "Multi Atlas Segmentation - Part 2: Fusion"
+		echo "Usage: $function_name [-T target_dir] [-t target_id] [-A atlas_name] [-a atlas_list] [-r result_dir]"
+		echo "       (optional) [-m target_mask] [-p parameter_cfg] [-c cleanup_flag]"
+		echo ""
+		return 1
+	}
+
+	local OPTIND
+	local options
+	while getopts ":T:t:m:A:a:r:p:c:h" options; do
+		case $options in
+			T ) echo "Target directory: $OPTARG"
+				local target_dir=$OPTARG;;
+			t ) echo "Target ID: $OPTARG"
+				local target_id=$OPTARG;;
+			m ) echo "Target mask: $OPTARG"
+				local target_mask=$OPTARG;;
+			A ) echo "Atlas name: $OPTARG"
+				local atlas_name=$OPTARG;;
+			a ) echo "Atlas list: $OPTARG"
+				local atlas_list=$OPTARG;;
+			r ) echo "Result directory: $OPTARG"
+				local result_dir=$OPTARG;;
+			p ) echo "Parameter config file: $OPTARG"
+				local parameter_cfg=$OPTARG;;
+			c ) echo "Cleanup flag: $OPTARG"
+				local cleanup_flag=$OPTARG;;
+			h ) usage; return 1;;
+			\?) echo "Unknown option"
+				usage; return 1;;
+			: ) usage; return 1;;
+		esac
+	done
+
+	if [[ $OPTIND -eq 1 ]]; then
+		echo "[$function_name] no option specified"
+		usage; return 1
+	fi 
+
+	# local target_id=$(echo $target_id | cut -d. -f1)
+
+	local mapping_dir="$result_dir/mapping/$atlas_name"
+	local mask_dir="$result_dir/mask/$atlas_name"
+	local label_dir="$result_dir/label/$atlas_name"
+	local tmp_dir="$result_dir/tmp/$atlas_name"
+	local error_flag=0
+	local k=5
+	local n=8
+
+
+	# checking target_file existance
+	check_image_file $target_dir/$target_id
+	if [[ $? -ne 0 ]]; then
+		echo "[$function_name] cannot find target file $target_dir/$target_id"
+		return 1
+	fi
+
+	# by default, cleanup temporary files
+	if [[ -z $cleanup_flag ]]; then
+		local cleanup_flag=1
+	fi
+
+	# check if $atlas_list file exist
+	if [[ ! -f $atlas_list ]]; then
+		echo "[$function_name] Cannot find atlas list file at: $atlas_list"
+		return 1
+	fi
+
+	# check label fusion input file
+	check_label_fusion_file $target_dir $target_id $atlas_name $atlas_list $result_dir
+	local file_tag=$?
+	if [[ $file_tag -eq 2 ]]; then
+		echo "[$function_name] ($target_id) label fusion output file already exist, exiting ..."
+		return 0
+	elif [[ $file_tag -ne 0 ]]; then
+		echo "[$function_name] ($target_id) cannot find label fusion input file"
+		return 1
+	fi
+	# prepare parameters for label fusion
+	local atlas_no=0
+	for atlas_id in $(cat $atlas_list); do
+		if [[ $atlas_no -eq 0 ]]; then
+			local mapping_1="$mapping_dir/$atlas_id.$target_id.f3d.nii.gz"
+			local mask_1="$result_dir/mask/$atlas_name/$target_id.mask.$atlas_id.nii.gz"
+			local label_1="$result_dir/label/$atlas_name/$target_id.label.$atlas_id.nii.gz"
+		else
+			local mapping_n="$mapping_n $mapping_dir/$atlas_id.$target_id.f3d.nii.gz"
+			local mask_n="$mask_n $result_dir/mask/$atlas_name/$target_id.mask.$atlas_id.nii.gz"
+			local label_n="$label_n $result_dir/label/$atlas_name/$target_id.label.$atlas_id.nii.gz"
+		fi
+		let atlas_no+=1
+	done
+	let atlas_no-=1
+
+	# prepare 4D images for label fusion if not precomputed
+	local seg_file
+	for seg_file in mapping label; do
+		local merged_4d_file="$result_dir/$seg_file/$atlas_name/$target_id.4D.nii.gz"
+		if [[ -f $merged_4d_file ]]; then
+			echo "[$function_name] 4D $seg_file file exist: $merged_4d_file, skipping ..."
+		else
+			echo "[$function_name] prepare 4D $seg_file file for label fusion ..."
+			local merge_cmd="seg_maths \$${seg_file}_1 -merge $atlas_no 4 \$${seg_file}_n $merged_4d_file"
+			# echo $merge_command
+			eval $merge_cmd
+		fi
+	done
+
+	# label fusion
+	local labfusion_param="-unc -v 1"
+	labfusion_param="$labfusion_param -in $result_dir/label/$atlas_name/$target_id.4D.nii.gz"
+	labfusion_param="$labfusion_param -out $result_dir/label/$target_id.label.$atlas_name.nii.gz"
+	# labfusion_param="$labfusion_param -SBA"
+	labfusion_param="$labfusion_param -STEPS $k $n $target_dir/$target_id $result_dir/mapping/$atlas_name/$target_id.4D.nii.gz"
+	# use mask file reduce computational time for label fusion
+	local merged_4d_label="$label_dir/$atlas_name/$target_id.4D.nii.gz"
+	local merged_4d_mask="$mask_dir/$atlas_name/$target_id.4D.nii.gz"
+	if [[ ! -z $target_mask ]] && [[ -f $target_mask ]]; then
+		# use predifined mask 
+		labfusion_param="$labfusion_param -mask $target_mask"
+	elif [[ -f $merged_4d_label ]]; then
+		# use dilated mapped labels to generate mask (majority voting)
+		local targetmask_mv="$mask_dir/$target_id.mask.mv.nii.gz"
+		seg_maths $merged_4d_label -tmean -bin $mask_majority_voting
+		labfusion_param="$labfusion_param -mask $targetmask_mv"
+	fi
+	echo -e "\n [$function_name] fuse mapped template $atlas_name to parcellate $target_id ..."
+	seg_LabFusion $labfusion_param
+
+	# generate quickcheck for label
+	check_image_file $result_dir/label/$target_id.label.$atlas_name.nii.gz
+	if [[ $? -ne 0 ]]; then
+		echo "[$function_name] ($target_id) failed to generate label file: $result_dir/label/$target_id.label.$atlas_name.nii.gz , skip quickcheck ..."
+		return 1
+	fi
+	echo "[$function_name] generating label quickcheck for: $target_id ..."
+	mas_quickcheck $target_dir/$target_id $result_dir/label/$target_id.label.$atlas_name.nii.gz $result_dir/quickcheck/ $target_id.label.$atlas_name
+
+	# generate mask
+	seg_maths $result_dir/label/$target_id.label.$atlas_name.nii.gz -bin $result_dir/mask/$target_id.mask_f3d.$atlas_name.nii.gz
+	check_image_file $result_dir/mask/$target_id.mask_f3d.$atlas_name.nii.gz
+	if [[ $? -ne 0 ]]; then
+		echo "[$function_name] ($target_id) failed to generate mask file: $result_dir/mask/$target_id.mask_f3d.$atlas_name.nii.gz , skip quickcheck ..."
+		return 1
+	fi
+	# generate quickcheck for mask
+	echo "[$function_name] generating mask quickcheck for: $target_id ..."
+	mas_quickcheck $target_dir/$target_id $result_dir/mask/$target_id.mask_f3d.$atlas_name.nii.gz $result_dir/quickcheck/ $target_id.mask_f3d.$atlas_name
+
+	# remove tmp files
+	return 0
+}
+
+# ------------------------------
+# function: mas_masking_batch
+# -------------------------------
+function mas_masking_batch(){
+	# printout function help 
+	local function_name=${FUNCNAME[0]}
+	usage() {
+		echo ""
+		echo "Multi Atlas Segmentation - Part 0: Masking (pbs generater)"
+		echo "Usage: $function_name [-T target_dir] [-t target_list] [-A atlas_dir] [-r result_dir]"
+		echo "       (optional) [-a atlas_list] [-p parameter_cfg] [-e execution mode (cluster/local)]"
+		echo "       for [-e] option: (cluster) will submit parallel pbs jobs to cluster; (local) will run job sequentially on local machine. cluster is set by default"
+		echo ""
+		return 1
+	}
+	# get options
+	local OPTIND
+	local options
+	while getopts ":T:t:M:s:f:A:a:r:e:p:h:" options; do
+		case $options in
+			T ) echo "Target directory:      $OPTARG"
+				local target_dir=$OPTARG;;
+			t ) echo "Target list:           $OPTARG"
+				local target_list=$OPTARG;;
+			M ) echo "Target mask directory: $OPTARG"
+				local targetmask_dir=$OPTARG;;
+			f ) echo "Target mask suffix:    $OPTARG"
+				local targetmask_suffix=$OPTARG;;
+			A ) echo "Atlas directory:       $OPTARG"
+				local atlas_dir=$OPTARG;;
+			a ) echo "Atlas list:            $OPTARG"
+				local atlas_list=$OPTARG;;
+			r ) echo "Result directory:      $OPTARG"
+				local result_dir=$OPTARG;;
+			e ) echo "Execution mode:        $OPTARG"
+				local exe_mode=$OPTARG;;
+			p ) echo "Parameter config file: $OPTARG"
+				local parameter_cfg=$OPTARG;;
+			h ) usage; return 1;;
+			: ) usage; return 1;;
+			\?) echo "Unknown option"
+				usage; return 1;;
+		esac
+	done
+
+	# check input option integrity
+	if [[ $OPTIND -eq 1 ]]; then
+		echo "[$function_name] no option specified"
+		return 1
+	fi 
+	# set default exe_mode to cluster
+	if [[ -z $exe_mode ]]; then
+		exe_mode="cluster"
+	fi
+
+	# pre-define default atlas_list if not specified
+	if [[ -z $atlas_list ]]; then
+		atlas_list=$atlas_dir/$AtlasListFileName
+	fi
+
+	local atlas_name=$(basename $atlas_dir)
+	echo " ========================="
+	echo "[$function_name] creating mask with Atlas: $atlas_name ..."
+	echo "target_dir=$target_dir"
+	echo "target_list=$target_list"
+	echo "result_dir=$result_dir"
+	echo "atlas_dir=$atlas_dir"
+	echo "atlas_name=$atlas_name"
+	echo "atlas_list=$atlas_list"
+	echo "exe_mode=$exe_mode"
+	echo " ========================="
+	
+	seg_file="mask" # for backward compatibility purpose, can delete
+
+	# if in cluster mode, generate pbs related parameters/folders
+	if [[ "$exe_mode" == "cluster" ]]; then
+		local PBS_DIR=$result_dir/pbs
+		local LOG_DIR=$result_dir/log
+		local JOB_DIR=$result_dir/job
+		local MEM="4gb"
+		local WALLTIME="12:00:00"
+		local MEM_MAPPING="4gb"
+		local MEM_FUSION="8gb"
+		local WALLTIME_MAPPING="12:00:00"
+		local WALLTIME_FUSION="4:00:00"
+
+		mkdir -p $PBS_DIR
+		mkdir -p $LOG_DIR
+		mkdir -p $JOB_DIR
+
+		local jid="${RANDOM}_mapping" # generate random number as job ID, alternatively, use: $$
+		local job_list=$JOB_DIR/$USER.$jid.$(date +%y%m%d%H%M%S).txt
+		rm -f $job_list
+	fi
+
+	# start individual masking
+	local target_id
+	for target_id in $(cat $target_list); do
+		# start individual masking
+		echo "[$function_name] affine masking for target: ($target_id) ..."
+		if [[ "$exe_mode" == "local" ]]; then	
+			########## mapping step #################
+			for atlas_id in $(cat $atlas_list); do
+				# check mapping output file existance
+				local result_file="$result_dir/mask/$atlas_name/$target_id.$seg_file.$atlas_id.affine.nii.gz"
+				if [[ -f $result_file ]]; then
+					echo "[$function_name] ($target_id) mask file already exist for atlas ($atlas_id): $result_file, skipping ..."
+					continue
+				fi
+				local masking_cmd="mas_masking -T $target_dir -t $target_id -A $atlas_dir -a $atlas_id -r $result_dir"
+				if [[ ! -z $parameter_cfg ]]; then
+					masking_cmd="$masking_cmd -p $parameter_cfg"
+				fi
+				eval $masking_cmd
+			done
+			########## label fusion step ############
+			echo -e "\n [$function_name] run label fusion locally for target: $target_id ... \n"
+			mas_masking_fusion $target_dir $target_id $result_dir $atlas_dir $atlas_list
+			# potential final step: cleanup unwanted files
+		elif [[ "$exe_mode" == "cluster" ]]; then
+			local job_name=$jid.$target_id
+			local pbs_file=$PBS_DIR/$job_name.mapping.pbs
+			local log_file=$PBS_DIR/$job_name.mapping.log
+			# clean up files if pre-exist
+			rm -f $pbs_file
+			rm -f $log_file
+			# add pbs header info
+			pbsBoilerPlate -n $job_name -m $MEM_MAPPING -w $WALLTIME_MAPPING -j -O $log_file -f $pbs_file # -O /dev/null
+			# calculate atlas number for job array
+			readarray atlas_array < $atlas_list
+			local atlas_no=$(( ${#atlas_array[@]} -1 ))
+			# determine the unmapped atlas to be added to pbs job array
+			local pbs_array_prefix="#PBS -t "
+			for ((idx=0;idx<=$atlas_no;idx+=1)); do
+				atlas_id=${atlas_array[$idx]}
+				# if output files already exist, don't include from job array
+				local result_file="$result_dir/mask/$atlas_name/$target_id.$seg_file.$atlas_id.affine.nii.gz"
+				if [[ ! -f $result_file ]]; then
+					pbs_array_prefix="${pbs_array_prefix}$idx"
+					# add comma if not the last atlas
+					if [[ $idx -ne $atlas_no ]]; then
+						pbs_array_prefix="${pbs_array_prefix},"
+					fi
+				fi
+			done
+			# add lines to determine atlas_id from ${PBS_ARRAYID}
+			echo "$pbs_array_prefix" >> $pbs_file
+			echo "source $mas_script_path" >> $pbs_file
+			echo "readarray atlas_array < $atlas_list" >> $pbs_file
+			echo "atlas_id=\${atlas_array[\${PBS_ARRAYID}]}" >> $$pbs_file
+			local masking_cmd="mas_masking -T $target_dir -t $target_id -A $atlas_dir -a $atlas_id -r $result_dir"
+			if [[ ! -z $parameter_cfg ]]; then
+				masking_cmd="$masking_cmd -p $parameter_cfg"
+			fi
+			echo -e "$masking_cmd" >> $pbs_file
+			# potential final step: cleanup unwanted files
+
+			# submit pbs job and store at joblist
+			qsub $pbs_file
+			echo "1,qsub $pbs_file" >> $job_list
+
+			############# label fusion step #############
+			local job_name_fusion=$jid.$target_id.fusion
+			local pbs_file=$PBS_DIR/$job_name_fusion.pbs
+			local log_file=$LOG_DIR/$job_name_fusion.log
+			pbsBoilerPlate -n $job_name_fusion -m $MEM_FUSION -w $WALLTIME_FUSION -j -O $log_file -f $pbs_file # -O /dev/null
+			if [[ ! -z $job_id_mapping ]]; then
+				# if mapping job array submitted, then add job dependency
+				echo "#PBS -W depend=afterokarray:$job_id_mapping" >> $pbs_file
+			else
+				local labfusion_mask=$result_dir/mask/$target_id.mask.$atlas_name.nii.gz
+				if [[ -f $labfusion_mask ]]; then
+					# all mapping already done previously, start fusion step without dependency
+					echo "[$function_name] ($target_id): fusion maps without mapping job array dependency ..."
+				fi
+
+				echo "source $mas_script_path" >> $pbs_file
+				echo "mas_masking_fusion $target_dir $target_id $result_dir $atlas_dir $atlas_list" >> $pbs_file
+				# submit pbs job and store at joblist
+				qsub $pbs_file
+				echo "1,qsub $pbs_file" >> $job_list
+			fi
+		fi
+	done
+
+	if [[ "$exe_mode" == "cluster" ]]; then
+		if [[ -f $job_list ]]; then
+			echo -e "\n [$function_name] joblist created under: $job_list \n"
+		else
+			echo -e "\n [$function_name] no job submitted \n"
+		fi
+	fi
+}
+
+function mas_masking_fusion(){
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 3 ]]; then
+		echo "Usage: $function_name [target_dir] [target_id] [result_dir] atlas_dir] [(optional) atlas_list]"
+		return 1
+	fi
+
+	local target_dir=$1
+	local target_id=$2
+	local result_dir=$3
+
+	local atlas_dir=$4
+	local atlas_list
+
+
+	if [[ ! -z $5 ]]; then
+		atlas_list=$5
+	else
+		# pre-define default atlas_list if not specified
+		atlas_list="$atlas_dir/$AtlasListFileName"
+	fi
+
+	local atlas_name=$(basename $atlas_dir)
+
+	echo "target_dir=$target_dir"
+	echo "target_id=$target_id"
+	echo "result_dir=$result_dir"
+	echo "atlas_dir=$atlas_dir"
+	echo "atlas_name=$atlas_name"
+
+	mask_dir="$result_dir/mask/$atlas_name"
+	
+	local mask_n=""
+	# prepare parameters for label fusion
+	local atlas_no=0
+	# cloud process need to run on rcg-queen
+	for atlas_id in $(cat $atlas_list); do
+		if [[ $atlas_no -eq 0 ]]; then
+			local mask_1="$mask_dir/$target_id.mask.$atlas_id.affine.nii.gz"
+		else
+			mask_n="$mask_n $mask_dir/$target_id.mask.$atlas_id.affine.nii.gz"
+		fi
+		let atlas_no+=1
+		# mas_masking -T $target_dir -t $target_id -A $atlas_dir -a $atlas_id -r $result_dir
+	done
+	let atlas_no-=1
+	# prepare 4D images for label fusion if not precomputed
+	local merged_4d_mask="$mask_dir/$target_id.4D.nii.gz"
+	if [[ -f $merged_4d_mask ]]; then
+		echo "[$function_name] 4D mask(affine) exist: $merged_4d_mask, skipping ..."
+	else
+		echo "[$function_name] prepare 4D mask file $merged_4d_mask for label fusion ..."
+		local merge_cmd="seg_maths \$mask_1 -merge $atlas_no 4 \$mask_n $merged_4d_mask"
+		eval $merge_cmd
+	fi
+	# start brain mask label fusion
+	labfusion_mask=$result_dir/mask/$target_id.mask.$atlas_name.nii.gz
+
+	# check if brain mask label fusion has already been created
+	check_image_file $labfusion_mask
+	if [[ $? -eq 0 ]]; then
+		echo "[$function_name] ($target_id) brain mask already exist: $labfusion_mask, skip label fusion ..."
+		return 0
+	fi
+
+	local labfusion_param="-unc -v 1"
+	labfusion_param="$labfusion_param -STAPLE "
+	labfusion_param="$labfusion_param -in $merged_4d_mask"
+	labfusion_param="$labfusion_param -out $labfusion_mask"
+	echo -e "\n [$function_name] fuse mapped mask from $atlas_name for $target_id"
+	seg_LabFusion $labfusion_param
+
+	# generate quickcheck for label
+	check_image_file $labfusion_mask
+	if [[ $? -ne 0 ]]; then
+		echo "[$function_name] ($target_id) failed to generate mask file: $labfusion_mask, skip quickcheck ..."
+		return 1
+	fi
+	echo "[$function_name] generating mask (affine) quickcheck for: $target_id ..."
+	mas_quickcheck $target_dir/$target_id $labfusion_mask $result_dir/quickcheck/ $target_id.mask.$atlas_name
+}
+
+function mas_masking_fusion_batch(){
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 3 ]]; then
+		echo "Usage: $function_name [target_dir] [target_list] [result_dir] [(optional) atlas_dir]"
+		return 1
+	fi
+
+	local target_dir=$1
+	local target_list=$2
+	local result_dir=$3
+	if [[ ! -z $4 ]]; then
+		atlas_dir=$4
+	else # default $atlas_dir
+		atlas_dir="/ensc/NEWTON5/STUDENTS/DMA73/Dropbox/Documents/SFU/Projects/BORG/Mouse_Brain_Atlas/NeAt_ex_vivo_LR"
+	fi
+
+	echo "target_dir=$target_dir"
+	echo "target_list=$target_list"
+	echo "result_dir=$result_dir"
+	echo "atlas_dir=$atlas_dir"
+
+	local target_id
+
+	for target_id in $(cat $target_list); do
+		# $$AtlasListFileName=template_list.cfg
+		mas_masking_fusion $target_dir $target_id $result_dir $atlas_dir
+	done
+}
+
+function mas_mapping_batch(){
+	# printout function help 
+	local function_name=${FUNCNAME[0]}
+	usage() {
+		echo ""
+		echo "Multi Atlas Segmentation - Part 1: Mapping (pbs generater)"
+		echo "Usage: $function_name [-T target_dir] [-t target_list] [-A atlas_dir] [-r result_dir]"
+		echo "       (optional) [-M targetmask_dir] [-f targetmask_suffix] [-a atlas_list] [-p parameter_cfg] [-e execution mode (cluster/local)]"
+		echo "       for [-e] option: (cluster) will submit parallel pbs jobs to cluster; (local) will run job sequentially on local machine. cluster is set by default"
+		echo ""
+		return 1
+	}
+	# get options
+	local OPTIND
+	local options
+	while getopts ":T:t:M:s:f:A:a:r:e:p:h:" options; do
+		case $options in
+			T ) echo "Target directory:      $OPTARG"
+				local target_dir=$OPTARG;;
+			t ) echo "Target list:           $OPTARG"
+				local target_list=$OPTARG;;
+			M ) echo "Target mask directory: $OPTARG"
+				local targetmask_dir=$OPTARG;;
+			f ) echo "Target mask suffix:    $OPTARG"
+				local targetmask_suffix=$OPTARG;;
+			A ) echo "Atlas directory:       $OPTARG"
+				local atlas_dir=$OPTARG;;
+			a ) echo "Atlas list:            $OPTARG"
+				local atlas_list=$OPTARG;;
+			r ) echo "Result directory:      $OPTARG"
+				local result_dir=$OPTARG;;
+			e ) echo "Execution mode:        $OPTARG"
+				local exe_mode=$OPTARG;;
+			p ) echo "Parameter config file: $OPTARG"
+				local parameter_cfg=$OPTARG;;
+			h ) usage; return 1;;
+			: ) usage; return 1;;
+			\?) echo "Unknown option"
+				usage; return 1;;
+		esac
+	done
+
+	# check input option integrity
+	if [[ $OPTIND -eq 1 ]]; then
+		echo "[$function_name] no option specified"
+		return 1
+	fi 
+
+	# predefine some essential local variable
+	local target_id
+	local atlas_id
+	local mas_mapping_param
+	
+	# check necessary input
+	if [[ -z $result_dir ]]; then
+		echo "[$function_name] result directory not specified"
+		return 1
+	fi
+	if [[ -z $atlas_dir ]]; then
+		echo "[$function_name] atlas directory not specified"
+		return 1
+	else
+		local atlas_name=$(basename $atlas_dir)
+	fi
+	if [[ ! -f $target_list ]]; then
+		echo "[$function_name] cannot find target list: $target_list"
+		return 1
+	fi
+	# set default $atlas_list if not defined
+	if [[ -z $atlas_list ]]; then
+		local atlas_list=$atlas_dir/$AtlasListFileName
+	fi
+	# check if $atlas_list file exist
+	if [[ ! -f $atlas_list ]]; then
+		echo "[$function_name] Cannot find atlas list file at: $atlas_list"
+		return 1
+	fi
+	# set execution mode as cluster by default
+	if [[ -z "$exe_mode" ]]; then
+		local exe_mode="cluster"
+	fi
+	if [[ "$exe_mode" != "cluster" ]] && [[ "$exe_mode" != "local" ]]; then
+		echo "[$function_name] \$exe_mode should be either \"cluster\" or \"local\" "
+		return 1
+	fi
+
+	# check atlas file
+	local atlas_name=$(basename $atlas_dir)
+	for atlas_id in $(cat $atlas_list);do
+		check_atlas_file $atlas_dir $atlas_id
+		if [[ $? -ne 0 ]]; then
+			echo "[$function_name] atlas ($atlas_name: $atlas_id) file missing, exiting ..."
+			return 1
+		fi
+	done
+
+	# if in cluster mode, generate pbs related parameters/folders
+	if [[ "$exe_mode" == "cluster" ]]; then
+		local PBS_DIR=$result_dir/pbs
+		local LOG_DIR=$result_dir/log
+		local JOB_DIR=$result_dir/job
+		local MEM="4gb"
+		local WALLTIME="12:00:00"
+
+		mkdir -p $PBS_DIR
+		mkdir -p $LOG_DIR
+		mkdir -p $JOB_DIR
+
+		local jid="${RANDOM}_mapping" # generate random number as job ID, alternatively, use: $$
+		local job_list=$JOB_DIR/$USER.$jid.$(date +%y%m%d%H%M%S).txt
+		rm -f $job_list
+	fi
+
+	# start mapping loop
+	for target_id in $(cat $target_list); do
+		# check mapping input/output file existance
+		check_mapping_file $target_dir $target_id $atlas_dir $atlas_list $result_dir
+		local file_exist=$?
+		if [[ $file_exist -eq 2 ]]; then
+			echo "[$function_name] output for target $target_id exist, skipping ..."
+			continue
+		elif [[ $file_exist -ne 0 ]]; then
+			echo "[$function_name] target $target_id file missing, skipping ..."
+			continue
+		fi
+		# star individual mapping
+		echo "[$function_name] target ($target_id) file checking success, start mapping .."
+		if [[ "$exe_mode" == "local" ]]; then
+			for atlas_id in $(cat $atlas_list); do
+				mas_mapping_param=""
+				mas_mapping_param="$mas_mapping_param -T $target_dir"
+				mas_mapping_param="$mas_mapping_param -t $target_id"
+				mas_mapping_param="$mas_mapping_param -A $atlas_dir"
+				mas_mapping_param="$mas_mapping_param -a $atlas_id"
+				mas_mapping_param="$mas_mapping_param -r $result_dir"
+				# Add Optional parameters if specified
+				if [[ ! -z $targetmask_dir ]]; then
+					# targetmask existance already checked
+					mas_mapping_param="$mas_mapping_param -m $targetmask_dir/$target_id$targetmask_suffix"
+				fi
+				if [[ ! -z $parameter_cfg ]]; then
+					mas_mapping_param="$mas_mapping_param -p $parameter_cfg"
+				fi
+				if [[ ! -z $cleanup_flag ]]; then
+					mas_mapping_param="$mas_mapping_param -c $cleanup_flag"
+				fi
+
+				mas_mapping $mas_mapping_param
+			done
+			# potential final step: cleanup unwanted files
+		elif [[ "$exe_mode" == "cluster" ]]; then
+			local job_name=$jid.$target_id
+			local pbs_file=$PBS_DIR/$job_name.pbs
+			local log_file=$LOG_DIR/$job_name.log
+			# clean up files if pre-exist
+			rm -f $pbs_file
+			rm -f $log_file
+			# add pbs header info
+			pbsBoilerPlate -n $job_name -m $MEM -w $WALLTIME -j -O $log_file -f $pbs_file # -O /dev/null
+			# calculate atlas number for job array
+			readarray atlas_array < $atlas_list
+			local atlas_no=$(( ${#atlas_array[@]} - 1 ))
+			# determine the unmapped atlas to be added to pbs job array
+			local pbs_array_prefix="#PBS -t " 
+			for ((idx=0;idx<=$atlas_no;idx+=1));do
+				atlas_id=${atlas_array[$idx]}
+				# if output files already exist, don't include from job array
+				if [[ ! -f "$result_dir/label/$atlas_name/$target_id.label.$atlas_id.nii.gz" ]] || \
+				   [[ ! -f "$result_dir/mask/$atlas_name/$target_id.mask.$atlas_id.nii.gz" ]] || \
+				   [[ ! -f "$result_dir/mapping/$atlas_name/$atlas_id.$target_id.f3d.nii.gz" ]]; then
+				   pbs_array_prefix="${pbs_array_prefix}$idx"
+				   # add comma if not the last atlas
+				   if [[ $idx -ne $atlas_no ]]; then
+						pbs_array_prefix="${pbs_array_prefix},"
+				   fi
+				fi
+			done
+			# add lines to determine atlas_id from ${PBS_ARRAYID}
+			echo "$pbs_array_prefix" >> $pbs_file
+			echo "source $mas_script_path" >> $pbs_file
+			echo "readarray atlas_array < $atlas_list" >> $pbs_file
+			echo "atlas_id=\${atlas_array[\${PBS_ARRAYID}]}" >> $pbs_file
+		
+			mas_mapping_param=""
+			mas_mapping_param="$mas_mapping_param -T $target_dir"
+			mas_mapping_param="$mas_mapping_param -t $target_id"
+			mas_mapping_param="$mas_mapping_param -A $atlas_dir"
+			mas_mapping_param="$mas_mapping_param -a \$atlas_id"
+			mas_mapping_param="$mas_mapping_param -r $result_dir"
+			
+			# Add Optional parameters if specified
+			if [[ ! -z $targetmask_dir ]]; then
+				local targetmask_name="$targetmask_dir/$target_id$targetmask_suffix"
+				check_image_file $targetmask_name
+				if [[ $? -ne 0 ]]; then
+					echo "[$function_name] cannot find target mask $targetmask_name"
+
+				fi
+				mas_mapping_param="$mas_mapping_param -m $targetmask_dir/$target_id$targetmask_suffix"
+			fi
+			if [[ ! -z $parameter_cfg ]]; then
+				mas_mapping_param="$mas_mapping_param -p $parameter_cfg"
+			fi
+			if [[ ! -z $cleanup_flag ]]; then
+				mas_mapping_param="$mas_mapping_param -c $cleanup_flag"
+			fi
+
+			echo -e "mas_mapping $mas_mapping_param" >> $pbs_file
+			# potential final step: cleanup unwanted files
+
+			# submit pbs job and store at joblist 
+			qsub $pbs_file
+			echo "1,qsub $pbs_file" >> $job_list
+		fi
+	done
+
+	if [[ "$exe_mode" == "cluster" ]]; then
+		if [[ -f $job_list ]]; then
+			echo -e "\n [$function_name] joblist created under: $job_list \n"
+		else
+			echo -e "\n [$function_name] all output exist, no job submitted \n"
+		fi
+	fi
+}
+
+#-----------------------------------
+# function: mas_fusion_batch
+# ----------------------------------
+function mas_fusion_batch(){
+	# printout function help 
+	local function_name=${FUNCNAME[0]}
+	usage() {
+		echo ""
+		echo "Multi Atlas Segmentation - Part 2: fusion (pbs generater)"
+		echo "Usage: $function_name [-T target_dir] [-t target_list] [-A atlas_name] [-a atlas_list] [-r result_dir]"
+		echo "                       (optional) [-M targetmask_dir] [-m targetmask_suffix] [-p parameter_cfg] [-c cleanup_flag] [-e execution mode (cluster/local)]"
+		echo "       for [-e] option: (cluster) will submit parallel pbs jobs to cluster; (local) will run job sequentially on local machine. cluster is set by default"
+		echo ""
+		return 1
+	}
+	# get options
+	local OPTIND
+	local options
+	while getopts "T:t:M:m:A:a:r:p:c:e:h" options; do
+		case $options in
+			T ) echo "Target directory:      $OPTARG"
+				local target_dir=$OPTARG;;
+			t ) echo "Target list:           $OPTARG"
+				local target_list=$OPTARG;;
+			M ) echo "Target mask directory: $OPTARG"
+				local targetmask_dir=$OPTARG;;
+			m ) echo "Target mask suffix:    $OPTARG"
+				local targetmask_suffix=$OPTARG;;
+			A ) echo "Atlas name:            $OPTARG"
+				local atlas_name=$OPTARG;;
+			a ) echo "Atlas list:            $OPTARG"
+				local atlas_list=$OPTARG;;
+			r ) echo "Result directory:      $OPTARG"
+				local result_dir=$OPTARG;;
+			e ) echo "Execution mode:        $OPTARG"
+				local exe_mode=$OPTARG;;
+			p ) echo "Parameter config file: $OPTARG"
+				local parameter_cfg=$OPTARG;;
+			c ) echo "Cleanup flag:          $OPTARG"
+				local cleanup_flag=$OPTARG;;
+			h ) usage;;
+			\?) echo "Unknown option"
+				usage
+				return 1;;
+		esac
+	done
+	# check necessary input
+	if [[ $OPTIND -eq 1 ]]; then
+		echo "[$function_name] no option specified"
+		usage; return 1
+	fi 
+	if [[ -z $result_dir ]]; then
+		echo "[$function_name] result directory not specified"
+		return 1
+	fi
+	if [[ ! -f $target_list ]]; then
+		echo "[$function_name] cannot find target list: $target_list"
+		return 1
+	fi
+	# check if $atlas_list file exist
+	if [[ ! -f $atlas_list ]]; then
+		echo "[$function_name] Cannot find atlas list file at: $atlas_list"
+		return 1
+	fi
+	# set execution mode as cluster by default
+	if [[ -z "$exe_mode" ]]; then
+		local exe_mode="cluster"
+	fi
+	if [[ "$exe_mode" != "cluster" ]] && [[ "$exe_mode" != "local" ]]; then
+		echo "[$function_name] \$exe_mode should be either \"cluster\" or \"local\" "
+		return 1
+	fi
+
+	# if in cluster mode, generate pbs related parameters/folders
+	if [[ "$exe_mode" == "cluster" ]]; then
+		local PBS_DIR=$result_dir/pbs
+		local LOG_DIR=$result_dir/log
+		local JOB_DIR=$result_dir/job
+		local MEM="4gb"
+		local WALLTIME="3:00:00"
+
+		mkdir -p $PBS_DIR
+		mkdir -p $LOG_DIR
+		mkdir -p $JOB_DIR
+
+		local jid="${RANDOM}_fusion" # generate random number as job ID, alternatively, use: $$
+		local job_list=$JOB_DIR/$USER.$jid.$(date +%y%m%d%H%M%S).txt
+		rm -f $job_list
+	fi
+
+	local target_id
+	local error_flag=0
+
+	for target_id in $(cat $target_list); do
+		# check input/output files
+		check_label_fusion_file $target_dir $target_id $atlas_name $atlas_list $result_dir
+		local label_fusion_file_exist=$?
+		if [[ $label_fusion_file_exist -eq 2 ]]; then
+			echo "[$function_name] ($target_id) output file exist, skipping ..."
+			continue
+		elif [[ $label_fusion_file_exist -ne 0 ]]; then
+			echo "[$function_name] ($target_id) input mapping file missing, skipping ..."
+			continue
+		fi
+		# pass file checking stage
+		echo "[$function_name] Input checking success, start label fusion for $target_id ..."
+
+		# preparing label fusion parameter for $target_id
+		local mas_fusion_param=""
+		mas_fusion_param="$mas_fusion_param -T $target_dir"
+		mas_fusion_param="$mas_fusion_param -t $target_id"
+		mas_fusion_param="$mas_fusion_param -A $atlas_name"
+		mas_fusion_param="$mas_fusion_param -a $atlas_list"
+		mas_fusion_param="$mas_fusion_param -r $result_dir"
+		# Add Optional parameters if specified
+		if [[ ! -z $targetmask_dir ]]; then
+			target_mask=$targetmask_dir/$target_id$targetmask_suffix
+			mas_fusion_param="$mas_fusion_param -m $target_mask"
+		fi
+		if [[ ! -z $parameter_cfg ]]; then
+			mas_fusion_param="$mas_fusion_param -p $parameter_cfg"
+		fi
+		if [[ ! -z $cleanup_flag ]]; then
+			mas_fusion_param="$mas_fusion_param -c $cleanup_flag"
+		fi
+
+		# start label fusion based on the $exe_mode
+		if [[ "$exe_mode" == "local" ]]; then
+			echo "[$function_name] run label fusion locally"
+			fusion_cmd="mas_fusion $mas_fusion_param"
+			eval $fusion_cmd
+		elif [[ "$exe_mode" == "cluster" ]]; then
+			local job_name=$jid.$target_id
+			local pbs_file=$PBS_DIR/$job_name.pbs
+			local log_file=$LOG_DIR/$job_name.log
+			pbsBoilerPlate -n $job_name -m $MEM -w $WALLTIME -j -O $log_file -f $pbs_file # -O /dev/null
+			echo "source $mas_script_path" >> $pbs_file
+			echo -e "mas_fusion $mas_fusion_param" >> $pbs_file
+			# submit pbs job and store at joblist 
+			qsub $pbs_file
+			echo "1,qsub $pbs_file" >> $job_list
+		fi
+	done
+
+	if [[ "$exe_mode" == "cluster" ]]; then
+		if [[ -f $job_list ]]; then
+			echo -e "\n [mas_fusion_pbs] joblist created under: $job_list \n"
+		else
+			echo -e "\n [mas_fusion_pbs] no job to submit \n"
+		fi
+	fi
+	
+	# potential final step: cleanup unwanted files
+	return $error_flag
+}
+
+#-----------------------------------
+# function: mas_parcellation_batch
+# ----------------------------------
+function mas_parcellation_batch(){
+	# printout function help 
+	local function_name=${FUNCNAME[0]}
+	usage() {
+		echo ""
+		echo "Multi Atlas Segmentation - Part 1+2 pipeline: multi-atlas parcellation (= mapping + label fusion)"
+		echo "Usage: $function_name [-T target_dir] [-t target_list] [-A atlas_dir] [-r result_dir]"
+		echo "                       (optional) [-M targetmask_dir] [-m targetmask_suffix] [-a atlas_list] [-p parameter_cfg] [-c cleanup_flag] [-e execution mode (cluster/local)] [-y cluster_type (SGE/SLURM) SGE will be used as default if not specified"
+		echo "       for [-e] option: (cluster) will submit parallel pbs jobs to cluster; (local) will run job sequentially on local machine. cluster is set by default"
+		echo ""
+		return 1
+	}
+	# get options
+	local OPTIND
+	local options
+	while getopts "T:t:M:m:A:a:r:y:p:c:e:h" options; do
+		case $options in
+			T ) echo "Target directory:      $OPTARG"
+				local target_dir=$OPTARG;;
+			t ) echo "Target list:           $OPTARG"
+				local target_list=$OPTARG;;
+			M ) echo "Target mask directory: $OPTARG"
+				local targetmask_dir=$OPTARG;;
+			m ) echo "Target mask suffix:    $OPTARG"
+				local targetmask_suffix=$OPTARG;;
+			A ) echo "Atlas directory:       $OPTARG"
+				local atlas_dir=$OPTARG;;
+			a ) echo "Atlas list:            $OPTARG"
+				local atlas_list=$OPTARG;;
+			r ) echo "Result directory:      $OPTARG"
+				local result_dir=$OPTARG;;
+			e ) echo "Execution mode:        $OPTARG"
+				local exe_mode=$OPTARG;;
+			y ) echo "Cluster Type:          $OPTARG"
+				local cluster_type=$OPTARG;;
+			p ) echo "Parameter config file: $OPTARG"
+				local parameter_cfg=$OPTARG;;
+			c ) echo "Cleanup flag:          $OPTARG"
+				local cleanup_flag=$OPTARG;;
+			h ) usage;;
+			\?) echo "Unknown option"
+				usage
+				return 1;;
+		esac
+	done
+
+	# check necessary input
+	if [[ $OPTIND -eq 1 ]]; then
+		echo "[$function_name] no option specified"
+		usage; return 1
+	fi 
+
+	if [[ -z $atlas_dir ]]; then
+		echo "[$function_name] no Atlas Directory specified"
+		return 1
+	fi
+	local atlas_name=$(basename $atlas_dir)
+
+	# check necessary input
+	if [[ $OPTIND -eq 1 ]]; then
+		echo "[$function_name] no option specified"
+		usage; return 1
+	fi 
+	if [[ -z $result_dir ]]; then
+		echo "[$function_name] result directory not specified"
+		return 1
+	fi
+	if [[ ! -f $target_list ]]; then
+		echo "[$function_name] cannot find target list: $target_list"
+		return 1
+	fi
+	# set default $atlas_list if not defined
+	if [[ -z $atlas_list ]]; then
+		local atlas_list=$atlas_dir/$AtlasListFileName
+	fi
+	# check if $atlas_list file exist
+	if [[ ! -f $atlas_list ]]; then
+		echo "[$function_name] Cannot find atlas list file at: $atlas_list"
+		return 1
+	fi
+
+	# check atlas file
+	for atlas_id in $(cat $atlas_list);do
+		check_atlas_file $atlas_dir $atlas_id
+		if [[ $? -ne 0 ]]; then
+			echo "[$function_name] atlas ($atlas_name: $atlas_id) file missing, exiting ..."
+			return 1
+		fi
+	done
+
+	# set execution mode as cluster by default
+	if [[ -z "$exe_mode" ]]; then
+		local exe_mode="cluster"
+	fi
+	if [[ "$exe_mode" != "cluster" ]] && [[ "$exe_mode" != "local" ]]; then
+		echo "[$function_name] \$exe_mode should be either \"cluster\" or \"local\" "
+		return 1
+	fi
+	# set cluster type to SGE by default
+	if [[ -z $cluster_type ]]; then
+		local cluster_type="SGE"
+	fi
+
+	# if in cluster mode, generate pbs related parameters/folders
+	if [[ "$exe_mode" == "cluster" ]]; then
+		local PBS_DIR=$result_dir/pbs
+		local LOG_DIR=$result_dir/log
+		local JOB_DIR=$result_dir/job
+		local MEM_MAPPING="4gb"
+		local MEM_FUSION="8gb"
+		local WALLTIME_MAPPING="12:00:00"
+		local WALLTIME_FUSION="4:00:00"
+
+		mkdir -p $PBS_DIR
+		mkdir -p $LOG_DIR
+		mkdir -p $JOB_DIR
+
+		local jid="${RANDOM}_parcellation" # generate random number as job ID, alternatively, use: $$
+		local job_list=$JOB_DIR/$USER.$jid.$(date +%y%m%d%H%M%S).txt
+		rm -f $job_list
+	fi
+
+	local target_id
+	local atlas_id
+	local error_flag=0
+
+	# start parcellation (mapping+fusion) loop
+	for target_id in $(cat $target_list); do
+		# check if output file already exist
+		check_label_fusion_file $target_dir $target_id $atlas_name $atlas_list $result_dir
+		local label_fusion_file_exist=$?
+		if [[ $label_fusion_file_exist -eq 2 ]]; then
+			echo "[$function_name] label fusion output file exist, skipping ..."
+			continue
+		fi
+		# fusion parameter generated (for both local/cluster) prior to loop
+		# mapping parameter generated inside the loop for local/cluster seperately)
+		local mas_parcell_fusion_param=""
+		mas_parcell_fusion_param="$mas_parcell_fusion_param -T $target_dir"
+		mas_parcell_fusion_param="$mas_parcell_fusion_param -t $target_id"
+		mas_parcell_fusion_param="$mas_parcell_fusion_param -A $atlas_name"
+		mas_parcell_fusion_param="$mas_parcell_fusion_param -a $atlas_list"
+		mas_parcell_fusion_param="$mas_parcell_fusion_param -r $result_dir"
+		# Add Optional parameters if specified
+		if [[ ! -z $targetmask_dir ]]; then
+			target_mask=$targetmask_dir/$target_id$targetmask_suffix
+			mas_parcell_fusion_param="$mas_parcell_fusion_param -m $target_mask"
+		fi
+		if [[ ! -z $parameter_cfg ]]; then
+			mas_parcell_fusion_param="$mas_parcell_fusion_param -p $parameter_cfg"
+		fi
+		if [[ ! -z $cleanup_flag ]]; then
+			mas_parcell_fusion_param="$mas_parcell_fusion_param -c $cleanup_flag"
+		fi
+
+		# start parcellation (mapping+fusion) for target $target_id
+		if [[ "$exe_mode" == "local" ]]; then
+			echo "[$function_name] start local parcellation for target ($target_id) ..."
+			####################
+			# mapping step
+			for atlas_id in $(cat $atlas_list); do
+				# check mapping input/output file
+				check_mapping_file $target_dir $target_id $atlas_dir $atlas_list $result_dir $targetmask_dir $targetmask_suffix
+				local mapping_file_exist=$?
+				if [[ $mapping_file_exist -eq 2 ]]; then
+					echo "[mas_mapping] Mapping output file already exist: ($target_id), skipping ..."
+					continue
+				elif [[ $mapping_file_exist -ne 0 ]]; then
+					echo "[mas_mapping] Mapping input file missing: ($target_id), skipping"
+					continue
+				fi
+				# start template/label mapping step
+				mas_mapping_param=""
+				mas_mapping_param="$mas_mapping_param -T $target_dir"
+				mas_mapping_param="$mas_mapping_param -t $target_id"
+				mas_mapping_param="$mas_mapping_param -A $atlas_dir"
+				mas_mapping_param="$mas_mapping_param -a $atlas_id"
+				mas_mapping_param="$mas_mapping_param -r $result_dir"
+				# Add Optional parameters if specified
+				if [[ ! -z $targetmask_dir ]]; then
+					# file exsitance already checked
+					targetmask_name="$targetmask_dir/$target_id$targetmask_suffix"
+					mas_mapping_param="$mas_mapping_param -m $targetmask_name"
+				fi
+				if [[ ! -z $parameter_cfg ]]; then
+					mas_mapping_param="$mas_mapping_param -p $parameter_cfg"
+				fi
+				if [[ ! -z $cleanup_flag ]]; then
+					mas_mapping_param="$mas_mapping_param -c $cleanup_flag"
+				fi
+				# start mapping
+				echo "[$function_name] run mapping locally for target: $target_id"
+				mas_mapping $mas_mapping_param
+			done
+			###############################
+			# label fusion step
+			echo -e "\n [$function_name] run label fusion locally for target: $target_id \n"
+			# echo "mas_fusion $mas_parcell_fusion_param"
+			mas_fusion $mas_parcell_fusion_param
+			# potential final step: cleanup unwanted files
+		elif [[ "$exe_mode" == "cluster" ]]; then
+			echo "[$function_name] start cluster parcellation for target: ($target_id) ..."
+			####################
+			# mapping step
+			# check mapping input/output file
+			check_mapping_file $target_dir $target_id $atlas_dir $atlas_list $result_dir $targetmask_dir $targetmask_suffix
+			local mapping_file_exist=$?
+			# start file checking
+			if [[ $mapping_file_exist -eq 2 ]]; then
+				echo "[$function_name] All mapping output file exist: ($target_id), go to fusion directly ..."
+			else
+				# not all mapping file pre-exist, need to run mapping step first
+				if [[ $mapping_file_exist -ne 0 ]]; then
+					echo "[$function_name] Mapping input file missing: ($target_id), skipping ..."
+					continue
+				fi
+				# pass file checking, preparing qsub pbs job parameters
+				local job_name_mapping=$jid.$target_id.mapping
+				local pbs_file=$PBS_DIR/$job_name_mapping.pbs
+				local log_file=$LOG_DIR/$job_name_mapping.log
+				# clean up files if pre-exist
+				rm -f $pbs_file
+				rm -f $log_file
+				# add pbs header info
+				pbsBoilerPlate -n $job_name_mapping -m $MEM_MAPPING -w $WALLTIME_MAPPING -j -O $log_file -f $pbs_file # -O /dev/null
+				# determine the unmapped atlas to be added to pbs job array
+				local pbs_array_prefix="#PBS -t " 
+				# calculate atlas number for job array
+				readarray atlas_array < $atlas_list
+				local atlas_no=${#atlas_array[@]}
+				for ((idx=0;idx<$atlas_no;idx+=1));do
+					atlas_id=${atlas_array[$idx]}
+					# if output files already exist, don't include from job array
+					if [[ ! -f "$result_dir/label/$atlas_name/$target_id.label.$atlas_id.nii.gz" ]] || \
+					   [[ ! -f "$result_dir/mask/$atlas_name/$target_id.mask.$atlas_id.nii.gz" ]] || \
+					   [[ ! -f "$result_dir/mapping/$atlas_name/$atlas_id.$target_id.f3d.nii.gz" ]]; then
+					    pbs_array_prefix="${pbs_array_prefix}$idx,"
+					    # add comma beforehand if not the first atlas
+					  #   if [[ $idx -lt (( $atlas_no - 1 )) ]]; then
+							# pbs_array_prefix="${pbs_array_prefix},"
+					  #   fi
+					fi
+				done
+				# cut the last comma ','
+				pbs_array_prefix=$(echo $pbs_array_prefix | rev | cut -c 2- | rev)
+
+				# add lines to determine atlas_id from ${PBS_ARRAYID}
+				echo "$pbs_array_prefix" >> $pbs_file
+				echo "source $mas_script_path" >> $pbs_file
+				echo "readarray atlas_array < $atlas_list" >> $pbs_file
+				echo "atlas_id=\${atlas_array[\${PBS_ARRAYID}]}" >> $pbs_file
+			
+				mas_mapping_param=""
+				mas_mapping_param="$mas_mapping_param -T $target_dir"
+				mas_mapping_param="$mas_mapping_param -t $target_id"
+				mas_mapping_param="$mas_mapping_param -A $atlas_dir"
+				mas_mapping_param="$mas_mapping_param -a \$atlas_id"
+				mas_mapping_param="$mas_mapping_param -r $result_dir"
+				
+				# Add Optional parameters if specified
+				if [[ ! -z $targetmask_dir ]]; then
+					local targetmask_name="$targetmask_dir/$target_id$targetmask_suffix"
+					check_image_file $targetmask_name
+					if [[ $? -ne 0 ]]; then
+						echo "[$function_name] cannot find target mask $targetmask_name"
+
+					fi
+					mas_mapping_param="$mas_mapping_param -m $targetmask_dir/$target_id$targetmask_suffix"
+				fi
+				if [[ ! -z $parameter_cfg ]]; then
+					mas_mapping_param="$mas_mapping_param -p $parameter_cfg"
+				fi
+				if [[ ! -z $cleanup_flag ]]; then
+					mas_mapping_param="$mas_mapping_param -c $cleanup_flag"
+				fi
+
+				echo -e "mas_mapping $mas_mapping_param" >> $pbs_file
+				# potential final step: cleanup unwanted files
+
+				# submit pbs job and save job in joblist
+				if [[ $cluster_type == "SGE" ]]; then
+					local job_id_mapping=$(qsub $pbs_file)
+					echo "1,qsub $pbs_file" >> $job_list
+				elif [[ $cluster_type == "SLURM" ]]; then
+					local job_id_mapping=$(sbatch $pbs_file)
+					echo "1,sbatch $pbs_file" >> $job_list
+				else
+					echo "wrong cluster type, quit ..."
+					return 1
+				fi
+				
+			fi
+
+			###############################
+			# label fusion step
+			local job_name_fusion=$jid.$target_id.fusion
+			local pbs_file=$PBS_DIR/$job_name_fusion.pbs
+			local log_file=$LOG_DIR/$job_name_fusion.log
+			pbsBoilerPlate -n $job_name_fusion -m $MEM_FUSION -w $WALLTIME_FUSION -j -O $log_file -f $pbs_file # -O /dev/null
+			if [[ ! -z $job_id_mapping ]]; then
+				# if mapping job array submitted, then add job dependency
+				if [[ cluster_type == "SGE" ]]; then
+  				echo "#PBS -W depend=afterokarray:$job_id_mapping" >> $pbs_file
+  			fi
+			else
+				if [[ $label_fusion_file_exist -eq 0 ]]; then
+					# all mapping already done previously, start fusion step without dependency
+					echo "[$function_name] ($target_id): all mapping file pre-exist, start fusion job directly without job array dependency ..."
+				else
+					echo "[$function_name] ($target_id): no mapping job submitted, and not all mapping file pre-exist, skipping ..."
+					continue
+				fi
+			fi
+
+			echo "source $mas_script_path" >> $pbs_file
+			echo "mas_fusion $mas_parcell_fusion_param" >> $pbs_file
+			# submit pbs job and store at joblist 
+			if [[ $cluster_type == "SGE" ]]; then
+				qsub $pbs_file
+				echo "1,qsub $pbs_file" >> $job_list
+			elif [[ $cluster_type == "SLURM" ]]; then
+				sbatch $pbs_file
+				echo "1,sbatch $pbs_file" >> $job_list
+			fi
+
+		fi
+	done
+
+	if [[ "$exe_mode" == "cluster" ]]; then
+		if [[ -f $job_list ]]; then
+			echo -e "\n [$function_name] joblist created under: $job_list \n"
+		else
+			echo -e "\n [$function_name] no job submitted \n"
+		fi
+	fi
+
+}
+
+#-----------------------------------
+# function: mas_quickcheck_batch
+# ----------------------------------
+function mas_quickcheck_batch(){
+	# printout function help 
+	local function_name=${FUNCNAME[0]}
+	usage() {
+	echo ""
+	echo "Multi Atlas Segmentation - Part 3: Quality control (QC - Quickcheck)"
+	echo "Usage: $function_name [-T target_dir] [-l target_list] [-s segmentation_dir] [-q quickcheck_dir]"
+	echo "       (optional) [-y seg_type(label/mask)] [-A atlas_name] [-a atlas_list (quickcheck for mapping images)]"
+	echo ""
+	return 1
+	}
+	# get options
+	local OPTIND
+	local options
+	while getopts "T:t:A:a:s:y:q:h" options; do
+		case $options in
+			T ) echo "Target directory:      $OPTARG"
+				local target_dir=$OPTARG;;
+			t ) echo "Target list:           $OPTARG"
+				local target_list=$OPTARG;;
+			A ) echo "Atlas name:            $OPTARG"
+				local atlas_name=$OPTARG;;
+			a ) echo "Atlas list:            $OPTARG"
+				local atlas_list=$OPTARG;;
+			s ) echo "segmentation dir:      $OPTARG "
+				local segmentation_dir=$OPTARG;;
+			y ) echo "Segmentation type:     $OPTARG"
+				local seg_type=$OPTARG;;
+			q ) echo "Quickcheck directory:  $OPTARG"
+				local quickcheck_dir=$OPTARG;;
+			h ) usage; return 1;;
+			\?) echo "Unknown option"
+				usage; return 1;;
+		esac
+	done
+
+	local error_flag=0
+
+	# Validate input requirement
+	if [[ $OPTIND -eq 1 ]]; then
+		echo "[$function_name] no option specified"
+		usage; return 1
+	fi
+	echo "[mas_quickcheck] start generating quickchecks"
+	if [[ ! -f $target_list ]]; then
+		echo "[$function_name] cannot find target list: $target_list"
+		return 1
+	fi
+
+	# start generating quickcheck images
+	for target_id in $(cat $target_list); do
+		echo "[$function_name] overlay mapped template $atlas_id to $target_id ..."
+		local bg_img="$target_dir/$target_id"
+		# check existence of background image
+		check_image_file $bg_img
+		if [[ $? -ne 0 ]]; then
+			echo "[$function_name] cannot find background image: $target_dir/$target_id"
+			return 1
+		fi
+		# quickcheck the fusion/mapping images depending on $atlas_list
+		local overlay_img
+		if [[ -z $atlas_list ]]; then # quickcheck for fusion
+			local quickcheck_name="$target_id"
+			overlay_img="$segmentation_dir/$target_id"
+			# additional file suffix for quickcheck if specified
+			if [[ ! -z $seg_type ]]; then
+				overlay_img="$overlay_img.$seg_type"
+				if [[ ! -z $atlas_name ]]; then
+					overlay_img="$overlay_img.$atlas_name"
+				fi
+			fi
+			# check existence of overlay image
+			check_image_file $overlay_img
+			if [[ $? -ne 0 ]]; then
+				echo "[$function_name] cannot find overlay image: $segmentation_dir/$target_id.$seg_type.$atlas_name"
+				return 1
+			fi
+			mas_quickcheck $bg_img $overlay_img $quickcheck_dir $quickcheck_name
+		else # quickcheck for all mapped atlas
+			local atlas_id
+			for atlas_id in $(cat $atlas_list); do
+				local quickcheck_subdir="$quickcheck_dir/$atlas_name"
+				mkdir -p $quickcheck_subdir
+				overlay_img="$segmentation_dir/$atlas_name/$target_id.$seg_type.$atlas_id"
+				check_image_file $overlay_img
+				if [[ $? -ne 0 ]]; then
+					echo "[$function_name] cannot find overlay image: $segmentation_dir/$atlas_name/$target_id.$seg_type.$atlas_id"
+					continue
+				fi
+				local quickcheck_name="$target_id.$seg_type.$atlas_id"
+				mas_quickcheck $bg_img $overlay_img $quickcheck_subdir $quickcheck_name
+			done
+		fi
+	done
+
+	return $error_flag
+}
+
+# -------------------------
+# function mas_mask_dilate_batch
+# -------------------------
+function mas_mask_dilate_batch(){
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 2 ]]; then
+		echo "$function_name [targetlist] [raw_mask_dir] [dilate_mask_dir] (Optionals:) [mask_suffix] [dil_voxel] [exe_mode]"
+		return 1
+	fi
+
+	local targetlist=$1
+	local raw_mask=$2
+	local dilate_mask=$3
+	local mask_suffix=$4
+	local dil_voxel=$5
+	local exe_mode=$6
+
+	mkdir -p $dilate_mask
+	local result_dir=$dilate_mask
+	# set default dilation voxel
+	if [[ -z $dil_voxel ]]; then
+		dil_voxel=1
+	fi
+	# set default execution mode
+	if [[ -z $exe_mode ]]; then
+		exe_mode="local" # "cluster" #
+	fi
+
+	echo " ========================="
+	echo "[$function_name] dilating mask ..."
+	echo "targetlist=$targetlist"
+	echo "raw_mask directory=$raw_mask"
+	echo "dilate_mask directory=$dilate_mask"
+	echo "mask_suffix=$mask_suffix"
+	echo "dil_voxel=$dil_voxel"
+	echo "exe_mode=$exe_mode"
+	echo " ========================="
+
+	# if in cluster mode, generate pbs related parameters/folders
+	if [[ "$exe_mode" == "cluster" ]]; then
+		local PBS_DIR=$result_dir/pbs
+		local LOG_DIR=$result_dir/log
+		local JOB_DIR=$result_dir/job
+		local MEM="4gb"
+		local WALLTIME="3:00:00"
+
+		mkdir -p $PBS_DIR
+		mkdir -p $LOG_DIR
+		mkdir -p $JOB_DIR
+
+		local jid="${RANDOM}_N4" # generate random number as job ID, alternatively, use: $$
+		local job_list=$JOB_DIR/$USER.$jid.$(date +%y%m%d%H%M%S).txt
+		rm -f $job_list
+	fi
+
+	local id
+	for target_id in $(cat $targetlist); do
+		local id=$target_id$mask_suffix
+		echo "[$exe_mode] $function_name $dil_voxel voxels for $id ..."
+		# local/cluster process
+		if [[ "$exe_mode" == "local" ]]; then
+			seg_maths $raw_mask/$id.nii.gz -dil $dil_voxel $dilate_mask/$id.nii.gz
+		elif [[ "$exe_mode" == "cluster" ]]; then
+			local job_name=$jid.$target_id
+			local pbs_file=$PBS_DIR/$job_name.pbs
+			local log_file=$LOG_DIR/$job_name.log
+			# clean up files if pre-exist
+			rm -f $pbs_file
+			rm -f $log_file
+			# add pbs header info
+			pbsBoilerPlate -n $job_name -m $MEM -w $WALLTIME -j -O $log_file -f $pbs_file # -O /dev/null
+			echo "seg_maths $raw_mask/$id.nii.gz -dil $dil_voxel $dilate_mask/$id.nii.gz" >> $pbs_file
+			qsub $pbs_file
+			echo "1,qsub $pbs_file" >> $job_list
+		fi
+	done
+}
+
+#-----------------------------------
+# function: mas_create_atlas
+# ----------------------------------
+function mas_create_atlas(){
+	# - mas_create_atlas (prerequisite: NiftyReg for mask dilation, to-do)
+	local function_name=${FUNCNAME[0]}
+	usage(){
+		echo """
+		Creating atlas folders from the Multi-Atlas-Segmentation result (TBA)
+		Usage: $function_name [-T target_dir] [-t target_list] [-O old_atlas_name] [-L label_dir] [-M mask_dir] [-N new_atlas_dir]
+		"""
+		return 1
+	}
+	local OPTIND
+	local options
+	while getopts ":T:t:O:L:M:N" options; do
+		case $options in
+			T ) echo "Target directory:    $OPTARG"
+				local target_dir=$OPTARG;;
+			t ) echo "Target list:         $OPTARG"
+				local target_list=$OPTARG;;
+			O ) echo "Old atlas name:      $OPTARG"
+				local old_atlas_name=$OPTARG;;
+			L ) echo "Label directory:     $OPTARG"
+				local label_dir=$OPTARG;;
+			M ) echo "Mask directory:      $OPTARG"
+				local mask_dir=$OPTARG;;
+			N ) echo "New atlas directory: $OPTARG"
+				local new_atlas_dir=$OPTARG;;
+			h ) usage; return 1;;
+			\?) echo "Unkown option"
+				usage; return 1;;
+			: ) usage; return 1;;
+		esac
+	done
+
+	if [[ $OPTIND -eq 1 ]]; then
+		echo "[$function_name] no option specified"
+		usage; return 1
+	fi
+
+	# check the existance of input files
+	for target_id in $(cat $target_list); do
+		# checking target file
+		check_image_file $target_dir/$target_id
+		if [[ $? -ne 0 ]]; then
+			echo "[$function_name] cannot find target file: $target_dir/$target_id"
+			return 1
+		fi
+		local seg_type
+		# checking label/masking files
+			# to be completed
+	done
+
+}
+
+#-----------------------------------
+# function: mas_template_function
+# ----------------------------------
+function mas_template_function(){
+	# printout function help 
+	local function_name=${FUNCNAME[0]}
+	usage_test() {
+		echo ""
+		echo "Multi Atlas Segmentation - testing"
+		echo "Usage: $function_name [-T target_dir] [-t target_list] [-m target_mask] [-A atlas_dir] [-a atlas_list] [-r result_dir] [-p parameter_cfg] [-c cleanup_flag (optional)]"
+		echo ""
+		return 1
+	}
+	# get options
+	local OPTIND
+	local options
+	while getopts ":T:t:m:A:a:r:p:c:h" options; do
+		case $options in
+			T ) echo "Target directory: $OPTARG"
+				target_dir=$OPTARG;;
+			t ) echo "Target ID: $OPTARG"
+				target_id=$OPTARG;;
+			m ) echo "Target mask: $OPTARG"
+				target_mask=$OPTARG;;
+			A ) echo "Atlas directory: $OPTARG"
+				atlas_dir=$OPTARG;;
+			a ) echo "Atlas list: $OPTARG"
+				atlas_list=$OPTARG;;
+			r ) echo "Result directory: $OPTARG"
+				result_dir=$OPTARG;;
+			p ) echo "Parameter config file: $OPTARG"
+				parameter_cfg=$OPTARG;;
+			c ) echo "Cleanup flag: $OPTARG"
+				cleanup_flag=$OPTARG;;
+			h ) usage_test; return 1;;
+			\?) echo "Unknown option"
+				usage_test; return 1;;
+			: ) usage; return 1;;
+		esac
+
+		if [[ $OPTIND -eq 1 ]]; then
+			echo "[$function_name] no option specified"
+			return 1
+		fi 
+
+done
+}
+
+#-----------------------------------
+# Other pre-/post-processing function
+# ----------------------------------
+function mas_fix_header_info(){
+	local function_name=${FUNCNAME[0]}
+    if [[ $# -lt 3 ]]; then
+		echo "$function_name Usage: $function_name [Input_file with wrong header] [orientation] [Output_file] [(Optional) output_type (analyze/nii)]"
+		return 1
+    fi
+
+    local input_file=$1
+    local orientation=$2
+    local output_file=$3
+    local output_type
+    if [[ ! -z $4 ]]; then
+    	output_type=$4
+    else
+    	output_type=nii
+    fi
+
+    mri_convert --in_orientation $orientation --out_orientation $orientation -ot $output_type $input_file $3 # -odt float
+}
+
+function mas_extract_label(){
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 4 ]]; then
+		echo "Usage: $function_name [parcellation_dir] [target_id] [label] [result_dir] [(optional) label_suffix] [(optional) extracted_label_suffix]"
+		return 1
+	fi
+
+	local parcellation_dir=$1
+	local target_id=$2
+	local label=$3
+	local result_dir=$4
+	local label_suffix=$5
+	local extracted_label_suffix=$6
+
+	seg_maths $parcellation_dir/$target_id$label_suffix.nii.gz -thr $(($label-1)) -uthr $(($label+1)) $result_dir/$target_id$extracted_label_suffix.nii.gz
+}
+
+function mas_extract_label_batch(){
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 4 ]]; then
+		echo "Usage: $function_name [parcellation_dir] [target_list] [label] [result_dir] [(optional) label_suffix] [(optional) extracted_label_suffix]"
+		return 1
+	fi
+
+	local parcellation_dir=$1
+	local target_list=$2
+	local label=$3
+	local result_dir=$4
+	local label_suffix=$5
+	local extracted_label_suffix=$6
+
+	for target_id in $(cat $target_list); do
+		mas_extract_label $parcellation_dir $target_id $label $result_dir $label_suffix $extracted_label_suffix
+	done
+}
+
+function mas_extract_volume(){
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 3 ]]; then
+		echo "Usage: $function_name [parcellation_dir] [target_id] [label] [(optional) label_suffix]"
+		return 1
+	fi
+
+	local parcellation_dir=$1
+	local target_id=$2
+	local label=$3
+	local label_suffix=$4
+
+	seg_stats "$parcellation_dir/$target_id$label_suffix.nii.gz" -vl | grep $label | cut -d' ' -f3
+}
+
+function mas_extract_volume_batch(){
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 4 ]]; then
+		echo "Usage: $function_name [parcellation_dir] [target_list] [label] [result_csv] [(optional) label_suffix]"
+		return 1
+	fi
+
+	local parcellation_dir=$1
+	local target_list=$2
+	local label=$3
+	local result_csv=$4
+	local label_suffix=$5
+
+	if [[ -f $result_csv ]]; then
+		echo "Output file: <$result_csv> exist, overwrite?"
+		local yn
+		select yn in "select 1 for Yes" "select 2 for No"; do
+			case $yn in
+				"select 1 for Yes" ) rm -f $result_csv; break;;
+				"select 2 for No" ) echo "no overwrite, exiting ..."; return 1;;
+			esac
+		done
+	fi
+
+	echo "Target_ID,Volume" >> $result_csv
+	for target_id in $(cat $target_list); do
+		echo "$target_id,$(mas_extract_volume $parcellation_dir $target_id $label $label_suffix)" >> $result_csv
+	done
+}
+
+function mas_quickcheck_panorama(){
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 2 ]]; then
+		echo "Usage: $function_name [target_dir] [targetlist] [qc_dir] [(option) label_dir] [(option) label_suffix] [(optional) target_name]"
+		return 1
+	fi
+
+	local target_dir=$1
+	local targetlist=$2
+	local qc_dir=$3
+	local label_dir=$4
+	local label_suffix=$5
+	local target_name=$6	
+
+	if [[ -z $target_name ]]; then
+		target_name=$(basename $target_dir)
+	fi
+
+	echo """
+target_dir=$target_dir
+qc_dir=$qc_dir
+label_dir=$label_dir
+label_suffix=$label_suffix
+targetlist=$targetlist
+target_name=$target_name
+	"""
+
+	mkdir -p $qc_dir
+
+	local target_id
+	local target_no=0
+	local pngappend_parameter
+	for target_id in $(cat $targetlist); do
+		local tmp_dir=$qc_dir/tmp_${RANDOM}
+		mkdir -p $tmp_dir
+		local LUT_file="$FSLDIR/etc/luts/renderjet.lut"
+		if [[ ! -f $LUT_file ]]; then
+			echo "[$function_name] cannot find color Look-up-table file: $LUT_file"
+			return 1
+		fi
+		echo "$target_no. quickcheck for $target_id"
+
+		local slicer_1st_parameter
+		if [[ ! -z $label_dir ]]; then
+			# overlay
+			local bg_img=$target_dir/$target_id.nii.gz
+			local overlay_img=$label_dir/$target_id$label_suffix.nii.gz
+			local bg_name=$(basename $bg_img | cut -d. -f1)
+			local overlay_name=$(basename $overlay_img | cut -d. -f1)
+			local overlay_nan=$tmp_dir/masknan.$bg_name.$overlay_name.nii.gz
+			local overlay_tmp=$tmp_dir/overlay.$bg_name.$overlay_name.nii.gz
+
+			# determine label range
+			seg_maths $overlay_img -masknan $overlay_img $overlay_nan
+			local label_range=$(seg_stats $overlay_nan -r)
+			echo "label_range = $label_range"
+			# generate overlay nifti file using FSL's overlay
+			overlay 1 0 $bg_img -a $overlay_nan $label_range $overlay_tmp
+			local LUT_file="$FSLDIR/etc/luts/renderjet.lut"
+			if [[ ! -f $LUT_file ]]; then
+				echo "[$function_name] cannot find color Look-up-table file: $LUT_file"
+				return 1
+			fi
+			slicer_1st_parameter="-t -n -l $LUT_file $overlay_tmp"
+		else
+			# no overlay
+			slicer_1st_parameter=$target_dir/$target_id.nii.gz
+		fi
+		slicer $slicer_1st_parameter \
+			-x 0.4 $tmp_dir/x_1.png -x 0.5 $tmp_dir/x_2.png -x 0.6 $tmp_dir/x_3.png \
+			-y 0.3 $tmp_dir/y_1.png -y 0.4 $tmp_dir/y_2.png -y 0.5 $tmp_dir/y_3.png \
+			-z 0.4 $tmp_dir/z_1.png -z 0.5 $tmp_dir/z_2.png -z 0.6 $tmp_dir/z_3.png
+		# append pngs
+		pngappend $tmp_dir/x_1.png - 2 $tmp_dir/x_2.png - 2 $tmp_dir/x_3.png $tmp_dir/x.png
+		pngappend $tmp_dir/y_1.png - 2 $tmp_dir/y_2.png - 2 $tmp_dir/y_3.png $tmp_dir/y.png
+		pngappend $tmp_dir/z_1.png - 2 $tmp_dir/z_2.png - 2 $tmp_dir/z_3.png $tmp_dir/z.png
+		pngappend $tmp_dir/x.png   - 2 $tmp_dir/y.png   - 2 $tmp_dir/z.png   $qc_dir/$target_id.png
+		rm -rf $tmp_dir
+
+		if [[ $target_no -eq 0 ]]; then
+			pngappend_parameter="$qc_dir/$target_id.png"
+		elif [[ $target_no -gt 0 ]]; then
+			pngappend_parameter="$pngappend_parameter + 2 $qc_dir/$target_id.png"
+		fi
+		(( target_no+=1 ))
+	done
+	pngappend $pngappend_parameter $qc_dir/$target_name.png
+}
+
+
+function mas_smooth_batch(){
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 2 ]]; then
+		echo "[$function_name] [input_dir] [target_list] [result_dir] [(optional) exe_mode]"
+		return 1
+	fi
+	local input_dir=$1
+	local target_list=$2
+	local result_dir=$3
+	local exe_mode=$4
+
+	local N4_parameter
+
+	# set default execution mode
+	if [[ -z $exe_mode ]]; then
+		exe_mode="local" # "cluster" #
+	fi
+
+	echo "input_dir = $input_dir"
+	echo "result_dir = $result_dir"
+	echo "target_list = $target_list"
+	echo "exe_mode = $exe_mode"
+
+	mkdir -p $result_dir
+
+	# if in cluster mode, generate pbs related parameters/folders
+	if [[ "$exe_mode" == "cluster" ]]; then
+		local PBS_DIR=$result_dir/pbs
+		local LOG_DIR=$result_dir/log
+		local JOB_DIR=$result_dir/job
+		local MEM="4gb"
+		local WALLTIME="3:00:00"
+
+		mkdir -p $PBS_DIR
+		mkdir -p $LOG_DIR
+		mkdir -p $JOB_DIR
+
+		local jid="${RANDOM}_N4" # generate random number as job ID, alternatively, use: $$
+		local job_list=$JOB_DIR/$USER.$jid.$(date +%y%m%d%H%M%S).txt
+		rm -f $job_list
+	fi
+
+	local target_id
+	for target_id in $(cat $target_list); do
+
+		local resolution=$(mri_info $input_dir/$target_id.nii.gz | grep "voxel sizes:" | cut -d: -f2)
+		local resolution_x=$(echo $resolution | cut -d, -f1)
+		local resolution_y=$(echo $resolution | cut -d, -f2)
+		local resolution_z=$(echo $resolution | cut -d, -f3)
+
+		if [[ "$exe_mode" == "local" ]]; then
+			echo "<$exe_mode> smoS_nii: $target_id ..."
+			reg_tools -in $input_dir/$target_id.nii.gz -out $result_dir/$target_id.nii.gz -smoS $resolution_x $resolution_y $resolution_z
+		elif [[ "$exe_mode" == "cluster" ]]; then
+			echo "<$exe_mode> smoS_nii: $target_id ..."
+			local job_name=$jid.$target_id
+			local pbs_file=$PBS_DIR/$job_name.pbs
+			local log_file=$LOG_DIR/$job_name.log
+			# clean up files if pre-exist
+			rm -f $pbs_file
+			rm -f $log_file
+			# add pbs header info
+			pbsBoilerPlate -n $job_name -m $MEM -w $WALLTIME -j -O $log_file -f $pbs_file # -O /dev/null
+			echo "reg_tools -in $input_dir/$target_id.nii.gz -out $result_dir/$target_id.nii.gz -smoS $resolution_x $resolution_y $resolution_z" >> $pbs_file
+			qsub $pbs_file
+			echo "1,qsub $pbs_file" >> $job_list
+		fi
+	done
+}
+
+function mas_N4_batch(){
+	local function_name=${FUNCNAME[0]}
+	if [[ $# -lt 2 ]]; then
+		echo "[$function_name] [input_dir] [target_list] [result_dir] (optional) [exe_mode] [mask_flag] [mask_dir] [mask_suffix]"
+		echo "    [Important] assuming target_list doesn include suffix, and file extension is nii.gz"
+		return 1
+	fi
+	local input_dir=$1
+	local target_list=$2
+	local result_dir=$3
+	local exe_mode=$4
+	local mask_flag=$5
+	local mask_dir=$6
+	local mask_suffix=$7
+
+	local N4_parameter
+
+	mkdir -p $result_dir
+
+	# set flag = 1 if using mask
+	if [[ -z $mask_flag ]]; then
+		mask_flag=0 # or 1 if no mask
+	fi
+	# set default execution mode
+	if [[ -z $exe_mode ]]; then
+		exe_mode="local" # "cluster" #
+	fi
+
+	# if in cluster mode, generate pbs related parameters/folders
+	if [[ "$exe_mode" == "cluster" ]]; then
+		local PBS_DIR=$result_dir/pbs
+		local LOG_DIR=$result_dir/log
+		local JOB_DIR=$result_dir/job
+		local MEM="4gb"
+		local WALLTIME="3:00:00"
+
+		mkdir -p $PBS_DIR
+		mkdir -p $LOG_DIR
+		mkdir -p $JOB_DIR
+
+		local jid="${RANDOM}_N4" # generate random number as job ID, alternatively, use: $$
+		local job_list=$JOB_DIR/$USER.$jid.$(date +%y%m%d%H%M%S).txt
+		rm -f $job_list
+	fi
+
+	local id
+	for id in $(cat $target_list); do
+		local target_id=$id
+		# N4 parameter
+		N4_parameter=""
+		if [[ $mask_flag -eq 1 ]]; then
+			# if [[ -z $mask_dir ]]; then
+			# 	mask_dir=$affine_mask_dir
+			# fi
+			N4_parameter="$N4_parameter -x $mask_dir/$id$mask_suffix.nii.gz"
+		fi
+		N4_parameter="$N4_parameter -i $input_dir/$id.nii.gz -o $result_dir/$id.nii.gz -r 1 -v"
+		# local/cluster process
+		if [[ "$exe_mode" == "local" ]]; then
+			echo "[local preprocessing] N4BiasFieldCorrection for $id ..."
+			N4BiasFieldCorrection $N4_parameter
+		elif [[ "$exe_mode" == "cluster" ]]; then
+			echo "[cluster preprocessing] N4BiasFieldCorrection for $id ..."
+			local job_name=$jid.$target_id
+			local pbs_file=$PBS_DIR/$job_name.pbs
+			local log_file=$LOG_DIR/$job_name.log
+			# clean up files if pre-exist
+			rm -f $pbs_file
+			rm -f $log_file
+			# add pbs header info
+			pbsBoilerPlate -n $job_name -m $MEM -w $WALLTIME -j -O $log_file -f $pbs_file # -O /dev/null
+			echo "N4BiasFieldCorrection $N4_parameter" >> $pbs_file
+			qsub $pbs_file
+			echo "1,qsub $pbs_file" >> $job_list
+		fi
+	done
+}
+
+
+# ---------------------------------
+#  function: slurmBoilerPlate
+# ---------------------------------
+function slurmBoilerPlate(){
+	local function_name=${FUNCNAME[0]}
+	usage() {
+		echo -e """[Usage] $function_name -a \$account -m \$mem -t \$time -n \$job_name  -f \$sbatch_file -o \$log_file		 """
+		return 1
+	}
+	# -o /dev/null
+
+	local OPTIND
+	local options
+	# print a seperate line
+	echo ""
+
+	while getopts ":a:m:t:n:f:o:h" options; do
+		case $options in
+			a ) echo "Account name:         $OPTARG"
+				local account=$OPTARG;;
+			m ) echo "Memory:               $OPTARG"
+				local mem_per_cpu=$OPTARG;;
+			t ) echo "Wall time:            $OPTARG"
+				local time=$OPTARG;;
+			n ) echo "Job name:             $OPTARG"
+				local job_name=$OPTARG;;
+			f ) echo "Sbatch file:          $OPTARG"
+				local sbatch_file=$OPTARG;;
+			o ) echo "Log file:             $OPTARG"
+				local log_file=$OPTARG;;
+			h ) usage; return 1;;
+			\?) echo "Unknown option"
+			 	usage; return 1;;
+			: ) usage; return 1;;
+		esac
+	done
+
+	if [[ $OPTIND -eq 1 ]]; then
+		echo "[$function_name] no option specified"
+		usage; return 1
+	fi 
+
+	echo "[$function_name] generating sbatch files $sbatch_file... "
+	echo "#!/bin/bash" > $sbatch_file
+	echo "#SBATCH --account=$account" >> $sbatch_file
+	echo "#SBATCH --time=$time"       >> $sbatch_file
+	echo "#SBATCH --mem-per-cpu=$mem" >> $sbatch_file
+	echo "#SBATCH --job-name=$name"   >> $sbatch_file
+	echo "#SBATCH --output=$log_file" >> $sbatch_file
+}
+
+
+# ---------------------------------
+#  function: slurm_mas_masking
+# ---------------------------------
+function slurm_mas_masking(){
+  local function_name=${FUNCNAME[0]}
+  usage() {
+    echo """[Usage] $function_name \$target_dir \$atlas_dir \$target_id \$atlas_id \$result_dir \$job_dir"""
+    return 1
+  }
+
+  if [[ $# -lt 4 ]]; then
+    usage; return 1;
+  fi
+    
+  local target_dir=$1
+  local atlas_dir=$2
+  local target_id=$3
+  local atlas_id=$4
+  
+  job_name="${target_id}-${atlas_id}_label_prop_affine"
+  sbatch_file="${job_dir}/sbatch/${job_name}.sh"
+  log_file="${job_dir}/logs/${job_name}.log"
+
+  #%% jobs
+  slurmBoilerPlate -a $account\
+                   -m $mem \
+                   -t $time \
+                   -n $job_name  \
+                   -f $sbatch_file \
+                   -o $log_file
+                    # -o /dev/null
+
+  mas_cmd=$(echo mas_masking \
+    -T $target_dir \
+    -t $target_id \
+    -A $atlas_dir \
+    -a $atlas_id \
+    -r $result_dir )
+
+    
+  # %%
+  echo $mas_cmd >> $sbatch_file
+
+  return 0
+}
+
+# ---------------------------------
+#  function: slurm_mas_masking
+# ---------------------------------
+function slurm_mas_masking_atlas_array(){
+  local function_name=${FUNCNAME[0]}
+  usage() {
+    echo """[Usage] $function_name \$target_dir \$atlas_dir \$target_id \$job_dir"""
+    return 1
+  }
+
+  if [[ $# -lt 4 ]]; then
+    usage; return 1;
+  fi
+    
+  local target_dir=$1
+  local atlas_dir=$2
+  local target_id=$3
+  local atlas_id=$4
+  
+  job_name="${target_id}-${atlas_id}_label_prop_affine"
+  sbatch_file="${job_dir}/sbatch/${job_name}.sh"
+  log_file="${job_dir}/logs/${job_name}.log"
+
+  #%% jobs
+  slurmBoilerPlate -a $account\
+                   -m $mem \
+                   -t $time \
+                   -n $job_name  \
+                   -f $sbatch_file \
+                   -o $log_file
+                    # -o /dev/null
+
+  mas_cmd=$(echo mas_masking \
+    -T $target_dir \
+    -t $target_id \
+    -A $atlas_dir \
+    -a $atlas_id \
+    -r $result_dir )
+
+    
+  # %%
+  echo $mas_cmd >> $sbatch_file
+
+  return 0
+}
\ No newline at end of file