]> Creatis software - gdcm.git/blobdiff - src/gdcmFileHelper.cxx
When 'Pixel Aspect Ratio' exists, don't add a default 'Pixel Spacing'
[gdcm.git] / src / gdcmFileHelper.cxx
index 27e1369ca2fa8cb476cd491676cc2ae6773f1420..9005f118feabe1612282f3c4fdf41397838d90d1 100644 (file)
@@ -4,8 +4,8 @@
   Module:    $RCSfile: gdcmFileHelper.cxx,v $
   Language:  C++
 
-  Date:      $Date: 2006/04/20 16:12:11 $
-  Version:   $Revision: 1.101 $
+  Date:      $Date: 2007/08/28 09:29:26 $
+  Version:   $Revision: 1.123 $
                                                                                 
   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
   l'Image). All rights reserved. See Doc/License.txt or
@@ -51,73 +51,114 @@ gdcm::File *f = new gdcm::File(fileName);
 // user may also decide he doesn't want to load some parts of the header
 gdcm::File *f = new gdcm::File();
 f->SetFileName(fileName);
-   f->SetLoadMode(LD_NOSEQ);             // or      
-   f->SetLoadMode(LD_NOSHADOW);          // or
+   f->SetLoadMode(LD_NOSEQ);               // or      
+   f->SetLoadMode(LD_NOSHADOW);            // or
    f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW); // or
    f->SetLoadMode(LD_NOSHADOWSEQ);
 f->Load();
 
+// To decide whether it's an 'image of interest for him, or not,
 // user can now check some values
 std::string v = f->GetEntryValue(groupNb,ElementNb);
 
 // to get the pixels, user needs a gdcm::FileHelper
 gdcm::FileHelper *fh = new gdcm::FileHelper(f);
-// user may ask not to convert Palette to RGB
+
+// user may ask not to convert Palette (if any) to RGB
 uint8_t *pixels = fh->GetImageDataRaw();
 int imageLength = fh->GetImageDataRawSize();
+
 // He can now use the pixels, create a new image, ...
 uint8_t *userPixels = ...
 
-To re-write the image, user re-uses the gdcm::FileHelper
+//To re-write the image, user re-uses the gdcm::FileHelper
+gdcm::File *fh = new gdcm::FileHelper();
 
-fh->SetImageData( userPixels, userPixelsLength);
 fh->SetTypeToRaw(); // Even if it was possible to convert Palette to RGB
-                     // (WriteMode is set)
+                    // (WriteMode is set)
+
+// If user wants to write the file as MONOCHROME1 (0=white)
+fh->SetPhotometricInterpretationToMonochrome1();
+
 fh->SetWriteTypeToDcmExpl();  // he wants Explicit Value Representation
-                              // Little Endian is the default
-                              // no other value is allowed
+                              // Little Endian is the default,
+                              // bigendian not supported for writting
                                 (-->SetWriteType(ExplicitVR);)
                                    -->WriteType = ExplicitVR;
+fh->SetWriteTypeToJPEG();     // lossless compression   
+fh->SetWriteTypeToJPEG2000(); // lossless compression   
+
+fh->SetImageData( userPixels, userPixelsLength);
+or
+fh->SetUserData( userPixels, userPixelsLength); // this one performs compression, when required
+   
 fh->Write(newFileName);      // overwrites the file, if any
 
-// or :
-fh->WriteDcmExplVR(newFileName);
 
 
-// ----------------------------- WARNING -------------------------
+
 
 These lines will be moved to the document-to-be 'Developer's Guide'
 
 WriteMode : WMODE_RAW / WMODE_RGB
 WriteType : ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO
+PhotometricInterpretation : MONOCHROME2 (0=black), MONOCHROME2 (0=white)
 
-fh1->Write(newFileName);
-   SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR();
-   (modifies TransferSyntax)
+fh->SetImageData( userPixels, userPixelsLength);
+or
+fh->SetUserData( userPixels, userPixelsLength);
+   PixelWriteConverter->SetUserData(inData, expectedSize);
+   
+   
+fh->SetWriteMode(WMODE_RAW / WMODE_RGB)
+
+fh->SetWriteType( ImplicitVR/ExplicitVR/ACR/ACR_LIBIDO/JPEG/JPEG2000)
+      
+fh->Write(newFileName);
+   CheckMandatoryElements(); // Checks existing ones / Add missing ones
+   Fix VR if unknown elements
+   SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR(); /
+   SetWriteFileTypeToACR() / SetWriteFileTypeToJPEG() / SetWriteFileTypeToJ2K()
+      (Modifies TransferSyntax if any; Pushes to the Archives old one)
    SetWriteToRaw(); / SetWriteToRGB();
