#! /usr/bin/env perl
#
# Andrew Janke - rotor@cmr.uq.edu.au
# Center for Magnetic Resonance
# The University of Queensland
# http://www.cmr.uq.edu.au/~rotor
#
# Copyright Andrew Janke, The University of Queensland.
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted,
# provided that the above copyright notice appear in all copies.  The
# author and the University of Queensland make no representations about the
# suitability of this software for any purpose.  It is provided "as is"
# without express or implied warranty.
#
# Script for dumping a miff file from a minc file to STDOUT.
#
# Thu Jun  1 17:46:19 EST 2000 - major rewrite to speed things
#                                  also added transverse/coronal/sagital
# Fri Jun  2 19:05:59 EST 2000 - removed pnm code and now use convert
#                                  to get "right" size images
# Wed Jun 21 20:45:33 EST 2000 - added correct flipping and floping of
#                                  output images as per the radiological
#                                  convention (-ve z dimension)
# Mon Jul  3 12:39:50 EST 2000 - added multiple dimension output
# Fri Sep  1 12:46:13 EST 2000 - added -axial option
# Tue Nov 21 16:14:07 EST 2000 - added strict and multiple image type output
# Thu Feb 22 15:52:27 EST 2001 - changed to mincextract for range stuff
# Thu Nov 22 14:05:11 EST 2001 - added -depth 8 to convert args
# Tue Dec  3 17:35:05 EST 2002 - added steves -lookup option (minclookup)
#                              - major rewrite of the dimension ordering
#                                  the hard bit is done by mincreshape now
# Thu Jan 23 17:05:41 EST 2003 - fixed bug with step directions
#                                  added -dimsize {z,y,z}space=-1


use strict;
use warnings "all";
use Getopt::Tabular;
use File::Basename;
use File::Temp qw/ tempdir /;

my($Help, $Usage, $me, @opt_table, $tmpdir, %opt);
my(@args, $args, $infile, $outfile, %ordering, $CODE);

# permutation 'matrix' for differing views
%ordering = (
   'zspace' => ['yspace', 'xspace'],
   'yspace' => ['zspace', 'xspace'],
   'xspace' => ['zspace', 'yspace'],
   );

$me = &basename($0);
$CODE = "GRAY";
%opt = ('verbose' => 0,
        'clobber' => 0,
        'slice' => undef,
        'scale' => 2,
        'bitdepth' => 8,
        'range' => undef,
        'image_range' => undef,
        'lookup' => undef,
        'dirs' => ['zspace'],
        );

$Help = <<HELP;
| $me generates image files from MINC volumes using the Imagemagick
|    convert utility. For a complete list of output file types see the
|    convert man pages. (man convert)
|
| EXAMPLES:
| To display a default view, transverse slicing, middle slice
| using display. (display is part of the Imagemagick package)
|
|    \$ mincpik infile.mnc MIFF:- | display -
|
| To generate a PNG file of the 15th coronal slice
|
|    \$ mincpik -slice 15 -coronal infile.mnc outfile.png
|
| To generate a JPG file using the hotmetal lookup table
|    with the image range 0 to 100
|
|    \$ mincpik -lookup -hotmetal -image_range 0 100 infile.mnc outfile.jpg
|
| Problems or comments should be sent to: rotor\@cmr.uq.edu.au
HELP

$Usage = "Usage: $me [options] <infile.mnc> [<image.type>]\n".
         "       $me -help to list options\n\n";

@opt_table = (
   ["-verbose", "boolean", 0, \$opt{'verbose'},
      "be verbose"],
   ["-clobber", "boolean", 0, \$opt{'clobber'},
      "overwrite existing files"],
   ["-slice", "integer", 1, \$opt{'slice'},
      "slice number to get"],
   ["-scale", "integer", 1, \$opt{'scale'},
      "scaling factor for resulting image (Default: 2)"],
   ["-depth", "integer", 1, \$opt{'bitdepth'},
      "bitdepth for resulting image 8 or 16 (Default: 8) (MSB machines only!)"],
   
   ["Image range and lookup table options", "section" ],
   ["-range", "float",   2, \@{$opt{'range'}},
      "valid range of values for MINC file"],
   ["-image_range", "float", 2, \@{$opt{'image_range'}},
      "range of image values to use for pixel intensity"],
   ["-lookup", "string", 1, \$opt{'lookup'},
      "arguments to pass to minclookup"],
   
   ["Slicing options", "section" ],
   ["-transverse", "arrayconst", ['zspace'], \@{$opt{'dirs'}},
      "get a transverse slice"],
   ["-axial", "arrayconst", ['zspace'], \@{$opt{'dirs'}},
      "synonym for transverse"],
   ["-coronal", "arrayconst", ['yspace'], \@{$opt{'dirs'}},
      "get a coronal slice"],
   ["-sagittal", "arrayconst", ['xspace'], \@{$opt{'dirs'}},
      "get a sagital slice"],
   ["-allthree", "arrayconst", ['zspace', 'xspace', 'yspace'], \@{$opt{'dirs'}},
      "get all three directions"],
   );

