1 /*=========================================================================
4 Module: $RCSfile: gdcmFileHelper.cxx,v $
7 Date: $Date: 2007/10/19 15:42:23 $
8 Version: $Revision: 1.135 $
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 #include <algorithm> // for transform?
39 #if defined(__BORLANDC__)
40 #include <mem.h> // for memset
46 // ----------------------------- WARNING -------------------------
48 These lines will be moved to the document-to-be 'User's Guide'
50 // To read an image, user needs a gdcm::File
51 gdcm::File *f = new gdcm::File(fileName);
53 // user may also decide he doesn't want to load some parts of the header
54 gdcm::File *f = new gdcm::File();
55 f->SetFileName(fileName);
56 f->SetLoadMode(LD_NOSEQ); // or
57 f->SetLoadMode(LD_NOSHADOW); // or
58 f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW); // or
59 f->SetLoadMode(LD_NOSHADOWSEQ);
62 // To decide whether it's an 'image of interest for him, or not,
63 // user can now check some values
64 std::string v = f->GetEntryValue(groupNb,ElementNb);
66 // to get the pixels, user needs a gdcm::FileHelper
67 gdcm::FileHelper *fh = new gdcm::FileHelper(f);
69 // user may ask not to convert Palette (if any) to RGB
70 uint8_t *pixels = fh->GetImageDataRaw();
71 int imageLength = fh->GetImageDataRawSize();
73 // He can now use the pixels, create a new image, ...
74 uint8_t *userPixels = ...
76 //To re-write the image, user re-uses the gdcm::FileHelper
77 gdcm::File *fh = new gdcm::FileHelper();
79 fh->SetTypeToRaw(); // Even if it was possible to convert Palette to RGB
82 // If user wants to write the file as MONOCHROME1 (0=white)
83 fh->SetPhotometricInterpretationToMonochrome1();
85 fh->SetWriteTypeToDcmExpl(); // he wants Explicit Value Representation
86 // Little Endian is the default,
87 // bigendian not supported for writting
88 (-->SetWriteType(ExplicitVR);)
89 -->WriteType = ExplicitVR;
90 fh->SetWriteTypeToJPEG(); // lossless compression
91 fh->SetWriteTypeToJPEG2000(); // lossless compression
93 fh->SetImageData( userPixels, userPixelsLength);
95 fh->SetUserData( userPixels, userPixelsLength); // this one performs compression, when required
97 fh->Write(newFileName); // overwrites the file, if any
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)
108 fh->SetImageData( userPixels, userPixelsLength);
110 fh->SetUserData( userPixels, userPixelsLength);
111 PixelWriteConverter->SetUserData(inData, expectedSize);
114 fh->SetWriteMode(WMODE_RAW / WMODE_RGB)
116 fh->SetWriteType( ImplicitVR/ExplicitVR/ACR/ACR_LIBIDO/JPEG/JPEG2000)
118 fh->Write(newFileName);
119 CheckMandatoryElements(); // Checks existing ones / Add missing ones
120 Fix VR if unknown elements
121 SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR(); /
122 SetWriteFileTypeToACR() / SetWriteFileTypeToJPEG() / SetWriteFileTypeToJ2K()
123 (Modifies TransferSyntax if any; Pushes to the Archives old one)
124 SetWriteToRaw(); / SetWriteToRGB();
125 (Modifies and pushes to the Archive, when necessary : photochr. interp.,
126 samples per pixel, Planar configuration,
127 bits allocated, bits stored, high bit -ACR 24 bits-
128 Pixels element VR, pushes out the LUT )
130 Sets Photometric Interpretation
131 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
132 Sets VR, BinArea, Length for PixelData
135 Archive->Push(photInt);
136 Archive->Push(pixel);
140 if NumberOfScalarComponents==1
141 SetWriteToRaw(); return;
142 PixelReadConverter->BuildRGBImage()
143 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
144 Archives spp, planConfig,photInt, pixel
146 CheckWriteIntegrity();
147 (checks user given pixels length)
148 FileInternal->Write(fileName,WriteType)
149 fp = opens file(fileName); // out|binary
150 ComputeGroup0002Length( );
151 Document::WriteContent(fp, writetype);
152 writes Dicom File Preamble not ACR-NEMA
153 ElementSet::WriteContent(fp, writetype);
154 writes recursively all DataElements
156 (moves back to the gdcm::File all the archived elements)
162 namespace GDCM_NAME_SPACE
164 typedef std::map<uint16_t, int> GroupHT; // Hash Table
165 //-------------------------------------------------------------------------
166 // Constructor / Destructor
168 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
169 * file (gdcm::File only deals with the ... header)
170 * Opens (in read only and when possible) an existing file and checks
171 * for DICOM compliance. Returns NULL on failure.
172 * It will be up to the user to load the pixels into memory
173 * ( GetImageDataSize() + GetImageData() methods)
174 * \note the in-memory representation of all available tags found in
175 * the DICOM header is post-poned to first header information access.
176 * This avoid a double parsing of public part of the header when
177 * one sets an a posteriori shadow dictionary (efficiency can be
178 * seen as a side effect).
180 FileHelper::FileHelper( )
182 FileInternal = File::New( );
187 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
188 * file (File only deals with the ... header)
189 * Opens (in read only and when possible) an existing file and checks
190 * for DICOM compliance. Returns NULL on failure.
191 * It will be up to the user to load the pixels into memory
192 * ( GetImageDataSize() + GetImageData() methods)
193 * \note the in-memory representation of all available tags found in
194 * the DICOM header is post-poned to first header information access.
195 * This avoid a double parsing of public part of the header when
196 * user sets an a posteriori shadow dictionary (efficiency can be
197 * seen as a side effect).
198 * @param header already built Header
200 FileHelper::FileHelper(File *header)
202 gdcmAssertMacro(header);
204 FileInternal = header;
205 FileInternal->Register();
207 if ( FileInternal->IsReadable() )
209 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
214 * \brief canonical destructor
215 * \note If the header (gdcm::File) was created by the FileHelper constructor,
216 * it is destroyed by the FileHelper
218 FileHelper::~FileHelper()
220 if ( PixelReadConverter )
222 delete PixelReadConverter;
224 if ( PixelWriteConverter )
226 delete PixelWriteConverter;
233 FileInternal->Unregister();
236 //-----------------------------------------------------------------------------
240 * \brief Sets the LoadMode of the internal gdcm::File as a boolean string.
241 * NO_SEQ, NO_SHADOW, NO_SHADOWSEQ ... (nothing more, right now)
242 * WARNING : before using NO_SHADOW, be sure *all* your files
243 * contain accurate values in the 0x0000 element (if any)
244 * of *each* Shadow Group. The parser will fail if the size is wrong !
245 * @param loadMode Load mode to be used
247 void FileHelper::SetLoadMode(int loadMode)
249 GetFile()->SetLoadMode( loadMode );
252 * \brief Sets the LoadMode of the internal gdcm::File
253 * @param fileName name of the file to be open
255 void FileHelper::SetFileName(std::string const &fileName)
257 FileInternal->SetFileName( fileName );
262 * @return false if file cannot be open or no swap info was found,
263 * or no tag was found.
265 bool FileHelper::Load()
267 if ( !FileInternal->Load() )
270 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
275 * \brief Accesses an existing DataEntry through its (group, element)
276 * and modifies its content with the given value.
277 * @param content new value (string) to substitute with
278 * @param group group number of the Dicom Element to modify
279 * @param elem element number of the Dicom Element to modify
280 * \return false if DataEntry not found
282 bool FileHelper::SetEntryString(std::string const &content,
283 uint16_t group, uint16_t elem)
285 return FileInternal->SetEntryString(content, group, elem);
290 * \brief Accesses an existing DataEntry through its (group, element)
291 * and modifies its content with the given value.
292 * @param content new value (void* -> uint8_t*) to substitute with
293 * @param lgth new value length
294 * @param group group number of the Dicom Element to modify
295 * @param elem element number of the Dicom Element to modify
296 * \return false if DataEntry not found
298 bool FileHelper::SetEntryBinArea(uint8_t *content, int lgth,
299 uint16_t group, uint16_t elem)
301 return FileInternal->SetEntryBinArea(content, lgth, group, elem);
305 * \brief Modifies the value of a given DataEntry when it exists.
306 * Creates it with the given value when unexistant.
307 * @param content (string) value to be set
308 * @param group Group number of the Entry
309 * @param elem Element number of the Entry
310 * @param vr Value Representation of the DataElement to be inserted
311 * \return pointer to the modified/created DataEntry (NULL when creation
314 DataEntry *FileHelper::InsertEntryString(std::string const &content,
315 uint16_t group, uint16_t elem,
318 return FileInternal->InsertEntryString(content, group, elem, vr);
322 * \brief Modifies the value of a given DataEntry when it exists.
323 * Creates it with the given value when unexistant.
324 * A copy of the binArea is made to be kept in the Document.
325 * @param binArea (binary) value to be set
326 * @param lgth new value length
327 * @param group Group number of the Entry
328 * @param elem Element number of the Entry
329 * @param vr Value Representation of the DataElement to be inserted
330 * \return pointer to the modified/created DataEntry (NULL when creation
333 DataEntry *FileHelper::InsertEntryBinArea(uint8_t *binArea, int lgth,
334 uint16_t group, uint16_t elem,
337 return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem, vr);
341 * \brief Adds an empty SeqEntry
342 * (remove any existing entry with same group,elem)
343 * @param group Group number of the Entry
344 * @param elem Element number of the Entry
345 * \return pointer to the created SeqEntry (NULL when creation
348 SeqEntry *FileHelper::InsertSeqEntry(uint16_t group, uint16_t elem)
350 return FileInternal->InsertSeqEntry(group, elem);
354 * \brief Get the size of the image data
355 * If the image can be RGB (with a lut or by default), the size
356 * corresponds to the RGB image
357 * (use GetImageDataRawSize if you want to be sure to get *only*
358 * the size of the pixels)
359 * @return The image size
361 size_t FileHelper::GetImageDataSize()
363 if ( PixelWriteConverter->GetUserData() )
365 return PixelWriteConverter->GetUserDataSize();
367 return PixelReadConverter->GetRGBSize();
371 * \brief Get the size of the image data.
372 * If the image could be converted to RGB using a LUT,
373 * this transformation is not taken into account by GetImageDataRawSize
374 * (use GetImageDataSize if you wish)
375 * @return The raw image size
377 size_t FileHelper::GetImageDataRawSize()
379 if ( PixelWriteConverter->GetUserData() )
381 return PixelWriteConverter->GetUserDataSize();
383 return PixelReadConverter->GetRawSize();
387 * \brief brings pixels into memory :
388 * - Allocates necessary memory,
389 * - Reads the pixels from disk (uncompress if necessary),
390 * - Transforms YBR pixels, if any, into RGB pixels,
391 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
392 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
393 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
394 * @return Pointer to newly allocated pixel data.
395 * (uint8_t is just for prototyping. feel free to cast)
396 * NULL if alloc fails
398 uint8_t *FileHelper::GetImageData()
400 if ( PixelWriteConverter->GetUserData() )
402 return PixelWriteConverter->GetUserData();
407 // If the decompression failed nothing can be done.
411 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
413 return PixelReadConverter->GetRGB();
417 // When no LUT or LUT conversion fails, return the Raw
418 return PixelReadConverter->GetRaw();
423 * \brief brings pixels into memory :
424 * - Allocates necessary memory,
425 * - Transforms YBR pixels (if any) into RGB pixels
426 * - Transforms 3 planes R, G, B (if any) into a single RGB Plane
427 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
428 * - DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
429 * @return Pointer to newly allocated pixel data.
430 * (uint8_t is just for prototyping. feel free to cast)
431 * NULL if alloc fails
433 uint8_t *FileHelper::GetImageDataRaw ()
438 //#ifndef GDCM_LEGACY_REMOVE
440 * \brief Useless function, since PixelReadConverter forces us
441 * copy the Pixels anyway.
442 * Reads the pixels from disk (uncompress if necessary),
443 * Transforms YBR pixels, if any, into RGB pixels
444 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
445 * Transforms single Grey plane + 3 Palettes into a RGB Plane
446 * Copies at most MaxSize bytes of pixel data to caller allocated
448 * \warning This function allows people that want to build a volume
449 * from an image stack *not to* have, first to get the image pixels,
450 * and then move them to the volume area.
451 * It's absolutely useless for any VTK user since vtk chooses
452 * to invert the lines of an image, that is the last line comes first
453 * (for some axis related reasons?). Hence he will have
454 * to load the image line by line, starting from the end.
455 * VTK users have to call GetImageData
457 * @param destination Address (in caller's memory space) at which the
458 * pixel data should be copied
459 * @param maxSize Maximum number of bytes to be copied. When MaxSize
460 * is not sufficient to hold the pixel data the copy is not
461 * executed (i.e. no partial copy).
462 * @return On success, the number of bytes actually copied. Zero on
463 * failure e.g. MaxSize is lower than necessary.
466 size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
470 // If the decompression failed nothing can be done.
474 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
476 if ( PixelReadConverter->GetRGBSize() > maxSize )
478 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
482 (void*)PixelReadConverter->GetRGB(),
483 PixelReadConverter->GetRGBSize() );
484 return PixelReadConverter->GetRGBSize();
487 // Either no LUT conversion necessary or LUT conversion failed
488 if ( PixelReadConverter->GetRawSize() > maxSize )
490 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
494 (void *)PixelReadConverter->GetRaw(),
495 PixelReadConverter->GetRawSize() );
496 return PixelReadConverter->GetRawSize();
502 * \brief Points the internal pointer to the callers inData
503 * image representation, BUT WITHOUT COPYING THE DATA.
504 * 'image' Pixels are presented as C-like 2D arrays : line per line.
505 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
506 * \warning Since the pixels are not copied, it is the caller's responsability
507 * not to deallocate its data before gdcm uses them (e.g. with
508 * the Write() method )
509 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
510 * user is allowed to pass any kind of pixels since the size is
512 * @param expectedSize total image size, *in Bytes*
514 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
516 PixelWriteConverter->SetUserData(inData, expectedSize);
517 /// \todo : shouldn't we call SetCompressJPEGUserData/SetCompressJPEG2000UserData
522 * \brief Set the image data defined by the user
523 * \warning When writting the file, this data are get as default data to write
524 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
525 * user is allowed to pass any kind of pixels since the size is
527 * @param expectedSize total image size, *in Bytes*
529 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
531 // Shouldn't we move theese lines to FileHelper::Write()?
533 if( WriteType == JPEG2000 )
535 PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
537 else if( WriteType == JPEG )
539 PixelWriteConverter->SetCompressJPEGUserData(inData, expectedSize, FileInternal);
543 PixelWriteConverter->SetUserData(inData, expectedSize);
547 PixelWriteConverter->SetUserData(inData, expectedSize);
551 * \brief Get the image data defined by the user
552 * \warning When writting the file, this data are get as default data to write
554 uint8_t *FileHelper::GetUserData()
556 return PixelWriteConverter->GetUserData();
560 * \brief Get the image data size defined by the user
561 * \warning When writting the file, this data are get as default data to write
563 size_t FileHelper::GetUserDataSize()
565 return PixelWriteConverter->GetUserDataSize();
569 * \brief Get the image data from the file.
570 * If a LUT is found, the data are expanded to be RGB
572 uint8_t *FileHelper::GetRGBData()
574 return PixelReadConverter->GetRGB();
578 * \brief Get the image data size from the file.
579 * If a LUT is found, the data are expanded to be RGB
581 size_t FileHelper::GetRGBDataSize()
583 return PixelReadConverter->GetRGBSize();
587 * \brief Get the image data from the file.
588 * Even when a LUT is found, the data are not expanded to RGB!
590 uint8_t *FileHelper::GetRawData()
592 return PixelReadConverter->GetRaw();
596 * \brief Get the image data size from the file.
597 * Even when a LUT is found, the data are not expanded to RGB!
599 size_t FileHelper::GetRawDataSize()
601 return PixelReadConverter->GetRawSize();
605 * \brief Access to the underlying PixelReadConverter RGBA LUT
607 uint8_t* FileHelper::GetLutRGBA()
609 if ( PixelReadConverter->GetLutRGBA() ==0 )
610 PixelReadConverter->BuildLUTRGBA();
611 return PixelReadConverter->GetLutRGBA();
615 * \brief Access to the underlying PixelReadConverter RGBA LUT Item Number
617 int FileHelper::GetLutItemNumber()
619 return PixelReadConverter->GetLutItemNumber();
623 * \brief Access to the underlying PixelReadConverter RGBA LUT Item Size
625 int FileHelper::GetLutItemSize()
627 return PixelReadConverter->GetLutItemSize();
631 * \brief Writes on disk A SINGLE Dicom file
632 * NO test is performed on processor "Endiannity".
633 * It's up to the user to call his Reader properly
634 * @param fileName name of the file to be created
635 * (any already existing file is over written)
636 * @return false if write fails
638 bool FileHelper::WriteRawData(std::string const &fileName)
640 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
643 gdcmWarningMacro( "Fail to open (write) file:" << fileName.c_str());
647 if ( PixelWriteConverter->GetUserData() )
649 fp1.write( (char *)PixelWriteConverter->GetUserData(),
650 PixelWriteConverter->GetUserDataSize() );
652 else if ( PixelReadConverter->GetRGB() )
654 fp1.write( (char *)PixelReadConverter->GetRGB(),
655 PixelReadConverter->GetRGBSize());
657 else if ( PixelReadConverter->GetRaw() )
659 fp1.write( (char *)PixelReadConverter->GetRaw(),
660 PixelReadConverter->GetRawSize());
664 gdcmErrorMacro( "Nothing written." );
673 * \brief Writes on disk A SINGLE Dicom file,
674 * using the Implicit Value Representation convention
675 * NO test is performed on processor "Endianity".
676 * @param fileName name of the file to be created
677 * (any already existing file is overwritten)
678 * @return false if write fails
681 bool FileHelper::WriteDcmImplVR (std::string const &fileName)
683 SetWriteTypeToDcmImplVR();
684 return Write(fileName);
688 * \brief Writes on disk A SINGLE Dicom file,
689 * using the Explicit Value Representation convention
690 * NO test is performed on processor "Endiannity".
691 * @param fileName name of the file to be created
692 * (any already existing file is overwritten)
693 * @return false if write fails
696 bool FileHelper::WriteDcmExplVR (std::string const &fileName)
698 SetWriteTypeToDcmExplVR();
699 return Write(fileName);
703 * \brief Writes on disk A SINGLE Dicom file,
704 * using the ACR-NEMA convention
705 * NO test is performed on processor "Endiannity".
706 * (a l'attention des logiciels cliniques
707 * qui ne prennent en entrée QUE des images ACR ...
708 * \warning if a DICOM_V3 header is supplied,
709 * groups < 0x0008 and shadow groups are ignored
710 * \warning NO TEST is performed on processor "Endiannity".
711 * @param fileName name of the file to be created
712 * (any already existing file is overwritten)
713 * @return false if write fails
716 bool FileHelper::WriteAcr (std::string const &fileName)
719 return Write(fileName);
723 * \brief Writes on disk A SINGLE Dicom file,
724 * @param fileName name of the file to be created
725 * (any already existing file is overwritten)
726 * @return false if write fails
728 bool FileHelper::Write(std::string const &fileName)
730 CheckMandatoryElements(); //called once, here !
735 SetWriteFileTypeToImplicitVR();
738 case Unknown: // should never happen; ExplicitVR is the default value
741 // We let DocEntry::WriteContent to put vr=UN for undocumented Shadow Groups !
742 SetWriteFileTypeToExplicitVR();
747 // NOTHING is done here just for LibIDO.
748 // Just to avoid further trouble if user creates a file ex-nihilo,
749 // wants to write it as an ACR-NEMA file,
750 // and forgets to create any Entry belonging to group 0008
752 // We add Recognition Code (RET)
753 if ( ! FileInternal->GetDataEntry(0x0008, 0x0010) )
754 FileInternal->InsertEntryString("ACR-NEMA V1.0 ",
755 0x0008, 0x0010, "LO");
756 SetWriteFileTypeToACR();
757 // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
760 /// \todo FIXME : JPEG/JPEG2000 may be either ExplicitVR or ImplicitVR
762 SetWriteFileTypeToJPEG();
764 //PixelWriteConverter->SetCompressJPEGUserData(
765 // inData, expectedSize, FileInternal);
766 PixelWriteConverter->SetCompressJPEGUserData(
767 PixelWriteConverter->GetUserData(),
768 PixelWriteConverter->GetUserDataSize(),FileInternal);
772 /// \todo Maybe we should consider doing the compression here !
773 // PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
775 SetWriteFileTypeToJPEG2000();
776 PixelWriteConverter->SetCompressJPEG2000UserData(
777 PixelWriteConverter->GetUserData(),
778 PixelWriteConverter->GetUserDataSize(),
783 // --------------------------------------------------------------
784 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
786 // if recognition code tells us we dealt with a LibIDO image
787 // we reproduce on disk the switch between lineNumber and columnNumber
788 // just before writting ...
789 /// \todo the best trick would be *change* the recognition code
790 /// but pb expected if user deals with, e.g. COMPLEX images
792 if ( WriteType == ACR_LIBIDO )
798 SetWriteToNoLibido();
800 // ----------------- End of Special Patch ----------------
805 SetWriteToRaw(); // modifies and pushes to the archive, when necessary
808 SetWriteToRGB(); // modifies and pushes to the archive, when necessary
813 if (WriteType == JPEG || WriteType == JPEG2000)
816 check = CheckWriteIntegrity(); // verifies length
820 check = FileInternal->Write(fileName,WriteType);
824 // RestoreWriteFileType();
825 // RestoreWriteMandatory();
827 // --------------------------------------------------------------
828 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
830 // ...and we restore the header to be Dicom Compliant again
831 // just after writting
832 RestoreWriteOfLibido();
833 // ----------------- End of Special Patch ----------------
838 //-----------------------------------------------------------------------------
841 * \brief Verifies the size of the user given PixelData
842 * @return true if check is successfull
844 bool FileHelper::CheckWriteIntegrity()
846 if ( PixelWriteConverter->GetUserData() )
848 int numberBitsAllocated = FileInternal->GetBitsAllocated();
849 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
851 gdcmWarningMacro( "numberBitsAllocated changed from "
852 << numberBitsAllocated << " to 16 "
853 << " for consistency purpose" );
854 numberBitsAllocated = 16;
857 size_t decSize = FileInternal->GetXSize()
858 * FileInternal->GetYSize()
859 * FileInternal->GetZSize()
860 * FileInternal->GetTSize()
861 * FileInternal->GetSamplesPerPixel()
862 * ( numberBitsAllocated / 8 );
863 size_t rgbSize = decSize;
864 if ( FileInternal->HasLUT() )
865 rgbSize = decSize * 3;
867 size_t userDataSize = PixelWriteConverter->GetUserDataSize();
871 if ( abs((long)(decSize-userDataSize))>1) // ignore padding zero
873 gdcmWarningMacro( "Data size (Raw) is incorrect. Should be "
874 << decSize << " / Found :"
880 if ( abs((long)(rgbSize-userDataSize))>1) // ignore padding zero
882 gdcmWarningMacro( "Data size (RGB) is incorrect. Should be "
883 << rgbSize << " / Found "
894 * \brief Updates the File to write RAW data (as opposed to RGB data)
895 * (modifies, when necessary, photochromatic interpretation,
896 * bits allocated, Pixels element VR)
897 * WARNING : if SetPhotometricInterpretationToMonochrome1() was called
898 * before Pixel Elements is modified :-(
900 void FileHelper::SetWriteToRaw()
902 if ( FileInternal->GetNumberOfScalarComponents() == 3
903 && !FileInternal->HasLUT() )
909 // 0x0028,0x0004 : Photometric Interpretation
910 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
911 if (FileInternal->HasLUT() )
913 photInt->SetString("PALETTE COLOR ");
917 if (GetPhotometricInterpretation() == 2)
918 photInt->SetString("MONOCHROME2 "); // 0 = Black
920 photInt->SetString("MONOCHROME1 "); // 0 = White !
923 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
924 PixelReadConverter->GetRawSize());
926 std::string vr = "OB";
927 if ( FileInternal->GetBitsAllocated()>8 )
929 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
931 // For non RAW data. Mainly JPEG/JPEG2000
932 if( WriteType == JPEG || WriteType == JPEG2000)
938 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
939 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
940 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
942 static_cast< uint32_t >(PixelWriteConverter->GetDataSize()) );
944 if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
946 ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );
949 Archive->Push(photInt);
950 Archive->Push(pixel);
958 * \brief Updates the File to write RGB data (as opposed to RAW data)
959 * (modifies, when necessary, photochromatic interpretation,
960 * samples per pixel, Planar configuration,
961 * bits allocated, bits stored, high bit -ACR 24 bits-
962 * Pixels element VR, pushes out the LUT, )
964 void FileHelper::SetWriteToRGB()
966 if ( FileInternal->GetNumberOfScalarComponents()==3 )
968 PixelReadConverter->BuildRGBImage();
970 DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
971 spp->SetString("3 "); // Don't drop trailing space
973 DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
974 planConfig->SetString("0 "); // Don't drop trailing space
976 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
977 photInt->SetString("RGB "); // Don't drop trailing space
979 if ( PixelReadConverter->GetRGB() )
981 PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
982 PixelReadConverter->GetRGBSize());
986 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
987 PixelReadConverter->GetRawSize());
990 std::string vr = "OB";
991 if ( FileInternal->GetBitsAllocated()>8 )
993 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
996 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
997 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
998 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
999 pixel->SetLength(PixelWriteConverter->GetDataSize());
1002 Archive->Push(planConfig);
1003 Archive->Push(photInt);
1004 Archive->Push(pixel);
1007 planConfig->Delete();
1012 Archive->Push(0x0028,0x1101);
1013 Archive->Push(0x0028,0x1102);
1014 Archive->Push(0x0028,0x1103);
1015 Archive->Push(0x0028,0x1201);
1016 Archive->Push(0x0028,0x1202);
1017 Archive->Push(0x0028,0x1203);
1019 // push out Palette Color Lookup Table UID, if any
1020 Archive->Push(0x0028,0x1199);
1022 // For old '24 Bits' ACR-NEMA
1023 // Thus, we have a RGB image and the bits allocated = 24 and
1024 // samples per pixels = 1 (in the read file)
1025 if ( FileInternal->GetBitsAllocated()==24 )
1027 DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
1028 bitsAlloc->SetString("8 ");
1030 DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
1031 bitsStored->SetString("8 ");
1033 DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
1034 highBit->SetString("7 ");
1036 Archive->Push(bitsAlloc);
1037 Archive->Push(bitsStored);
1038 Archive->Push(highBit);
1040 bitsAlloc->Delete();
1041 bitsStored->Delete();
1052 * \brief Restore the File write mode
1054 void FileHelper::RestoreWrite()
1056 Archive->Restore(0x0028,0x0002);
1057 Archive->Restore(0x0028,0x0004);
1059 Archive->Restore(0x0028,0x0006);
1060 Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1062 // For old ACR-NEMA (24 bits problem)
1063 Archive->Restore(0x0028,0x0100);
1064 Archive->Restore(0x0028,0x0101);
1065 Archive->Restore(0x0028,0x0102);
1068 Archive->Restore(0x0028,0x1101);
1069 Archive->Restore(0x0028,0x1102);
1070 Archive->Restore(0x0028,0x1103);
1071 Archive->Restore(0x0028,0x1201);
1072 Archive->Restore(0x0028,0x1202);
1073 Archive->Restore(0x0028,0x1203);
1075 // For the Palette Color Lookup Table UID
1076 Archive->Restore(0x0028,0x1203);
1078 // group 0002 may be pushed out for ACR-NEMA writting purposes
1079 Archive->Restore(0x0002,0x0000);
1080 Archive->Restore(0x0002,0x0001);
1081 Archive->Restore(0x0002,0x0002);
1082 Archive->Restore(0x0002,0x0003);
1083 Archive->Restore(0x0002,0x0010);
1084 Archive->Restore(0x0002,0x0012);
1085 Archive->Restore(0x0002,0x0013);
1086 Archive->Restore(0x0002,0x0016);
1087 Archive->Restore(0x0002,0x0100);
1088 Archive->Restore(0x0002,0x0102);
1093 * \brief Pushes out the whole group 0002
1094 * FIXME : better, set a flag to tell the writer not to write it ...
1095 * FIXME : method should probably have an other name !
1096 * SetWriteFileTypeToACR is NOT opposed to
1097 * SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1099 void FileHelper::SetWriteFileTypeToACR()
1101 Archive->Push(0x0002,0x0000);
1102 Archive->Push(0x0002,0x0001);
1103 Archive->Push(0x0002,0x0002);
1104 Archive->Push(0x0002,0x0003);
1105 Archive->Push(0x0002,0x0010);
1106 Archive->Push(0x0002,0x0012);
1107 Archive->Push(0x0002,0x0013);
1108 Archive->Push(0x0002,0x0016);
1109 Archive->Push(0x0002,0x0100);
1110 Archive->Push(0x0002,0x0102);
1114 * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1116 void FileHelper::SetWriteFileTypeToJPEG2000()
1118 std::string ts = Util::DicomString(
1119 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1121 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1129 * \brief Sets in the File the TransferSyntax to 'JPEG'
1131 void FileHelper::SetWriteFileTypeToJPEG()
1133 std::string ts = Util::DicomString(
1134 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGLosslessProcess14_1) );
1136 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1144 * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"
1146 void FileHelper::SetWriteFileTypeToExplicitVR()
1148 std::string ts = Util::DicomString(
1149 Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1151 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1158 * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"
1160 void FileHelper::SetWriteFileTypeToImplicitVR()
1162 std::string ts = Util::DicomString(
1163 Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1165 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1172 * \brief Set the Write not to Libido format
1174 void FileHelper::SetWriteToLibido()
1176 DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1177 DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1179 if ( oldRow && oldCol )
1181 std::string rows, columns;
1183 DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1184 DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1186 newRow->Copy(oldCol);
1187 newCol->Copy(oldRow);
1189 newRow->SetString(oldCol->GetString());
1190 newCol->SetString(oldRow->GetString());
1192 Archive->Push(newRow);
1193 Archive->Push(newCol);
1199 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1200 libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1201 Archive->Push(libidoCode);
1202 libidoCode->Delete();
1206 * \brief Set the Write not to No Libido format
1208 void FileHelper::SetWriteToNoLibido()
1210 DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1213 if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1215 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1216 libidoCode->SetString("");
1217 Archive->Push(libidoCode);
1218 libidoCode->Delete();
1224 * \brief Restore the Write format
1226 void FileHelper::RestoreWriteOfLibido()
1228 Archive->Restore(0x0028,0x0010);
1229 Archive->Restore(0x0028,0x0011);
1230 Archive->Restore(0x0008,0x0010);
1232 // Restore 'LibIDO-special' entries, if any
1233 Archive->Restore(0x0028,0x0015);
1234 Archive->Restore(0x0028,0x0016);
1235 Archive->Restore(0x0028,0x0017);
1236 Archive->Restore(0x0028,0x00199);
1240 * \brief Duplicates a DataEntry or creates it.
1241 * @param group Group number of the Entry
1242 * @param elem Element number of the Entry
1243 * @param vr Value Representation of the Entry
1244 * \return pointer to the new Bin Entry (NULL when creation failed).
1246 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1249 DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1252 if ( oldE && vr != GDCM_VRUNKNOWN )
1253 if ( oldE->GetVR() != vr )
1258 newE = DataEntry::New(group, elem, vr);
1263 newE = GetFile()->NewDataEntry(group, elem, vr);
1270 * \brief This method is called automatically, just before writting
1271 * in order to produce a 'True Dicom V3' image.
1273 * We cannot know *how* the user made the File :
1274 * (reading an old ACR-NEMA file or a not very clean DICOM file ...)
1275 * Just before writting :
1276 * - we check the Entries
1277 * - we create the mandatory entries if they are missing
1278 * - we modify the values if necessary
1279 * - we push the sensitive entries to the Archive
1280 * The writing process will restore the entries as they where before
1281 * entering FileHelper::CheckMandatoryElements, so the user will always
1282 * see the entries just as they were before he decided to write.
1285 * - Entries whose type is 1 are mandatory, with a mandatory value
1286 * - Entries whose type is 1c are mandatory-inside-a-Sequence,
1287 * with a mandatory value
1288 * - Entries whose type is 2 are mandatory, with an optional value
1289 * - Entries whose type is 2c are mandatory-inside-a-Sequence,
1290 * with an optional value
1291 * - Entries whose type is 3 are optional
1294 * - warn the user if we had to add some entries :
1295 * even if a mandatory entry is missing, we add it, with a default value
1296 * (we don't want to give up the writting process if user forgot to
1297 * specify Lena's Patient ID, for instance ...)
1298 * - read the whole PS 3.3 Part of DICOM (890 pages)
1299 * and write a *full* checker (probably one method per Modality ...)
1300 * Any contribution is welcome.
1301 * - write a user callable full checker, to allow post reading
1302 * and/or pre writting image consistency check.
1305 /* -------------------------------------------------------------------------------------
1306 To be moved to User's guide / WIKI ?
1308 We have to deal with 4 *very* different cases :
1309 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1311 -2) user modified the pixels of an existing image.
1313 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1315 -4) user modified/added some tags *without processing* the pixels (anonymization...)
1316 UNMODIFIED_PIXELS_IMAGE
1317 -Probabely some more to be added.
1318 --> Set it with FileHelper::SetContentType(int);
1320 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1323 0008 0012 Instance Creation Date
1324 0008 0013 Instance Creation Time
1325 0008 0018 SOP Instance UID
1326 are *always* created with the current values; user has *no* possible intervention on
1329 'Serie Instance UID'(0x0020,0x000e)
1330 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1331 created if it doesn't.
1332 The user is allowed to create his own Series/Studies,
1333 keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1335 The user shouldn't add any image to a 'Manufacturer Serie'
1336 but there is no way no to allow him to do that
1338 None of the 'shadow elements' are droped out.
1342 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1345 'Media Storage SOP Class UID' (0x0002,0x0002)
1346 'SOP Class UID' (0x0008,0x0016) are set to
1347 [Secondary Capture Image Storage]
1348 'Image Type' (0x0008,0x0008) is forced to "DERIVED\PRIMARY"
1349 Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1352 If 'SOP Class UID' exists in the native image ('true DICOM' image)
1353 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1354 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1355 whose value is the original 'SOP Class UID'
1356 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1357 whose value is the original 'SOP Class UID'
1359 3) TO DO : find a trick to allow user to pass to the writter the list of the Dicom images
1360 or the Series, (or the Study ?) he used to created his image
1361 (MIP, MPR, cartography image, ...)
1362 These info should be stored (?)
1363 0008 1110 SQ 1 Referenced Study Sequence
1364 0008 1115 SQ 1 Referenced Series Sequence
1365 0008 1140 SQ 1 Referenced Image Sequence
1367 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1368 'Media Storage SOP Class UID' (0x0002,0x0002)
1369 'SOP Class UID' (0x0008,0x0016)
1370 'Image Type' (0x0008,0x0008)
1371 'Conversion Type' (0x0008,0x0064)
1374 Bellow follows the full description (hope so !) of the consistency checks performed
1375 by gdcm::FileHelper::CheckMandatoryElements()
1378 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1379 -->'SOP Class UID' (0x0008,0x0016) are defaulted to
1380 [Secondary Capture Image Storage]
1381 --> 'Image Type' (0x0008,0x0008)
1382 is forced to "DERIVED\PRIMARY"
1383 (The written image is no longer an 'ORIGINAL' one)
1384 Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1386 --> Conversion Type (0x0008,0x0064)
1387 is defaulted to 'SYN' (Synthetic Image)
1388 when *he* knows he created his own image ex nihilo
1390 --> 'Modality' (0x0008,0x0060)
1391 is defaulted to "OT" (other) if missing.
1392 (a fully user created image belongs to *no* modality)
1394 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1395 --> 'Implementation Class UID' (0x0002,0x0012)
1396 are automatically generated; no user intervention possible
1398 --> 'Serie Instance UID'(0x0020,0x000e)
1399 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1400 created if it doesn't.
1401 The user is allowed to create his own Series/Studies,
1402 keeping the same 'Serie Instance UID' / 'Study Instance UID'
1405 The user shouldn't add any image to a 'Manufacturer Serie'
1406 but there is no way no to allowed him to do that
1408 --> If 'SOP Class UID' exists in the native image ('true DICOM' image)
1409 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1411 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1412 whose value is the original 'SOP Class UID'
1413 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1414 whose value is the original 'SOP Class UID'
1416 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1417 --> Pixel Spacing (0x0028,0x0030) is defaulted to "1.0\1.0"
1418 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1420 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1422 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1424 --> Study Date, Study Time are defaulted to current Date and Time
1425 (they remain unchanged if they exist)
1427 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from
1428 Image Orientation (Patient) : (0020|0037) or from
1429 Image Orientation (RET) : (0020 0035)
1431 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1432 are created, with empty value if there are missing.
1434 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1435 are defaulted with a 'gdcm' value.
1437 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1438 --> Referring Physician's Name (Type 2)
1439 are created, with empty value if there are missing.
1441 -------------------------------------------------------------------------------------*/
1443 void FileHelper::CheckMandatoryElements()
1445 std::string sop = Util::CreateUniqueUID();
1447 // --------------------- For Meta Elements ---------------------
1448 // just to remember : 'official' 0002 group
1449 if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1451 // Group 000002 (Meta Elements) already pushed out
1453 //0002 0000 UL 1 Meta Group Length
1454 //0002 0001 OB 1 File Meta Information Version
1455 //0002 0002 UI 1 Media Storage SOP Class UID
1456 //0002 0003 UI 1 Media Storage SOP Instance UID
1457 //0002 0010 UI 1 Transfer Syntax UID
1458 //0002 0012 UI 1 Implementation Class UID
1459 //0002 0013 SH 1 Implementation Version Name
1460 //0002 0016 AE 1 Source Application Entity Title
1461 //0002 0100 UI 1 Private Information Creator
1462 //0002 0102 OB 1 Private Information
1464 // Push out 'ACR-NEMA-special' entries, if any
1465 Archive->Push(0x0008,0x0001); // Length to End
1466 Archive->Push(0x0008,0x0010); // Recognition Code
1467 Archive->Push(0x0028,0x0005); // Image Dimension
1469 // Create them if not found
1470 // Always modify the value
1471 // Push the entries to the archive.
1472 CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1474 DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1475 e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1477 e_0002_0001->SetLength(2);
1478 Archive->Push(e_0002_0001);
1479 e_0002_0001->Delete();
1481 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1483 // we keep the original 'Media Storage SOP Class UID', we default it if missing
1484 CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1488 // It's *not* an image comming straight from a source. We force
1489 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1490 CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1493 // 'Media Storage SOP Instance UID'
1494 CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1496 // 'Implementation Class UID'
1497 // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1498 // seems to be Root UID + 4 digits (?)
1499 CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1501 // 'Implementation Version Name'
1502 std::string version = "GDCM ";
1503 version += Util::GetVersion();
1504 CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1507 // --------------------- For DataSet ---------------------
1509 // check whether 0018|0015 [CS] [Body Part Examined] value is UPPER CASE
1510 // (avoid dciodvfy to complain!)
1511 DataEntry *e_0018_0015 = FileInternal->GetDataEntry(0x0018, 0x0015);
1514 std::string bodyPartExamined = e_0018_0015->GetString();
1515 std::transform(bodyPartExamined.begin(), bodyPartExamined.end(), bodyPartExamined.begin(),
1516 (int(*)(int)) toupper);
1517 CopyMandatoryEntry(0x0018,0x0015,bodyPartExamined,"CS");
1520 if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1522 // If 'SOP Class UID' and 'SOP Instance UID' exist ('true DICOM' image)
1523 // we create the 'Source Image Sequence' SeqEntry
1524 // to hold informations about the Source Image
1526 // 'SOP Instance UID'
1527 DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1529 DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1530 if ( e_0008_0016 && e_0008_0018)
1532 // Create 'Source Image Sequence' SeqEntry
1533 SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1534 SQItem *sqi = SQItem::New(1);
1536 // create 'Referenced SOP Class UID' from 'SOP Class UID'
1538 DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1539 e_0008_1150->SetString( e_0008_0016->GetString());
1540 sqi->AddEntry(e_0008_1150);
1541 e_0008_1150->Delete();
1543 // create 'Referenced SOP Instance UID' from 'SOP Instance UID'
1544 // DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1546 DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1547 e_0008_1155->SetString( e_0008_0018->GetString());
1548 sqi->AddEntry(e_0008_1155);
1549 e_0008_1155->Delete();
1551 sis->AddSQItem(sqi,1);
1554 // temporarily replaces any previous 'Source Image Sequence'
1557 // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1559 if ( ContentType == FILTERED_IMAGE) // the user *knows* he just modified the pixels
1561 DataEntry *e_0008_0008 = FileInternal->GetDataEntry(0x0008, 0x0008);
1564 std::string imageType = e_0008_0008->GetString();
1565 std::string::size_type p = imageType.find("ORIGINAL");
1566 if (p == 0) // image is ORIGINAL one
1568 // the image is no longer an 'Original' one
1569 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1571 // if Image Type was not ORIGINAL\..., we keep it.
1573 else // 0008_0008 was missing, wee add it.
1575 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1581 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1583 // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1584 CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");
1588 // It's *not* an image comming straight from a source. We force
1589 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1590 CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");
1593 Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1594 // Push out 'LibIDO-special' entries, if any
1595 Archive->Push(0x0028,0x0015);
1596 Archive->Push(0x0028,0x0016);
1597 Archive->Push(0x0028,0x0017);
1598 Archive->Push(0x0028,0x0198); // very old versions
1599 Archive->Push(0x0028,0x0199);
1601 // Replace deprecated 0028 0012 US Planes
1602 // by new 0028 0008 IS Number of Frames
1604 ///\todo : find if there is a rule!
1605 DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1608 CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1609 Archive->Push(0x0028,0x0012);
1612 // Deal with the pb of (Bits Stored = 12)
1613 // - we're gonna write the image as Bits Stored = 16
1614 if ( FileInternal->GetEntryString(0x0028,0x0100) == "12")
1616 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1619 // Check if user wasn't drunk ;-)
1621 std::ostringstream s;
1622 // check 'Bits Allocated' vs decent values
1623 int nbBitsAllocated = FileInternal->GetBitsAllocated();
1624 if ( (nbBitsAllocated == 0 || nbBitsAllocated > 32)
1625 || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1627 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1628 gdcmWarningMacro("(0028,0100) changed from "
1629 << nbBitsAllocated << " to 16 for consistency purpose");
1630 nbBitsAllocated = 16;
1632 // check 'Bits Stored' vs 'Bits Allocated'
1633 int nbBitsStored = FileInternal->GetBitsStored();
1634 if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1637 s << nbBitsAllocated;
1638 CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1639 gdcmWarningMacro("(0028,0101) changed from "
1640 << nbBitsStored << " to " << nbBitsAllocated
1641 << " for consistency purpose" );
1642 nbBitsStored = nbBitsAllocated;
1644 // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1645 int highBitPosition = FileInternal->GetHighBitPosition();
1646 if ( highBitPosition == 0 ||
1647 highBitPosition > nbBitsAllocated-1 ||
1648 highBitPosition < nbBitsStored-1 )
1651 s << nbBitsStored - 1;
1652 CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1653 gdcmWarningMacro("(0028,0102) changed from "
1654 << highBitPosition << " to " << nbBitsAllocated-1
1655 << " for consistency purpose");
1658 // check Pixel Representation (default it as 0 -unsigned-)
1660 DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1663 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1664 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1668 int sign = (int)e_0028_0103->GetValue(0);
1669 if (sign !=1 && sign !=0)
1671 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1672 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1676 std::string pixelAspectRatio = FileInternal->GetEntryString(0x0028,0x0034);
1677 if ( pixelAspectRatio == GDCM_UNFOUND ) // avoid conflict with pixelSpacing !
1679 std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1680 if ( pixelSpacing == GDCM_UNFOUND )
1682 pixelSpacing = "1.0\\1.0";
1683 // if missing, Pixel Spacing forced to "1.0\1.0"
1684 CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1687 // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1688 // --> This one is the *legal* one !
1689 if ( ContentType != USER_OWN_IMAGE)
1690 // we write it only when we are *sure* the image comes from
1691 // an imager (see also 0008,0x0064)
1692 CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1695 ///Exact meaning of RETired fields
1697 // See page 73 of ACR-NEMA_300-1988.pdf !
1699 // 0020,0020 : Patient Orientation :
1700 Patient direction of the first row and
1701 column of the images. The first entry id the direction of the raws, given by the
1702 direction of the last pixel in the first row from the first pixel in tha row.
1703 the second entry is the direction of the columns, given by the direction of the
1704 last pixel in the first column from the first pixel in that column.
1705 L : Left, F : Feet, A : Anterior, P : Posterior.
1706 Up to 3 letters can be used in combination to indicate oblique planes.
1708 //0020,0030 Image Position (RET)
1709 x,y,z coordinates im mm of the first pixel in the image
1711 // 0020,0035 Image Orientation (RET)
1712 Direction cosines of the R axis of the image system with respect to the
1713 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1714 the image system with respect to the same axes
1716 //0020,0050 Location
1717 An image location reference, standard for the modality (such as CT bed position),
1718 used to indicate position. Calculation of position for other purposes
1719 is only from (0020,0030) and (0020,0035)
1723 // if imagePositionPatient not found, default it with imagePositionRet, if any
1724 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1726 std::string imagePositionRet = FileInternal->GetEntryString(0x0020,0x0030);
1727 std::string imageOrientationRet = FileInternal->GetEntryString(0x0020,0x0035);
1728 std::string imagePositionPatient = FileInternal->GetEntryString(0x0020,0x0032);
1729 std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1731 if( imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1732 && imagePositionRet != GDCM_UNFOUND && imageOrientationRet != GDCM_UNFOUND)
1734 CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1735 Archive->Push(0x0020,0x0030);
1736 CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1737 Archive->Push(0x0020,0x0035);
1741 // Samples Per Pixel (type 1) : default to grayscale
1742 CheckMandatoryEntry(0x0028,0x0002,"1","US");
1744 // --- Check UID-related Entries ---
1746 // At the end, not to overwrite the original ones,
1747 // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
1748 // 'SOP Instance UID'
1749 CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1751 if ( ContentType == USER_OWN_IMAGE)
1753 gdcmDebugMacro( "USER_OWN_IMAGE (2)");
1755 // Other possible values are :
1756 // See PS 3.3, Page 408
1758 // DV = Digitized Video
1759 // DI = Digital Interface
1760 // DF = Digitized Film
1761 // WSD = Workstation
1762 // SD = Scanned Document
1763 // SI = Scanned Image
1765 // SYN = Synthetic Image
1767 CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1770 if ( ContentType == CREATED_IMAGE)
1772 /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1777 // ---- The user will never have to take any action on the following ----
1779 // new value for 'SOP Instance UID'
1780 //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1782 // Instance Creation Date
1783 const std::string &date = Util::GetCurrentDate();
1784 CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1786 // Instance Creation Time
1787 const std::string &time = Util::GetCurrentTime();
1788 CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1791 CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1793 CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1796 //CopyMandatoryEntry(0x0008,0x0050,"");
1797 CheckMandatoryEntry(0x0008,0x0050,"","SH");
1800 // ----- Add Mandatory Entries if missing ---
1801 // Entries whose type is 1 are mandatory, with a mandatory value
1802 // Entries whose type is 1c are mandatory-inside-a-Sequence,
1803 // with a mandatory value
1804 // Entries whose type is 2 are mandatory, with an optional value
1805 // Entries whose type is 2c are mandatory-inside-a-Sequence,
1806 // with an optional value
1807 // Entries whose type is 3 are optional
1809 // 'Study Instance UID'
1810 // Keep the value if exists
1811 // The user is allowed to create his own Study,
1812 // keeping the same 'Study Instance UID' for various images
1813 // The user may add images to a 'Manufacturer Study',
1814 // adding new Series to an already existing Study
1815 CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1817 // 'Serie Instance UID'
1818 // Keep the value if exists
1819 // The user is allowed to create his own Series,
1820 // keeping the same 'Serie Instance UID' for various images
1821 // The user shouldn't add any image to a 'Manufacturer Serie'
1822 // but there is no way no to prevent him for doing that
1823 CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1826 CheckMandatoryEntry(0x0020,0x0010,"","SH");
1829 CheckMandatoryEntry(0x0020,0x0011,"","IS");
1832 CheckMandatoryEntry(0x0020,0x0013,"","IS");
1834 // Patient Orientation
1835 // Can be computed from (0020|0037) : Image Orientation (Patient)
1836 GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1837 std::string ori = o->GetOrientation ( FileInternal );
1839 if (ori != "\\" && ori != GDCM_UNFOUND)
1840 CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1842 CheckMandatoryEntry(0x0020,0x0020,"","CS");
1844 // Default Patient Position to HFS
1845 CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1847 // Modality : if missing we set it to 'OTher'
1848 CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1850 // Manufacturer : if missing we set it to 'GDCM Factory'
1851 CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1853 // Institution Name : if missing we set it to 'GDCM Hospital'
1854 CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1856 // Patient's Name : if missing, we set it to 'GDCM^Patient'
1857 CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1859 // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1860 CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1862 // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1863 CheckMandatoryEntry(0x0010,0x0030,"","DA");
1865 // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1866 CheckMandatoryEntry(0x0010,0x0040,"","CS");
1868 // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1869 CheckMandatoryEntry(0x0008,0x0090,"","PN");
1872 // Deal with element 0x0000 (group length) of each group.
1873 // First stage : get all the different Groups
1876 DocEntry *d = FileInternal->GetFirstEntry();
1879 grHT[d->GetGroup()] = 0;
1880 d=FileInternal->GetNextEntry();
1882 // Second stage : add the missing ones (if any)
1883 for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)
1885 CheckMandatoryEntry(it->first, 0x0000, "0");
1887 // Third stage : update all 'zero level' groups length
1891 if (PhotometricInterpretation == 1)
1896 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1898 DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1901 //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1902 entry = DataEntry::New(group,elem,vr);
1903 entry->SetString(value);
1904 Archive->Push(entry);
1909 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
1910 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1912 //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1913 DataEntry *entry = DataEntry::New(group,elem,vr);
1914 entry->SetString(value);
1915 Archive->Push(entry);
1919 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1921 DataEntry *entry = CopyDataEntry(group,elem,vr);
1922 entry->SetString(value);
1923 Archive->Push(entry);
1928 * \brief Restore in the File the initial group 0002
1930 void FileHelper::RestoreWriteMandatory()
1932 // group 0002 may be pushed out for ACR-NEMA writting purposes
1933 Archive->Restore(0x0002,0x0000);
1934 Archive->Restore(0x0002,0x0001);
1935 Archive->Restore(0x0002,0x0002);
1936 Archive->Restore(0x0002,0x0003);
1937 Archive->Restore(0x0002,0x0010);
1938 Archive->Restore(0x0002,0x0012);
1939 Archive->Restore(0x0002,0x0013);
1940 Archive->Restore(0x0002,0x0016);
1941 Archive->Restore(0x0002,0x0100);
1942 Archive->Restore(0x0002,0x0102);
1944 // FIXME : Check if none is missing !
1946 Archive->Restore(0x0008,0x0012);
1947 Archive->Restore(0x0008,0x0013);
1948 Archive->Restore(0x0008,0x0016);
1949 Archive->Restore(0x0008,0x0018);
1950 Archive->Restore(0x0008,0x0060);
1951 Archive->Restore(0x0008,0x0070);
1952 Archive->Restore(0x0008,0x0080);
1953 Archive->Restore(0x0008,0x0090);
1954 Archive->Restore(0x0008,0x2112);
1956 Archive->Restore(0x0010,0x0010);
1957 Archive->Restore(0x0010,0x0030);
1958 Archive->Restore(0x0010,0x0040);
1960 Archive->Restore(0x0020,0x000d);
1961 Archive->Restore(0x0020,0x000e);
1965 * \brief CallStartMethod
1967 void FileHelper::CallStartMethod()
1971 CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1975 * \brief CallProgressMethod
1977 void FileHelper::CallProgressMethod()
1979 CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1983 * \brief CallEndMethod
1985 void FileHelper::CallEndMethod()
1988 CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1991 //-----------------------------------------------------------------------------
1994 * \brief Factorization for various forms of constructors.
1996 void FileHelper::Initialize()
1999 ContentType = USER_OWN_IMAGE;
2001 WriteMode = WMODE_RAW;
2002 WriteType = ExplicitVR;
2004 PhotometricInterpretation = 2; // Black = 0
2006 PixelReadConverter = new PixelReadConvert;
2007 PixelWriteConverter = new PixelWriteConvert;
2008 Archive = new DocEntryArchive( FileInternal );
2010 KeepOverlays = false;
2014 * \brief Reads/[decompresses] the pixels,
2015 * *without* making RGB from Palette Colors
2016 * @return the pixels area, whatever its type
2017 * (uint8_t is just for prototyping : feel free to Cast it)
2019 uint8_t *FileHelper::GetRaw()
2021 PixelReadConverter->SetUserFunction( UserFunction );
2023 uint8_t *raw = PixelReadConverter->GetRaw();
2026 // The Raw image migth not be loaded yet:
2027 std::ifstream *fp = FileInternal->OpenFile();
2028 PixelReadConverter->ReadAndDecompressPixelData( fp );
2030 FileInternal->CloseFile();
2032 raw = PixelReadConverter->GetRaw();
2035 gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
2043 * \brief Deal with Grey levels i.e. re-arange them
2044 * to have low values = dark, high values = bright
2046 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
2048 uint32_t i; // to please M$VC6
2051 // Number of Bits Allocated for storing a Pixel is defaulted to 16
2052 // when absent from the file.
2053 int bitsAllocated = FileInternal->GetBitsAllocated();
2054 if ( bitsAllocated == 0 )
2059 else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2063 // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2064 // when absent from the file.
2065 int bitsStored = FileInternal->GetBitsStored();
2066 if ( bitsStored == 0 )
2068 bitsStored = bitsAllocated;
2071 if (!FileInternal->IsSignedPixelData())
2073 if ( bitsAllocated == 8 )
2075 uint8_t *deb = (uint8_t *)raw;
2076 for (i=0; i<rawSize; i++)
2084 if ( bitsAllocated == 16 )
2087 for (j=0; j<bitsStored-1; j++)
2089 mask = (mask << 1) +1; // will be fff when BitsStored=12
2092 uint16_t *deb = (uint16_t *)raw;
2093 for (i=0; i<rawSize/2; i++)
2103 if ( bitsAllocated == 8 )
2105 uint8_t smask8 = 255;
2106 uint8_t *deb = (uint8_t *)raw;
2107 for (i=0; i<rawSize; i++)
2109 *deb = smask8 - *deb;
2114 if ( bitsAllocated == 16 )
2116 uint16_t smask16 = 65535;
2117 uint16_t *deb = (uint16_t *)raw;
2118 for (i=0; i<rawSize/2; i++)
2120 *deb = smask16 - *deb;
2128 //-----------------------------------------------------------------------------
2130 * \brief Prints the FileInternal + info on PixelReadConvertor
2131 * @param os ostream we want to print in
2132 * @param indent (unused)
2134 void FileHelper::Print(std::ostream &os, std::string const &)
2136 FileInternal->SetPrintLevel(PrintLevel);
2137 FileInternal->Print(os);
2139 if ( FileInternal->IsReadable() )
2141 PixelReadConverter->SetPrintLevel(PrintLevel);
2142 PixelReadConverter->Print(os);
2146 //-----------------------------------------------------------------------------
2147 } // end namespace gdcm
2150 /* Probabely something to be added to use Rescale Slope/Intercept
2151 Have a look at ITK code !
2153 // Internal function to rescale pixel according to Rescale Slope/Intercept
2154 template<class TBuffer, class TSource>
2155 void RescaleFunction(TBuffer* buffer, TSource *source,
2156 double slope, double intercept, size_t size)
2158 size /= sizeof(TSource);
2160 if (slope != 1.0 && intercept != 0.0)
2162 // Duff's device. Instead of this code:
2164 // for(unsigned int i=0; i<size; i++)
2166 // buffer[i] = (TBuffer)(source[i]*slope + intercept);
2169 // use Duff's device which exploits "fall through"
2170 register size_t n = (size + 7) / 8;
2173 case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2174 case 7: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2175 case 6: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2176 case 5: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2177 case 4: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2178 case 3: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2179 case 2: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2180 case 1: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2184 else if (slope == 1.0 && intercept != 0.0)
2186 // Duff's device. Instead of this code:
2188 // for(unsigned int i=0; i<size; i++)
2190 // buffer[i] = (TBuffer)(source[i] + intercept);
2193 // use Duff's device which exploits "fall through"
2194 register size_t n = (size + 7) / 8;
2197 case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2198 case 7: *buffer++ = (TBuffer)(*source++ + intercept);
2199 case 6: *buffer++ = (TBuffer)(*source++ + intercept);
2200 case 5: *buffer++ = (TBuffer)(*source++ + intercept);
2201 case 4: *buffer++ = (TBuffer)(*source++ + intercept);
2202 case 3: *buffer++ = (TBuffer)(*source++ + intercept);
2203 case 2: *buffer++ = (TBuffer)(*source++ + intercept);
2204 case 1: *buffer++ = (TBuffer)(*source++ + intercept);
2208 else if (slope != 1.0 && intercept == 0.0)
2210 // Duff's device. Instead of this code:
2212 // for(unsigned int i=0; i<size; i++)
2214 // buffer[i] = (TBuffer)(source[i]*slope);
2217 // use Duff's device which exploits "fall through"
2218 register size_t n = (size + 7) / 8;
2221 case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2222 case 7: *buffer++ = (TBuffer)((*source++)*slope);
2223 case 6: *buffer++ = (TBuffer)((*source++)*slope);
2224 case 5: *buffer++ = (TBuffer)((*source++)*slope);
2225 case 4: *buffer++ = (TBuffer)((*source++)*slope);
2226 case 3: *buffer++ = (TBuffer)((*source++)*slope);
2227 case 2: *buffer++ = (TBuffer)((*source++)*slope);
2228 case 1: *buffer++ = (TBuffer)((*source++)*slope);
2234 // Duff's device. Instead of this code:
2236 // for(unsigned int i=0; i<size; i++)
2238 // buffer[i] = (TBuffer)(source[i]);
2241 // use Duff's device which exploits "fall through"
2242 register size_t n = (size + 7) / 8;
2245 case 0: do { *buffer++ = (TBuffer)(*source++);
2246 case 7: *buffer++ = (TBuffer)(*source++);
2247 case 6: *buffer++ = (TBuffer)(*source++);
2248 case 5: *buffer++ = (TBuffer)(*source++);
2249 case 4: *buffer++ = (TBuffer)(*source++);
2250 case 3: *buffer++ = (TBuffer)(*source++);
2251 case 2: *buffer++ = (TBuffer)(*source++);
2252 case 1: *buffer++ = (TBuffer)(*source++);
2259 template<class TSource>
2260 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2261 void* buffer, TSource *source,
2262 double slope, double intercept, size_t size)
2266 case ImageIOBase::UCHAR:
2267 RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2269 case ImageIOBase::CHAR:
2270 RescaleFunction( (char *)buffer, source, slope, intercept, size);
2272 case ImageIOBase::USHORT:
2273 RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2275 case ImageIOBase::SHORT:
2276 RescaleFunction( (short *)buffer, source, slope, intercept, size);
2278 case ImageIOBase::UINT:
2279 RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2281 case ImageIOBase::INT:
2282 RescaleFunction( (int *)buffer, source, slope, intercept, size);
2284 case ImageIOBase::FLOAT:
2285 RescaleFunction( (float *)buffer, source, slope, intercept, size);
2287 case ImageIOBase::DOUBLE:
2288 RescaleFunction( (double *)buffer, source, slope, intercept, size);
2291 ::itk::OStringStream message;
2292 message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2293 ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);