-      (modifies, when necessary : photochromatic interpretation, 
-         samples per pixel, Planar configuration, 
-         bits allocated, bits stored, high bit -ACR 24 bits-
-         Pixels element VR, pushes out the LUT )
+      (Modifies and pushes to the Archive, when necessary : photochr. interp., 
+       samples per pixel, Planar configuration, 
+       bits allocated, bits stored, high bit -ACR 24 bits-
+       Pixels element VR, pushes out the LUT )
+          SetWriteToRaw()
+             Sets Photometric Interpretation
+             DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
+             Sets VR, BinArea, Length for PixelData
+             if MONOCHROME1
+                ConvertFixGreyLevels
+             Archive->Push(photInt);
+             Archive->Push(pixel);
+             photInt->Delete();
+             pixel->Delete();
+        SetWriteToRGB()
+           if NumberOfScalarComponents==1
+              SetWriteToRaw(); return;
+           PixelReadConverter->BuildRGBImage()
+           DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
+           Archives spp, planConfig,photInt, pixel
+           Pushes out any LUT               
    CheckWriteIntegrity();
       (checks user given pixels length)
    FileInternal->Write(fileName,WriteType)
-   fp = opens file(fileName);
-   ComputeGroup0002Length( );
-   BitsAllocated 12->16
-      RemoveEntry(palettes, etc)
+      fp = opens file(fileName); // out|binary
+      ComputeGroup0002Length( );
       Document::WriteContent(fp, writetype);
+         writes Dicom File Preamble not ACR-NEMA
+         ElementSet::WriteContent(fp, writetype);
+            writes recursively all DataElements    
    RestoreWrite();
-      (moves back to the File all the archived elements)
-   RestoreWriteFileType();
-      (pushes back group 0002, with TransferSyntax)
+         (moves back to the gdcm::File all the archived elements)
 */
 
 
 
 
-namespace gdcm 
+namespace GDCM_NAME_SPACE 
 {
 typedef std::map<uint16_t, int> GroupHT;    //  Hash Table
 //-------------------------------------------------------------------------
@@ -230,8 +271,8 @@ bool FileHelper::Load()
 }
 
 /**
- * \brief   Accesses an existing DataEntry through it's (group, element) 
- *          and modifies it's content with the given value.
+ * \brief   Accesses an existing DataEntry through its (group, element) 
+ *          and modifies its content with the given value.
  * @param   content new value (string) to substitute with
  * @param   group  group number of the Dicom Element to modify
  * @param   elem element number of the Dicom Element to modify
@@ -245,8 +286,8 @@ bool FileHelper::SetEntryString(std::string const &content,
 
 
 /**
- * \brief   Accesses an existing DataEntry through it's (group, element) 
- *          and modifies it's content with the given value.
+ * \brief   Accesses an existing DataEntry through its (group, element) 
+ *          and modifies its content with the given value.
  * @param   content new value (void*  -> uint8_t*) to substitute with
  * @param   lgth new value length
  * @param   group  group number of the Dicom Element to modify
@@ -265,30 +306,34 @@ bool FileHelper::SetEntryBinArea(uint8_t *content, int lgth,
  * @param   content (string) value to be set
  * @param   group   Group number of the Entry 
  * @param   elem  Element number of the Entry
+ * @param   vr  Value Representation of the DataElement to be inserted
  * \return  pointer to the modified/created DataEntry (NULL when creation
  *          failed).
  */ 
 DataEntry *FileHelper::InsertEntryString(std::string const &content,
-                                                uint16_t group, uint16_t elem)
+                                         uint16_t group, uint16_t elem,
+                                         VRKey const &vr )
 {
-   return FileInternal->InsertEntryString(content, group, elem);
+   return FileInternal->InsertEntryString(content, group, elem, vr);
 }
 
 /**
  * \brief   Modifies the value of a given DataEntry when it exists.
  *          Creates it with the given value when unexistant.
  *          A copy of the binArea is made to be kept in the Document.
- * @param   binArea (binary)value to be set
+ * @param   binArea (binary) value to be set
  * @param   lgth new value length
  * @param   group   Group number of the Entry 
  * @param   elem  Element number of the Entry
+ * @param   vr  Value Representation of the DataElement to be inserted 
  * \return  pointer to the modified/created DataEntry (NULL when creation
  *          failed).
  */
 DataEntry *FileHelper::InsertEntryBinArea(uint8_t *binArea, int lgth,
-                                          uint16_t group, uint16_t elem)
+                                          uint16_t group, uint16_t elem,
+                                          VRKey const &vr )
 {
-   return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem);
+   return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem, vr);
 }
 
 /**
@@ -389,7 +434,7 @@ uint8_t *FileHelper::GetImageDataRaw ()
    return GetRaw();
 }
 
-#ifndef GDCM_LEGACY_REMOVE
+//#ifndef GDCM_LEGACY_REMOVE
 /*
  * \brief   Useless function, since PixelReadConverter forces us 
  *          copy the Pixels anyway.  
@@ -416,6 +461,7 @@ uint8_t *FileHelper::GetImageDataRaw ()
  * @return  On success, the number of bytes actually copied. Zero on
  *          failure e.g. MaxSize is lower than necessary.
  */
