/*=========================================================================

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/VolViewCopyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkDICOMCollector.h"

#include "vtkCommand.h"
#include "vtkDirectory.h"
#include "vtkGenericMathUtilities.h"
#include "vtkImageData.h"
#include "vtkMath.h"
#include "vtkMedicalImageProperties.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkStringArray.h"
#include "vtkUnsignedCharArray.h"
#include "vtkUnsignedShortArray.h"
#include "vtkDICOMCollectorOptions.h"

#include <vtksys/ios/sstream>
#include <vtksys/stl/algorithm>
#include <vtksys/stl/set>
#include <vtksys/stl/string>
#include <vtksys/stl/vector>
#include <vtksys/SystemTools.hxx>

#ifdef MACOS
#include <files.h>
#elif defined(_MSC_VER) || defined(__BORLANDC__)
#include <io.h>
#include <fcntl.h>
#else
#include <sys/file.h>
#endif

#include <ctype.h>
#include <time.h>
#include <assert.h>

#ifdef KWCommonPro_USE_GDCM
  #include "gdcmFileHelper.h"
  #include "gdcmFile.h"
  #include "gdcmDebug.h"
  #include "gdcmCommon.h"
#endif

#ifdef KWCommonPro_USE_CTNLIB
#if !(defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_IMAGE)
// JPEG12 support has been removed in favor of GDCM's own JPEG12
// #include "vtkJPEG12Reader.h"
// #include "jpegless.h"
#endif
  // CTN library
  #ifndef __WIN32
  extern "C" {
  #endif
  #include "dicom.h"
  #include "dicom_uids.h"
  #include "condition.h"
  #include "lst.h"
  #include "dicom_objects.h"
  #ifndef __WIN32
  }
  #endif
#endif // KWCommonPro_USE_CTNLIB

#include "vtkArrayMap.txx"

#define VTK_DICOM_TOLERANCE 0.9
#define VTK_DICOM_ACQUISITION_NUMBER_MIN 4
#define max(x,y) ((x>y) ? (x) : (y))

//----------------------------------------------------------------------------
vtkStandardNewMacro(vtkDICOMCollector);
vtkCxxRevisionMacro(vtkDICOMCollector, "$Revision: 1.176 $");

vtkCxxSetObjectMacro(vtkDICOMCollector, Options, vtkDICOMCollectorOptions);

//----------------------------------------------------------------------------
class vtkDICOMCollectorInternals
{
public:
  typedef vtksys_stl::vector<vtkDICOMCollector::ImageSlot*> ImagesContainer;
  typedef vtksys_stl::vector<vtkDICOMCollector::ImageSlot*>::iterator ImagesContainerIterator;
  ImagesContainer Slices;
};

//----------------------------------------------------------------------------
// Number of Volumes within the same Series UID.
// There a couple of scenario where you can have multiple volume within the same DICOM
// series UID. For instance SCOUT, or to some extent a DWI/DTI aquisition (where
// you consider each component of the tensor to be an independant image).
class vtkVolumeSliceIndexesInternals
{
public:
  vtkstd::vector<unsigned int> Start;
  vtkstd::vector<unsigned int> End;
  vtkstd::vector<double> Spacing;
  vtkVolumeSliceIndexesInternals() : Start(0), End(0), Spacing(0) {}
  void SetNumberOfVolumes(unsigned int numvolumes)
    {
    Start.resize(numvolumes);
    End.resize(numvolumes);
    Spacing.resize(numvolumes);
    }
  unsigned int GetNumberOfVolumes()
    {
    // You have to make sure SetNumberOfVolumes is calling resize
    // on *all* elements, then you can pick anyone to return the size:
      return (unsigned int)Start.size();
    }
  void SetStart(unsigned int volumeidx, unsigned int start)
    {
    assert(Start.size() > volumeidx);
    Start[ volumeidx ] = start;
    }
  unsigned int GetStart(unsigned int volumeidx)
    {
    return Start[ volumeidx ];
    }
  void SetEnd(unsigned int volumeidx, unsigned int end)
    {
    assert(End.size() > volumeidx);
    End[ volumeidx ] = end;
    }
  unsigned int GetEnd(unsigned int volumeidx)
    {
    return End[ volumeidx ];
    }
  void SetSpacing(unsigned int volumeidx, double spc)
    {
    Spacing[volumeidx] = spc;
    }
  double GetSpacing(unsigned int volumeidx)
    {
    return Spacing[volumeidx];
    }
};

//----------------------------------------------------------------------------
vtkDICOMCollector::vtkDICOMCollector()
{
  this->Internals = new vtkDICOMCollectorInternals;
  this->SliceIndexes = new vtkVolumeSliceIndexesInternals;
  this->SliceSpacing = 1.0;
  this->CurrentImage = 0;
  this->SelectedImage = 0;
  this->PaddedExtents = NULL;
  this->FailureStatus = vtkDICOMCollector::FailureNone;
  this->FileNames = vtkStringArray::New();
  this->CurrentVolume = 0;
  this->Options = vtkDICOMCollectorOptions::New();
}

//----------------------------------------------------------------------------
vtkDICOMCollector::~vtkDICOMCollector()
{
  if (this->FileNames)
    {
    this->FileNames->Delete();
    this->FileNames = NULL;
    }

  this->ClearCollectedSlices();

  if (this->Internals)
    {
    delete this->Internals;
    this->Internals = NULL;
    }
  if (this->SliceIndexes)
    {
    delete this->SliceIndexes;
    this->SliceIndexes = NULL;
    }
  this->Options->Delete();
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::DeleteCurrentImage()
{
  if (this->CurrentImage)
    {
    delete this->CurrentImage;
    this->CurrentImage = 0;
    }
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::DeleteAllSlices()
{
  // Delete all slices

  vtkDICOMCollectorInternals::ImagesContainerIterator it = 
    this->Internals->Slices.begin();
  vtkDICOMCollectorInternals::ImagesContainerIterator end = 
    this->Internals->Slices.end();
  for (; it != end; ++it)
    {
    if (*it)
      {
      delete (*it);
      }
    }

  this->Internals->Slices.clear();
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::ClearCollectedSlices()
{
  // Make the current image invalid

  this->DeleteCurrentImage();

  // Remove all collected slices

  this->DeleteAllSlices();

  // Invalidate PaddedExtents array

  if (this->PaddedExtents)
    {
    this->PaddedExtents->Delete();
    this->PaddedExtents = NULL;
    }

  // Now seems a good time to clear the failure status, since we are going to
  // re-read everything.

  this->FailureStatus = vtkDICOMCollector::FailureNone;
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::SetFileName(const char *filename)
{ 
  this->FileNames->Reset();
  if (filename && *filename)
    {
    this->FileNames->InsertNextValue(filename);
    }

  // Make the current image and all collected slices invalid

  this->ClearCollectedSlices();
}
 
//----------------------------------------------------------------------------
const char* vtkDICOMCollector::GetFileName()
{
  return this->GetNthFileName(0);
}

//----------------------------------------------------------------------------
const char* vtkDICOMCollector::GetNthFileName(int i)
{ 
  if (i >= 0 && i < this->FileNames->GetNumberOfValues())
    {
    const char *filename = this->FileNames->GetValue(i).c_str();
    return *filename ? filename : NULL;
    }
  return NULL;
}
 
//----------------------------------------------------------------------------
void vtkDICOMCollector::SetFileNames(vtkStringArray *filenames)
{ 
  this->FileNames->DeepCopy(filenames);

  // Make the current image and all collected slices invalid

  this->ClearCollectedSlices();
}
 
//----------------------------------------------------------------------------
int vtkDICOMCollector::GetNumberOfFileNames()
{ 
  return this->FileNames ? this->FileNames->GetNumberOfValues() : 0;
}
 
//----------------------------------------------------------------------------
vtkDICOMCollector::ImageInfo::ImageInfo()
{
  this->SamplesPerPixel = 0;
  this->Rows    = 0;
  this->Columns = 0;
  this->Planes  = 0;
  this->NumberOfFrames  = 1;
  this->InstanceNumber  = 0;
  this->RescaleIntercept = 0.0;
  this->RescaleSlope = 1.0;

  this->SOPInstanceUID     = 0;
  this->SeriesInstanceUID  = 0;
  this->StudyInstanceUID   = 0;
  this->TransferSyntaxUID  = 0;
}

//----------------------------------------------------------------------------
vtkDICOMCollector::ImageInfo::~ImageInfo()
{
  this->SetSOPInstanceUID(0);
  this->SetSeriesInstanceUID(0);
  this->SetStudyInstanceUID(0);
  this->SetTransferSyntaxUID(0);
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::ImageInfo::SetSeriesInstanceUID(const char *arg)
{
  if ((!arg && !this->SeriesInstanceUID) ||
      (this->SeriesInstanceUID && arg && !strcmp(this->SeriesInstanceUID, arg)))
    {
    return;
    }

  if (this->SeriesInstanceUID)
    {
    delete [] this->SeriesInstanceUID;
    }

  if (arg)
    {
    this->SeriesInstanceUID = new char[strlen(arg) + 1];
    strcpy(this->SeriesInstanceUID, arg);
    }
  else
    {
    this->SeriesInstanceUID = NULL;
    }
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::ImageInfo::SetSOPInstanceUID(const char *arg)
{
  if ((!arg && !this->SOPInstanceUID) ||
      (this->SOPInstanceUID && arg && !strcmp(this->SOPInstanceUID, arg)))
    {
    return;
    }

  if (this->SOPInstanceUID)
    {
    delete [] this->SOPInstanceUID;
    }

  if (arg)
    {
    this->SOPInstanceUID = new char[strlen(arg) + 1];
    strcpy(this->SOPInstanceUID, arg);
    }
  else
    {
    this->SOPInstanceUID = NULL;
    }
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::ImageInfo::SetStudyInstanceUID(const char *arg)
{
  if ((!arg && !this->StudyInstanceUID) ||
      (this->StudyInstanceUID && arg && !strcmp(this->StudyInstanceUID, arg)))
    {
    return;
    }

  if (this->StudyInstanceUID)
    {
    delete [] this->StudyInstanceUID;
    }

  if (arg)
    {
    this->StudyInstanceUID = new char[strlen(arg) + 1];
    strcpy(this->StudyInstanceUID, arg);
    }
  else
    {
    this->StudyInstanceUID = NULL;
    }
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::ImageInfo::SetTransferSyntaxUID(const char *arg)
{
  if ((!arg && !this->TransferSyntaxUID) ||
      (this->TransferSyntaxUID && arg && !strcmp(this->TransferSyntaxUID,arg)))
    {
    return;
    }

  if (this->TransferSyntaxUID)
    {
    delete [] this->TransferSyntaxUID;
    }

  if (arg)
    {
    this->TransferSyntaxUID = new char[strlen(arg) + 1];
    strcpy(this->TransferSyntaxUID, arg);
    }
  else
    {
    this->TransferSyntaxUID = NULL;
    }
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::ImageInfo::IsRelatedTo(ImageInfo &info, 
                                              int consider_acq)
{
  double dot1 = vtkMath::Dot(this->GetOrientationRow(), info.GetOrientationRow());
  double dot2 = vtkMath::Dot(this->GetOrientationColumn(), info.GetOrientationColumn());

  if (this->Rows                == info.Rows &&
      this->Columns             == info.Columns &&
      this->Planes              == info.Planes &&
      this->BitsAllocated       == info.BitsAllocated &&
      this->BitsStored          == info.BitsStored &&
      this->HighBit             == info.HighBit &&
      this->PixelRepresentation == info.PixelRepresentation &&
      (!consider_acq || this->AcquisitionNumber == info.AcquisitionNumber) &&
      // If both images do not have Series Instance UID
      (!this->SeriesInstanceUID || !info.SeriesInstanceUID ||
       !strcmp(this->SeriesInstanceUID, info.SeriesInstanceUID)) &&
      // If both images do not have Study Instance UID
      (!this->StudyInstanceUID || !info.StudyInstanceUID ||
       !strcmp(this->StudyInstanceUID,  info.StudyInstanceUID)) &&
      dot1 > VTK_DICOM_TOLERANCE &&
      dot2 > VTK_DICOM_TOLERANCE)
    {
    return 1;
    }

  return 0;
}

//----------------------------------------------------------------------------
vtkDICOMCollector::ImageSlot::ImageSlot()
{
  this->FileName   = 0;
  this->Info       = 0;
  this->MedicalProperties = 0;
}

//----------------------------------------------------------------------------
vtkDICOMCollector::ImageSlot::~ImageSlot()
{
  this->SetFileName(0);

  if (this->Info)
    {
    delete this->Info;
    this->Info = NULL;
    }

  if (this->MedicalProperties)
    {
    this->MedicalProperties->Delete();
    this->MedicalProperties = NULL;
    }
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::ImageSlot::SetFileName(const char *arg)
{
  if ((!arg && !this->FileName) ||
      (this->FileName && arg && !strcmp(this->FileName, arg)))
    {
    return;
    }

  if (this->FileName)
    {
    delete [] this->FileName;
    }

  if (arg)
    {
    this->FileName = new char[strlen(arg) + 1];
    strcpy(this->FileName, arg);
    }
  else
    {
    this->FileName = NULL;
    }
}

//----------------------------------------------------------------------------
// Comparison is done using distance between the Image Position (Patient) along
// the normal of the two images (deduced from Image Orientation (Patient)).
bool ImageSlotCompare(vtkDICOMCollector::ImageSlot *targ1, 
                      vtkDICOMCollector::ImageSlot *targ2)
{
  // Compare distance of two valid images

  double dist1, dist2;
  
  if (!targ1->Info || !targ2->Info)
    {
    return 0;
    }

  dist1 = vtkMath::Dot(targ1->Info->GetOrientationImage(), targ1->Info->Position);
  dist2 = vtkMath::Dot(targ2->Info->GetOrientationImage(), targ2->Info->Position);
  
  if (dist1 < dist2)
    {
    return true;
    }

  return false;
}


//----------------------------------------------------------------------------
// Comparison is done using the Instance Number of the DICOM file. This is a Type #2
// so might not always be present in the DICOM file.
// This sort is only used in the complex DTI/DWI case.
bool ImageSlotCompareInstance(vtkDICOMCollector::ImageSlot *targ1,
                              vtkDICOMCollector::ImageSlot *targ2)
{
  unsigned int id1 = targ1->Info->InstanceNumber;
  unsigned int id2 = targ2->Info->InstanceNumber;
  return id1 < id2;
}

#ifdef KWCommonPro_USE_CTNLIB
//----------------------------------------------------------------------------
int vtkDICOMCollector::OpenDicomFile(const char *filename, DCM_OBJECT **object)
{
  DCM_Debug(FALSE);

  // Let's relax the reader by using DCM_ACCEPTVRMISMATCH

  unsigned long special_opts = DCM_ACCEPTVRMISMATCH;

  // Try different options
  // Part 10, Part 10 (Implicit VR), 
  // no Part10 (Little, Big, Implicit, Explicit)

  unsigned long opts[6] = 
    {
      DCM_PART10FILE,
      DCM_EFILM | DCM_ORDERLITTLEENDIAN,
      DCM_ORDERLITTLEENDIAN,
      DCM_ORDERBIGENDIAN,
      DCM_EXPLICITLITTLEENDIAN,
      DCM_EXPLICITBIGENDIAN
    };

  int i;
  for (i = 0; i < 6; i++)
    {
    if (DCM_OpenFile(filename, opts[i] | special_opts, object) == DCM_NORMAL)
      {
      (void) COND_PopCondition(TRUE);
      return 1;
      }
    }

  (void) COND_PopCondition(FALSE);
  return 0;
}
#endif

//----------------------------------------------------------------------------
int vtkDICOMCollector::RetrieveImageInfo(ImageSlot* image, int quiet)
{
  // Open DICOM file

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  gdcm::File *file = new gdcm::File();
  file->SetFileName(image->GetFileName());
  file->SetLoadMode(gdcm::LD_NOSEQ);
  file->Load();
  if (!file->IsReadable())
#elif defined(KWCommonPro_USE_CTNLIB)
    DCM_OBJECT *object = NULL;
    DCM_Debug(FALSE);
    DCM_CreateObject(&object, 0);
  if (!this->OpenDicomFile(image->GetFileName(), &object))
#endif
    {
    if  (!quiet)
      {
      vtkErrorMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => unable to load!");
      }
    this->FailureStatus |= vtkDICOMCollector::FailureNotReadable;
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
#elif defined(KWCommonPro_USE_CTNLIB)
    (void) DCM_CloseObject(&object);
    (void) COND_PopCondition(FALSE);
#endif
    return vtkDICOMCollector::FailureNotReadable;
    }

  // Allocate info

  if (!image->Info)
    {
    image->Info = new vtkDICOMCollector::ImageInfo;
    }
  vtkDICOMCollector::ImageInfo *info = image->Info;

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  info->Rows = file->GetYSize();
  info->Columns = file->GetXSize();
  info->Planes = file->GetZSize();
  info->BitsStored = file->GetBitsStored();
  info->PixelRepresentation = file->IsSignedPixelData() ? 1 : 0;
#elif defined(KWCommonPro_USE_CTNLIB)

  this->CTReconstructionRadius      = 0.0;
  this->PixelPaddingValueTagAssumed = 0;

  
  // Initialize the Required Elements to prepare for retrieval...

  void *ctx;
  U32 rtn_length;

  DCM_ELEMENT required[] = {
    {DCM_IMGROWS, DCM_US, "", 1,
     sizeof(info->Rows), {(char *) &(info->Rows)}},
    {DCM_IMGCOLUMNS, DCM_US, "", 1,
     sizeof(info->Columns), {(char *) &(info->Columns)}},
    {DCM_IMGBITSSTORED, DCM_US, "", 1,
     sizeof(info->BitsStored), {(char *) &(info->BitsStored)}},
    {DCM_IMGPIXELREPRESENTATION, DCM_US, "", 1,
     sizeof(info->PixelRepresentation), 
     {(char *)&(info->PixelRepresentation)}}
  };
  
  info->Planes = 1;

  //  Get the required elements...

  if (DCM_ParseObject(&object, required, 4, NULL, 0, NULL) != DCM_NORMAL) 
    {
    if (image == this->CurrentImage)
      {
      vtkErrorMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => missing required elements!");
      }
    this->FailureStatus |= vtkDICOMCollector::FailureMissingRequiredElements;
    (void) DCM_CloseFile(&object);
    (void) DCM_CloseObject(&object);
    (void) COND_PopCondition(FALSE);
    return vtkDICOMCollector::FailureMissingRequiredElements;
    }
#endif

#if defined(KWCommonPro_USE_CTNLIB)
  // First thing first we need to know the SOP class UID of the image
  // this will not work on very old ACR NEMA v1 or v2.
  //(0008,0016)
  char dcm_sopclassuid[1024];
  DCM_ELEMENT iscu =
    {DCM_IDSOPCLASSUID, DCM_UI, "", 1, 
     sizeof(dcm_sopclassuid), {(char*)dcm_sopclassuid}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &iscu, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length) 
    {
    vtkWarningMacro(
      "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
      << "]\n => unable to get the SOP Class UID. Attempting to guess values.");
    *dcm_sopclassuid = '\0';
    }
  else
    {
    dcm_sopclassuid[rtn_length] = '\0';
    }
  if (this->Options->GetRequireSOPClassUID() && ! *dcm_sopclassuid)
    {
    this->FailureStatus |= vtkDICOMCollector::FailureNoSOPClassUID;
    (void) DCM_CloseFile(&object);
    (void) DCM_CloseObject(&object);
    (void) COND_PopCondition(FALSE);
    return vtkDICOMCollector::FailureNoSOPClassUID;
    }
#endif

  // For now we do not support Non-ASCII character set
  if (this->Options->GetSupportASCIICharacterSetOnly())
    {
#if defined(KWCommonPro_USE_CTNLIB)
    char specificcharacter[1024];
    DCM_ELEMENT sc =
      {DCM_IDSPECIFICCHARACTER, DCM_CS, "", 1, 
      sizeof(specificcharacter), {(char*)specificcharacter}};
    ctx = NULL;
    if (DCM_GetElementValue(
        &object, &sc, &rtn_length, &ctx) != DCM_NORMAL) 
      {
      vtkWarningMacro("Character set was not set assuming ASCII: " << image->GetFileName());
      *specificcharacter = '\0';
      }
    else
      {
      specificcharacter[rtn_length] = '\0';
      }
    // if character set was not specified assumed they meant ASCII (backward compat with ACR-NEMA)
    if (*specificcharacter && strcmp(specificcharacter, "ISO_IR 100") != 0)
      {
      this->FailureStatus |= vtkDICOMCollector::FailureNonASCII;
      vtkErrorMacro("Non-ASCII character set is not supported");
      (void) DCM_CloseFile(&object);
      (void) DCM_CloseObject(&object);
      (void) COND_PopCondition(FALSE);
      return vtkDICOMCollector::FailureNonASCII;
      }
#endif
    }

  
  // Check for PixelSpacing (or set to 1, 1)

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  info->Spacing[0] = file->GetXSpacing();
  info->Spacing[1] = file->GetYSpacing();
  info->Spacing[2] = file->GetZSpacing();
#elif defined(KWCommonPro_USE_CTNLIB)
  // Some modality simply do not have Pixel Spacing all you have is 
  // Pixel Aspect Ratio (0028,0034)
  if (
    // Ultrasound Multi-Frame Image Storage (Retired)
    strcmp("1.2.840.10008.5.1.4.1.1.3", dcm_sopclassuid) == 0 ||
    // Ultrasound Multi-Frame Image Storage
    strcmp("1.2.840.10008.5.1.4.1.1.3.1", dcm_sopclassuid) == 0 ||
    // Ultrasound Image Storage (Retired)
    strcmp("1.2.840.10008.5.1.4.1.1.6", dcm_sopclassuid) == 0 ||
    // Ultrasound Image Storage
    strcmp("1.2.840.10008.5.1.4.1.1.6.1", dcm_sopclassuid) == 0 ||
    // X-Ray Angiographic Image Storage (this one has Aspect Ratio)
    strcmp("1.2.840.10008.5.1.4.1.1.12.1", dcm_sopclassuid) == 0
 )
    {
    char pixel_aspect_ratio[1024];
    DCM_ELEMENT ipar = {DCM_IMGPIXELASPECTRATIO, DCM_IS, "", 1,
      sizeof(pixel_aspect_ratio), {pixel_aspect_ratio}};
    ctx = NULL;
    if (DCM_GetElementValue(&object, &ipar, &rtn_length, &ctx) != DCM_NORMAL)
      {
      if (image == this->CurrentImage)
        {
        vtkWarningMacro(
          "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
          << "]\n => unable to get pixel aspect ratio, assuming 1.0, 1.0!");
        }
      info->Spacing[0] = info->Spacing[1] = 1.0;
      }
    else
      {
      pixel_aspect_ratio[rtn_length] = '\0';
      float vertical_size, horizontal_size;
      int r = sscanf(pixel_aspect_ratio, "%f\\%f", &vertical_size, &horizontal_size);
      // We only have a ratio thus, fix x spacing to be 1.0
      // default y spacing to 1 in case of problem parsing the string
      info->Spacing[0] = info->Spacing[1] = 1.0;
      if (horizontal_size && r == 2)
        {
        info->Spacing[1] = vertical_size/horizontal_size;
        }
      else
        {
        vtkWarningMacro(
          "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
          << "]\n => unable to decode pixel aspect ratio, assuming 1.0, 1.0!");
        }
      }
    info->Spacing[2] = 1.0;

    // Image Position (Patient)
    info->Position[0] = info->Position[1] = info->Position[2] = 0.0;

    // Image Orientation (Patient)
    info->Orientation[0] = 1.0;
    info->Orientation[1] = 0.0;
    info->Orientation[2] = 0.0;
    info->Orientation[3] = 0.0;
    info->Orientation[4] = 1.0;
    info->Orientation[5] = 0.0;
    }
  // For CR we have Imager Pixel Spacing (0018,1164)
  else if (
    // Computed Radiography Image Storage
    strcmp("1.2.840.10008.5.1.4.1.1.1", dcm_sopclassuid) == 0 ||
    // Digital X-Ray Image Storage - For Presentation
    strcmp("1.2.840.10008.5.1.4.1.1.1.1", dcm_sopclassuid) == 0 ||
    // Digital X-Ray Image Storage - For Processing
    strcmp("1.2.840.10008.5.1.4.1.1.1.1.1", dcm_sopclassuid) == 0 ||
    // Digital Mammography Image Storage - For Presentation
    strcmp("1.2.840.10008.5.1.4.1.1.1.2", dcm_sopclassuid) == 0 ||
    // Digital Mammography Image Storage - For Processing
    strcmp("1.2.840.10008.5.1.4.1.1.1.2.1", dcm_sopclassuid) == 0
 )
    {
    char imager_pixel_spacing[1024];
    DCM_ELEMENT iips = {DCM_ACQIMAGERPIXELSPACING, DCM_DS, "", 1,
      sizeof(imager_pixel_spacing), {imager_pixel_spacing}};
    ctx = NULL;
    if (DCM_GetElementValue(&object, &iips, &rtn_length, &ctx) != DCM_NORMAL)
      {
      if (image == this->CurrentImage)
        {
        vtkWarningMacro(
          "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
          << "]\n => unable to get imager pixel spacing, assuming 1.0, 1.0!");
        }
      info->Spacing[0] = info->Spacing[1] = 1.0;
      }
    else
      {
      imager_pixel_spacing[rtn_length] = '\0';
      sscanf(imager_pixel_spacing, "%lf\\%lf", info->Spacing, info->Spacing + 1);
      }
    info->Spacing[2] = 1.0;

    // Image Position (Patient)
    info->Position[0] = info->Position[1] = info->Position[2] = 0.0;

    // Image Orientation (Patient)
    info->Orientation[0] = 1.0; 
    info->Orientation[1] = 0.0;
    info->Orientation[2] = 0.0;
    info->Orientation[3] = 0.0;
    info->Orientation[4] = 1.0;
    info->Orientation[5] = 0.0;
    }
  else // Cross sectional like CT, MR, PET have 'Pixel Spacing' (0028,0030)
    // This is also the default when no SOPClassUID was found (old ACRNEMA ?)
    {
    char pixel_spacing[1024];
    DCM_ELEMENT ips = {DCM_IMGPIXELSPACING, DCM_DS, "", 1,
      sizeof(pixel_spacing), {pixel_spacing}};
    ctx = NULL;
    if (DCM_GetElementValue(&object, &ips, &rtn_length, &ctx) != DCM_NORMAL)
      {
      if (image == this->CurrentImage)
        {
        vtkWarningMacro(
          "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
          << "]\n => unable to get pixel spacing, assuming 1.0, 1.0!");
        }
      info->Spacing[0] = info->Spacing[1] = 1.0;
      if (this->Options->GetRequireSpatialInformation() && *dcm_sopclassuid &&
           strcmp("1.2.840.10008.5.1.4.1.1.7", dcm_sopclassuid) != 0)
        {
        this->FailureStatus |= vtkDICOMCollector::FailureNoSpatialInformation;
        (void) DCM_CloseFile(&object);
        (void) DCM_CloseObject(&object);
        (void) COND_PopCondition(FALSE);
        return FailureNoSpatialInformation;
        }
      }
    else
      {
      pixel_spacing[rtn_length] = '\0';
      sscanf(pixel_spacing, "%lf\\%lf", info->Spacing, info->Spacing + 1);
      }
    info->Spacing[2] = 1.0;

    // Check for ImagePosition (or set to 0, 0, 0)

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
    info->Position[0] = file->GetXOrigin();
    info->Position[1] = file->GetYOrigin();
    info->Position[2] = file->GetZOrigin();
#elif defined(KWCommonPro_USE_CTNLIB)
    char image_position[1024];
    DCM_ELEMENT ipos = {DCM_RELIMAGEPOSITIONPATIENT, DCM_DS, "", 1,
      sizeof(image_position), {image_position}};
    ctx = NULL;
    if (DCM_GetElementValue(&object, &ipos, &rtn_length, &ctx) != DCM_NORMAL)
      {
      if (image == this->CurrentImage)
        {
        vtkWarningMacro(
          "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
          << "]\n => unable to get image position, assuming 0.0, 0.0, 0.0!");
        }
      info->Position[0] = info->Position[1] = info->Position[2] = 0.0;
      if (this->Options->GetRequireSpatialInformation() && *dcm_sopclassuid &&
           strcmp("1.2.840.10008.5.1.4.1.1.7", dcm_sopclassuid) != 0)
        {
        this->FailureStatus |= vtkDICOMCollector::FailureNoSpatialInformation;
        (void) DCM_CloseFile(&object);
        (void) DCM_CloseObject(&object);
        (void) COND_PopCondition(FALSE);
        return vtkDICOMCollector::FailureNoSpatialInformation;
        }
      }
    else
      {
      image_position[rtn_length] = '\0';
      sscanf(image_position, "%lf\\%lf\\%lf", 
        info->Position, info->Position + 1, info->Position + 2);
      }
#endif

    // Check for ImageOrientation. 
    int found_ori = this->GetImageOrientation(
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
      file, info
#elif defined(KWCommonPro_USE_CTNLIB)
      object, info, image
#endif  
   ); 
    if (!found_ori)
      {
      if (this->Options->GetRequireSpatialInformation() && *dcm_sopclassuid &&
        strcmp("1.2.840.10008.5.1.4.1.1.7", dcm_sopclassuid) != 0)
        {
        this->FailureStatus |= vtkDICOMCollector::FailureNoSpatialInformation;
        (void) DCM_CloseFile(&object);
        (void) DCM_CloseObject(&object);
        (void) COND_PopCondition(FALSE);
        return vtkDICOMCollector::FailureNoSpatialInformation;
        }
      }
    }
#endif
  double dot = vtkMath::Dot(info->GetOrientationRow(), info->GetOrientationColumn());
  if (fabs(dot) > 1e-4)
    {
    this->FailureStatus |= vtkDICOMCollector::FailureNotOrthogonal;
    vtkErrorMacro("Non-orthognal aquisition is not supported: " << fabs(dot));
    return vtkDICOMCollector::FailureNotOrthogonal;
    }
  
  // Make sure the Gantry tilt is not present:
#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char gantry_tilt[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_gantry_tilt = file->GetEntryValue(0x0018, 0x1120);
  if (dcm_gantry_tilt == gdcm::GDCM_UNFOUND || dcm_gantry_tilt == "")
    {
    // ok no gantry tilt
    }
  else
    {
    sprintf(gantry_tilt, dcm_gantry_tilt.c_str());
    rtn_length = dcm_gantry_tilt.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_gantry_tilt =
    {DCM_ACQGANTRYTILT, DCM_DS, "", 1,
     sizeof(gantry_tilt), {(char *)gantry_tilt}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_gantry_tilt, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    // ok no gantry tilt
    }
  else
    {
#endif
    gantry_tilt[rtn_length] = '\0';
    if (!this->Options->GetSupportGantryTilt() && atof(gantry_tilt) != 0.0)
      {
      this->FailureStatus |= vtkDICOMCollector::FailureGantryTilt;
      (void) DCM_CloseFile(&object);
      (void) DCM_CloseObject(&object);
      (void) COND_PopCondition(FALSE);
      return vtkDICOMCollector::FailureGantryTilt;
      }
    }
#endif

  // Ok let's cleanup the orientation, since we want the following to be seen as
  // identical:
  // (0020,0037) DS [0.00973553\0.999953\0\0.0158696\-0.000152587\-0.999874]
  // (0020,0037) DS [0.00973554\0.999953\0\0.0158696\-0.000152587\-0.999874]
  // (0020,0037) DS [0.00973555\0.999953\0\0.0158695\-0.000152587\-0.999874]
  // (0020,0037) DS [0.00973555\0.999953\0\0.0158696\-0.000152558\-0.999874]
  // (0020,0037) DS [0.00973555\0.999953\0\0.0158696\-0.000152587\-0.999874]
  for(int idx = 0; idx < 6; ++idx)
    {
    const int precision = 100000;
    float round_ori = floor((info->Orientation[idx]*precision)+0.5) / precision;
    info->Orientation[idx] = round_ori;
    }

  // Compute the cross product to get the image orientation

  vtkMath::Cross(info->GetOrientationRow(),
                 info->GetOrientationColumn(),
                 info->GetOrientationImage());

  // Check for the samples per pixel
  // Or guess it from the photometric interpretation
  
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  info->SamplesPerPixel = file->GetSamplesPerPixel();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT spp = {DCM_IMGSAMPLESPERPIXEL, DCM_US, "", 1,
                     sizeof(info->SamplesPerPixel), 
                     {(char *)&(info->SamplesPerPixel)}};
  ctx = NULL;
  if (DCM_GetElementValue(&object, &spp, &rtn_length, &ctx) != DCM_NORMAL) 
    {
    char photo_interp[1024];
    DCM_ELEMENT pi = {DCM_IMGPHOTOMETRICINTERP, DCM_CS, "", 1,
                      sizeof(photo_interp), {(char *)photo_interp}};
    ctx = NULL;
    if (DCM_GetElementValue(&object, &pi, &rtn_length, &ctx) != DCM_NORMAL) 
      {
      if (image == this->CurrentImage)
        {
        vtkWarningMacro(
          "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
          << "]\n => unable to compute samples/pixel, assuming greyscale!");
        }
      info->SamplesPerPixel = 1;
      }
    else
      {
      // remove trailing spaces
      while (rtn_length && photo_interp[rtn_length - 1] == ' ')
        {
        rtn_length--;
        }
      photo_interp[rtn_length] = '\0';
      if (!strcmp(DCM_IMGPHOTOINTERPMONOCHROME1, photo_interp))
        {
        info->SamplesPerPixel = 1;
        }
      else if (!strcmp(DCM_IMGPHOTOINTERPMONOCHROME2, photo_interp))
        {
        info->SamplesPerPixel = 1;
        }
      else if (!strcmp(DCM_IMGPHOTOINTERPRGB, photo_interp))
        {
        info->SamplesPerPixel = 3;
        }
      else if (!strcmp(DCM_IMGPHOTOINTERPHSV, photo_interp))
        {
        info->SamplesPerPixel = 3;
        }
      else if (!strcmp(DCM_IMGPHOTOINTERPRGBA, photo_interp))
        {
        info->SamplesPerPixel = 4;
        }
      else if (!strcmp(DCM_IMGPHOTOINTERPCMYK, photo_interp))
        {
        info->SamplesPerPixel = 4;
        }
      else
        {
        vtkWarningMacro(
          "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
          << "]\n => unable to get photometric interpretation: " << photo_interp);
        info->SamplesPerPixel = 0;
        }
      }
    }
#endif
  if (!this->Options->GetSupportMultipleSamplesPerPixel() && 
      info->SamplesPerPixel != 1)
    {
    this->FailureStatus |= vtkDICOMCollector::FailureMultipleSamplesPerPixel;
    (void) DCM_CloseFile(&object);
    (void) DCM_CloseObject(&object);
    (void) COND_PopCondition(FALSE);
    return vtkDICOMCollector::FailureMultipleSamplesPerPixel;
    }

  // Check for High Bit
  // if we cannot get HighBit from the data, assume it is BitsStored - 1

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  if (file->CheckIfEntryExist(0x0028, 0x0102))
    {
    info->HighBit = file->GetHighBitPosition();
    }
  else
    {
    info->HighBit = info->BitsStored - 1;
    }
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT hb = {DCM_IMGHIGHBIT, DCM_US, "", 1,
                    sizeof(info->HighBit), {(char *) &(info->HighBit)}};
  ctx = NULL;
  if (DCM_GetElementValue(&object, &hb, &rtn_length, &ctx) != DCM_NORMAL) 
    {
    if (image == this->CurrentImage)
      {
      vtkWarningMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => does not indicate the HighBit, we will assume it is "
        "BitsStored - 1!");
      }
    info->HighBit = info->BitsStored - 1;
    }
#endif
  
  // Check for BitsAllocated
  // If we cannot get BitsAllocated from the data, compute it using BitsStored

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  info->BitsAllocated = file->GetBitsAllocated();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT ba = {DCM_IMGBITSALLOCATED, DCM_US, "", 1,
                    sizeof(info->BitsAllocated), 
                    {(char *) &(info->BitsAllocated)}};
  ctx = NULL;
  if (DCM_GetElementValue(&object, &ba, &rtn_length, &ctx) != DCM_NORMAL) 
    {
    if (image == this->CurrentImage)
      {
      vtkWarningMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => does not indicate the Bits Allocated, we will compute it "
        "based on BitsStored");
      }
    info->BitsAllocated = ((info->BitsStored + 7) / 8) * 8;
    }
#endif

  // Check for Acquisition Number 0x0020,0x0012

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string acquisition_number = file->GetEntryValue(0x0020, 0x0012);
  if (acquisition_number != gdcm::GDCM_UNFOUND && acquisition_number != "")  
    {
    info->AcquisitionNumber = atoi(acquisition_number.c_str());
    }
  else
    {
    info->AcquisitionNumber = -1;
    }
#elif defined(KWCommonPro_USE_CTNLIB)
  char acquisition_number[1024];
  DCM_ELEMENT an = {DCM_RELACQUISITIONNUMBER, DCM_IS, "", 1,
                    sizeof(acquisition_number), {acquisition_number}};
  ctx = NULL;
  if (DCM_GetElementValue(&object, &an, &rtn_length, &ctx) == DCM_NORMAL) 
    {
    acquisition_number[rtn_length] = '\0';
    info->AcquisitionNumber = atoi(acquisition_number);
    }
  else
    {
    info->AcquisitionNumber = -1;
    }
#endif

  // Get the slightly less required elements...
  // Study instance UID
  // Series instance UID
  // SOP instance UID (0008|0018) (unique identifier of a file in a series)

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string study_instance_uid = file->GetEntryValue(0x0020, 0x000d);
  if (study_instance_uid != gdcm::GDCM_UNFOUND && study_instance_uid != "")  
    {
    info->SetStudyInstanceUID(study_instance_uid.c_str());
    }
  else
    {
    info->SetStudyInstanceUID(0);
    }

  vtksys_stl::string series_instance_uid = file->GetEntryValue(0x0020, 0x000e);
  if (series_instance_uid != gdcm::GDCM_UNFOUND && series_instance_uid != "")  
    {
    info->SetSeriesInstanceUID(series_instance_uid.c_str());
    }
  else
    {
    info->SetSeriesInstanceUID(0);
    }

  vtksys_stl::string sop_instance_uid = file->GetEntryValue(0x0008, 0x0018);
  if (sop_instance_uid != gdcm::GDCM_UNFOUND && sop_instance_uid != "")  
    {
    info->SetSOPInstanceUID(sop_instance_uid.c_str());
    }
  else
    {
    info->SetSOPInstanceUID(0);
    }
  
#elif defined(KWCommonPro_USE_CTNLIB)
  
  char study_instance_uid[1024];  //  0x0020,0x000d
  char series_instance_uid[1024]; //  0x0020,0x000e

  DCM_ELEMENT opts[] = {
    {DCM_RELSTUDYINSTANCEUID, DCM_UI, "", 1,
     sizeof(study_instance_uid), {study_instance_uid}},
    {DCM_RELSERIESINSTANCEUID, DCM_UI, "", 1,
     sizeof(series_instance_uid), {series_instance_uid}}
  };
  
  if (DCM_ParseObject(&object, opts, 2, NULL, 0, NULL) != DCM_NORMAL) 
    {
    if (image == this->CurrentImage)
      {
      vtkWarningMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => missing some optional elements, study, series, and "
        "acquisition numbers may not be valid.");
      }
    info->SetStudyInstanceUID(0);
    info->SetSeriesInstanceUID(0);
    }
  else
    {
    info->SetStudyInstanceUID(study_instance_uid);
    info->SetSeriesInstanceUID(series_instance_uid);
    }

  // Get the SOP Instance UID (0008,0018).
  
  char dcm_sopinstanceuid[1024];
  DCM_ELEMENT isiu =
    {DCM_IDSOPINSTANCEUID, DCM_UI, "", 1, 
     sizeof(dcm_sopinstanceuid), {(char*)dcm_sopinstanceuid}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &isiu, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length) 
    {
    vtkWarningMacro(
      "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
      << "]\n => unable to get the SOP Class UID. Attempting to guess values.");
    *dcm_sopinstanceuid = '\0';
    info->SetSOPInstanceUID(0);
    }
  else
    {
    dcm_sopinstanceuid[rtn_length] = '\0';
    info->SetSOPInstanceUID(dcm_sopinstanceuid);
    }
#endif


#if defined(KWCommonPro_USE_CTNLIB)
  // Check for the number of frames: (0028,0008). This is not required
  // but if we find something >1, then fail
  char number_of_frames[1024];
  DCM_ELEMENT inof = {DCM_IMGNUMBEROFFRAMES, DCM_IS, "", 1,
                    sizeof(number_of_frames), {number_of_frames}};
  ctx = NULL;
  if (DCM_GetElementValue(&object, &inof, &rtn_length, &ctx) == DCM_NORMAL)
    {
    number_of_frames[rtn_length] = '\0';
    unsigned int numFrames;
    if (sscanf(number_of_frames, "%ud", &numFrames) == 1)
      {
      info->NumberOfFrames = numFrames;
      }
    else
      {
      info->NumberOfFrames = 1;
      }
    }
#endif

#if defined(KWCommonPro_USE_CTNLIB)
  // Check for the instance number (type 2): (0020,0013). This is not required
  // unless we are in the case of hidden series within same serie UID
  char instance_number[1024];
  DCM_ELEMENT in = {DCM_RELIMAGENUMBER, DCM_IS, "", 1,
                    sizeof(instance_number), {instance_number}};
  ctx = NULL;
  if (DCM_GetElementValue(&object, &in, &rtn_length, &ctx) == DCM_NORMAL)
    {
    instance_number[rtn_length] = '\0';
    unsigned int instance;
    if (sscanf(instance_number, "%ud", &instance) == 1)
      {
      info->InstanceNumber = instance;
      }
    else
      {
      info->InstanceNumber = 0;
      }
    }
#endif

  // Get the transfer syntax

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  info->SetTransferSyntaxUID(file->GetTransferSyntax().c_str());
#elif defined(KWCommonPro_USE_CTNLIB)
  char transfer_syntax_uid[1024];

  DCM_ELEMENT syn[] = {
    {DCM_METATRANSFERSYNTAX, DCM_UI, "", 1,
     sizeof(transfer_syntax_uid), {transfer_syntax_uid}}
  };

  if (DCM_ParseObject(&object, syn, 1, NULL, 0, NULL) != DCM_NORMAL)
    {
    info->SetTransferSyntaxUID(0);
    }
  else
    {
    info->SetTransferSyntaxUID(transfer_syntax_uid);
    }
#endif
 
  // Get the pixel padding value.
  // Pixel Padding Value (0028,0120) is used to pad non-rectangular images to 
  // rectangular format. (set to smallest signed short if not found)
  // ftp://medical.nema.org/medical/dicom/final/cp414_ft.pdf
  // 
  // The logic is as follows:
  //  - If it is CT, and PixelPaddingValue is not found, assume it is -1024.
  //  - If it is CT, and PixelPaddingValue is found, use it.
  //  - If not CT,   and PixelPaddingValue is found, use it.
  //  - If not CT,   and PixelPaddingValue is not found, use VTK_SHORT_MIN 

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string pixelpaddingvalue = file->GetEntryValue(0x0028, 0x0120);
  if (pixelpaddingvalue != gdcm::GDCM_UNFOUND && pixelpaddingvalue != "")  
    {
    info->PixelPaddingValue = atoi(pixelpaddingvalue.c_str());
    info->PixelPaddingValueTagFound = true;
    }
  else
    {
    info->PixelPaddingValue = VTK_SHORT_MIN;
    info->PixelPaddingValueTagFound = false;
    }
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT pixelpaddingvalue = 
    {DCM_IMGPIXELPADDINGVALUE, DCM_CTX, "", 1,
     sizeof(info->PixelPaddingValue), {(char *) &(info->PixelPaddingValue)}};
  ctx = NULL;
  if (DCM_GetElementValue(&object, &pixelpaddingvalue, &rtn_length, &ctx) != DCM_NORMAL) 
    {
    // 
    info->PixelPaddingValue = VTK_SHORT_MIN;
    info->PixelPaddingValueTagFound = false;
    }
  else 
    {
    info->PixelPaddingValueTagFound = true;
    }
 
  ctx = NULL;
#endif

  // Check for RescaleIntercept (or set it to 0.0)

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  info->RescaleIntercept = file->GetRescaleIntercept();
#elif defined(KWCommonPro_USE_CTNLIB)
  char rescale_intercept[1024];
  DCM_ELEMENT ri = {DCM_IMGRESCALEINTERCEPT, DCM_DS, "", 1,
                    sizeof(rescale_intercept), {rescale_intercept}};
  ctx = NULL;
  if (DCM_GetElementValue(&object, &ri, &rtn_length, &ctx) != DCM_NORMAL) 
    {
    info->RescaleIntercept = 0.0;
    }
  else
    {
    rescale_intercept[rtn_length] = '\0';
    info->RescaleIntercept = atof(rescale_intercept);
    }
#endif

  // Check for RescaleSlope (or set it to 1.0)

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  info->RescaleSlope = file->GetRescaleSlope();
#elif defined(KWCommonPro_USE_CTNLIB)
  char rescale_slope[1024];
  DCM_ELEMENT rs = {DCM_IMGRESCALESLOPE, DCM_DS, "", 1,
                    sizeof(rescale_slope), {rescale_slope}};
  ctx = NULL;
  if (DCM_GetElementValue(&object, &rs, &rtn_length, &ctx) != DCM_NORMAL) 
    {
    info->RescaleSlope = 1.0;
    }
  else
    {
    rescale_slope[rtn_length] = '\0';
    info->RescaleSlope = atof(rescale_slope);
    }
#endif

  // Close the DICOM File

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  delete file;
#elif defined(KWCommonPro_USE_CTNLIB)
  (void) DCM_CloseFile(&object);
  (void) DCM_CloseObject(&object);
  (void) COND_PopCondition(TRUE);
#endif

  return vtkDICOMCollector::FailureNone;
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::RetrieveImageMedicalProperties(ImageSlot* image, int quiet)
{

  // Open DICOM file

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
   gdcm::File *file = new gdcm::File();
   file->SetFileName(image->GetFileName());
   file->Load();
   if (!file->IsReadable())
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_OBJECT *object = NULL;
  DCM_Debug(FALSE);
  DCM_CreateObject(&object, 0);
  if (!this->OpenDicomFile(image->GetFileName(), &object))
#endif
    {
    if  (!quiet)
      {
      vtkErrorMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => unable to load!");
      }
    (void) DCM_CloseFile(&object);
    (void) DCM_CloseObject(&object);
    (void) COND_PopCondition(FALSE);
    return vtkDICOMCollector::FailureNotReadable;
    }

  // Allocate header info

  if (!image->MedicalProperties)
    {
    image->MedicalProperties = vtkMedicalImageProperties::New();
    }

  vtkMedicalImageProperties *hinfo = image->MedicalProperties;

  // Get elelemts

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  size_t rtn_length;
#elif defined(KWCommonPro_USE_CTNLIB)
  void *ctx;
  U32 rtn_length;
#endif

  // Patient Name

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char patient_name[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_patient_name = file->GetEntryValue(0x0010, 0x0010);
  if (dcm_patient_name == gdcm::GDCM_UNFOUND || dcm_patient_name == "")  
    {
    hinfo->SetPatientName(NULL);
    }
  else
    {
    sprintf(patient_name, dcm_patient_name.c_str());
    rtn_length = dcm_patient_name.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_patient_name = 
    {DCM_PATNAME, DCM_PN, "", 1, 
     sizeof(patient_name), {patient_name}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_patient_name, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length) 
    {
    hinfo->SetPatientName(NULL);
    }
  else
    {
#endif
    // remove trailing spaces
    while (rtn_length && patient_name[rtn_length - 1] == ' ')
      {
      rtn_length--;
      }
    patient_name[rtn_length] = '\0';
    this->RearrangeName(patient_name);
    hinfo->SetPatientName(patient_name);
    }
#endif

  // Patient ID

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char patient_id[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_patient_id = file->GetEntryValue(0x0010, 0x0020);
  if (dcm_patient_id == gdcm::GDCM_UNFOUND || dcm_patient_id == "")  
    {
    hinfo->SetPatientID(NULL);
    }
  else
    {
    sprintf(patient_id, dcm_patient_id.c_str());
    rtn_length = dcm_patient_id.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_patient_id = 
    {DCM_PATID, DCM_LO, "", 1, 
     sizeof(patient_id), {patient_id}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_patient_id, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length)
    {
    hinfo->SetPatientID(NULL);
    }
  else
    {
#endif
    // remove trailing spaces
    while (rtn_length && patient_id[rtn_length - 1] == ' ')
      {
      rtn_length--;
      }
    patient_id[rtn_length] = '\0';
    hinfo->SetPatientID(patient_id);
    }
#endif

  // Patient age

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char patient_age[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_patient_age = file->GetEntryValue(0x0010, 0x1010);
  if (dcm_patient_age == gdcm::GDCM_UNFOUND || dcm_patient_age == "")  
    {
    hinfo->SetPatientAge(NULL);
    }
  else
    {
    sprintf(patient_age, dcm_patient_age.c_str());
    rtn_length = dcm_patient_age.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_patient_age = 
    {DCM_PATAGE, DCM_AS, "", 1,
     sizeof(patient_age), {(char *)patient_age}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_patient_age, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length)
    {
    hinfo->SetPatientAge(NULL);
    }
  else
    {
#endif
    patient_age[rtn_length] = '\0';
    hinfo->SetPatientAge(patient_age);
    }
#endif

  // Patient sex

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char patient_sex[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_patient_sex = file->GetEntryValue(0x0010, 0x0040);
  if (dcm_patient_sex == gdcm::GDCM_UNFOUND || dcm_patient_sex == "")  
    {
    hinfo->SetPatientSex(NULL);
    }
  else
    {
    sprintf(patient_sex, dcm_patient_sex.c_str());
    rtn_length = dcm_patient_sex.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_patient_sex = 
    {DCM_PATSEX, DCM_CS, "", 1,
     sizeof(patient_sex), {(char *)patient_sex}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_patient_sex, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length)
    {
    hinfo->SetPatientSex(NULL);
    }
  else
    {
#endif
    // remove trailing spaces
    while (rtn_length && patient_sex[rtn_length - 1] == ' ')
      {
      rtn_length--;
      }
    patient_sex[rtn_length] = '\0';
    hinfo->SetPatientSex(patient_sex);
    }
#endif

  // Patient birth date

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char patient_birth_date[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_patient_birth_date = 
    file->GetEntryValue(0x0010, 0x0030);
  if (dcm_patient_birth_date == gdcm::GDCM_UNFOUND || 
      dcm_patient_birth_date == "")  
    {
    hinfo->SetPatientBirthDate(NULL);
    }
  else
    {
    sprintf(patient_birth_date, dcm_patient_birth_date.c_str());
    rtn_length = dcm_patient_birth_date.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_patient_birth_date = 
    {DCM_PATBIRTHDATE, DCM_DA, "", 1,
     sizeof(patient_birth_date), {patient_birth_date}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_patient_birth_date, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length) 
    {
    hinfo->SetPatientBirthDate(NULL);
    }
  else
    {
#endif
    patient_birth_date[rtn_length] = '\0';
    hinfo->SetPatientBirthDate(patient_birth_date);
    }
#endif
  // Image Date

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char image_date[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_image_date = file->GetEntryValue(0x0008, 0x0023);
  if (dcm_image_date == gdcm::GDCM_UNFOUND || dcm_image_date == "")  
    {
    hinfo->SetImageDate(NULL);
    }
  else
    {
    sprintf(image_date, dcm_image_date.c_str());
    rtn_length = dcm_image_date.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_image_date = 
    {DCM_IDIMAGEDATE, DCM_DA, "", 1, 
     sizeof(image_date), {image_date}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_image_date, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length) 
    {
    hinfo->SetImageDate(NULL);
    }
  else
    {
#endif
    image_date[rtn_length] = '\0';
    hinfo->SetImageDate(image_date);
    }
#endif

  // Image Time

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char image_time[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_image_time = file->GetEntryValue(0x0008, 0x0033);
  if (dcm_image_time == gdcm::GDCM_UNFOUND || dcm_image_time == "")  
    {
    hinfo->SetImageTime(NULL);
    }
  else
    {
    sprintf(image_time, dcm_image_time.c_str());
    rtn_length = dcm_image_time.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_image_time = 
    {DCM_IDIMAGETIME, DCM_TM, "", 1, 
     sizeof(image_time), {image_time}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_image_time, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length) 
    {
    hinfo->SetImageTime(NULL);
    }
  else
    {
#endif
    // Rewrite 091443 into 09:14:43
    if (rtn_length == 6)
      {
      image_time[8] = '\0';
      image_time[7] = image_time[5];
      image_time[6] = image_time[4];
      image_time[5] = ':';
      image_time[4] = image_time[3];
      image_time[3] = image_time[2];
      image_time[2] = ':';
      }
    // else simply pass an empty string (rtn_length will be == 0)
    image_time[rtn_length] = '\0';
    hinfo->SetImageTime(image_time);
    }
#endif

  // Image Number

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char image_number[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_image_number = file->GetEntryValue(0x0020, 0x0013);
  if (dcm_image_number == gdcm::GDCM_UNFOUND || dcm_image_number == "")  
    {
    hinfo->SetImageNumber(NULL);
    }
  else
    {
    sprintf(image_number, dcm_image_number.c_str());
    rtn_length = dcm_image_number.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_image_number = 
    {DCM_RELIMAGENUMBER, DCM_IS, "", 1, 
     sizeof(image_number), {image_number}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_image_number, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length) 
    {
    hinfo->SetImageNumber(NULL);
    }
  else
    {
#endif
    image_number[rtn_length] = '\0';
    hinfo->SetImageNumber(image_number);
    }
#endif

  // Acquisition Date

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char acquisition_date[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_acquisition_date = 
    file->GetEntryValue(0x0008, 0x0022);
  if (dcm_acquisition_date == gdcm::GDCM_UNFOUND || 
      dcm_acquisition_date == "")  
    {
    hinfo->SetAcquisitionDate(NULL);
    }
  else
    {
    sprintf(acquisition_date, dcm_acquisition_date.c_str());
    rtn_length = dcm_acquisition_date.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_acquisition_date = 
    {DCM_IDACQUISITIONDATE, DCM_DA, "", 1, 
     sizeof(acquisition_date), {acquisition_date}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_acquisition_date, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length)  
    {
    hinfo->SetAcquisitionDate(NULL);
    }
  else
    {
#endif
    acquisition_date[8] = '\0';
    hinfo->SetAcquisitionDate(acquisition_date);
    }
#endif

  // Acquisition Time

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char acquisition_time[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_acquisition_time = 
    file->GetEntryValue(0x0008, 0x0032);
  if (dcm_acquisition_time == gdcm::GDCM_UNFOUND || 
      dcm_acquisition_time == "")  
    {
    hinfo->SetAcquisitionTime(NULL);
    }
  else
    {
    sprintf(acquisition_time, dcm_acquisition_time.c_str());
    rtn_length = dcm_acquisition_time.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_acquisition_time = 
    {DCM_IDACQUISITIONTIME, DCM_TM, "", 1, 
     sizeof(acquisition_time), {acquisition_time}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_acquisition_time, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length)  
    {
    hinfo->SetAcquisitionTime(NULL);
    }
  else
    {
#endif
    acquisition_time[8] = '\0';
    acquisition_time[7] = acquisition_time[5];
    acquisition_time[6] = acquisition_time[4];
    acquisition_time[5] = ':';
    acquisition_time[4] = acquisition_time[3];
    acquisition_time[3] = acquisition_time[2];
    acquisition_time[2] = ':';
    hinfo->SetAcquisitionTime(acquisition_time);
    }
#endif

  // Series Number

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char series_number[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_series_number = file->GetEntryValue(0x0020, 0x0011);
  if (dcm_series_number == gdcm::GDCM_UNFOUND || dcm_series_number == "")  
    {
    hinfo->SetSeriesNumber(NULL);
    }
  else
    {
    sprintf(series_number, dcm_series_number.c_str());
    rtn_length = dcm_series_number.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_series_number = 
    {DCM_RELSERIESNUMBER, DCM_IS,"", 1, 
     sizeof(series_number), {series_number}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_series_number, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length)  
    {
    hinfo->SetSeriesNumber(NULL);
    }
  else
    {
#endif
    series_number[rtn_length] = '\0';
    hinfo->SetSeriesNumber(series_number);
    }
#endif

  // Study ID

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char study_id[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER 
  vtksys_stl::string dcm_study_id = file->GetEntryValue(0x0020, 0x0010);
  if (dcm_study_id == gdcm::GDCM_UNFOUND || dcm_study_id == "")  
    {
    hinfo->SetStudyID(NULL);
    }
  else
    {
    sprintf(study_id, dcm_study_id.c_str());
    rtn_length = dcm_study_id.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_study_id = 
    {DCM_RELSTUDYID, DCM_SH, "", 1, 
     sizeof(study_id), {study_id}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_study_id, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length) 
    {
    hinfo->SetStudyID(NULL);
    }
  else
    {
#endif
    study_id[rtn_length] = '\0';
    hinfo->SetStudyID(study_id);
    }
#endif

  // Study description

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char study_description[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_study_description = 
    file->GetEntryValue(0x0008, 0x1030);
  if (dcm_study_description == gdcm::GDCM_UNFOUND || 
      dcm_study_description == "")  
    {
    hinfo->SetStudyDescription(NULL);
    }
  else
    {
    sprintf(study_description, dcm_study_description.c_str());
    rtn_length = dcm_study_description.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_study_description = 
    {DCM_IDSTUDYDESCRIPTION, DCM_LO, "", 1, 
     sizeof(study_description), {study_description}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_study_description, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length)  
    {
    hinfo->SetStudyDescription(NULL);
    }
  else
    {
#endif
    // remove trailing spaces
    while (rtn_length && study_description[rtn_length - 1] == ' ')
      {
      rtn_length--;
      }
    study_description[rtn_length] = '\0';
    hinfo->SetStudyDescription(study_description);
    }
#endif

  // Modality

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char modality[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_modality = file->GetEntryValue(0x0008, 0x0060);
  if (dcm_modality == gdcm::GDCM_UNFOUND || dcm_modality == "")
    {
    hinfo->SetModality(NULL);
    }
  else
    {
    sprintf(modality, dcm_modality.c_str());
    rtn_length = dcm_modality.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_modality =
    {DCM_IDMODALITY, DCM_CS, "", 1,
     sizeof(modality), {(char *)modality}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_modality, &rtn_length, &ctx) != DCM_NORMAL || 
      !rtn_length) 
    {
    hinfo->SetModality(NULL);
    }
  else
    {
#endif
    modality[rtn_length] = '\0';
    hinfo->SetModality(modality);
    }
#endif

  // Check for WindowCenter and WindowWidth
  /* Example:
     Window Center (0028,1050)  1-n  DS [00045\000470]
     Window Width (0028,1051)  1-n  DS [0106\03412]
  */

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char window_centers[1024] = {'\0'};
  char window_widths[1024] = {'\0'};
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_window_centers = file->GetEntryValue(0x0028, 0x1050);
  if (dcm_window_centers != gdcm::GDCM_UNFOUND && dcm_window_centers != "")  
    {
    sprintf(window_centers, dcm_window_centers.c_str());
    }
  vtksys_stl::string dcm_window_widths = file->GetEntryValue(0x0028, 0x1051);
  if (dcm_window_widths != gdcm::GDCM_UNFOUND && dcm_window_widths != "")  
    {
    sprintf(window_widths, dcm_window_widths.c_str());
    }
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_window_centers = 
    {DCM_IMGWINDOWCENTER, DCM_DS, "", 1,
     sizeof(window_centers), {window_centers}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_window_centers, &rtn_length, &ctx) == DCM_NORMAL) 
    {
    window_centers[rtn_length] = '\0';
    }

  DCM_ELEMENT dcm_window_widths = 
    {DCM_IMGWINDOWWIDTH, DCM_DS, "", 1,
     sizeof(window_widths), {window_widths}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_window_widths, &rtn_length, &ctx) == DCM_NORMAL) 
    {
    window_widths[rtn_length] = '\0';
    }
