1 /*=========================================================================
4 Module: $RCSfile: gdcmFileHelper.cxx,v $
7 Date: $Date: 2007/08/24 10:45:18 $
8 Version: $Revision: 1.121 $
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);
67 // user may ask not to convert Palette (if any) to RGB
68 uint8_t *pixels = fh->GetImageDataRaw();
69 int imageLength = fh->GetImageDataRawSize();
71 // He can now use the pixels, create a new image, ...
72 uint8_t *userPixels = ...
74 //To re-write the image, user re-uses the gdcm::FileHelper
75 gdcm::File *fh = new gdcm::FileHelper();
77 fh->SetTypeToRaw(); // Even if it was possible to convert Palette to RGB
80 // If user wants to write the file as MONOCHROME1 (0=white)
81 fh->SetPhotometricInterpretationToMonochrome1();
83 fh->SetWriteTypeToDcmExpl(); // he wants Explicit Value Representation
84 // Little Endian is the default,
85 // bigendian not supported for writting
86 (-->SetWriteType(ExplicitVR);)
87 -->WriteType = ExplicitVR;
88 fh->SetWriteTypeToJPEG(); // lossless compression
89 fh->SetWriteTypeToJPEG2000(); // lossless compression
91 fh->SetImageData( userPixels, userPixelsLength);
93 fh->SetUserData( userPixels, userPixelsLength); // this one performs compression, when required
95 fh->Write(newFileName); // overwrites the file, if any
100 // ----------------------------- WARNING -------------------------
102 These lines will be moved to the document-to-be 'Developer's Guide'
104 WriteMode : WMODE_RAW / WMODE_RGB
105 WriteType : ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO
106 PhotometricInterpretation : MONOCHROME2 (0=black), MONOCHROME2 (0=white)
109 fh->SetWriteMode(WMODE_RAW / WMODE_RGB)
111 fh->SetWriteType( ImplicitVR/ExplicitVR/ACR/ACR_LIBIDO/JPEG/JPEG2000)
113 fh->Write(newFileName);
114 CheckMandatoryElements(); // Checks existing ones / Add missing ones
115 Fix VR if unknown elements
116 SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR(); /
117 SetWriteFileTypeToACR() / SetWriteFileTypeToJPEG() / SetWriteFileTypeToJ2K()
118 (Modifies TransferSyntax if any; Pushes to the Archives old one)
119 SetWriteToRaw(); / SetWriteToRGB();
120 (Modifies and pushes to the Archive, when necessary : photochr. interp.,
121 samples per pixel, Planar configuration,
122 bits allocated, bits stored, high bit -ACR 24 bits-
123 Pixels element VR, pushes out the LUT )
125 Sets Photometric Interpretation
126 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
127 Sets VR, BinArea, Length for PixelData
130 Archive->Push(photInt);
131 Archive->Push(pixel);
135 if NumberOfScalarComponents==1
136 SetWriteToRaw(); return;
137 PixelReadConverter->BuildRGBImage()
138 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
139 Archives spp, planConfig,photInt, pixel
141 CheckWriteIntegrity();
142 (checks user given pixels length)
143 FileInternal->Write(fileName,WriteType)
144 fp = opens file(fileName); // out|binary
145 ComputeGroup0002Length( );
146 Document::WriteContent(fp, writetype);
147 writes Dicom File Preamble not ACR-NEMA
148 ElementSet::WriteContent(fp, writetype);
149 writes recursively all DataElements
151 (moves back to the gdcm::File all the archived elements)
157 namespace GDCM_NAME_SPACE
159 typedef std::map<uint16_t, int> GroupHT; // Hash Table
160 //-------------------------------------------------------------------------
161 // Constructor / Destructor
163 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
164 * file (gdcm::File only deals with the ... header)
165 * Opens (in read only and when possible) an existing file and checks
166 * for DICOM compliance. Returns NULL on failure.
167 * It will be up to the user to load the pixels into memory
168 * ( GetImageDataSize() + GetImageData() methods)
169 * \note the in-memory representation of all available tags found in
170 * the DICOM header is post-poned to first header information access.
171 * This avoid a double parsing of public part of the header when
172 * one sets an a posteriori shadow dictionary (efficiency can be
173 * seen as a side effect).
175 FileHelper::FileHelper( )
177 FileInternal = File::New( );
182 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
183 * file (File only deals with the ... header)
184 * Opens (in read only and when possible) an existing file and checks
185 * for DICOM compliance. Returns NULL on failure.
186 * It will be up to the user to load the pixels into memory
187 * ( GetImageDataSize() + GetImageData() methods)
188 * \note the in-memory representation of all available tags found in
189 * the DICOM header is post-poned to first header information access.
190 * This avoid a double parsing of public part of the header when
191 * user sets an a posteriori shadow dictionary (efficiency can be
192 * seen as a side effect).
193 * @param header already built Header
195 FileHelper::FileHelper(File *header)
197 gdcmAssertMacro(header);
199 FileInternal = header;
200 FileInternal->Register();
202 if ( FileInternal->IsReadable() )
204 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
209 * \brief canonical destructor
210 * \note If the header (gdcm::File) was created by the FileHelper constructor,
211 * it is destroyed by the FileHelper
213 FileHelper::~FileHelper()
215 if ( PixelReadConverter )
217 delete PixelReadConverter;
219 if ( PixelWriteConverter )
221 delete PixelWriteConverter;
228 FileInternal->Unregister();
231 //-----------------------------------------------------------------------------
235 * \brief Sets the LoadMode of the internal gdcm::File as a boolean string.
236 * NO_SEQ, NO_SHADOW, NO_SHADOWSEQ ... (nothing more, right now)
237 * WARNING : before using NO_SHADOW, be sure *all* your files
238 * contain accurate values in the 0x0000 element (if any)
239 * of *each* Shadow Group. The parser will fail if the size is wrong !
240 * @param loadMode Load mode to be used
242 void FileHelper::SetLoadMode(int loadMode)
244 GetFile()->SetLoadMode( loadMode );
247 * \brief Sets the LoadMode of the internal gdcm::File
248 * @param fileName name of the file to be open
250 void FileHelper::SetFileName(std::string const &fileName)
252 FileInternal->SetFileName( fileName );
257 * @return false if file cannot be open or no swap info was found,
258 * or no tag was found.
260 bool FileHelper::Load()
262 if ( !FileInternal->Load() )
265 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
270 * \brief Accesses an existing DataEntry through its (group, element)
271 * and modifies its content with the given value.
272 * @param content new value (string) to substitute with
273 * @param group group number of the Dicom Element to modify
274 * @param elem element number of the Dicom Element to modify
275 * \return false if DataEntry not found
277 bool FileHelper::SetEntryString(std::string const &content,
278 uint16_t group, uint16_t elem)
280 return FileInternal->SetEntryString(content, group, elem);
285 * \brief Accesses an existing DataEntry through its (group, element)
286 * and modifies its content with the given value.
287 * @param content new value (void* -> uint8_t*) to substitute with
288 * @param lgth new value length
289 * @param group group number of the Dicom Element to modify
290 * @param elem element number of the Dicom Element to modify
291 * \return false if DataEntry not found
293 bool FileHelper::SetEntryBinArea(uint8_t *content, int lgth,
294 uint16_t group, uint16_t elem)
296 return FileInternal->SetEntryBinArea(content, lgth, group, elem);
300 * \brief Modifies the value of a given DataEntry when it exists.
301 * Creates it with the given value when unexistant.
302 * @param content (string) value to be set
303 * @param group Group number of the Entry
304 * @param elem Element number of the Entry
305 * @param vr Value Representation of the DataElement to be inserted
306 * \return pointer to the modified/created DataEntry (NULL when creation
309 DataEntry *FileHelper::InsertEntryString(std::string const &content,
310 uint16_t group, uint16_t elem,
313 return FileInternal->InsertEntryString(content, group, elem, vr);
317 * \brief Modifies the value of a given DataEntry when it exists.
318 * Creates it with the given value when unexistant.
319 * A copy of the binArea is made to be kept in the Document.
320 * @param binArea (binary) value to be set
321 * @param lgth new value length
322 * @param group Group number of the Entry
323 * @param elem Element number of the Entry
324 * @param vr Value Representation of the DataElement to be inserted
325 * \return pointer to the modified/created DataEntry (NULL when creation
328 DataEntry *FileHelper::InsertEntryBinArea(uint8_t *binArea, int lgth,
329 uint16_t group, uint16_t elem,
332 return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem, vr);
336 * \brief Adds an empty SeqEntry
337 * (remove any existing entry with same group,elem)
338 * @param group Group number of the Entry
339 * @param elem Element number of the Entry
340 * \return pointer to the created SeqEntry (NULL when creation
343 SeqEntry *FileHelper::InsertSeqEntry(uint16_t group, uint16_t elem)
345 return FileInternal->InsertSeqEntry(group, elem);
349 * \brief Get the size of the image data
350 * If the image can be RGB (with a lut or by default), the size
351 * corresponds to the RGB image
352 * (use GetImageDataRawSize if you want to be sure to get *only*
353 * the size of the pixels)
354 * @return The image size
356 size_t FileHelper::GetImageDataSize()
358 if ( PixelWriteConverter->GetUserData() )
360 return PixelWriteConverter->GetUserDataSize();
362 return PixelReadConverter->GetRGBSize();
366 * \brief Get the size of the image data.
367 * If the image could be converted to RGB using a LUT,
368 * this transformation is not taken into account by GetImageDataRawSize
369 * (use GetImageDataSize if you wish)
370 * @return The raw image size
372 size_t FileHelper::GetImageDataRawSize()
374 if ( PixelWriteConverter->GetUserData() )
376 return PixelWriteConverter->GetUserDataSize();
378 return PixelReadConverter->GetRawSize();
382 * \brief brings pixels into memory :
383 * - Allocates necessary memory,
384 * - Reads the pixels from disk (uncompress if necessary),
385 * - Transforms YBR pixels, if any, into RGB pixels,
386 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
387 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
388 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
389 * @return Pointer to newly allocated pixel data.
390 * (uint8_t is just for prototyping. feel free to cast)
391 * NULL if alloc fails
393 uint8_t *FileHelper::GetImageData()
395 if ( PixelWriteConverter->GetUserData() )
397 return PixelWriteConverter->GetUserData();
402 // If the decompression failed nothing can be done.
406 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
408 return PixelReadConverter->GetRGB();
412 // When no LUT or LUT conversion fails, return the Raw
413 return PixelReadConverter->GetRaw();
418 * \brief brings pixels into memory :
419 * - Allocates necessary memory,
420 * - Transforms YBR pixels (if any) into RGB pixels
421 * - Transforms 3 planes R, G, B (if any) into a single RGB Plane
422 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
423 * - DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
424 * @return Pointer to newly allocated pixel data.
425 * (uint8_t is just for prototyping. feel free to cast)
426 * NULL if alloc fails
428 uint8_t *FileHelper::GetImageDataRaw ()
433 //#ifndef GDCM_LEGACY_REMOVE
435 * \brief Useless function, since PixelReadConverter forces us
436 * copy the Pixels anyway.
437 * Reads the pixels from disk (uncompress if necessary),
438 * Transforms YBR pixels, if any, into RGB pixels
439 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
440 * Transforms single Grey plane + 3 Palettes into a RGB Plane
441 * Copies at most MaxSize bytes of pixel data to caller allocated
443 * \warning This function allows people that want to build a volume
444 * from an image stack *not to* have, first to get the image pixels,
445 * and then move them to the volume area.
446 * It's absolutely useless for any VTK user since vtk chooses
447 * to invert the lines of an image, that is the last line comes first
448 * (for some axis related reasons?). Hence he will have
449 * to load the image line by line, starting from the end.
450 * VTK users have to call GetImageData
452 * @param destination Address (in caller's memory space) at which the
453 * pixel data should be copied
454 * @param maxSize Maximum number of bytes to be copied. When MaxSize
455 * is not sufficient to hold the pixel data the copy is not
456 * executed (i.e. no partial copy).
457 * @return On success, the number of bytes actually copied. Zero on
458 * failure e.g. MaxSize is lower than necessary.
461 size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
465 // If the decompression failed nothing can be done.
469 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
471 if ( PixelReadConverter->GetRGBSize() > maxSize )
473 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
477 (void*)PixelReadConverter->GetRGB(),
478 PixelReadConverter->GetRGBSize() );
479 return PixelReadConverter->GetRGBSize();
482 // Either no LUT conversion necessary or LUT conversion failed
483 if ( PixelReadConverter->GetRawSize() > maxSize )
485 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
489 (void *)PixelReadConverter->GetRaw(),
490 PixelReadConverter->GetRawSize() );
491 return PixelReadConverter->GetRawSize();
497 * \brief Points the internal pointer to the callers inData
498 * image representation, BUT WITHOUT COPYING THE DATA.
499 * 'image' Pixels are presented as C-like 2D arrays : line per line.
500 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
501 * \warning Since the pixels are not copied, it is the caller's responsability
502 * not to deallocate its data before gdcm uses them (e.g. with
503 * the Write() method )
504 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
505 * user is allowed to pass any kind of pixels since the size is
507 * @param expectedSize total image size, *in Bytes*
509 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
511 PixelWriteConverter->SetUserData(inData, expectedSize);
515 * \brief Set the image data defined by the user
516 * \warning When writting the file, this data are get as default data to write
517 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
518 * user is allowed to pass any kind of pixels since the size is
520 * @param expectedSize total image size, *in Bytes*
522 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
524 if( WriteType == JPEG2000 )
526 PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
528 else if( WriteType == JPEG )
530 PixelWriteConverter->SetCompressJPEGUserData(inData, expectedSize, FileInternal);
534 PixelWriteConverter->SetUserData(inData, expectedSize);
539 * \brief Get the image data defined by the user
540 * \warning When writting the file, this data are get as default data to write
542 uint8_t *FileHelper::GetUserData()
544 return PixelWriteConverter->GetUserData();
548 * \brief Get the image data size defined by the user
549 * \warning When writting the file, this data are get as default data to write
551 size_t FileHelper::GetUserDataSize()
553 return PixelWriteConverter->GetUserDataSize();
557 * \brief Get the image data from the file.
558 * If a LUT is found, the data are expanded to be RGB
560 uint8_t *FileHelper::GetRGBData()
562 return PixelReadConverter->GetRGB();
566 * \brief Get the image data size from the file.
567 * If a LUT is found, the data are expanded to be RGB
569 size_t FileHelper::GetRGBDataSize()
571 return PixelReadConverter->GetRGBSize();
575 * \brief Get the image data from the file.
576 * Even when a LUT is found, the data are not expanded to RGB!
578 uint8_t *FileHelper::GetRawData()
580 return PixelReadConverter->GetRaw();
584 * \brief Get the image data size from the file.
585 * Even when a LUT is found, the data are not expanded to RGB!
587 size_t FileHelper::GetRawDataSize()
589 return PixelReadConverter->GetRawSize();
593 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT
595 uint8_t* FileHelper::GetLutRGBA()
597 if ( PixelReadConverter->GetLutRGBA() ==0 )
598 PixelReadConverter->BuildLUTRGBA();
599 return PixelReadConverter->GetLutRGBA();
603 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Number
605 int FileHelper::GetLutItemNumber()
607 return PixelReadConverter->GetLutItemNumber();
611 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Size
613 int FileHelper::GetLutItemSize()
615 return PixelReadConverter->GetLutItemSize();
619 * \brief Writes on disk A SINGLE Dicom file
620 * NO test is performed on processor "Endiannity".
621 * It's up to the user to call his Reader properly
622 * @param fileName name of the file to be created
623 * (any already existing file is over written)
624 * @return false if write fails
626 bool FileHelper::WriteRawData(std::string const &fileName)
628 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
631 gdcmWarningMacro( "Fail to open (write) file:" << fileName.c_str());
635 if ( PixelWriteConverter->GetUserData() )
637 fp1.write( (char *)PixelWriteConverter->GetUserData(),
638 PixelWriteConverter->GetUserDataSize() );
640 else if ( PixelReadConverter->GetRGB() )
642 fp1.write( (char *)PixelReadConverter->GetRGB(),
643 PixelReadConverter->GetRGBSize());
645 else if ( PixelReadConverter->GetRaw() )
647 fp1.write( (char *)PixelReadConverter->GetRaw(),
648 PixelReadConverter->GetRawSize());
652 gdcmErrorMacro( "Nothing written." );
661 * \brief Writes on disk A SINGLE Dicom file,
662 * using the Implicit Value Representation convention
663 * NO test is performed on processor "Endianity".
664 * @param fileName name of the file to be created
665 * (any already existing file is overwritten)
666 * @return false if write fails
669 bool FileHelper::WriteDcmImplVR (std::string const &fileName)
671 SetWriteTypeToDcmImplVR();
672 return Write(fileName);
676 * \brief Writes on disk A SINGLE Dicom file,
677 * using the Explicit Value Representation convention
678 * NO test is performed on processor "Endiannity".
679 * @param fileName name of the file to be created
680 * (any already existing file is overwritten)
681 * @return false if write fails
684 bool FileHelper::WriteDcmExplVR (std::string const &fileName)
686 SetWriteTypeToDcmExplVR();
687 return Write(fileName);
691 * \brief Writes on disk A SINGLE Dicom file,
692 * using the ACR-NEMA convention
693 * NO test is performed on processor "Endiannity".
694 * (a l'attention des logiciels cliniques
695 * qui ne prennent en entrée QUE des images ACR ...
696 * \warning if a DICOM_V3 header is supplied,
697 * groups < 0x0008 and shadow groups are ignored
698 * \warning NO TEST is performed on processor "Endiannity".
699 * @param fileName name of the file to be created
700 * (any already existing file is overwritten)
701 * @return false if write fails
704 bool FileHelper::WriteAcr (std::string const &fileName)
707 return Write(fileName);
711 * \brief Writes on disk A SINGLE Dicom file,
712 * @param fileName name of the file to be created
713 * (any already existing file is overwritten)
714 * @return false if write fails
716 bool FileHelper::Write(std::string const &fileName)
718 CheckMandatoryElements(); //called once, here !
725 SetWriteFileTypeToImplicitVR();
728 case Unknown: // should never happen; ExplicitVR is the default value
731 // User should ask gdcm to write an image in Explicit VR mode
732 // only when he is sure *all* the VR of *all* the DataElements is known.
733 // i.e : when there are *only* Public Groups
734 // or *all* the Shadow Groups are fully described in the relevant Shadow
736 // Let's just *dream* about it; *never* trust a user !
737 // We turn to Implicit VR if at least the VR of one element is unknown.
739 // Better we let DocEntry::WriteContent to put vr=UN for undocumented Shadow Groups !
742 e = FileInternal->GetFirstEntry();
745 if (e->GetVR() == " ")
747 SetWriteTypeToDcmImplVR();
748 SetWriteFileTypeToImplicitVR();
752 e = FileInternal->GetNextEntry();
757 SetWriteFileTypeToExplicitVR();
762 SetWriteFileTypeToExplicitVR();
767 // NOTHING is done here just for LibIDO.
768 // Just to avoid further trouble if user creates a file ex-nihilo,
769 // wants to write it as an ACR-NEMA file,
770 // and forgets to create any Entry belonging to group 0008
772 // We add Recognition Code (RET)
773 if ( ! FileInternal->GetDataEntry(0x0008, 0x0010) )
774 FileInternal->InsertEntryString("ACR-NEMA V1.0 ",
775 0x0008, 0x0010, "LO");
776 SetWriteFileTypeToACR();
777 // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
780 /// \todo FIXME : JPEG may be either ExplicitVR or ImplicitVR
782 SetWriteFileTypeToJPEG();
786 SetWriteFileTypeToJPEG2000();
790 // --------------------------------------------------------------
791 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
793 // if recognition code tells us we dealt with a LibIDO image
794 // we reproduce on disk the switch between lineNumber and columnNumber
795 // just before writting ...
796 /// \todo the best trick would be *change* the recognition code
797 /// but pb expected if user deals with, e.g. COMPLEX images
799 if ( WriteType == ACR_LIBIDO )
805 SetWriteToNoLibido();
807 // ----------------- End of Special Patch ----------------
812 SetWriteToRaw(); // modifies and pushes to the archive, when necessary
815 SetWriteToRGB(); // modifies and pushes to the archive, when necessary
820 if (WriteType == JPEG || WriteType == JPEG2000)
823 check = CheckWriteIntegrity(); // verifies length
827 check = FileInternal->Write(fileName,WriteType);
831 // RestoreWriteFileType();
832 // RestoreWriteMandatory();
834 // --------------------------------------------------------------
835 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
837 // ...and we restore the header to be Dicom Compliant again
838 // just after writting
839 RestoreWriteOfLibido();
840 // ----------------- End of Special Patch ----------------
845 //-----------------------------------------------------------------------------
848 * \brief Verifies the size of the user given PixelData
849 * @return true if check is successfull
851 bool FileHelper::CheckWriteIntegrity()
853 if ( PixelWriteConverter->GetUserData() )
855 int numberBitsAllocated = FileInternal->GetBitsAllocated();
856 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
858 gdcmWarningMacro( "numberBitsAllocated changed from "
859 << numberBitsAllocated << " to 16 "
860 << " for consistency purpose" );
861 numberBitsAllocated = 16;
864 size_t decSize = FileInternal->GetXSize()
865 * FileInternal->GetYSize()
866 * FileInternal->GetZSize()
867 * FileInternal->GetTSize()
868 * FileInternal->GetSamplesPerPixel()
869 * ( numberBitsAllocated / 8 );
870 size_t rgbSize = decSize;
871 if ( FileInternal->HasLUT() )
872 rgbSize = decSize * 3;
877 if ( decSize!=PixelWriteConverter->GetUserDataSize() )
879 gdcmWarningMacro( "Data size (Raw) is incorrect. Should be "
880 << decSize << " / Found :"
881 << PixelWriteConverter->GetUserDataSize() );
886 if ( rgbSize!=PixelWriteConverter->GetUserDataSize() )
888 gdcmWarningMacro( "Data size (RGB) is incorrect. Should be "
889 << decSize << " / Found "
890 << PixelWriteConverter->GetUserDataSize() );
900 * \brief Updates the File to write RAW data (as opposed to RGB data)
901 * (modifies, when necessary, photochromatic interpretation,
902 * bits allocated, Pixels element VR)
903 * WARNING : if SetPhotometricInterpretationToMonochrome1() was called
904 * before Pixel Elements if modified :-(
906 void FileHelper::SetWriteToRaw()
908 if ( FileInternal->GetNumberOfScalarComponents() == 3
909 && !FileInternal->HasLUT() )
915 // 0x0028,0x0004 : Photometric Interpretation
916 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
917 if (FileInternal->HasLUT() )
919 photInt->SetString("PALETTE COLOR ");
923 if (GetPhotometricInterpretation() == 2)
924 photInt->SetString("MONOCHROME2 "); // 0 = Black
926 photInt->SetString("MONOCHROME1 "); // 0 = White !
929 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
930 PixelReadConverter->GetRawSize());
932 std::string vr = "OB";
933 if ( FileInternal->GetBitsAllocated()>8 )
935 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
937 // For non RAW data. Mainly JPEG
938 if( WriteType == JPEG || WriteType == JPEG2000)
944 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
945 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
946 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
948 static_cast< uint32_t >(PixelWriteConverter->GetDataSize()) );
950 if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
952 ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );
955 Archive->Push(photInt);
956 Archive->Push(pixel);
964 * \brief Updates the File to write RGB data (as opposed to RAW data)
965 * (modifies, when necessary, photochromatic interpretation,
966 * samples per pixel, Planar configuration,
967 * bits allocated, bits stored, high bit -ACR 24 bits-
968 * Pixels element VR, pushes out the LUT, )
970 void FileHelper::SetWriteToRGB()
972 if ( FileInternal->GetNumberOfScalarComponents()==3 )
974 PixelReadConverter->BuildRGBImage();
976 DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
977 spp->SetString("3 ");
979 DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
980 planConfig->SetString("0 ");
982 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
983 photInt->SetString("RGB ");
985 if ( PixelReadConverter->GetRGB() )
987 PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
988 PixelReadConverter->GetRGBSize());
992 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
993 PixelReadConverter->GetRawSize());
996 std::string vr = "OB";
997 if ( FileInternal->GetBitsAllocated()>8 )
999 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
1002 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
1003 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
1004 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
1005 pixel->SetLength(PixelWriteConverter->GetDataSize());
1008 Archive->Push(planConfig);
1009 Archive->Push(photInt);
1010 Archive->Push(pixel);
1013 planConfig->Delete();
1018 Archive->Push(0x0028,0x1101);
1019 Archive->Push(0x0028,0x1102);
1020 Archive->Push(0x0028,0x1103);
1021 Archive->Push(0x0028,0x1201);
1022 Archive->Push(0x0028,0x1202);
1023 Archive->Push(0x0028,0x1203);
1025 // push out Palette Color Lookup Table UID, if any
1026 Archive->Push(0x0028,0x1199);
1028 // For old '24 Bits' ACR-NEMA
1029 // Thus, we have a RGB image and the bits allocated = 24 and
1030 // samples per pixels = 1 (in the read file)
1031 if ( FileInternal->GetBitsAllocated()==24 )
1033 DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
1034 bitsAlloc->SetString("8 ");
1036 DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
1037 bitsStored->SetString("8 ");
1039 DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
1040 highBit->SetString("7 ");
1042 Archive->Push(bitsAlloc);
1043 Archive->Push(bitsStored);
1044 Archive->Push(highBit);
1046 bitsAlloc->Delete();
1047 bitsStored->Delete();
1058 * \brief Restore the File write mode
1060 void FileHelper::RestoreWrite()
1062 Archive->Restore(0x0028,0x0002);
1063 Archive->Restore(0x0028,0x0004);
1065 Archive->Restore(0x0028,0x0006);
1066 Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1068 // For old ACR-NEMA (24 bits problem)
1069 Archive->Restore(0x0028,0x0100);
1070 Archive->Restore(0x0028,0x0101);
1071 Archive->Restore(0x0028,0x0102);
1074 Archive->Restore(0x0028,0x1101);
1075 Archive->Restore(0x0028,0x1102);
1076 Archive->Restore(0x0028,0x1103);
1077 Archive->Restore(0x0028,0x1201);
1078 Archive->Restore(0x0028,0x1202);
1079 Archive->Restore(0x0028,0x1203);
1081 // For the Palette Color Lookup Table UID
1082 Archive->Restore(0x0028,0x1203);
1084 // group 0002 may be pushed out for ACR-NEMA writting purposes
1085 Archive->Restore(0x0002,0x0000);
1086 Archive->Restore(0x0002,0x0001);
1087 Archive->Restore(0x0002,0x0002);
1088 Archive->Restore(0x0002,0x0003);
1089 Archive->Restore(0x0002,0x0010);
1090 Archive->Restore(0x0002,0x0012);
1091 Archive->Restore(0x0002,0x0013);
1092 Archive->Restore(0x0002,0x0016);
1093 Archive->Restore(0x0002,0x0100);
1094 Archive->Restore(0x0002,0x0102);
1099 * \brief Pushes out the whole group 0002
1100 * FIXME : better, set a flag to tell the writer not to write it ...
1101 * FIXME : method should probably have an other name !
1102 * SetWriteFileTypeToACR is NOT opposed to
1103 * SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1105 void FileHelper::SetWriteFileTypeToACR()
1107 Archive->Push(0x0002,0x0000);
1108 Archive->Push(0x0002,0x0001);
1109 Archive->Push(0x0002,0x0002);
1110 Archive->Push(0x0002,0x0003);
1111 Archive->Push(0x0002,0x0010);
1112 Archive->Push(0x0002,0x0012);
1113 Archive->Push(0x0002,0x0013);
1114 Archive->Push(0x0002,0x0016);
1115 Archive->Push(0x0002,0x0100);
1116 Archive->Push(0x0002,0x0102);
1120 * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1122 void FileHelper::SetWriteFileTypeToJPEG2000()
1124 std::string ts = Util::DicomString(
1125 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1127 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1135 * \brief Sets in the File the TransferSyntax to 'JPEG'
1137 void FileHelper::SetWriteFileTypeToJPEG()
1139 std::string ts = Util::DicomString(
1140 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGLosslessProcess14_1) );
1142 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1150 * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"
1152 void FileHelper::SetWriteFileTypeToExplicitVR()
1154 std::string ts = Util::DicomString(
1155 Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1157 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1164 * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"
1166 void FileHelper::SetWriteFileTypeToImplicitVR()
1168 std::string ts = Util::DicomString(
1169 Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1171 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1178 * \brief Set the Write not to Libido format
1180 void FileHelper::SetWriteToLibido()
1182 DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1183 DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1185 if ( oldRow && oldCol )
1187 std::string rows, columns;
1189 DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1190 DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1192 newRow->Copy(oldCol);
1193 newCol->Copy(oldRow);
1195 newRow->SetString(oldCol->GetString());
1196 newCol->SetString(oldRow->GetString());
1198 Archive->Push(newRow);
1199 Archive->Push(newCol);
1205 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1206 libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1207 Archive->Push(libidoCode);
1208 libidoCode->Delete();
1212 * \brief Set the Write not to No Libido format
1214 void FileHelper::SetWriteToNoLibido()
1216 DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1219 if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1221 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1222 libidoCode->SetString("");
1223 Archive->Push(libidoCode);
1224 libidoCode->Delete();
1230 * \brief Restore the Write format
1232 void FileHelper::RestoreWriteOfLibido()
1234 Archive->Restore(0x0028,0x0010);
1235 Archive->Restore(0x0028,0x0011);
1236 Archive->Restore(0x0008,0x0010);
1238 // Restore 'LibIDO-special' entries, if any
1239 Archive->Restore(0x0028,0x0015);
1240 Archive->Restore(0x0028,0x0016);
1241 Archive->Restore(0x0028,0x0017);
1242 Archive->Restore(0x0028,0x00199);
1246 * \brief Duplicates a DataEntry or creates it.
1247 * @param group Group number of the Entry
1248 * @param elem Element number of the Entry
1249 * @param vr Value Representation of the Entry
1250 * \return pointer to the new Bin Entry (NULL when creation failed).
1252 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1255 DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1258 if ( oldE && vr != GDCM_VRUNKNOWN )
1259 if ( oldE->GetVR() != vr )
1264 newE = DataEntry::New(group, elem, vr);
1269 newE = GetFile()->NewDataEntry(group, elem, vr);
1276 * \brief This method is called automatically, just before writting
1277 * in order to produce a 'True Dicom V3' image.
1279 * We cannot know *how* the user made the File :
1280 * (reading an old ACR-NEMA file or a not very clean DICOM file ...)
1281 * Just before writting :
1282 * - we check the Entries
1283 * - we create the mandatory entries if they are missing
1284 * - we modify the values if necessary
1285 * - we push the sensitive entries to the Archive
1286 * The writing process will restore the entries as they where before
1287 * entering FileHelper::CheckMandatoryElements, so the user will always
1288 * see the entries just as they were before he decided to write.
1291 * - Entries whose type is 1 are mandatory, with a mandatory value
1292 * - Entries whose type is 1c are mandatory-inside-a-Sequence,
1293 * with a mandatory value
1294 * - Entries whose type is 2 are mandatory, with an optional value
1295 * - Entries whose type is 2c are mandatory-inside-a-Sequence,
1296 * with an optional value
1297 * - Entries whose type is 3 are optional
1300 * - warn the user if we had to add some entries :
1301 * even if a mandatory entry is missing, we add it, with a default value
1302 * (we don't want to give up the writting process if user forgot to
1303 * specify Lena's Patient ID, for instance ...)
1304 * - read the whole PS 3.3 Part of DICOM (890 pages)
1305 * and write a *full* checker (probably one method per Modality ...)
1306 * Any contribution is welcome.
1307 * - write a user callable full checker, to allow post reading
1308 * and/or pre writting image consistency check.
1311 /* -------------------------------------------------------------------------------------
1312 To be moved to User's guide / WIKI ?
1314 We have to deal with 4 *very* different cases :
1315 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1317 -2) user modified the pixels of an existing image.
1319 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1321 -4) user modified/added some tags *without processing* the pixels (anonymization...)
1322 UNMODIFIED_PIXELS_IMAGE
1323 -Probabely some more to be added.
1325 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1328 0008 0012 Instance Creation Date
1329 0008 0013 Instance Creation Time
1330 0008 0018 SOP Instance UID
1331 are *always* created with the current values; user has *no* possible intervention on
1334 'Serie Instance UID'(0x0020,0x000e)
1335 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1336 created if it doesn't.
1337 The user is allowed to create his own Series/Studies,
1338 keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1340 The user shouldn't add any image to a 'Manufacturer Serie'
1341 but there is no way no to allow him to do that
1343 None of the 'shadow elements' are droped out.
1347 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1350 'Media Storage SOP Class UID' (0x0002,0x0002)
1351 'SOP Class UID' (0x0008,0x0016) are set to
1352 [Secondary Capture Image Storage]
1353 'Image Type' (0x0008,0x0008) is forced to "DERIVED\PRIMARY"
1354 Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1357 If 'SOP Class UID' exists in the native image ('true DICOM' image)
1358 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1359 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1360 whose value is the original 'SOP Class UID'
1361 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1362 whose value is the original 'SOP Class UID'
1364 3) TODO : find a trick to allow user to pass to the writter the list of the Dicom images
1365 or the Series, (or the Study ?) he used to created his image
1366 (MIP, MPR, cartography image, ...)
1367 These info should be stored (?)
1368 0008 1110 SQ 1 Referenced Study Sequence
1369 0008 1115 SQ 1 Referenced Series Sequence
1370 0008 1140 SQ 1 Referenced Image Sequence
1372 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1373 'Media Storage SOP Class UID' (0x0002,0x0002)
1374 'SOP Class UID' (0x0008,0x0016)
1375 'Image Type' (0x0008,0x0008)
1376 'Conversion Type' (0x0008,0x0064)
1379 Bellow follows the full description (hope so !) of the consistency checks performed
1380 by gdcm::FileHelper::CheckMandatoryElements()
1383 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1384 -->'SOP Class UID' (0x0008,0x0016) are defaulted to
1385 [Secondary Capture Image Storage]
1386 --> 'Image Type' (0x0008,0x0008)
1387 is forced to "DERIVED\PRIMARY"
1388 (The written image is no longer an 'ORIGINAL' one)
1389 Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1391 --> Conversion Type (0x0008,0x0064)
1392 is defaulted to 'SYN' (Synthetic Image)
1393 when *he* knows he created his own image ex nihilo
1395 --> 'Modality' (0x0008,0x0060)
1396 is defaulted to "OT" (other) if missing.
1397 (a fully user created image belongs to *no* modality)
1399 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1400 --> 'Implementation Class UID' (0x0002,0x0012)
1401 are automatically generated; no user intervention possible
1403 --> 'Serie Instance UID'(0x0020,0x000e)
1404 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1405 created if it doesn't.
1406 The user is allowed to create his own Series/Studies,
1407 keeping the same 'Serie Instance UID' / 'Study Instance UID'
1410 The user shouldn't add any image to a 'Manufacturer Serie'
1411 but there is no way no to allowed him to do that
1413 --> If 'SOP Class UID' exists in the native image ('true DICOM' image)
1414 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1416 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1417 whose value is the original 'SOP Class UID'
1418 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1419 whose value is the original 'SOP Class UID'
1421 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1422 --> Pixel Spacing (0x0028,0x0030) is defaulted to "1.0\1.0"
1423 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1425 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1427 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1429 --> Study Date, Study Time are defaulted to current Date and Time
1430 (they remain unchanged if they exist)
1432 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from
1433 Image Orientation (Patient) : (0020|0037) or from
1434 Image Orientation (RET) : (0020 0035)
1436 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1437 are created, with empty value if there are missing.
1439 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1440 are defaulted with a 'gdcm' value.
1442 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1443 --> Referring Physician's Name (Type 2)
1444 are created, with empty value if there are missing.
1446 -------------------------------------------------------------------------------------*/
1448 void FileHelper::CheckMandatoryElements()
1450 std::string sop = Util::CreateUniqueUID();
1452 // --------------------- For Meta Elements ---------------------
1453 // just to remember : 'official' 0002 group
1454 if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1456 // Group 000002 (Meta Elements) already pushed out
1458 //0002 0000 UL 1 Meta Group Length
1459 //0002 0001 OB 1 File Meta Information Version
1460 //0002 0002 UI 1 Media Storage SOP Class UID
1461 //0002 0003 UI 1 Media Storage SOP Instance UID
1462 //0002 0010 UI 1 Transfer Syntax UID
1463 //0002 0012 UI 1 Implementation Class UID
1464 //0002 0013 SH 1 Implementation Version Name
1465 //0002 0016 AE 1 Source Application Entity Title
1466 //0002 0100 UI 1 Private Information Creator
1467 //0002 0102 OB 1 Private Information
1469 // Push out 'ACR-NEMA-special' entries, if any
1470 Archive->Push(0x0008,0x0001); // Length to End
1471 Archive->Push(0x0008,0x0010); // Recognition Code
1472 Archive->Push(0x0028,0x0005); // Image Dimension
1474 // Create them if not found
1475 // Always modify the value
1476 // Push the entries to the archive.
1477 CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1479 DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1480 e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1482 e_0002_0001->SetLength(2);
1483 Archive->Push(e_0002_0001);
1484 e_0002_0001->Delete();
1486 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1488 // we keep the original 'Media Storage SOP Class UID', we default it if missing
1489 CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1493 // It's *not* an image comming straight from a source. We force
1494 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1495 CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1498 // 'Media Storage SOP Instance UID'
1499 CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1501 // 'Implementation Class UID'
1502 // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1503 // seems to be Root UID + 4 digits (?)
1504 CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1506 // 'Implementation Version Name'
1507 std::string version = "GDCM ";
1508 version += Util::GetVersion();
1509 CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1512 // --------------------- For DataSet ---------------------
1514 if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1517 gdcmDebugMacro( "USER_OWN_IMAGE (1)");
1518 // If 'SOP Class UID' exists ('true DICOM' image)
1519 // we create the 'Source Image Sequence' SeqEntry
1520 // to hold informations about the Source Image
1522 DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1525 // Create 'Source Image Sequence' SeqEntry
1526 // SeqEntry *sis = SeqEntry::New (
1527 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x2112) );
1528 SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1529 SQItem *sqi = SQItem::New(1);
1530 // (we assume 'SOP Instance UID' exists too)
1531 // create 'Referenced SOP Class UID'
1532 // DataEntry *e_0008_1150 = DataEntry::New(
1533 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1150) );
1534 DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1535 e_0008_1150->SetString( e_0008_0016->GetString());
1536 sqi->AddEntry(e_0008_1150);
1537 e_0008_1150->Delete();
1539 // create 'Referenced SOP Instance UID'
1540 DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1541 // DataEntry *e_0008_1155 = DataEntry::New(
1542 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1155) );
1543 DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1544 e_0008_1155->SetString( e_0008_0018->GetString());
1545 sqi->AddEntry(e_0008_1155);
1546 e_0008_1155->Delete();
1548 sis->AddSQItem(sqi,1);
1551 // temporarily replaces any previous 'Source Image Sequence'
1555 // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1556 if ( ContentType == FILTERED_IMAGE)
1557 // the user *knows* he just modified the pixels
1558 // the image is no longer an 'Original' one
1559 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1563 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1565 // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1566 CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");
1570 // It's *not* an image comming straight from a source. We force
1571 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1572 CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");
1575 Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1576 // Push out 'LibIDO-special' entries, if any
1577 Archive->Push(0x0028,0x0015);
1578 Archive->Push(0x0028,0x0016);
1579 Archive->Push(0x0028,0x0017);
1580 Archive->Push(0x0028,0x0198); // very old versions
1581 Archive->Push(0x0028,0x0199);
1583 // Replace deprecated 0028 0012 US Planes
1584 // by new 0028 0008 IS Number of Frames
1586 ///\todo : find if there is a rule!
1587 DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1590 CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1591 Archive->Push(0x0028,0x0012);
1594 // Deal with the pb of (Bits Stored = 12)
1595 // - we're gonna write the image as Bits Stored = 16
1596 if ( FileInternal->GetEntryString(0x0028,0x0100) == "12")
1598 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1601 // Check if user wasn't drunk ;-)
1603 std::ostringstream s;
1604 // check 'Bits Allocated' vs decent values
1605 int nbBitsAllocated = FileInternal->GetBitsAllocated();
1606 if ( (nbBitsAllocated == 0 || nbBitsAllocated > 32)
1607 || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1609 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1610 gdcmWarningMacro("(0028,0100) changed from "
1611 << nbBitsAllocated << " to 16 for consistency purpose");
1612 nbBitsAllocated = 16;
1614 // check 'Bits Stored' vs 'Bits Allocated'
1615 int nbBitsStored = FileInternal->GetBitsStored();
1616 if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1619 s << nbBitsAllocated;
1620 CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1621 gdcmWarningMacro("(0028,0101) changed from "
1622 << nbBitsStored << " to " << nbBitsAllocated
1623 << " for consistency purpose" );
1624 nbBitsStored = nbBitsAllocated;
1626 // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1627 int highBitPosition = FileInternal->GetHighBitPosition();
1628 if ( highBitPosition == 0 ||
1629 highBitPosition > nbBitsAllocated-1 ||
1630 highBitPosition < nbBitsStored-1 )
1633 s << nbBitsStored - 1;
1634 CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1635 gdcmWarningMacro("(0028,0102) changed from "
1636 << highBitPosition << " to " << nbBitsAllocated-1
1637 << " for consistency purpose");
1640 // check Pixel Representation (default it as 0 -unsigned-)
1642 DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1645 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1646 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1650 int sign = (int)e_0028_0103->GetValue(0);
1651 if (sign !=1 && sign !=0)
1653 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1654 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1658 std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1659 if ( pixelSpacing == GDCM_UNFOUND )
1661 pixelSpacing = "1.0\\1.0";
1662 // if missing, Pixel Spacing forced to "1.0\1.0"
1663 CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1666 // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1667 // --> This one is the *legal* one !
1668 if ( ContentType != USER_OWN_IMAGE)
1669 // we write it only when we are *sure* the image comes from
1670 // an imager (see also 0008,0x0064)
1671 CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1674 ///Exact meaning of RETired fields
1676 // See page 73 of ACR-NEMA_300-1988.pdf !
1678 // 0020,0020 : Patient Orientation :
1679 Patient direction of the first row and
1680 column of the images. The first entry id the direction of the raws, given by the
1681 direction of the last pixel in the first row from the first pixel in tha row.
1682 the second entry is the direction of the columns, given by the direction of the
1683 last pixel in the first column from the first pixel in that column.
1684 L : Left, F : Feet, A : Anterior, P : Posterior.
1685 Up to 3 letters can be used in combination to indicate oblique planes.
1687 //0020,0030 Image Position (RET)
1688 x,y,z coordinates im mm of the first pixel in the image
1690 // 0020,0035 Image Orientation (RET)
1691 Direction cosines of the R axis of the image system with respect to the
1692 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1693 the image system with respect to the same axes
1695 //0020,0050 Location
1696 An image location reference, standard for the modality (such as CT bed position),
1697 used to indicate position. Calculation of position for other purposes
1698 is only from (0020,0030) and (0020,0035)
1702 // if imagePositionPatient not found, default it with imagePositionRet, if any
1703 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1705 std::string imagePositionRet = FileInternal->GetEntryString(0x0020,0x0030);
1706 std::string imageOrientationRet = FileInternal->GetEntryString(0x0020,0x0035);
1707 std::string imagePositionPatient = FileInternal->GetEntryString(0x0020,0x0032);
1708 std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1710 if( imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1711 && imagePositionRet != GDCM_UNFOUND && imageOrientationRet != GDCM_UNFOUND)
1713 CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1714 Archive->Push(0x0020,0x0030);
1715 CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1716 Archive->Push(0x0020,0x0035);
1720 // Samples Per Pixel (type 1) : default to grayscale
1721 CheckMandatoryEntry(0x0028,0x0002,"1","US");
1723 // --- Check UID-related Entries ---
1725 // At the end, not to overwrite the original ones,
1726 // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
1727 // 'SOP Instance UID'
1728 CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1730 if ( ContentType == USER_OWN_IMAGE)
1732 gdcmDebugMacro( "USER_OWN_IMAGE (2)");
1734 // Other possible values are :
1735 // See PS 3.3, Page 408
1737 // DV = Digitized Video
1738 // DI = Digital Interface
1739 // DF = Digitized Film
1740 // WSD = Workstation
1741 // SD = Scanned Document
1742 // SI = Scanned Image
1744 // SYN = Synthetic Image
1746 CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1749 if ( ContentType == CREATED_IMAGE)
1751 /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1756 // ---- The user will never have to take any action on the following ----
1758 // new value for 'SOP Instance UID'
1759 //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1761 // Instance Creation Date
1762 const std::string &date = Util::GetCurrentDate();
1763 CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1765 // Instance Creation Time
1766 const std::string &time = Util::GetCurrentTime();
1767 CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1770 CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1772 CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1775 //CopyMandatoryEntry(0x0008,0x0050,"");
1776 CheckMandatoryEntry(0x0008,0x0050,"","SH");
1779 // ----- Add Mandatory Entries if missing ---
1780 // Entries whose type is 1 are mandatory, with a mandatory value
1781 // Entries whose type is 1c are mandatory-inside-a-Sequence,
1782 // with a mandatory value
1783 // Entries whose type is 2 are mandatory, with an optional value
1784 // Entries whose type is 2c are mandatory-inside-a-Sequence,
1785 // with an optional value
1786 // Entries whose type is 3 are optional
1788 // 'Study Instance UID'
1789 // Keep the value if exists
1790 // The user is allowed to create his own Study,
1791 // keeping the same 'Study Instance UID' for various images
1792 // The user may add images to a 'Manufacturer Study',
1793 // adding new Series to an already existing Study
1794 CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1796 // 'Serie Instance UID'
1797 // Keep the value if exists
1798 // The user is allowed to create his own Series,
1799 // keeping the same 'Serie Instance UID' for various images
1800 // The user shouldn't add any image to a 'Manufacturer Serie'
1801 // but there is no way no to prevent him for doing that
1802 CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1805 CheckMandatoryEntry(0x0020,0x0010,"","SH");
1808 CheckMandatoryEntry(0x0020,0x0011,"","IS");
1811 CheckMandatoryEntry(0x0020,0x0013,"","IS");
1813 // Patient Orientation
1814 // Can be computed from (0020|0037) : Image Orientation (Patient)
1815 GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1816 std::string ori = o->GetOrientation ( FileInternal );
1818 if (ori != "\\" && ori != GDCM_UNFOUND)
1819 CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1821 CheckMandatoryEntry(0x0020,0x0020,"","CS");
1823 // Default Patient Position to HFS
1824 CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1826 // Modality : if missing we set it to 'OTher'
1827 CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1829 // Manufacturer : if missing we set it to 'GDCM Factory'
1830 CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1832 // Institution Name : if missing we set it to 'GDCM Hospital'
1833 CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1835 // Patient's Name : if missing, we set it to 'GDCM^Patient'
1836 CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1838 // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1839 CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1841 // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1842 CheckMandatoryEntry(0x0010,0x0030,"","DA");
1844 // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1845 CheckMandatoryEntry(0x0010,0x0040,"","CS");
1847 // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1848 CheckMandatoryEntry(0x0008,0x0090,"","PN");
1851 // Deal with element 0x0000 (group length) of each group.
1852 // First stage : get all the different Groups
1855 DocEntry *d = FileInternal->GetFirstEntry();
1858 grHT[d->GetGroup()] = 0;
1859 d=FileInternal->GetNextEntry();
1861 // Second stage : add the missing ones (if any)
1862 for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)
1864 CheckMandatoryEntry(it->first, 0x0000, "0");
1866 // Third stage : update all 'zero level' groups length
1870 if (PhotometricInterpretation == 1)
1875 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1877 DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1880 //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1881 entry = DataEntry::New(group,elem,vr);
1882 entry->SetString(value);
1883 Archive->Push(entry);
1888 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
1889 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1891 //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1892 DataEntry *entry = DataEntry::New(group,elem,vr);
1893 entry->SetString(value);
1894 Archive->Push(entry);
1898 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1900 DataEntry *entry = CopyDataEntry(group,elem,vr);
1901 entry->SetString(value);
1902 Archive->Push(entry);
1907 * \brief Restore in the File the initial group 0002
1909 void FileHelper::RestoreWriteMandatory()
1911 // group 0002 may be pushed out for ACR-NEMA writting purposes
1912 Archive->Restore(0x0002,0x0000);
1913 Archive->Restore(0x0002,0x0001);
1914 Archive->Restore(0x0002,0x0002);
1915 Archive->Restore(0x0002,0x0003);
1916 Archive->Restore(0x0002,0x0010);
1917 Archive->Restore(0x0002,0x0012);
1918 Archive->Restore(0x0002,0x0013);
1919 Archive->Restore(0x0002,0x0016);
1920 Archive->Restore(0x0002,0x0100);
1921 Archive->Restore(0x0002,0x0102);
1923 // FIXME : Check if none is missing !
1925 Archive->Restore(0x0008,0x0012);
1926 Archive->Restore(0x0008,0x0013);
1927 Archive->Restore(0x0008,0x0016);
1928 Archive->Restore(0x0008,0x0018);
1929 Archive->Restore(0x0008,0x0060);
1930 Archive->Restore(0x0008,0x0070);
1931 Archive->Restore(0x0008,0x0080);
1932 Archive->Restore(0x0008,0x0090);
1933 Archive->Restore(0x0008,0x2112);
1935 Archive->Restore(0x0010,0x0010);
1936 Archive->Restore(0x0010,0x0030);
1937 Archive->Restore(0x0010,0x0040);
1939 Archive->Restore(0x0020,0x000d);
1940 Archive->Restore(0x0020,0x000e);
1944 * \brief CallStartMethod
1946 void FileHelper::CallStartMethod()
1950 CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1954 * \brief CallProgressMethod
1956 void FileHelper::CallProgressMethod()
1958 CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1962 * \brief CallEndMethod
1964 void FileHelper::CallEndMethod()
1967 CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1970 //-----------------------------------------------------------------------------
1973 * \brief Factorization for various forms of constructors.
1975 void FileHelper::Initialize()
1978 ContentType = USER_OWN_IMAGE;
1980 WriteMode = WMODE_RAW;
1981 WriteType = ExplicitVR;
1983 PhotometricInterpretation = 2; // Black = 0
1985 PixelReadConverter = new PixelReadConvert;
1986 PixelWriteConverter = new PixelWriteConvert;
1987 Archive = new DocEntryArchive( FileInternal );
1991 * \brief Reads/[decompresses] the pixels,
1992 * *without* making RGB from Palette Colors
1993 * @return the pixels area, whatever its type
1994 * (uint8_t is just for prototyping : feel free to Cast it)
1996 uint8_t *FileHelper::GetRaw()
1998 PixelReadConverter->SetUserFunction( UserFunction );
2000 uint8_t *raw = PixelReadConverter->GetRaw();
2003 // The Raw image migth not be loaded yet:
2004 std::ifstream *fp = FileInternal->OpenFile();
2005 PixelReadConverter->ReadAndDecompressPixelData( fp );
2007 FileInternal->CloseFile();
2009 raw = PixelReadConverter->GetRaw();
2012 gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
2020 * \brief Deal with Grey levels i.e. re-arange them
2021 * to have low values = dark, high values = bright
2023 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
2025 uint32_t i; // to please M$VC6
2028 // Number of Bits Allocated for storing a Pixel is defaulted to 16
2029 // when absent from the file.
2030 int bitsAllocated = FileInternal->GetBitsAllocated();
2031 if ( bitsAllocated == 0 )
2036 else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2040 // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2041 // when absent from the file.
2042 int bitsStored = FileInternal->GetBitsStored();
2043 if ( bitsStored == 0 )
2045 bitsStored = bitsAllocated;
2048 if (!FileInternal->IsSignedPixelData())
2050 if ( bitsAllocated == 8 )
2052 uint8_t *deb = (uint8_t *)raw;
2053 for (i=0; i<rawSize; i++)
2061 if ( bitsAllocated == 16 )
2064 for (j=0; j<bitsStored-1; j++)
2066 mask = (mask << 1) +1; // will be fff when BitsStored=12
2069 uint16_t *deb = (uint16_t *)raw;
2070 for (i=0; i<rawSize/2; i++)
2080 if ( bitsAllocated == 8 )
2082 uint8_t smask8 = 255;
2083 uint8_t *deb = (uint8_t *)raw;
2084 for (i=0; i<rawSize; i++)
2086 *deb = smask8 - *deb;
2091 if ( bitsAllocated == 16 )
2093 uint16_t smask16 = 65535;
2094 uint16_t *deb = (uint16_t *)raw;
2095 for (i=0; i<rawSize/2; i++)
2097 *deb = smask16 - *deb;
2105 //-----------------------------------------------------------------------------
2107 * \brief Prints the FileInternal + info on PixelReadConvertor
2108 * @param os ostream we want to print in
2109 * @param indent (unused)
2111 void FileHelper::Print(std::ostream &os, std::string const &)
2113 FileInternal->SetPrintLevel(PrintLevel);
2114 FileInternal->Print(os);
2116 if ( FileInternal->IsReadable() )
2118 PixelReadConverter->SetPrintLevel(PrintLevel);
2119 PixelReadConverter->Print(os);
2123 //-----------------------------------------------------------------------------
2124 } // end namespace gdcm
2127 /* Probabely something to be added to use Rescale Slope/Intercept
2128 Have a look ,at ITK code !
2130 // Internal function to rescale pixel according to Rescale Slope/Intercept
2131 template<class TBuffer, class TSource>
2132 void RescaleFunction(TBuffer* buffer, TSource *source,
2133 double slope, double intercept, size_t size)
2135 size /= sizeof(TSource);
2137 if (slope != 1.0 && intercept != 0.0)
2139 // Duff's device. Instead of this code:
2141 // for(unsigned int i=0; i<size; i++)
2143 // buffer[i] = (TBuffer)(source[i]*slope + intercept);
2146 // use Duff's device which exploits "fall through"
2147 register size_t n = (size + 7) / 8;
2150 case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2151 case 7: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2152 case 6: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2153 case 5: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2154 case 4: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2155 case 3: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2156 case 2: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2157 case 1: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2161 else if (slope == 1.0 && intercept != 0.0)
2163 // Duff's device. Instead of this code:
2165 // for(unsigned int i=0; i<size; i++)
2167 // buffer[i] = (TBuffer)(source[i] + intercept);
2170 // use Duff's device which exploits "fall through"
2171 register size_t n = (size + 7) / 8;
2174 case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2175 case 7: *buffer++ = (TBuffer)(*source++ + intercept);
2176 case 6: *buffer++ = (TBuffer)(*source++ + intercept);
2177 case 5: *buffer++ = (TBuffer)(*source++ + intercept);
2178 case 4: *buffer++ = (TBuffer)(*source++ + intercept);
2179 case 3: *buffer++ = (TBuffer)(*source++ + intercept);
2180 case 2: *buffer++ = (TBuffer)(*source++ + intercept);
2181 case 1: *buffer++ = (TBuffer)(*source++ + intercept);
2185 else if (slope != 1.0 && intercept == 0.0)
2187 // Duff's device. Instead of this code:
2189 // for(unsigned int i=0; i<size; i++)
2191 // buffer[i] = (TBuffer)(source[i]*slope);
2194 // use Duff's device which exploits "fall through"
2195 register size_t n = (size + 7) / 8;
2198 case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2199 case 7: *buffer++ = (TBuffer)((*source++)*slope);
2200 case 6: *buffer++ = (TBuffer)((*source++)*slope);
2201 case 5: *buffer++ = (TBuffer)((*source++)*slope);
2202 case 4: *buffer++ = (TBuffer)((*source++)*slope);
2203 case 3: *buffer++ = (TBuffer)((*source++)*slope);
2204 case 2: *buffer++ = (TBuffer)((*source++)*slope);
2205 case 1: *buffer++ = (TBuffer)((*source++)*slope);
2211 // Duff's device. Instead of this code:
2213 // for(unsigned int i=0; i<size; i++)
2215 // buffer[i] = (TBuffer)(source[i]);
2218 // use Duff's device which exploits "fall through"
2219 register size_t n = (size + 7) / 8;
2222 case 0: do { *buffer++ = (TBuffer)(*source++);
2223 case 7: *buffer++ = (TBuffer)(*source++);
2224 case 6: *buffer++ = (TBuffer)(*source++);
2225 case 5: *buffer++ = (TBuffer)(*source++);
2226 case 4: *buffer++ = (TBuffer)(*source++);
2227 case 3: *buffer++ = (TBuffer)(*source++);
2228 case 2: *buffer++ = (TBuffer)(*source++);
2229 case 1: *buffer++ = (TBuffer)(*source++);
2236 template<class TSource>
2237 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2238 void* buffer, TSource *source,
2239 double slope, double intercept, size_t size)
2243 case ImageIOBase::UCHAR:
2244 RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2246 case ImageIOBase::CHAR:
2247 RescaleFunction( (char *)buffer, source, slope, intercept, size);
2249 case ImageIOBase::USHORT:
2250 RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2252 case ImageIOBase::SHORT:
2253 RescaleFunction( (short *)buffer, source, slope, intercept, size);
2255 case ImageIOBase::UINT:
2256 RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2258 case ImageIOBase::INT:
2259 RescaleFunction( (int *)buffer, source, slope, intercept, size);
2261 case ImageIOBase::FLOAT:
2262 RescaleFunction( (float *)buffer, source, slope, intercept, size);
2264 case ImageIOBase::DOUBLE:
2265 RescaleFunction( (double *)buffer, source, slope, intercept, size);
2268 ::itk::OStringStream message;
2269 message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2270 ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);