+ /*
 size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
 {
    if ( ! GetRaw() )
@@ -448,7 +494,8 @@ size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
            PixelReadConverter->GetRawSize() );
    return PixelReadConverter->GetRawSize();
 }
-#endif
+*/
+//#endif
 
 /**
  * \brief   Points the internal pointer to the callers inData
@@ -459,13 +506,15 @@ size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
  *          not to deallocate its data before gdcm uses them (e.g. with
  *          the Write() method )
  * @param inData user supplied pixel area (uint8_t* is just for the compiler.
- *               user is allowed to pass any kind of pixelsn since the size is
+ *               user is allowed to pass any kind of pixels since the size is
  *               given in bytes) 
  * @param expectedSize total image size, *in Bytes*
  */
 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
 {
-   SetUserData(inData, expectedSize);
+   PixelWriteConverter->SetUserData(inData, expectedSize);
+   /// \todo : shouldn't we call SetCompressJPEGUserData/SetCompressJPEG2000UserData
+   ///         here, too?
 }
 
 /**
@@ -478,6 +527,22 @@ void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
  */
 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
 {
+  // Shouldn't we move theese lines to FileHelper::Write()?
+/*  
+   if( WriteType == JPEG2000 )
+   {
+      PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
+   }
+   else if( WriteType == JPEG )
+   {
+      PixelWriteConverter->SetCompressJPEGUserData(inData, expectedSize, FileInternal);
+   }
+   else
+   {
+      PixelWriteConverter->SetUserData(inData, expectedSize);
+   }
+   */
+   // Just try!
    PixelWriteConverter->SetUserData(inData, expectedSize);
 }
 
@@ -660,8 +725,7 @@ bool FileHelper::WriteAcr (std::string const &fileName)
  * @return false if write fails
  */
 bool FileHelper::Write(std::string const &fileName)
-{
-
+{ 
    CheckMandatoryElements(); //called once, here !
    
    bool flag = false;
@@ -674,36 +738,10 @@ bool FileHelper::Write(std::string const &fileName)
  
       case Unknown:  // should never happen; ExplicitVR is the default value
       case ExplicitVR:
-
-   // User should ask gdcm to write an image in Explicit VR mode
-   // only when he is sure *all* the VR of *all* the DataElements is known.
-   // i.e : when there are *only* Public Groups
-   // or *all* the Shadow Groups are fully described in the relevant Shadow
-   // Dictionnary
-   // Let's just *dream* about it; *never* trust a user !
-   // We turn to Implicit VR if at least the VR of one element is unknown.
    
-         e = FileInternal->GetFirstEntry();
-         while (e != 0)
-         {
-            if (e->GetVR() == "  ")  
-            {
-               SetWriteTypeToDcmImplVR();
-               SetWriteFileTypeToImplicitVR();
-               flag = true;
-               break;         
-            } 
-            e = FileInternal->GetNextEntry();
-         }        
-
-         if (!flag)
-         {
-            SetWriteFileTypeToExplicitVR();
-         }
-         break;
+   // We let DocEntry::WriteContent to put vr=UN for undocumented Shadow Groups !
+         SetWriteFileTypeToExplicitVR();
 
-  SetWriteFileTypeToExplicitVR(); // to see JPRx
   break;
       case ACR:
       case ACR_LIBIDO:
@@ -720,12 +758,30 @@ bool FileHelper::Write(std::string const &fileName)
         // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
          break;
  
-      /// \todo FIXME : JPEG may be either ExplicitVR or ImplicitVR
+      /// \todo FIXME : JPEG/JPEG2000 may be either ExplicitVR or ImplicitVR      
       case JPEG:
          SetWriteFileTypeToJPEG();
+         // was :
+         //PixelWriteConverter->SetCompressJPEGUserData(
+         //   inData, expectedSize, FileInternal);
+ PixelWriteConverter->SetCompressJPEGUserData(
+    PixelWriteConverter->GetUserData(),
+    PixelWriteConverter->GetUserDataSize(),FileInternal);
          break;
-   }
+
+      case JPEG2000:
+         /// \TODO Maybe we should consider doing the compression here !
+         // PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
+
+         SetWriteFileTypeToJPEG2000();
+         PixelWriteConverter->SetCompressJPEG2000UserData(
+            PixelWriteConverter->GetUserData(),
+            PixelWriteConverter->GetUserDataSize(),
+            FileInternal);
  
+         break;
+   }
+
    // --------------------------------------------------------------
    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
    //
@@ -755,17 +811,20 @@ bool FileHelper::Write(std::string const &fileName)
          break;
    }
 
-   bool check = CheckWriteIntegrity(); // verifies length
-   if (WriteType == JPEG ) check = true;
+   bool check;
+   if (WriteType == JPEG || WriteType == JPEG2000)
+      check = true;
+   else
+      check = CheckWriteIntegrity(); // verifies length
+
    if (check)
    {
       check = FileInternal->Write(fileName,WriteType);
    }
 