#endif

  /*
    Note that if there is a rescale, then Window Width and Window Level are
    specified in rescaled units.  This means that you first apply rescale
    to the pixel value, then apply window/level.
    UPDATE: this is true most of the time, except, unfortunately, for PET 
    where the scale is so small that some vendors ignore it and specificy 
    window width and center in stored values, not rescaled values :( 
    http://groups.google.com/group/comp.protocols.dicom/msg/2b05e7eed494d2a7
  */

  if (*window_centers && *window_widths)
    {
    int fixwl = (hinfo->GetModality() && !strcmp(hinfo->GetModality(), "PT") &&
                 image->Info && image->Info->RescaleSlope < 0.1);
    char *ptr_wc = window_centers;
    char *ptr_ww = window_widths;
    while (ptr_wc && ptr_ww)
      {
      double w = atof(ptr_ww);
      double l = atof(ptr_wc);
      if (fixwl)
        {
        w = w * image->Info->RescaleSlope + image->Info->RescaleIntercept;
        l = l * image->Info->RescaleSlope + image->Info->RescaleIntercept;
        }
      hinfo->AddWindowLevelPreset(w, l);
      ptr_wc = strchr(ptr_wc, '\\');
      if (ptr_wc)
        {
        ptr_wc++;
        }
      ptr_ww = strchr(ptr_ww, '\\');
      if (ptr_ww)
        {
        ptr_ww++;
        }
      }
    }
