1 /*=========================================================================
4 Module: $RCSfile: gdcmFileHelper.cxx,v $
7 Date: $Date: 2007/07/04 10:40:56 $
8 Version: $Revision: 1.113 $
10 Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
11 l'Image). All rights reserved. See Doc/License.txt or
12 http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
14 This software is distributed WITHOUT ANY WARRANTY; without even
15 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 PURPOSE. See the above copyright notices for more information.
18 =========================================================================*/
20 #include "gdcmFileHelper.h"
21 #include "gdcmGlobal.h"
23 #include "gdcmDocument.h"
24 #include "gdcmDebug.h"
26 #include "gdcmSeqEntry.h"
27 #include "gdcmSQItem.h"
28 #include "gdcmDataEntry.h"
29 #include "gdcmDocEntry.h"
31 #include "gdcmPixelReadConvert.h"
32 #include "gdcmPixelWriteConvert.h"
33 #include "gdcmDocEntryArchive.h"
34 #include "gdcmDictSet.h"
35 #include "gdcmOrientation.h"
37 #if defined(__BORLANDC__)
38 #include <mem.h> // for memset
44 // ----------------------------- WARNING -------------------------
46 These lines will be moved to the document-to-be 'User's Guide'
48 // To read an image, user needs a gdcm::File
49 gdcm::File *f = new gdcm::File(fileName);
51 // user may also decide he doesn't want to load some parts of the header
52 gdcm::File *f = new gdcm::File();
53 f->SetFileName(fileName);
54 f->SetLoadMode(LD_NOSEQ); // or
55 f->SetLoadMode(LD_NOSHADOW); // or
56 f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW); // or
57 f->SetLoadMode(LD_NOSHADOWSEQ);
60 // To decide whether it's an 'image of interest for him, or not,
61 // user can now check some values
62 std::string v = f->GetEntryValue(groupNb,ElementNb);
64 // to get the pixels, user needs a gdcm::FileHelper
65 gdcm::FileHelper *fh = new gdcm::FileHelper(f);
66 // user may ask not to convert Palette (if any) to RGB
67 uint8_t *pixels = fh->GetImageDataRaw();
68 int imageLength = fh->GetImageDataRawSize();
69 // He can now use the pixels, create a new image, ...
70 uint8_t *userPixels = ...
72 To re-write the image, user re-uses the gdcm::FileHelper
74 fh->SetImageData( userPixels, userPixelsLength);
75 fh->SetTypeToRaw(); // Even if it was possible to convert Palette to RGB
78 fh->SetWriteTypeToDcmExpl(); // he wants Explicit Value Representation
79 // Little Endian is the default
80 // no other value is allowed
81 (-->SetWriteType(ExplicitVR);)
82 -->WriteType = ExplicitVR;
83 fh->Write(newFileName); // overwrites the file, if any
86 fh->WriteDcmExplVR(newFileName);
89 // ----------------------------- WARNING -------------------------
91 These lines will be moved to the document-to-be 'Developer's Guide'
93 WriteMode : WMODE_RAW / WMODE_RGB
94 WriteType : ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO
96 fh1->Write(newFileName);
97 SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR();
98 (modifies TransferSyntax)
99 SetWriteToRaw(); / SetWriteToRGB();
100 (modifies, when necessary : photochromatic interpretation,
101 samples per pixel, Planar configuration,
102 bits allocated, bits stored, high bit -ACR 24 bits-
103 Pixels element VR, pushes out the LUT )
104 CheckWriteIntegrity();
105 (checks user given pixels length)
106 FileInternal->Write(fileName,WriteType)
107 fp = opens file(fileName);
108 ComputeGroup0002Length( );
110 RemoveEntry(palettes, etc)
111 Document::WriteContent(fp, writetype);
113 (moves back to the File all the archived elements)
114 RestoreWriteFileType();
115 (pushes back group 0002, with TransferSyntax)
121 namespace GDCM_NAME_SPACE
123 typedef std::map<uint16_t, int> GroupHT; // Hash Table
124 //-------------------------------------------------------------------------
125 // Constructor / Destructor
127 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
128 * file (gdcm::File only deals with the ... header)
129 * Opens (in read only and when possible) an existing file and checks
130 * for DICOM compliance. Returns NULL on failure.
131 * It will be up to the user to load the pixels into memory
132 * ( GetImageDataSize() + GetImageData() methods)
133 * \note the in-memory representation of all available tags found in
134 * the DICOM header is post-poned to first header information access.
135 * This avoid a double parsing of public part of the header when
136 * one sets an a posteriori shadow dictionary (efficiency can be
137 * seen as a side effect).
139 FileHelper::FileHelper( )
141 FileInternal = File::New( );
146 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
147 * file (File only deals with the ... header)
148 * Opens (in read only and when possible) an existing file and checks
149 * for DICOM compliance. Returns NULL on failure.
150 * It will be up to the user to load the pixels into memory
151 * ( GetImageDataSize() + GetImageData() methods)
152 * \note the in-memory representation of all available tags found in
153 * the DICOM header is post-poned to first header information access.
154 * This avoid a double parsing of public part of the header when
155 * user sets an a posteriori shadow dictionary (efficiency can be
156 * seen as a side effect).
157 * @param header already built Header
159 FileHelper::FileHelper(File *header)
161 gdcmAssertMacro(header);
163 FileInternal = header;
164 FileInternal->Register();
166 if ( FileInternal->IsReadable() )
168 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
173 * \brief canonical destructor
174 * \note If the header (gdcm::File) was created by the FileHelper constructor,
175 * it is destroyed by the FileHelper
177 FileHelper::~FileHelper()
179 if ( PixelReadConverter )
181 delete PixelReadConverter;
183 if ( PixelWriteConverter )
185 delete PixelWriteConverter;
192 FileInternal->Unregister();
195 //-----------------------------------------------------------------------------
199 * \brief Sets the LoadMode of the internal gdcm::File as a boolean string.
200 * NO_SEQ, NO_SHADOW, NO_SHADOWSEQ ... (nothing more, right now)
201 * WARNING : before using NO_SHADOW, be sure *all* your files
202 * contain accurate values in the 0x0000 element (if any)
203 * of *each* Shadow Group. The parser will fail if the size is wrong !
204 * @param loadMode Load mode to be used
206 void FileHelper::SetLoadMode(int loadMode)
208 GetFile()->SetLoadMode( loadMode );
211 * \brief Sets the LoadMode of the internal gdcm::File
212 * @param fileName name of the file to be open
214 void FileHelper::SetFileName(std::string const &fileName)
216 FileInternal->SetFileName( fileName );
221 * @return false if file cannot be open or no swap info was found,
222 * or no tag was found.
224 bool FileHelper::Load()
226 if ( !FileInternal->Load() )
229 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
234 * \brief Accesses an existing DataEntry through its (group, element)
235 * and modifies its content with the given value.
236 * @param content new value (string) to substitute with
237 * @param group group number of the Dicom Element to modify
238 * @param elem element number of the Dicom Element to modify
239 * \return false if DataEntry not found
241 bool FileHelper::SetEntryString(std::string const &content,
242 uint16_t group, uint16_t elem)
244 return FileInternal->SetEntryString(content, group, elem);
249 * \brief Accesses an existing DataEntry through its (group, element)
250 * and modifies its content with the given value.
251 * @param content new value (void* -> uint8_t*) to substitute with
252 * @param lgth new value length
253 * @param group group number of the Dicom Element to modify
254 * @param elem element number of the Dicom Element to modify
255 * \return false if DataEntry not found
257 bool FileHelper::SetEntryBinArea(uint8_t *content, int lgth,
258 uint16_t group, uint16_t elem)
260 return FileInternal->SetEntryBinArea(content, lgth, group, elem);
264 * \brief Modifies the value of a given DataEntry when it exists.
265 * Creates it with the given value when unexistant.
266 * @param content (string) value to be set
267 * @param group Group number of the Entry
268 * @param elem Element number of the Entry
269 * @param vr Value Representation of the DataElement to be inserted
270 * \return pointer to the modified/created DataEntry (NULL when creation
273 DataEntry *FileHelper::InsertEntryString(std::string const &content,
274 uint16_t group, uint16_t elem,
277 return FileInternal->InsertEntryString(content, group, elem, vr);
281 * \brief Modifies the value of a given DataEntry when it exists.
282 * Creates it with the given value when unexistant.
283 * A copy of the binArea is made to be kept in the Document.
284 * @param binArea (binary) value to be set
285 * @param lgth new value length
286 * @param group Group number of the Entry
287 * @param elem Element number of the Entry
288 * @param vr Value Representation of the DataElement to be inserted
289 * \return pointer to the modified/created DataEntry (NULL when creation
292 DataEntry *FileHelper::InsertEntryBinArea(uint8_t *binArea, int lgth,
293 uint16_t group, uint16_t elem,
296 return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem, vr);
300 * \brief Adds an empty SeqEntry
301 * (remove any existing entry with same group,elem)
302 * @param group Group number of the Entry
303 * @param elem Element number of the Entry
304 * \return pointer to the created SeqEntry (NULL when creation
307 SeqEntry *FileHelper::InsertSeqEntry(uint16_t group, uint16_t elem)
309 return FileInternal->InsertSeqEntry(group, elem);
313 * \brief Get the size of the image data
314 * If the image can be RGB (with a lut or by default), the size
315 * corresponds to the RGB image
316 * (use GetImageDataRawSize if you want to be sure to get *only*
317 * the size of the pixels)
318 * @return The image size
320 size_t FileHelper::GetImageDataSize()
322 if ( PixelWriteConverter->GetUserData() )
324 return PixelWriteConverter->GetUserDataSize();
326 return PixelReadConverter->GetRGBSize();
330 * \brief Get the size of the image data.
331 * If the image could be converted to RGB using a LUT,
332 * this transformation is not taken into account by GetImageDataRawSize
333 * (use GetImageDataSize if you wish)
334 * @return The raw image size
336 size_t FileHelper::GetImageDataRawSize()
338 if ( PixelWriteConverter->GetUserData() )
340 return PixelWriteConverter->GetUserDataSize();
342 return PixelReadConverter->GetRawSize();
346 * \brief brings pixels into memory :
347 * - Allocates necessary memory,
348 * - Reads the pixels from disk (uncompress if necessary),
349 * - Transforms YBR pixels, if any, into RGB pixels,
350 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
351 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
352 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
353 * @return Pointer to newly allocated pixel data.
354 * (uint8_t is just for prototyping. feel free to cast)
355 * NULL if alloc fails
357 uint8_t *FileHelper::GetImageData()
359 if ( PixelWriteConverter->GetUserData() )
361 return PixelWriteConverter->GetUserData();
366 // If the decompression failed nothing can be done.
370 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
372 return PixelReadConverter->GetRGB();
376 // When no LUT or LUT conversion fails, return the Raw
377 return PixelReadConverter->GetRaw();
382 * \brief brings pixels into memory :
383 * - Allocates necessary memory,
384 * - Transforms YBR pixels (if any) into RGB pixels
385 * - Transforms 3 planes R, G, B (if any) into a single RGB Plane
386 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
387 * - DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
388 * @return Pointer to newly allocated pixel data.
389 * (uint8_t is just for prototyping. feel free to cast)
390 * NULL if alloc fails
392 uint8_t *FileHelper::GetImageDataRaw ()
397 //#ifndef GDCM_LEGACY_REMOVE
399 * \brief Useless function, since PixelReadConverter forces us
400 * copy the Pixels anyway.
401 * Reads the pixels from disk (uncompress if necessary),
402 * Transforms YBR pixels, if any, into RGB pixels
403 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
404 * Transforms single Grey plane + 3 Palettes into a RGB Plane
405 * Copies at most MaxSize bytes of pixel data to caller allocated
407 * \warning This function allows people that want to build a volume
408 * from an image stack *not to* have, first to get the image pixels,
409 * and then move them to the volume area.
410 * It's absolutely useless for any VTK user since vtk chooses
411 * to invert the lines of an image, that is the last line comes first
412 * (for some axis related reasons?). Hence he will have
413 * to load the image line by line, starting from the end.
414 * VTK users have to call GetImageData
416 * @param destination Address (in caller's memory space) at which the
417 * pixel data should be copied
418 * @param maxSize Maximum number of bytes to be copied. When MaxSize
419 * is not sufficient to hold the pixel data the copy is not
420 * executed (i.e. no partial copy).
421 * @return On success, the number of bytes actually copied. Zero on
422 * failure e.g. MaxSize is lower than necessary.
425 size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
429 // If the decompression failed nothing can be done.
433 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
435 if ( PixelReadConverter->GetRGBSize() > maxSize )
437 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
441 (void*)PixelReadConverter->GetRGB(),
442 PixelReadConverter->GetRGBSize() );
443 return PixelReadConverter->GetRGBSize();
446 // Either no LUT conversion necessary or LUT conversion failed
447 if ( PixelReadConverter->GetRawSize() > maxSize )
449 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
453 (void *)PixelReadConverter->GetRaw(),
454 PixelReadConverter->GetRawSize() );
455 return PixelReadConverter->GetRawSize();
461 * \brief Points the internal pointer to the callers inData
462 * image representation, BUT WITHOUT COPYING THE DATA.
463 * 'image' Pixels are presented as C-like 2D arrays : line per line.
464 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
465 * \warning Since the pixels are not copied, it is the caller's responsability
466 * not to deallocate its data before gdcm uses them (e.g. with
467 * the Write() method )
468 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
469 * user is allowed to pass any kind of pixelsn since the size is
471 * @param expectedSize total image size, *in Bytes*
473 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
475 PixelWriteConverter->SetUserData(inData, expectedSize);
479 * \brief Set the image data defined by the user
480 * \warning When writting the file, this data are get as default data to write
481 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
482 * user is allowed to pass any kind of pixels since the size is
484 * @param expectedSize total image size, *in Bytes*
486 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
488 PixelWriteConverter->SetUserData(inData, expectedSize);
492 * \brief Get the image data defined by the user
493 * \warning When writting the file, this data are get as default data to write
495 uint8_t *FileHelper::GetUserData()
497 return PixelWriteConverter->GetUserData();
501 * \brief Get the image data size defined by the user
502 * \warning When writting the file, this data are get as default data to write
504 size_t FileHelper::GetUserDataSize()
506 return PixelWriteConverter->GetUserDataSize();
510 * \brief Get the image data from the file.
511 * If a LUT is found, the data are expanded to be RGB
513 uint8_t *FileHelper::GetRGBData()
515 return PixelReadConverter->GetRGB();
519 * \brief Get the image data size from the file.
520 * If a LUT is found, the data are expanded to be RGB
522 size_t FileHelper::GetRGBDataSize()
524 return PixelReadConverter->GetRGBSize();
528 * \brief Get the image data from the file.
529 * Even when a LUT is found, the data are not expanded to RGB!
531 uint8_t *FileHelper::GetRawData()
533 return PixelReadConverter->GetRaw();
537 * \brief Get the image data size from the file.
538 * Even when a LUT is found, the data are not expanded to RGB!
540 size_t FileHelper::GetRawDataSize()
542 return PixelReadConverter->GetRawSize();
546 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT
548 uint8_t* FileHelper::GetLutRGBA()
550 if ( PixelReadConverter->GetLutRGBA() ==0 )
551 PixelReadConverter->BuildLUTRGBA();
552 return PixelReadConverter->GetLutRGBA();
556 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Number
558 int FileHelper::GetLutItemNumber()
560 return PixelReadConverter->GetLutItemNumber();
564 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Size
566 int FileHelper::GetLutItemSize()
568 return PixelReadConverter->GetLutItemSize();
572 * \brief Writes on disk A SINGLE Dicom file
573 * NO test is performed on processor "Endiannity".
574 * It's up to the user to call his Reader properly
575 * @param fileName name of the file to be created
576 * (any already existing file is over written)
577 * @return false if write fails
579 bool FileHelper::WriteRawData(std::string const &fileName)
581 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
584 gdcmWarningMacro( "Fail to open (write) file:" << fileName.c_str());
588 if ( PixelWriteConverter->GetUserData() )
590 fp1.write( (char *)PixelWriteConverter->GetUserData(),
591 PixelWriteConverter->GetUserDataSize() );
593 else if ( PixelReadConverter->GetRGB() )
595 fp1.write( (char *)PixelReadConverter->GetRGB(),
596 PixelReadConverter->GetRGBSize());
598 else if ( PixelReadConverter->GetRaw() )
600 fp1.write( (char *)PixelReadConverter->GetRaw(),
601 PixelReadConverter->GetRawSize());
605 gdcmErrorMacro( "Nothing written." );
614 * \brief Writes on disk A SINGLE Dicom file,
615 * using the Implicit Value Representation convention
616 * NO test is performed on processor "Endianity".
617 * @param fileName name of the file to be created
618 * (any already existing file is overwritten)
619 * @return false if write fails
622 bool FileHelper::WriteDcmImplVR (std::string const &fileName)
624 SetWriteTypeToDcmImplVR();
625 return Write(fileName);
629 * \brief Writes on disk A SINGLE Dicom file,
630 * using the Explicit Value Representation convention
631 * NO test is performed on processor "Endiannity".
632 * @param fileName name of the file to be created
633 * (any already existing file is overwritten)
634 * @return false if write fails
637 bool FileHelper::WriteDcmExplVR (std::string const &fileName)
639 SetWriteTypeToDcmExplVR();
640 return Write(fileName);
644 * \brief Writes on disk A SINGLE Dicom file,
645 * using the ACR-NEMA convention
646 * NO test is performed on processor "Endiannity".
647 * (a l'attention des logiciels cliniques
648 * qui ne prennent en entrée QUE des images ACR ...
649 * \warning if a DICOM_V3 header is supplied,
650 * groups < 0x0008 and shadow groups are ignored
651 * \warning NO TEST is performed on processor "Endiannity".
652 * @param fileName name of the file to be created
653 * (any already existing file is overwritten)
654 * @return false if write fails
657 bool FileHelper::WriteAcr (std::string const &fileName)
660 return Write(fileName);
664 * \brief Writes on disk A SINGLE Dicom file,
665 * @param fileName name of the file to be created
666 * (any already existing file is overwritten)
667 * @return false if write fails
669 bool FileHelper::Write(std::string const &fileName)
672 CheckMandatoryElements(); //called once, here !
679 SetWriteFileTypeToImplicitVR();
682 case Unknown: // should never happen; ExplicitVR is the default value
685 // User should ask gdcm to write an image in Explicit VR mode
686 // only when he is sure *all* the VR of *all* the DataElements is known.
687 // i.e : when there are *only* Public Groups
688 // or *all* the Shadow Groups are fully described in the relevant Shadow
690 // Let's just *dream* about it; *never* trust a user !
691 // We turn to Implicit VR if at least the VR of one element is unknown.
694 e = FileInternal->GetFirstEntry();
697 if (e->GetVR() == " ")
699 SetWriteTypeToDcmImplVR();
700 SetWriteFileTypeToImplicitVR();
704 e = FileInternal->GetNextEntry();
709 SetWriteFileTypeToExplicitVR();
713 SetWriteFileTypeToExplicitVR(); // to see JPRx
717 // NOTHING is done here just for LibIDO.
718 // Just to avoid further trouble if user creates a file ex-nihilo,
719 // wants to write it as an ACR-NEMA file,
720 // and forgets to create any Entry belonging to group 0008
722 // We add Recognition Code (RET)
723 if ( ! FileInternal->GetDataEntry(0x0008, 0x0010) )
724 FileInternal->InsertEntryString("ACR-NEMA V1.0 ",
725 0x0008, 0x0010, "LO");
726 SetWriteFileTypeToACR();
727 // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
730 /// \todo FIXME : JPEG may be either ExplicitVR or ImplicitVR
732 SetWriteFileTypeToJPEG();
736 SetWriteFileTypeToJPEG2000();
740 // --------------------------------------------------------------
741 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
743 // if recognition code tells us we dealt with a LibIDO image
744 // we reproduce on disk the switch between lineNumber and columnNumber
745 // just before writting ...
746 /// \todo the best trick would be *change* the recognition code
747 /// but pb expected if user deals with, e.g. COMPLEX images
749 if ( WriteType == ACR_LIBIDO )
755 SetWriteToNoLibido();
757 // ----------------- End of Special Patch ----------------
762 SetWriteToRaw(); // modifies and pushes to the archive, when necessary
765 SetWriteToRGB(); // modifies and pushes to the archive, when necessary
769 bool check = CheckWriteIntegrity(); // verifies length
770 if (WriteType == JPEG || WriteType == JPEG2000) check = true;
773 check = FileInternal->Write(fileName,WriteType);
777 // RestoreWriteFileType();
778 // RestoreWriteMandatory();
781 // --------------------------------------------------------------
782 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
784 // ...and we restore the header to be Dicom Compliant again
785 // just after writting
786 RestoreWriteOfLibido();
787 // ----------------- End of Special Patch ----------------
792 //-----------------------------------------------------------------------------
795 * \brief Checks the write integrity
797 * The tests made are :
798 * - verify the size of the image to write with the possible write
799 * when the user set an image data
800 * @return true if check is successfull
802 bool FileHelper::CheckWriteIntegrity()
804 if ( PixelWriteConverter->GetUserData() )
806 int numberBitsAllocated = FileInternal->GetBitsAllocated();
807 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
809 gdcmWarningMacro( "numberBitsAllocated changed from "
810 << numberBitsAllocated << " to 16 "
811 << " for consistency purpose" );
812 numberBitsAllocated = 16;
815 size_t decSize = FileInternal->GetXSize()
816 * FileInternal->GetYSize()
817 * FileInternal->GetZSize()
818 * FileInternal->GetTSize()
819 * FileInternal->GetSamplesPerPixel()
820 * ( numberBitsAllocated / 8 );
821 size_t rgbSize = decSize;
822 if ( FileInternal->HasLUT() )
823 rgbSize = decSize * 3;
828 if ( decSize!=PixelWriteConverter->GetUserDataSize() )
830 gdcmWarningMacro( "Data size (Raw) is incorrect. Should be "
831 << decSize << " / Found :"
832 << PixelWriteConverter->GetUserDataSize() );
837 if ( rgbSize!=PixelWriteConverter->GetUserDataSize() )
839 gdcmWarningMacro( "Data size (RGB) is incorrect. Should be "
840 << decSize << " / Found "
841 << PixelWriteConverter->GetUserDataSize() );
852 * \brief Updates the File to write RAW data (as opposed to RGB data)
853 * (modifies, when necessary, photochromatic interpretation,
854 * bits allocated, Pixels element VR)
855 * WARNING : if SetPhotometricInterpretationToMonochrome1() was called before
856 * Pixel Elements if modified :-(
858 void FileHelper::SetWriteToRaw()
860 std::cout << "entry in FileHelper::SetWriteToRaw " << std::endl;
862 if ( FileInternal->GetNumberOfScalarComponents() == 3
863 && !FileInternal->HasLUT() )
869 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
870 if (FileInternal->HasLUT() )
872 photInt->SetString("PALETTE COLOR ");
876 if (GetPhotometricInterpretation() == 2)
877 photInt->SetString("MONOCHROME2 "); // 0 = Black
879 photInt->SetString("MONOCHROME1 "); // 0 = White !
882 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
883 PixelReadConverter->GetRawSize());
885 std::string vr = "OB";
886 if ( FileInternal->GetBitsAllocated()>8 )
888 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
890 // For non RAW data. Mainly JPEG
891 if( WriteType == JPEG || WriteType == JPEG2000)
897 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
898 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
899 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
900 pixel->SetLength(PixelWriteConverter->GetDataSize());
903 /// \TODO : fixme : I'm not too much happy with this feature :
904 /// It modifies the Pixel Data
905 /// If user calls twice the writer, images will not be equal !!!
906 if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
908 ConvertFixGreyLevels(PixelWriteConverter->GetData(),
909 PixelWriteConverter->GetDataSize());
912 Archive->Push(photInt);
913 Archive->Push(pixel);
921 * \brief Updates the File to write RGB data (as opposed to RAW data)
922 * (modifies, when necessary, photochromatic interpretation,
923 * samples per pixel, Planar configuration,
924 * bits allocated, bits stored, high bit -ACR 24 bits-
925 * Pixels element VR, pushes out the LUT, )
927 void FileHelper::SetWriteToRGB()
929 if ( FileInternal->GetNumberOfScalarComponents()==3 )
931 PixelReadConverter->BuildRGBImage();
933 DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
934 spp->SetString("3 ");
936 DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
937 planConfig->SetString("0 ");
939 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
940 photInt->SetString("RGB ");
942 if ( PixelReadConverter->GetRGB() )
944 PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
945 PixelReadConverter->GetRGBSize());
949 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
950 PixelReadConverter->GetRawSize());
953 std::string vr = "OB";
954 if ( FileInternal->GetBitsAllocated()>8 )
956 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
959 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
960 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
961 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
962 pixel->SetLength(PixelWriteConverter->GetDataSize());
965 Archive->Push(planConfig);
966 Archive->Push(photInt);
967 Archive->Push(pixel);
970 planConfig->Delete();
975 Archive->Push(0x0028,0x1101);
976 Archive->Push(0x0028,0x1102);
977 Archive->Push(0x0028,0x1103);
978 Archive->Push(0x0028,0x1201);
979 Archive->Push(0x0028,0x1202);
980 Archive->Push(0x0028,0x1203);
982 // push out Palette Color Lookup Table UID, if any
983 Archive->Push(0x0028,0x1199);
985 // For old '24 Bits' ACR-NEMA
986 // Thus, we have a RGB image and the bits allocated = 24 and
987 // samples per pixels = 1 (in the read file)
988 if ( FileInternal->GetBitsAllocated()==24 )
990 DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
991 bitsAlloc->SetString("8 ");
993 DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
994 bitsStored->SetString("8 ");
996 DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
997 highBit->SetString("7 ");
999 Archive->Push(bitsAlloc);
1000 Archive->Push(bitsStored);
1001 Archive->Push(highBit);
1003 bitsAlloc->Delete();
1004 bitsStored->Delete();
1015 * \brief Restore the File write mode
1017 void FileHelper::RestoreWrite()
1020 Archive->Restore(0x0028,0x0002);
1021 Archive->Restore(0x0028,0x0004);
1023 Archive->Restore(0x0028,0x0006);
1024 Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1026 // For old ACR-NEMA (24 bits problem)
1027 Archive->Restore(0x0028,0x0100);
1028 Archive->Restore(0x0028,0x0101);
1029 Archive->Restore(0x0028,0x0102);
1032 Archive->Restore(0x0028,0x1101);
1033 Archive->Restore(0x0028,0x1102);
1034 Archive->Restore(0x0028,0x1103);
1035 Archive->Restore(0x0028,0x1201);
1036 Archive->Restore(0x0028,0x1202);
1037 Archive->Restore(0x0028,0x1203);
1039 // For the Palette Color Lookup Table UID
1040 Archive->Restore(0x0028,0x1203);
1042 // group 0002 may be pushed out for ACR-NEMA writting purposes
1043 Archive->Restore(0x0002,0x0000);
1044 Archive->Restore(0x0002,0x0001);
1045 Archive->Restore(0x0002,0x0002);
1046 Archive->Restore(0x0002,0x0003);
1047 Archive->Restore(0x0002,0x0010);
1048 Archive->Restore(0x0002,0x0012);
1049 Archive->Restore(0x0002,0x0013);
1050 Archive->Restore(0x0002,0x0016);
1051 Archive->Restore(0x0002,0x0100);
1052 Archive->Restore(0x0002,0x0102);
1057 * \brief Pushes out the whole group 0002
1058 * FIXME : better, set a flag to tell the writer not to write it ...
1059 * FIXME : method should probably have an other name !
1060 * SetWriteFileTypeToACR is NOT opposed to
1061 * SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1063 void FileHelper::SetWriteFileTypeToACR()
1065 Archive->Push(0x0002,0x0000);
1066 Archive->Push(0x0002,0x0001);
1067 Archive->Push(0x0002,0x0002);
1068 Archive->Push(0x0002,0x0003);
1069 Archive->Push(0x0002,0x0010);
1070 Archive->Push(0x0002,0x0012);
1071 Archive->Push(0x0002,0x0013);
1072 Archive->Push(0x0002,0x0016);
1073 Archive->Push(0x0002,0x0100);
1074 Archive->Push(0x0002,0x0102);
1078 * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1080 void FileHelper::SetWriteFileTypeToJPEG2000()
1082 std::string ts = Util::DicomString(
1083 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1085 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1093 * \brief Sets in the File the TransferSyntax to 'JPEG'
1095 void FileHelper::SetWriteFileTypeToJPEG()
1097 std::string ts = Util::DicomString(
1098 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGBaselineProcess1) );
1100 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1108 * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"
1110 void FileHelper::SetWriteFileTypeToExplicitVR()
1112 std::string ts = Util::DicomString(
1113 Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1115 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1122 * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"
1124 void FileHelper::SetWriteFileTypeToImplicitVR()
1126 std::string ts = Util::DicomString(
1127 Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1129 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1137 * \brief Restore in the File the initial group 0002
1139 void FileHelper::RestoreWriteFileType()
1144 * \brief Set the Write not to Libido format
1146 void FileHelper::SetWriteToLibido()
1148 DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1149 DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1151 if ( oldRow && oldCol )
1153 std::string rows, columns;
1155 //DataEntry *newRow=DataEntry::New(oldRow->GetDictEntry());
1156 //DataEntry *newCol=DataEntry::New(oldCol->GetDictEntry());
1158 DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1159 DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1161 newRow->Copy(oldCol);
1162 newCol->Copy(oldRow);
1164 newRow->SetString(oldCol->GetString());
1165 newCol->SetString(oldRow->GetString());
1167 Archive->Push(newRow);
1168 Archive->Push(newCol);
1174 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1175 libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1176 Archive->Push(libidoCode);
1177 libidoCode->Delete();
1181 * \brief Set the Write not to No Libido format
1183 void FileHelper::SetWriteToNoLibido()
1185 DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1188 if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1190 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1191 libidoCode->SetString("");
1192 Archive->Push(libidoCode);
1193 libidoCode->Delete();
1199 * \brief Restore the Write format
1201 void FileHelper::RestoreWriteOfLibido()
1203 Archive->Restore(0x0028,0x0010);
1204 Archive->Restore(0x0028,0x0011);
1205 Archive->Restore(0x0008,0x0010);
1207 // Restore 'LibIDO-special' entries, if any
1208 Archive->Restore(0x0028,0x0015);
1209 Archive->Restore(0x0028,0x0016);
1210 Archive->Restore(0x0028,0x0017);
1211 Archive->Restore(0x0028,0x00199);
1215 * \brief Duplicates a DataEntry or creates it.
1216 * @param group Group number of the Entry
1217 * @param elem Element number of the Entry
1218 * @param vr Value Representation of the Entry
1219 * \return pointer to the new Bin Entry (NULL when creation failed).
1221 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1224 DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1227 if ( oldE && vr != GDCM_VRUNKNOWN )
1228 if ( oldE->GetVR() != vr )
1233 //newE = DataEntry::New(oldE->GetDictEntry());
1234 newE = DataEntry::New(group, elem, vr);
1239 newE = GetFile()->NewDataEntry(group, elem, vr);
1246 * \brief This method is called automatically, just before writting
1247 * in order to produce a 'True Dicom V3' image.
1249 * We cannot know *how* the user made the File :
1250 * (reading an old ACR-NEMA file or a not very clean DICOM file ...)
1251 * Just before writting :
1252 * - we check the Entries
1253 * - we create the mandatory entries if they are missing
1254 * - we modify the values if necessary
1255 * - we push the sensitive entries to the Archive
1256 * The writing process will restore the entries as they where before
1257 * entering FileHelper::CheckMandatoryElements, so the user will always
1258 * see the entries just as they were before he decided to write.
1261 * - Entries whose type is 1 are mandatory, with a mandatory value
1262 * - Entries whose type is 1c are mandatory-inside-a-Sequence,
1263 * with a mandatory value
1264 * - Entries whose type is 2 are mandatory, with an optional value
1265 * - Entries whose type is 2c are mandatory-inside-a-Sequence,
1266 * with an optional value
1267 * - Entries whose type is 3 are optional
1270 * - warn the user if we had to add some entries :
1271 * even if a mandatory entry is missing, we add it, with a default value
1272 * (we don't want to give up the writting process if user forgot to
1273 * specify Lena's Patient ID, for instance ...)
1274 * - read the whole PS 3.3 Part of DICOM (890 pages)
1275 * and write a *full* checker (probably one method per Modality ...)
1276 * Any contribution is welcome.
1277 * - write a user callable full checker, to allow post reading
1278 * and/or pre writting image consistency check.
1281 /* -------------------------------------------------------------------------------------
1282 To be moved to User's guide / WIKI ?
1284 We have to deal with 4 *very* different cases :
1285 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1287 -2) user modified the pixels of an existing image.
1289 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1291 -4) user modified/added some tags *without processing* the pixels (anonymization..
1292 UNMODIFIED_PIXELS_IMAGE
1293 -Probabely some more to be added
1295 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1298 0008 0012 Instance Creation Date
1299 0008 0013 Instance Creation Time
1300 0008 0018 SOP Instance UID
1301 are *always* created with the current values; user has *no* possible intervention on
1304 'Serie Instance UID'(0x0020,0x000e)
1305 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1306 created if it doesn't.
1307 The user is allowed to create his own Series/Studies,
1308 keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1310 The user shouldn't add any image to a 'Manufacturer Serie'
1311 but there is no way no to allow him to do that
1313 None of the 'shadow elements' are droped out.
1317 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1320 'Media Storage SOP Class UID' (0x0002,0x0002)
1321 'SOP Class UID' (0x0008,0x0016) are set to
1322 [Secondary Capture Image Storage]
1323 'Image Type' (0x0008,0x0008) is forced to "DERIVED\PRIMARY"
1324 Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1327 If 'SOP Class UID' exists in the native image ('true DICOM' image)
1328 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1329 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1330 whose value is the original 'SOP Class UID'
1331 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1332 whose value is the original 'SOP Class UID'
1334 3) TODO : find a trick to allow user to pass to the writter the list of the Dicom images
1335 or the Series, (or the Study ?) he used to created his image
1336 (MIP, MPR, cartography image, ...)
1337 These info should be stored (?)
1338 0008 1110 SQ 1 Referenced Study Sequence
1339 0008 1115 SQ 1 Referenced Series Sequence
1340 0008 1140 SQ 1 Referenced Image Sequence
1342 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1343 'Media Storage SOP Class UID' (0x0002,0x0002)
1344 'SOP Class UID' (0x0008,0x0016)
1345 'Image Type' (0x0008,0x0008)
1346 'Conversion Type' (0x0008,0x0064)
1349 Bellow follows the full description (hope so !) of the consistency checks performed
1350 by gdcm::FileHelper::CheckMandatoryElements()
1353 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1354 -->'SOP Class UID' (0x0008,0x0016) are defaulted to
1355 [Secondary Capture Image Storage]
1356 --> 'Image Type' (0x0008,0x0008)
1357 is forced to "DERIVED\PRIMARY"
1358 (The written image is no longer an 'ORIGINAL' one)
1359 Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1361 --> Conversion Type (0x0008,0x0064)
1362 is defaulted to 'SYN' (Synthetic Image)
1363 when *he* knows he created his own image ex nihilo
1365 --> 'Modality' (0x0008,0x0060)
1366 is defaulted to "OT" (other) if missing.
1367 (a fully user created image belongs to *no* modality)
1369 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1370 --> 'Implementation Class UID' (0x0002,0x0012)
1371 are automatically generated; no user intervention possible
1373 --> 'Serie Instance UID'(0x0020,0x000e)
1374 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1375 created if it doesn't.
1376 The user is allowed to create his own Series/Studies,
1377 keeping the same 'Serie Instance UID' / 'Study Instance UID'
1380 The user shouldn't add any image to a 'Manufacturer Serie'
1381 but there is no way no to allowed him to do that
1383 --> If 'SOP Class UID' exists in the native image ('true DICOM' image)
1384 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1386 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1387 whose value is the original 'SOP Class UID'
1388 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1389 whose value is the original 'SOP Class UID'
1391 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1392 --> Pixel Spacing (0x0028,0x0030) is defaulted to "1.0\1.0"
1393 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1395 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1397 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1399 --> Study Date, Study Time are defaulted to current Date and Time
1400 (they remain unchanged if they exist)
1402 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from
1403 Image Orientation (Patient) : (0020|0037) or from
1404 Image Orientation (RET) : (0020 0035)
1406 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1407 are created, with empty value if there are missing.
1409 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1410 are defaulted with a 'gdcm' value.
1412 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1413 --> Referring Physician's Name (Type 2)
1414 are created, with empty value if there are missing.
1416 -------------------------------------------------------------------------------------*/
1418 void FileHelper::CheckMandatoryElements()
1420 std::string sop = Util::CreateUniqueUID();
1422 // --------------------- For Meta Elements ---------------------
1423 // just to remember : 'official' 0002 group
1424 if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1426 // Group 000002 (Meta Elements) already pushed out
1428 //0002 0000 UL 1 Meta Group Length
1429 //0002 0001 OB 1 File Meta Information Version
1430 //0002 0002 UI 1 Media Stored SOP Class UID
1431 //0002 0003 UI 1 Media Stored SOP Instance UID
1432 //0002 0010 UI 1 Transfer Syntax UID
1433 //0002 0012 UI 1 Implementation Class UID
1434 //0002 0013 SH 1 Implementation Version Name
1435 //0002 0016 AE 1 Source Application Entity Title
1436 //0002 0100 UI 1 Private Information Creator
1437 //0002 0102 OB 1 Private Information
1439 // Push out 'ACR-NEMA-special' entries, if any
1440 Archive->Push(0x0008,0x0001); // Length to End
1441 Archive->Push(0x0008,0x0010); // Recognition Code
1442 Archive->Push(0x0028,0x0005); // Image Dimension
1444 // Create them if not found
1445 // Always modify the value
1446 // Push the entries to the archive.
1447 CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1449 DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1450 e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1452 e_0002_0001->SetLength(2);
1453 Archive->Push(e_0002_0001);
1454 e_0002_0001->Delete();
1456 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1458 // we keep the original 'Media Storage SOP Class UID', we default it if missing
1459 CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1463 // It's *not* an image comming straight from a source. We force
1464 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1465 CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1468 // 'Media Storage SOP Instance UID'
1469 CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1471 // 'Implementation Class UID'
1472 // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1473 // seems to be Root UID + 4 digits (?)
1474 CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1476 // 'Implementation Version Name'
1477 std::string version = "GDCM ";
1478 version += Util::GetVersion();
1479 CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1482 // --------------------- For DataSet ---------------------
1484 if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1487 gdcmDebugMacro( "USER_OWN_IMAGE (1)");
1488 // If 'SOP Class UID' exists ('true DICOM' image)
1489 // we create the 'Source Image Sequence' SeqEntry
1490 // to hold informations about the Source Image
1492 DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1495 // Create 'Source Image Sequence' SeqEntry
1496 // SeqEntry *sis = SeqEntry::New (
1497 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x2112) );
1498 SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1499 SQItem *sqi = SQItem::New(1);
1500 // (we assume 'SOP Instance UID' exists too)
1501 // create 'Referenced SOP Class UID'
1502 // DataEntry *e_0008_1150 = DataEntry::New(
1503 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1150) );
1504 DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1505 e_0008_1150->SetString( e_0008_0016->GetString());
1506 sqi->AddEntry(e_0008_1150);
1507 e_0008_1150->Delete();
1509 // create 'Referenced SOP Instance UID'
1510 DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1511 // DataEntry *e_0008_1155 = DataEntry::New(
1512 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1155) );
1513 DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1514 e_0008_1155->SetString( e_0008_0018->GetString());
1515 sqi->AddEntry(e_0008_1155);
1516 e_0008_1155->Delete();
1518 sis->AddSQItem(sqi,1);
1521 // temporarily replaces any previous 'Source Image Sequence'
1525 // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1526 if ( ContentType == FILTERED_IMAGE)
1527 // the user *knows* he just modified the pixels
1528 // the image is no longer an 'Original' one
1529 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1533 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1535 // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1536 CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");
1540 // It's *not* an image comming straight from a source. We force
1541 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1542 CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");
1545 Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1546 // Push out 'LibIDO-special' entries, if any
1547 Archive->Push(0x0028,0x0015);
1548 Archive->Push(0x0028,0x0016);
1549 Archive->Push(0x0028,0x0017);
1550 Archive->Push(0x0028,0x0198); // very old versions
1551 Archive->Push(0x0028,0x0199);
1553 // Replace deprecated 0028 0012 US Planes
1554 // by new 0028 0008 IS Number of Frames
1556 ///\todo : find if there is a rule!
1557 DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1560 CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1561 Archive->Push(0x0028,0x0012);
1564 // Deal with the pb of (Bits Stored = 12)
1565 // - we're gonna write the image as Bits Stored = 16
1566 if ( FileInternal->GetEntryString(0x0028,0x0100) == "12")
1568 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1571 // Check if user wasn't drunk ;-)
1573 std::ostringstream s;
1574 // check 'Bits Allocated' vs decent values
1575 int nbBitsAllocated = FileInternal->GetBitsAllocated();
1576 if ( nbBitsAllocated == 0 || nbBitsAllocated > 32
1577 || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1579 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1580 gdcmWarningMacro("(0028,0100) changed from "
1581 << nbBitsAllocated << " to 16 for consistency purpose");
1582 nbBitsAllocated = 16;
1584 // check 'Bits Stored' vs 'Bits Allocated'
1585 int nbBitsStored = FileInternal->GetBitsStored();
1586 if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1589 s << nbBitsAllocated;
1590 CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1591 gdcmWarningMacro("(0028,0101) changed from "
1592 << nbBitsStored << " to " << nbBitsAllocated
1593 << " for consistency purpose" );
1594 nbBitsStored = nbBitsAllocated;
1596 // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1597 int highBitPosition = FileInternal->GetHighBitPosition();
1598 if ( highBitPosition == 0 ||
1599 highBitPosition > nbBitsAllocated-1 ||
1600 highBitPosition < nbBitsStored-1 )
1603 s << nbBitsStored - 1;
1604 CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1605 gdcmWarningMacro("(0028,0102) changed from "
1606 << highBitPosition << " to " << nbBitsAllocated-1
1607 << " for consistency purpose");
1610 // check Pixel Representation (default it as 0 -unsigned-)
1612 DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1615 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1616 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1620 int sign = (int)e_0028_0103->GetValue(0);
1621 if (sign !=1 && sign !=0)
1623 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1624 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1628 std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1629 if ( pixelSpacing == GDCM_UNFOUND )
1631 pixelSpacing = "1.0\\1.0";
1632 // if missing, Pixel Spacing forced to "1.0\1.0"
1633 CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1636 // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1637 // --> This one is the *legal* one !
1638 if ( ContentType != USER_OWN_IMAGE)
1639 // we write it only when we are *sure* the image comes from
1640 // an imager (see also 0008,0x0064)
1641 CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1644 ///Exact meaning of RETired fields
1646 // See page 73 of ACR-NEMA_300-1988.pdf !
1648 // 0020,0020 : Patient Orientation :
1649 Patient direction of the first row and
1650 column of the images. The first entry id the direction of the raws, given by the
1651 direction of the last pixel in the first row from the first pixel in tha row.
1652 the second entry is the direction of the columns, given by the direction of the
1653 last pixel in the first column from the first pixel in that column.
1654 L : Left, F : Feet, A : Anterior, P : Posterior.
1655 Up to 3 letters can be used in combination to indicate oblique planes.
1657 //0020,0030 Image Position (RET)
1658 x,y,z coordinates im mm of the first pixel in the image
1660 // 0020,0035 Image Orientation (RET)
1661 Direction cosines of the R axis of the image system with respect to the
1662 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1663 the image system with respect to the same axes
1665 //0020,0050 Location
1666 An image location reference, standard for the modality (such as CT bed
1667 position), used to indicate position. Calculation of position for other purposes
1668 is only from (0020,0030) and (0020,0035)
1672 // if imagePositionPatient not found, default it with imagePositionRet, if any
1673 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1675 std::string imagePositionRet = FileInternal->GetEntryString(0x0020,0x0030);
1676 std::string imageOrientationRet = FileInternal->GetEntryString(0x0020,0x0035);
1677 std::string imagePositionPatient = FileInternal->GetEntryString(0x0020,0x0032);
1678 std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1680 if( imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1681 && imagePositionRet != GDCM_UNFOUND && imageOrientationRet != GDCM_UNFOUND)
1683 CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1684 Archive->Push(0x0020,0x0030);
1685 CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1686 Archive->Push(0x0020,0x0035);
1690 // Samples Per Pixel (type 1) : default to grayscale
1691 CheckMandatoryEntry(0x0028,0x0002,"1","US");
1693 // --- Check UID-related Entries ---
1695 // At the end, not to overwrite the original ones,
1696 // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
1697 // 'SOP Instance UID'
1698 CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1700 if ( ContentType == USER_OWN_IMAGE)
1702 gdcmDebugMacro( "USER_OWN_IMAGE (2)");
1704 // Other possible values are :
1705 // See PS 3.3, Page 408
1707 // DV = Digitized Video
1708 // DI = Digital Interface
1709 // DF = Digitized Film
1710 // WSD = Workstation
1711 // SD = Scanned Document
1712 // SI = Scanned Image
1714 // SYN = Synthetic Image
1716 CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1719 if ( ContentType == CREATED_IMAGE)
1721 /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1726 // ---- The user will never have to take any action on the following ----
1728 // new value for 'SOP Instance UID'
1729 //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1731 // Instance Creation Date
1732 const std::string &date = Util::GetCurrentDate();
1733 CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1735 // Instance Creation Time
1736 const std::string &time = Util::GetCurrentTime();
1737 CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1740 CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1742 CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1745 //CopyMandatoryEntry(0x0008,0x0050,"");
1746 CheckMandatoryEntry(0x0008,0x0050,"","SH");
1749 // ----- Add Mandatory Entries if missing ---
1750 // Entries whose type is 1 are mandatory, with a mandatory value
1751 // Entries whose type is 1c are mandatory-inside-a-Sequence,
1752 // with a mandatory value
1753 // Entries whose type is 2 are mandatory, with an optional value
1754 // Entries whose type is 2c are mandatory-inside-a-Sequence,
1755 // with an optional value
1756 // Entries whose type is 3 are optional
1758 // 'Study Instance UID'
1759 // Keep the value if exists
1760 // The user is allowed to create his own Study,
1761 // keeping the same 'Study Instance UID' for various images
1762 // The user may add images to a 'Manufacturer Study',
1763 // adding new Series to an already existing Study
1764 CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1766 // 'Serie Instance UID'
1767 // Keep the value if exists
1768 // The user is allowed to create his own Series,
1769 // keeping the same 'Serie Instance UID' for various images
1770 // The user shouldn't add any image to a 'Manufacturer Serie'
1771 // but there is no way no to prevent him for doing that
1772 CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1775 CheckMandatoryEntry(0x0020,0x0010,"","SH");
1778 CheckMandatoryEntry(0x0020,0x0011,"","IS");
1781 CheckMandatoryEntry(0x0020,0x0013,"","IS");
1783 // Patient Orientation
1784 // Can be computed from (0020|0037) : Image Orientation (Patient)
1785 GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1786 std::string ori = o->GetOrientation ( FileInternal );
1788 if (ori != "\\" && ori != GDCM_UNFOUND)
1789 CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1791 CheckMandatoryEntry(0x0020,0x0020,"","CS");
1793 // Default Patient Position to HFS
1794 CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1796 // Modality : if missing we set it to 'OTher'
1797 CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1799 // Manufacturer : if missing we set it to 'GDCM Factory'
1800 CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1802 // Institution Name : if missing we set it to 'GDCM Hospital'
1803 CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1805 // Patient's Name : if missing, we set it to 'GDCM^Patient'
1806 CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1808 // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1809 CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1811 // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1812 CheckMandatoryEntry(0x0010,0x0030,"","DA");
1814 // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1815 CheckMandatoryEntry(0x0010,0x0040,"","CS");
1817 // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1818 CheckMandatoryEntry(0x0008,0x0090,"","PN");
1821 // Deal with element 0x0000 (group length) of each group.
1822 // First stage : get all the different Groups
1825 DocEntry *d = FileInternal->GetFirstEntry();
1828 grHT[d->GetGroup()] = 0;
1829 d=FileInternal->GetNextEntry();
1831 // Second stage : add the missing ones (if any)
1832 for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)
1834 CheckMandatoryEntry(it->first, 0x0000, "0");
1836 // Third stage : update all 'zero level' groups length
1840 if (PhotometricInterpretation == 1)
1845 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1847 DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1850 //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1851 entry = DataEntry::New(group,elem,vr);
1852 entry->SetString(value);
1853 Archive->Push(entry);
1858 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
1859 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1861 //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1862 DataEntry *entry = DataEntry::New(group,elem,vr);
1863 entry->SetString(value);
1864 Archive->Push(entry);
1868 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1870 DataEntry *entry = CopyDataEntry(group,elem,vr);
1871 entry->SetString(value);
1872 Archive->Push(entry);
1877 * \brief Restore in the File the initial group 0002
1879 void FileHelper::RestoreWriteMandatory()
1881 // group 0002 may be pushed out for ACR-NEMA writting purposes
1882 Archive->Restore(0x0002,0x0000);
1883 Archive->Restore(0x0002,0x0001);
1884 Archive->Restore(0x0002,0x0002);
1885 Archive->Restore(0x0002,0x0003);
1886 Archive->Restore(0x0002,0x0010);
1887 Archive->Restore(0x0002,0x0012);
1888 Archive->Restore(0x0002,0x0013);
1889 Archive->Restore(0x0002,0x0016);
1890 Archive->Restore(0x0002,0x0100);
1891 Archive->Restore(0x0002,0x0102);
1893 // FIXME : Check if none is missing !
1895 Archive->Restore(0x0008,0x0012);
1896 Archive->Restore(0x0008,0x0013);
1897 Archive->Restore(0x0008,0x0016);
1898 Archive->Restore(0x0008,0x0018);
1899 Archive->Restore(0x0008,0x0060);
1900 Archive->Restore(0x0008,0x0070);
1901 Archive->Restore(0x0008,0x0080);
1902 Archive->Restore(0x0008,0x0090);
1903 Archive->Restore(0x0008,0x2112);
1905 Archive->Restore(0x0010,0x0010);
1906 Archive->Restore(0x0010,0x0030);
1907 Archive->Restore(0x0010,0x0040);
1909 Archive->Restore(0x0020,0x000d);
1910 Archive->Restore(0x0020,0x000e);
1915 * \brief CallStartMethod
1917 void FileHelper::CallStartMethod()
1921 CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1925 * \brief CallProgressMethod
1927 void FileHelper::CallProgressMethod()
1929 CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1933 * \brief CallEndMethod
1935 void FileHelper::CallEndMethod()
1938 CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1941 //-----------------------------------------------------------------------------
1944 * \brief Factorization for various forms of constructors.
1946 void FileHelper::Initialize()
1949 ContentType = USER_OWN_IMAGE;
1951 WriteMode = WMODE_RAW;
1952 WriteType = ExplicitVR;
1954 PhotometricInterpretation = 2; // Black = 0
1956 PixelReadConverter = new PixelReadConvert;
1957 PixelWriteConverter = new PixelWriteConvert;
1958 Archive = new DocEntryArchive( FileInternal );
1962 * \brief Reads/[decompresses] the pixels,
1963 * *without* making RGB from Palette Colors
1964 * @return the pixels area, whatever its type
1965 * (uint8_t is just for prototyping : feel free to Cast it)
1967 uint8_t *FileHelper::GetRaw()
1969 PixelReadConverter->SetUserFunction( UserFunction );
1971 uint8_t *raw = PixelReadConverter->GetRaw();
1974 // The Raw image migth not be loaded yet:
1975 std::ifstream *fp = FileInternal->OpenFile();
1976 PixelReadConverter->ReadAndDecompressPixelData( fp );
1978 FileInternal->CloseFile();
1980 raw = PixelReadConverter->GetRaw();
1983 gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
1992 * \brief Deal with Grey levels i.e. re-arange them
1993 * to have low values = dark, high values = bright
1995 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
1997 uint32_t i; // to please M$VC6
2000 // Number of Bits Allocated for storing a Pixel is defaulted to 16
2001 // when absent from the file.
2002 int bitsAllocated = FileInternal->GetBitsAllocated();
2003 if ( bitsAllocated == 0 )
2008 else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2012 // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2013 // when absent from the file.
2014 int bitsStored = FileInternal->GetBitsStored();
2015 if ( bitsStored == 0 )
2017 bitsStored = bitsAllocated;
2020 if (!FileInternal->IsSignedPixelData())
2022 if ( bitsAllocated == 8 )
2024 uint8_t *deb = (uint8_t *)raw;
2025 for (i=0; i<rawSize; i++)
2033 if ( bitsAllocated == 16 )
2036 for (j=0; j<bitsStored-1; j++)
2038 mask = (mask << 1) +1; // will be fff when BitsStored=12
2041 uint16_t *deb = (uint16_t *)raw;
2042 for (i=0; i<rawSize/2; i++)
2052 if ( bitsAllocated == 8 )
2054 uint8_t smask8 = 255;
2055 uint8_t *deb = (uint8_t *)raw;
2056 for (i=0; i<rawSize; i++)
2058 *deb = smask8 - *deb;
2063 if ( bitsAllocated == 16 )
2065 uint16_t smask16 = 65535;
2066 uint16_t *deb = (uint16_t *)raw;
2067 for (i=0; i<rawSize/2; i++)
2069 *deb = smask16 - *deb;
2077 //-----------------------------------------------------------------------------
2079 * \brief Prints the common part of DataEntry, SeqEntry
2080 * @param os ostream we want to print in
2081 * @param indent (unused)
2083 void FileHelper::Print(std::ostream &os, std::string const &)
2085 FileInternal->SetPrintLevel(PrintLevel);
2086 FileInternal->Print(os);
2088 if ( FileInternal->IsReadable() )
2090 PixelReadConverter->SetPrintLevel(PrintLevel);
2091 PixelReadConverter->Print(os);
2095 //-----------------------------------------------------------------------------
2096 } // end namespace gdcm
2099 /* Probabely something to be added to use Rescale Slope/Intercept
2100 Have a look ,at ITK code !
2102 // Internal function to rescale pixel according to Rescale Slope/Intercept
2103 template<class TBuffer, class TSource>
2104 void RescaleFunction(TBuffer* buffer, TSource *source,
2105 double slope, double intercept, size_t size)
2107 size /= sizeof(TSource);
2109 if (slope != 1.0 && intercept != 0.0)
2111 // Duff's device. Instead of this code:
2113 // for(unsigned int i=0; i<size; i++)
2115 // buffer[i] = (TBuffer)(source[i]*slope + intercept);
2118 // use Duff's device which exploits "fall through"
2119 register size_t n = (size + 7) / 8;
2122 case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2123 case 7: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2124 case 6: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2125 case 5: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2126 case 4: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2127 case 3: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2128 case 2: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2129 case 1: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2133 else if (slope == 1.0 && intercept != 0.0)
2135 // Duff's device. Instead of this code:
2137 // for(unsigned int i=0; i<size; i++)
2139 // buffer[i] = (TBuffer)(source[i] + intercept);
2142 // use Duff's device which exploits "fall through"
2143 register size_t n = (size + 7) / 8;
2146 case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2147 case 7: *buffer++ = (TBuffer)(*source++ + intercept);
2148 case 6: *buffer++ = (TBuffer)(*source++ + intercept);
2149 case 5: *buffer++ = (TBuffer)(*source++ + intercept);
2150 case 4: *buffer++ = (TBuffer)(*source++ + intercept);
2151 case 3: *buffer++ = (TBuffer)(*source++ + intercept);
2152 case 2: *buffer++ = (TBuffer)(*source++ + intercept);
2153 case 1: *buffer++ = (TBuffer)(*source++ + intercept);
2157 else if (slope != 1.0 && intercept == 0.0)
2159 // Duff's device. Instead of this code:
2161 // for(unsigned int i=0; i<size; i++)
2163 // buffer[i] = (TBuffer)(source[i]*slope);
2166 // use Duff's device which exploits "fall through"
2167 register size_t n = (size + 7) / 8;
2170 case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2171 case 7: *buffer++ = (TBuffer)((*source++)*slope);
2172 case 6: *buffer++ = (TBuffer)((*source++)*slope);
2173 case 5: *buffer++ = (TBuffer)((*source++)*slope);
2174 case 4: *buffer++ = (TBuffer)((*source++)*slope);
2175 case 3: *buffer++ = (TBuffer)((*source++)*slope);
2176 case 2: *buffer++ = (TBuffer)((*source++)*slope);
2177 case 1: *buffer++ = (TBuffer)((*source++)*slope);
2183 // Duff's device. Instead of this code:
2185 // for(unsigned int i=0; i<size; i++)
2187 // buffer[i] = (TBuffer)(source[i]);
2190 // use Duff's device which exploits "fall through"
2191 register size_t n = (size + 7) / 8;
2194 case 0: do { *buffer++ = (TBuffer)(*source++);
2195 case 7: *buffer++ = (TBuffer)(*source++);
2196 case 6: *buffer++ = (TBuffer)(*source++);
2197 case 5: *buffer++ = (TBuffer)(*source++);
2198 case 4: *buffer++ = (TBuffer)(*source++);
2199 case 3: *buffer++ = (TBuffer)(*source++);
2200 case 2: *buffer++ = (TBuffer)(*source++);
2201 case 1: *buffer++ = (TBuffer)(*source++);
2210 template<class TSource>
2211 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2212 void* buffer, TSource *source,
2213 double slope, double intercept, size_t size)
2217 case ImageIOBase::UCHAR:
2218 RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2220 case ImageIOBase::CHAR:
2221 RescaleFunction( (char *)buffer, source, slope, intercept, size);
2223 case ImageIOBase::USHORT:
2224 RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2226 case ImageIOBase::SHORT:
2227 RescaleFunction( (short *)buffer, source, slope, intercept, size);
2229 case ImageIOBase::UINT:
2230 RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2232 case ImageIOBase::INT:
2233 RescaleFunction( (int *)buffer, source, slope, intercept, size);
2235 case ImageIOBase::FLOAT:
2236 RescaleFunction( (float *)buffer, source, slope, intercept, size);
2238 case ImageIOBase::DOUBLE:
2239 RescaleFunction( (double *)buffer, source, slope, intercept, size);
2242 ::itk::OStringStream message;
2243 message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2244 ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);