-   RestoreWrite(); 
+   RestoreWrite();
   // RestoreWriteFileType();
   // RestoreWriteMandatory();
-   
 
    // --------------------------------------------------------------
    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
@@ -781,11 +840,7 @@ bool FileHelper::Write(std::string const &fileName)
 //-----------------------------------------------------------------------------
 // Protected
 /**
- * \brief Checks the write integrity
- *
- * The tests made are :
- *  - verify the size of the image to write with the possible write
- *    when the user set an image data
+ * \brief Verifies the size of the user given PixelData
  * @return true if check is successfull
  */
 bool FileHelper::CheckWriteIntegrity()
@@ -833,7 +888,6 @@ bool FileHelper::CheckWriteIntegrity()
             break;
       }
    }
-
    return true;
 }
 
@@ -841,6 +895,8 @@ bool FileHelper::CheckWriteIntegrity()
  * \brief Updates the File to write RAW data (as opposed to RGB data)
  *       (modifies, when necessary, photochromatic interpretation, 
  *       bits allocated, Pixels element VR)
+ *       WARNING : if SetPhotometricInterpretationToMonochrome1() was called
+ *                 before Pixel Elements is modified :-( 
  */ 
 void FileHelper::SetWriteToRaw()
 {
@@ -851,6 +907,7 @@ void FileHelper::SetWriteToRaw()
    } 
    else
    {
+      // 0x0028,0x0004 : Photometric Interpretation
       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
       if (FileInternal->HasLUT() )
       {
@@ -858,7 +915,10 @@ void FileHelper::SetWriteToRaw()
       }
       else
       {
-         photInt->SetString("MONOCHROME2 ");
+         if (GetPhotometricInterpretation() == 2)
+            photInt->SetString("MONOCHROME2 ");  // 0 = Black
+         else
+            photInt->SetString("MONOCHROME1 ");  // 0 = White !
       }
 
       PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
@@ -869,11 +929,23 @@ void FileHelper::SetWriteToRaw()
          vr = "OW";
       if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files 
          vr = "OB";
+       // For non RAW data. Mainly JPEG/JPEG2000
+      if( WriteType == JPEG || WriteType == JPEG2000)
+      {
+         vr = "OW";
+      }
+
       DataEntry *pixel = 
          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
-      pixel->SetLength(PixelWriteConverter->GetDataSize());
+      pixel->SetLength(
+         static_cast< uint32_t >(PixelWriteConverter->GetDataSize()) );
+
+      if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
+      {
+          ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );
+      }
 
       Archive->Push(photInt);
       Archive->Push(pixel);
@@ -897,13 +969,13 @@ void FileHelper::SetWriteToRGB()
       PixelReadConverter->BuildRGBImage();
       
       DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
-      spp->SetString("3 ");
+      spp->SetString("3 ");  // Don't drop trailing space
 
       DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
-      planConfig->SetString("0 ");
+      planConfig->SetString("0 "); // Don't drop trailing space
 
       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
-      photInt->SetString("RGB ");
+      photInt->SetString("RGB "); // Don't drop trailing space
 
       if ( PixelReadConverter->GetRGB() )
       {
@@ -982,7 +1054,6 @@ void FileHelper::SetWriteToRGB()
  */ 
 void FileHelper::RestoreWrite()
 {
-
    Archive->Restore(0x0028,0x0002);
    Archive->Restore(0x0028,0x0004);
    
@@ -1040,13 +1111,28 @@ void FileHelper::SetWriteFileTypeToACR()
    Archive->Push(0x0002,0x0102);
 }
 
+/**
+ * \brief Sets in the File the TransferSyntax to 'JPEG2000'
+ */
+void FileHelper::SetWriteFileTypeToJPEG2000()
+{
+   std::string ts = Util::DicomString(
+   Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
+
+   DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
+   tss->SetString(ts);
+
+   Archive->Push(tss);
+   tss->Delete();   
+}
+
 /**
  * \brief Sets in the File the TransferSyntax to 'JPEG'
- */ 
+ */
 void FileHelper::SetWriteFileTypeToJPEG()
 {
-   std::string ts = Util::DicomString( 
-      Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGBaselineProcess1) );
+   std::string ts = Util::DicomString(
+      Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGLosslessProcess14_1) );
 
    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
    tss->SetString(ts);
@@ -1083,14 +1169,6 @@ void FileHelper::SetWriteFileTypeToImplicitVR()
    tss->Delete();
 }
 
-
-/**
- * \brief Restore in the File the initial group 0002
- */ 
-void FileHelper::RestoreWriteFileType()
-{
-}
-
 /**
  * \brief Set the Write not to Libido format
  */ 
@@ -1103,12 +1181,9 @@ void FileHelper::SetWriteToLibido()
    {
       std::string rows, columns; 
 
-      //DataEntry *newRow=DataEntry::New(oldRow->GetDictEntry());
-      //DataEntry *newCol=DataEntry::New(oldCol->GetDictEntry());
-      
       DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
       DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
-      
+
       newRow->Copy(oldCol);
       newCol->Copy(oldRow);
 
@@ -1181,7 +1256,6 @@ DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
 
    if ( oldE )
    {
-      //newE = DataEntry::New(oldE->GetDictEntry());
       newE = DataEntry::New(group, elem, vr);
       newE->Copy(oldE);
    }
@@ -1237,11 +1311,12 @@ We have to deal with 4 *very* different cases :
     USER_OWN_IMAGE
 -2) user modified the pixels of an existing image.
    FILTERED_IMAGE
--3) user created a new image, using existing images (eg MIP, MPR, cartography image)
+-3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
    CREATED_IMAGE