# Check arguments
&Getopt::Tabular::SetHelp ($Help, $Usage);
&GetOptions (\@opt_table, \@ARGV) || exit 1;
die $Usage if ($#ARGV < 0);

# create temporary directory
$tmpdir = &tempdir( "$me-XXXXXXXX", TMPDIR => 1, CLEANUP => 1 );

# set up file names and do a few checks
$infile = $ARGV[0];
$outfile = (defined($ARGV[1])) ? $ARGV[1] : 'MIFF:-';

die "$me: Couldn't find $infile\n\n" if (!-e $infile);
if($outfile ne 'MIFF:-' && -e $outfile && !$opt{'clobber'}){
   die "$me: $outfile exists, use -clobber to overwrite\n\n";
   }

if($opt{'bitdepth'} != 16 && $opt{'bitdepth'} != 8) {
   die "$me: Invalid bitdepth specified - $opt{'bitdepth'} instead of 8 or 16\n\n";
   }

my ($space, $n_slices, $convert_infile, @extract_args, @convert_args, $pipe_args, $dimorder);
my ($img_x, $img_y, $img_step_x, $img_step_y, $img_length_x, $img_length_y, $dim_names);

# foreach slicing direction
foreach $space (@{$opt{'dirs'}}){
   
   # Get the info we need
   $args = "mincinfo ".
           "-dimlength $space ".
           "-dimlength $ordering{$space}[0] ".
           "-dimlength $ordering{$space}[1] ".
           "-attvalue $ordering{$space}[0]:step ". 
           "-attvalue $ordering{$space}[1]:step ".
           "-dimnames ".
           $infile;
   ($n_slices, $img_x, $img_y, $img_step_x, $img_step_y, $dim_names) = split("\n", `$args`);
   
   $img_length_x = abs(int($img_step_x * $img_x * $opt{'scale'}));
   $img_length_y = abs(int($img_step_y * $img_y * $opt{'scale'}));
   
   # figure out the slice to get
   if(!defined($opt{'slice'})){
      $opt{'slice'} = int($n_slices/2);
      }
   if($opt{'slice'} >= $n_slices || $opt{'slice'} < 0){ 
      die "Slice $opt{'slice'} out of range (0-" . ($n_slices-1) . ")\n\n";
      }

   # check if we have a vector_dimension already
   if($dim_names =~ m/vector_dimension/){
      $CODE = 'RGB';
      }
   
   # do the reshaping
   $dimorder = join(',', $space, @{$ordering{$space}});
   if($CODE eq 'RGB'){
      $dimorder .= ',vector_dimension';
      }
   @args = ('mincreshape', '-clobber', '-quiet',
            '-normalize',
            '+direction',
            '-dimsize', "$space=-1",
            '-dimsize', "$ordering{$space}[0]=-1",
            '-dimsize', "$ordering{$space}[1]=-1",
            '-dimorder', $dimorder,
            '-dimrange', "$space=$opt{'slice'},1",
            $infile, "$tmpdir/reshaped.mnc");                  
   if(scalar(@{$opt{'range'}}) != 0){
      push(@args, '-valid_range', @{$opt{'range'}}[0], @{$opt{'range'}}[1]);
      }
   if(scalar(@{$opt{'image_range'}}) != 0){
      push(@args, '-image_range', @{$opt{'image_range'}}[0], @{$opt{'image_range'}}[1]);
      }
   &do_cmd(@args);
   
   # do the lookup if required
   $convert_infile = "$tmpdir/reshaped.mnc";
   if($opt{'lookup'}){
      if($CODE eq 'RGB'){
         warn "$me: Input is vector-valued already.  No colour lookup done.\n";
         }
      else{
         $convert_infile = "$tmpdir/lookup.mnc";
         $CODE = 'RGB';
         &do_cmd('minclookup', '-quiet', 
                 split(' ', $opt{'lookup'}), 
                 "$tmpdir/reshaped.mnc", $convert_infile);
         }
      }

   # set up mincextract command
   @extract_args = ('mincextract', $convert_infile,
                    '-normalize',
                    ($opt{'bitdepth'} == 16) ? ('-short', '-unsigned') : '-byte');
   
   # set up convert arguments
   # a flip is 'normal' due to the difference between mnc and most image co-ordinates
   @convert_args = ('convert',
                    '-depth', $opt{'bitdepth'},
                    '-flip', 
                    '-size', $img_y . 'x' . $img_x,
                    '-geometry', $img_length_y . 'x' . $img_length_x . '!',
                    "$CODE:-", $outfile);
   
   # check if we are big or little endian for convert's MSB wierdity
   $pipe_args = '|';
   if($opt{'bitdepth'} == 16){
      if(unpack("c",substr(pack("s",1),0,1))){
         warn "$me: LSB machine, swapping bytes with dd and crossing fingers\n";
         $pipe_args .= ' dd conv=swab | ';
         }
      }
   
   &do_cmd(join(' ', @extract_args, $pipe_args, @convert_args));
   }

sub do_cmd {
   print STDERR "@_\n" if $opt{'verbose'};
   system(@_) == 0 or die;
   }
