]> Creatis software - gdcm.git/blobdiff - src/gdcmFileHelper.cxx
We are no longer cheated by Elem belonging to group 0x0002,
[gdcm.git] / src / gdcmFileHelper.cxx
index 70034af2efb06763e4c44b8e6ca5952325c0e5a9..2f54d3edc9579bb3a11f6306b7c40f93bdee9894 100644 (file)
@@ -4,8 +4,8 @@
   Module:    $RCSfile: gdcmFileHelper.cxx,v $
   Language:  C++
 
-  Date:      $Date: 2007/04/12 13:22:49 $
-  Version:   $Revision: 1.111 $
+  Date:      $Date: 2007/07/26 08:36:49 $
+  Version:   $Revision: 1.119 $
                                                                                 
   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
   l'Image). All rights reserved. See Doc/License.txt or
@@ -51,18 +51,19 @@ 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, ...
@@ -72,8 +73,11 @@ To re-write the image, user re-uses the 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
@@ -91,33 +95,58 @@ 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->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
 //-------------------------------------------------------------------------
@@ -265,6 +294,7 @@ 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).
  */ 
@@ -279,11 +309,11 @@ DataEntry *FileHelper::InsertEntryString(std::string const &content,
  * \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 Represenation of the DataElement to be inserted 
+ * @param   vr  Value Representation of the DataElement to be inserted 
  * \return  pointer to the modified/created DataEntry (NULL when creation
  *          failed).
  */
@@ -470,7 +500,7 @@ size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
  */
 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
 {
-   SetUserData(inData, expectedSize);
+   PixelWriteConverter->SetUserData(inData, expectedSize);
 }
 
 /**
@@ -483,7 +513,18 @@ void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
  */
 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
 {
-   PixelWriteConverter->SetUserData(inData, expectedSize);
+   if( WriteType == JPEG2000 )
+   {
+      PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
+   }
+   else if( WriteType == JPEG )
+   {
+      PixelWriteConverter->SetCompressJPEGUserData(inData, expectedSize, FileInternal);
+   }
+   else
+   {
+      PixelWriteConverter->SetUserData(inData, expectedSize);
+   }
 }
 
 /**
@@ -666,7 +707,6 @@ bool FileHelper::WriteAcr (std::string const &fileName)
  */
 bool FileHelper::Write(std::string const &fileName)
 {
-
    CheckMandatoryElements(); //called once, here !
    
    bool flag = false;
@@ -688,12 +728,15 @@ bool FileHelper::Write(std::string const &fileName)
    // 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.
    
+   // Better we let DocEntry::WriteContent to put vr=UN for undocumented Shadow Groups !
+
+/*
          e = FileInternal->GetFirstEntry();
          while (e != 0)
          {
             if (e->GetVR() == "  ")  
             {
+
                SetWriteTypeToDcmImplVR();
                SetWriteFileTypeToImplicitVR();
                flag = true;
@@ -707,8 +750,10 @@ bool FileHelper::Write(std::string const &fileName)
             SetWriteFileTypeToExplicitVR();
          }
          break;
+*/
+
+         SetWriteFileTypeToExplicitVR();
 
-         SetWriteFileTypeToExplicitVR(); // to see JPRx
   break;
       case ACR:
       case ACR_LIBIDO:
@@ -730,7 +775,7 @@ bool FileHelper::Write(std::string const &fileName)
          SetWriteFileTypeToJPEG();
          break;
 
-       case JPEG2000:
+      case JPEG2000:
          SetWriteFileTypeToJPEG2000();
          break;
    }
@@ -765,16 +810,17 @@ bool FileHelper::Write(std::string const &fileName)
    }
 
    bool check = CheckWriteIntegrity(); // verifies length
-   if (WriteType == JPEG || WriteType == JPEG2000) check = true;
+   if (WriteType == JPEG || WriteType == JPEG2000) 
+      check = true;
+
    if (check)
    {
       check = FileInternal->Write(fileName,WriteType);
    }
 
-   RestoreWrite(); 
+   RestoreWrite();
   // RestoreWriteFileType();
   // RestoreWriteMandatory();
-   
 
    // --------------------------------------------------------------
    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
@@ -790,11 +836,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()
@@ -842,7 +884,6 @@ bool FileHelper::CheckWriteIntegrity()
             break;
       }
    }
-
    return true;
 }
 