--4) user modified/added some tags *without processing* the pixels (anonymization..
+-4) user modified/added some tags *without processing* the pixels (anonymization...)
    UNMODIFIED_PIXELS_IMAGE
-   
+-Probabely some more to be added.
 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
 
 1)2)3)4)
@@ -1377,15 +1452,20 @@ void FileHelper::CheckMandatoryElements()
   
    //0002 0000 UL 1 Meta Group Length
    //0002 0001 OB 1 File Meta Information Version
-   //0002 0002 UI 1 Media Stored SOP Class UID
-   //0002 0003 UI 1 Media Stored SOP Instance UID
+   //0002 0002 UI 1 Media Storage SOP Class UID
+   //0002 0003 UI 1 Media Storage SOP Instance UID
    //0002 0010 UI 1 Transfer Syntax UID
    //0002 0012 UI 1 Implementation Class UID
    //0002 0013 SH 1 Implementation Version Name
    //0002 0016 AE 1 Source Application Entity Title
    //0002 0100 UI 1 Private Information Creator
    //0002 0102 OB 1 Private Information
-  
+
+   // Push out 'ACR-NEMA-special' entries, if any
+      Archive->Push(0x0008,0x0001); // Length to End
+      Archive->Push(0x0008,0x0010); // Recognition Code
+      Archive->Push(0x0028,0x0005); // Image Dimension
+
    // Create them if not found
    // Always modify the value
    // Push the entries to the archive.
@@ -1409,10 +1489,10 @@ void FileHelper::CheckMandatoryElements()
    // 'Media Storage SOP Class UID'  --> [Secondary Capture Image Storage]
          CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
       }
-      
-   // 'Media Storage SOP Instance UID'   
+
+   // 'Media Storage SOP Instance UID'
       CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
-      
+
    // 'Implementation Class UID'
    // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
    //         seems to be Root UID + 4 digits (?)
