1 /*=========================================================================
4 Module: $RCSfile: gdcmFileHelper.cxx,v $
7 Date: $Date: 2007/07/13 08:17:21 $
8 Version: $Revision: 1.117 $
10 Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
11 l'Image). All rights reserved. See Doc/License.txt or
12 http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
14 This software is distributed WITHOUT ANY WARRANTY; without even
15 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 PURPOSE. See the above copyright notices for more information.
18 =========================================================================*/
20 #include "gdcmFileHelper.h"
21 #include "gdcmGlobal.h"
23 #include "gdcmDocument.h"
24 #include "gdcmDebug.h"
26 #include "gdcmSeqEntry.h"
27 #include "gdcmSQItem.h"
28 #include "gdcmDataEntry.h"
29 #include "gdcmDocEntry.h"
31 #include "gdcmPixelReadConvert.h"
32 #include "gdcmPixelWriteConvert.h"
33 #include "gdcmDocEntryArchive.h"
34 #include "gdcmDictSet.h"
35 #include "gdcmOrientation.h"
37 #if defined(__BORLANDC__)
38 #include <mem.h> // for memset
44 // ----------------------------- WARNING -------------------------
46 These lines will be moved to the document-to-be 'User's Guide'
48 // To read an image, user needs a gdcm::File
49 gdcm::File *f = new gdcm::File(fileName);
51 // user may also decide he doesn't want to load some parts of the header
52 gdcm::File *f = new gdcm::File();
53 f->SetFileName(fileName);
54 f->SetLoadMode(LD_NOSEQ); // or
55 f->SetLoadMode(LD_NOSHADOW); // or
56 f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW); // or
57 f->SetLoadMode(LD_NOSHADOWSEQ);
60 // To decide whether it's an 'image of interest for him, or not,
61 // user can now check some values
62 std::string v = f->GetEntryValue(groupNb,ElementNb);
64 // to get the pixels, user needs a gdcm::FileHelper
65 gdcm::FileHelper *fh = new gdcm::FileHelper(f);
66 // user may ask not to convert Palette (if any) to RGB
67 uint8_t *pixels = fh->GetImageDataRaw();
68 int imageLength = fh->GetImageDataRawSize();
69 // He can now use the pixels, create a new image, ...
70 uint8_t *userPixels = ...
72 To re-write the image, user re-uses the gdcm::FileHelper
74 fh->SetImageData( userPixels, userPixelsLength);
75 fh->SetTypeToRaw(); // Even if it was possible to convert Palette to RGB
78 // If user wants to write the file as MONOCHROME1 (0=white)
79 fh->SetPhotometricInterpretationToMonochrome1();
81 fh->SetWriteTypeToDcmExpl(); // he wants Explicit Value Representation
82 // Little Endian is the default
83 // no other value is allowed
84 (-->SetWriteType(ExplicitVR);)
85 -->WriteType = ExplicitVR;
86 fh->Write(newFileName); // overwrites the file, if any
89 fh->WriteDcmExplVR(newFileName);
92 // ----------------------------- WARNING -------------------------
94 These lines will be moved to the document-to-be 'Developer's Guide'
96 WriteMode : WMODE_RAW / WMODE_RGB
97 WriteType : ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO
98 PhotometricInterpretation : MONOCHROME2 (0=black), MONOCHROME2 (0=white)
101 fh->SetWriteMode(WMODE_RAW / WMODE_RGB)
103 fh->SetWriteType( ImplicitVR/ExplicitVR/ACR/ACR_LIBIDO/JPEG/JPEG2000)
105 fh->Write(newFileName);
106 CheckMandatoryElements(); // Checks existing ones / Add missing ones
107 Fix VR if unknown elements
108 SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR(); /
109 SetWriteFileTypeToACR() / SetWriteFileTypeToJPEG() / SetWriteFileTypeToJ2K()
110 (Modifies TransferSyntax if any; Pushes to the Archives old one)
111 SetWriteToRaw(); / SetWriteToRGB();
112 (Modifies and pushes to the Archive, when necessary : photochr. interp.,
113 samples per pixel, Planar configuration,
114 bits allocated, bits stored, high bit -ACR 24 bits-
115 Pixels element VR, pushes out the LUT )
117 Sets Photometric Interpretation
118 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
119 Sets VR, BinArea, Length for PixelData
122 Archive->Push(photInt);
123 Archive->Push(pixel);
127 if NumberOfScalarComponents==1
128 SetWriteToRaw(); return;
129 PixelReadConverter->BuildRGBImage()
130 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
131 Archives spp, planConfig,photInt, pixel
133 CheckWriteIntegrity();
134 (checks user given pixels length)
135 FileInternal->Write(fileName,WriteType)
136 fp = opens file(fileName); // out|binary
137 ComputeGroup0002Length( );
138 Document::WriteContent(fp, writetype);
139 writes Dicom File Preamble not ACR-NEMA
140 ElementSet::WriteContent(fp, writetype);
141 writes recursively all DataElements
143 (moves back to the gdcm::File all the archived elements)
149 namespace GDCM_NAME_SPACE
151 typedef std::map<uint16_t, int> GroupHT; // Hash Table
152 //-------------------------------------------------------------------------
153 // Constructor / Destructor
155 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
156 * file (gdcm::File only deals with the ... header)
157 * Opens (in read only and when possible) an existing file and checks
158 * for DICOM compliance. Returns NULL on failure.
159 * It will be up to the user to load the pixels into memory
160 * ( GetImageDataSize() + GetImageData() methods)
161 * \note the in-memory representation of all available tags found in
162 * the DICOM header is post-poned to first header information access.
163 * This avoid a double parsing of public part of the header when
164 * one sets an a posteriori shadow dictionary (efficiency can be
165 * seen as a side effect).
167 FileHelper::FileHelper( )
169 FileInternal = File::New( );
174 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
175 * file (File only deals with the ... header)
176 * Opens (in read only and when possible) an existing file and checks
177 * for DICOM compliance. Returns NULL on failure.
178 * It will be up to the user to load the pixels into memory
179 * ( GetImageDataSize() + GetImageData() methods)
180 * \note the in-memory representation of all available tags found in
181 * the DICOM header is post-poned to first header information access.
182 * This avoid a double parsing of public part of the header when
183 * user sets an a posteriori shadow dictionary (efficiency can be
184 * seen as a side effect).
185 * @param header already built Header
187 FileHelper::FileHelper(File *header)
189 gdcmAssertMacro(header);
191 FileInternal = header;
192 FileInternal->Register();
194 if ( FileInternal->IsReadable() )
196 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
201 * \brief canonical destructor
202 * \note If the header (gdcm::File) was created by the FileHelper constructor,
203 * it is destroyed by the FileHelper
205 FileHelper::~FileHelper()
207 if ( PixelReadConverter )
209 delete PixelReadConverter;
211 if ( PixelWriteConverter )
213 delete PixelWriteConverter;
220 FileInternal->Unregister();
223 //-----------------------------------------------------------------------------
227 * \brief Sets the LoadMode of the internal gdcm::File as a boolean string.
228 * NO_SEQ, NO_SHADOW, NO_SHADOWSEQ ... (nothing more, right now)
229 * WARNING : before using NO_SHADOW, be sure *all* your files
230 * contain accurate values in the 0x0000 element (if any)
231 * of *each* Shadow Group. The parser will fail if the size is wrong !
232 * @param loadMode Load mode to be used
234 void FileHelper::SetLoadMode(int loadMode)
236 GetFile()->SetLoadMode( loadMode );
239 * \brief Sets the LoadMode of the internal gdcm::File
240 * @param fileName name of the file to be open
242 void FileHelper::SetFileName(std::string const &fileName)
244 FileInternal->SetFileName( fileName );
249 * @return false if file cannot be open or no swap info was found,
250 * or no tag was found.
252 bool FileHelper::Load()
254 if ( !FileInternal->Load() )
257 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
262 * \brief Accesses an existing DataEntry through its (group, element)
263 * and modifies its content with the given value.
264 * @param content new value (string) to substitute with
265 * @param group group number of the Dicom Element to modify
266 * @param elem element number of the Dicom Element to modify
267 * \return false if DataEntry not found
269 bool FileHelper::SetEntryString(std::string const &content,
270 uint16_t group, uint16_t elem)
272 return FileInternal->SetEntryString(content, group, elem);
277 * \brief Accesses an existing DataEntry through its (group, element)
278 * and modifies its content with the given value.
279 * @param content new value (void* -> uint8_t*) to substitute with
280 * @param lgth new value length
281 * @param group group number of the Dicom Element to modify
282 * @param elem element number of the Dicom Element to modify
283 * \return false if DataEntry not found
285 bool FileHelper::SetEntryBinArea(uint8_t *content, int lgth,
286 uint16_t group, uint16_t elem)
288 return FileInternal->SetEntryBinArea(content, lgth, group, elem);
292 * \brief Modifies the value of a given DataEntry when it exists.
293 * Creates it with the given value when unexistant.
294 * @param content (string) value to be set
295 * @param group Group number of the Entry
296 * @param elem Element number of the Entry
297 * @param vr Value Representation of the DataElement to be inserted
298 * \return pointer to the modified/created DataEntry (NULL when creation
301 DataEntry *FileHelper::InsertEntryString(std::string const &content,
302 uint16_t group, uint16_t elem,
305 return FileInternal->InsertEntryString(content, group, elem, vr);
309 * \brief Modifies the value of a given DataEntry when it exists.
310 * Creates it with the given value when unexistant.
311 * A copy of the binArea is made to be kept in the Document.
312 * @param binArea (binary) value to be set
313 * @param lgth new value length
314 * @param group Group number of the Entry
315 * @param elem Element number of the Entry
316 * @param vr Value Representation of the DataElement to be inserted
317 * \return pointer to the modified/created DataEntry (NULL when creation
320 DataEntry *FileHelper::InsertEntryBinArea(uint8_t *binArea, int lgth,
321 uint16_t group, uint16_t elem,
324 return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem, vr);
328 * \brief Adds an empty SeqEntry
329 * (remove any existing entry with same group,elem)
330 * @param group Group number of the Entry
331 * @param elem Element number of the Entry
332 * \return pointer to the created SeqEntry (NULL when creation
335 SeqEntry *FileHelper::InsertSeqEntry(uint16_t group, uint16_t elem)
337 return FileInternal->InsertSeqEntry(group, elem);
341 * \brief Get the size of the image data
342 * If the image can be RGB (with a lut or by default), the size
343 * corresponds to the RGB image
344 * (use GetImageDataRawSize if you want to be sure to get *only*
345 * the size of the pixels)
346 * @return The image size
348 size_t FileHelper::GetImageDataSize()
350 if ( PixelWriteConverter->GetUserData() )
352 return PixelWriteConverter->GetUserDataSize();
354 return PixelReadConverter->GetRGBSize();
358 * \brief Get the size of the image data.
359 * If the image could be converted to RGB using a LUT,
360 * this transformation is not taken into account by GetImageDataRawSize
361 * (use GetImageDataSize if you wish)
362 * @return The raw image size
364 size_t FileHelper::GetImageDataRawSize()
366 if ( PixelWriteConverter->GetUserData() )
368 return PixelWriteConverter->GetUserDataSize();
370 return PixelReadConverter->GetRawSize();
374 * \brief brings pixels into memory :
375 * - Allocates necessary memory,
376 * - Reads the pixels from disk (uncompress if necessary),
377 * - Transforms YBR pixels, if any, into RGB pixels,
378 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
379 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
380 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
381 * @return Pointer to newly allocated pixel data.
382 * (uint8_t is just for prototyping. feel free to cast)
383 * NULL if alloc fails
385 uint8_t *FileHelper::GetImageData()
387 if ( PixelWriteConverter->GetUserData() )
389 return PixelWriteConverter->GetUserData();
394 // If the decompression failed nothing can be done.
398 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
400 return PixelReadConverter->GetRGB();
404 // When no LUT or LUT conversion fails, return the Raw
405 return PixelReadConverter->GetRaw();
410 * \brief brings pixels into memory :
411 * - Allocates necessary memory,
412 * - Transforms YBR pixels (if any) into RGB pixels
413 * - Transforms 3 planes R, G, B (if any) into a single RGB Plane
414 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
415 * - DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
416 * @return Pointer to newly allocated pixel data.
417 * (uint8_t is just for prototyping. feel free to cast)
418 * NULL if alloc fails
420 uint8_t *FileHelper::GetImageDataRaw ()
425 //#ifndef GDCM_LEGACY_REMOVE
427 * \brief Useless function, since PixelReadConverter forces us
428 * copy the Pixels anyway.
429 * Reads the pixels from disk (uncompress if necessary),
430 * Transforms YBR pixels, if any, into RGB pixels
431 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
432 * Transforms single Grey plane + 3 Palettes into a RGB Plane
433 * Copies at most MaxSize bytes of pixel data to caller allocated
435 * \warning This function allows people that want to build a volume
436 * from an image stack *not to* have, first to get the image pixels,
437 * and then move them to the volume area.
438 * It's absolutely useless for any VTK user since vtk chooses
439 * to invert the lines of an image, that is the last line comes first
440 * (for some axis related reasons?). Hence he will have
441 * to load the image line by line, starting from the end.
442 * VTK users have to call GetImageData
444 * @param destination Address (in caller's memory space) at which the
445 * pixel data should be copied
446 * @param maxSize Maximum number of bytes to be copied. When MaxSize
447 * is not sufficient to hold the pixel data the copy is not
448 * executed (i.e. no partial copy).
449 * @return On success, the number of bytes actually copied. Zero on
450 * failure e.g. MaxSize is lower than necessary.
453 size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
457 // If the decompression failed nothing can be done.
461 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
463 if ( PixelReadConverter->GetRGBSize() > maxSize )
465 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
469 (void*)PixelReadConverter->GetRGB(),
470 PixelReadConverter->GetRGBSize() );
471 return PixelReadConverter->GetRGBSize();
474 // Either no LUT conversion necessary or LUT conversion failed
475 if ( PixelReadConverter->GetRawSize() > maxSize )
477 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
481 (void *)PixelReadConverter->GetRaw(),
482 PixelReadConverter->GetRawSize() );
483 return PixelReadConverter->GetRawSize();
489 * \brief Points the internal pointer to the callers inData
490 * image representation, BUT WITHOUT COPYING THE DATA.
491 * 'image' Pixels are presented as C-like 2D arrays : line per line.
492 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
493 * \warning Since the pixels are not copied, it is the caller's responsability
494 * not to deallocate its data before gdcm uses them (e.g. with
495 * the Write() method )
496 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
497 * user is allowed to pass any kind of pixelsn since the size is
499 * @param expectedSize total image size, *in Bytes*
501 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
503 PixelWriteConverter->SetUserData(inData, expectedSize);
507 * \brief Set the image data defined by the user
508 * \warning When writting the file, this data are get as default data to write
509 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
510 * user is allowed to pass any kind of pixels since the size is
512 * @param expectedSize total image size, *in Bytes*
514 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
516 if( WriteType == JPEG2000 )
518 PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
520 else if( WriteType == JPEG )
522 PixelWriteConverter->SetCompressJPEGUserData(inData, expectedSize, FileInternal);
526 PixelWriteConverter->SetUserData(inData, expectedSize);
531 * \brief Get the image data defined by the user
532 * \warning When writting the file, this data are get as default data to write
534 uint8_t *FileHelper::GetUserData()
536 return PixelWriteConverter->GetUserData();
540 * \brief Get the image data size defined by the user
541 * \warning When writting the file, this data are get as default data to write
543 size_t FileHelper::GetUserDataSize()
545 return PixelWriteConverter->GetUserDataSize();
549 * \brief Get the image data from the file.
550 * If a LUT is found, the data are expanded to be RGB
552 uint8_t *FileHelper::GetRGBData()
554 return PixelReadConverter->GetRGB();
558 * \brief Get the image data size from the file.
559 * If a LUT is found, the data are expanded to be RGB
561 size_t FileHelper::GetRGBDataSize()
563 return PixelReadConverter->GetRGBSize();
567 * \brief Get the image data from the file.
568 * Even when a LUT is found, the data are not expanded to RGB!
570 uint8_t *FileHelper::GetRawData()
572 return PixelReadConverter->GetRaw();
576 * \brief Get the image data size from the file.
577 * Even when a LUT is found, the data are not expanded to RGB!
579 size_t FileHelper::GetRawDataSize()
581 return PixelReadConverter->GetRawSize();
585 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT
587 uint8_t* FileHelper::GetLutRGBA()
589 if ( PixelReadConverter->GetLutRGBA() ==0 )
590 PixelReadConverter->BuildLUTRGBA();
591 return PixelReadConverter->GetLutRGBA();
595 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Number
597 int FileHelper::GetLutItemNumber()
599 return PixelReadConverter->GetLutItemNumber();
603 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Size
605 int FileHelper::GetLutItemSize()
607 return PixelReadConverter->GetLutItemSize();
611 * \brief Writes on disk A SINGLE Dicom file
612 * NO test is performed on processor "Endiannity".
613 * It's up to the user to call his Reader properly
614 * @param fileName name of the file to be created
615 * (any already existing file is over written)
616 * @return false if write fails
618 bool FileHelper::WriteRawData(std::string const &fileName)
620 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
623 gdcmWarningMacro( "Fail to open (write) file:" << fileName.c_str());
627 if ( PixelWriteConverter->GetUserData() )
629 fp1.write( (char *)PixelWriteConverter->GetUserData(),
630 PixelWriteConverter->GetUserDataSize() );
632 else if ( PixelReadConverter->GetRGB() )
634 fp1.write( (char *)PixelReadConverter->GetRGB(),
635 PixelReadConverter->GetRGBSize());
637 else if ( PixelReadConverter->GetRaw() )
639 fp1.write( (char *)PixelReadConverter->GetRaw(),
640 PixelReadConverter->GetRawSize());
644 gdcmErrorMacro( "Nothing written." );
653 * \brief Writes on disk A SINGLE Dicom file,
654 * using the Implicit Value Representation convention
655 * NO test is performed on processor "Endianity".
656 * @param fileName name of the file to be created
657 * (any already existing file is overwritten)
658 * @return false if write fails
661 bool FileHelper::WriteDcmImplVR (std::string const &fileName)
663 SetWriteTypeToDcmImplVR();
664 return Write(fileName);
668 * \brief Writes on disk A SINGLE Dicom file,
669 * using the Explicit Value Representation convention
670 * NO test is performed on processor "Endiannity".
671 * @param fileName name of the file to be created
672 * (any already existing file is overwritten)
673 * @return false if write fails
676 bool FileHelper::WriteDcmExplVR (std::string const &fileName)
678 SetWriteTypeToDcmExplVR();
679 return Write(fileName);
683 * \brief Writes on disk A SINGLE Dicom file,
684 * using the ACR-NEMA convention
685 * NO test is performed on processor "Endiannity".
686 * (a l'attention des logiciels cliniques
687 * qui ne prennent en entrée QUE des images ACR ...
688 * \warning if a DICOM_V3 header is supplied,
689 * groups < 0x0008 and shadow groups are ignored
690 * \warning NO TEST is performed on processor "Endiannity".
691 * @param fileName name of the file to be created
692 * (any already existing file is overwritten)
693 * @return false if write fails
696 bool FileHelper::WriteAcr (std::string const &fileName)
699 return Write(fileName);
703 * \brief Writes on disk A SINGLE Dicom file,
704 * @param fileName name of the file to be created
705 * (any already existing file is overwritten)
706 * @return false if write fails
708 bool FileHelper::Write(std::string const &fileName)
710 CheckMandatoryElements(); //called once, here !
717 SetWriteFileTypeToImplicitVR();
720 case Unknown: // should never happen; ExplicitVR is the default value
723 // User should ask gdcm to write an image in Explicit VR mode
724 // only when he is sure *all* the VR of *all* the DataElements is known.
725 // i.e : when there are *only* Public Groups
726 // or *all* the Shadow Groups are fully described in the relevant Shadow
728 // Let's just *dream* about it; *never* trust a user !
729 // We turn to Implicit VR if at least the VR of one element is unknown.
731 /// \TODO : better we put vr=UN for undocumented Shadow Groups !
733 e = FileInternal->GetFirstEntry();
736 if (e->GetVR() == " ")
738 SetWriteTypeToDcmImplVR();
739 SetWriteFileTypeToImplicitVR();
743 e = FileInternal->GetNextEntry();
748 SetWriteFileTypeToExplicitVR();
752 SetWriteFileTypeToExplicitVR(); // to see JPRx
756 // NOTHING is done here just for LibIDO.
757 // Just to avoid further trouble if user creates a file ex-nihilo,
758 // wants to write it as an ACR-NEMA file,
759 // and forgets to create any Entry belonging to group 0008
761 // We add Recognition Code (RET)
762 if ( ! FileInternal->GetDataEntry(0x0008, 0x0010) )
763 FileInternal->InsertEntryString("ACR-NEMA V1.0 ",
764 0x0008, 0x0010, "LO");
765 SetWriteFileTypeToACR();
766 // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
769 /// \todo FIXME : JPEG may be either ExplicitVR or ImplicitVR
771 SetWriteFileTypeToJPEG();
775 SetWriteFileTypeToJPEG2000();
779 // --------------------------------------------------------------
780 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
782 // if recognition code tells us we dealt with a LibIDO image
783 // we reproduce on disk the switch between lineNumber and columnNumber
784 // just before writting ...
785 /// \todo the best trick would be *change* the recognition code
786 /// but pb expected if user deals with, e.g. COMPLEX images
788 if ( WriteType == ACR_LIBIDO )
794 SetWriteToNoLibido();
796 // ----------------- End of Special Patch ----------------
801 SetWriteToRaw(); // modifies and pushes to the archive, when necessary
804 SetWriteToRGB(); // modifies and pushes to the archive, when necessary
808 bool check = CheckWriteIntegrity(); // verifies length
809 if (WriteType == JPEG || WriteType == JPEG2000)
814 check = FileInternal->Write(fileName,WriteType);
818 // RestoreWriteFileType();
819 // RestoreWriteMandatory();
821 // --------------------------------------------------------------
822 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
824 // ...and we restore the header to be Dicom Compliant again
825 // just after writting
826 RestoreWriteOfLibido();
827 // ----------------- End of Special Patch ----------------
832 //-----------------------------------------------------------------------------
835 * \brief Verifies the size of the user given PixelData
836 * @return true if check is successfull
838 bool FileHelper::CheckWriteIntegrity()
840 if ( PixelWriteConverter->GetUserData() )
842 int numberBitsAllocated = FileInternal->GetBitsAllocated();
843 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
845 gdcmWarningMacro( "numberBitsAllocated changed from "
846 << numberBitsAllocated << " to 16 "
847 << " for consistency purpose" );
848 numberBitsAllocated = 16;
851 size_t decSize = FileInternal->GetXSize()
852 * FileInternal->GetYSize()
853 * FileInternal->GetZSize()
854 * FileInternal->GetTSize()
855 * FileInternal->GetSamplesPerPixel()
856 * ( numberBitsAllocated / 8 );
857 size_t rgbSize = decSize;
858 if ( FileInternal->HasLUT() )
859 rgbSize = decSize * 3;
864 if ( decSize!=PixelWriteConverter->GetUserDataSize() )
866 gdcmWarningMacro( "Data size (Raw) is incorrect. Should be "
867 << decSize << " / Found :"
868 << PixelWriteConverter->GetUserDataSize() );
873 if ( rgbSize!=PixelWriteConverter->GetUserDataSize() )
875 gdcmWarningMacro( "Data size (RGB) is incorrect. Should be "
876 << decSize << " / Found "
877 << PixelWriteConverter->GetUserDataSize() );
887 * \brief Updates the File to write RAW data (as opposed to RGB data)
888 * (modifies, when necessary, photochromatic interpretation,
889 * bits allocated, Pixels element VR)
890 * WARNING : if SetPhotometricInterpretationToMonochrome1() was called
891 * before Pixel Elements if modified :-(
893 void FileHelper::SetWriteToRaw()
895 if ( FileInternal->GetNumberOfScalarComponents() == 3
896 && !FileInternal->HasLUT() )
902 // 0x0028,0x0004 : Photometric Interpretation
903 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
904 if (FileInternal->HasLUT() )
906 photInt->SetString("PALETTE COLOR ");
910 if (GetPhotometricInterpretation() == 2)
911 photInt->SetString("MONOCHROME2 "); // 0 = Black
913 photInt->SetString("MONOCHROME1 "); // 0 = White !
916 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
917 PixelReadConverter->GetRawSize());
919 std::string vr = "OB";
920 if ( FileInternal->GetBitsAllocated()>8 )
922 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
924 // For non RAW data. Mainly JPEG
925 if( WriteType == JPEG || WriteType == JPEG2000)
931 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
932 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
933 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
934 pixel->SetLength(PixelWriteConverter->GetDataSize());
936 if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
938 ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );
941 Archive->Push(photInt);
942 Archive->Push(pixel);
950 * \brief Updates the File to write RGB data (as opposed to RAW data)
951 * (modifies, when necessary, photochromatic interpretation,
952 * samples per pixel, Planar configuration,
953 * bits allocated, bits stored, high bit -ACR 24 bits-
954 * Pixels element VR, pushes out the LUT, )
956 void FileHelper::SetWriteToRGB()
958 if ( FileInternal->GetNumberOfScalarComponents()==3 )
960 PixelReadConverter->BuildRGBImage();
962 DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
963 spp->SetString("3 ");
965 DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
966 planConfig->SetString("0 ");
968 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
969 photInt->SetString("RGB ");
971 if ( PixelReadConverter->GetRGB() )
973 PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
974 PixelReadConverter->GetRGBSize());
978 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
979 PixelReadConverter->GetRawSize());
982 std::string vr = "OB";
983 if ( FileInternal->GetBitsAllocated()>8 )
985 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
988 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
989 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
990 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
991 pixel->SetLength(PixelWriteConverter->GetDataSize());
994 Archive->Push(planConfig);
995 Archive->Push(photInt);
996 Archive->Push(pixel);
999 planConfig->Delete();
1004 Archive->Push(0x0028,0x1101);
1005 Archive->Push(0x0028,0x1102);
1006 Archive->Push(0x0028,0x1103);
1007 Archive->Push(0x0028,0x1201);
1008 Archive->Push(0x0028,0x1202);
1009 Archive->Push(0x0028,0x1203);
1011 // push out Palette Color Lookup Table UID, if any
1012 Archive->Push(0x0028,0x1199);
1014 // For old '24 Bits' ACR-NEMA
1015 // Thus, we have a RGB image and the bits allocated = 24 and
1016 // samples per pixels = 1 (in the read file)
1017 if ( FileInternal->GetBitsAllocated()==24 )
1019 DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
1020 bitsAlloc->SetString("8 ");
1022 DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
1023 bitsStored->SetString("8 ");
1025 DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
1026 highBit->SetString("7 ");
1028 Archive->Push(bitsAlloc);
1029 Archive->Push(bitsStored);
1030 Archive->Push(highBit);
1032 bitsAlloc->Delete();
1033 bitsStored->Delete();
1044 * \brief Restore the File write mode
1046 void FileHelper::RestoreWrite()
1048 Archive->Restore(0x0028,0x0002);
1049 Archive->Restore(0x0028,0x0004);
1051 Archive->Restore(0x0028,0x0006);
1052 Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1054 // For old ACR-NEMA (24 bits problem)
1055 Archive->Restore(0x0028,0x0100);
1056 Archive->Restore(0x0028,0x0101);
1057 Archive->Restore(0x0028,0x0102);
1060 Archive->Restore(0x0028,0x1101);
1061 Archive->Restore(0x0028,0x1102);
1062 Archive->Restore(0x0028,0x1103);
1063 Archive->Restore(0x0028,0x1201);
1064 Archive->Restore(0x0028,0x1202);
1065 Archive->Restore(0x0028,0x1203);
1067 // For the Palette Color Lookup Table UID
1068 Archive->Restore(0x0028,0x1203);
1070 // group 0002 may be pushed out for ACR-NEMA writting purposes
1071 Archive->Restore(0x0002,0x0000);
1072 Archive->Restore(0x0002,0x0001);
1073 Archive->Restore(0x0002,0x0002);
1074 Archive->Restore(0x0002,0x0003);
1075 Archive->Restore(0x0002,0x0010);
1076 Archive->Restore(0x0002,0x0012);
1077 Archive->Restore(0x0002,0x0013);
1078 Archive->Restore(0x0002,0x0016);
1079 Archive->Restore(0x0002,0x0100);
1080 Archive->Restore(0x0002,0x0102);
1085 * \brief Pushes out the whole group 0002
1086 * FIXME : better, set a flag to tell the writer not to write it ...
1087 * FIXME : method should probably have an other name !
1088 * SetWriteFileTypeToACR is NOT opposed to
1089 * SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1091 void FileHelper::SetWriteFileTypeToACR()
1093 Archive->Push(0x0002,0x0000);
1094 Archive->Push(0x0002,0x0001);
1095 Archive->Push(0x0002,0x0002);
1096 Archive->Push(0x0002,0x0003);
1097 Archive->Push(0x0002,0x0010);
1098 Archive->Push(0x0002,0x0012);
1099 Archive->Push(0x0002,0x0013);
1100 Archive->Push(0x0002,0x0016);
1101 Archive->Push(0x0002,0x0100);
1102 Archive->Push(0x0002,0x0102);
1106 * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1108 void FileHelper::SetWriteFileTypeToJPEG2000()
1110 std::string ts = Util::DicomString(
1111 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1113 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1121 * \brief Sets in the File the TransferSyntax to 'JPEG'
1123 void FileHelper::SetWriteFileTypeToJPEG()
1125 std::string ts = Util::DicomString(
1126 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGLosslessProcess14_1) );
1128 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1136 * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"
1138 void FileHelper::SetWriteFileTypeToExplicitVR()
1140 std::string ts = Util::DicomString(
1141 Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1143 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1150 * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"
1152 void FileHelper::SetWriteFileTypeToImplicitVR()
1154 std::string ts = Util::DicomString(
1155 Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1157 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1164 * \brief Set the Write not to Libido format
1166 void FileHelper::SetWriteToLibido()
1168 DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1169 DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1171 if ( oldRow && oldCol )
1173 std::string rows, columns;
1175 DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1176 DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1178 newRow->Copy(oldCol);
1179 newCol->Copy(oldRow);
1181 newRow->SetString(oldCol->GetString());
1182 newCol->SetString(oldRow->GetString());
1184 Archive->Push(newRow);
1185 Archive->Push(newCol);
1191 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1192 libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1193 Archive->Push(libidoCode);
1194 libidoCode->Delete();
1198 * \brief Set the Write not to No Libido format
1200 void FileHelper::SetWriteToNoLibido()
1202 DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1205 if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1207 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1208 libidoCode->SetString("");
1209 Archive->Push(libidoCode);
1210 libidoCode->Delete();
1216 * \brief Restore the Write format
1218 void FileHelper::RestoreWriteOfLibido()
1220 Archive->Restore(0x0028,0x0010);
1221 Archive->Restore(0x0028,0x0011);
1222 Archive->Restore(0x0008,0x0010);
1224 // Restore 'LibIDO-special' entries, if any
1225 Archive->Restore(0x0028,0x0015);
1226 Archive->Restore(0x0028,0x0016);
1227 Archive->Restore(0x0028,0x0017);
1228 Archive->Restore(0x0028,0x00199);
1232 * \brief Duplicates a DataEntry or creates it.
1233 * @param group Group number of the Entry
1234 * @param elem Element number of the Entry
1235 * @param vr Value Representation of the Entry
1236 * \return pointer to the new Bin Entry (NULL when creation failed).
1238 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1241 DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1244 if ( oldE && vr != GDCM_VRUNKNOWN )
1245 if ( oldE->GetVR() != vr )
1250 newE = DataEntry::New(group, elem, vr);
1255 newE = GetFile()->NewDataEntry(group, elem, vr);
1262 * \brief This method is called automatically, just before writting
1263 * in order to produce a 'True Dicom V3' image.
1265 * We cannot know *how* the user made the File :
1266 * (reading an old ACR-NEMA file or a not very clean DICOM file ...)
1267 * Just before writting :
1268 * - we check the Entries
1269 * - we create the mandatory entries if they are missing
1270 * - we modify the values if necessary
1271 * - we push the sensitive entries to the Archive
1272 * The writing process will restore the entries as they where before
1273 * entering FileHelper::CheckMandatoryElements, so the user will always
1274 * see the entries just as they were before he decided to write.
1277 * - Entries whose type is 1 are mandatory, with a mandatory value
1278 * - Entries whose type is 1c are mandatory-inside-a-Sequence,
1279 * with a mandatory value
1280 * - Entries whose type is 2 are mandatory, with an optional value
1281 * - Entries whose type is 2c are mandatory-inside-a-Sequence,
1282 * with an optional value
1283 * - Entries whose type is 3 are optional
1286 * - warn the user if we had to add some entries :
1287 * even if a mandatory entry is missing, we add it, with a default value
1288 * (we don't want to give up the writting process if user forgot to
1289 * specify Lena's Patient ID, for instance ...)
1290 * - read the whole PS 3.3 Part of DICOM (890 pages)
1291 * and write a *full* checker (probably one method per Modality ...)
1292 * Any contribution is welcome.
1293 * - write a user callable full checker, to allow post reading
1294 * and/or pre writting image consistency check.
1297 /* -------------------------------------------------------------------------------------
1298 To be moved to User's guide / WIKI ?
1300 We have to deal with 4 *very* different cases :
1301 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1303 -2) user modified the pixels of an existing image.
1305 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1307 -4) user modified/added some tags *without processing* the pixels (anonymization...)
1308 UNMODIFIED_PIXELS_IMAGE
1309 -Probabely some more to be added.
1311 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1314 0008 0012 Instance Creation Date
1315 0008 0013 Instance Creation Time
1316 0008 0018 SOP Instance UID
1317 are *always* created with the current values; user has *no* possible intervention on
1320 'Serie Instance UID'(0x0020,0x000e)
1321 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1322 created if it doesn't.
1323 The user is allowed to create his own Series/Studies,
1324 keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1326 The user shouldn't add any image to a 'Manufacturer Serie'
1327 but there is no way no to allow him to do that
1329 None of the 'shadow elements' are droped out.
1333 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1336 'Media Storage SOP Class UID' (0x0002,0x0002)
1337 'SOP Class UID' (0x0008,0x0016) are set to
1338 [Secondary Capture Image Storage]
1339 'Image Type' (0x0008,0x0008) is forced to "DERIVED\PRIMARY"
1340 Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1343 If 'SOP Class UID' exists in the native image ('true DICOM' image)
1344 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1345 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1346 whose value is the original 'SOP Class UID'
1347 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1348 whose value is the original 'SOP Class UID'
1350 3) TODO : find a trick to allow user to pass to the writter the list of the Dicom images
1351 or the Series, (or the Study ?) he used to created his image
1352 (MIP, MPR, cartography image, ...)
1353 These info should be stored (?)
1354 0008 1110 SQ 1 Referenced Study Sequence
1355 0008 1115 SQ 1 Referenced Series Sequence
1356 0008 1140 SQ 1 Referenced Image Sequence
1358 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1359 'Media Storage SOP Class UID' (0x0002,0x0002)
1360 'SOP Class UID' (0x0008,0x0016)
1361 'Image Type' (0x0008,0x0008)
1362 'Conversion Type' (0x0008,0x0064)
1365 Bellow follows the full description (hope so !) of the consistency checks performed
1366 by gdcm::FileHelper::CheckMandatoryElements()
1369 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1370 -->'SOP Class UID' (0x0008,0x0016) are defaulted to
1371 [Secondary Capture Image Storage]
1372 --> 'Image Type' (0x0008,0x0008)
1373 is forced to "DERIVED\PRIMARY"
1374 (The written image is no longer an 'ORIGINAL' one)
1375 Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1377 --> Conversion Type (0x0008,0x0064)
1378 is defaulted to 'SYN' (Synthetic Image)
1379 when *he* knows he created his own image ex nihilo
1381 --> 'Modality' (0x0008,0x0060)
1382 is defaulted to "OT" (other) if missing.
1383 (a fully user created image belongs to *no* modality)
1385 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1386 --> 'Implementation Class UID' (0x0002,0x0012)
1387 are automatically generated; no user intervention possible
1389 --> 'Serie Instance UID'(0x0020,0x000e)
1390 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1391 created if it doesn't.
1392 The user is allowed to create his own Series/Studies,
1393 keeping the same 'Serie Instance UID' / 'Study Instance UID'
1396 The user shouldn't add any image to a 'Manufacturer Serie'
1397 but there is no way no to allowed him to do that
1399 --> If 'SOP Class UID' exists in the native image ('true DICOM' image)
1400 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1402 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1403 whose value is the original 'SOP Class UID'
1404 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1405 whose value is the original 'SOP Class UID'
1407 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1408 --> Pixel Spacing (0x0028,0x0030) is defaulted to "1.0\1.0"
1409 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1411 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1413 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1415 --> Study Date, Study Time are defaulted to current Date and Time
1416 (they remain unchanged if they exist)
1418 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from
1419 Image Orientation (Patient) : (0020|0037) or from
1420 Image Orientation (RET) : (0020 0035)
1422 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1423 are created, with empty value if there are missing.
1425 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1426 are defaulted with a 'gdcm' value.
1428 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1429 --> Referring Physician's Name (Type 2)
1430 are created, with empty value if there are missing.
1432 -------------------------------------------------------------------------------------*/
1434 void FileHelper::CheckMandatoryElements()
1436 std::string sop = Util::CreateUniqueUID();
1438 // --------------------- For Meta Elements ---------------------
1439 // just to remember : 'official' 0002 group
1440 if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1442 // Group 000002 (Meta Elements) already pushed out
1444 //0002 0000 UL 1 Meta Group Length
1445 //0002 0001 OB 1 File Meta Information Version
1446 //0002 0002 UI 1 Media Storage SOP Class UID
1447 //0002 0003 UI 1 Media Storage SOP Instance UID
1448 //0002 0010 UI 1 Transfer Syntax UID
1449 //0002 0012 UI 1 Implementation Class UID
1450 //0002 0013 SH 1 Implementation Version Name
1451 //0002 0016 AE 1 Source Application Entity Title
1452 //0002 0100 UI 1 Private Information Creator
1453 //0002 0102 OB 1 Private Information
1455 // Push out 'ACR-NEMA-special' entries, if any
1456 Archive->Push(0x0008,0x0001); // Length to End
1457 Archive->Push(0x0008,0x0010); // Recognition Code
1458 Archive->Push(0x0028,0x0005); // Image Dimension
1460 // Create them if not found
1461 // Always modify the value
1462 // Push the entries to the archive.
1463 CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1465 DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1466 e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1468 e_0002_0001->SetLength(2);
1469 Archive->Push(e_0002_0001);
1470 e_0002_0001->Delete();
1472 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1474 // we keep the original 'Media Storage SOP Class UID', we default it if missing
1475 CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1479 // It's *not* an image comming straight from a source. We force
1480 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1481 CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1484 // 'Media Storage SOP Instance UID'
1485 CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1487 // 'Implementation Class UID'
1488 // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1489 // seems to be Root UID + 4 digits (?)
1490 CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1492 // 'Implementation Version Name'
1493 std::string version = "GDCM ";
1494 version += Util::GetVersion();
1495 CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1498 // --------------------- For DataSet ---------------------
1500 if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1503 gdcmDebugMacro( "USER_OWN_IMAGE (1)");
1504 // If 'SOP Class UID' exists ('true DICOM' image)
1505 // we create the 'Source Image Sequence' SeqEntry
1506 // to hold informations about the Source Image
1508 DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1511 // Create 'Source Image Sequence' SeqEntry
1512 // SeqEntry *sis = SeqEntry::New (
1513 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x2112) );
1514 SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1515 SQItem *sqi = SQItem::New(1);
1516 // (we assume 'SOP Instance UID' exists too)
1517 // create 'Referenced SOP Class UID'
1518 // DataEntry *e_0008_1150 = DataEntry::New(
1519 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1150) );
1520 DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1521 e_0008_1150->SetString( e_0008_0016->GetString());
1522 sqi->AddEntry(e_0008_1150);
1523 e_0008_1150->Delete();
1525 // create 'Referenced SOP Instance UID'
1526 DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1527 // DataEntry *e_0008_1155 = DataEntry::New(
1528 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1155) );
1529 DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1530 e_0008_1155->SetString( e_0008_0018->GetString());
1531 sqi->AddEntry(e_0008_1155);
1532 e_0008_1155->Delete();
1534 sis->AddSQItem(sqi,1);
1537 // temporarily replaces any previous 'Source Image Sequence'
1541 // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1542 if ( ContentType == FILTERED_IMAGE)
1543 // the user *knows* he just modified the pixels
1544 // the image is no longer an 'Original' one
1545 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1549 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1551 // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1552 CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");
1556 // It's *not* an image comming straight from a source. We force
1557 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1558 CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");
1561 Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1562 // Push out 'LibIDO-special' entries, if any
1563 Archive->Push(0x0028,0x0015);
1564 Archive->Push(0x0028,0x0016);
1565 Archive->Push(0x0028,0x0017);
1566 Archive->Push(0x0028,0x0198); // very old versions
1567 Archive->Push(0x0028,0x0199);
1569 // Replace deprecated 0028 0012 US Planes
1570 // by new 0028 0008 IS Number of Frames
1572 ///\todo : find if there is a rule!
1573 DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1576 CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1577 Archive->Push(0x0028,0x0012);
1580 // Deal with the pb of (Bits Stored = 12)
1581 // - we're gonna write the image as Bits Stored = 16
1582 if ( FileInternal->GetEntryString(0x0028,0x0100) == "12")
1584 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1587 // Check if user wasn't drunk ;-)
1589 std::ostringstream s;
1590 // check 'Bits Allocated' vs decent values
1591 int nbBitsAllocated = FileInternal->GetBitsAllocated();
1592 if ( nbBitsAllocated == 0 || nbBitsAllocated > 32
1593 || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1595 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1596 gdcmWarningMacro("(0028,0100) changed from "
1597 << nbBitsAllocated << " to 16 for consistency purpose");
1598 nbBitsAllocated = 16;
1600 // check 'Bits Stored' vs 'Bits Allocated'
1601 int nbBitsStored = FileInternal->GetBitsStored();
1602 if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1605 s << nbBitsAllocated;
1606 CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1607 gdcmWarningMacro("(0028,0101) changed from "
1608 << nbBitsStored << " to " << nbBitsAllocated
1609 << " for consistency purpose" );
1610 nbBitsStored = nbBitsAllocated;
1612 // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1613 int highBitPosition = FileInternal->GetHighBitPosition();
1614 if ( highBitPosition == 0 ||
1615 highBitPosition > nbBitsAllocated-1 ||
1616 highBitPosition < nbBitsStored-1 )
1619 s << nbBitsStored - 1;
1620 CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1621 gdcmWarningMacro("(0028,0102) changed from "
1622 << highBitPosition << " to " << nbBitsAllocated-1
1623 << " for consistency purpose");
1626 // check Pixel Representation (default it as 0 -unsigned-)
1628 DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1631 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1632 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1636 int sign = (int)e_0028_0103->GetValue(0);
1637 if (sign !=1 && sign !=0)
1639 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1640 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1644 std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1645 if ( pixelSpacing == GDCM_UNFOUND )
1647 pixelSpacing = "1.0\\1.0";
1648 // if missing, Pixel Spacing forced to "1.0\1.0"
1649 CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1652 // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1653 // --> This one is the *legal* one !
1654 if ( ContentType != USER_OWN_IMAGE)
1655 // we write it only when we are *sure* the image comes from
1656 // an imager (see also 0008,0x0064)
1657 CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1660 ///Exact meaning of RETired fields
1662 // See page 73 of ACR-NEMA_300-1988.pdf !
1664 // 0020,0020 : Patient Orientation :
1665 Patient direction of the first row and
1666 column of the images. The first entry id the direction of the raws, given by the
1667 direction of the last pixel in the first row from the first pixel in tha row.
1668 the second entry is the direction of the columns, given by the direction of the
1669 last pixel in the first column from the first pixel in that column.
1670 L : Left, F : Feet, A : Anterior, P : Posterior.
1671 Up to 3 letters can be used in combination to indicate oblique planes.
1673 //0020,0030 Image Position (RET)
1674 x,y,z coordinates im mm of the first pixel in the image
1676 // 0020,0035 Image Orientation (RET)
1677 Direction cosines of the R axis of the image system with respect to the
1678 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1679 the image system with respect to the same axes
1681 //0020,0050 Location
1682 An image location reference, standard for the modality (such as CT bed
1683 position), used to indicate position. Calculation of position for other purposes
1684 is only from (0020,0030) and (0020,0035)
1688 // if imagePositionPatient not found, default it with imagePositionRet, if any
1689 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1691 std::string imagePositionRet = FileInternal->GetEntryString(0x0020,0x0030);
1692 std::string imageOrientationRet = FileInternal->GetEntryString(0x0020,0x0035);
1693 std::string imagePositionPatient = FileInternal->GetEntryString(0x0020,0x0032);
1694 std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1696 if( imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1697 && imagePositionRet != GDCM_UNFOUND && imageOrientationRet != GDCM_UNFOUND)
1699 CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1700 Archive->Push(0x0020,0x0030);
1701 CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1702 Archive->Push(0x0020,0x0035);
1706 // Samples Per Pixel (type 1) : default to grayscale
1707 CheckMandatoryEntry(0x0028,0x0002,"1","US");
1709 // --- Check UID-related Entries ---
1711 // At the end, not to overwrite the original ones,
1712 // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
1713 // 'SOP Instance UID'
1714 CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1716 if ( ContentType == USER_OWN_IMAGE)
1718 gdcmDebugMacro( "USER_OWN_IMAGE (2)");
1720 // Other possible values are :
1721 // See PS 3.3, Page 408
1723 // DV = Digitized Video
1724 // DI = Digital Interface
1725 // DF = Digitized Film
1726 // WSD = Workstation
1727 // SD = Scanned Document
1728 // SI = Scanned Image
1730 // SYN = Synthetic Image
1732 CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1735 if ( ContentType == CREATED_IMAGE)
1737 /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1742 // ---- The user will never have to take any action on the following ----
1744 // new value for 'SOP Instance UID'
1745 //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1747 // Instance Creation Date
1748 const std::string &date = Util::GetCurrentDate();
1749 CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1751 // Instance Creation Time
1752 const std::string &time = Util::GetCurrentTime();
1753 CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1756 CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1758 CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1761 //CopyMandatoryEntry(0x0008,0x0050,"");
1762 CheckMandatoryEntry(0x0008,0x0050,"","SH");
1765 // ----- Add Mandatory Entries if missing ---
1766 // Entries whose type is 1 are mandatory, with a mandatory value
1767 // Entries whose type is 1c are mandatory-inside-a-Sequence,
1768 // with a mandatory value
1769 // Entries whose type is 2 are mandatory, with an optional value
1770 // Entries whose type is 2c are mandatory-inside-a-Sequence,
1771 // with an optional value
1772 // Entries whose type is 3 are optional
1774 // 'Study Instance UID'
1775 // Keep the value if exists
1776 // The user is allowed to create his own Study,
1777 // keeping the same 'Study Instance UID' for various images
1778 // The user may add images to a 'Manufacturer Study',
1779 // adding new Series to an already existing Study
1780 CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1782 // 'Serie Instance UID'
1783 // Keep the value if exists
1784 // The user is allowed to create his own Series,
1785 // keeping the same 'Serie Instance UID' for various images
1786 // The user shouldn't add any image to a 'Manufacturer Serie'
1787 // but there is no way no to prevent him for doing that
1788 CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1791 CheckMandatoryEntry(0x0020,0x0010,"","SH");
1794 CheckMandatoryEntry(0x0020,0x0011,"","IS");
1797 CheckMandatoryEntry(0x0020,0x0013,"","IS");
1799 // Patient Orientation
1800 // Can be computed from (0020|0037) : Image Orientation (Patient)
1801 GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1802 std::string ori = o->GetOrientation ( FileInternal );
1804 if (ori != "\\" && ori != GDCM_UNFOUND)
1805 CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1807 CheckMandatoryEntry(0x0020,0x0020,"","CS");
1809 // Default Patient Position to HFS
1810 CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1812 // Modality : if missing we set it to 'OTher'
1813 CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1815 // Manufacturer : if missing we set it to 'GDCM Factory'
1816 CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1818 // Institution Name : if missing we set it to 'GDCM Hospital'
1819 CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1821 // Patient's Name : if missing, we set it to 'GDCM^Patient'
1822 CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1824 // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1825 CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1827 // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1828 CheckMandatoryEntry(0x0010,0x0030,"","DA");
1830 // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1831 CheckMandatoryEntry(0x0010,0x0040,"","CS");
1833 // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1834 CheckMandatoryEntry(0x0008,0x0090,"","PN");
1837 // Deal with element 0x0000 (group length) of each group.
1838 // First stage : get all the different Groups
1841 DocEntry *d = FileInternal->GetFirstEntry();
1844 grHT[d->GetGroup()] = 0;
1845 d=FileInternal->GetNextEntry();
1847 // Second stage : add the missing ones (if any)
1848 for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)
1850 CheckMandatoryEntry(it->first, 0x0000, "0");
1852 // Third stage : update all 'zero level' groups length
1856 if (PhotometricInterpretation == 1)
1861 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1863 DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1866 //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1867 entry = DataEntry::New(group,elem,vr);
1868 entry->SetString(value);
1869 Archive->Push(entry);
1874 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
1875 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1877 //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1878 DataEntry *entry = DataEntry::New(group,elem,vr);
1879 entry->SetString(value);
1880 Archive->Push(entry);
1884 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1886 DataEntry *entry = CopyDataEntry(group,elem,vr);
1887 entry->SetString(value);
1888 Archive->Push(entry);
1893 * \brief Restore in the File the initial group 0002
1895 void FileHelper::RestoreWriteMandatory()
1897 // group 0002 may be pushed out for ACR-NEMA writting purposes
1898 Archive->Restore(0x0002,0x0000);
1899 Archive->Restore(0x0002,0x0001);
1900 Archive->Restore(0x0002,0x0002);
1901 Archive->Restore(0x0002,0x0003);
1902 Archive->Restore(0x0002,0x0010);
1903 Archive->Restore(0x0002,0x0012);
1904 Archive->Restore(0x0002,0x0013);
1905 Archive->Restore(0x0002,0x0016);
1906 Archive->Restore(0x0002,0x0100);
1907 Archive->Restore(0x0002,0x0102);
1909 // FIXME : Check if none is missing !
1911 Archive->Restore(0x0008,0x0012);
1912 Archive->Restore(0x0008,0x0013);
1913 Archive->Restore(0x0008,0x0016);
1914 Archive->Restore(0x0008,0x0018);
1915 Archive->Restore(0x0008,0x0060);
1916 Archive->Restore(0x0008,0x0070);
1917 Archive->Restore(0x0008,0x0080);
1918 Archive->Restore(0x0008,0x0090);
1919 Archive->Restore(0x0008,0x2112);
1921 Archive->Restore(0x0010,0x0010);
1922 Archive->Restore(0x0010,0x0030);
1923 Archive->Restore(0x0010,0x0040);
1925 Archive->Restore(0x0020,0x000d);
1926 Archive->Restore(0x0020,0x000e);
1930 * \brief CallStartMethod
1932 void FileHelper::CallStartMethod()
1936 CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1940 * \brief CallProgressMethod
1942 void FileHelper::CallProgressMethod()
1944 CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1948 * \brief CallEndMethod
1950 void FileHelper::CallEndMethod()
1953 CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1956 //-----------------------------------------------------------------------------
1959 * \brief Factorization for various forms of constructors.
1961 void FileHelper::Initialize()
1964 ContentType = USER_OWN_IMAGE;
1966 WriteMode = WMODE_RAW;
1967 WriteType = ExplicitVR;
1969 PhotometricInterpretation = 2; // Black = 0
1971 PixelReadConverter = new PixelReadConvert;
1972 PixelWriteConverter = new PixelWriteConvert;
1973 Archive = new DocEntryArchive( FileInternal );
1977 * \brief Reads/[decompresses] the pixels,
1978 * *without* making RGB from Palette Colors
1979 * @return the pixels area, whatever its type
1980 * (uint8_t is just for prototyping : feel free to Cast it)
1982 uint8_t *FileHelper::GetRaw()
1984 PixelReadConverter->SetUserFunction( UserFunction );
1986 uint8_t *raw = PixelReadConverter->GetRaw();
1989 // The Raw image migth not be loaded yet:
1990 std::ifstream *fp = FileInternal->OpenFile();
1991 PixelReadConverter->ReadAndDecompressPixelData( fp );
1993 FileInternal->CloseFile();
1995 raw = PixelReadConverter->GetRaw();
1998 gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
2006 * \brief Deal with Grey levels i.e. re-arange them
2007 * to have low values = dark, high values = bright
2009 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
2011 uint32_t i; // to please M$VC6
2014 // Number of Bits Allocated for storing a Pixel is defaulted to 16
2015 // when absent from the file.
2016 int bitsAllocated = FileInternal->GetBitsAllocated();
2017 if ( bitsAllocated == 0 )
2022 else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2026 // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2027 // when absent from the file.
2028 int bitsStored = FileInternal->GetBitsStored();
2029 if ( bitsStored == 0 )
2031 bitsStored = bitsAllocated;
2034 if (!FileInternal->IsSignedPixelData())
2036 if ( bitsAllocated == 8 )
2038 uint8_t *deb = (uint8_t *)raw;
2039 for (i=0; i<rawSize; i++)
2047 if ( bitsAllocated == 16 )
2050 for (j=0; j<bitsStored-1; j++)
2052 mask = (mask << 1) +1; // will be fff when BitsStored=12
2055 uint16_t *deb = (uint16_t *)raw;
2056 for (i=0; i<rawSize/2; i++)
2066 if ( bitsAllocated == 8 )
2068 uint8_t smask8 = 255;
2069 uint8_t *deb = (uint8_t *)raw;
2070 for (i=0; i<rawSize; i++)
2072 *deb = smask8 - *deb;
2077 if ( bitsAllocated == 16 )
2079 uint16_t smask16 = 65535;
2080 uint16_t *deb = (uint16_t *)raw;
2081 for (i=0; i<rawSize/2; i++)
2083 *deb = smask16 - *deb;
2091 //-----------------------------------------------------------------------------
2093 * \brief Prints the common part of DataEntry, SeqEntry
2094 * @param os ostream we want to print in
2095 * @param indent (unused)
2097 void FileHelper::Print(std::ostream &os, std::string const &)
2099 FileInternal->SetPrintLevel(PrintLevel);
2100 FileInternal->Print(os);
2102 if ( FileInternal->IsReadable() )
2104 PixelReadConverter->SetPrintLevel(PrintLevel);
2105 PixelReadConverter->Print(os);
2109 //-----------------------------------------------------------------------------
2110 } // end namespace gdcm
2113 /* Probabely something to be added to use Rescale Slope/Intercept
2114 Have a look ,at ITK code !
2116 // Internal function to rescale pixel according to Rescale Slope/Intercept
2117 template<class TBuffer, class TSource>
2118 void RescaleFunction(TBuffer* buffer, TSource *source,
2119 double slope, double intercept, size_t size)
2121 size /= sizeof(TSource);
2123 if (slope != 1.0 && intercept != 0.0)
2125 // Duff's device. Instead of this code:
2127 // for(unsigned int i=0; i<size; i++)
2129 // buffer[i] = (TBuffer)(source[i]*slope + intercept);
2132 // use Duff's device which exploits "fall through"
2133 register size_t n = (size + 7) / 8;
2136 case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2137 case 7: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2138 case 6: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2139 case 5: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2140 case 4: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2141 case 3: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2142 case 2: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2143 case 1: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2147 else if (slope == 1.0 && intercept != 0.0)
2149 // Duff's device. Instead of this code:
2151 // for(unsigned int i=0; i<size; i++)
2153 // buffer[i] = (TBuffer)(source[i] + intercept);
2156 // use Duff's device which exploits "fall through"
2157 register size_t n = (size + 7) / 8;
2160 case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2161 case 7: *buffer++ = (TBuffer)(*source++ + intercept);
2162 case 6: *buffer++ = (TBuffer)(*source++ + intercept);
2163 case 5: *buffer++ = (TBuffer)(*source++ + intercept);
2164 case 4: *buffer++ = (TBuffer)(*source++ + intercept);
2165 case 3: *buffer++ = (TBuffer)(*source++ + intercept);
2166 case 2: *buffer++ = (TBuffer)(*source++ + intercept);
2167 case 1: *buffer++ = (TBuffer)(*source++ + intercept);
2171 else if (slope != 1.0 && intercept == 0.0)
2173 // Duff's device. Instead of this code:
2175 // for(unsigned int i=0; i<size; i++)
2177 // buffer[i] = (TBuffer)(source[i]*slope);
2180 // use Duff's device which exploits "fall through"
2181 register size_t n = (size + 7) / 8;
2184 case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2185 case 7: *buffer++ = (TBuffer)((*source++)*slope);
2186 case 6: *buffer++ = (TBuffer)((*source++)*slope);
2187 case 5: *buffer++ = (TBuffer)((*source++)*slope);
2188 case 4: *buffer++ = (TBuffer)((*source++)*slope);
2189 case 3: *buffer++ = (TBuffer)((*source++)*slope);
2190 case 2: *buffer++ = (TBuffer)((*source++)*slope);
2191 case 1: *buffer++ = (TBuffer)((*source++)*slope);
2197 // Duff's device. Instead of this code:
2199 // for(unsigned int i=0; i<size; i++)
2201 // buffer[i] = (TBuffer)(source[i]);
2204 // use Duff's device which exploits "fall through"
2205 register size_t n = (size + 7) / 8;
2208 case 0: do { *buffer++ = (TBuffer)(*source++);
2209 case 7: *buffer++ = (TBuffer)(*source++);
2210 case 6: *buffer++ = (TBuffer)(*source++);
2211 case 5: *buffer++ = (TBuffer)(*source++);
2212 case 4: *buffer++ = (TBuffer)(*source++);
2213 case 3: *buffer++ = (TBuffer)(*source++);
2214 case 2: *buffer++ = (TBuffer)(*source++);
2215 case 1: *buffer++ = (TBuffer)(*source++);
2222 template<class TSource>
2223 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2224 void* buffer, TSource *source,
2225 double slope, double intercept, size_t size)
2229 case ImageIOBase::UCHAR:
2230 RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2232 case ImageIOBase::CHAR:
2233 RescaleFunction( (char *)buffer, source, slope, intercept, size);
2235 case ImageIOBase::USHORT:
2236 RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2238 case ImageIOBase::SHORT:
2239 RescaleFunction( (short *)buffer, source, slope, intercept, size);
2241 case ImageIOBase::UINT:
2242 RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2244 case ImageIOBase::INT:
2245 RescaleFunction( (int *)buffer, source, slope, intercept, size);
2247 case ImageIOBase::FLOAT:
2248 RescaleFunction( (float *)buffer, source, slope, intercept, size);
2250 case ImageIOBase::DOUBLE:
2251 RescaleFunction( (double *)buffer, source, slope, intercept, size);
2254 ::itk::OStringStream message;
2255 message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2256 ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);