1 /*=========================================================================
4 Module: $RCSfile: gdcmFileHelper.cxx,v $
7 Date: $Date: 2007/10/17 12:14:02 $
8 Version: $Revision: 1.134 $
10 Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
11 l'Image). All rights reserved. See Doc/License.txt or
12 http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
14 This software is distributed WITHOUT ANY WARRANTY; without even
15 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 PURPOSE. See the above copyright notices for more information.
18 =========================================================================*/
20 #include "gdcmFileHelper.h"
21 #include "gdcmGlobal.h"
23 #include "gdcmDocument.h"
24 #include "gdcmDebug.h"
26 #include "gdcmSeqEntry.h"
27 #include "gdcmSQItem.h"
28 #include "gdcmDataEntry.h"
29 #include "gdcmDocEntry.h"
31 #include "gdcmPixelReadConvert.h"
32 #include "gdcmPixelWriteConvert.h"
33 #include "gdcmDocEntryArchive.h"
34 #include "gdcmDictSet.h"
35 #include "gdcmOrientation.h"
37 #if defined(__BORLANDC__)
38 #include <mem.h> // for memset
44 // ----------------------------- WARNING -------------------------
46 These lines will be moved to the document-to-be 'User's Guide'
48 // To read an image, user needs a gdcm::File
49 gdcm::File *f = new gdcm::File(fileName);
51 // user may also decide he doesn't want to load some parts of the header
52 gdcm::File *f = new gdcm::File();
53 f->SetFileName(fileName);
54 f->SetLoadMode(LD_NOSEQ); // or
55 f->SetLoadMode(LD_NOSHADOW); // or
56 f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW); // or
57 f->SetLoadMode(LD_NOSHADOWSEQ);
60 // To decide whether it's an 'image of interest for him, or not,
61 // user can now check some values
62 std::string v = f->GetEntryValue(groupNb,ElementNb);
64 // to get the pixels, user needs a gdcm::FileHelper
65 gdcm::FileHelper *fh = new gdcm::FileHelper(f);
67 // user may ask not to convert Palette (if any) to RGB
68 uint8_t *pixels = fh->GetImageDataRaw();
69 int imageLength = fh->GetImageDataRawSize();
71 // He can now use the pixels, create a new image, ...
72 uint8_t *userPixels = ...
74 //To re-write the image, user re-uses the gdcm::FileHelper
75 gdcm::File *fh = new gdcm::FileHelper();
77 fh->SetTypeToRaw(); // Even if it was possible to convert Palette to RGB
80 // If user wants to write the file as MONOCHROME1 (0=white)
81 fh->SetPhotometricInterpretationToMonochrome1();
83 fh->SetWriteTypeToDcmExpl(); // he wants Explicit Value Representation
84 // Little Endian is the default,
85 // bigendian not supported for writting
86 (-->SetWriteType(ExplicitVR);)
87 -->WriteType = ExplicitVR;
88 fh->SetWriteTypeToJPEG(); // lossless compression
89 fh->SetWriteTypeToJPEG2000(); // lossless compression
91 fh->SetImageData( userPixels, userPixelsLength);
93 fh->SetUserData( userPixels, userPixelsLength); // this one performs compression, when required
95 fh->Write(newFileName); // overwrites the file, if any
100 These lines will be moved to the document-to-be 'Developer's Guide'
102 WriteMode : WMODE_RAW / WMODE_RGB
103 WriteType : ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO
104 PhotometricInterpretation : MONOCHROME2 (0=black), MONOCHROME2 (0=white)
106 fh->SetImageData( userPixels, userPixelsLength);
108 fh->SetUserData( userPixels, userPixelsLength);
109 PixelWriteConverter->SetUserData(inData, expectedSize);
112 fh->SetWriteMode(WMODE_RAW / WMODE_RGB)
114 fh->SetWriteType( ImplicitVR/ExplicitVR/ACR/ACR_LIBIDO/JPEG/JPEG2000)
116 fh->Write(newFileName);
117 CheckMandatoryElements(); // Checks existing ones / Add missing ones
118 Fix VR if unknown elements
119 SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR(); /
120 SetWriteFileTypeToACR() / SetWriteFileTypeToJPEG() / SetWriteFileTypeToJ2K()
121 (Modifies TransferSyntax if any; Pushes to the Archives old one)
122 SetWriteToRaw(); / SetWriteToRGB();
123 (Modifies and pushes to the Archive, when necessary : photochr. interp.,
124 samples per pixel, Planar configuration,
125 bits allocated, bits stored, high bit -ACR 24 bits-
126 Pixels element VR, pushes out the LUT )
128 Sets Photometric Interpretation
129 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
130 Sets VR, BinArea, Length for PixelData
133 Archive->Push(photInt);
134 Archive->Push(pixel);
138 if NumberOfScalarComponents==1
139 SetWriteToRaw(); return;
140 PixelReadConverter->BuildRGBImage()
141 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
142 Archives spp, planConfig,photInt, pixel
144 CheckWriteIntegrity();
145 (checks user given pixels length)
146 FileInternal->Write(fileName,WriteType)
147 fp = opens file(fileName); // out|binary
148 ComputeGroup0002Length( );
149 Document::WriteContent(fp, writetype);
150 writes Dicom File Preamble not ACR-NEMA
151 ElementSet::WriteContent(fp, writetype);
152 writes recursively all DataElements
154 (moves back to the gdcm::File all the archived elements)
160 namespace GDCM_NAME_SPACE
162 typedef std::map<uint16_t, int> GroupHT; // Hash Table
163 //-------------------------------------------------------------------------
164 // Constructor / Destructor
166 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
167 * file (gdcm::File only deals with the ... header)
168 * Opens (in read only and when possible) an existing file and checks
169 * for DICOM compliance. Returns NULL on failure.
170 * It will be up to the user to load the pixels into memory
171 * ( GetImageDataSize() + GetImageData() methods)
172 * \note the in-memory representation of all available tags found in
173 * the DICOM header is post-poned to first header information access.
174 * This avoid a double parsing of public part of the header when
175 * one sets an a posteriori shadow dictionary (efficiency can be
176 * seen as a side effect).
178 FileHelper::FileHelper( )
180 FileInternal = File::New( );
185 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
186 * file (File only deals with the ... header)
187 * Opens (in read only and when possible) an existing file and checks
188 * for DICOM compliance. Returns NULL on failure.
189 * It will be up to the user to load the pixels into memory
190 * ( GetImageDataSize() + GetImageData() methods)
191 * \note the in-memory representation of all available tags found in
192 * the DICOM header is post-poned to first header information access.
193 * This avoid a double parsing of public part of the header when
194 * user sets an a posteriori shadow dictionary (efficiency can be
195 * seen as a side effect).
196 * @param header already built Header
198 FileHelper::FileHelper(File *header)
200 gdcmAssertMacro(header);
202 FileInternal = header;
203 FileInternal->Register();
205 if ( FileInternal->IsReadable() )
207 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
212 * \brief canonical destructor
213 * \note If the header (gdcm::File) was created by the FileHelper constructor,
214 * it is destroyed by the FileHelper
216 FileHelper::~FileHelper()
218 if ( PixelReadConverter )
220 delete PixelReadConverter;
222 if ( PixelWriteConverter )
224 delete PixelWriteConverter;
231 FileInternal->Unregister();
234 //-----------------------------------------------------------------------------
238 * \brief Sets the LoadMode of the internal gdcm::File as a boolean string.
239 * NO_SEQ, NO_SHADOW, NO_SHADOWSEQ ... (nothing more, right now)
240 * WARNING : before using NO_SHADOW, be sure *all* your files
241 * contain accurate values in the 0x0000 element (if any)
242 * of *each* Shadow Group. The parser will fail if the size is wrong !
243 * @param loadMode Load mode to be used
245 void FileHelper::SetLoadMode(int loadMode)
247 GetFile()->SetLoadMode( loadMode );
250 * \brief Sets the LoadMode of the internal gdcm::File
251 * @param fileName name of the file to be open
253 void FileHelper::SetFileName(std::string const &fileName)
255 FileInternal->SetFileName( fileName );
260 * @return false if file cannot be open or no swap info was found,
261 * or no tag was found.
263 bool FileHelper::Load()
265 if ( !FileInternal->Load() )
268 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
273 * \brief Accesses an existing DataEntry through its (group, element)
274 * and modifies its content with the given value.
275 * @param content new value (string) to substitute with
276 * @param group group number of the Dicom Element to modify
277 * @param elem element number of the Dicom Element to modify
278 * \return false if DataEntry not found
280 bool FileHelper::SetEntryString(std::string const &content,
281 uint16_t group, uint16_t elem)
283 return FileInternal->SetEntryString(content, group, elem);
288 * \brief Accesses an existing DataEntry through its (group, element)
289 * and modifies its content with the given value.
290 * @param content new value (void* -> uint8_t*) to substitute with
291 * @param lgth new value length
292 * @param group group number of the Dicom Element to modify
293 * @param elem element number of the Dicom Element to modify
294 * \return false if DataEntry not found
296 bool FileHelper::SetEntryBinArea(uint8_t *content, int lgth,
297 uint16_t group, uint16_t elem)
299 return FileInternal->SetEntryBinArea(content, lgth, group, elem);
303 * \brief Modifies the value of a given DataEntry when it exists.
304 * Creates it with the given value when unexistant.
305 * @param content (string) value to be set
306 * @param group Group number of the Entry
307 * @param elem Element number of the Entry
308 * @param vr Value Representation of the DataElement to be inserted
309 * \return pointer to the modified/created DataEntry (NULL when creation
312 DataEntry *FileHelper::InsertEntryString(std::string const &content,
313 uint16_t group, uint16_t elem,
316 return FileInternal->InsertEntryString(content, group, elem, vr);
320 * \brief Modifies the value of a given DataEntry when it exists.
321 * Creates it with the given value when unexistant.
322 * A copy of the binArea is made to be kept in the Document.
323 * @param binArea (binary) value to be set
324 * @param lgth new value length
325 * @param group Group number of the Entry
326 * @param elem Element number of the Entry
327 * @param vr Value Representation of the DataElement to be inserted
328 * \return pointer to the modified/created DataEntry (NULL when creation
331 DataEntry *FileHelper::InsertEntryBinArea(uint8_t *binArea, int lgth,
332 uint16_t group, uint16_t elem,
335 return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem, vr);
339 * \brief Adds an empty SeqEntry
340 * (remove any existing entry with same group,elem)
341 * @param group Group number of the Entry
342 * @param elem Element number of the Entry
343 * \return pointer to the created SeqEntry (NULL when creation
346 SeqEntry *FileHelper::InsertSeqEntry(uint16_t group, uint16_t elem)
348 return FileInternal->InsertSeqEntry(group, elem);
352 * \brief Get the size of the image data
353 * If the image can be RGB (with a lut or by default), the size
354 * corresponds to the RGB image
355 * (use GetImageDataRawSize if you want to be sure to get *only*
356 * the size of the pixels)
357 * @return The image size
359 size_t FileHelper::GetImageDataSize()
361 if ( PixelWriteConverter->GetUserData() )
363 return PixelWriteConverter->GetUserDataSize();
365 return PixelReadConverter->GetRGBSize();
369 * \brief Get the size of the image data.
370 * If the image could be converted to RGB using a LUT,
371 * this transformation is not taken into account by GetImageDataRawSize
372 * (use GetImageDataSize if you wish)
373 * @return The raw image size
375 size_t FileHelper::GetImageDataRawSize()
377 if ( PixelWriteConverter->GetUserData() )
379 return PixelWriteConverter->GetUserDataSize();
381 return PixelReadConverter->GetRawSize();
385 * \brief brings pixels into memory :
386 * - Allocates necessary memory,
387 * - Reads the pixels from disk (uncompress if necessary),
388 * - Transforms YBR pixels, if any, into RGB pixels,
389 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
390 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
391 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
392 * @return Pointer to newly allocated pixel data.
393 * (uint8_t is just for prototyping. feel free to cast)
394 * NULL if alloc fails
396 uint8_t *FileHelper::GetImageData()
398 if ( PixelWriteConverter->GetUserData() )
400 return PixelWriteConverter->GetUserData();
405 // If the decompression failed nothing can be done.
409 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
411 return PixelReadConverter->GetRGB();
415 // When no LUT or LUT conversion fails, return the Raw
416 return PixelReadConverter->GetRaw();
421 * \brief brings pixels into memory :
422 * - Allocates necessary memory,
423 * - Transforms YBR pixels (if any) into RGB pixels
424 * - Transforms 3 planes R, G, B (if any) into a single RGB Plane
425 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
426 * - DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
427 * @return Pointer to newly allocated pixel data.
428 * (uint8_t is just for prototyping. feel free to cast)
429 * NULL if alloc fails
431 uint8_t *FileHelper::GetImageDataRaw ()
436 //#ifndef GDCM_LEGACY_REMOVE
438 * \brief Useless function, since PixelReadConverter forces us
439 * copy the Pixels anyway.
440 * Reads the pixels from disk (uncompress if necessary),
441 * Transforms YBR pixels, if any, into RGB pixels
442 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
443 * Transforms single Grey plane + 3 Palettes into a RGB Plane
444 * Copies at most MaxSize bytes of pixel data to caller allocated
446 * \warning This function allows people that want to build a volume
447 * from an image stack *not to* have, first to get the image pixels,
448 * and then move them to the volume area.
449 * It's absolutely useless for any VTK user since vtk chooses
450 * to invert the lines of an image, that is the last line comes first
451 * (for some axis related reasons?). Hence he will have
452 * to load the image line by line, starting from the end.
453 * VTK users have to call GetImageData
455 * @param destination Address (in caller's memory space) at which the
456 * pixel data should be copied
457 * @param maxSize Maximum number of bytes to be copied. When MaxSize
458 * is not sufficient to hold the pixel data the copy is not
459 * executed (i.e. no partial copy).
460 * @return On success, the number of bytes actually copied. Zero on
461 * failure e.g. MaxSize is lower than necessary.
464 size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
468 // If the decompression failed nothing can be done.
472 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
474 if ( PixelReadConverter->GetRGBSize() > maxSize )
476 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
480 (void*)PixelReadConverter->GetRGB(),
481 PixelReadConverter->GetRGBSize() );
482 return PixelReadConverter->GetRGBSize();
485 // Either no LUT conversion necessary or LUT conversion failed
486 if ( PixelReadConverter->GetRawSize() > maxSize )
488 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
492 (void *)PixelReadConverter->GetRaw(),
493 PixelReadConverter->GetRawSize() );
494 return PixelReadConverter->GetRawSize();
500 * \brief Points the internal pointer to the callers inData
501 * image representation, BUT WITHOUT COPYING THE DATA.
502 * 'image' Pixels are presented as C-like 2D arrays : line per line.
503 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
504 * \warning Since the pixels are not copied, it is the caller's responsability
505 * not to deallocate its data before gdcm uses them (e.g. with
506 * the Write() method )
507 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
508 * user is allowed to pass any kind of pixels since the size is
510 * @param expectedSize total image size, *in Bytes*
512 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
514 PixelWriteConverter->SetUserData(inData, expectedSize);
515 /// \todo : shouldn't we call SetCompressJPEGUserData/SetCompressJPEG2000UserData
520 * \brief Set the image data defined by the user
521 * \warning When writting the file, this data are get as default data to write
522 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
523 * user is allowed to pass any kind of pixels since the size is
525 * @param expectedSize total image size, *in Bytes*
527 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
529 // Shouldn't we move theese lines to FileHelper::Write()?
531 if( WriteType == JPEG2000 )
533 PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
535 else if( WriteType == JPEG )
537 PixelWriteConverter->SetCompressJPEGUserData(inData, expectedSize, FileInternal);
541 PixelWriteConverter->SetUserData(inData, expectedSize);
545 PixelWriteConverter->SetUserData(inData, expectedSize);
549 * \brief Get the image data defined by the user
550 * \warning When writting the file, this data are get as default data to write
552 uint8_t *FileHelper::GetUserData()
554 return PixelWriteConverter->GetUserData();
558 * \brief Get the image data size defined by the user
559 * \warning When writting the file, this data are get as default data to write
561 size_t FileHelper::GetUserDataSize()
563 return PixelWriteConverter->GetUserDataSize();
567 * \brief Get the image data from the file.
568 * If a LUT is found, the data are expanded to be RGB
570 uint8_t *FileHelper::GetRGBData()
572 return PixelReadConverter->GetRGB();
576 * \brief Get the image data size from the file.
577 * If a LUT is found, the data are expanded to be RGB
579 size_t FileHelper::GetRGBDataSize()
581 return PixelReadConverter->GetRGBSize();
585 * \brief Get the image data from the file.
586 * Even when a LUT is found, the data are not expanded to RGB!
588 uint8_t *FileHelper::GetRawData()
590 return PixelReadConverter->GetRaw();
594 * \brief Get the image data size from the file.
595 * Even when a LUT is found, the data are not expanded to RGB!
597 size_t FileHelper::GetRawDataSize()
599 return PixelReadConverter->GetRawSize();
603 * \brief Access to the underlying PixelReadConverter RGBA LUT
605 uint8_t* FileHelper::GetLutRGBA()
607 if ( PixelReadConverter->GetLutRGBA() ==0 )
608 PixelReadConverter->BuildLUTRGBA();
609 return PixelReadConverter->GetLutRGBA();
613 * \brief Access to the underlying PixelReadConverter RGBA LUT Item Number
615 int FileHelper::GetLutItemNumber()
617 return PixelReadConverter->GetLutItemNumber();
621 * \brief Access to the underlying PixelReadConverter RGBA LUT Item Size
623 int FileHelper::GetLutItemSize()
625 return PixelReadConverter->GetLutItemSize();
629 * \brief Writes on disk A SINGLE Dicom file
630 * NO test is performed on processor "Endiannity".
631 * It's up to the user to call his Reader properly
632 * @param fileName name of the file to be created
633 * (any already existing file is over written)
634 * @return false if write fails
636 bool FileHelper::WriteRawData(std::string const &fileName)
638 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
641 gdcmWarningMacro( "Fail to open (write) file:" << fileName.c_str());
645 if ( PixelWriteConverter->GetUserData() )
647 fp1.write( (char *)PixelWriteConverter->GetUserData(),
648 PixelWriteConverter->GetUserDataSize() );
650 else if ( PixelReadConverter->GetRGB() )
652 fp1.write( (char *)PixelReadConverter->GetRGB(),
653 PixelReadConverter->GetRGBSize());
655 else if ( PixelReadConverter->GetRaw() )
657 fp1.write( (char *)PixelReadConverter->GetRaw(),
658 PixelReadConverter->GetRawSize());
662 gdcmErrorMacro( "Nothing written." );
671 * \brief Writes on disk A SINGLE Dicom file,
672 * using the Implicit Value Representation convention
673 * NO test is performed on processor "Endianity".
674 * @param fileName name of the file to be created
675 * (any already existing file is overwritten)
676 * @return false if write fails
679 bool FileHelper::WriteDcmImplVR (std::string const &fileName)
681 SetWriteTypeToDcmImplVR();
682 return Write(fileName);
686 * \brief Writes on disk A SINGLE Dicom file,
687 * using the Explicit Value Representation convention
688 * NO test is performed on processor "Endiannity".
689 * @param fileName name of the file to be created
690 * (any already existing file is overwritten)
691 * @return false if write fails
694 bool FileHelper::WriteDcmExplVR (std::string const &fileName)
696 SetWriteTypeToDcmExplVR();
697 return Write(fileName);
701 * \brief Writes on disk A SINGLE Dicom file,
702 * using the ACR-NEMA convention
703 * NO test is performed on processor "Endiannity".
704 * (a l'attention des logiciels cliniques
705 * qui ne prennent en entrée QUE des images ACR ...
706 * \warning if a DICOM_V3 header is supplied,
707 * groups < 0x0008 and shadow groups are ignored
708 * \warning NO TEST is performed on processor "Endiannity".
709 * @param fileName name of the file to be created
710 * (any already existing file is overwritten)
711 * @return false if write fails
714 bool FileHelper::WriteAcr (std::string const &fileName)
717 return Write(fileName);
721 * \brief Writes on disk A SINGLE Dicom file,
722 * @param fileName name of the file to be created
723 * (any already existing file is overwritten)
724 * @return false if write fails
726 bool FileHelper::Write(std::string const &fileName)
728 CheckMandatoryElements(); //called once, here !
733 SetWriteFileTypeToImplicitVR();
736 case Unknown: // should never happen; ExplicitVR is the default value
739 // We let DocEntry::WriteContent to put vr=UN for undocumented Shadow Groups !
740 SetWriteFileTypeToExplicitVR();
745 // NOTHING is done here just for LibIDO.
746 // Just to avoid further trouble if user creates a file ex-nihilo,
747 // wants to write it as an ACR-NEMA file,
748 // and forgets to create any Entry belonging to group 0008
750 // We add Recognition Code (RET)
751 if ( ! FileInternal->GetDataEntry(0x0008, 0x0010) )
752 FileInternal->InsertEntryString("ACR-NEMA V1.0 ",
753 0x0008, 0x0010, "LO");
754 SetWriteFileTypeToACR();
755 // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
758 /// \todo FIXME : JPEG/JPEG2000 may be either ExplicitVR or ImplicitVR
760 SetWriteFileTypeToJPEG();
762 //PixelWriteConverter->SetCompressJPEGUserData(
763 // inData, expectedSize, FileInternal);
764 PixelWriteConverter->SetCompressJPEGUserData(
765 PixelWriteConverter->GetUserData(),
766 PixelWriteConverter->GetUserDataSize(),FileInternal);
770 /// \todo Maybe we should consider doing the compression here !
771 // PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
773 SetWriteFileTypeToJPEG2000();
774 PixelWriteConverter->SetCompressJPEG2000UserData(
775 PixelWriteConverter->GetUserData(),
776 PixelWriteConverter->GetUserDataSize(),
781 // --------------------------------------------------------------
782 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
784 // if recognition code tells us we dealt with a LibIDO image
785 // we reproduce on disk the switch between lineNumber and columnNumber
786 // just before writting ...
787 /// \todo the best trick would be *change* the recognition code
788 /// but pb expected if user deals with, e.g. COMPLEX images
790 if ( WriteType == ACR_LIBIDO )
796 SetWriteToNoLibido();
798 // ----------------- End of Special Patch ----------------
803 SetWriteToRaw(); // modifies and pushes to the archive, when necessary
806 SetWriteToRGB(); // modifies and pushes to the archive, when necessary
811 if (WriteType == JPEG || WriteType == JPEG2000)
814 check = CheckWriteIntegrity(); // verifies length
818 check = FileInternal->Write(fileName,WriteType);
822 // RestoreWriteFileType();
823 // RestoreWriteMandatory();
825 // --------------------------------------------------------------
826 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
828 // ...and we restore the header to be Dicom Compliant again
829 // just after writting
830 RestoreWriteOfLibido();
831 // ----------------- End of Special Patch ----------------
836 //-----------------------------------------------------------------------------
839 * \brief Verifies the size of the user given PixelData
840 * @return true if check is successfull
842 bool FileHelper::CheckWriteIntegrity()
844 if ( PixelWriteConverter->GetUserData() )
846 int numberBitsAllocated = FileInternal->GetBitsAllocated();
847 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
849 gdcmWarningMacro( "numberBitsAllocated changed from "
850 << numberBitsAllocated << " to 16 "
851 << " for consistency purpose" );
852 numberBitsAllocated = 16;
855 size_t decSize = FileInternal->GetXSize()
856 * FileInternal->GetYSize()
857 * FileInternal->GetZSize()
858 * FileInternal->GetTSize()
859 * FileInternal->GetSamplesPerPixel()
860 * ( numberBitsAllocated / 8 );
861 size_t rgbSize = decSize;
862 if ( FileInternal->HasLUT() )
863 rgbSize = decSize * 3;
865 size_t userDataSize = PixelWriteConverter->GetUserDataSize();
869 if ( abs((long)(decSize-userDataSize))>1) // ignore padding zero
871 gdcmWarningMacro( "Data size (Raw) is incorrect. Should be "
872 << decSize << " / Found :"
878 if ( abs((long)(rgbSize-userDataSize))>1) // ignore padding zero
880 gdcmWarningMacro( "Data size (RGB) is incorrect. Should be "
881 << rgbSize << " / Found "
892 * \brief Updates the File to write RAW data (as opposed to RGB data)
893 * (modifies, when necessary, photochromatic interpretation,
894 * bits allocated, Pixels element VR)
895 * WARNING : if SetPhotometricInterpretationToMonochrome1() was called
896 * before Pixel Elements is modified :-(
898 void FileHelper::SetWriteToRaw()
900 if ( FileInternal->GetNumberOfScalarComponents() == 3
901 && !FileInternal->HasLUT() )
907 // 0x0028,0x0004 : Photometric Interpretation
908 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
909 if (FileInternal->HasLUT() )
911 photInt->SetString("PALETTE COLOR ");
915 if (GetPhotometricInterpretation() == 2)
916 photInt->SetString("MONOCHROME2 "); // 0 = Black
918 photInt->SetString("MONOCHROME1 "); // 0 = White !
921 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
922 PixelReadConverter->GetRawSize());
924 std::string vr = "OB";
925 if ( FileInternal->GetBitsAllocated()>8 )
927 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
929 // For non RAW data. Mainly JPEG/JPEG2000
930 if( WriteType == JPEG || WriteType == JPEG2000)
936 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
937 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
938 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
940 static_cast< uint32_t >(PixelWriteConverter->GetDataSize()) );
942 if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
944 ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );
947 Archive->Push(photInt);
948 Archive->Push(pixel);
956 * \brief Updates the File to write RGB data (as opposed to RAW data)
957 * (modifies, when necessary, photochromatic interpretation,
958 * samples per pixel, Planar configuration,
959 * bits allocated, bits stored, high bit -ACR 24 bits-
960 * Pixels element VR, pushes out the LUT, )
962 void FileHelper::SetWriteToRGB()
964 if ( FileInternal->GetNumberOfScalarComponents()==3 )
966 PixelReadConverter->BuildRGBImage();
968 DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
969 spp->SetString("3 "); // Don't drop trailing space
971 DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
972 planConfig->SetString("0 "); // Don't drop trailing space
974 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
975 photInt->SetString("RGB "); // Don't drop trailing space
977 if ( PixelReadConverter->GetRGB() )
979 PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
980 PixelReadConverter->GetRGBSize());
984 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
985 PixelReadConverter->GetRawSize());
988 std::string vr = "OB";
989 if ( FileInternal->GetBitsAllocated()>8 )
991 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
994 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
995 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
996 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
997 pixel->SetLength(PixelWriteConverter->GetDataSize());
1000 Archive->Push(planConfig);
1001 Archive->Push(photInt);
1002 Archive->Push(pixel);
1005 planConfig->Delete();
1010 Archive->Push(0x0028,0x1101);
1011 Archive->Push(0x0028,0x1102);
1012 Archive->Push(0x0028,0x1103);
1013 Archive->Push(0x0028,0x1201);
1014 Archive->Push(0x0028,0x1202);
1015 Archive->Push(0x0028,0x1203);
1017 // push out Palette Color Lookup Table UID, if any
1018 Archive->Push(0x0028,0x1199);
1020 // For old '24 Bits' ACR-NEMA
1021 // Thus, we have a RGB image and the bits allocated = 24 and
1022 // samples per pixels = 1 (in the read file)
1023 if ( FileInternal->GetBitsAllocated()==24 )
1025 DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
1026 bitsAlloc->SetString("8 ");
1028 DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
1029 bitsStored->SetString("8 ");
1031 DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
1032 highBit->SetString("7 ");
1034 Archive->Push(bitsAlloc);
1035 Archive->Push(bitsStored);
1036 Archive->Push(highBit);
1038 bitsAlloc->Delete();
1039 bitsStored->Delete();
1050 * \brief Restore the File write mode
1052 void FileHelper::RestoreWrite()
1054 Archive->Restore(0x0028,0x0002);
1055 Archive->Restore(0x0028,0x0004);
1057 Archive->Restore(0x0028,0x0006);
1058 Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1060 // For old ACR-NEMA (24 bits problem)
1061 Archive->Restore(0x0028,0x0100);
1062 Archive->Restore(0x0028,0x0101);
1063 Archive->Restore(0x0028,0x0102);
1066 Archive->Restore(0x0028,0x1101);
1067 Archive->Restore(0x0028,0x1102);
1068 Archive->Restore(0x0028,0x1103);
1069 Archive->Restore(0x0028,0x1201);
1070 Archive->Restore(0x0028,0x1202);
1071 Archive->Restore(0x0028,0x1203);
1073 // For the Palette Color Lookup Table UID
1074 Archive->Restore(0x0028,0x1203);
1076 // group 0002 may be pushed out for ACR-NEMA writting purposes
1077 Archive->Restore(0x0002,0x0000);
1078 Archive->Restore(0x0002,0x0001);
1079 Archive->Restore(0x0002,0x0002);
1080 Archive->Restore(0x0002,0x0003);
1081 Archive->Restore(0x0002,0x0010);
1082 Archive->Restore(0x0002,0x0012);
1083 Archive->Restore(0x0002,0x0013);
1084 Archive->Restore(0x0002,0x0016);
1085 Archive->Restore(0x0002,0x0100);
1086 Archive->Restore(0x0002,0x0102);
1091 * \brief Pushes out the whole group 0002
1092 * FIXME : better, set a flag to tell the writer not to write it ...
1093 * FIXME : method should probably have an other name !
1094 * SetWriteFileTypeToACR is NOT opposed to
1095 * SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1097 void FileHelper::SetWriteFileTypeToACR()
1099 Archive->Push(0x0002,0x0000);
1100 Archive->Push(0x0002,0x0001);
1101 Archive->Push(0x0002,0x0002);
1102 Archive->Push(0x0002,0x0003);
1103 Archive->Push(0x0002,0x0010);
1104 Archive->Push(0x0002,0x0012);
1105 Archive->Push(0x0002,0x0013);
1106 Archive->Push(0x0002,0x0016);
1107 Archive->Push(0x0002,0x0100);
1108 Archive->Push(0x0002,0x0102);
1112 * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1114 void FileHelper::SetWriteFileTypeToJPEG2000()
1116 std::string ts = Util::DicomString(
1117 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1119 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1127 * \brief Sets in the File the TransferSyntax to 'JPEG'
1129 void FileHelper::SetWriteFileTypeToJPEG()
1131 std::string ts = Util::DicomString(
1132 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGLosslessProcess14_1) );
1134 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1142 * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"
1144 void FileHelper::SetWriteFileTypeToExplicitVR()
1146 std::string ts = Util::DicomString(
1147 Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1149 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1156 * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"
1158 void FileHelper::SetWriteFileTypeToImplicitVR()
1160 std::string ts = Util::DicomString(
1161 Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1163 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1170 * \brief Set the Write not to Libido format
1172 void FileHelper::SetWriteToLibido()
1174 DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1175 DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1177 if ( oldRow && oldCol )
1179 std::string rows, columns;
1181 DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1182 DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1184 newRow->Copy(oldCol);
1185 newCol->Copy(oldRow);
1187 newRow->SetString(oldCol->GetString());
1188 newCol->SetString(oldRow->GetString());
1190 Archive->Push(newRow);
1191 Archive->Push(newCol);
1197 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1198 libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1199 Archive->Push(libidoCode);
1200 libidoCode->Delete();
1204 * \brief Set the Write not to No Libido format
1206 void FileHelper::SetWriteToNoLibido()
1208 DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1211 if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1213 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1214 libidoCode->SetString("");
1215 Archive->Push(libidoCode);
1216 libidoCode->Delete();
1222 * \brief Restore the Write format
1224 void FileHelper::RestoreWriteOfLibido()
1226 Archive->Restore(0x0028,0x0010);
1227 Archive->Restore(0x0028,0x0011);
1228 Archive->Restore(0x0008,0x0010);
1230 // Restore 'LibIDO-special' entries, if any
1231 Archive->Restore(0x0028,0x0015);
1232 Archive->Restore(0x0028,0x0016);
1233 Archive->Restore(0x0028,0x0017);
1234 Archive->Restore(0x0028,0x00199);
1238 * \brief Duplicates a DataEntry or creates it.
1239 * @param group Group number of the Entry
1240 * @param elem Element number of the Entry
1241 * @param vr Value Representation of the Entry
1242 * \return pointer to the new Bin Entry (NULL when creation failed).
1244 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1247 DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1250 if ( oldE && vr != GDCM_VRUNKNOWN )
1251 if ( oldE->GetVR() != vr )
1256 newE = DataEntry::New(group, elem, vr);
1261 newE = GetFile()->NewDataEntry(group, elem, vr);
1268 * \brief This method is called automatically, just before writting
1269 * in order to produce a 'True Dicom V3' image.
1271 * We cannot know *how* the user made the File :
1272 * (reading an old ACR-NEMA file or a not very clean DICOM file ...)
1273 * Just before writting :
1274 * - we check the Entries
1275 * - we create the mandatory entries if they are missing
1276 * - we modify the values if necessary
1277 * - we push the sensitive entries to the Archive
1278 * The writing process will restore the entries as they where before
1279 * entering FileHelper::CheckMandatoryElements, so the user will always
1280 * see the entries just as they were before he decided to write.
1283 * - Entries whose type is 1 are mandatory, with a mandatory value
1284 * - Entries whose type is 1c are mandatory-inside-a-Sequence,
1285 * with a mandatory value
1286 * - Entries whose type is 2 are mandatory, with an optional value
1287 * - Entries whose type is 2c are mandatory-inside-a-Sequence,
1288 * with an optional value
1289 * - Entries whose type is 3 are optional
1292 * - warn the user if we had to add some entries :
1293 * even if a mandatory entry is missing, we add it, with a default value
1294 * (we don't want to give up the writting process if user forgot to
1295 * specify Lena's Patient ID, for instance ...)
1296 * - read the whole PS 3.3 Part of DICOM (890 pages)
1297 * and write a *full* checker (probably one method per Modality ...)
1298 * Any contribution is welcome.
1299 * - write a user callable full checker, to allow post reading
1300 * and/or pre writting image consistency check.
1303 /* -------------------------------------------------------------------------------------
1304 To be moved to User's guide / WIKI ?
1306 We have to deal with 4 *very* different cases :
1307 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1309 -2) user modified the pixels of an existing image.
1311 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1313 -4) user modified/added some tags *without processing* the pixels (anonymization...)
1314 UNMODIFIED_PIXELS_IMAGE
1315 -Probabely some more to be added.
1316 --> Set it with FileHelper::SetContentType(int);
1318 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1321 0008 0012 Instance Creation Date
1322 0008 0013 Instance Creation Time
1323 0008 0018 SOP Instance UID
1324 are *always* created with the current values; user has *no* possible intervention on
1327 'Serie Instance UID'(0x0020,0x000e)
1328 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1329 created if it doesn't.
1330 The user is allowed to create his own Series/Studies,
1331 keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1333 The user shouldn't add any image to a 'Manufacturer Serie'
1334 but there is no way no to allow him to do that
1336 None of the 'shadow elements' are droped out.
1340 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1343 'Media Storage SOP Class UID' (0x0002,0x0002)
1344 'SOP Class UID' (0x0008,0x0016) are set to
1345 [Secondary Capture Image Storage]
1346 'Image Type' (0x0008,0x0008) is forced to "DERIVED\PRIMARY"
1347 Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1350 If 'SOP Class UID' exists in the native image ('true DICOM' image)
1351 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1352 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1353 whose value is the original 'SOP Class UID'
1354 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1355 whose value is the original 'SOP Class UID'
1357 3) TO DO : find a trick to allow user to pass to the writter the list of the Dicom images
1358 or the Series, (or the Study ?) he used to created his image
1359 (MIP, MPR, cartography image, ...)
1360 These info should be stored (?)
1361 0008 1110 SQ 1 Referenced Study Sequence
1362 0008 1115 SQ 1 Referenced Series Sequence
1363 0008 1140 SQ 1 Referenced Image Sequence
1365 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1366 'Media Storage SOP Class UID' (0x0002,0x0002)
1367 'SOP Class UID' (0x0008,0x0016)
1368 'Image Type' (0x0008,0x0008)
1369 'Conversion Type' (0x0008,0x0064)
1372 Bellow follows the full description (hope so !) of the consistency checks performed
1373 by gdcm::FileHelper::CheckMandatoryElements()
1376 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1377 -->'SOP Class UID' (0x0008,0x0016) are defaulted to
1378 [Secondary Capture Image Storage]
1379 --> 'Image Type' (0x0008,0x0008)
1380 is forced to "DERIVED\PRIMARY"
1381 (The written image is no longer an 'ORIGINAL' one)
1382 Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1384 --> Conversion Type (0x0008,0x0064)
1385 is defaulted to 'SYN' (Synthetic Image)
1386 when *he* knows he created his own image ex nihilo
1388 --> 'Modality' (0x0008,0x0060)
1389 is defaulted to "OT" (other) if missing.
1390 (a fully user created image belongs to *no* modality)
1392 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1393 --> 'Implementation Class UID' (0x0002,0x0012)
1394 are automatically generated; no user intervention possible
1396 --> 'Serie Instance UID'(0x0020,0x000e)
1397 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1398 created if it doesn't.
1399 The user is allowed to create his own Series/Studies,
1400 keeping the same 'Serie Instance UID' / 'Study Instance UID'
1403 The user shouldn't add any image to a 'Manufacturer Serie'
1404 but there is no way no to allowed him to do that
1406 --> If 'SOP Class UID' exists in the native image ('true DICOM' image)
1407 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1409 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1410 whose value is the original 'SOP Class UID'
1411 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1412 whose value is the original 'SOP Class UID'
1414 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1415 --> Pixel Spacing (0x0028,0x0030) is defaulted to "1.0\1.0"
1416 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1418 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1420 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1422 --> Study Date, Study Time are defaulted to current Date and Time
1423 (they remain unchanged if they exist)
1425 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from
1426 Image Orientation (Patient) : (0020|0037) or from
1427 Image Orientation (RET) : (0020 0035)
1429 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1430 are created, with empty value if there are missing.
1432 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1433 are defaulted with a 'gdcm' value.
1435 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1436 --> Referring Physician's Name (Type 2)
1437 are created, with empty value if there are missing.
1439 -------------------------------------------------------------------------------------*/
1441 void FileHelper::CheckMandatoryElements()
1443 std::string sop = Util::CreateUniqueUID();
1445 // --------------------- For Meta Elements ---------------------
1446 // just to remember : 'official' 0002 group
1447 if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1449 // Group 000002 (Meta Elements) already pushed out
1451 //0002 0000 UL 1 Meta Group Length
1452 //0002 0001 OB 1 File Meta Information Version
1453 //0002 0002 UI 1 Media Storage SOP Class UID
1454 //0002 0003 UI 1 Media Storage SOP Instance UID
1455 //0002 0010 UI 1 Transfer Syntax UID
1456 //0002 0012 UI 1 Implementation Class UID
1457 //0002 0013 SH 1 Implementation Version Name
1458 //0002 0016 AE 1 Source Application Entity Title
1459 //0002 0100 UI 1 Private Information Creator
1460 //0002 0102 OB 1 Private Information
1462 // Push out 'ACR-NEMA-special' entries, if any
1463 Archive->Push(0x0008,0x0001); // Length to End
1464 Archive->Push(0x0008,0x0010); // Recognition Code
1465 Archive->Push(0x0028,0x0005); // Image Dimension
1467 // Create them if not found
1468 // Always modify the value
1469 // Push the entries to the archive.
1470 CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1472 DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1473 e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1475 e_0002_0001->SetLength(2);
1476 Archive->Push(e_0002_0001);
1477 e_0002_0001->Delete();
1479 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1481 // we keep the original 'Media Storage SOP Class UID', we default it if missing
1482 CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1486 // It's *not* an image comming straight from a source. We force
1487 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1488 CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1491 // 'Media Storage SOP Instance UID'
1492 CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1494 // 'Implementation Class UID'
1495 // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1496 // seems to be Root UID + 4 digits (?)
1497 CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1499 // 'Implementation Version Name'
1500 std::string version = "GDCM ";
1501 version += Util::GetVersion();
1502 CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1505 // --------------------- For DataSet ---------------------
1507 // check whether 0018|0015 [CS] [Body Part Examined] value is UPPER CASE
1508 // (avoid dciodvfy to complain!)
1509 DataEntry *e_0018_0015 = FileInternal->GetDataEntry(0x0018, 0x0015);
1512 std::string bodyPartExamined = e_0018_0015->GetString();
1513 std::transform(bodyPartExamined.begin(), bodyPartExamined.end(), bodyPartExamined.begin(),
1514 (int(*)(int)) toupper);
1515 CopyMandatoryEntry(0x0018,0x0015,bodyPartExamined,"CS");
1518 if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1520 // If 'SOP Class UID' and 'SOP Instance UID' exist ('true DICOM' image)
1521 // we create the 'Source Image Sequence' SeqEntry
1522 // to hold informations about the Source Image
1524 DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1525 DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1526 if ( e_0008_0016 && e_0008_0018)
1528 // Create 'Source Image Sequence' SeqEntry
1529 SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1530 SQItem *sqi = SQItem::New(1);
1532 // create 'Referenced SOP Class UID' from 'SOP Class UID'
1534 DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1535 e_0008_1150->SetString( e_0008_0016->GetString());
1536 sqi->AddEntry(e_0008_1150);
1537 e_0008_1150->Delete();
1539 // create 'Referenced SOP Instance UID' from 'SOP Instance UID'
1540 DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1542 DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1543 e_0008_1155->SetString( e_0008_0018->GetString());
1544 sqi->AddEntry(e_0008_1155);
1545 e_0008_1155->Delete();
1547 sis->AddSQItem(sqi,1);
1550 // temporarily replaces any previous 'Source Image Sequence'
1553 // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1555 if ( ContentType == FILTERED_IMAGE) // the user *knows* he just modified the pixels
1557 DataEntry *e_0008_0008 = FileInternal->GetDataEntry(0x0008, 0x0008);
1560 std::string imageType = e_0008_0008->GetString();
1561 std::string::size_type p = imageType.find("ORIGINAL");
1562 if (p == 0) // image is ORIGINAL one
1564 // the image is no longer an 'Original' one
1565 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1567 // if Image Type was not ORIGINAL\..., we keep it.
1569 else // 0008_0008 was missing, wee add it.
1571 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1577 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1579 // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1580 CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");
1584 // It's *not* an image comming straight from a source. We force
1585 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1586 CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");
1589 Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1590 // Push out 'LibIDO-special' entries, if any
1591 Archive->Push(0x0028,0x0015);
1592 Archive->Push(0x0028,0x0016);
1593 Archive->Push(0x0028,0x0017);
1594 Archive->Push(0x0028,0x0198); // very old versions
1595 Archive->Push(0x0028,0x0199);
1597 // Replace deprecated 0028 0012 US Planes
1598 // by new 0028 0008 IS Number of Frames
1600 ///\todo : find if there is a rule!
1601 DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1604 CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1605 Archive->Push(0x0028,0x0012);
1608 // Deal with the pb of (Bits Stored = 12)
1609 // - we're gonna write the image as Bits Stored = 16
1610 if ( FileInternal->GetEntryString(0x0028,0x0100) == "12")
1612 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1615 // Check if user wasn't drunk ;-)
1617 std::ostringstream s;
1618 // check 'Bits Allocated' vs decent values
1619 int nbBitsAllocated = FileInternal->GetBitsAllocated();
1620 if ( (nbBitsAllocated == 0 || nbBitsAllocated > 32)
1621 || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1623 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1624 gdcmWarningMacro("(0028,0100) changed from "
1625 << nbBitsAllocated << " to 16 for consistency purpose");
1626 nbBitsAllocated = 16;
1628 // check 'Bits Stored' vs 'Bits Allocated'
1629 int nbBitsStored = FileInternal->GetBitsStored();
1630 if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1633 s << nbBitsAllocated;
1634 CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1635 gdcmWarningMacro("(0028,0101) changed from "
1636 << nbBitsStored << " to " << nbBitsAllocated
1637 << " for consistency purpose" );
1638 nbBitsStored = nbBitsAllocated;
1640 // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1641 int highBitPosition = FileInternal->GetHighBitPosition();
1642 if ( highBitPosition == 0 ||
1643 highBitPosition > nbBitsAllocated-1 ||
1644 highBitPosition < nbBitsStored-1 )
1647 s << nbBitsStored - 1;
1648 CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1649 gdcmWarningMacro("(0028,0102) changed from "
1650 << highBitPosition << " to " << nbBitsAllocated-1
1651 << " for consistency purpose");
1654 // check Pixel Representation (default it as 0 -unsigned-)
1656 DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1659 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1660 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1664 int sign = (int)e_0028_0103->GetValue(0);
1665 if (sign !=1 && sign !=0)
1667 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1668 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1672 std::string pixelAspectRatio = FileInternal->GetEntryString(0x0028,0x0034);
1673 if ( pixelAspectRatio == GDCM_UNFOUND ) // avoid conflict with pixelSpacing !
1675 std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1676 if ( pixelSpacing == GDCM_UNFOUND )
1678 pixelSpacing = "1.0\\1.0";
1679 // if missing, Pixel Spacing forced to "1.0\1.0"
1680 CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1683 // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1684 // --> This one is the *legal* one !
1685 if ( ContentType != USER_OWN_IMAGE)
1686 // we write it only when we are *sure* the image comes from
1687 // an imager (see also 0008,0x0064)
1688 CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1691 ///Exact meaning of RETired fields
1693 // See page 73 of ACR-NEMA_300-1988.pdf !
1695 // 0020,0020 : Patient Orientation :
1696 Patient direction of the first row and
1697 column of the images. The first entry id the direction of the raws, given by the
1698 direction of the last pixel in the first row from the first pixel in tha row.
1699 the second entry is the direction of the columns, given by the direction of the
1700 last pixel in the first column from the first pixel in that column.
1701 L : Left, F : Feet, A : Anterior, P : Posterior.
1702 Up to 3 letters can be used in combination to indicate oblique planes.
1704 //0020,0030 Image Position (RET)
1705 x,y,z coordinates im mm of the first pixel in the image
1707 // 0020,0035 Image Orientation (RET)
1708 Direction cosines of the R axis of the image system with respect to the
1709 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1710 the image system with respect to the same axes
1712 //0020,0050 Location
1713 An image location reference, standard for the modality (such as CT bed position),
1714 used to indicate position. Calculation of position for other purposes
1715 is only from (0020,0030) and (0020,0035)
1719 // if imagePositionPatient not found, default it with imagePositionRet, if any
1720 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1722 std::string imagePositionRet = FileInternal->GetEntryString(0x0020,0x0030);
1723 std::string imageOrientationRet = FileInternal->GetEntryString(0x0020,0x0035);
1724 std::string imagePositionPatient = FileInternal->GetEntryString(0x0020,0x0032);
1725 std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1727 if( imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1728 && imagePositionRet != GDCM_UNFOUND && imageOrientationRet != GDCM_UNFOUND)
1730 CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1731 Archive->Push(0x0020,0x0030);
1732 CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1733 Archive->Push(0x0020,0x0035);
1737 // Samples Per Pixel (type 1) : default to grayscale
1738 CheckMandatoryEntry(0x0028,0x0002,"1","US");
1740 // --- Check UID-related Entries ---
1742 // At the end, not to overwrite the original ones,
1743 // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
1744 // 'SOP Instance UID'
1745 CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1747 if ( ContentType == USER_OWN_IMAGE)
1749 gdcmDebugMacro( "USER_OWN_IMAGE (2)");
1751 // Other possible values are :
1752 // See PS 3.3, Page 408
1754 // DV = Digitized Video
1755 // DI = Digital Interface
1756 // DF = Digitized Film
1757 // WSD = Workstation
1758 // SD = Scanned Document
1759 // SI = Scanned Image
1761 // SYN = Synthetic Image
1763 CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1766 if ( ContentType == CREATED_IMAGE)
1768 /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1773 // ---- The user will never have to take any action on the following ----
1775 // new value for 'SOP Instance UID'
1776 //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1778 // Instance Creation Date
1779 const std::string &date = Util::GetCurrentDate();
1780 CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1782 // Instance Creation Time
1783 const std::string &time = Util::GetCurrentTime();
1784 CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1787 CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1789 CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1792 //CopyMandatoryEntry(0x0008,0x0050,"");
1793 CheckMandatoryEntry(0x0008,0x0050,"","SH");
1796 // ----- Add Mandatory Entries if missing ---
1797 // Entries whose type is 1 are mandatory, with a mandatory value
1798 // Entries whose type is 1c are mandatory-inside-a-Sequence,
1799 // with a mandatory value
1800 // Entries whose type is 2 are mandatory, with an optional value
1801 // Entries whose type is 2c are mandatory-inside-a-Sequence,
1802 // with an optional value
1803 // Entries whose type is 3 are optional
1805 // 'Study Instance UID'
1806 // Keep the value if exists
1807 // The user is allowed to create his own Study,
1808 // keeping the same 'Study Instance UID' for various images
1809 // The user may add images to a 'Manufacturer Study',
1810 // adding new Series to an already existing Study
1811 CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1813 // 'Serie Instance UID'
1814 // Keep the value if exists
1815 // The user is allowed to create his own Series,
1816 // keeping the same 'Serie Instance UID' for various images
1817 // The user shouldn't add any image to a 'Manufacturer Serie'
1818 // but there is no way no to prevent him for doing that
1819 CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1822 CheckMandatoryEntry(0x0020,0x0010,"","SH");
1825 CheckMandatoryEntry(0x0020,0x0011,"","IS");
1828 CheckMandatoryEntry(0x0020,0x0013,"","IS");
1830 // Patient Orientation
1831 // Can be computed from (0020|0037) : Image Orientation (Patient)
1832 GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1833 std::string ori = o->GetOrientation ( FileInternal );
1835 if (ori != "\\" && ori != GDCM_UNFOUND)
1836 CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1838 CheckMandatoryEntry(0x0020,0x0020,"","CS");
1840 // Default Patient Position to HFS
1841 CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1843 // Modality : if missing we set it to 'OTher'
1844 CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1846 // Manufacturer : if missing we set it to 'GDCM Factory'
1847 CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1849 // Institution Name : if missing we set it to 'GDCM Hospital'
1850 CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1852 // Patient's Name : if missing, we set it to 'GDCM^Patient'
1853 CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1855 // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1856 CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1858 // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1859 CheckMandatoryEntry(0x0010,0x0030,"","DA");
1861 // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1862 CheckMandatoryEntry(0x0010,0x0040,"","CS");
1864 // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1865 CheckMandatoryEntry(0x0008,0x0090,"","PN");
1868 // Deal with element 0x0000 (group length) of each group.
1869 // First stage : get all the different Groups
1872 DocEntry *d = FileInternal->GetFirstEntry();
1875 grHT[d->GetGroup()] = 0;
1876 d=FileInternal->GetNextEntry();
1878 // Second stage : add the missing ones (if any)
1879 for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)
1881 CheckMandatoryEntry(it->first, 0x0000, "0");
1883 // Third stage : update all 'zero level' groups length
1887 if (PhotometricInterpretation == 1)
1892 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1894 DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1897 //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1898 entry = DataEntry::New(group,elem,vr);
1899 entry->SetString(value);
1900 Archive->Push(entry);
1905 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
1906 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1908 //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1909 DataEntry *entry = DataEntry::New(group,elem,vr);
1910 entry->SetString(value);
1911 Archive->Push(entry);
1915 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1917 DataEntry *entry = CopyDataEntry(group,elem,vr);
1918 entry->SetString(value);
1919 Archive->Push(entry);
1924 * \brief Restore in the File the initial group 0002
1926 void FileHelper::RestoreWriteMandatory()
1928 // group 0002 may be pushed out for ACR-NEMA writting purposes
1929 Archive->Restore(0x0002,0x0000);
1930 Archive->Restore(0x0002,0x0001);
1931 Archive->Restore(0x0002,0x0002);
1932 Archive->Restore(0x0002,0x0003);
1933 Archive->Restore(0x0002,0x0010);
1934 Archive->Restore(0x0002,0x0012);
1935 Archive->Restore(0x0002,0x0013);
1936 Archive->Restore(0x0002,0x0016);
1937 Archive->Restore(0x0002,0x0100);
1938 Archive->Restore(0x0002,0x0102);
1940 // FIXME : Check if none is missing !
1942 Archive->Restore(0x0008,0x0012);
1943 Archive->Restore(0x0008,0x0013);
1944 Archive->Restore(0x0008,0x0016);
1945 Archive->Restore(0x0008,0x0018);
1946 Archive->Restore(0x0008,0x0060);
1947 Archive->Restore(0x0008,0x0070);
1948 Archive->Restore(0x0008,0x0080);
1949 Archive->Restore(0x0008,0x0090);
1950 Archive->Restore(0x0008,0x2112);
1952 Archive->Restore(0x0010,0x0010);
1953 Archive->Restore(0x0010,0x0030);
1954 Archive->Restore(0x0010,0x0040);
1956 Archive->Restore(0x0020,0x000d);
1957 Archive->Restore(0x0020,0x000e);
1961 * \brief CallStartMethod
1963 void FileHelper::CallStartMethod()
1967 CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1971 * \brief CallProgressMethod
1973 void FileHelper::CallProgressMethod()
1975 CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1979 * \brief CallEndMethod
1981 void FileHelper::CallEndMethod()
1984 CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1987 //-----------------------------------------------------------------------------
1990 * \brief Factorization for various forms of constructors.
1992 void FileHelper::Initialize()
1995 ContentType = USER_OWN_IMAGE;
1997 WriteMode = WMODE_RAW;
1998 WriteType = ExplicitVR;
2000 PhotometricInterpretation = 2; // Black = 0
2002 PixelReadConverter = new PixelReadConvert;
2003 PixelWriteConverter = new PixelWriteConvert;
2004 Archive = new DocEntryArchive( FileInternal );
2006 KeepOverlays = false;
2010 * \brief Reads/[decompresses] the pixels,
2011 * *without* making RGB from Palette Colors
2012 * @return the pixels area, whatever its type
2013 * (uint8_t is just for prototyping : feel free to Cast it)
2015 uint8_t *FileHelper::GetRaw()
2017 PixelReadConverter->SetUserFunction( UserFunction );
2019 uint8_t *raw = PixelReadConverter->GetRaw();
2022 // The Raw image migth not be loaded yet:
2023 std::ifstream *fp = FileInternal->OpenFile();
2024 PixelReadConverter->ReadAndDecompressPixelData( fp );
2026 FileInternal->CloseFile();
2028 raw = PixelReadConverter->GetRaw();
2031 gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
2039 * \brief Deal with Grey levels i.e. re-arange them
2040 * to have low values = dark, high values = bright
2042 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
2044 uint32_t i; // to please M$VC6
2047 // Number of Bits Allocated for storing a Pixel is defaulted to 16
2048 // when absent from the file.
2049 int bitsAllocated = FileInternal->GetBitsAllocated();
2050 if ( bitsAllocated == 0 )
2055 else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2059 // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2060 // when absent from the file.
2061 int bitsStored = FileInternal->GetBitsStored();
2062 if ( bitsStored == 0 )
2064 bitsStored = bitsAllocated;
2067 if (!FileInternal->IsSignedPixelData())
2069 if ( bitsAllocated == 8 )
2071 uint8_t *deb = (uint8_t *)raw;
2072 for (i=0; i<rawSize; i++)
2080 if ( bitsAllocated == 16 )
2083 for (j=0; j<bitsStored-1; j++)
2085 mask = (mask << 1) +1; // will be fff when BitsStored=12
2088 uint16_t *deb = (uint16_t *)raw;
2089 for (i=0; i<rawSize/2; i++)
2099 if ( bitsAllocated == 8 )
2101 uint8_t smask8 = 255;
2102 uint8_t *deb = (uint8_t *)raw;
2103 for (i=0; i<rawSize; i++)
2105 *deb = smask8 - *deb;
2110 if ( bitsAllocated == 16 )
2112 uint16_t smask16 = 65535;
2113 uint16_t *deb = (uint16_t *)raw;
2114 for (i=0; i<rawSize/2; i++)
2116 *deb = smask16 - *deb;
2124 //-----------------------------------------------------------------------------
2126 * \brief Prints the FileInternal + info on PixelReadConvertor
2127 * @param os ostream we want to print in
2128 * @param indent (unused)
2130 void FileHelper::Print(std::ostream &os, std::string const &)
2132 FileInternal->SetPrintLevel(PrintLevel);
2133 FileInternal->Print(os);
2135 if ( FileInternal->IsReadable() )
2137 PixelReadConverter->SetPrintLevel(PrintLevel);
2138 PixelReadConverter->Print(os);
2142 //-----------------------------------------------------------------------------
2143 } // end namespace gdcm
2146 /* Probabely something to be added to use Rescale Slope/Intercept
2147 Have a look at ITK code !
2149 // Internal function to rescale pixel according to Rescale Slope/Intercept
2150 template<class TBuffer, class TSource>
2151 void RescaleFunction(TBuffer* buffer, TSource *source,
2152 double slope, double intercept, size_t size)
2154 size /= sizeof(TSource);
2156 if (slope != 1.0 && intercept != 0.0)
2158 // Duff's device. Instead of this code:
2160 // for(unsigned int i=0; i<size; i++)
2162 // buffer[i] = (TBuffer)(source[i]*slope + intercept);
2165 // use Duff's device which exploits "fall through"
2166 register size_t n = (size + 7) / 8;
2169 case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2170 case 7: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2171 case 6: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2172 case 5: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2173 case 4: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2174 case 3: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2175 case 2: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2176 case 1: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2180 else if (slope == 1.0 && intercept != 0.0)
2182 // Duff's device. Instead of this code:
2184 // for(unsigned int i=0; i<size; i++)
2186 // buffer[i] = (TBuffer)(source[i] + intercept);
2189 // use Duff's device which exploits "fall through"
2190 register size_t n = (size + 7) / 8;
2193 case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2194 case 7: *buffer++ = (TBuffer)(*source++ + intercept);
2195 case 6: *buffer++ = (TBuffer)(*source++ + intercept);
2196 case 5: *buffer++ = (TBuffer)(*source++ + intercept);
2197 case 4: *buffer++ = (TBuffer)(*source++ + intercept);
2198 case 3: *buffer++ = (TBuffer)(*source++ + intercept);
2199 case 2: *buffer++ = (TBuffer)(*source++ + intercept);
2200 case 1: *buffer++ = (TBuffer)(*source++ + intercept);
2204 else if (slope != 1.0 && intercept == 0.0)
2206 // Duff's device. Instead of this code:
2208 // for(unsigned int i=0; i<size; i++)
2210 // buffer[i] = (TBuffer)(source[i]*slope);
2213 // use Duff's device which exploits "fall through"
2214 register size_t n = (size + 7) / 8;
2217 case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2218 case 7: *buffer++ = (TBuffer)((*source++)*slope);
2219 case 6: *buffer++ = (TBuffer)((*source++)*slope);
2220 case 5: *buffer++ = (TBuffer)((*source++)*slope);
2221 case 4: *buffer++ = (TBuffer)((*source++)*slope);
2222 case 3: *buffer++ = (TBuffer)((*source++)*slope);
2223 case 2: *buffer++ = (TBuffer)((*source++)*slope);
2224 case 1: *buffer++ = (TBuffer)((*source++)*slope);
2230 // Duff's device. Instead of this code:
2232 // for(unsigned int i=0; i<size; i++)
2234 // buffer[i] = (TBuffer)(source[i]);
2237 // use Duff's device which exploits "fall through"
2238 register size_t n = (size + 7) / 8;
2241 case 0: do { *buffer++ = (TBuffer)(*source++);
2242 case 7: *buffer++ = (TBuffer)(*source++);
2243 case 6: *buffer++ = (TBuffer)(*source++);
2244 case 5: *buffer++ = (TBuffer)(*source++);
2245 case 4: *buffer++ = (TBuffer)(*source++);
2246 case 3: *buffer++ = (TBuffer)(*source++);
2247 case 2: *buffer++ = (TBuffer)(*source++);
2248 case 1: *buffer++ = (TBuffer)(*source++);
2255 template<class TSource>
2256 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2257 void* buffer, TSource *source,
2258 double slope, double intercept, size_t size)
2262 case ImageIOBase::UCHAR:
2263 RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2265 case ImageIOBase::CHAR:
2266 RescaleFunction( (char *)buffer, source, slope, intercept, size);
2268 case ImageIOBase::USHORT:
2269 RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2271 case ImageIOBase::SHORT:
2272 RescaleFunction( (short *)buffer, source, slope, intercept, size);
2274 case ImageIOBase::UINT:
2275 RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2277 case ImageIOBase::INT:
2278 RescaleFunction( (int *)buffer, source, slope, intercept, size);
2280 case ImageIOBase::FLOAT:
2281 RescaleFunction( (float *)buffer, source, slope, intercept, size);
2283 case ImageIOBase::DOUBLE:
2284 RescaleFunction( (double *)buffer, source, slope, intercept, size);
2287 ::itk::OStringStream message;
2288 message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2289 ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);