@@ -1428,9 +1508,9 @@ void FileHelper::CheckMandatoryElements()
 
    if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
    { 
-   
+
       gdcmDebugMacro( "USER_OWN_IMAGE (1)");
-    // If 'SOP Class UID' exists ('true DICOM' image)
+   // If 'SOP Class UID' exists ('true DICOM' image)
    // we create the 'Source Image Sequence' SeqEntry
    // to hold informations about the Source Image
   
@@ -1494,9 +1574,11 @@ void FileHelper::CheckMandatoryElements()
    Archive->Push(0x0028,0x0017);
    Archive->Push(0x0028,0x0198);  // very old versions
    Archive->Push(0x0028,0x0199);
+
    // Replace deprecated 0028 0012 US Planes   
    // by new             0028 0008 IS Number of Frames
+
+  ///\todo : find if there is a rule!
    DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
    if ( e_0028_0012 )
    {
@@ -1516,7 +1598,8 @@ void FileHelper::CheckMandatoryElements()
    std::ostringstream s;
    // check 'Bits Allocated' vs decent values
    int nbBitsAllocated = FileInternal->GetBitsAllocated();
-   if ( nbBitsAllocated == 0 || nbBitsAllocated > 32)
+   if ( (nbBitsAllocated == 0 || nbBitsAllocated > 32)
+     || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
    {
       CopyMandatoryEntry(0x0028,0x0100,"16","US");
       gdcmWarningMacro("(0028,0100) changed from "
@@ -1549,47 +1632,115 @@ void FileHelper::CheckMandatoryElements()
                        << " for consistency purpose");
    }
 
-   std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
-   if ( pixelSpacing == GDCM_UNFOUND )
+   // check Pixel Representation (default it as 0 -unsigned-)
+
+   DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
+   if ( !e_0028_0103 )
    {
-      pixelSpacing = "1.0\\1.0";
-       // if missing, Pixel Spacing forced to "1.0\1.0"
-      CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
+      gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
+      CopyMandatoryEntry(0x0028, 0x0103,"0","US"); 
    }
-   
-   // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
-   // --> This one is the *legal* one !
-   if ( ContentType != USER_OWN_IMAGE)
-   //  we write it only when we are *sure* the image comes from
-   //         an imager (see also 0008,0x0064)          
-      CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
-   
-   // Samples Per Pixel (type 1) : default to grayscale 
+   else
+   {
+      int sign = (int)e_0028_0103->GetValue(0);
+      if (sign !=1 && sign !=0)
+      {
+         gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
+         CopyMandatoryEntry(0x0028, 0x0103,"0","US");
+      }
+   }
+
+   std::string pixelAspectRatio = FileInternal->GetEntryString(0x0028,0x0034);
+   if ( pixelAspectRatio == GDCM_UNFOUND ) // avoid conflict with pixelSpacing !
+   {
+      std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
+      if ( pixelSpacing == GDCM_UNFOUND )
+      {
+         pixelSpacing = "1.0\\1.0";
+          // if missing, Pixel Spacing forced to "1.0\1.0"
+         CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
+      }
+  
+      // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
+      // --> This one is the *legal* one !
+      if ( ContentType != USER_OWN_IMAGE)
+      //  we write it only when we are *sure* the image comes from
+      //         an imager (see also 0008,0x0064)
+         CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
+   } 
+/*
+///Exact meaning of RETired fields
+
+// See page 73 of ACR-NEMA_300-1988.pdf !
+
+// 0020,0020 : Patient Orientation :
+Patient direction of the first row and
+column of the images. The first entry id the direction of the raws, given by the
+direction of the last pixel in the first row from the first pixel in tha row.
+the second entry is the direction of the columns, given by the direction of the
+last pixel in the first column from the first pixel in that column.
+L : Left, F : Feet, A : Anterior, P : Posterior.
+Up to 3 letters can be used in combination to indicate oblique planes.
+
+//0020,0030 Image Position (RET)
+x,y,z coordinates im mm of the first pixel in the image
+
+// 0020,0035 Image Orientation (RET)
+Direction cosines of the R axis of the image system with respect to the
+equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
+the image system with respect to the same axes
+
+//0020,0050 Location
+An image location reference, standard for the modality (such as CT bed position),
+used to indicate position. Calculation of position for other purposes
+is only from (0020,0030) and (0020,0035)
+*/
+
+/*
+// if imagePositionPatient    not found, default it with imagePositionRet,    if any
+// if imageOrientationPatient not found, default it with imageOrientationRet, if any
+
+   std::string imagePositionRet        = FileInternal->GetEntryString(0x0020,0x0030);
+   std::string imageOrientationRet     = FileInternal->GetEntryString(0x0020,0x0035);
+   std::string imagePositionPatient    = FileInternal->GetEntryString(0x0020,0x0032);
+   std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
+
+   if(  imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
+     && imagePositionRet     != GDCM_UNFOUND && imageOrientationRet     != GDCM_UNFOUND)
+   {
+      CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
+      Archive->Push(0x0020,0x0030); 
+      CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
+      Archive->Push(0x0020,0x0035);
+   }
+*/
+
+   // Samples Per Pixel (type 1) : default to grayscale
    CheckMandatoryEntry(0x0028,0x0002,"1","US");
 
    // --- Check UID-related Entries ---
  
    // At the end, not to overwrite the original ones,
-   // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'   
+   // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
    // 'SOP Instance UID'  
    CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
 
    if ( ContentType == USER_OWN_IMAGE)
    {
-      gdcmDebugMacro( "USER_OWN_IMAGE (2)");   
+      gdcmDebugMacro( "USER_OWN_IMAGE (2)");
        // Conversion Type.
        // Other possible values are :
        // See PS 3.3, Page 408
-   
+
        // DV = Digitized Video
-       // DI = Digital Interface   
+       // DI = Digital Interface 
        // DF = Digitized Film
        // WSD = Workstation
        // SD = Scanned Document
        // SI = Scanned Image
        // DRW = Drawing
        // SYN = Synthetic Image
-           
+
       CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
    } 
 /*
@@ -1599,7 +1750,7 @@ void FileHelper::CheckMandatoryElements()
    
    }
 */
-           
+
    // ---- The user will never have to take any action on the following ----
 
    // new value for 'SOP Instance UID'
@@ -1608,7 +1759,7 @@ void FileHelper::CheckMandatoryElements()
    // Instance Creation Date
    const std::string &date = Util::GetCurrentDate();
    CopyMandatoryEntry(0x0008,0x0012,date,"DA");
+
    // Instance Creation Time
    const std::string &time = Util::GetCurrentTime();
    CopyMandatoryEntry(0x0008,0x0013,time,"TM");
@@ -1656,15 +1807,15 @@ void FileHelper::CheckMandatoryElements()
 
    // Instance Number
    CheckMandatoryEntry(0x0020,0x0013,"","IS");
-   
+
    // Patient Orientation
    // Can be computed from (0020|0037) :  Image Orientation (Patient)
-   gdcm::Orientation *o = gdcm::Orientation::New();
+   GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
    std::string ori = o->GetOrientation ( FileInternal );
    o->Delete();
    if (ori != "\\" && ori != GDCM_UNFOUND)
       CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
-   else   
+   else
       CheckMandatoryEntry(0x0020,0x0020,"","CS");
 
    // Default Patient Position to HFS
@@ -1697,7 +1848,7 @@ void FileHelper::CheckMandatoryElements()
  /*
    // Deal with element 0x0000 (group length) of each group.
    // First stage : get all the different Groups
-   
+
   GroupHT grHT;
   DocEntry *d = FileInternal->GetFirstEntry();
   while(d)
@@ -1711,8 +1862,12 @@ void FileHelper::CheckMandatoryElements()
       CheckMandatoryEntry(it->first, 0x0000, "0"); 
   }    
   // Third stage : update all 'zero level' groups length
-*/ 
+*/
+
 
+   if (PhotometricInterpretation == 1)
+   {
+   }
 } 
 
 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
@@ -1728,7 +1883,7 @@ void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string va
    }    
 }
 