@@ -850,6 +891,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 if modified :-( 
  */ 
 void FileHelper::SetWriteToRaw()
 {
@@ -860,16 +903,20 @@ void FileHelper::SetWriteToRaw()
    } 
    else
    {
+      // 0x0028,0x0004 : Photometric Interpretation
       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
       if (FileInternal->HasLUT() )
       {
          photInt->SetString("PALETTE COLOR ");
       }
       else
-      {
-         photInt->SetString("MONOCHROME2 ");
+      {     
+         if (GetPhotometricInterpretation() == 2)
+            photInt->SetString("MONOCHROME2 ");  // 0 = Black
+         else
+            photInt->SetString("MONOCHROME1 ");  // 0 = White !
       }
-
+    
       PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
                                        PixelReadConverter->GetRawSize());
 
@@ -883,11 +930,17 @@ void FileHelper::SetWriteToRaw()
       {
          vr = "OW";
       }
+      
       DataEntry *pixel = 
          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
       pixel->SetLength(PixelWriteConverter->GetDataSize());
+     
+      if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
+      {
+          ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );
+      }      
 
       Archive->Push(photInt);
       Archive->Push(pixel);
@@ -996,7 +1049,6 @@ void FileHelper::SetWriteToRGB()
  */ 
 void FileHelper::RestoreWrite()
 {
-
    Archive->Restore(0x0028,0x0002);
    Archive->Restore(0x0028,0x0004);
    
@@ -1054,14 +1106,14 @@ void FileHelper::SetWriteFileTypeToACR()
    Archive->Push(0x0002,0x0102);
 }
 
- /**
 * \brief Sets in the File the TransferSyntax to 'JPEG2000'
 */
+/**
+ * \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);
 
@@ -1071,11 +1123,11 @@ void FileHelper::SetWriteFileTypeToJPEG2000()
 
 /**
  * \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);
@@ -1112,14 +1164,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
  */ 
@@ -1131,9 +1175,6 @@ void FileHelper::SetWriteToLibido()
    if ( oldRow && oldCol )
    {
       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");
@@ -1210,7 +1251,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);
    }
@@ -1268,9 +1308,9 @@ We have to deal with 4 *very* different cases :
    FILTERED_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  
+-Probabely some more to be added.  
  
 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
 
@@ -1407,20 +1447,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  
-  
+      Archive->Push(0x0028,0x0005); // Image Dimension
+
    // Create them if not found
    // Always modify the value
    // Push the entries to the archive.
@@ -1620,8 +1660,6 @@ void FileHelper::CheckMandatoryElements()
    //         an imager (see also 0008,0x0064)          
       CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
 
-
-
 /*
 ///Exact meaning of RETired fields
 
@@ -1665,17 +1703,17 @@ is only from (0020,0030) and (0020,0035)
       CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
       Archive->Push(0x0020,0x0030); 
       CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
-      Archive->Push(0x0020,0x0035);        
-   }        
+      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");
 
@@ -1687,7 +1725,7 @@ is only from (0020,0030) and (0020,0035)
        // See PS 3.3, Page 408
    
        // DV = Digitized Video
-       // DI = Digital Interface   
+       // DI = Digital Interface 
        // DF = Digitized Film
        // WSD = Workstation
        // SD = Scanned Document
@@ -1704,7 +1742,7 @@ is only from (0020,0030) and (0020,0035)
    
    }
 */
-           
+  
    // ---- The user will never have to take any action on the following ----
 
    // new value for 'SOP Instance UID'
@@ -1713,7 +1751,7 @@ is only from (0020,0030) and (0020,0035)
    // 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");
@@ -1761,15 +1799,15 @@ is only from (0020,0030) and (0020,0035)
 
    // 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
@@ -1816,8 +1854,12 @@ is only from (0020,0030) and (0020,0035)
       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 )
@@ -1888,7 +1930,6 @@ void FileHelper::RestoreWriteMandatory()
    Archive->Restore(0x0020,0x000e);
 }
 
-
 /**
  * \brief   CallStartMethod
  */
@@ -1925,9 +1966,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;
@@ -1963,9 +2006,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)
  */
@@ -2090,9 +2219,7 @@ void RescaleFunction(TBuffer* buffer, TSource *source,
       case 1:      *buffer++ = (TBuffer)(*source++);
                  }  while (--n > 0);
       }
-    }
-    
-    
+   }   
 }