#endif

  // Series description

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char seriesdescription[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_seriesdescription = file->GetEntryValue(0x0008, 0x103e);
  if (dcm_seriesdescription == gdcm::GDCM_UNFOUND || dcm_seriesdescription == "")
    {
    hinfo->SetSeriesDescription(NULL);
    }
  else
    {
    sprintf(seriesdescription, dcm_seriesdescription.c_str());
    rtn_length = dcm_seriesdescription.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_seriesdescription =
    {DCM_IDSERIESDESCR, DCM_LO, "", 1,
     sizeof(seriesdescription), {(char *)seriesdescription}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_seriesdescription, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    hinfo->SetSeriesDescription(NULL);
    }
  else
    {
#endif
    seriesdescription[rtn_length] = '\0';
    hinfo->SetSeriesDescription(seriesdescription);
    }
#endif

  // Manufacturer

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char manufacturer[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_manufacturer =
    file->GetEntryValue(0x0008, 0x1090);
  if (dcm_manufacturer == gdcm::GDCM_UNFOUND ||
      dcm_manufacturer == "")
    {
    hinfo->SetManufacturer(NULL);
    }
  else
    {
    sprintf(manufacturer, dcm_manufacturer.c_str());
    rtn_length = dcm_manufacturer.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_manufacturer =
    {DCM_IDMANUFACTURER, DCM_LO, "", 1,
     sizeof(manufacturer), {(char *)manufacturer}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_manufacturer, &rtn_length, &ctx) != DCM_NORMAL
      || !rtn_length)
    {
    hinfo->SetManufacturer(NULL);
    }
  else
    {
#endif
    // remove trailing spaces
    while (rtn_length && manufacturer[rtn_length - 1] == ' ')
      {
      rtn_length--;
      }
    manufacturer[rtn_length] = '\0';
    hinfo->SetManufacturer(manufacturer);
    }
#endif

  // Manufacturer's model name

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char manufacturer_model_name[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_manufacturer_model_name =
    file->GetEntryValue(0x0008, 0x1090);
  if (dcm_manufacturer_model_name == gdcm::GDCM_UNFOUND ||
      dcm_manufacturer_model_name == "")
    {
    hinfo->SetManufacturerModelName(NULL);
    }
  else
    {
    sprintf(manufacturer_model_name, dcm_manufacturer_model_name.c_str());
    rtn_length = dcm_manufacturer_model_name.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_manufacturer_model_name =
    {DCM_IDMANUFACTURERMODEL, DCM_LO, "", 1,
     sizeof(manufacturer_model_name), {(char *)manufacturer_model_name}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_manufacturer_model_name, &rtn_length, &ctx) != DCM_NORMAL
      || !rtn_length)
    {
    hinfo->SetManufacturerModelName(NULL);
    }
  else
    {
#endif
    // remove trailing spaces
    while (rtn_length && manufacturer_model_name[rtn_length - 1] == ' ')
      {
      rtn_length--;
      }
    manufacturer_model_name[rtn_length] = '\0';
    hinfo->SetManufacturerModelName(manufacturer_model_name);
    }
#endif

  // Station name

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char station_name[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_station_name = file->GetEntryValue(0x0008, 0x1010);
  if (dcm_station_name == gdcm::GDCM_UNFOUND || dcm_station_name == "")
    {
    hinfo->SetStationName(NULL);
    }
  else
    {
    sprintf(station_name, dcm_station_name.c_str());
    rtn_length = dcm_station_name.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_station_name =
    {DCM_IDSTATIONNAME, DCM_SH, "", 1,
     sizeof(station_name), {(char *)station_name}};
  ctx = NULL;
  if (DCM_GetElementValue(
        &object, &dcm_station_name, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    hinfo->SetStationName(NULL);
    }
  else
    {
#endif
    station_name[rtn_length] = '\0';
    hinfo->SetStationName(station_name);
    }
#endif

  // Institution name

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char institution_name[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_institution_name =
    file->GetEntryValue(0x0008, 0x0080);
  if (dcm_institution_name == gdcm::GDCM_UNFOUND || dcm_institution_name == "")
    {
    hinfo->SetInstitutionName(NULL);
    }
  else
    {
    sprintf(institution_name, dcm_institution_name.c_str());
    rtn_length = dcm_institution_name.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_institution_name =
    {DCM_IDINSTITUTIONNAME, DCM_LO, "", 1,
     sizeof(institution_name), {(char *)institution_name}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_institution_name, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    hinfo->SetInstitutionName(NULL);
    }
  else
    {
#endif
    // remove trailing spaces
    while (rtn_length && institution_name[rtn_length - 1] == ' ')
      {
      rtn_length--;
      }
    institution_name[rtn_length] = '\0';
    hinfo->SetInstitutionName(institution_name);
    }
#endif

  // Convolution kernel

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char convolution_kernel[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_convolution_kernel =
    file->GetEntryValue(0x0018, 0x1210);
  if (dcm_convolution_kernel == gdcm::GDCM_UNFOUND ||
      dcm_convolution_kernel == "")
    {
    hinfo->SetConvolutionKernel(NULL);
    }
  else
    {
    sprintf(convolution_kernel, dcm_convolution_kernel.c_str());
    rtn_length = dcm_convolution_kernel.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_convolution_kernel =
    {DCM_ACQCONVOLUTIONKERNEL, DCM_SH, "", 1,
     sizeof(convolution_kernel), {(char *)convolution_kernel}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_convolution_kernel, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    hinfo->SetConvolutionKernel(NULL);
    }
  else
    {
#endif
    convolution_kernel[rtn_length] = '\0';
    hinfo->SetConvolutionKernel(convolution_kernel);
    }
#endif

  // Slice Thickness

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char slice_thickness[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_slice_thickness =
    file->GetEntryValue(0x0018, 0x0050);
  if (dcm_slice_thickness == gdcm::GDCM_UNFOUND ||
      dcm_slice_thickness == "")
    {
    hinfo->SetSliceThickness(NULL);
    }
  else
    {
    sprintf(slice_thickness, dcm_slice_thickness.c_str());
    rtn_length = dcm_slice_thickness.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_slice_thickness =
    {DCM_ACQSLICETHICKNESS, DCM_DS, "", 1,
     sizeof(slice_thickness), {(char *)slice_thickness}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_slice_thickness, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    hinfo->SetSliceThickness(NULL);
    }
  else
    {
#endif
    slice_thickness[rtn_length] = '\0';
    hinfo->SetSliceThickness(slice_thickness);
    }
#endif

  // Peak Kilo Voltage (kVP)

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char kvp[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_kvp = file->GetEntryValue(0x0018, 0x0060);
  if (dcm_kvp == gdcm::GDCM_UNFOUND || dcm_kvp == "")
    {
    hinfo->SetKVP(NULL);
    }
  else
    {
    sprintf(kvp, dcm_kvp.c_str());
    rtn_length = dcm_kvp.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_kvp =
    {DCM_ACQKVP, DCM_DS, "", 1,
     sizeof(kvp), {(char *)kvp}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_kvp, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    hinfo->SetKVP(NULL);
    }
  else
    {
#endif
    kvp[rtn_length] = '\0';
    long int kvp_i = atol(kvp);
    sprintf(kvp, "%li", kvp_i);
    hinfo->SetKVP(kvp);
    }
#endif

  // Gantry Tilt

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char gantry_tilt[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_gantry_tilt = file->GetEntryValue(0x0018, 0x1120);
  if (dcm_gantry_tilt == gdcm::GDCM_UNFOUND || dcm_gantry_tilt == "")
    {
    hinfo->SetGantryTilt(NULL);
    }
  else
    {
    sprintf(gantry_tilt, dcm_gantry_tilt.c_str());
    rtn_length = dcm_gantry_tilt.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_gantry_tilt =
    {DCM_ACQGANTRYTILT, DCM_DS, "", 1,
     sizeof(gantry_tilt), {(char *)gantry_tilt}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_gantry_tilt, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    hinfo->SetGantryTilt(NULL);
    }
  else
    {
#endif
    gantry_tilt[rtn_length] = '\0';
    hinfo->SetGantryTilt(gantry_tilt);
    }
#endif

  // Exposure Time

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char exposure_time[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_exposure_time = file->GetEntryValue(0x0018, 0x1150);
  if (dcm_exposure_time == gdcm::GDCM_UNFOUND || dcm_exposure_time == "")
    {
    hinfo->SetExposureTime(NULL);
    }
  else
    {
    sprintf(exposure_time, dcm_exposure_time.c_str());
    rtn_length = dcm_exposure_time.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_exposure_time =
    {DCM_ACQEXPOSURETIME, DCM_IS, "", 1,
     sizeof(exposure_time), {(char *)exposure_time}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_exposure_time, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    hinfo->SetExposureTime(NULL);
    }
  else
    {
#endif
    exposure_time[rtn_length] = '\0';
    long int exposure_time_i = atol(exposure_time);
    sprintf(exposure_time, "%li", exposure_time_i);
    hinfo->SetExposureTime(exposure_time);
    }
#endif

  // X-ray Tube Current

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char xray_tube_current[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_xray_tube_current =
    file->GetEntryValue(0x0018, 0x1151);
  if (dcm_xray_tube_current == gdcm::GDCM_UNFOUND ||
      dcm_xray_tube_current == "")
    {
    hinfo->SetXRayTubeCurrent(NULL);
    }
  else
    {
    sprintf(xray_tube_current, dcm_xray_tube_current.c_str());
    rtn_length = dcm_xray_tube_current.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_xray_tube_current =
    {DCM_ACQXRAYTUBECURRENT, DCM_IS, "", 1,
     sizeof(xray_tube_current), {(char *)xray_tube_current}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_xray_tube_current, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    hinfo->SetXRayTubeCurrent(NULL);
    }
  else
    {
#endif
    xray_tube_current[rtn_length] = '\0';
    long int xray_tube_current_i = atol(xray_tube_current);
    sprintf(xray_tube_current, "%li", xray_tube_current_i);
    hinfo->SetXRayTubeCurrent(xray_tube_current);
    }
#endif

  // Exposure

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char exposure[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_exposure = file->GetEntryValue(0x0018, 0x1152);
  if (dcm_exposure == gdcm::GDCM_UNFOUND || dcm_exposure == "")
    {
    hinfo->SetExposure(NULL);
    }
  else
    {
    sprintf(exposure, dcm_exposure.c_str());
    rtn_length = dcm_exposure.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_exposure =
    {DCM_ACQEXPOSURE, DCM_IS, "", 1,
     sizeof(exposure), {(char *)exposure}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_exposure, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    hinfo->SetExposure(NULL);
    }
  else
    {
#endif
    exposure[rtn_length] = '\0';
    long int exposure_i = atol(exposure);
    sprintf(exposure, "%li", exposure_i);
    hinfo->SetExposure(exposure);
    }
#endif

  // The three following tag: Echo Time, Repetition Time and maybe
  // EchoTrainLength are meant for MR images
  // Echo Time

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char echo_time[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_echo_time = file->GetEntryValue(0x0018, 0x0081);
  if (dcm_echo_time == gdcm::GDCM_UNFOUND || dcm_echo_time == "")
    {
    hinfo->SetEchoTime(NULL);
    }
  else
    {
    sprintf(echo_time, dcm_echo_time.c_str());
    rtn_length = dcm_echo_time.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_echo_time =
    {DCM_ACQECHOTIME, DCM_DS, "", 1,
     sizeof(echo_time), {(char *)echo_time}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_echo_time, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    hinfo->SetEchoTime(NULL);
    }
  else
    {
#endif
    echo_time[rtn_length] = '\0';
    hinfo->SetEchoTime(echo_time);
    }
#endif

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char repetition_time[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_repetition_time = file->GetEntryValue(0x0018, 0x0081);
  if (dcm_repetition_time == gdcm::GDCM_UNFOUND || dcm_repetition_time == "")
    {
    hinfo->SetRepetitionTime(NULL);
    }
  else
    {
    sprintf(repetition_time, dcm_repetition_time.c_str());
    rtn_length = dcm_repetition_time.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_repetition_time =
    {DCM_ACQREPETITIONTIME, DCM_DS, "", 1,
     sizeof(repetition_time), {(char *)repetition_time}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_repetition_time, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    hinfo->SetRepetitionTime(NULL);
    }
  else
    {
#endif
    repetition_time[rtn_length] = '\0';
    hinfo->SetRepetitionTime(repetition_time);
    }
#endif

  // Table Height

#if defined(KWCommonPro_USE_GDCM) || defined(KWCommonPro_USE_CTNLIB)
  char table_height[1024];
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  vtksys_stl::string dcm_table_height = file->GetEntryValue(0x0018, 0x1130);
  if (dcm_table_height == gdcm::GDCM_UNFOUND || dcm_table_height == "")
    {
    hinfo->SetTableHeight(NULL);
    }
  else
    {
    sprintf(table_height, dcm_table_height.c_str());
    rtn_length = dcm_table_height.size();
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_ELEMENT dcm_table_height =
    {DCM_ACQTABLEHEIGHT, DCM_DS, "", 1,
     sizeof(table_height), {(char *)table_height}};
  ctx = NULL;
  if (DCM_GetElementValue
      (&object, &dcm_table_height, &rtn_length, &ctx) != DCM_NORMAL ||
      !rtn_length)
    {
    hinfo->SetTableHeight(NULL);
    }
  else
    {
#endif
    table_height[rtn_length] = '\0';
    hinfo->SetTableHeight(table_height);
    }
#endif
  
  // Close the DICOM File

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
  delete file;
#elif defined(KWCommonPro_USE_CTNLIB)
  (void) DCM_CloseFile(&object);
  (void) DCM_CloseObject(&object);
  (void) COND_PopCondition(TRUE);
#endif

  return vtkDICOMCollector::FailureNone;
}

struct vtkDICOMCompStruct
{
  unsigned char *Pointer;
  unsigned long BytesExported;
};

#ifndef KWCommonPro_USE_GDCM
static CONDITION
dumpCallback(void *buf, U32 bytesExported, int index,
             int vtkNotUsed(startFlag),
             int lastFlag,
             int vtkNotUsed(startOfFragment), void *ctx)
{
  vtkDICOMCompStruct *results = (vtkDICOMCompStruct *)ctx;

  if (index == 0)
    {
    U32 i;
    U32 *p;
    p = (U32*)buf;
    for (i = 0; i < bytesExported; i += 4, p++)
      {
      printf("%5u %8x %8u\n", (unsigned int)i, (unsigned int)*p, (unsigned  int)*p);
      }
    }
  else if (lastFlag)
    {
    if (bytesExported != 0)
      {
      printf("Hmmm, we really don't expect the %u bytes you exported to me\n",
             (unsigned int)bytesExported);
      printf("I don't know what to do with these\n");
      }
    }
  else
    {
    memcpy(results->Pointer + results->BytesExported, buf, bytesExported);
    results->BytesExported += bytesExported;
    }
  return DCM_NORMAL;
}
#endif

#define vtkDicomCollectorPixelPaddingValueReplaceMacro1 \
              if (determinePaddedExtents) \
                { \
                ++curr_col; \
                curr_col = curr_col % info->Columns; \
                if (curr_col == 0) \
                  { \
                  min_col_found = false; \
                  this->PaddedExtents->InsertTupleValue(curr_row, padded_extents); \
                  padded_extents[0]=0; \
                  padded_extents[1]=0; \
                  ++curr_row; \
                  } \
                if (*cs != pixelPaddingValue) \
                  { \
                  if (curr_col > padded_extents[1]) \
                    { \
                    padded_extents[1] = curr_col; \
                    } \
                  if (!min_col_found) \
                    { \
                    padded_extents[0] = curr_col; \
                    min_col_found = true; \
                    } \
                  } \
                } \
              if (*cs == pixelPaddingValue) \
                { \
                *cs = replacedValue; \
                }

#define vtkDicomCollectorPixelPaddingValueReplaceMacro \
          if (doPixelPaddingValueReplace) \
            { \
            vtkDicomCollectorPixelPaddingValueReplaceMacro1 \
            } \
          else \
            { \
            if (shift_num) \
              { \
              *cs >>= shift_num; \
              } \
            if (mask_data)  \
              { \
              if (and_flag)  \
                { \
                *cs &= mask_data; \
                } \
              else \
                { \
                if (*cs & mask_data) \
                  { \
                  *cs |= mask_data; \
                  } \
                } \
              } \
            } \
          if (min && *cs < *min) \
            { \
            *min = *cs; \
            } \
          if (max && *cs > *max) \
            { \
            *max = *cs; \
            } \
          cs += pixel_step;


//----------------------------------------------------------------------------
int vtkDICOMCollector::RetrieveImageData(ImageSlot* image, 
                                         void *data, 
                                         int shift_mask,
                                         int quiet,
                                         double *min,
                                         double *max,
                                         int pixel_step)
{
  // Make sure we have the image info

  vtkDICOMCollector::ImageInfo *info = this->GetImageInfo(image);
  if (!info)
    {
    if (!quiet)
      {
      vtkErrorMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => unable to query DICOM info!");
      }
    return vtkDICOMCollector::FailureUnknownError;
    }

  // Open DICOM file to get the pixel data

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_IMAGE
   gdcm::File *file = new gdcm::File();
   file->SetFileName(image->GetFileName());
   file->Load();
   if (!file->IsReadable())
#elif defined(KWCommonPro_USE_CTNLIB)
  DCM_OBJECT *object = NULL;
  DCM_Debug(FALSE);
  DCM_CreateObject(&object, 0);
  if (!this->OpenDicomFile(image->GetFileName(), &object))
#endif
    {
    if  (!quiet)
      {
      vtkErrorMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => unable to load!");
      }
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_IMAGE
#elif defined(KWCommonPro_USE_CTNLIB)
    (void) DCM_CloseFile(&object);
    (void) DCM_CloseObject(&object);
    (void) COND_PopCondition(FALSE);
#endif
    return vtkDICOMCollector::FailureNotReadable;
    }

  unsigned short bits_stored = info->BitsStored;
  unsigned short high_bit = info->HighBit;

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_IMAGE

  gdcm::FileHelper *file_helper = new gdcm::FileHelper(file);
  size_t image_data_size = file_helper->GetImageDataSize();
  memcpy(data, file_helper->GetImageData(), image_data_size);
  delete file_helper;

  high_bit = bits_stored - 1;

#elif defined(KWCommonPro_USE_CTNLIB)
  // Check the amount of data in PixelData

  DCM_ELEMENT p = {DCM_PXLPIXELDATA, DCM_OT, "", 1, 0, {NULL}};
  U32 rtn_length;
  if (DCM_GetElementSize(&object, p.tag, &rtn_length) != DCM_NORMAL) 
    {
    if  (!quiet)
      {
      vtkWarningMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => can't get pixel data size!");
      }
    this->FailureStatus |= vtkDICOMCollector::FailureCannotGetPixelDataSize;
    (void) DCM_CloseFile(&object);
    (void) DCM_CloseObject(&object);
    (void) COND_PopCondition(FALSE);
    return vtkDICOMCollector::FailureCannotGetPixelDataSize;
    }
  
  // Check the size of the pixel data to make sure we have the correct amount

  U32 real_length = rtn_length;
  U32 check_length_in_bytes = 
    info->Rows * info->Columns * info->Planes * info->SamplesPerPixel;
  if (info->BitsAllocated == 16)
    {
    check_length_in_bytes *= 2;
    }

  if (check_length_in_bytes > real_length) 
    {
    if  (!quiet)
      {
      vtkWarningMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => too little pixel data supplied!");
      }
    this->FailureStatus |= vtkDICOMCollector::FailureTooLittlePixelData;
    (void) DCM_CloseFile(&object);
    (void) DCM_CloseObject(&object);
    (void) COND_PopCondition(FALSE);
    return vtkDICOMCollector::FailureTooLittlePixelData;
    }
  
  // Now retrieve the data

  p.length = check_length_in_bytes;
  p.d.ot = (void *)data;

  void *ctx = NULL;
  CONDITION cond;
  unsigned char *compDataValues = 0;
  if (real_length == DCM_UNSPECIFIEDLENGTH)
    {
    DCM_TAG tag = DCM_PXLPIXELDATA;
    unsigned char *buf = new unsigned char [check_length_in_bytes + 1024];
    compDataValues = new unsigned char [check_length_in_bytes + 1024];

    vtkDICOMCompStruct results;
    results.BytesExported = 0;
    results.Pointer = compDataValues;
    cond = 
      DCM_GetCompressedValue(&object, tag, buf, sizeof buf, dumpCallback,
                             &results);
    real_length = results.BytesExported;
    memcpy(data, compDataValues, real_length);
    delete [] compDataValues;
    delete [] buf;
    }
  else
    {
    cond = DCM_GetElementValue(&object, &p, &rtn_length, &ctx);
    }

  if ((cond != DCM_NORMAL) && (cond != DCM_GETINCOMPLETE)) 
    {
    if  (!quiet)
      {
      vtkWarningMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => can't get pixel data!");
      }
    this->FailureStatus |= vtkDICOMCollector::FailureTooLittlePixelData;
    (void) DCM_CloseFile(&object);
    (void) DCM_CloseObject(&object);
    (void) COND_PopCondition(FALSE);
    return vtkDICOMCollector::FailureTooLittlePixelData;
    }

  if ((check_length_in_bytes < real_length) && (cond != DCM_GETINCOMPLETE)) 
    {
    if  (!quiet)
      {
      vtkWarningMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => can't get all pixel data!");
      }
    return vtkDICOMCollector::FailureTooLittlePixelData;
    (void) DCM_CloseFile(&object);
    (void) DCM_CloseObject(&object);
    (void) COND_PopCondition(FALSE);
    return vtkDICOMCollector::FailureTooLittlePixelData;
    }

  // Uncompress the data if it is jpeg

  if (info->GetTransferSyntaxUID() &&
      !strcmp(info->GetTransferSyntaxUID(), 
              DICOM_TRANSFERJPEGEXTENDEDPROC2AND4))
    {
    // is it 12 bit
    if (info->BitsStored >= 12)
      {
      vtkErrorMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => in JPEG12 format! Enable GDCM support!");
      /*
      // pass the jpeg stream to a reader for decompression
      vtkJPEG12Reader *rdr = vtkJPEG12Reader::New();
      vtkUnsignedCharArray *uca = vtkUnsignedCharArray::New();
      uca->SetVoidArray(data,real_length,1);
      rdr->SetMemoryBuffer(uca);
      rdr->Update();

      // store the result in memory, we must invert Y since the following
      // code is expecting upper right origin and the JPEG reader is
      // returning lower left
      unsigned char *rdrPtr = static_cast<unsigned char *>(
        rdr->GetOutput()->GetPointData()->GetScalars()->GetVoidPointer(0));
      unsigned short rowSize = info->Columns*2;
      for (unsigned short yidx = 0; yidx < info->Rows; yidx++)
        {
        memcpy((unsigned char *)data + yidx * rowSize,
               rdrPtr + (info->Rows - yidx - 1) * rowSize,
               rowSize);
        }
      rdr->Delete();
      uca->Delete();
      */
      }
    }

  // Uncompress the data if it is jpeg lossless

  if (info->GetTransferSyntaxUID() &&
      (!strcmp(info->GetTransferSyntaxUID(),
              DICOM_TRANSFERJPEGLOSSLESSPROC14) ||
       !strcmp(info->GetTransferSyntaxUID(),
               DICOM_TRANSFERJPEGLOSSLESSPROCFIRSTORDERREDICT)))
    {
    // is it 12 bit
    if (info->BitsStored >= 12)
      {
      vtkErrorMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => in JPEG lossless format! Enable GDCM support!");
      /*
      JPEGLosslessDecodeImage((unsigned char *)data, (unsigned short *)data,
                              16, real_length);
      */
      }
    }
#endif

  // Perform preliminary shifting and masking if necessary
  // or search for min/max
  bool replacePaddingValueNeedsDoing = false;
  bool determinePaddedExtents = false;

  // If the PixelPaddingValueTag is found and modality is CT,
  if ((info->PixelPaddingValueTagFound || this->PixelPaddingValueTagAssumed) && 
      !strcmp(this->GetImageMedicalProperties(image)->GetModality(), "CT"))
    {
    replacePaddingValueNeedsDoing = true;
    if (!PaddedExtents)
      {
      determinePaddedExtents = true;
      this->PaddedExtents = vtkUnsignedShortArray::New();
      this->PaddedExtents->SetNumberOfComponents(2);
      this->PaddedExtents->SetNumberOfTuples(info->Rows);
      }
    }

  unsigned short curr_row = 0;
  unsigned short curr_col = 0;
  unsigned short padded_extents[2];
  padded_extents[0]=0; padded_extents[1]=0;
  bool min_col_found=false;

  // Padding value to replace with if the tag is found or assumed
  unsigned short paddingValue;
  if (info->PixelPaddingValueTagFound)
    {
    paddingValue = info->PixelPaddingValue;
    }
  else if (this->PixelPaddingValueTagAssumed)
    {
    paddingValue = this->PixelPaddingValue;
    }

  double air_value = 0.0;
  const char *modality =
    this->GetImageMedicalProperties(image)->GetModality();
  if (modality && !strcmp(modality, "CT"))
    {
    air_value = -1024.0;
    }

  if (info->SamplesPerPixel == 1 && (shift_mask || (min && max)))
    {
    int mask_data = 0, and_flag = 0, shift_num = 0;

    // GDCM masks for us already

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_IMAGE
#else
    // Mask the 16 bit data if necessary

    if (info->BitsAllocated != bits_stored)
      {
      if (info->PixelRepresentation == 
          vtkDICOMCollector::ImageInfo::PixelRepresentationSigned)
        {
        mask_data = 0xFFFFFFFF << (high_bit);
        } 
      else if (info->PixelRepresentation == 
               vtkDICOMCollector::ImageInfo::PixelRepresentationUnsigned)
        {
        and_flag = 1;
        mask_data = 0x0FFFF >> (15 - high_bit);
        }
      }

    // Check to see if the data was shifted...

    if ((bits_stored - 1) != high_bit) 
      {  
      shift_num = high_bit - bits_stored + 1;
      }
#endif

    // Perform it

    if (mask_data || shift_num || (min && max)) 
      {
      bool doPixelPaddingValueReplace = false;
      if (info->BitsAllocated == 16) 
        {
        if (info->PixelRepresentation == 
            vtkDICOMCollector::ImageInfo::PixelRepresentationUnsigned)
          {
          typedef unsigned short PixelRepresentationType; 
          PixelRepresentationType *cs = (PixelRepresentationType *) data;
          PixelRepresentationType *cs_end = 
            cs + info->Rows * info->Columns * info->Planes;

          if (info->PixelPaddingValueTagFound ||
              this->PixelPaddingValueTagAssumed)
            { 
            doPixelPaddingValueReplace = true;
            }
          const PixelRepresentationType pixelPaddingValue = 
              static_cast< PixelRepresentationType >(paddingValue);
          const PixelRepresentationType replacedValue = 
            static_cast< PixelRepresentationType >(((double)(air_value)
            -(double)info->RescaleIntercept)/(double) info->RescaleSlope); 
          while (cs < cs_end) 
            {
            vtkDicomCollectorPixelPaddingValueReplaceMacro
            }
          }
        else
          {
          typedef short PixelRepresentationType;
          PixelRepresentationType *cs = (PixelRepresentationType *) data;
          PixelRepresentationType *cs_end = 
            cs + info->Rows * info->Columns * info->Planes;

          if (info->PixelPaddingValueTagFound 
              || this->PixelPaddingValueTagAssumed)
            {
            doPixelPaddingValueReplace = true;
            }
          const PixelRepresentationType pixelPaddingValue = 
              static_cast< PixelRepresentationType >(paddingValue);
          const PixelRepresentationType replacedValue = 
            static_cast< PixelRepresentationType >(((double)(air_value)
            -(double)info->RescaleIntercept)/(double) info->RescaleSlope); 

          while (cs < cs_end) 
            {
            vtkDicomCollectorPixelPaddingValueReplaceMacro
            }
          }
        } 
      else if (info->BitsAllocated == 8) 
        {
        if (info->PixelRepresentation == 
            vtkDICOMCollector::ImageInfo::PixelRepresentationUnsigned)
          {
          typedef unsigned char PixelRepresentationType;
          if (info->PixelPaddingValueTagFound)
            {
            doPixelPaddingValueReplace = true;
            }
          const PixelRepresentationType pixelPaddingValue = 
              static_cast< PixelRepresentationType >(paddingValue);
          const PixelRepresentationType replacedValue = 
            static_cast< PixelRepresentationType >(((double)(air_value)
            -(double)info->RescaleIntercept)/(double) info->RescaleSlope); 

          PixelRepresentationType *cs = (PixelRepresentationType *) data;
          PixelRepresentationType *cs_end = 
            cs + info->Rows * info->Columns * info->Planes;
          while (cs < cs_end) 
            {
            vtkDicomCollectorPixelPaddingValueReplaceMacro
            }
          }
        else
          {
          typedef char PixelRepresentationType;
          PixelRepresentationType *cs = (PixelRepresentationType *) data;
          PixelRepresentationType *cs_end = 
            cs + info->Rows * info->Columns * info->Planes;
          if (info->PixelPaddingValueTagFound)
            {
            doPixelPaddingValueReplace = true;
            }
          const PixelRepresentationType pixelPaddingValue = 
              static_cast< PixelRepresentationType >(paddingValue);
          const PixelRepresentationType replacedValue = 
            static_cast< PixelRepresentationType >(((double)(air_value)
            -(double)info->RescaleIntercept)/(double) info->RescaleSlope); 

          while (cs < cs_end) 
            {
            vtkDicomCollectorPixelPaddingValueReplaceMacro
            }
          }
        }
      //high_bit = bits_stored - 1;

      replacePaddingValueNeedsDoing = replacePaddingValueNeedsDoing & false;
      if (determinePaddedExtents)
        {
        this->PaddedExtents->InsertTupleValue(curr_row, padded_extents);
        }

      }
    else
      {
      replacePaddingValueNeedsDoing = replacePaddingValueNeedsDoing & true;
      }
    }

  if (replacePaddingValueNeedsDoing)
    {
    // Replace pixel padding value 
    //
    if (info->BitsAllocated == 16) 
      {
      if (info->PixelRepresentation == 
          vtkDICOMCollector::ImageInfo::PixelRepresentationUnsigned)
        {
        typedef unsigned short PixelRepresentationType;
        const PixelRepresentationType pixelPaddingValue = 
              static_cast< PixelRepresentationType >(paddingValue);
        const PixelRepresentationType replacedValue = 
          static_cast< PixelRepresentationType >(((double)(air_value)
            -(double)info->RescaleIntercept)/(double) info->RescaleSlope); 
        PixelRepresentationType *cs = (PixelRepresentationType *) data;
        PixelRepresentationType *cs_end = 
          cs + info->Rows * info->Columns * info->Planes;
        while (cs < cs_end) 
          {
          vtkDicomCollectorPixelPaddingValueReplaceMacro1
          cs += pixel_step;
          }
        }
      else
        {
        typedef short PixelRepresentationType;
        const PixelRepresentationType pixelPaddingValue = 
              static_cast< PixelRepresentationType >(paddingValue);
        const PixelRepresentationType replacedValue = 
          static_cast< PixelRepresentationType >(((double)(air_value)
            -(double)info->RescaleIntercept)/(double) info->RescaleSlope); 
        PixelRepresentationType *cs = (PixelRepresentationType *) data;
        PixelRepresentationType *cs_end = 
          cs + info->Rows * info->Columns * info->Planes;
        while (cs < cs_end) 
          {
          vtkDicomCollectorPixelPaddingValueReplaceMacro1
          cs += pixel_step;
          }
        }
      }
    else if (info->BitsAllocated == 8) 
      {
      if (  info->PixelRepresentation == 
             vtkDICOMCollector::ImageInfo::PixelRepresentationUnsigned)
        {
        typedef unsigned char PixelRepresentationType;
        PixelRepresentationType *cs = (PixelRepresentationType *) data;
        PixelRepresentationType *cs_end = 
          cs + info->Rows * info->Columns * info->Planes;
        const PixelRepresentationType pixelPaddingValue = 
              static_cast< PixelRepresentationType >(paddingValue);
        const PixelRepresentationType replacedValue = 
          static_cast< PixelRepresentationType >(((double)(air_value)
            -(double)info->RescaleIntercept)/(double) info->RescaleSlope); 
        while (cs < cs_end) 
          {
          vtkDicomCollectorPixelPaddingValueReplaceMacro1
          cs += pixel_step;
          }
        }
      else 
        {
        typedef char PixelRepresentationType;
        PixelRepresentationType *cs = (PixelRepresentationType *) data;
        PixelRepresentationType *cs_end = 
          cs + info->Rows * info->Columns * info->Planes;
        const PixelRepresentationType pixelPaddingValue = 
              static_cast< PixelRepresentationType >(paddingValue);
        const PixelRepresentationType replacedValue = 
          static_cast< PixelRepresentationType >((double)(air_value)
            *(double)info->RescaleIntercept/(double) info->RescaleSlope); 
        while (cs < cs_end) 
          {
          vtkDicomCollectorPixelPaddingValueReplaceMacro1
          cs += pixel_step;
          }
        }        
      }
    }
  
  // Close the DICOM File

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_IMAGE
  delete file;
#elif defined(KWCommonPro_USE_CTNLIB)
  (void) DCM_CloseFile(&object);
  (void) DCM_CloseObject(&object);
  (void) COND_PopCondition(TRUE);
#endif

  return vtkDICOMCollector::FailureNone;
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::RearrangeName(char *name)
{
  vtksys_stl::vector<vtksys_stl::string> split_elems;
  vtksys::SystemTools::Split(name, split_elems, '^');

  vtksys_stl::string full_name;

  /*
    The five components in their order of occurrence are: 
    0: family name complex,
    1: given name complex, 
    2: middle name, 
    3: name prefix, 
    4: name suffix. 
    Any of the five components may be an empty string.
  */
  
  unsigned int order[] = {3, 1, 2, 0, 4};
  unsigned int i;
  for (i = 0; i < sizeof(order) / sizeof(order[0]); i++)
    {
    if (split_elems.size() > order[i])
      {
      if (full_name.size())
        {
        full_name += " ";
        }
      full_name += split_elems[order[i]];
      }
    }

  strcpy(name, full_name.c_str());
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::CollectCurrentImage()
{
  // Check if we have a filename

  if (!this->GetFileName())
    {
    vtkWarningMacro("FileName is not set, can not collect current image!");
    return vtkDICOMCollector::FailureMissingFile;
    }

  // This class acts as a cache, image info should not be recollected if they
  // have been collected already. 
  // The cache is emptied automatically when FileName is changed.
  // In that case, if CurrentImage has still been allocated, then it has been
  // collected already (when FileName is changed, the image is deallocated)

  if (this->CurrentImage)
    {
    return vtkDICOMCollector::FailureNone;
    }

  this->CurrentImage = new ImageSlot;

  // Build info for the image corresponding to FileName

  this->CurrentImage->SetFileName(this->GetFileName());
  int retrieve_status = this->RetrieveImageInfo(this->CurrentImage);
  if (retrieve_status != vtkDICOMCollector::FailureNone)
    {
    // might be a DICOM file per-se, but not one we support
    return retrieve_status; 
    }
  // In case of success make selectedimage points to CurrentImage:
  this->SelectedImage = this->CurrentImage;

  return vtkDICOMCollector::FailureNone;
}

template<int DIM>
bool lower_orientation(const double *p, const double *q)
{
  if (p[DIM-1] < q[DIM-1]) return true;
  else if (p[DIM-1] > q[DIM-1]) return false;
  else return lower_orientation<DIM-1>(p, q);
}

template<>
bool lower_orientation<1>(const double *p, const double *q)
{
  return p[0] < q[0];
}

struct ltpos
{
  bool operator()(const double* pos1, const double* pos2) const
    {
    return lower_orientation<3>(pos1, pos2);
    }
};
struct ltori
{
  bool operator()(const double* ori1, const double* ori2) const
    {
    return lower_orientation<6>(ori1, ori2);
    }
};

//----------------------------------------------------------------------------
bool ImageSlotCompareOrientation(vtkDICOMCollector::ImageSlot *targ1,
                                 vtkDICOMCollector::ImageSlot *targ2)
{
  return lower_orientation<6>(targ1->Info->Orientation, 
                              targ2->Info->Orientation);
}


//----------------------------------------------------------------------------
int vtkDICOMCollector::CollectAllSlices()
{
  // Check if we have a filename

  if (!this->GetFileName())
    {
    vtkWarningMacro("FileName is not set, can not collect all slices!");
    return 0;
    }

  // This class acts as a cache, slices should not be recollected if they
  // have been collected already. 
  // The cache is emptied automatically when FileName is changed.

  if (this->Internals->Slices.size())
    {
    return (int)this->Internals->Slices.size();
    }

  // Collect the image corresponding to the current FileName

  if (this->CollectCurrentImage() != vtkDICOMCollector::FailureNone)
    {
    return 0;
    }

  // Find the directory part and then list all files
  // Unless they were specified explicitly

  vtkStringArray *filenames = vtkStringArray::New();
  if (this->FileNames->GetNumberOfValues() > 1 || 
      !this->Options->GetExploreDirectory())
    {
    // In all cases we want all files to be read, otherwise simply give up
    filenames->SetNumberOfTuples(this->FileNames->GetNumberOfValues());
    for (int file = 0; file < this->FileNames->GetNumberOfValues(); ++file)
      {
      vtkStdString &filename = this->FileNames->GetValue(file);
      if (vtksys::SystemTools::FileExists(filename.c_str()))
        {
        filenames->SetValue(file, filename);
        }
      else /*if (!this->ExploreDirectory)*/
        {
        // We were passed a file that do not exist.
        // This is pretty bad, let's mark that as a failure
        this->FailureStatus |= vtkDICOMCollector::FailureMissingFile;
        vtkErrorMacro("Missing: " << filename.c_str());
        filenames->Delete();
        return 0;
        }
      }
    }
  else
    {
    vtksys_stl::string directory = 
      vtksys::SystemTools::GetFilenamePath(this->GetFileName());

    vtkDirectory *dir = vtkDirectory::New();
    if (!dir->Open(directory.c_str()))
      {
      vtkWarningMacro(
        "DICOM file [" << (this->GetFileName() ? this->GetFileName() : "")
        << "]\n => could not read directory information!");
      dir->Delete();
      return 0;
      }
    for (int i = 0; i < dir->GetNumberOfFiles(); i++)
      {
      if (strcmp(dir->GetFile(i), ".") == 0 ||
          strcmp(dir->GetFile(i), "..") == 0)
        {
        continue;
        }

      vtksys_stl::string filename(directory);
      filename += '/';
      filename += dir->GetFile(i);
      filenames->InsertNextValue(filename.c_str());
      }
    dir->Delete();
    }

  this->InvokeEvent(vtkCommand::StartEvent);
  double progress;

  // First collect all the slices

  // clock_t start_clock = clock();

  ImageSlot *slice = NULL;
  vtkDICOMCollectorInternals::ImagesContainer slices;

  int i;
  for (i = 0; i < filenames->GetNumberOfValues(); i++)
    {
    const char *filename = filenames->GetValue(i).c_str(); 
    if (!slice)
      {
      slice = new ImageSlot;
      }

    slice->SetFileName(filename);

    // Try to retrieve the info

    int retrieve_status = this->RetrieveImageInfo(slice);
    if (retrieve_status == vtkDICOMCollector::FailureNone)
      {
      slices.push_back(slice);
      slice = NULL; // so that the next one is allocated
      }
    // If it was a DICOM file, but not something we could read, we may
    // eventually abort the whole stack if SkipProblematicFile is Off.
    else if (retrieve_status != vtkDICOMCollector::FailureNotReadable &&
             !this->Options->GetSkipProblematicFile())
      {
      if (slice)
        {
        delete slice;
        }
      filenames->Delete();
      return 0;
      }

    progress = (double)i / (double)filenames->GetNumberOfValues();
    this->InvokeEvent(vtkCommand::ProgressEvent, &progress);
    }
  filenames->Delete();

  // clock_t end_clock = clock();
  //cout << "vtkDICOMCollector - Done collecting in " << ((double)(end_clock - start_clock) / (double)CLOCKS_PER_SEC) << " s." << endl;

  if (slices.empty())
    {
    if (slice)
      {
      delete slice;
      }
    return 0;
    }

  // Do we want to verify if DICOM are related ?

  if (this->Options->GetExploreDirectory())
    {
    // Now check which one are related, considering the acquisition first

    vtkDICOMCollectorInternals::ImagesContainerIterator it = slices.begin();
    vtkDICOMCollectorInternals::ImagesContainerIterator end = slices.end();
    for (; it != end; ++it)
      {
      if (this->CurrentImage->Info->IsRelatedTo(*(*it)->Info, 1))
        {
        this->Internals->Slices.push_back((*it));
        }
      }

    // If only a few slices were collected, maybe the aquisition number is 
    // changing so let's try again but this time do not worry about the 
    // aquisition number

    if (this->Internals->Slices.size() <= VTK_DICOM_ACQUISITION_NUMBER_MIN)
      {
      this->Internals->Slices.clear();
      it = slices.begin();
      for (; it != end; ++it)
        {
        if (this->CurrentImage->Info->IsRelatedTo(*(*it)->Info, 0))
          {
          this->Internals->Slices.push_back((*it));
          }
        }
      }
    }
  else // simply copy everybody
    {
    // Now check which one are related, considering the acquisition first
    vtkDICOMCollectorInternals::ImagesContainerIterator it = slices.begin();
    vtkDICOMCollectorInternals::ImagesContainerIterator end = slices.end();
    for (; it != end; ++it)
      {
      this->Internals->Slices.push_back((*it));
      }
    }

  // Free up this memory

  if (slice)
    {
    delete slice;
    }

  // If we have more than one slice, make sure the spacing results
  // in a contiguous volume

  vtkDICOMCollector::ImageInfo *info = this->GetCurrentImageInfo();
  if (info)
    {
    this->SliceSpacing = info->Spacing[2];
    }
  else
    {
    this->SliceSpacing = 1.0;
    }

  this->InvokeEvent(vtkCommand::EndEvent);

  if (!this->Internals->Slices.size())
    {
    return 0;
    }

  // Single slice ?

  if (this->Internals->Slices.size() == 1)
    {
    this->CurrentVolume = 0;
    this->SliceIndexes->SetNumberOfVolumes(1);
    this->SliceIndexes->SetStart(0, 0);
    this->SliceIndexes->SetEnd(0, (int)this->Internals->Slices.size() - 1);
    this->SliceIndexes->SetSpacing(0, this->SliceSpacing);

    // Ok all the slices are ordered let's build up a mapping from 
    // sliceposition (index)
    // into the DICOM Instance UID. There is only one single volume
    vtkMedicalImageProperties *hinfo = 
      this->GetCurrentImageMedicalProperties();
    int volume = 0;
    {
    i = 0;
    unsigned int ss = 0;
    for(unsigned int jj=this->SliceIndexes->GetStart(volume);
        jj <= this->SliceIndexes->GetEnd(volume);
        ++jj)
      {
      ImageSlot *slice = this->Internals->Slices[i];
      hinfo->SetInstanceUIDFromSliceID(
        volume, ss, slice->Info->GetSOPInstanceUID());
      }
    }

    // Now that everybody is separate into subvolume let's store the original
    // aquisition type:

    for (i = 0; i < (int)this->SliceIndexes->GetNumberOfVolumes(); ++i)
      {
      this->SelectVolume(i);
      int idx[3];
      if (!this->GetOrientationPermutations(idx))
        {
        return 0;
        }
      // If idx == {0, 1, 2} : Axial
      // If idx == {1, 2, 0} : Sagittal
      // If idx == {0, 2, 1} : Coronal
      vtkMedicalImageProperties *hinfo = 
        this->GetCurrentImageMedicalProperties();
      if (idx[0] == 0 && idx[1] == 1 && idx[2] == 2)
        {
        hinfo->SetOrientationType(i, vtkMedicalImageProperties::AXIAL);
        }
      else if (idx[0] == 1 && idx[1] == 0 && idx[2] == 2) // Axial inverted
        {
        hinfo->SetOrientationType(i, vtkMedicalImageProperties::AXIAL);
        }
      else if (idx[0] == 1 && idx[1] == 2 && idx[2] == 0)
        {
        hinfo->SetOrientationType(i, vtkMedicalImageProperties::SAGITTAL);
        }
      else if (idx[0] == 2 && idx[1] == 1 && idx[2] == 0) // Sagittal inverted
        {
        hinfo->SetOrientationType(i, vtkMedicalImageProperties::SAGITTAL);
        }
      else if (idx[0] == 0 && idx[1] == 2 && idx[2] == 1)
        {
        hinfo->SetOrientationType(i, vtkMedicalImageProperties::CORONAL);
        }
      else if (idx[0] == 2 && idx[1] == 0 && idx[2] == 1) // Coronal inverted
        {
        hinfo->SetOrientationType(i, vtkMedicalImageProperties::CORONAL);
        }
      else
        {
        vtkErrorMacro("Unknown Orientation: " << idx[0] << "," << idx[1] << "," << idx[2]);
        }
      }
  
    return (int)this->Internals->Slices.size();
    }

  unsigned int numSerie = 1;
  vtkDICOMCollectorOptions::SeriesTypes type = 
    vtkDICOMCollectorOptions::InvalidSeries;
  if (this->Options->GetForceSeriesType() == 
      vtkDICOMCollectorOptions::UnknownSeries)
    {
    // Now sort based on the distance between images
    vtksys_stl::stable_sort(
      this->Internals->Slices.begin(),
      this->Internals->Slices.end(),
      ImageSlotCompare);

    // Make sure we are not in a degenerate case of multi series within 
    // the same UID
    
    std::set<unsigned int> setinstance;
    std::set<const double *, ltpos> setpos;
    std::set<const double *, ltori> setori;
    for (i = 0; i < (int)this->Internals->Slices.size(); ++i)
      {
      double *newpos = this->Internals->Slices[i]->Info->Position;
      unsigned int new_instance_num = 
        this->Internals->Slices[i]->Info->InstanceNumber;
      double *dir_cos = this->Internals->Slices[i]->Info->Orientation;
#if 0  // VVN1p3
      if (setinstance.count(new_instance_num) == 0)
#endif
        {
        setinstance.insert(new_instance_num);
        setpos.insert(newpos);
        setori.insert(dir_cos);
        }
      }
    // Check if we have an Instance Number per slice:
#if 0  // VVN1p3
    if (setinstance.size() != this->Internals->Slices.size())
      {
      // Ok somebody put either:
      // 1. twice the same file (same instance #)
      // 2. The series protocol aquisition is not configured correctly that 
      //    at least one instance number is repeated
      vtkErrorMacro("Unhandled case");
      type = vtkDICOMCollectorOptions::UnknownSeries;
      }
    // Is it a nice n' easy single series:
    else
#endif
      if (setinstance.size() == this->Internals->Slices.size() &&  // guarantee
          setori.size() == 1
          && setpos.size() == setinstance.size())
        {
        type = vtkDICOMCollectorOptions::SingleSeries;
        numSerie = 1;
        // Are there missing slices ? First let's do a quick check:
        std::set<unsigned int>::const_iterator it = setinstance.begin();
        unsigned int min_instance = *it;
        it = setinstance.end();
        --it;
        unsigned int max_instance = *it;
        //assert(max_instance >= min_instance); // this is the definition of a set !
        if ((max_instance - min_instance) + 1 == setinstance.size())
          {
          // ok we have all instances nicely partitioned:
          }
        else
          {
          // Is it a linear incremented scale of the form y = a*x+b
          it = setinstance.begin();
          // find b:
          unsigned int b = min_instance;
          ++it;
          unsigned int a = *it - b;
          unsigned int x = 1;
          while(it != setinstance.end() && (a*x+b == *it))
            {
            ++it;
            ++x;
            }
          if (it == setinstance.end())
            {
            vtkWarningMacro("Handled. This is a linear incremented Series "
                             "with value a=" << a << " and b=" << b );
            }
          else
            {
            #if 0
            vtkErrorMacro("Unhandled. The Instance Number are not sequential.");
            // It does not make any sense. The user specified randomly a 
            // couple of files that are not even consecutive. Simply give up
            // and mark Series as Unknown:
            type = vtkDICOMCollectorOptions::UnknownSeries;
            return 0;
            #endif
            }
          }
        }
    // Is it a SCOUT series ?
      else if (setori.size() != 1)
        {
        // Ok looks like it
        numSerie = (unsigned int)setori.size();
        // Are there missing slices ? First let's do a quick check:
        std::set<unsigned int>::const_iterator it = setinstance.begin();
        unsigned int min_instance = *it;
        it = setinstance.end();
        --it;
        unsigned int max_instance = *it;
        //assert(max_instance >= min_instance); 
        // this is the definition of a set !
#if 0  // VVN1p3
        if ((max_instance - min_instance) + 1 == setinstance.size())
#endif
          {
          // ok we have all instances nicely partitioned:
          type = vtkDICOMCollectorOptions::SCOUTSeries;
          }
#if 0  // VVN1p3
        else
          {
          vtkErrorMacro("Unhandled case");
          type = vtkDICOMCollectorOptions::UnknownSeries;
          }
#endif
        }
    // Is it a DWI/DTI ?
      else if (setpos.size() != setinstance.size())
        {
        // let's see if we are in the DWI complete case
        if (!(setinstance.size() % setpos.size()))
          {
          // Let's make sure all instance are regularly spaced in term of
          // instance number we might later on refine each subvolume to check
          // there spacing is consistant...
          numSerie = (unsigned int)(setinstance.size() / setpos.size());
          unsigned int numSlicePerSerie = (unsigned int)setpos.size();
          type = vtkDICOMCollectorOptions::DWISeries;
          if (setori.size() != 1 || numSlicePerSerie == 1)
            {
            vtkErrorMacro("Unhandled: Looks like a Siemens MOSAIC Series");
            type = vtkDICOMCollectorOptions::UnknownSeries;
            }
          }
        else
          {
          // Is it a DWI with missing slice or a single series with missing
          // slices ?
          vtkErrorMacro("Unhandled case");
          type = vtkDICOMCollectorOptions::UnknownSeries;
          }
        }
      else
        {
        vtkErrorMacro("Unhandled case");
        type = vtkDICOMCollectorOptions::UnknownSeries;
        }
    assert(type != vtkDICOMCollectorOptions::InvalidSeries);
    }
  else
    {
    // Great we have the information from the vtkDICOMReader
    type = vtkDICOMCollectorOptions::SeriesTypes(
      this->Options->GetForceSeriesType());
    if (type == vtkDICOMCollectorOptions::SingleSeries)
      {
      numSerie = 1;
      }
    else if (type == vtkDICOMCollectorOptions::SCOUTSeries)
      {
      std::set<unsigned int> setinstance;
      std::set<const double *, ltori> setori;
      for (i = 0; i < (int)this->Internals->Slices.size(); ++i)
        {
        unsigned int new_instance_num = 
          this->Internals->Slices[i]->Info->InstanceNumber;
        double *dir_cos = this->Internals->Slices[i]->Info->Orientation;
        if (setinstance.count(new_instance_num) == 0)
          {
          setinstance.insert(new_instance_num);
          setori.insert(dir_cos);
          }
        // else should not happen
        }
      numSerie = (unsigned int)setori.size();
      }
    else if (type == vtkDICOMCollectorOptions::DWISeries)
      {
      std::set<unsigned int> setinstance;
      std::set<const double *, ltpos> setpos;
      for (i = 0; i < (int)this->Internals->Slices.size(); ++i)
        {
        double *newpos = this->Internals->Slices[i]->Info->Position;
        unsigned int new_instance_num = 
          this->Internals->Slices[i]->Info->InstanceNumber;
        if (setinstance.count(new_instance_num) == 0)
          {
          setinstance.insert(new_instance_num);
          setpos.insert(newpos);
          }
        }
      numSerie = (unsigned int)(setinstance.size() / setpos.size());
      if (setinstance.size() % setpos.size())
        {
        vtkErrorMacro("Unhandled case");
        }
      }
    else
      {
      vtkErrorMacro("Unhandled case");
      }
    }

  // Now reorder all the slices
  if (type == vtkDICOMCollectorOptions::SingleSeries)
    {
    this->CurrentVolume = 0;
    this->SliceIndexes->SetNumberOfVolumes(numSerie);
    this->SliceIndexes->SetStart(0, 0);
    this->SliceIndexes->SetEnd(0, (int)this->Internals->Slices.size() - 1);

    // Check there is no missing slice:
    this->CheckSpacingConsistancy(0);

    // Ok all the slices are ordered let's build up a mapping from
    // sliceposition (index) into the DICOM Instance UID. 
    // There is only one single volume
    vtkMedicalImageProperties *hinfo = 
      this->GetCurrentImageMedicalProperties();
    i = 0;
    for (unsigned int volume = 0; 
         volume < this->SliceIndexes->GetNumberOfVolumes(); ++volume)
      {
      ImageSlot *begin = this->Internals->Slices[i];
      double ones_vector[3] = {1.0, 1.0, 1.0};
      double dir = 
        vtkMath::Dot(begin->Info->GetOrientationImage(), ones_vector);
      int ss = 0;
      int idir = 1;
      if (dir<0)
        {
        idir = -1;
        ss = this->SliceIndexes->GetEnd(volume) - 
          this->SliceIndexes->GetStart(volume);
        }
      for(unsigned int jj=this->SliceIndexes->GetStart(volume);
          jj <= this->SliceIndexes->GetEnd(volume);
          ++jj)
        {
        ImageSlot *slice = this->Internals->Slices[i];
        ++i;
        hinfo->SetInstanceUIDFromSliceID(
          volume, ss, slice->Info->GetSOPInstanceUID());
        ss += idir;
        }
      }
    }
  else if (type == vtkDICOMCollectorOptions::SCOUTSeries)
    {
    vtksys_stl::stable_sort(
      this->Internals->Slices.begin(),
      this->Internals->Slices.end(),
      ImageSlotCompareOrientation);
    //unsigned int numSlicePerSerie;
    unsigned int numSlices = (unsigned int)this->Internals->Slices.size();
    //numSlicePerSerie = numSlices/numSerie;
    double *cur_ori = this->CurrentImage->Info->Orientation;
    this->SliceIndexes->SetNumberOfVolumes(numSerie);
    assert(numSerie);
    // initialize
    this->SliceIndexes->SetStart(0, 0);
    this->SliceIndexes->SetEnd(
      numSerie - 1, (int)this->Internals->Slices.size() - 1);
    // start counting:
    int idxSerie = 0;
    double *ref_ori = this->Internals->Slices[0]->Info->Orientation;
    for (i = 0; i < (int)this->Internals->Slices.size(); ++i)
      {
      double *dir_cos = this->Internals->Slices[i]->Info->Orientation;
      if (dir_cos[0] == ref_ori[0]
          && dir_cos[1] == ref_ori[1]
          && dir_cos[2] == ref_ori[2]
          && dir_cos[3] == ref_ori[3]
          && dir_cos[4] == ref_ori[4]
          && dir_cos[5] == ref_ori[5])
        {
        // same serie nothing to do
        }
      else
        {
        // finish current scout:
        this->SliceIndexes->SetEnd(idxSerie, i - 1);
        // move on to the next scout
        idxSerie++;
        this->SliceIndexes->SetStart(idxSerie, i);
        ref_ori = dir_cos;
        }
      }
    // For each volume
    int old_current_volume = this->CurrentVolume;
    for(i = 0; (unsigned int) i < numSerie; ++i)
      {
      vtksys_stl::stable_sort(
        this->Internals->Slices.begin() + this->SliceIndexes->GetStart(i),
        this->Internals->Slices.begin() + this->SliceIndexes->GetEnd(i),
        ImageSlotCompare);
      const ImageSlot *ref = 
        this->Internals->Slices[this->SliceIndexes->GetStart(i)];
      const double *ori = ref->Info->Orientation;
      if (cur_ori[0] == ori[0]
          && cur_ori[1] == ori[1]
          && cur_ori[2] == ori[2]
          && cur_ori[3] == ori[3]
          && cur_ori[4] == ori[4]
          && cur_ori[5] == ori[5])
        {
        this->CurrentVolume = i;
        }
      // Check there is no missing slice:
      if (this->CheckSpacingConsistancy(i) != 1)
        {
        vtkWarningMacro("Check Spacing Consistancy");
        }
      }
    this->CurrentVolume = old_current_volume;

    // Ok all the slices are ordered let's build up a mapping from 
    // sliceposition (index) into the DICOM SOP Instance UID. 
    // There is only one single volume
    vtkMedicalImageProperties *hinfo = 
      this->GetCurrentImageMedicalProperties();
    i = 0;
    for (unsigned int volume = 0; 
         volume < this->SliceIndexes->GetNumberOfVolumes(); ++volume)
      {
      ImageSlot *begin = this->Internals->Slices[i];
      double ones_vector[3] = {1.0, 1.0, 1.0};
      double dir = 
        vtkMath::Dot(begin->Info->GetOrientationImage(), ones_vector);
      int ss = 0;
      int idir = 1;
      if (dir<0)
        {
        idir = -1;
        ss = this->SliceIndexes->GetEnd(volume) - 
          this->SliceIndexes->GetStart(volume);
        }
      for(unsigned int jj=this->SliceIndexes->GetStart(volume);
          jj <= this->SliceIndexes->GetEnd(volume);
          ++jj)
        {
        ImageSlot *slice = this->Internals->Slices[i];
        ++i;
        hinfo->SetInstanceUIDFromSliceID(
          volume, ss, slice->Info->GetSOPInstanceUID());
        ss += idir;
        }
      }
    }
  else if (type == vtkDICOMCollectorOptions::DWISeries)
    {
    unsigned int numSlicePerSerie;
    unsigned int numSlices = (unsigned int)this->Internals->Slices.size();
    if (!(numSlices % numSerie))
      {
      numSlicePerSerie = numSlices/numSerie;
      // We are in the case with multiple series (DTI/DWI)
      // First thing order by Instance Number
      vtksys_stl::stable_sort(
        this->Internals->Slices.begin(),
        this->Internals->Slices.end(),
        ImageSlotCompareInstance);
      // Now we need to figure out if we are in the Philips or GE case:
      // One is ordered using Instance Number. 
      // The other one increase Instance Number and 
      // switch to the next series
      // Case 1 (GE Medical Systems)
      // SubSerie#1 Instance#1
      // SubSerie#1 Instance#2
      // ...
      // SubSerie#2 Instance#10
      // SubSerie#2 Instance#11
      //
      // Case 2 (Philips Medical Systems) 
      // SubSerie#1 Instance#258*0+7
      // SubSerie#1 Instance#258*0+8
      // ...
      // SubSerie#2 Instance#258*1+7
      // SubSerie#2 Instance#258*1+8
      ImageSlot *ref0 = this->Internals->Slices[0];
      ImageSlot *ref1 = this->Internals->Slices[1];
      // Store instance number that will help us navigate amoung the sub series:
      unsigned int cur_instance = this->CurrentImage->Info->InstanceNumber;
      unsigned int min_instance = 
        this->Internals->Slices[0]->Info->InstanceNumber;
      unsigned int max_instance =
        this->Internals->Slices[this->Internals->Slices.size()-1]->Info->InstanceNumber;
      double *pos0 = ref0->Info->Position;
      double *pos1 = ref1->Info->Position;
      if (pos0[0] == pos1[0] && pos0[1] == pos1[1] && pos0[2] == pos1[2])
        {
        // We are in case #2 (PMS)
        // shift scale function: 
        // InstanceNumber = shift*SerieSubId+Min_Instance+IdWithinSerie
        // 0 <= IdWithinSerie < 24 usually
        // 0 <= SerieSubId < 8 usually
        // shift = 258 usually
        int shift = 
          ((max_instance - min_instance) - (numSerie-1))/(numSlicePerSerie-1);
        // make sure there is no rounding error:
        if (max_instance - min_instance != (numSlicePerSerie - 1)*shift+(numSerie-1))
          {
          vtkErrorMacro("Unhandled case. Shift is not constant");
          }
        // Find the index of the serie (0 <= index < numSerie)
        int index = (cur_instance - min_instance) % shift;
        this->CurrentVolume = index;
        // Make a copy of the pointers:
        vtkDICOMCollectorInternals::ImagesContainer slice_copy = 
          this->Internals->Slices;
        for (i = 0; i < (int)slice_copy.size(); ++i)
          {
          this->Internals->Slices[i] = 
            slice_copy[(i % numSlicePerSerie)*numSerie+i/numSlicePerSerie];
          }
        }
      else
        {
        // We are in case #1 (GEMS)
        // Compute offset
        if (max_instance - min_instance != 
            (int)this->Internals->Slices.size() - 1)
          {
          vtkErrorMacro(
            "Unhandled case: " << max_instance - min_instance << " instead of "
            << this->Internals->Slices.size() - 1);
          }
        int index = (cur_instance - min_instance) / numSlicePerSerie;
        this->CurrentVolume = index;
        }
      // Reorder the sub serie. Indeed the previous sort was done on 
      // Instance Number. There is no garantee that it will be increasing 
      // along the normal of the slices
      this->SliceIndexes->SetNumberOfVolumes(numSerie);
      for(i = 0; (unsigned int) i < numSerie; ++i)
        {
        vtksys_stl::stable_sort(
          this->Internals->Slices.begin()+i*numSlicePerSerie,
          this->Internals->Slices.begin()+(i+1)*numSlicePerSerie,
          ImageSlotCompare);
        // Set start/end for each sub volumes of this series:
        this->SliceIndexes->SetStart(i, i*numSlicePerSerie);
        this->SliceIndexes->SetEnd  (i, (i+1)*numSlicePerSerie - 1);

        // Check there is no missing slice:
        this->CheckSpacingConsistancy(i);
        }
      // Ok all the slices are ordered let's build up a mapping from 
      // sliceposition (index) into the DICOM SOP Instance UID. There is
      // only one single volume
      vtkMedicalImageProperties *hinfo = 
        this->GetCurrentImageMedicalProperties();
      i = 0;
      for (unsigned int volume = 0; 
           volume < this->SliceIndexes->GetNumberOfVolumes(); ++volume)
        {
        ImageSlot *begin = this->Internals->Slices[i];
        double ones_vector[3] = {1.0, 1.0, 1.0};
        double dir = 
          vtkMath::Dot(begin->Info->GetOrientationImage(), ones_vector);
        int ss = 0;
        int idir = 1;
        if (dir<0)
          {
          idir = -1;
          ss = this->SliceIndexes->GetEnd(volume) - 
            this->SliceIndexes->GetStart(volume);
          }
        for(unsigned int jj=this->SliceIndexes->GetStart(volume);
            jj <= this->SliceIndexes->GetEnd(volume);
            ++jj)
          {
          ImageSlot *slice = this->Internals->Slices[i];
          ++i;
          hinfo->SetInstanceUIDFromSliceID(
            volume, ss, slice->Info->GetSOPInstanceUID());
          ss += idir;
          }
        }
      }
    ImageSlot *slicei = 0, *slicej = 0;
    for (i = 0; i < (int)this->Internals->Slices.size()-1; ++i)
      {
      slicei = this->Internals->Slices[i];
      slicej = this->Internals->Slices[i + 1];
      if (!ImageSlotCompare(slicei, slicej) && (i+1)%numSlicePerSerie)
        {
        vtkWarningMacro("Something went wrong in ordering the DWI series");
        }
      }
    }
  else
    {
    // UnknownSeries
    this->FailureStatus |= vtkDICOMCollector::FailureUnknownSeriesType;
    return 0;
    }

  // Now that everybody is separate into subvolume let's store the original
  // acquisition type:
  for (i = 0; i < (int)this->SliceIndexes->GetNumberOfVolumes(); ++i)
    {
    this->SelectVolume(i);
    int idx[3];
    if (!this->GetOrientationPermutations(idx))
      {
      return 0;
      }
    // If idx == {0, 1, 2} : Axial
    // If idx == {1, 2, 0} : Sagittal
    // If idx == {0, 2, 1} : Coronal
    vtkMedicalImageProperties *hinfo = 
      this->GetCurrentImageMedicalProperties();
    if (idx[0] == 0 && idx[1] == 1 && idx[2] == 2)
      {
      hinfo->SetOrientationType(i, vtkMedicalImageProperties::AXIAL);
      }
    else if (idx[0] == 1 && idx[1] == 2 && idx[2] == 0)
      {
      hinfo->SetOrientationType(i, vtkMedicalImageProperties::SAGITTAL);
      }
    else if (idx[0] == 0 && idx[1] == 2 && idx[2] == 1)
      {
      hinfo->SetOrientationType(i, vtkMedicalImageProperties::CORONAL);
      }
    else
      {
      vtkErrorMacro("Impossible happen");
      }
    }
 
  // Collect all spacings (maintain a map of spacing -> spacing occurrence)
  int old_current_volume = this->CurrentVolume;
  for (i = 0; i < (int)this->SliceIndexes->GetNumberOfVolumes(); ++i)
    {
    this->CurrentVolume = i;
    int numSlices = this->GetNumberOfCollectedSlicesForVolume(i);
    if (numSlices == 1)
      {
      this->SliceIndexes->SetSpacing(i, 1.0);
      }
    else
      {
      this->ComputeSliceSpacing(i);
      this->SliceIndexes->SetSpacing(i, this->SliceSpacing);
      }
    }
  this->CurrentVolume = old_current_volume;
  int current_vol = this->GetCurrentVolume();
  this->ComputeSliceSpacing(current_vol);

  // Check that the images are all facing the same direction, except in the
  // case of a SCOUT acquisition where we need to verify the contrary

  if (type != vtkDICOMCollectorOptions::SCOUTSeries)
    {
    ImageSlot *slicei = 0, *slicej = 0;
    double delta_pos[3];
    for (i = 0; i < (int)this->Internals->Slices.size() - 2; i++)
      {
      slicei = this->Internals->Slices[i];
      slicej = this->Internals->Slices[i + 1];

      if (fabs(
            vtkMath::Dot(
              slicei->Info->GetOrientationImage(),
              this->CurrentImage->Info->GetOrientationImage())) < VTK_DICOM_TOLERANCE)
        {
        this->FailureStatus |= 
          vtkDICOMCollector::FailureImagesNotFacingTheSameDirection;
        vtkWarningMacro(
          "DICOM file [" << (this->GetFileName() ? this->GetFileName() : "")
          << "]\n => images in this series are not all facing the same "
          "direction and cannot be used.");
        this->DeleteAllSlices();
        break;
        }
      else
        {
        delta_pos[0] = slicej->Info->Position[0] - slicei->Info->Position[0];
        delta_pos[1] = slicej->Info->Position[1] - slicei->Info->Position[1];
        delta_pos[2] = slicej->Info->Position[2] - slicei->Info->Position[2];

        vtkMath::Normalize(delta_pos);
        double dot = fabs(
          vtkMath::Dot(delta_pos, 
                       this->CurrentImage->Info->GetOrientationImage()));
        if (dot < VTK_DICOM_TOLERANCE)
          {
          this->FailureStatus |= vtkDICOMCollector::FailureGantryTilt;
          vtkWarningMacro(
            "DICOM file [" << (this->GetFileName() ? this->GetFileName() : "")
            << "]\n => images appear to have a gantry tilt which cannot be used "
            "for building a volume.");
          this->DeleteAllSlices();
          break;
          }
        }
      }
    }

  return (int)this->Internals->Slices.size();
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::CheckSpacingConsistancy(unsigned int volumeidx)
{
  unsigned int currentvolumeidx = volumeidx;
  unsigned int start = this->SliceIndexes->GetStart(volumeidx);
  unsigned int end   = this->SliceIndexes->GetEnd  (volumeidx);
  // Handle the case where there is only one slice in the series.
  if (start == end)
    {
    return 1;
    }
  // More than one slice:
  std::set<float> setspacing;
  for (unsigned int i = start; i < end - 1; ++i)
    {
    ImageSlot *slicei = this->Internals->Slices[i];
    ImageSlot *slicej = this->Internals->Slices[i + 1];
    double spacing = fabs(vtkMath::Dot(slicej->Info->Position,
        slicej->Info->GetOrientationImage()) -
      vtkMath::Dot(slicei->Info->Position,
        slicei->Info->GetOrientationImage()));

    double roundspacing = spacing;
    // Let's get rid of the precision: (5.599999 really is 5.56)
    // in our case we need spacing is going to be >= 0.5 / precision
//BEGIN QC VVN1p2
    const int precision = 100;
    //const int precision = 1000; // too much precision for some case
//END QC VVN1p2
    if (roundspacing >= (0.5 / precision))
      {
      roundspacing = floor((spacing * precision) + 0.5) / precision;
      }
    if (setspacing.count(roundspacing) == 0 && i != start)
      {
      // This should be very rare to subdivide a volume, print a warning
      // (In VVN1p2 there used to be a bug).
      // DO NOT EDIT THE TEXT OF THIS WARNING:
      // vtkWarningMacro("VVN: Multiple Spacing: " << this->GetFileName());
      // ok we found a new spacing, therefore this is the start of a new series
      // and the end of the current one
      currentvolumeidx = this->SliceIndexes->GetNumberOfVolumes();

      this->SliceIndexes->SetEnd(currentvolumeidx-1, i);
      currentvolumeidx++;
      this->SliceIndexes->SetNumberOfVolumes(currentvolumeidx);
      // Create a new volume at the end:
      this->SliceIndexes->SetStart(currentvolumeidx-1, i+1);
      this->SliceIndexes->SetEnd  (currentvolumeidx-1, end);
      }
    setspacing.insert(roundspacing);
    }
  if (setspacing.size() == 1)
    {
    // perfect nothing to change:
    return 1;
    }
  // Ok there was more than one spacing...
  return (int)setspacing.size();
}

//----------------------------------------------------------------------------
double vtkDICOMCollector::GetSpacing(int volumeidx)
{
  assert(volumeidx == this->CurrentVolume);
  return this->SliceIndexes->GetSpacing(volumeidx);
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::GetNumberOfVolumes()
{
  return this->SliceIndexes->GetNumberOfVolumes();
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::GetStartSliceForVolume(int volumeidx)
{
  assert(volumeidx == this->CurrentVolume);
  return this->SliceIndexes->GetStart(volumeidx);
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::GetEndSliceForVolume(int volumeidx)
{
  assert(volumeidx == this->CurrentVolume);
  return this->SliceIndexes->GetEnd(volumeidx);
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::GetNumberOfCollectedSlicesForVolume(int volumeidx)
{
  assert(volumeidx == this->CurrentVolume);
  return this->SliceIndexes->GetEnd(volumeidx)
    - this->SliceIndexes->GetStart(volumeidx) + 1;
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::GetNumberOfCollectedSlices()
{
  return (int)this->Internals->Slices.size();
}

//----------------------------------------------------------------------------
double vtkDICOMCollector::GetSliceSpacing()
{
  if (!this->CollectAllSlices())
    {
    return 1.0;
    }

  return this->SliceSpacing;
}

//----------------------------------------------------------------------------
vtkDICOMCollector::ImageInfo* 
vtkDICOMCollector::GetImageInfo(ImageSlot *image)
{
  if (!image)
    {
    return NULL;
    }

  // If the info has already been allocated, then it has been
  // retrieved (cached).

  if (!image->Info && 
      this->RetrieveImageInfo(image) != vtkDICOMCollector::FailureNone)
    {
    return NULL;
    }

  return image->Info;
}

//----------------------------------------------------------------------------
vtkMedicalImageProperties* 
vtkDICOMCollector::GetImageMedicalProperties(ImageSlot *image)
{
  if (!image)
    {
    return NULL;
    }

  // If the header info has already been allocated, then it has been
  // retrieved (cached).

  if (!image->MedicalProperties && 
      this->RetrieveImageMedicalProperties(image) != 
      vtkDICOMCollector::FailureNone)
    {
    return NULL;
    }

  return image->MedicalProperties;
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::CanReadImage(ImageSlot *image)
{
  if (!image || !image->Info)
    {
    return 0;
    }

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_IMAGE
#else
  const char *syntax = image->Info->GetTransferSyntaxUID();
#endif

  // GDCM support perfectly any SamplesPerPixel/NumberOfFrames, but the code was written
  // for ctlnlib/dcmtk and is difficult to fix
  if (image->Info->SamplesPerPixel != 1)
    {
    vtkWarningMacro(
          "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
          << "]\n => images have more than 1 sample per pixel.");
    this->FailureStatus |= vtkDICOMCollector::FailureMoreThanOneSamplePerPixel;
    return 0; 
    }
  if (image->Info->NumberOfFrames != 1)
    {
    vtkWarningMacro(
          "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
          << "]\n => images have more than 1 frame.");
    this->FailureStatus |= vtkDICOMCollector::FailureMoreThanOneNumberOfFrames;
    return 0; 
    }

#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_IMAGE
  return 1;
#elif defined(KWCommonPro_USE_CTNLIB)
  return (!syntax ||
          !strcmp(syntax, DICOM_TRANSFERLITTLEENDIAN) ||
          !strcmp(syntax, DICOM_TRANSFERJPEGLOSSLESSPROC14) ||
          !strcmp(syntax, DICOM_TRANSFERJPEGLOSSLESSPROCFIRSTORDERREDICT) ||
          !strcmp(syntax, DICOM_TRANSFERJPEGEXTENDEDPROC2AND4) ||
          !strcmp(syntax, DICOM_TRANSFERLITTLEENDIANEXPLICIT) ||
          !strcmp(syntax, DICOM_TRANSFERBIGENDIANEXPLICIT));
#else
  return 0;
#endif
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::CanReadFile(const char *filename)
{
  // If no filename was provided, check if we can read the current FileName

  if (!filename)
    {
    filename = this->GetFileName();
    }

  // Still empty ? bye.

  if (!filename)
    {
    return 0;
    }

  // If FileName is empty, then actually set it to the value of filename
  // because it is likely that once we tested if a file can be read, we
  // will use the same reader to actually read the file. By setting
  // FileName right now we start caching infos. 

  if (!this->GetFileName())
    {
    this->SetFileName(filename);
    return this->CanReadImage(this->GetCurrentImage());
    }

  // If FileName and filename match, then use the current cached info,
  // otherwise create a new vtkDICOMCollector instance so that cached
  // info are not destroyed.

  else if (!strcmp(filename, this->GetFileName()))
    {
    return this->CanReadImage(this->GetCurrentImage());
    }

  vtkDICOMCollector *collector = vtkDICOMCollector::New();
  collector->SetFileName(filename);
  int res = collector->CanReadFile();
  collector->Delete();
  return res;
}

//----------------------------------------------------------------------------
vtkDICOMCollector::ImageSlot* 
vtkDICOMCollector::GetCurrentImage()
{
  if (this->CollectCurrentImage() != vtkDICOMCollector::FailureNone)
    {
    return NULL;
    }

  return this->CurrentImage;
}

//----------------------------------------------------------------------------
vtkDICOMCollector::ImageInfo* 
vtkDICOMCollector::GetCurrentImageInfo()
{
  if (this->CollectCurrentImage() != vtkDICOMCollector::FailureNone)
    {
    return NULL;
    }

  return this->GetImageInfo(this->CurrentImage);
}
//----------------------------------------------------------------------------
vtkDICOMCollector::ImageInfo* 
vtkDICOMCollector::GetSelectedImageInfo()
{
  if (this->CollectCurrentImage() != vtkDICOMCollector::FailureNone)
    {
    return NULL;
    }

  return this->GetImageInfo(this->SelectedImage);
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::SelectVolume(int vol_idx)
{
  if (vol_idx >= this->GetNumberOfVolumes() || vol_idx < 0)
    {
    vtkWarningMacro("No volume");
    return;
    }
  this->CurrentVolume = vol_idx;
  // Find out the start of this volume (first slice)
  unsigned int start = this->SliceIndexes->GetStart(vol_idx);
  // Take the first ImageSlot from the 'subvolume':
  this->SelectedImage = this->Internals->Slices[start];
//BEGIN QC VVN1p2
  unsigned int numSlices = (unsigned int)this->Internals->Slices.size();
  unsigned int numSlicePerSerie = numSlices/this->GetNumberOfVolumes();
  if (vol_idx*numSlicePerSerie != start)
    {
    // DO NOT EDIT THE TEXT OF THIS WARNING:
    //vtkWarningMacro("VVN: Select Volume: " << this->GetFileName());
    }
//END QC VVN1p2
}

//----------------------------------------------------------------------------
vtkMedicalImageProperties* 
vtkDICOMCollector::GetCurrentImageMedicalProperties()
{
  if (this->CollectCurrentImage() != vtkDICOMCollector::FailureNone)
    {
    return NULL;
    }

  return this->GetImageMedicalProperties(this->CurrentImage);
}

//----------------------------------------------------------------------------
const char* vtkDICOMCollector::GetSliceFileName(int slice_idx)
{
  if (!this->CollectAllSlices() ||
      slice_idx < 0 || slice_idx >= this->GetNumberOfCollectedSlices())
    {
    return NULL;
    }

  ImageSlot *slice = this->Internals->Slices[slice_idx];
  if (!slice)
    {
    return NULL;
    }

  return slice->GetFileName();
}

//----------------------------------------------------------------------------
vtkDICOMCollector::ImageInfo* 
vtkDICOMCollector::GetSliceImageInfo(int slice_idx)
{
  if (!this->CollectAllSlices() ||
      slice_idx < 0 || slice_idx >= this->GetNumberOfCollectedSlices())
    {
    return NULL;
    }

  ImageSlot *slice = this->Internals->Slices[slice_idx];
  if (!slice)
    {
    return NULL;
    }

  return this->GetImageInfo(slice);
}

//----------------------------------------------------------------------------
vtkMedicalImageProperties* 
vtkDICOMCollector::GetSliceImageMedicalProperties(int slice_idx)
{
  if (!this->CollectAllSlices() ||
      slice_idx < 0 || slice_idx >= this->GetNumberOfCollectedSlices())
    {
    return NULL;
    }

  ImageSlot *slice = this->Internals->Slices[slice_idx];
  if (!slice)
    {
    return NULL;
    }

  return this->GetImageMedicalProperties(slice);
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::GetSliceImageData(int slice_idx, 
                                         void *data,
                                         int shift_mask)
{
  if (!this->CollectAllSlices() ||
      slice_idx < 0 || slice_idx >= this->GetNumberOfCollectedSlices())
    {
    return 0;
    }

  ImageSlot *slice = this->Internals->Slices[slice_idx];
  if (!slice)
    {
    return 0;
    }

  return 
    (this->RetrieveImageData(slice, data, shift_mask, 0) == 
     vtkDICOMCollector::FailureNone) ? 1 : 0;
}


//----------------------------------------------------------------------------
vtkUnsignedShortArray * const vtkDICOMCollector::GetSlicePixelPaddingValueExtents(
                            int /* slice_idx */) const
{
  return this->PaddedExtents;
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::GetSlicesScalarRange(int slice_idx_start, 
                                            int slice_idx_end,
                                            double *min, double *max,
                                            int max_nb_slices,
                                            int pixel_step)
{
  vtkDICOMCollector::ImageInfo *info = this->GetCurrentImageInfo();
  if (!info || 
      !this->CollectAllSlices() || 
      slice_idx_start < 0 || 
      slice_idx_start >= this->GetNumberOfCollectedSlices() ||
      slice_idx_end < 0 || 
      slice_idx_end >= this->GetNumberOfCollectedSlices())
    {
    return 0;
    }

  this->InvokeEvent(vtkCommand::StartEvent);

  double progress;

  *min = VTK_DOUBLE_MAX;
  *max = VTK_DOUBLE_MIN;

  size_t image_data_size = 
    ((info->BitsAllocated + 7) / 8) * 
    info->Columns * info->Rows * info->Planes * info->SamplesPerPixel;

  char *buffer = new char [image_data_size];

  double idx, slice_step;
  int nb_slices = slice_idx_end - slice_idx_start + 1;
  if (max_nb_slices <= 0 || nb_slices <= max_nb_slices)
    {
    slice_step = 1.0;
    idx = (double)slice_idx_start;
    }
  else
    {
    slice_step = (double)nb_slices / (double)max_nb_slices;
    idx = (double)slice_idx_start + slice_step * 0.5;
    }

  for (; idx <= (double)slice_idx_end; idx += slice_step)
    {
    int slice_idx = vtkMath::Floor(idx);
    ImageSlot *slice = this->Internals->Slices[slice_idx];
    if (slice)
      {
      this->RetrieveImageData(slice, buffer, 1, 0, min, max, pixel_step);
      }
    progress = (double)(slice_idx - slice_idx_start) / 
      (double)(slice_idx_end - slice_idx_start);
    this->InvokeEvent(vtkCommand::ProgressEvent, &progress);
    }

  delete [] buffer;

  this->InvokeEvent(vtkCommand::EndEvent);

  return 1;
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::GetOrientationPermutationsAndIncrements(int idx[3], 
                                                               int incs[3], 
                                                               long *offset)
{
  // Get image info (and slice orientation, which should be the same
  // for all slices in the volume) 

  vtkDICOMCollector::ImageInfo *info = this->GetSelectedImageInfo();
  //this->GetCurrentImageInfo();
  if (!info)
    {
    return 0;
    }

  // Collect all slices

  if (!this->CollectAllSlices())
    {
    return 0;
    }

  /* Some notes about the DICOM orientation.
     (see DICOM newsgroup + DICOM PS3.3 page 168 and DICOM PS3.3 Annex E):

     What does the patient coordinate system look like?
     This is the LPS coordinate system.


          HEAD/SUPERIOR               +Z(H/S)
                   POSTERIOR           |  / +Y(P)
              ()                       | /
      RIGHT  -/\-  LEFT                |/
              ||           -X(R) ------+------ +X(L)
     ANTERIOR                         /|
         FEET/INFERIOR               / |
                                    /  |
                               -Y(A)  -Z(F/I)

     In this diagram the patient is facing forward. Anterior refers to the
     front of the patient. Posterior refers to the back of the patient. The
     origin of the patient coordinate system is arbitrary, and selected by
     the imaging modality.

     What is a direction cosine?

     Basically, a direction cosine is the cosine of the angle which is
     created between a unit vector which starts at the origin with some
     direction, and a unit vector coincident with one of the axis of the
     coordinate system.

     To fully define the point in space at the end of a vector it requires
     nine direction cosines.  Since in a right handed cartesian coordinate
     system the cross product of two unit vectors defines the third, you can
     fully describe the location using only six direction cosines.  The other
     three may be calculated.

     How do I orient a single image in the patient coordinate system?

     In order to orient a single image plane in the patient coordinate system
     a 4x4 transform matrix must be defined.  This matrix is derived as follows:
     Given:

     Rx = x row direction cosine
     Ry = y row direction cosine
     Rz = z row direction cosine

     Cx = x column direction cosine
     Cy = y column direction cosine
     Cz = z column direction cosine

     First calculate the slice direction cosine values for x, y, and z.  This
     is the cross-product S of the row R and column C direction cosines.

     Sx = (Ry * Cz) - (Rz * Cy)
     Sy = (Rz * Cx) - (Rx * Cz)
     Sz = (Rx * Cy) - (Ry * Cx)

     Then, the 4x4 matrix is as follows:

            [ Rx Ry Rz 1 ]
        M = [ Cx Cy Cz 1 ]
            [ Sx Sy Sz 1 ]
            [ 0  0  0  1 ]

     The image plane is then transformed from the origin to the proper
     location in the patient coordinate system as follows:

     Given:

          (c, r, s) = column, row, slice of pixel

          (x,y,z) = x, y, and z coordinates for pixel in patient
                    coordinate system.

     Through matrix multiplication, the transformed coordinates are:

          (c, r, s, 1) * M = (x, y, z, 1)

     i.e:
          x = c.Rx + r.Cx + s.Sx
          y = c.Ry + r.Cy + s.Sy
          z = c.Rz + r.Cz + s.Sz

     Example 1:

     The usual orientation value: [1.0000\0.0000\0.0000\0.0000\1.0000\0.0000]
     Leading to the following Row, Column, Slice direction cosine vectors:

           R     C     S
         [ 1 ] [ 0 ] [ 0 ]
         [ 0 ] [ 1 ] [ 0 ]
         [ 0 ] [ 0 ] [ 1 ]

     Thus:

        x = c
        y = r
        z = s

     Example 2:

     A less usual orientation value: [0.0000\1.0000\0.0000\0.0000\0.0000\-1.0000]
     Leading to the following Row, Column, Slice direction cosine vectors:

           R     C     S
         [ 0 ] [ 0 ] [-1 ]
         [ 1 ] [ 0 ] [ 0 ]
         [ 0 ] [-1 ] [ 0 ]

     Thus:

        x = -s
        y = c
        z = -r
  */

  int signs[3];
  signs[0] = signs[1] = signs[2] = 1;

  idx[0] = idx[1] = idx[2] = 0;

  int i, j;
  for (i = 0; i < 3; i++)
    {
    if (fabs(info->GetOrientationRow()[i]) >= 
        fabs(info->GetOrientationRow()[idx[0]]))
      {
      idx[0] = i;
      signs[0] = (info->GetOrientationRow()[i] < 0) ? -1 : 1;
      }
    if (fabs(info->GetOrientationColumn()[i]) >= 
        fabs(info->GetOrientationColumn()[idx[1]]))
      {
      idx[1] = i;
      signs[1] = (info->GetOrientationColumn()[i] < 0) ? -1 : 1;
      }
    if (fabs(info->GetOrientationImage()[i]) >= 
        fabs(info->GetOrientationImage()[idx[2]]))
      {
      idx[2] = i;
      signs[2] = (info->GetOrientationImage()[i] < 0) ? -1 : 1;
      }
    }

  // Dimensions on disk, dimensions in mem, create the permutations

  int dims_on_disk[3], dims[3], iidx[3];

  dims_on_disk[0] = info->Columns;
  dims_on_disk[1] = info->Rows;
  int current_vol = this->GetCurrentVolume();
  dims_on_disk[2] =
    this->GetNumberOfCollectedSlicesForVolume(current_vol) * info->Planes;

  for (i = 0; i < 3; i++)
    {
    dims[idx[i]] = dims_on_disk[i];
    iidx[idx[i]] = i;
    }

  // DICOM's coordinate system seems is LPS, VTK is RAS.
  // Reorient to RAS. If you need to leave the data as is
  // (i.e. assume it's RAS and let an orientation filter down the pipeline
  // perform the orientation job), you need to flip x and y in the diagram
  // above.
  // So you would uncomment this..
  //signs[iidx[0]] = -signs[iidx[0]];
  //signs[iidx[1]] = -signs[iidx[1]];

  // Compute increments and offset

  for (i = 0; i < 3; i++)
    {
    incs[i] = info->SamplesPerPixel;
    for (j = idx[i] - 1; j >= 0; j--)
      {
      incs[i] *= dims[j];
      }
    *offset += (signs[i] < 0 ? (incs[i] * (dims[idx[i]] - 1)) : 0);
    incs[i] *= signs[i];
    }

  // Conceptually the incs array dictates the signed increments along the
  // x, y and z directions.
  //
  // Example:
  // 1.  (0020|0037) Image Orientation (Patient) =
  //      -1.000000\0.000000\0.000000\0.000000\-1.000000\0.000000
  //     incs[3] = [-1, -XDims, XDims*YDims]
  // 2. (0020|0037) Image Orientation (Patient) =
  //      1.000000\0.000000\0.000000\0.000000\1.000000\0.000000
  //     incs[3] = [1, XDims, XDims*YDims]
  // 3. (0020|0037) Image Orientation (Patient) = 1\0\0\0\0\-1
  //     incs[3] = [1, -XDims*YDims, XDims]

  return 1;
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::GetOrientationPermutations(int idx[3])
{
  int incs[3];
  long offset;
  return this->GetOrientationPermutationsAndIncrements(idx, incs, &offset);
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::GetOrientationIncrements(int incs[3], long *offset)
{
  int idx[3];
  return this->GetOrientationPermutationsAndIncrements(idx, incs, offset);
}

//----------------------------------------------------------------------------
int vtkDICOMCollector::DoesIncludeFile(const char *fname)
{
  if (fname && this->CollectAllSlices())
    {
    int slice, nb_slices = this->GetNumberOfCollectedSlices();
    for (slice = 0; slice < nb_slices; slice++)
      {
      if (!strcmp(fname, this->GetSliceFileName(slice)))
        {
        return 1;
        }
      }
    }

  return 0;
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "FileName: "
     << (this->GetFileName() ? this->GetFileName() : "(none)") << endl;

  os << indent << "SliceSpacing: " << this->SliceSpacing << endl;
  if (this->PaddedExtents)
    os << indent << "PaddedExtents: Extents have been determined for " <<
          this->PaddedExtents->GetNumberOfTuples() << " rows." << endl;
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::ImageInfo::PrintSelf(ostream& os, vtkIndent indent)
{
  os << indent << "SamplesPerPixel: " << this->SamplesPerPixel << endl;
  os << indent << "Rows: " << this->Rows << endl;
  os << indent << "Columns: " << this->Columns << endl;
  os << indent << "BitsAllocated: " << this->BitsAllocated << endl;
  os << indent << "BitsStored: " << this->BitsStored << endl;
  os << indent << "HighBit: " << this->HighBit << endl;
  os << indent << "PixelRepresentation: " << this->PixelRepresentation << endl;
  os << indent << "Spacing: ("
     << this->Spacing[0] << ", "
     << this->Spacing[1]<< ")" << endl;
  os << indent << "Position: ("
     << this->Position[0] << ", "
     << this->Position[1] << ", "
     << this->Position[2]<< ")" << endl;
  os << indent << "OrientationRow: ("
     << this->GetOrientationRow()[0] << ", "
     << this->GetOrientationRow()[1] << ", "
     << this->GetOrientationRow()[2]<< ")" << endl;
  os << indent << "OrientationColumn: ("
     << this->GetOrientationColumn()[0] << ", "
     << this->GetOrientationColumn()[1] << ", "
     << this->GetOrientationColumn()[2]<< ")" << endl;
  os << indent << "OrientationImage: ("
     << this->GetOrientationImage()[0] << ", "
     << this->GetOrientationImage()[1] << ", "
     << this->GetOrientationImage()[2]<< ")" << endl;
  os << indent << "AcquisitionNumber: " << this->AcquisitionNumber << endl;
  os << indent << "PixelPaddingValue: " << this->PixelPaddingValue << endl;
  os << indent << "PixelPaddingValueTagFound: "
     << this->PixelPaddingValueTagFound << endl;
  os << indent << "SOPInstanceUID: "
     << (this->SOPInstanceUID ? SOPInstanceUID : "(none)") << endl;
  os << indent << "SeriesInstanceUID: "
     << (this->SeriesInstanceUID ? SeriesInstanceUID : "(none)") << endl;
  os << indent << "StudyInstanceUID: "
     << (this->StudyInstanceUID ? StudyInstanceUID : "(none)") << endl;
  os << indent << "TransferSyntaxUID: "
     << (this->TransferSyntaxUID ? TransferSyntaxUID : "(none)") << endl;
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::ImageSlot::PrintSelf(ostream& os, vtkIndent indent)
{
  os << indent << "FileName: "
     << (this->FileName ? FileName : "(none)") << endl;
  if (this->Info)
    {
    this->Info->PrintSelf(os, indent.GetNextIndent());
    }
  if (this->MedicalProperties)
    {
    this->MedicalProperties->PrintSelf(os, indent.GetNextIndent());
    }
}

//----------------------------------------------------------------------------
// There are CT datasets that mess up the scalar range because of data
// outside the circular reconstruction perimeter. Scanners are supposed to
// provide a PixelPaddingValue tag to signify this. However, not all of them
// do. What we will do for these datasets is to estimate the circle that will
// discard the crap outside the circular perimeter. It is assumed that the 
// circle will have its center at the center of the dataset. A heuristic
// is used that estimates a circle and then looks at the histogram of pixels
// outside the circle to determine the pixel value to be removed.
//
// Note, these methods go through this trouble only if 
//   modality is CT
//   bitsallocated is > 8 and <=16 ie, SS/US data 
//   PixelPaddingValue tag is not already present.
//   the dataset is a square, Rows = Cols, xspacing = yspacing
//
// This following methods scans through a slice (bulk data contained in buffer). 
// The buffer is expected to be of size (rows * cols) * sizeof(TType).
//
// Since it is assumed that xspacing = yspacing, the radius computed does not 
// take spacing into account. Its a double value in Pixel units, (NOT physical
// units) 
//
template< class TType >
void vtkGetSlicesInscribedCircleTemplate(int rows, int cols, TType *buffer,
                double& radius, std::map< short, unsigned int > & histogram)
{
  radius = VTK_DOUBLE_MAX;
  unsigned long pos = 0;
  double centerX = (double)cols/2.0;
  double centerY = (double)rows/2.0;
  double minRadius = centerX;
  double maxRadius = 0.0;

  for (int y=0; y < rows; y++)
    {
    double minX = centerX;
    double maxX = 0.0;
    for (int x=0; x < cols; x++)
      {
      double absDiff = fabs(x-centerX);
      TType v = *(buffer+pos);
      if (v < -1900 || v > 4095) // comparison is always true for unsigned case
        {
        if ((x > 0 && x < centerX
            && fabs((double)(v - (*(buffer+pos-1)))) < 3)
          || (x < (cols-1) && x > centerX
            && fabs((double)(v - (*(buffer+pos+1)))) < 3))
          {

          if (histogram.find((short)v) == histogram.end())
            {
            histogram[(short)v] = 0;
            }
          ++histogram[(short)v];

          if (absDiff < minX)
            {
            minX = absDiff;
            }
          }
        }
      else if (v > -1024 && v < 3071) // comparison is always true in unsigned case
        {
        if (absDiff > maxX)
          {
          maxX = absDiff;
          }
        }
      ++pos;
      }

    double r = sqrt(minX * minX +
        fabs((double)y - centerY) * fabs((double)y - centerY));
    if (r < minRadius)
      {
      minRadius = r;
      }
    r = sqrt(maxX * maxX + 
        fabs((double)y - centerY) * fabs((double)y - centerY));
    if (r > maxRadius)
      {
      maxRadius = r;
      }
    }
  
  if (minRadius < centerX && maxRadius > 0 && fabs(maxRadius - minRadius) < 3)
    {
    radius = max(minRadius, maxRadius);
    }
  if (radius > centerX) 
    {
    radius = centerX;
    }

  //std::cout << "Estimated Radius: " << radius 
  //<< " MinR : " << minRadius << " MaxR: " << 
  //maxRadius << " MaxPos: " << centerX << std::endl;
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::GetSlicesInscribedCircle(int slice_idx_start, 
                          int slice_idx_end, double *min, double *max,
                          double *Radius, int max_nb_slices)
{
  vtkDICOMCollector::ImageInfo *info = this->GetCurrentImageInfo();
  if (!info || 
      !this->CollectAllSlices() || 
      slice_idx_start < 0 || 
      slice_idx_start >= this->GetNumberOfCollectedSlices() ||
      slice_idx_end < 0 || 
      slice_idx_end >= this->GetNumberOfCollectedSlices() ||
      info->PixelPaddingValueTagFound ||
      info->Rows != info->Columns || 
      info->Spacing[0] != info->Spacing[1]
     )
    {
    return;
    }

  this->InvokeEvent(vtkCommand::StartEvent);

  double progress;

  size_t image_data_size = 
    ((info->BitsAllocated + 7) / 8) * 
    info->Columns * info->Rows * info->Planes * info->SamplesPerPixel;

  char *buffer = new char [image_data_size];

  double idx, slice_step;
  int nb_slices = slice_idx_end - slice_idx_start + 1;
  if (max_nb_slices <= 0 || nb_slices <= max_nb_slices)
    {
    slice_step = 1.0;
    idx = (double)slice_idx_start;
    }
  else
    {
    slice_step = (double)nb_slices / (double)max_nb_slices;
    idx = (double)slice_idx_start + slice_step * 0.5;
    }

  double radius;

  std::map< short, unsigned int > histogram;
  
  for (; idx <= (double)slice_idx_end; idx += slice_step)
    {
    int slice_idx = vtkMath::Floor(idx);
    ImageSlot *slice = this->Internals->Slices[slice_idx];
    if (slice)
      { 
      if (strcmp(this->GetImageMedicalProperties(slice)->GetModality(), "CT"))
        {
        *Radius = VTK_DOUBLE_MAX;
        return;
        }
      this->RetrieveImageData(slice, buffer, 1, 0, min, max, 1);
      }
    progress = (double)(slice_idx - slice_idx_start) /
      (double)(slice_idx_end - slice_idx_start);

    // Look at the buffer to see if there are any pixels outside the range
    if (info->BitsAllocated <= 16 && info->BitsAllocated > 8)
      {
      if (info->PixelRepresentation ==
          vtkDICOMCollector::ImageInfo::PixelRepresentationUnsigned)
        {
        vtkGetSlicesInscribedCircleTemplate(
          info->Rows, info->Columns,
          static_cast<unsigned short*>((void *)buffer), radius, histogram);
        }
      else
        {
        vtkGetSlicesInscribedCircleTemplate(
          info->Rows, info->Columns,
          static_cast<short*>((void *)buffer), radius, histogram);
        }
      }

    this->InvokeEvent(vtkCommand::ProgressEvent, &progress);
    }

  // Get the median of the histogram. This must be the value to be replaced
  vtkGenericMathUtilities * utils = vtkGenericMathUtilities::New();
  short median;
  double factor = utils->GetMedian(histogram, median);
  // std::cout << "Median: " << median << " factor: " << factor << std::endl;
  utils->Delete();

  if (factor > 0.95 &&
      ((median*info->RescaleSlope + info->RescaleIntercept) < -1024 ||
       (median*info->RescaleSlope + info->RescaleIntercept) > 4095))
    {
    this->PixelPaddingValueTagAssumed = 1;
    this->PixelPaddingValue           = median;
    }

  delete [] buffer;

  this->InvokeEvent(vtkCommand::EndEvent);

  *Radius = radius;
  this->CTReconstructionRadius = radius;
}

//----------------------------------------------------------------------------
void vtkDICOMCollector::ComputeSliceSpacing(int current_vol)
{
  typedef vtkArrayMap<double, int> SpacingsContainer;
  typedef vtkArrayMapIterator<double, int> SpacingsContainerIterator;

  SpacingsContainer *spacings = SpacingsContainer::New();
  SpacingsContainerIterator *s_it = spacings->NewIterator();

  double spacing = 1.0, spacing_error = 0.01;
  int i, j, occ;

  ImageSlot *slicei = 0, *slicej = 0;
  int slice_start = this->GetStartSliceForVolume(current_vol);
  int slice_end = this->GetEndSliceForVolume(current_vol);
  for (i = slice_start; i < slice_end - 1; ++i)
    {
    slicei = this->Internals->Slices[i];
    slicej = this->Internals->Slices[i + 1];
    spacing = fabs(vtkMath::Dot(slicej->Info->Position,
                                slicej->Info->GetOrientationImage()) -
                   vtkMath::Dot(slicei->Info->Position,
                                slicei->Info->GetOrientationImage()));
    
    // Check if that spacing is represented already (within error)
    
    double stored_spacing;
    s_it->InitTraversal();
    while (!s_it->IsDoneWithTraversal())
      {
      if (s_it->GetKey(stored_spacing) == VTK_OK && 
          s_it->GetData(occ) == VTK_OK)
        {
        double error = stored_spacing * spacing_error;
        if (spacing >= stored_spacing - error &&
            spacing <= stored_spacing + error)
          {
          spacing = stored_spacing;
          break;
          }
        }
      s_it->GoToNextItem();
      }
    
    occ = 0;
    if (spacings->IsItemPresent(spacing))
      {
      spacings->GetItem(spacing, occ);
      }
    spacings->SetItem(spacing, ++occ);
    }
  
  // Compute the original spacing between the first two slices
  // (which is the last spacing that was computed)
  
  this->SliceSpacing = spacing;
  
  // If more than one invalid spacing was found, ooops, display them
  // Also, search the most represented one
  
  if (spacings->GetNumberOfItems() > 1)
    {
    vtksys_ios::ostringstream spacings_str;
    int ok = 1, max_occ = 0;
    double most_represented_spacing = this->SliceSpacing;
    double error = this->SliceSpacing * spacing_error;
    
    s_it->InitTraversal();
    while (!s_it->IsDoneWithTraversal())
      {
      if (s_it->GetKey(spacing) == VTK_OK && s_it->GetData(occ) == VTK_OK)
        {
        spacings_str << spacing << " (x " << occ << "), ";
        if (!(spacing >= this->SliceSpacing - error &&
              spacing <= this->SliceSpacing + error))
          {
          ok = 0;
          }
        if (occ > max_occ)
          {
          max_occ = occ;
          most_represented_spacing = spacing;
          }
        }
      s_it->GoToNextItem();
      }
    
    // Too many invalid spacings. Pick the most represented one
    // And remove all slices not matching this spacing
    
    if (!ok)
      {
      this->SliceSpacing = most_represented_spacing;
      vtkstd::vector<unsigned int> slices_to_delete;
      
      for (i = 0; i < (int)this->Internals->Slices.size() - 2; i++)
        {
        slicei = this->Internals->Slices[i];
        slicej = this->Internals->Slices[i + 1];
        spacing = fabs(vtkMath::Dot(slicej->Info->Position,
                                    slicej->Info->GetOrientationImage())-
                       vtkMath::Dot(slicei->Info->Position,
                                    slicei->Info->GetOrientationImage()));
        if (!(spacing >= this->SliceSpacing - error &&
              spacing <= this->SliceSpacing + error))
          {
          slices_to_delete.push_back(i);
          }
        }

      // Now that we have a list of slices to remove, we need to fix
      // the start and end indexes of all volumes, i.e. shift them
      // accordingly.

      for (i = (int)slices_to_delete.size() - 1; i >= 0; --i)
        {
        
        int slice_to_delete = slices_to_delete[i];
        for (j = 0; j < (int)this->SliceIndexes->GetNumberOfVolumes(); ++j)
          {
          unsigned int vol_start = this->SliceIndexes->GetStart(j);
          if (vol_start >= slice_to_delete)
            {
            this->SliceIndexes->SetStart(j, vol_start - 1);
            }
          unsigned int vol_end = this->SliceIndexes->GetEnd(j);
          if (vol_end >= slice_to_delete)
            {
            this->SliceIndexes->SetEnd(j, vol_end - 1);
            }
          }
        }

      // Delete the slices

      for (i = (int)slices_to_delete.size() - 1; i >= 0; --i)
        {
        vtkDICOMCollectorInternals::ImagesContainerIterator icit =
          this->Internals->Slices.begin() + slices_to_delete[i];
        this->Internals->Slices.erase(icit);
        }

      vtkWarningMacro(
        "DICOM file [" << (this->GetFileName() ? this->GetFileName() : "")
        << "]\n => images in this series do not have a constant spacing "
        "which is required. " << spacings->GetNumberOfItems() 
        << " different spacings were found: " 
        << spacings_str.str().c_str() << ". Picking " << this->SliceSpacing);

      // Now sort based on the distance between images

      vtksys_stl::stable_sort(
        this->Internals->Slices.begin(),
        this->Internals->Slices.end(),
        ImageSlotCompare);
      }
    }

  s_it->Delete();
  spacings->Delete();
}

//----------------------------------------------------------------------------
// (0020|0032) Image Position (patient) is a type 1 tag that specifies the 
// position of top left corner of any DICOM image in the RCS. This position
// is invariant to the direction cosines (Image Orientation). See page
// 275, DICOM part 3.
//
// For any given co-ordinate system, this method will return the origin as the
// position of the point (-X,-Y,-Z). So for an image in the LPS co-ordinate
// system, it would be the position of the voxel that's most RAI. When in the
// RAS co-ordinate system, its the position of the voxel that's most LPI.
//
int vtkDICOMCollector::ComputeImageOrigin(double origin[3])
{
  origin[0] = origin[1] = origin[2] = 0.0;
  if (!this->CollectAllSlices())
    {
    return 0;
    }

  // Find out he sub volume we are working on:
  int current_vol = this->GetCurrentVolume();
  // Slices are ordered in increasing order, so slice2 is at least >= slice1
  vtkDICOMCollector::ImageInfo *info1
   = this->Internals->Slices[this->GetStartSliceForVolume(current_vol)]->Info;
  vtkDICOMCollector::ImageInfo *info2
   = this->Internals->Slices[this->GetEndSliceForVolume(current_vol)]->Info;
  vtkDICOMCollector::ImageInfo *info = info1;

  vtksys_ios::ostringstream orientationString; // Help user understand code

  // Slices are axial
  if (fabs(info1->GetOrientationRow()[2]) < 0.5 && fabs(info1->GetOrientationColumn()[2])<0.5)
    {
    if (info2->Position[2] < info1->Position[2])
      {
      info = info2;
      }

    orientationString << "Slices are axial.\nIf you load this slice in "
      << "Tomovision, you should find that the \"Image Position (Patient)\""
     << " tag on file is the co-ordinate that\'s most : ";

    origin[2] = info->Position[2];
    if (info->GetOrientationRow()[0] < 0.0)
      {
      origin[0] = info->Position[0] - info->Spacing[0] * info->Columns; 
      orientationString << "L";
      }
    else
      {
      orientationString << "R";
      origin[0] = info->Position[0];
      }

    if (info->GetOrientationColumn()[1] < 0.0)
      {
      origin[1] = info->Position[1] - info->Spacing[1] * info->Rows; 
      orientationString << "P";
      }
    else
      {
      orientationString << "A";
      origin[1] = info->Position[1];
      }

    orientationString << "\nThe slice has " << info->Rows << " rows (A-P) and "
      << info->Columns << " columns (L-R).";
    orientationString << "\nSpacing in direction (A-P) is "
                                          << info->Spacing[0];
    orientationString << "\nSpacing in direction (R-L) is "
                                          << info->Spacing[1];
    }

  // Slices are coronal
  else if (fabs(info1->GetOrientationRow()[1]) < 0.5 && fabs(info1->GetOrientationColumn()[1])<0.5)
    {
    if (info2->Position[1] < info1->Position[1])
      {
      info = info2;
      }

    orientationString << "Slices are coronal.\nIf you load this slice in "
      << "Tomovision, you should find that the \"Image Position (Patient)\""
     << " tag on file is the co-ordinate that\'s most : ";

    origin[1] = info->Position[1];
    if (info->GetOrientationRow()[0] < 0.0)
      {
      origin[0] = info->Position[0] - info->Spacing[0] * info->Columns;
      orientationString << "L";
      }
    else
      {
      orientationString << "R";
      origin[0] = info->Position[0];
      }

    if (info->GetOrientationColumn()[2] < 0.0)
      {
      origin[2] = info->Position[2] - info->Spacing[1] * info->Rows;
      orientationString << "S";
      }
    else
      {
      orientationString << "I";
      origin[2] = info->Position[2];
      }

    orientationString << "\nThe slice has " << info->Rows << " rows (S-I) and "
      << info->Columns << " columns (L-R).";
    orientationString << "\nSpacing in direction (S-I) is "
                                          << info->Spacing[1];
    orientationString << "\nSpacing in direction (R-L) is "
                                          << info->Spacing[0];
    }

  // Slices are sagittal
  else if (fabs(info1->GetOrientationRow()[0]) < 0.5 && fabs(info1->GetOrientationColumn()[0])<0.5)
    {
    if (info2->Position[0] < info1->Position[0])
      {
      info = info2;
      }

    orientationString << "Slices are coronal.\nIf you load this slice in "
      << "Tomovision, you should find that the \"Image Position (Patient)\""
     << " tag on file is the co-ordinate that\'s most : ";

    origin[0] = info->Position[0];
    if (info->GetOrientationRow()[1] < 0.0)
      {
      origin[1] = info->Position[1] - info->Spacing[1] * info->Rows;
      orientationString << "P";
      }
    else
      {
      orientationString << "A";
      origin[1] = info->Position[1];
      }

    if (info->GetOrientationColumn()[2] < 0.0)
      {
      origin[2] = info->Position[2] - info->Spacing[0] * info->Columns;
      orientationString << "S";
      }
    else
      {
      orientationString << "I";
      origin[2] = info->Position[2];
      }

    orientationString << "\nThe slice has " << info->Rows << " rows (A-P) and "
      << info->Columns << " columns (S-I).";
    orientationString << "\nSpacing in direction (S-I) is "
                                          << info->Spacing[0];
    orientationString << "\nSpacing in direction (A-P) is "
                                          << info->Spacing[1];
    }

  vtkDebugMacro(<< orientationString.str().c_str());

  // Print out the orientation string and compare with Tomovision if you
  // are reporting a bug.

  return 1;
}


//----------------------------------------------------------------------------
#if defined(KWCommonPro_USE_GDCM) && KWCommonPro_USE_GDCM_FOR_HEADER
void vtkDICOMCollector::GetImageOrientation(const gdcmFile *, 
                          vtkDICOMCollector::ImageInfo *info)
{
  float iop[6];
  if ((file->CheckIfEntryExist(0x0020,0x0037) ||
       file->CheckIfEntryExist(0x0020,0x0035)) &&
      file->GetImageOrientationPatient(iop))
    {
    info->Orientation[0] = iop[0]; 
    info->Orientation[1] = iop[1];
    info->Orientation[2] = iop[2];
    info->Orientation[3] = iop[3];
    info->Orientation[4] = iop[4];
    info->Orientation[5] = iop[5];
    }
  else
    {
    info->Orientation[0] = 1.0; 
    info->Orientation[1] = 0.0;
    info->Orientation[2] = 0.0;
    info->Orientation[3] = 0.0;
    info->Orientation[4] = 1.0;
    info->Orientation[5] = 0.0;
    }
}

#elif defined(KWCommonPro_USE_CTNLIB)

int vtkDICOMCollector::GetImageOrientation(DCM_OBJECT *object,
                          vtkDICOMCollector::ImageInfo *info,
                          vtkDICOMCollector::ImageSlot* image)
{
  char orientation[1024];
  U32 rtn_length;
  void *ctx = NULL;
  DCM_ELEMENT iorient = {DCM_RELIMAGEORIENTATIONPATIENT, DCM_DS, "", 1,
                         sizeof(orientation), {orientation}};
  if (DCM_GetElementValue(&object, &iorient, &rtn_length, &ctx) != DCM_NORMAL)
    {
    // Assuming identity matrix for direction cosines

    if (image == this->CurrentImage)
      {
      vtkWarningMacro(
        "DICOM file [" << (image->GetFileName() ? image->GetFileName() : "")
        << "]\n => unable to get image orientation, assuming 1, 0, 0/0, 1, 0!");
      }
    info->Orientation[0] = 1.0; 
    info->Orientation[1] = 0.0;
    info->Orientation[2] = 0.0;
    info->Orientation[3] = 0.0;
    info->Orientation[4] = 1.0;
    info->Orientation[5] = 0.0;
    return 0;
    }
  else
    {
    // We found the [Image Position (Patient)] tag.. use it.
    orientation[rtn_length] = '\0';
    sscanf(orientation, "%lf\\%lf\\%lf\\%lf\\%lf\\%lf", 
           info->Orientation + 0, 
           info->Orientation + 1, 
           info->Orientation + 2,
           info->Orientation + 3, 
           info->Orientation + 4,
           info->Orientation + 5);
    }
  return 1;
}
#endif