-/// \todo : what is it used for ? (FileHelper::SetMandatoryEntry) 
+/// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
 {
    //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
@@ -1783,7 +1938,6 @@ void FileHelper::RestoreWriteMandatory()
    Archive->Restore(0x0020,0x000e);
 }
 
-
 /**
  * \brief   CallStartMethod
  */
@@ -1820,9 +1974,11 @@ void FileHelper::Initialize()
 {
    UserFunction = 0;
    ContentType = USER_OWN_IMAGE;
-   
+
    WriteMode = WMODE_RAW;
    WriteType = ExplicitVR;
+   
+   PhotometricInterpretation = 2; // Black = 0
 
    PixelReadConverter  = new PixelReadConvert;
    PixelWriteConverter = new PixelWriteConvert;
@@ -1858,9 +2014,95 @@ uint8_t *FileHelper::GetRaw()
    return raw;
 }
 
+/**
+ * \brief Deal with Grey levels i.e. re-arange them
+ *        to have low values = dark, high values = bright
+ */
+void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
+{
+   uint32_t i; // to please M$VC6
+   int16_t j;
+
+   // Number of Bits Allocated for storing a Pixel is defaulted to 16
+   // when absent from the file.
+   int bitsAllocated = FileInternal->GetBitsAllocated();
+   if ( bitsAllocated == 0 )
+   {
+      bitsAllocated = 16;
+   }
+
+   else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
+   {
+      bitsAllocated = 16;
+   }   
+   // Number of "Bits Stored", defaulted to number of "Bits Allocated"
+   // when absent from the file.
+   int bitsStored = FileInternal->GetBitsStored();
+   if ( bitsStored == 0 )
+   {
+      bitsStored = bitsAllocated;
+   }
+
+   if (!FileInternal->IsSignedPixelData())
+   {
+      if ( bitsAllocated == 8 )
+      {
+         uint8_t *deb = (uint8_t *)raw;
+         for (i=0; i<rawSize; i++)      
+         {
+            *deb = 255 - *deb;
+            deb++;
+         }
+         return;
+      }
+
+      if ( bitsAllocated == 16 )
+      {
+         uint16_t mask =1;
+         for (j=0; j<bitsStored-1; j++)
+         {
+            mask = (mask << 1) +1; // will be fff when BitsStored=12
+         }
+
+         uint16_t *deb = (uint16_t *)raw;
+         for (i=0; i<rawSize/2; i++)      
+         {
+            *deb = mask - *deb;
+            deb++;
+         }
+         return;
+       }
+   }
+   else
+   {
+      if ( bitsAllocated == 8 )
+      {
+         uint8_t smask8 = 255;
+         uint8_t *deb = (uint8_t *)raw;
+         for (i=0; i<rawSize; i++)      
+         {
+            *deb = smask8 - *deb;
+            deb++;
+         }
+         return;
+      }
+      if ( bitsAllocated == 16 )
+      {
+         uint16_t smask16 = 65535;
+         uint16_t *deb = (uint16_t *)raw;
+         for (i=0; i<rawSize/2; i++)      
+         {
+            *deb = smask16 - *deb;
+            deb++;
+         }
+         return;
+      }
+   }
+}
+
 //-----------------------------------------------------------------------------
 /**
- * \brief   Prints the common part of DataEntry, SeqEntry
+ * \brief   Prints the FileInternal + info on PixelReadConvertor
  * @param   os ostream we want to print in
  * @param indent (unused)
  */
@@ -1878,3 +2120,153 @@ void FileHelper::Print(std::ostream &os, std::string const &)
 
 //-----------------------------------------------------------------------------
 } // end namespace gdcm
