1 /*=========================================================================
4 Module: $RCSfile: gdcmFileHelper.cxx,v $
7 Date: $Date: 2007/10/25 07:52:59 $
8 Version: $Revision: 1.136 $
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"
39 #include <algorithm> // for transform?
41 #if defined(__BORLANDC__)
42 #include <mem.h> // for memset
43 #include <ctype.h> //for toupper
50 // ----------------------------- WARNING -------------------------
52 These lines will be moved to the document-to-be 'User's Guide'
54 // To read an image, user needs a gdcm::File
55 gdcm::File *f = new gdcm::File(fileName);
57 // user may also decide he doesn't want to load some parts of the header
58 gdcm::File *f = new gdcm::File();
59 f->SetFileName(fileName);
60 f->SetLoadMode(LD_NOSEQ); // or
61 f->SetLoadMode(LD_NOSHADOW); // or
62 f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW); // or
63 f->SetLoadMode(LD_NOSHADOWSEQ);
66 // To decide whether it's an 'image of interest for him, or not,
67 // user can now check some values
68 std::string v = f->GetEntryValue(groupNb,ElementNb);
70 // to get the pixels, user needs a gdcm::FileHelper
71 gdcm::FileHelper *fh = new gdcm::FileHelper(f);
73 // user may ask not to convert Palette (if any) to RGB
74 uint8_t *pixels = fh->GetImageDataRaw();
75 int imageLength = fh->GetImageDataRawSize();
77 // He can now use the pixels, create a new image, ...
78 uint8_t *userPixels = ...
80 //To re-write the image, user re-uses the gdcm::FileHelper
81 gdcm::File *fh = new gdcm::FileHelper();
83 fh->SetTypeToRaw(); // Even if it was possible to convert Palette to RGB
86 // If user wants to write the file as MONOCHROME1 (0=white)
87 fh->SetPhotometricInterpretationToMonochrome1();
89 fh->SetWriteTypeToDcmExpl(); // he wants Explicit Value Representation
90 // Little Endian is the default,
91 // bigendian not supported for writting
92 (-->SetWriteType(ExplicitVR);)
93 -->WriteType = ExplicitVR;
94 fh->SetWriteTypeToJPEG(); // lossless compression
95 fh->SetWriteTypeToJPEG2000(); // lossless compression
97 fh->SetImageData( userPixels, userPixelsLength);
99 fh->SetUserData( userPixels, userPixelsLength); // this one performs compression, when required
101 fh->Write(newFileName); // overwrites the file, if any
106 These lines will be moved to the document-to-be 'Developer's Guide'
108 WriteMode : WMODE_RAW / WMODE_RGB
109 WriteType : ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO
110 PhotometricInterpretation : MONOCHROME2 (0=black), MONOCHROME2 (0=white)
112 fh->SetImageData( userPixels, userPixelsLength);
114 fh->SetUserData( userPixels, userPixelsLength);
115 PixelWriteConverter->SetUserData(inData, expectedSize);
118 fh->SetWriteMode(WMODE_RAW / WMODE_RGB)
120 fh->SetWriteType( ImplicitVR/ExplicitVR/ACR/ACR_LIBIDO/JPEG/JPEG2000)
122 fh->Write(newFileName);
123 CheckMandatoryElements(); // Checks existing ones / Add missing ones
124 Fix VR if unknown elements
125 SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR(); /
126 SetWriteFileTypeToACR() / SetWriteFileTypeToJPEG() / SetWriteFileTypeToJ2K()
127 (Modifies TransferSyntax if any; Pushes to the Archives old one)
128 SetWriteToRaw(); / SetWriteToRGB();
129 (Modifies and pushes to the Archive, when necessary : photochr. interp.,
130 samples per pixel, Planar configuration,
131 bits allocated, bits stored, high bit -ACR 24 bits-
132 Pixels element VR, pushes out the LUT )
134 Sets Photometric Interpretation
135 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
136 Sets VR, BinArea, Length for PixelData
139 Archive->Push(photInt);
140 Archive->Push(pixel);
144 if NumberOfScalarComponents==1
145 SetWriteToRaw(); return;
146 PixelReadConverter->BuildRGBImage()
147 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
148 Archives spp, planConfig,photInt, pixel
150 CheckWriteIntegrity();
151 (checks user given pixels length)
152 FileInternal->Write(fileName,WriteType)
153 fp = opens file(fileName); // out|binary
154 ComputeGroup0002Length( );
155 Document::WriteContent(fp, writetype);
156 writes Dicom File Preamble not ACR-NEMA
157 ElementSet::WriteContent(fp, writetype);
158 writes recursively all DataElements
160 (moves back to the gdcm::File all the archived elements)
166 namespace GDCM_NAME_SPACE
168 typedef std::map<uint16_t, int> GroupHT; // Hash Table
169 //-------------------------------------------------------------------------
170 // Constructor / Destructor
172 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
173 * file (gdcm::File only deals with the ... header)
174 * Opens (in read only and when possible) an existing file and checks
175 * for DICOM compliance. Returns NULL on failure.
176 * It will be up to the user to load the pixels into memory
177 * ( GetImageDataSize() + GetImageData() methods)
178 * \note the in-memory representation of all available tags found in
179 * the DICOM header is post-poned to first header information access.
180 * This avoid a double parsing of public part of the header when
181 * one sets an a posteriori shadow dictionary (efficiency can be
182 * seen as a side effect).
184 FileHelper::FileHelper( )
186 FileInternal = File::New( );
191 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
192 * file (File only deals with the ... header)
193 * Opens (in read only and when possible) an existing file and checks
194 * for DICOM compliance. Returns NULL on failure.
195 * It will be up to the user to load the pixels into memory
196 * ( GetImageDataSize() + GetImageData() methods)
197 * \note the in-memory representation of all available tags found in
198 * the DICOM header is post-poned to first header information access.
199 * This avoid a double parsing of public part of the header when
200 * user sets an a posteriori shadow dictionary (efficiency can be
201 * seen as a side effect).
202 * @param header already built Header
204 FileHelper::FileHelper(File *header)
206 gdcmAssertMacro(header);
208 FileInternal = header;
209 FileInternal->Register();
211 if ( FileInternal->IsReadable() )
213 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
218 * \brief canonical destructor
219 * \note If the header (gdcm::File) was created by the FileHelper constructor,
220 * it is destroyed by the FileHelper
222 FileHelper::~FileHelper()
224 if ( PixelReadConverter )
226 delete PixelReadConverter;
228 if ( PixelWriteConverter )
230 delete PixelWriteConverter;
237 FileInternal->Unregister();
240 //-----------------------------------------------------------------------------
244 * \brief Sets the LoadMode of the internal gdcm::File as a boolean string.
245 * NO_SEQ, NO_SHADOW, NO_SHADOWSEQ ... (nothing more, right now)
246 * WARNING : before using NO_SHADOW, be sure *all* your files
247 * contain accurate values in the 0x0000 element (if any)
248 * of *each* Shadow Group. The parser will fail if the size is wrong !
249 * @param loadMode Load mode to be used
251 void FileHelper::SetLoadMode(int loadMode)
253 GetFile()->SetLoadMode( loadMode );
256 * \brief Sets the LoadMode of the internal gdcm::File
257 * @param fileName name of the file to be open
259 void FileHelper::SetFileName(std::string const &fileName)
261 FileInternal->SetFileName( fileName );
266 * @return false if file cannot be open or no swap info was found,
267 * or no tag was found.
269 bool FileHelper::Load()
271 if ( !FileInternal->Load() )
274 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
279 * \brief Accesses an existing DataEntry through its (group, element)
280 * and modifies its content with the given value.
281 * @param content new value (string) to substitute with
282 * @param group group number of the Dicom Element to modify
283 * @param elem element number of the Dicom Element to modify
284 * \return false if DataEntry not found
286 bool FileHelper::SetEntryString(std::string const &content,
287 uint16_t group, uint16_t elem)
289 return FileInternal->SetEntryString(content, group, elem);
294 * \brief Accesses an existing DataEntry through its (group, element)
295 * and modifies its content with the given value.
296 * @param content new value (void* -> uint8_t*) to substitute with
297 * @param lgth new value length
298 * @param group group number of the Dicom Element to modify
299 * @param elem element number of the Dicom Element to modify
300 * \return false if DataEntry not found
302 bool FileHelper::SetEntryBinArea(uint8_t *content, int lgth,
303 uint16_t group, uint16_t elem)
305 return FileInternal->SetEntryBinArea(content, lgth, group, elem);
309 * \brief Modifies the value of a given DataEntry when it exists.
310 * Creates it with the given value when unexistant.
311 * @param content (string) value to be set
312 * @param group Group number of the Entry
313 * @param elem Element number of the Entry
314 * @param vr Value Representation of the DataElement to be inserted
315 * \return pointer to the modified/created DataEntry (NULL when creation
318 DataEntry *FileHelper::InsertEntryString(std::string const &content,
319 uint16_t group, uint16_t elem,
322 return FileInternal->InsertEntryString(content, group, elem, vr);
326 * \brief Modifies the value of a given DataEntry when it exists.
327 * Creates it with the given value when unexistant.
328 * A copy of the binArea is made to be kept in the Document.
329 * @param binArea (binary) value to be set
330 * @param lgth new value length
331 * @param group Group number of the Entry
332 * @param elem Element number of the Entry
333 * @param vr Value Representation of the DataElement to be inserted
334 * \return pointer to the modified/created DataEntry (NULL when creation
337 DataEntry *FileHelper::InsertEntryBinArea(uint8_t *binArea, int lgth,
338 uint16_t group, uint16_t elem,
341 return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem, vr);
345 * \brief Adds an empty SeqEntry
346 * (remove any existing entry with same group,elem)
347 * @param group Group number of the Entry
348 * @param elem Element number of the Entry
349 * \return pointer to the created SeqEntry (NULL when creation
352 SeqEntry *FileHelper::InsertSeqEntry(uint16_t group, uint16_t elem)
354 return FileInternal->InsertSeqEntry(group, elem);
358 * \brief Get the size of the image data
359 * If the image can be RGB (with a lut or by default), the size
360 * corresponds to the RGB image
361 * (use GetImageDataRawSize if you want to be sure to get *only*
362 * the size of the pixels)
363 * @return The image size
365 size_t FileHelper::GetImageDataSize()
367 if ( PixelWriteConverter->GetUserData() )
369 return PixelWriteConverter->GetUserDataSize();
371 return PixelReadConverter->GetRGBSize();
375 * \brief Get the size of the image data.
376 * If the image could be converted to RGB using a LUT,
377 * this transformation is not taken into account by GetImageDataRawSize
378 * (use GetImageDataSize if you wish)
379 * @return The raw image size
381 size_t FileHelper::GetImageDataRawSize()
383 if ( PixelWriteConverter->GetUserData() )
385 return PixelWriteConverter->GetUserDataSize();
387 return PixelReadConverter->GetRawSize();
391 * \brief brings pixels into memory :
392 * - Allocates necessary memory,
393 * - Reads the pixels from disk (uncompress if necessary),
394 * - Transforms YBR pixels, if any, into RGB pixels,
395 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
396 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
397 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
398 * @return Pointer to newly allocated pixel data.
399 * (uint8_t is just for prototyping. feel free to cast)
400 * NULL if alloc fails
402 uint8_t *FileHelper::GetImageData()
404 if ( PixelWriteConverter->GetUserData() )
406 return PixelWriteConverter->GetUserData();
411 // If the decompression failed nothing can be done.
415 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
417 return PixelReadConverter->GetRGB();
421 // When no LUT or LUT conversion fails, return the Raw
422 return PixelReadConverter->GetRaw();
427 * \brief brings pixels into memory :
428 * - Allocates necessary memory,
429 * - Transforms YBR pixels (if any) into RGB pixels
430 * - Transforms 3 planes R, G, B (if any) into a single RGB Plane
431 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
432 * - DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
433 * @return Pointer to newly allocated pixel data.
434 * (uint8_t is just for prototyping. feel free to cast)
435 * NULL if alloc fails
437 uint8_t *FileHelper::GetImageDataRaw ()
442 //#ifndef GDCM_LEGACY_REMOVE
444 * \brief Useless function, since PixelReadConverter forces us
445 * copy the Pixels anyway.
446 * Reads the pixels from disk (uncompress if necessary),
447 * Transforms YBR pixels, if any, into RGB pixels
448 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
449 * Transforms single Grey plane + 3 Palettes into a RGB Plane
450 * Copies at most MaxSize bytes of pixel data to caller allocated
452 * \warning This function allows people that want to build a volume
453 * from an image stack *not to* have, first to get the image pixels,
454 * and then move them to the volume area.
455 * It's absolutely useless for any VTK user since vtk chooses
456 * to invert the lines of an image, that is the last line comes first
457 * (for some axis related reasons?). Hence he will have
458 * to load the image line by line, starting from the end.
459 * VTK users have to call GetImageData
461 * @param destination Address (in caller's memory space) at which the
462 * pixel data should be copied
463 * @param maxSize Maximum number of bytes to be copied. When MaxSize
464 * is not sufficient to hold the pixel data the copy is not
465 * executed (i.e. no partial copy).
466 * @return On success, the number of bytes actually copied. Zero on
467 * failure e.g. MaxSize is lower than necessary.
470 size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
474 // If the decompression failed nothing can be done.
478 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
480 if ( PixelReadConverter->GetRGBSize() > maxSize )
482 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
486 (void*)PixelReadConverter->GetRGB(),
487 PixelReadConverter->GetRGBSize() );
488 return PixelReadConverter->GetRGBSize();
491 // Either no LUT conversion necessary or LUT conversion failed
492 if ( PixelReadConverter->GetRawSize() > maxSize )
494 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
498 (void *)PixelReadConverter->GetRaw(),
499 PixelReadConverter->GetRawSize() );
500 return PixelReadConverter->GetRawSize();
506 * \brief Points the internal pointer to the callers inData
507 * image representation, BUT WITHOUT COPYING THE DATA.
508 * 'image' Pixels are presented as C-like 2D arrays : line per line.
509 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
510 * \warning Since the pixels are not copied, it is the caller's responsability
511 * not to deallocate its data before gdcm uses them (e.g. with
512 * the Write() method )
513 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
514 * user is allowed to pass any kind of pixels since the size is
516 * @param expectedSize total image size, *in Bytes*
518 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
520 PixelWriteConverter->SetUserData(inData, expectedSize);
521 /// \todo : shouldn't we call SetCompressJPEGUserData/SetCompressJPEG2000UserData
526 * \brief Set the image data defined by the user
527 * \warning When writting the file, this data are get as default data to write
528 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
529 * user is allowed to pass any kind of pixels since the size is
531 * @param expectedSize total image size, *in Bytes*
533 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
535 // Shouldn't we move theese lines to FileHelper::Write()?
537 if( WriteType == JPEG2000 )
539 PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
541 else if( WriteType == JPEG )
543 PixelWriteConverter->SetCompressJPEGUserData(inData, expectedSize, FileInternal);
547 PixelWriteConverter->SetUserData(inData, expectedSize);
551 PixelWriteConverter->SetUserData(inData, expectedSize);
555 * \brief Get the image data defined by the user
556 * \warning When writting the file, this data are get as default data to write
558 uint8_t *FileHelper::GetUserData()
560 return PixelWriteConverter->GetUserData();
564 * \brief Get the image data size defined by the user
565 * \warning When writting the file, this data are get as default data to write
567 size_t FileHelper::GetUserDataSize()
569 return PixelWriteConverter->GetUserDataSize();
573 * \brief Get the image data from the file.
574 * If a LUT is found, the data are expanded to be RGB
576 uint8_t *FileHelper::GetRGBData()
578 return PixelReadConverter->GetRGB();
582 * \brief Get the image data size from the file.
583 * If a LUT is found, the data are expanded to be RGB
585 size_t FileHelper::GetRGBDataSize()
587 return PixelReadConverter->GetRGBSize();
591 * \brief Get the image data from the file.
592 * Even when a LUT is found, the data are not expanded to RGB!
594 uint8_t *FileHelper::GetRawData()
596 return PixelReadConverter->GetRaw();
600 * \brief Get the image data size from the file.
601 * Even when a LUT is found, the data are not expanded to RGB!
603 size_t FileHelper::GetRawDataSize()
605 return PixelReadConverter->GetRawSize();
609 * \brief Access to the underlying PixelReadConverter RGBA LUT
611 uint8_t* FileHelper::GetLutRGBA()
613 if ( PixelReadConverter->GetLutRGBA() ==0 )
614 PixelReadConverter->BuildLUTRGBA();
615 return PixelReadConverter->GetLutRGBA();
619 * \brief Access to the underlying PixelReadConverter RGBA LUT Item Number
621 int FileHelper::GetLutItemNumber()
623 return PixelReadConverter->GetLutItemNumber();
627 * \brief Access to the underlying PixelReadConverter RGBA LUT Item Size
629 int FileHelper::GetLutItemSize()
631 return PixelReadConverter->GetLutItemSize();
635 * \brief Writes on disk A SINGLE Dicom file
636 * NO test is performed on processor "Endiannity".
637 * It's up to the user to call his Reader properly
638 * @param fileName name of the file to be created
639 * (any already existing file is over written)
640 * @return false if write fails
642 bool FileHelper::WriteRawData(std::string const &fileName)
644 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
647 gdcmWarningMacro( "Fail to open (write) file:" << fileName.c_str());
651 if ( PixelWriteConverter->GetUserData() )
653 fp1.write( (char *)PixelWriteConverter->GetUserData(),
654 PixelWriteConverter->GetUserDataSize() );
656 else if ( PixelReadConverter->GetRGB() )
658 fp1.write( (char *)PixelReadConverter->GetRGB(),
659 PixelReadConverter->GetRGBSize());
661 else if ( PixelReadConverter->GetRaw() )
663 fp1.write( (char *)PixelReadConverter->GetRaw(),
664 PixelReadConverter->GetRawSize());
668 gdcmErrorMacro( "Nothing written." );
677 * \brief Writes on disk A SINGLE Dicom file,
678 * using the Implicit Value Representation convention
679 * NO test is performed on processor "Endianity".
680 * @param fileName name of the file to be created
681 * (any already existing file is overwritten)
682 * @return false if write fails
685 bool FileHelper::WriteDcmImplVR (std::string const &fileName)
687 SetWriteTypeToDcmImplVR();
688 return Write(fileName);
692 * \brief Writes on disk A SINGLE Dicom file,
693 * using the Explicit Value Representation convention
694 * NO test is performed on processor "Endiannity".
695 * @param fileName name of the file to be created
696 * (any already existing file is overwritten)
697 * @return false if write fails
700 bool FileHelper::WriteDcmExplVR (std::string const &fileName)
702 SetWriteTypeToDcmExplVR();
703 return Write(fileName);
707 * \brief Writes on disk A SINGLE Dicom file,
708 * using the ACR-NEMA convention
709 * NO test is performed on processor "Endiannity".
710 * (a l'attention des logiciels cliniques
711 * qui ne prennent en entrée QUE des images ACR ...
712 * \warning if a DICOM_V3 header is supplied,
713 * groups < 0x0008 and shadow groups are ignored
714 * \warning NO TEST is performed on processor "Endiannity".
715 * @param fileName name of the file to be created
716 * (any already existing file is overwritten)
717 * @return false if write fails
720 bool FileHelper::WriteAcr (std::string const &fileName)
723 return Write(fileName);
727 * \brief Writes on disk A SINGLE Dicom file,
728 * @param fileName name of the file to be created
729 * (any already existing file is overwritten)
730 * @return false if write fails
732 bool FileHelper::Write(std::string const &fileName)
734 CheckMandatoryElements(); //called once, here !
739 SetWriteFileTypeToImplicitVR();
742 case Unknown: // should never happen; ExplicitVR is the default value
745 // We let DocEntry::WriteContent to put vr=UN for undocumented Shadow Groups !
746 SetWriteFileTypeToExplicitVR();
751 // NOTHING is done here just for LibIDO.
752 // Just to avoid further trouble if user creates a file ex-nihilo,
753 // wants to write it as an ACR-NEMA file,
754 // and forgets to create any Entry belonging to group 0008
756 // We add Recognition Code (RET)
757 if ( ! FileInternal->GetDataEntry(0x0008, 0x0010) )
758 FileInternal->InsertEntryString("ACR-NEMA V1.0 ",
759 0x0008, 0x0010, "LO");
760 SetWriteFileTypeToACR();
761 // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
764 /// \todo FIXME : JPEG/JPEG2000 may be either ExplicitVR or ImplicitVR
766 SetWriteFileTypeToJPEG();
768 //PixelWriteConverter->SetCompressJPEGUserData(
769 // inData, expectedSize, FileInternal);
770 PixelWriteConverter->SetCompressJPEGUserData(
771 PixelWriteConverter->GetUserData(),
772 PixelWriteConverter->GetUserDataSize(),FileInternal);
776 /// \todo Maybe we should consider doing the compression here !
777 // PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
779 SetWriteFileTypeToJPEG2000();
780 PixelWriteConverter->SetCompressJPEG2000UserData(
781 PixelWriteConverter->GetUserData(),
782 PixelWriteConverter->GetUserDataSize(),
787 // --------------------------------------------------------------
788 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
790 // if recognition code tells us we dealt with a LibIDO image
791 // we reproduce on disk the switch between lineNumber and columnNumber
792 // just before writting ...
793 /// \todo the best trick would be *change* the recognition code
794 /// but pb expected if user deals with, e.g. COMPLEX images
796 if ( WriteType == ACR_LIBIDO )
802 SetWriteToNoLibido();
804 // ----------------- End of Special Patch ----------------
809 SetWriteToRaw(); // modifies and pushes to the archive, when necessary
812 SetWriteToRGB(); // modifies and pushes to the archive, when necessary
817 if (WriteType == JPEG || WriteType == JPEG2000)
820 check = CheckWriteIntegrity(); // verifies length
824 check = FileInternal->Write(fileName,WriteType);
828 // RestoreWriteFileType();
829 // RestoreWriteMandatory();
831 // --------------------------------------------------------------
832 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
834 // ...and we restore the header to be Dicom Compliant again
835 // just after writting
836 RestoreWriteOfLibido();
837 // ----------------- End of Special Patch ----------------
842 //-----------------------------------------------------------------------------
845 * \brief Verifies the size of the user given PixelData
846 * @return true if check is successfull
848 bool FileHelper::CheckWriteIntegrity()
850 if ( PixelWriteConverter->GetUserData() )
852 int numberBitsAllocated = FileInternal->GetBitsAllocated();
853 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
855 gdcmWarningMacro( "numberBitsAllocated changed from "
856 << numberBitsAllocated << " to 16 "
857 << " for consistency purpose" );
858 numberBitsAllocated = 16;
861 size_t decSize = FileInternal->GetXSize()
862 * FileInternal->GetYSize()
863 * FileInternal->GetZSize()
864 * FileInternal->GetTSize()
865 * FileInternal->GetSamplesPerPixel()
866 * ( numberBitsAllocated / 8 );
867 size_t rgbSize = decSize;
868 if ( FileInternal->HasLUT() )
869 rgbSize = decSize * 3;
871 size_t userDataSize = PixelWriteConverter->GetUserDataSize();
875 if ( abs((long)(decSize-userDataSize))>1) // ignore padding zero
877 gdcmWarningMacro( "Data size (Raw) is incorrect. Should be "
878 << decSize << " / Found :"
884 if ( abs((long)(rgbSize-userDataSize))>1) // ignore padding zero
886 gdcmWarningMacro( "Data size (RGB) is incorrect. Should be "
887 << rgbSize << " / Found "
898 * \brief Updates the File to write RAW data (as opposed to RGB data)
899 * (modifies, when necessary, photochromatic interpretation,
900 * bits allocated, Pixels element VR)
901 * WARNING : if SetPhotometricInterpretationToMonochrome1() was called
902 * before Pixel Elements is modified :-(
904 void FileHelper::SetWriteToRaw()
906 if ( FileInternal->GetNumberOfScalarComponents() == 3
907 && !FileInternal->HasLUT() )
913 // 0x0028,0x0004 : Photometric Interpretation
914 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
915 if (FileInternal->HasLUT() )
917 photInt->SetString("PALETTE COLOR ");
921 if (GetPhotometricInterpretation() == 2)
922 photInt->SetString("MONOCHROME2 "); // 0 = Black
924 photInt->SetString("MONOCHROME1 "); // 0 = White !
927 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
928 PixelReadConverter->GetRawSize());
930 std::string vr = "OB";
931 if ( FileInternal->GetBitsAllocated()>8 )
933 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
935 // For non RAW data. Mainly JPEG/JPEG2000
936 if( WriteType == JPEG || WriteType == JPEG2000)
942 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
943 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
944 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
946 static_cast< uint32_t >(PixelWriteConverter->GetDataSize()) );
948 if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
950 ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );
953 Archive->Push(photInt);
954 Archive->Push(pixel);
962 * \brief Updates the File to write RGB data (as opposed to RAW data)
963 * (modifies, when necessary, photochromatic interpretation,
964 * samples per pixel, Planar configuration,
965 * bits allocated, bits stored, high bit -ACR 24 bits-
966 * Pixels element VR, pushes out the LUT, )
968 void FileHelper::SetWriteToRGB()
970 if ( FileInternal->GetNumberOfScalarComponents()==3 )
972 PixelReadConverter->BuildRGBImage();
974 DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
975 spp->SetString("3 "); // Don't drop trailing space
977 DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
978 planConfig->SetString("0 "); // Don't drop trailing space
980 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
981 photInt->SetString("RGB "); // Don't drop trailing space
983 if ( PixelReadConverter->GetRGB() )
985 PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
986 PixelReadConverter->GetRGBSize());
990 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
991 PixelReadConverter->GetRawSize());
994 std::string vr = "OB";
995 if ( FileInternal->GetBitsAllocated()>8 )
997 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
1000 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
1001 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
1002 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
1003 pixel->SetLength(PixelWriteConverter->GetDataSize());
1006 Archive->Push(planConfig);
1007 Archive->Push(photInt);
1008 Archive->Push(pixel);
1011 planConfig->Delete();
1016 Archive->Push(0x0028,0x1101);
1017 Archive->Push(0x0028,0x1102);
1018 Archive->Push(0x0028,0x1103);
1019 Archive->Push(0x0028,0x1201);
1020 Archive->Push(0x0028,0x1202);
1021 Archive->Push(0x0028,0x1203);
1023 // push out Palette Color Lookup Table UID, if any
1024 Archive->Push(0x0028,0x1199);
1026 // For old '24 Bits' ACR-NEMA
1027 // Thus, we have a RGB image and the bits allocated = 24 and
1028 // samples per pixels = 1 (in the read file)
1029 if ( FileInternal->GetBitsAllocated()==24 )
1031 DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
1032 bitsAlloc->SetString("8 ");
1034 DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
1035 bitsStored->SetString("8 ");
1037 DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
1038 highBit->SetString("7 ");
1040 Archive->Push(bitsAlloc);
1041 Archive->Push(bitsStored);
1042 Archive->Push(highBit);
1044 bitsAlloc->Delete();
1045 bitsStored->Delete();
1056 * \brief Restore the File write mode
1058 void FileHelper::RestoreWrite()
1060 Archive->Restore(0x0028,0x0002);
1061 Archive->Restore(0x0028,0x0004);
1063 Archive->Restore(0x0028,0x0006);
1064 Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1066 // For old ACR-NEMA (24 bits problem)
1067 Archive->Restore(0x0028,0x0100);
1068 Archive->Restore(0x0028,0x0101);
1069 Archive->Restore(0x0028,0x0102);
1072 Archive->Restore(0x0028,0x1101);
1073 Archive->Restore(0x0028,0x1102);
1074 Archive->Restore(0x0028,0x1103);
1075 Archive->Restore(0x0028,0x1201);
1076 Archive->Restore(0x0028,0x1202);
1077 Archive->Restore(0x0028,0x1203);
1079 // For the Palette Color Lookup Table UID
1080 Archive->Restore(0x0028,0x1203);
1082 // group 0002 may be pushed out for ACR-NEMA writting purposes
1083 Archive->Restore(0x0002,0x0000);
1084 Archive->Restore(0x0002,0x0001);
1085 Archive->Restore(0x0002,0x0002);
1086 Archive->Restore(0x0002,0x0003);
1087 Archive->Restore(0x0002,0x0010);
1088 Archive->Restore(0x0002,0x0012);
1089 Archive->Restore(0x0002,0x0013);
1090 Archive->Restore(0x0002,0x0016);
1091 Archive->Restore(0x0002,0x0100);
1092 Archive->Restore(0x0002,0x0102);
1097 * \brief Pushes out the whole group 0002
1098 * FIXME : better, set a flag to tell the writer not to write it ...
1099 * FIXME : method should probably have an other name !
1100 * SetWriteFileTypeToACR is NOT opposed to
1101 * SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1103 void FileHelper::SetWriteFileTypeToACR()
1105 Archive->Push(0x0002,0x0000);
1106 Archive->Push(0x0002,0x0001);
1107 Archive->Push(0x0002,0x0002);
1108 Archive->Push(0x0002,0x0003);
1109 Archive->Push(0x0002,0x0010);
1110 Archive->Push(0x0002,0x0012);
1111 Archive->Push(0x0002,0x0013);
1112 Archive->Push(0x0002,0x0016);
1113 Archive->Push(0x0002,0x0100);
1114 Archive->Push(0x0002,0x0102);
1118 * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1120 void FileHelper::SetWriteFileTypeToJPEG2000()
1122 std::string ts = Util::DicomString(
1123 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1125 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1133 * \brief Sets in the File the TransferSyntax to 'JPEG'
1135 void FileHelper::SetWriteFileTypeToJPEG()
1137 std::string ts = Util::DicomString(
1138 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGLosslessProcess14_1) );
1140 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1148 * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"
1150 void FileHelper::SetWriteFileTypeToExplicitVR()
1152 std::string ts = Util::DicomString(
1153 Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1155 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1162 * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"
1164 void FileHelper::SetWriteFileTypeToImplicitVR()
1166 std::string ts = Util::DicomString(
1167 Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1169 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1176 * \brief Set the Write not to Libido format
1178 void FileHelper::SetWriteToLibido()
1180 DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1181 DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1183 if ( oldRow && oldCol )
1185 std::string rows, columns;
1187 DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1188 DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1190 newRow->Copy(oldCol);
1191 newCol->Copy(oldRow);
1193 newRow->SetString(oldCol->GetString());
1194 newCol->SetString(oldRow->GetString());
1196 Archive->Push(newRow);
1197 Archive->Push(newCol);
1203 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1204 libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1205 Archive->Push(libidoCode);
1206 libidoCode->Delete();
1210 * \brief Set the Write not to No Libido format
1212 void FileHelper::SetWriteToNoLibido()
1214 DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1217 if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1219 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1220 libidoCode->SetString("");
1221 Archive->Push(libidoCode);
1222 libidoCode->Delete();
1228 * \brief Restore the Write format
1230 void FileHelper::RestoreWriteOfLibido()
1232 Archive->Restore(0x0028,0x0010);
1233 Archive->Restore(0x0028,0x0011);
1234 Archive->Restore(0x0008,0x0010);
1236 // Restore 'LibIDO-special' entries, if any
1237 Archive->Restore(0x0028,0x0015);
1238 Archive->Restore(0x0028,0x0016);
1239 Archive->Restore(0x0028,0x0017);
1240 Archive->Restore(0x0028,0x00199);
1244 * \brief Duplicates a DataEntry or creates it.
1245 * @param group Group number of the Entry
1246 * @param elem Element number of the Entry
1247 * @param vr Value Representation of the Entry
1248 * \return pointer to the new Bin Entry (NULL when creation failed).
1250 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1253 DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1256 if ( oldE && vr != GDCM_VRUNKNOWN )
1257 if ( oldE->GetVR() != vr )
1262 newE = DataEntry::New(group, elem, vr);
1267 newE = GetFile()->NewDataEntry(group, elem, vr);
1274 * \brief This method is called automatically, just before writting
1275 * in order to produce a 'True Dicom V3' image.
1277 * We cannot know *how* the user made the File :
1278 * (reading an old ACR-NEMA file or a not very clean DICOM file ...)
1279 * Just before writting :
1280 * - we check the Entries
1281 * - we create the mandatory entries if they are missing
1282 * - we modify the values if necessary
1283 * - we push the sensitive entries to the Archive
1284 * The writing process will restore the entries as they where before
1285 * entering FileHelper::CheckMandatoryElements, so the user will always
1286 * see the entries just as they were before he decided to write.
1289 * - Entries whose type is 1 are mandatory, with a mandatory value
1290 * - Entries whose type is 1c are mandatory-inside-a-Sequence,
1291 * with a mandatory value
1292 * - Entries whose type is 2 are mandatory, with an optional value
1293 * - Entries whose type is 2c are mandatory-inside-a-Sequence,
1294 * with an optional value
1295 * - Entries whose type is 3 are optional
1298 * - warn the user if we had to add some entries :
1299 * even if a mandatory entry is missing, we add it, with a default value
1300 * (we don't want to give up the writting process if user forgot to
1301 * specify Lena's Patient ID, for instance ...)
1302 * - read the whole PS 3.3 Part of DICOM (890 pages)
1303 * and write a *full* checker (probably one method per Modality ...)
1304 * Any contribution is welcome.
1305 * - write a user callable full checker, to allow post reading
1306 * and/or pre writting image consistency check.
1309 /* -------------------------------------------------------------------------------------
1310 To be moved to User's guide / WIKI ?
1312 We have to deal with 4 *very* different cases :
1313 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1315 -2) user modified the pixels of an existing image.
1317 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1319 -4) user modified/added some tags *without processing* the pixels (anonymization...)
1320 UNMODIFIED_PIXELS_IMAGE
1321 -Probabely some more to be added.
1322 --> Set it with FileHelper::SetContentType(int);
1324 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1327 0008 0012 Instance Creation Date
1328 0008 0013 Instance Creation Time
1329 0008 0018 SOP Instance UID
1330 are *always* created with the current values; user has *no* possible intervention on
1333 'Serie Instance UID'(0x0020,0x000e)
1334 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1335 created if it doesn't.
1336 The user is allowed to create his own Series/Studies,
1337 keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1339 The user shouldn't add any image to a 'Manufacturer Serie'
1340 but there is no way no to allow him to do that
1342 None of the 'shadow elements' are droped out.
1346 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1349 'Media Storage SOP Class UID' (0x0002,0x0002)
1350 'SOP Class UID' (0x0008,0x0016) are set to
1351 [Secondary Capture Image Storage]
1352 'Image Type' (0x0008,0x0008) is forced to "DERIVED\PRIMARY"
1353 Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1356 If 'SOP Class UID' exists in the native image ('true DICOM' image)
1357 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1358 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1359 whose value is the original 'SOP Class UID'
1360 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1361 whose value is the original 'SOP Class UID'
1363 3) TO DO : find a trick to allow user to pass to the writter the list of the Dicom images
1364 or the Series, (or the Study ?) he used to created his image
1365 (MIP, MPR, cartography image, ...)
1366 These info should be stored (?)
1367 0008 1110 SQ 1 Referenced Study Sequence
1368 0008 1115 SQ 1 Referenced Series Sequence
1369 0008 1140 SQ 1 Referenced Image Sequence
1371 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1372 'Media Storage SOP Class UID' (0x0002,0x0002)
1373 'SOP Class UID' (0x0008,0x0016)
1374 'Image Type' (0x0008,0x0008)
1375 'Conversion Type' (0x0008,0x0064)
1378 Bellow follows the full description (hope so !) of the consistency checks performed
1379 by gdcm::FileHelper::CheckMandatoryElements()
1382 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1383 -->'SOP Class UID' (0x0008,0x0016) are defaulted to
1384 [Secondary Capture Image Storage]
1385 --> 'Image Type' (0x0008,0x0008)
1386 is forced to "DERIVED\PRIMARY"
1387 (The written image is no longer an 'ORIGINAL' one)
1388 Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1390 --> Conversion Type (0x0008,0x0064)
1391 is defaulted to 'SYN' (Synthetic Image)
1392 when *he* knows he created his own image ex nihilo
1394 --> 'Modality' (0x0008,0x0060)
1395 is defaulted to "OT" (other) if missing.
1396 (a fully user created image belongs to *no* modality)
1398 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1399 --> 'Implementation Class UID' (0x0002,0x0012)
1400 are automatically generated; no user intervention possible
1402 --> 'Serie Instance UID'(0x0020,0x000e)
1403 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1404 created if it doesn't.
1405 The user is allowed to create his own Series/Studies,
1406 keeping the same 'Serie Instance UID' / 'Study Instance UID'
1409 The user shouldn't add any image to a 'Manufacturer Serie'
1410 but there is no way no to allowed him to do that
1412 --> If 'SOP Class UID' exists in the native image ('true DICOM' image)
1413 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1415 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1416 whose value is the original 'SOP Class UID'
1417 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1418 whose value is the original 'SOP Class UID'
1420 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1421 --> Pixel Spacing (0x0028,0x0030) is defaulted to "1.0\1.0"
1422 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1424 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1426 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1428 --> Study Date, Study Time are defaulted to current Date and Time
1429 (they remain unchanged if they exist)
1431 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from
1432 Image Orientation (Patient) : (0020|0037) or from
1433 Image Orientation (RET) : (0020 0035)
1435 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1436 are created, with empty value if there are missing.
1438 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1439 are defaulted with a 'gdcm' value.
1441 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1442 --> Referring Physician's Name (Type 2)
1443 are created, with empty value if there are missing.
1445 -------------------------------------------------------------------------------------*/
1447 void FileHelper::CheckMandatoryElements()
1449 std::string sop = Util::CreateUniqueUID();
1451 // --------------------- For Meta Elements ---------------------
1452 // just to remember : 'official' 0002 group
1453 if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1455 // Group 000002 (Meta Elements) already pushed out
1457 //0002 0000 UL 1 Meta Group Length
1458 //0002 0001 OB 1 File Meta Information Version
1459 //0002 0002 UI 1 Media Storage SOP Class UID
1460 //0002 0003 UI 1 Media Storage SOP Instance UID
1461 //0002 0010 UI 1 Transfer Syntax UID
1462 //0002 0012 UI 1 Implementation Class UID
1463 //0002 0013 SH 1 Implementation Version Name
1464 //0002 0016 AE 1 Source Application Entity Title
1465 //0002 0100 UI 1 Private Information Creator
1466 //0002 0102 OB 1 Private Information
1468 // Push out 'ACR-NEMA-special' entries, if any
1469 Archive->Push(0x0008,0x0001); // Length to End
1470 Archive->Push(0x0008,0x0010); // Recognition Code
1471 Archive->Push(0x0028,0x0005); // Image Dimension
1473 // Create them if not found
1474 // Always modify the value
1475 // Push the entries to the archive.
1476 CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1478 DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1479 e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1481 e_0002_0001->SetLength(2);
1482 Archive->Push(e_0002_0001);
1483 e_0002_0001->Delete();
1485 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1487 // we keep the original 'Media Storage SOP Class UID', we default it if missing
1488 CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1492 // It's *not* an image comming straight from a source. We force
1493 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1494 CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1497 // 'Media Storage SOP Instance UID'
1498 CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1500 // 'Implementation Class UID'
1501 // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1502 // seems to be Root UID + 4 digits (?)
1503 CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1505 // 'Implementation Version Name'
1506 std::string version = "GDCM ";
1507 version += Util::GetVersion();
1508 CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1511 // --------------------- For DataSet ---------------------
1513 // check whether 0018|0015 [CS] [Body Part Examined] value is UPPER CASE
1514 // (avoid dciodvfy to complain!)
1515 DataEntry *e_0018_0015 = FileInternal->GetDataEntry(0x0018, 0x0015);
1518 std::string bodyPartExamined = e_0018_0015->GetString();
1519 std::transform(bodyPartExamined.begin(), bodyPartExamined.end(), bodyPartExamined.begin(),
1520 (int(*)(int)) toupper);
1521 CopyMandatoryEntry(0x0018,0x0015,bodyPartExamined,"CS");
1524 if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1526 // If 'SOP Class UID' and 'SOP Instance UID' exist ('true DICOM' image)
1527 // we create the 'Source Image Sequence' SeqEntry
1528 // to hold informations about the Source Image
1530 // 'SOP Instance UID'
1531 DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1533 DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1534 if ( e_0008_0016 && e_0008_0018)
1536 // Create 'Source Image Sequence' SeqEntry
1537 SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1538 SQItem *sqi = SQItem::New(1);
1540 // create 'Referenced SOP Class UID' from 'SOP Class UID'
1542 DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1543 e_0008_1150->SetString( e_0008_0016->GetString());
1544 sqi->AddEntry(e_0008_1150);
1545 e_0008_1150->Delete();
1547 // create 'Referenced SOP Instance UID' from 'SOP Instance UID'
1548 // DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1550 DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1551 e_0008_1155->SetString( e_0008_0018->GetString());
1552 sqi->AddEntry(e_0008_1155);
1553 e_0008_1155->Delete();
1555 sis->AddSQItem(sqi,1);
1558 // temporarily replaces any previous 'Source Image Sequence'
1561 // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1563 if ( ContentType == FILTERED_IMAGE) // the user *knows* he just modified the pixels
1565 DataEntry *e_0008_0008 = FileInternal->GetDataEntry(0x0008, 0x0008);
1568 std::string imageType = e_0008_0008->GetString();
1569 std::string::size_type p = imageType.find("ORIGINAL");
1570 if (p == 0) // image is ORIGINAL one
1572 // the image is no longer an 'Original' one
1573 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1575 // if Image Type was not ORIGINAL\..., we keep it.
1577 else // 0008_0008 was missing, wee add it.
1579 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1585 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1587 // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1588 CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");
1592 // It's *not* an image comming straight from a source. We force
1593 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1594 CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");
1597 Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1598 // Push out 'LibIDO-special' entries, if any
1599 Archive->Push(0x0028,0x0015);
1600 Archive->Push(0x0028,0x0016);
1601 Archive->Push(0x0028,0x0017);
1602 Archive->Push(0x0028,0x0198); // very old versions
1603 Archive->Push(0x0028,0x0199);
1605 // Replace deprecated 0028 0012 US Planes
1606 // by new 0028 0008 IS Number of Frames
1608 ///\todo : find if there is a rule!
1609 DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1612 CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1613 Archive->Push(0x0028,0x0012);
1616 // Deal with the pb of (Bits Stored = 12)
1617 // - we're gonna write the image as Bits Stored = 16
1618 if ( FileInternal->GetEntryString(0x0028,0x0100) == "12")
1620 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1623 // Check if user wasn't drunk ;-)
1625 std::ostringstream s;
1626 // check 'Bits Allocated' vs decent values
1627 int nbBitsAllocated = FileInternal->GetBitsAllocated();
1628 if ( (nbBitsAllocated == 0 || nbBitsAllocated > 32)
1629 || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1631 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1632 gdcmWarningMacro("(0028,0100) changed from "
1633 << nbBitsAllocated << " to 16 for consistency purpose");
1634 nbBitsAllocated = 16;
1636 // check 'Bits Stored' vs 'Bits Allocated'
1637 int nbBitsStored = FileInternal->GetBitsStored();
1638 if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1641 s << nbBitsAllocated;
1642 CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1643 gdcmWarningMacro("(0028,0101) changed from "
1644 << nbBitsStored << " to " << nbBitsAllocated
1645 << " for consistency purpose" );
1646 nbBitsStored = nbBitsAllocated;
1648 // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1649 int highBitPosition = FileInternal->GetHighBitPosition();
1650 if ( highBitPosition == 0 ||
1651 highBitPosition > nbBitsAllocated-1 ||
1652 highBitPosition < nbBitsStored-1 )
1655 s << nbBitsStored - 1;
1656 CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1657 gdcmWarningMacro("(0028,0102) changed from "
1658 << highBitPosition << " to " << nbBitsAllocated-1
1659 << " for consistency purpose");
1662 // check Pixel Representation (default it as 0 -unsigned-)
1664 DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1667 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1668 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1672 int sign = (int)e_0028_0103->GetValue(0);
1673 if (sign !=1 && sign !=0)
1675 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1676 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1680 std::string pixelAspectRatio = FileInternal->GetEntryString(0x0028,0x0034);
1681 if ( pixelAspectRatio == GDCM_UNFOUND ) // avoid conflict with pixelSpacing !
1683 std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1684 if ( pixelSpacing == GDCM_UNFOUND )
1686 pixelSpacing = "1.0\\1.0";
1687 // if missing, Pixel Spacing forced to "1.0\1.0"
1688 CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1691 // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1692 // --> This one is the *legal* one !
1693 if ( ContentType != USER_OWN_IMAGE)
1694 // we write it only when we are *sure* the image comes from
1695 // an imager (see also 0008,0x0064)
1696 CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1699 ///Exact meaning of RETired fields
1701 // See page 73 of ACR-NEMA_300-1988.pdf !
1703 // 0020,0020 : Patient Orientation :
1704 Patient direction of the first row and
1705 column of the images. The first entry id the direction of the raws, given by the
1706 direction of the last pixel in the first row from the first pixel in tha row.
1707 the second entry is the direction of the columns, given by the direction of the
1708 last pixel in the first column from the first pixel in that column.
1709 L : Left, F : Feet, A : Anterior, P : Posterior.
1710 Up to 3 letters can be used in combination to indicate oblique planes.
1712 //0020,0030 Image Position (RET)
1713 x,y,z coordinates im mm of the first pixel in the image
1715 // 0020,0035 Image Orientation (RET)
1716 Direction cosines of the R axis of the image system with respect to the
1717 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1718 the image system with respect to the same axes
1720 //0020,0050 Location
1721 An image location reference, standard for the modality (such as CT bed position),
1722 used to indicate position. Calculation of position for other purposes
1723 is only from (0020,0030) and (0020,0035)
1727 // if imagePositionPatient not found, default it with imagePositionRet, if any
1728 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1730 std::string imagePositionRet = FileInternal->GetEntryString(0x0020,0x0030);
1731 std::string imageOrientationRet = FileInternal->GetEntryString(0x0020,0x0035);
1732 std::string imagePositionPatient = FileInternal->GetEntryString(0x0020,0x0032);
1733 std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1735 if( imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1736 && imagePositionRet != GDCM_UNFOUND && imageOrientationRet != GDCM_UNFOUND)
1738 CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1739 Archive->Push(0x0020,0x0030);
1740 CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1741 Archive->Push(0x0020,0x0035);
1745 // Samples Per Pixel (type 1) : default to grayscale
1746 CheckMandatoryEntry(0x0028,0x0002,"1","US");
1748 // --- Check UID-related Entries ---
1750 // At the end, not to overwrite the original ones,
1751 // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
1752 // 'SOP Instance UID'
1753 CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1755 if ( ContentType == USER_OWN_IMAGE)
1757 gdcmDebugMacro( "USER_OWN_IMAGE (2)");
1759 // Other possible values are :
1760 // See PS 3.3, Page 408
1762 // DV = Digitized Video
1763 // DI = Digital Interface
1764 // DF = Digitized Film
1765 // WSD = Workstation
1766 // SD = Scanned Document
1767 // SI = Scanned Image
1769 // SYN = Synthetic Image
1771 CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1774 if ( ContentType == CREATED_IMAGE)
1776 /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1781 // ---- The user will never have to take any action on the following ----
1783 // new value for 'SOP Instance UID'
1784 //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1786 // Instance Creation Date
1787 const std::string &date = Util::GetCurrentDate();
1788 CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1790 // Instance Creation Time
1791 const std::string &time = Util::GetCurrentTime();
1792 CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1795 CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1797 CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1800 //CopyMandatoryEntry(0x0008,0x0050,"");
1801 CheckMandatoryEntry(0x0008,0x0050,"","SH");
1804 // ----- Add Mandatory Entries if missing ---
1805 // Entries whose type is 1 are mandatory, with a mandatory value
1806 // Entries whose type is 1c are mandatory-inside-a-Sequence,
1807 // with a mandatory value
1808 // Entries whose type is 2 are mandatory, with an optional value
1809 // Entries whose type is 2c are mandatory-inside-a-Sequence,
1810 // with an optional value
1811 // Entries whose type is 3 are optional
1813 // 'Study Instance UID'
1814 // Keep the value if exists
1815 // The user is allowed to create his own Study,
1816 // keeping the same 'Study Instance UID' for various images
1817 // The user may add images to a 'Manufacturer Study',
1818 // adding new Series to an already existing Study
1819 CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1821 // 'Serie Instance UID'
1822 // Keep the value if exists
1823 // The user is allowed to create his own Series,
1824 // keeping the same 'Serie Instance UID' for various images
1825 // The user shouldn't add any image to a 'Manufacturer Serie'
1826 // but there is no way no to prevent him for doing that
1827 CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1830 CheckMandatoryEntry(0x0020,0x0010,"","SH");
1833 CheckMandatoryEntry(0x0020,0x0011,"","IS");
1836 CheckMandatoryEntry(0x0020,0x0013,"","IS");
1838 // Patient Orientation
1839 // Can be computed from (0020|0037) : Image Orientation (Patient)
1840 GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1841 std::string ori = o->GetOrientation ( FileInternal );
1843 if (ori != "\\" && ori != GDCM_UNFOUND)
1844 CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1846 CheckMandatoryEntry(0x0020,0x0020,"","CS");
1848 // Default Patient Position to HFS
1849 CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1851 // Modality : if missing we set it to 'OTher'
1852 CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1854 // Manufacturer : if missing we set it to 'GDCM Factory'
1855 CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1857 // Institution Name : if missing we set it to 'GDCM Hospital'
1858 CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1860 // Patient's Name : if missing, we set it to 'GDCM^Patient'
1861 CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1863 // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1864 CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1866 // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1867 CheckMandatoryEntry(0x0010,0x0030,"","DA");
1869 // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1870 CheckMandatoryEntry(0x0010,0x0040,"","CS");
1872 // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1873 CheckMandatoryEntry(0x0008,0x0090,"","PN");
1876 // Deal with element 0x0000 (group length) of each group.
1877 // First stage : get all the different Groups
1880 DocEntry *d = FileInternal->GetFirstEntry();
1883 grHT[d->GetGroup()] = 0;
1884 d=FileInternal->GetNextEntry();
1886 // Second stage : add the missing ones (if any)
1887 for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)
1889 CheckMandatoryEntry(it->first, 0x0000, "0");
1891 // Third stage : update all 'zero level' groups length
1895 if (PhotometricInterpretation == 1)
1900 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1902 DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1905 //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1906 entry = DataEntry::New(group,elem,vr);
1907 entry->SetString(value);
1908 Archive->Push(entry);
1913 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
1914 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1916 //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1917 DataEntry *entry = DataEntry::New(group,elem,vr);
1918 entry->SetString(value);
1919 Archive->Push(entry);
1923 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1925 DataEntry *entry = CopyDataEntry(group,elem,vr);
1926 entry->SetString(value);
1927 Archive->Push(entry);
1932 * \brief Restore in the File the initial group 0002
1934 void FileHelper::RestoreWriteMandatory()
1936 // group 0002 may be pushed out for ACR-NEMA writting purposes
1937 Archive->Restore(0x0002,0x0000);
1938 Archive->Restore(0x0002,0x0001);
1939 Archive->Restore(0x0002,0x0002);
1940 Archive->Restore(0x0002,0x0003);
1941 Archive->Restore(0x0002,0x0010);
1942 Archive->Restore(0x0002,0x0012);
1943 Archive->Restore(0x0002,0x0013);
1944 Archive->Restore(0x0002,0x0016);
1945 Archive->Restore(0x0002,0x0100);
1946 Archive->Restore(0x0002,0x0102);
1948 // FIXME : Check if none is missing !
1950 Archive->Restore(0x0008,0x0012);
1951 Archive->Restore(0x0008,0x0013);
1952 Archive->Restore(0x0008,0x0016);
1953 Archive->Restore(0x0008,0x0018);
1954 Archive->Restore(0x0008,0x0060);
1955 Archive->Restore(0x0008,0x0070);
1956 Archive->Restore(0x0008,0x0080);
1957 Archive->Restore(0x0008,0x0090);
1958 Archive->Restore(0x0008,0x2112);
1960 Archive->Restore(0x0010,0x0010);
1961 Archive->Restore(0x0010,0x0030);
1962 Archive->Restore(0x0010,0x0040);
1964 Archive->Restore(0x0020,0x000d);
1965 Archive->Restore(0x0020,0x000e);
1969 * \brief CallStartMethod
1971 void FileHelper::CallStartMethod()
1975 CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1979 * \brief CallProgressMethod
1981 void FileHelper::CallProgressMethod()
1983 CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1987 * \brief CallEndMethod
1989 void FileHelper::CallEndMethod()
1992 CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1995 //-----------------------------------------------------------------------------
1998 * \brief Factorization for various forms of constructors.
2000 void FileHelper::Initialize()
2003 ContentType = USER_OWN_IMAGE;
2005 WriteMode = WMODE_RAW;
2006 WriteType = ExplicitVR;
2008 PhotometricInterpretation = 2; // Black = 0
2010 PixelReadConverter = new PixelReadConvert;
2011 PixelWriteConverter = new PixelWriteConvert;
2012 Archive = new DocEntryArchive( FileInternal );
2014 KeepOverlays = false;
2018 * \brief Reads/[decompresses] the pixels,
2019 * *without* making RGB from Palette Colors
2020 * @return the pixels area, whatever its type
2021 * (uint8_t is just for prototyping : feel free to Cast it)
2023 uint8_t *FileHelper::GetRaw()
2025 PixelReadConverter->SetUserFunction( UserFunction );
2027 uint8_t *raw = PixelReadConverter->GetRaw();
2030 // The Raw image migth not be loaded yet:
2031 std::ifstream *fp = FileInternal->OpenFile();
2032 PixelReadConverter->ReadAndDecompressPixelData( fp );
2034 FileInternal->CloseFile();
2036 raw = PixelReadConverter->GetRaw();
2039 gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
2047 * \brief Deal with Grey levels i.e. re-arange them
2048 * to have low values = dark, high values = bright
2050 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
2052 uint32_t i; // to please M$VC6
2055 // Number of Bits Allocated for storing a Pixel is defaulted to 16
2056 // when absent from the file.
2057 int bitsAllocated = FileInternal->GetBitsAllocated();
2058 if ( bitsAllocated == 0 )
2063 else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2067 // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2068 // when absent from the file.
2069 int bitsStored = FileInternal->GetBitsStored();
2070 if ( bitsStored == 0 )
2072 bitsStored = bitsAllocated;
2075 if (!FileInternal->IsSignedPixelData())
2077 if ( bitsAllocated == 8 )
2079 uint8_t *deb = (uint8_t *)raw;
2080 for (i=0; i<rawSize; i++)
2088 if ( bitsAllocated == 16 )
2091 for (j=0; j<bitsStored-1; j++)
2093 mask = (mask << 1) +1; // will be fff when BitsStored=12
2096 uint16_t *deb = (uint16_t *)raw;
2097 for (i=0; i<rawSize/2; i++)
2107 if ( bitsAllocated == 8 )
2109 uint8_t smask8 = 255;
2110 uint8_t *deb = (uint8_t *)raw;
2111 for (i=0; i<rawSize; i++)
2113 *deb = smask8 - *deb;
2118 if ( bitsAllocated == 16 )
2120 uint16_t smask16 = 65535;
2121 uint16_t *deb = (uint16_t *)raw;
2122 for (i=0; i<rawSize/2; i++)
2124 *deb = smask16 - *deb;
2132 //-----------------------------------------------------------------------------
2134 * \brief Prints the FileInternal + info on PixelReadConvertor
2135 * @param os ostream we want to print in
2136 * @param indent (unused)
2138 void FileHelper::Print(std::ostream &os, std::string const &)
2140 FileInternal->SetPrintLevel(PrintLevel);
2141 FileInternal->Print(os);
2143 if ( FileInternal->IsReadable() )
2145 PixelReadConverter->SetPrintLevel(PrintLevel);
2146 PixelReadConverter->Print(os);
2150 //-----------------------------------------------------------------------------
2151 } // end namespace gdcm
2154 /* Probabely something to be added to use Rescale Slope/Intercept
2155 Have a look at ITK code !
2157 // Internal function to rescale pixel according to Rescale Slope/Intercept
2158 template<class TBuffer, class TSource>
2159 void RescaleFunction(TBuffer* buffer, TSource *source,
2160 double slope, double intercept, size_t size)
2162 size /= sizeof(TSource);
2164 if (slope != 1.0 && intercept != 0.0)
2166 // Duff's device. Instead of this code:
2168 // for(unsigned int i=0; i<size; i++)
2170 // buffer[i] = (TBuffer)(source[i]*slope + intercept);
2173 // use Duff's device which exploits "fall through"
2174 register size_t n = (size + 7) / 8;
2177 case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2178 case 7: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2179 case 6: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2180 case 5: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2181 case 4: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2182 case 3: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2183 case 2: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2184 case 1: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2188 else if (slope == 1.0 && intercept != 0.0)
2190 // Duff's device. Instead of this code:
2192 // for(unsigned int i=0; i<size; i++)
2194 // buffer[i] = (TBuffer)(source[i] + intercept);
2197 // use Duff's device which exploits "fall through"
2198 register size_t n = (size + 7) / 8;
2201 case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2202 case 7: *buffer++ = (TBuffer)(*source++ + intercept);
2203 case 6: *buffer++ = (TBuffer)(*source++ + intercept);
2204 case 5: *buffer++ = (TBuffer)(*source++ + intercept);
2205 case 4: *buffer++ = (TBuffer)(*source++ + intercept);
2206 case 3: *buffer++ = (TBuffer)(*source++ + intercept);
2207 case 2: *buffer++ = (TBuffer)(*source++ + intercept);
2208 case 1: *buffer++ = (TBuffer)(*source++ + intercept);
2212 else if (slope != 1.0 && intercept == 0.0)
2214 // Duff's device. Instead of this code:
2216 // for(unsigned int i=0; i<size; i++)
2218 // buffer[i] = (TBuffer)(source[i]*slope);
2221 // use Duff's device which exploits "fall through"
2222 register size_t n = (size + 7) / 8;
2225 case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2226 case 7: *buffer++ = (TBuffer)((*source++)*slope);
2227 case 6: *buffer++ = (TBuffer)((*source++)*slope);
2228 case 5: *buffer++ = (TBuffer)((*source++)*slope);
2229 case 4: *buffer++ = (TBuffer)((*source++)*slope);
2230 case 3: *buffer++ = (TBuffer)((*source++)*slope);
2231 case 2: *buffer++ = (TBuffer)((*source++)*slope);
2232 case 1: *buffer++ = (TBuffer)((*source++)*slope);
2238 // Duff's device. Instead of this code:
2240 // for(unsigned int i=0; i<size; i++)
2242 // buffer[i] = (TBuffer)(source[i]);
2245 // use Duff's device which exploits "fall through"
2246 register size_t n = (size + 7) / 8;
2249 case 0: do { *buffer++ = (TBuffer)(*source++);
2250 case 7: *buffer++ = (TBuffer)(*source++);
2251 case 6: *buffer++ = (TBuffer)(*source++);
2252 case 5: *buffer++ = (TBuffer)(*source++);
2253 case 4: *buffer++ = (TBuffer)(*source++);
2254 case 3: *buffer++ = (TBuffer)(*source++);
2255 case 2: *buffer++ = (TBuffer)(*source++);
2256 case 1: *buffer++ = (TBuffer)(*source++);
2263 template<class TSource>
2264 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2265 void* buffer, TSource *source,
2266 double slope, double intercept, size_t size)
2270 case ImageIOBase::UCHAR:
2271 RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2273 case ImageIOBase::CHAR:
2274 RescaleFunction( (char *)buffer, source, slope, intercept, size);
2276 case ImageIOBase::USHORT:
2277 RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2279 case ImageIOBase::SHORT:
2280 RescaleFunction( (short *)buffer, source, slope, intercept, size);
2282 case ImageIOBase::UINT:
2283 RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2285 case ImageIOBase::INT:
2286 RescaleFunction( (int *)buffer, source, slope, intercept, size);
2288 case ImageIOBase::FLOAT:
2289 RescaleFunction( (float *)buffer, source, slope, intercept, size);
2291 case ImageIOBase::DOUBLE:
2292 RescaleFunction( (double *)buffer, source, slope, intercept, size);
2295 ::itk::OStringStream message;
2296 message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2297 ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);