changeset 730:067a424554b0

Added code to automatically distinguish between old and new file types (Numaris 2 and 3). Added code to do world coordinates for Numaris 2 (old).
author neelin <neelin>
date Tue, 31 Jan 1995 13:36:08 +0000
parents b277e0f8384a
children d253c4719c4c
files conversion/mri_to_minc/siemens_to_minc.pl
diffstat 1 files changed, 326 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/conversion/mri_to_minc/siemens_to_minc.pl
+++ b/conversion/mri_to_minc/siemens_to_minc.pl
@@ -41,33 +41,214 @@
                   scalar(reverse(substr($string, $offset+2, 2)))) / 4.0;
 }
 
-# Routine to initialize tape drive
-sub siemens_initialize_tape_drive {
+# Routines to do vector and matrix operations
+
+sub vector_normalize {
+    local(*vector) = @_;
+    local(@result);
+    $#result = 2;
+    local($scale);
+    local($mag) = 0;
+    for $i (0..2) {
+        $mag += ($vector[$i] ** 2);
+    }
+    $mag = sqrt($mag);
+    if ($mag > 0) {$scale = 1 / $mag;}
+    else {$scale = 1;}
+    for $i (0..2) {
+        $result[$i] = $scale * $vector[$i];
+    }
+    return @result;
+}
+
+sub vector_equal {
+    local(*vec1, *vec2) = @_;
+    local($result) =1;
+    for $i (0..2) {
+        $result = ($result && ($vec1[$i] == $vec2[$i]));
+    }
+    return $result;
+}
+
+sub vector_dot_product {
+    local(*vec1, *vec2) = @_;
+    local(@result);
+    $#result = 2;
+    for $i (0..2) {
+        $result[$i] = $vec1[$i] * $vec2[$i];
+    }
+    return @result;
+}
+
+sub vector_cross_product {
+    local(*vec1, *vec2) = @_;
+    local(@result);
+    $#result = 2;
+    $result[0] = $vec1[1] * $vec2[2] - $vec1[2] * $vec2[1];
+    $result[1] = $vec1[2] * $vec2[0] - $vec1[0] * $vec2[2];
+    $result[2] = $vec1[0] * $vec2[1] - $vec1[1] * $vec2[0];
+    return @result;
+}
+
+sub matrix_multiply {
+    local(*mat1, *mat2) = @_;
+    local(@result);
+    $#result = 8;
+    local($temp);
+    for $i (0..2) {
+        for $j (0..2) {
+            $temp = 0;
+            for $k (0..2) {
+                $temp += $mat1[$i*3 + $k] * $mat2[$k*3 + $j];
+            }
+            $result[$i*3+$j] = $temp;
+        }
+    }
+    return @result;
+}
+
+sub matrix_vector_multiply {
+    local(*matrix, *vector) = @_;
+    local(@result);
+    $#result = 2;
+    local($temp);
+    for $i (0..2) {
+        $temp = 0;
+        for $j (0..2) {
+            $temp += $matrix[$i*3 + $j] * $vector[$j];
+        }
+        $result[$i] = $temp;
+    }
+    return @result;
+}
+
+sub matrix_transpose {
+    local(*matrix) = @_;
+    local(@result);
+    $#result = 8;
+    for $i (0..2) {
+        for $j (0..2) {
+            $result[$i*3 + $j] = $matrix[$j*3 + $i];
+        }
+    }
+    return @result;
 }
 
-# Subroutine to read the siemens file headers
-sub siemens_read_headers {
+sub matrix_create_transform {
+    local(*vec1, *vec2, *vec3) = @_;
+    local(@result);
+    $#result = 8;
+    for $i (0..2) {
+        $result[$i*3+0] = $vec1[$i];
+        $result[$i*3+1] = $vec2[$i];
+        $result[$i*3+2] = $vec3[$i];
+    }
+    return @result;
+}
+
+sub matrix_identity {
+    return (1,0,0, 0,1,0, 0,0,1);
+}
+
+sub get_transform_from_normal {
+    local(*major, *normal) = @_;
+    local(@result);
+    local(@a, @b, @bprime);
+    local(@z, @zprime);
+    local(@first, @second);
+    local(@zero_vector) = (0,0,0);
+    @z = &vector_normalize(*major);
+    @zprime = &vector_normalize(*normal);
+    @a = &vector_cross_product(*z, *zprime);
+    @a = &vector_normalize(*a);
+    if (&vector_equal(*a, *zero_vector)) {
+        return &matrix_identity();
+    }
+    @b = &vector_cross_product(*z, *a);
+    @bprime = &vector_cross_product(*zprime, *a);
+    @first = &matrix_create_transform(*a, *b, *z);
+    @first = &matrix_transpose(*first);
+    @second = &matrix_create_transform(*a, *bprime, *zprime);
+    @result = &matrix_multiply(*second, *first);
+    return @result;
+}
+
+sub get_direction_cosines {
+    local($orientation, *normal, *col_dircos, *row_dircos, *slc_dircos) = @_;
+
+    # Check for normal that is not in one of the major planes
+    local($danger_normal) = 1;
+    foreach $coord (@normal) {
+        if ($coord == 0.0) { 
+            $danger_normal = 0;
+        }
+    }
+    if ($danger_normal) {
+        warn "\n\nWARNING!!!!! ".
+        "Found old Siemens file with dangerous image normal (2 rotations).\n".
+        "Getting out of here fast! Time to fix this program!\n\n\n";
+        &cleanup_and_die("Sorry!\n", -1);
+    }
+
+    # Get transformation from col,row,slice (axis) to x,y,z (world)
+    local(@axis_to_world, @world_to_axis);
+    if ($orientation eq "transverse") {
+        @world_to_axis = (1,0,0, 0,1,0, 0,0,1);
+    }
+    elsif ($orientation eq "sagittal") {
+        @world_to_axis = (0,1,0, 0,0,1, 1,0,0);
+    }
+    elsif ($orientation eq "coronal") {
+        @world_to_axis = (1,0,0, 0,0,1, 0,1,0);
+    }
+    else {die "Unknown orientation \"$orientation\"";}
+    @axis_to_world = &matrix_transpose(*world_to_axis);
+
+    # Transform normal and set closest axis
+    local(@new_normal) = &matrix_vector_multiply(*world_to_axis, *normal);
+    local(@major) = (0,0,1);
+
+    # Get transform
+    local(@transform) = &get_transform_from_normal(*major, *new_normal);
+
+    # Extract direction cosines
+    local(@transpose) = &matrix_transpose(*transform);
+    @col_dircos = @transpose[0..2];
+    @row_dircos = @transpose[3..5];
+    @slc_dircos = @transpose[6..8];
+
+    # Transform the direction cosines back to x,y,z (world) coordinates
+    @col_dircos = &matrix_vector_multiply(*axis_to_world, *col_dircos);
+    @row_dircos = &matrix_vector_multiply(*axis_to_world, *row_dircos);
+    @slc_dircos = &matrix_vector_multiply(*axis_to_world, *slc_dircos);
+
+}
+
+sub convert_coordinates {
+    local(@coords) = @_;
+    $coords[0] *= -1;
+    $coords[2] *= -1;
+    return @coords;
+}
+
+# NUMARIS 2 ROUTINES
+
+# Subroutine to read the siemens Numaris 2 file headers
+sub numaris2_read_headers {
 
     # Set constants for reading file
-    local($blksize) = 512;
-    local($ident_hdr_off) = 0;
-    local($ident_hdr_len) = 244;
+    local($header_off) = 0;
+    local($header_len) = 8192;
     local($magic_off) = 244+42;
     local($magic_len) = 12;
-    local($patient_hdr_off) = 6*$blksize;
-    local($patient_hdr_len) = 512;
-    local($meas_hdr_off) = 7*$blksize;
-    local($meas_hdr_len) = 3072;
-    local($image_hdr_off) = 13*$blksize;
-    local($image_hdr_len) = 512;
     local($magic_value) = "SIEMENS MED ";
 
+
     # Check arguements
-    if (scalar(@_) != 5) {
-        &cleanup_and_die("Argument error in siemens_read_headers",1);
+    if (scalar(@_) != 2) {
+        &cleanup_and_die("Argument error in numaris2_read_headers",1);
     }
-    local($filename, *ident_hdr, *patient_hdr, *meas_hdr, *image_hdr) = @_;
-    local($magic);
+    local($filename, *header) = @_;
 
     # Open the file
     if (!open(SMNF, "<".$filename)) {
@@ -75,27 +256,17 @@
         return 1;
     }
 
-    # Check the image file magic number
-    if (!seek(SMNF, $magic_off, 0) || 
-        (read(SMNF, $magic, $magic_len) != $magic_len) ||
-        ($magic ne $magic_value)) {
-        warn "Bad image file magic number in \"$filename\"";
+    # Read in the header
+    if (!seek(SMNF, $header_off, 0) ||
+        (read(SMNF, $header, $header_len) != $header_len)) {
         return 1;
     }
 
-    # Read in file headers
-    return 1
-        if (!seek(SMNF, $ident_hdr_off, 0) || 
-            (read(SMNF, $ident_hdr, $ident_hdr_len) != $ident_hdr_len));
-    return 1
-        if (!seek(SMNF, $patient_hdr_off, 0) || 
-            (read(SMNF, $patient_hdr, $patient_hdr_len) != $patient_hdr_len));
-    return 1
-        if (!seek(SMNF, $meas_hdr_off, 0) || 
-            (read(SMNF, $meas_hdr, $meas_hdr_len) != $meas_hdr_len));
-    return 1
-        if (!seek(SMNF, $image_hdr_off, 0) || 
-            (read(SMNF, $image_hdr, $image_hdr_len) != $image_hdr_len));
+    # Check the image file magic number
+    if (substr($header, $magic_off, $magic_len) ne $magic_value) {
+        warn "Bad image file magic number in \"$filename\"";
+        return 1;
+    }
 
     # Close input file
     close(SMNF);
@@ -103,38 +274,47 @@
     return 0;
 }
 
-# Routine to get file info
-sub siemens_read_file_info {
+# Routine to get Numaris 2 file info
+sub numaris2_read_file_info {
     if (scalar(@_) != 3) {
         &cleanup_and_die("Argument error in read_file_info",1);
     }
     local($filename, *file_info, *specific_file_info) = @_;
 
     # Get headers
-    local($ident_hdr, $patient_hdr, $meas_hdr, $image_hdr);
-    if (&siemens_read_headers($filename, *ident_hdr, *patient_hdr, 
-                              *meas_hdr, *image_hdr)) {
+    local($header);
+    if (&numaris2_read_headers($filename, *header)) {
         return 1;
     }
 
+    # Get the file headers
+    local($blksize) = 512;
+    local($ident_hdr) = substr($header, 0, 244);
+    local($relat_hdr) = substr($header, 1*$blksize+162, 166);
+    local($patient_hdr) = substr($header, 6*$blksize, 512);
+    local($meas_hdr) = substr($header, 7*$blksize, 3072);
+    local($image_hdr) = substr($header, 13*$blksize, 512);
+
     # Get interesting values
-    local($fovx, $fovy);
     $file_info{'numechos'} = &unpack_int(*meas_hdr, 512);
     if ($file_info{'numechos'} <= 0) {$file_info{'numechos'} = 1;}
     # We cheat and use patient id for exam, getting rid of weird characters
     ($file_info{'exam'} = &unpack_value(*patient_hdr, 28, 'A12'))
         =~ s/^\W//g;
     $file_info{'series'} = &unpack_int(*patient_hdr, 112);
-    $file_info{'image'} = &unpack_int(*meas_hdr, 364);
+    $file_info{'image'} = &unpack_value(*relat_hdr, 56, 'A6') + 0;
+
     $file_info{'echo'} = &unpack_int(*meas_hdr, 516);
     $file_info{'width'} = &unpack_short(*image_hdr, 138);
     $file_info{'height'} = &unpack_short(*image_hdr, 140);
     $file_info{'pixel_size'} = 2;
-    $file_info{'slicepos'} = &unpack_float(*meas_hdr, 404);
     $file_info{'patient_name'} = &unpack_value(*patient_hdr, 0, "A26");
-    local($norm_r) = &unpack_float(*meas_hdr, 380);
-    local($norm_a) = &unpack_float(*meas_hdr, 384);
-    local($norm_s) = &unpack_float(*meas_hdr, 388);
+    local(@normal) = &convert_coordinates(&unpack_float(*meas_hdr, 380),
+                                          &unpack_float(*meas_hdr, 384),
+                                          &unpack_float(*meas_hdr, 388));
+    local($norm_r) = &abs($normal[0]);
+    local($norm_a) = &abs($normal[1]);
+    local($norm_s) = &abs($normal[2]);
     local($plane) = 'transverse';
     local($max) = $norm_s;
     if ($norm_r > $max) {
@@ -146,11 +326,47 @@
         $max = $norm_a;
     }
     $file_info{'orientation'} = $plane;
+
+    # Get coordinate information
+    local($fovx, $fovy);
+    local(@col_dircos, @row_dircos, @slc_dircos);
     $fovx = &unpack_float(*meas_hdr, 400);
     $fovy = &unpack_float(*meas_hdr, 400);
+    if ($fovy == 0) {$fovy = $fovx};
     $file_info{'colstep'} = -$fovx / $file_info{'width'};
-    $file_info{'rowstep'} = 
-        -(($fovy != 0) ? $fovy : $fovx) / $file_info{'height'};
+    $file_info{'rowstep'} = -$fovy / $file_info{'height'};
+    &get_direction_cosines($file_info{'orientation'}, *normal, 
+                           *col_dircos, *row_dircos, *slc_dircos);
+    @col_dircos = &get_dircos(@col_dircos);
+    @row_dircos = &get_dircos(@row_dircos);
+    @slc_dircos = &get_dircos(@slc_dircos);
+    local($xcentre, $ycentre, $zcentre) = 
+        &convert_coordinates(&unpack_float(*meas_hdr, 368),
+                             &unpack_float(*meas_hdr, 372),
+                             &unpack_float(*meas_hdr, 376));
+    $file_info{'slicepos'} = $xcentre * $slc_dircos[0] + 
+        $ycentre * $slc_dircos[1] + $zcentre * $slc_dircos[2];
+    $file_info{'colstart'} =
+        ($xcentre * $col_dircos[0] + 
+         $ycentre * $col_dircos[1] + 
+         $zcentre * $col_dircos[2]) - 
+             $file_info{'colstep'} * ($file_info{'width'} - 1) / 2;
+    $file_info{'rowstart'} =
+        ($xcentre * $row_dircos[0] + 
+         $ycentre * $row_dircos[1] + 
+         $zcentre * $row_dircos[2]) - 
+             $file_info{'rowstep'} * ($file_info{'height'} - 1) / 2;
+    $file_info{'col_dircos_x'} = $col_dircos[0];
+    $file_info{'col_dircos_y'} = $col_dircos[1];
+    $file_info{'col_dircos_z'} = $col_dircos[2];
+    $file_info{'row_dircos_x'} = $row_dircos[0];
+    $file_info{'row_dircos_y'} = $row_dircos[1];
+    $file_info{'row_dircos_z'} = $row_dircos[2];
+    $file_info{'slc_dircos_x'} = $slc_dircos[0];
+    $file_info{'slc_dircos_y'} = $slc_dircos[1];
+    $file_info{'slc_dircos_z'} = $slc_dircos[2];
+
+    # Get other info
     $file_info{'tr'} = &unpack_float(*meas_hdr, 268)/1000;
     $file_info{'te'} = &unpack_float(*meas_hdr, 520)/1000;
     $file_info{'ti'} = &unpack_float(*meas_hdr, 264)/1000;
@@ -158,7 +374,6 @@
     $file_info{'scan_seq'} = &unpack_value(*meas_hdr, 172, 'a8');
     $file_info{'scan_seq'} =~ s/\0.*$//;
     $file_info{'scan_seq'} =~ s/\n//;
-
     ($file_info{'patient_birthdate'} = &unpack_value(*patient_hdr, 40, 'a10'))
         =~ s/\./-/g;
     local($sex_flag) = &unpack_value(*patient_hdr, 52, 'a2');
@@ -183,9 +398,9 @@
     return 0;
 }
 
-sub siemens_get_image_cmd {
+sub numaris2_get_image_cmd {
     if (scalar(@_) != 2) {
-        &cleanup_and_die("Argument error in siemens_get_image_cmd",1);
+        &cleanup_and_die("Argument error in numaris2_get_image_cmd",1);
     }
     local($cur_file, *specific_file_info) = @_;
 
@@ -198,6 +413,66 @@
     return $cmd;
 }
 
+# NUMARIS 3 ROUTINES
+
+# GENERAL SIEMENS ROUTINES
+
+# Routine to initialize tape drive
+sub siemens_initialize_tape_drive {
+}
+
+# Routine to read the file info
+sub siemens_read_file_info {
+    if (scalar(@_) != 3) {
+        &cleanup_and_die("Argument error in read_file_info",1);
+    }
+    local($filename, *file_info, *specific_file_info) = @_;
+
+    local($numaris2_magic) = "SIEMENS MED ";
+    local($numaris2_magic_id) = "0x0009 0x0011";
+    local($numaris3_magic) = "SIEMENS CM VA0  CMS ";
+    local($numaris3_magic_id) = "0x0009 0x0012";
+
+    local($magic);
+
+    # Check for a numaris 2 file
+    $magic = `extract_acr_nema $filename $numaris2_magic_id 2>/dev/null`;
+    if ($magic eq $numaris2_magic) {
+        $specific_file_info{'siemens_file_type'} = "Numaris2";
+        return &numaris2_read_file_info(@_);
+    }
+
+    # Check for a numaris 3 file
+    $magic = `extract_acr_nema $filename $numaris3_magic_id 2>/dev/null`;
+    if ($magic eq $numaris3_magic) {
+        $specific_file_info{'siemens_file_type'} = "Numaris3";
+        return &numaris3_read_file_info(@_);
+    }
+
+    # Can't figure out file type
+    warn "Unable to identify siemens file type in \"$filename\"";
+    return 1;
+
+}
+
+# Routine to get the image
+sub siemens_get_image_cmd {
+    if (scalar(@_) != 2) {
+        &cleanup_and_die("Argument error in siemens_get_image_cmd",1);
+    }
+    local($cur_file, *specific_file_info) = @_;
+
+    if ($specific_file_info{'siemens_file_type'} eq "Numaris2") {
+        return &numaris2_get_image_cmd(@_);
+    }
+    elsif ($specific_file_info{'siemens_file_type'} eq "Numaris3") {
+        return &numaris2_get_image_cmd(@_);
+    }
+    else {
+        die "Unknown siemens file type";
+    }
+}
+
 # MAIN PROGRAM
 
 *initialize_tape_drive = *siemens_initialize_tape_drive;