+
+
+/* Probabely something to be added to use Rescale Slope/Intercept
+Have a look ,at ITK code !
+
+// Internal function to rescale pixel according to Rescale Slope/Intercept
+template<class TBuffer, class TSource>
+void RescaleFunction(TBuffer* buffer, TSource *source,
+                     double slope, double intercept, size_t size)
+{
+  size /= sizeof(TSource);
+
+  if (slope != 1.0 && intercept != 0.0)
+    {
+    // Duff's device.  Instead of this code:
+    //
+    //   for(unsigned int i=0; i<size; i++)
+    //    {
+    //    buffer[i] = (TBuffer)(source[i]*slope + intercept);
+    //    }
+    //
+    // use Duff's device which exploits "fall through"
+    register size_t n = (size + 7) / 8;
+    switch ( size % 8)
+      {
+      case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
+      case 7:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
+      case 6:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
+      case 5:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
+      case 4:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
+      case 3:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
+      case 2:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
+      case 1:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
+                 }  while (--n > 0);
+      }
+    }
+  else if (slope == 1.0 && intercept != 0.0)
+    {
+    // Duff's device.  Instead of this code:
+    //
+    //   for(unsigned int i=0; i<size; i++)
+    //    {
+    //    buffer[i] = (TBuffer)(source[i] + intercept);
+    //    }
+    //
+    // use Duff's device which exploits "fall through"
+    register size_t n = (size + 7) / 8;
+    switch ( size % 8)
+      {
+      case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
+      case 7:      *buffer++ = (TBuffer)(*source++ + intercept);
+      case 6:      *buffer++ = (TBuffer)(*source++ + intercept);
+      case 5:      *buffer++ = (TBuffer)(*source++ + intercept);
+      case 4:      *buffer++ = (TBuffer)(*source++ + intercept);
+      case 3:      *buffer++ = (TBuffer)(*source++ + intercept);
+      case 2:      *buffer++ = (TBuffer)(*source++ + intercept);
+      case 1:      *buffer++ = (TBuffer)(*source++ + intercept);
+                 }  while (--n > 0);
+      }
+    }
+  else if (slope != 1.0 && intercept == 0.0)
+    {
+    // Duff's device.  Instead of this code:
+    //
+    //   for(unsigned int i=0; i<size; i++)
+    //    {
+    //    buffer[i] = (TBuffer)(source[i]*slope);
+    //    }
+    //
+    // use Duff's device which exploits "fall through"
+    register size_t n = (size + 7) / 8;
+    switch ( size % 8)
+      {
+      case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
+      case 7:      *buffer++ = (TBuffer)((*source++)*slope);
+      case 6:      *buffer++ = (TBuffer)((*source++)*slope);
+      case 5:      *buffer++ = (TBuffer)((*source++)*slope);
+      case 4:      *buffer++ = (TBuffer)((*source++)*slope);
+      case 3:      *buffer++ = (TBuffer)((*source++)*slope);
+      case 2:      *buffer++ = (TBuffer)((*source++)*slope);
+      case 1:      *buffer++ = (TBuffer)((*source++)*slope);
+                 }  while (--n > 0);
+      }
+    }
+  else
+    {
+    // Duff's device.  Instead of this code:
+    //
+    //   for(unsigned int i=0; i<size; i++)
+    //    {
+    //    buffer[i] = (TBuffer)(source[i]);
+    //    }
+    //
+    // use Duff's device which exploits "fall through"
+    register size_t n = (size + 7) / 8;
+    switch ( size % 8)
+      {
+      case 0: do { *buffer++ = (TBuffer)(*source++);
+      case 7:      *buffer++ = (TBuffer)(*source++);
+      case 6:      *buffer++ = (TBuffer)(*source++);
+      case 5:      *buffer++ = (TBuffer)(*source++);
+      case 4:      *buffer++ = (TBuffer)(*source++);
+      case 3:      *buffer++ = (TBuffer)(*source++);
+      case 2:      *buffer++ = (TBuffer)(*source++);
+      case 1:      *buffer++ = (TBuffer)(*source++);
+                 }  while (--n > 0);
+      }
+   }   
+}
+
+
+template<class TSource>
+void RescaleFunction(ImageIOBase::IOComponentType bufferType,
+                     void* buffer, TSource *source,
+                     double slope, double intercept, size_t size)
+{
+  switch (bufferType)
+    {
+    case ImageIOBase::UCHAR:
+      RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
+      break;
+    case ImageIOBase::CHAR:
+      RescaleFunction( (char *)buffer, source, slope, intercept, size);
+      break;
+    case ImageIOBase::USHORT:
+      RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
+      break;
+    case ImageIOBase::SHORT:
+      RescaleFunction( (short *)buffer, source, slope, intercept, size);
+      break;
+    case ImageIOBase::UINT:
+      RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
+      break;
+    case ImageIOBase::INT:
+      RescaleFunction( (int *)buffer, source, slope, intercept, size);
+      break;
+    case ImageIOBase::FLOAT:
+      RescaleFunction( (float *)buffer, source, slope, intercept, size);
+      break;
+    case ImageIOBase::DOUBLE:
+      RescaleFunction( (double *)buffer, source, slope, intercept, size);
+      break;
+    default:
+      ::itk::OStringStream message;
+      message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
+      ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);
+      throw e;
+    }
+}
+*/