1 /*=========================================================================
4 Module: $RCSfile: gdcmFileHelper.cxx,v $
7 Date: $Date: 2007/07/26 08:36:49 $
8 Version: $Revision: 1.119 $
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 // Better we let DocEntry::WriteContent to put vr=UN for undocumented Shadow Groups !
734 e = FileInternal->GetFirstEntry();
737 if (e->GetVR() == " ")
740 SetWriteTypeToDcmImplVR();
741 SetWriteFileTypeToImplicitVR();
745 e = FileInternal->GetNextEntry();
750 SetWriteFileTypeToExplicitVR();
755 SetWriteFileTypeToExplicitVR();
760 // NOTHING is done here just for LibIDO.
761 // Just to avoid further trouble if user creates a file ex-nihilo,
762 // wants to write it as an ACR-NEMA file,
763 // and forgets to create any Entry belonging to group 0008
765 // We add Recognition Code (RET)
766 if ( ! FileInternal->GetDataEntry(0x0008, 0x0010) )
767 FileInternal->InsertEntryString("ACR-NEMA V1.0 ",
768 0x0008, 0x0010, "LO");
769 SetWriteFileTypeToACR();
770 // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
773 /// \todo FIXME : JPEG may be either ExplicitVR or ImplicitVR
775 SetWriteFileTypeToJPEG();
779 SetWriteFileTypeToJPEG2000();
783 // --------------------------------------------------------------
784 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
786 // if recognition code tells us we dealt with a LibIDO image
787 // we reproduce on disk the switch between lineNumber and columnNumber
788 // just before writting ...
789 /// \todo the best trick would be *change* the recognition code
790 /// but pb expected if user deals with, e.g. COMPLEX images
792 if ( WriteType == ACR_LIBIDO )
798 SetWriteToNoLibido();
800 // ----------------- End of Special Patch ----------------
805 SetWriteToRaw(); // modifies and pushes to the archive, when necessary
808 SetWriteToRGB(); // modifies and pushes to the archive, when necessary
812 bool check = CheckWriteIntegrity(); // verifies length
813 if (WriteType == JPEG || WriteType == JPEG2000)
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;
868 if ( decSize!=PixelWriteConverter->GetUserDataSize() )
870 gdcmWarningMacro( "Data size (Raw) is incorrect. Should be "
871 << decSize << " / Found :"
872 << PixelWriteConverter->GetUserDataSize() );
877 if ( rgbSize!=PixelWriteConverter->GetUserDataSize() )
879 gdcmWarningMacro( "Data size (RGB) is incorrect. Should be "
880 << decSize << " / Found "
881 << PixelWriteConverter->GetUserDataSize() );
891 * \brief Updates the File to write RAW data (as opposed to RGB data)
892 * (modifies, when necessary, photochromatic interpretation,
893 * bits allocated, Pixels element VR)
894 * WARNING : if SetPhotometricInterpretationToMonochrome1() was called
895 * before Pixel Elements if modified :-(
897 void FileHelper::SetWriteToRaw()
899 if ( FileInternal->GetNumberOfScalarComponents() == 3
900 && !FileInternal->HasLUT() )
906 // 0x0028,0x0004 : Photometric Interpretation
907 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
908 if (FileInternal->HasLUT() )
910 photInt->SetString("PALETTE COLOR ");
914 if (GetPhotometricInterpretation() == 2)
915 photInt->SetString("MONOCHROME2 "); // 0 = Black
917 photInt->SetString("MONOCHROME1 "); // 0 = White !
920 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
921 PixelReadConverter->GetRawSize());
923 std::string vr = "OB";
924 if ( FileInternal->GetBitsAllocated()>8 )
926 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
928 // For non RAW data. Mainly JPEG
929 if( WriteType == JPEG || WriteType == JPEG2000)
935 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
936 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
937 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
938 pixel->SetLength(PixelWriteConverter->GetDataSize());
940 if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
942 ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );
945 Archive->Push(photInt);
946 Archive->Push(pixel);
954 * \brief Updates the File to write RGB data (as opposed to RAW data)
955 * (modifies, when necessary, photochromatic interpretation,
956 * samples per pixel, Planar configuration,
957 * bits allocated, bits stored, high bit -ACR 24 bits-
958 * Pixels element VR, pushes out the LUT, )
960 void FileHelper::SetWriteToRGB()
962 if ( FileInternal->GetNumberOfScalarComponents()==3 )
964 PixelReadConverter->BuildRGBImage();
966 DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
967 spp->SetString("3 ");
969 DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
970 planConfig->SetString("0 ");
972 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
973 photInt->SetString("RGB ");
975 if ( PixelReadConverter->GetRGB() )
977 PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
978 PixelReadConverter->GetRGBSize());
982 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
983 PixelReadConverter->GetRawSize());
986 std::string vr = "OB";
987 if ( FileInternal->GetBitsAllocated()>8 )
989 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
992 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
993 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
994 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
995 pixel->SetLength(PixelWriteConverter->GetDataSize());
998 Archive->Push(planConfig);
999 Archive->Push(photInt);
1000 Archive->Push(pixel);
1003 planConfig->Delete();
1008 Archive->Push(0x0028,0x1101);
1009 Archive->Push(0x0028,0x1102);
1010 Archive->Push(0x0028,0x1103);
1011 Archive->Push(0x0028,0x1201);
1012 Archive->Push(0x0028,0x1202);
1013 Archive->Push(0x0028,0x1203);
1015 // push out Palette Color Lookup Table UID, if any
1016 Archive->Push(0x0028,0x1199);
1018 // For old '24 Bits' ACR-NEMA
1019 // Thus, we have a RGB image and the bits allocated = 24 and
1020 // samples per pixels = 1 (in the read file)
1021 if ( FileInternal->GetBitsAllocated()==24 )
1023 DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
1024 bitsAlloc->SetString("8 ");
1026 DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
1027 bitsStored->SetString("8 ");
1029 DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
1030 highBit->SetString("7 ");
1032 Archive->Push(bitsAlloc);
1033 Archive->Push(bitsStored);
1034 Archive->Push(highBit);
1036 bitsAlloc->Delete();
1037 bitsStored->Delete();
1048 * \brief Restore the File write mode
1050 void FileHelper::RestoreWrite()
1052 Archive->Restore(0x0028,0x0002);
1053 Archive->Restore(0x0028,0x0004);
1055 Archive->Restore(0x0028,0x0006);
1056 Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1058 // For old ACR-NEMA (24 bits problem)
1059 Archive->Restore(0x0028,0x0100);
1060 Archive->Restore(0x0028,0x0101);
1061 Archive->Restore(0x0028,0x0102);
1064 Archive->Restore(0x0028,0x1101);
1065 Archive->Restore(0x0028,0x1102);
1066 Archive->Restore(0x0028,0x1103);
1067 Archive->Restore(0x0028,0x1201);
1068 Archive->Restore(0x0028,0x1202);
1069 Archive->Restore(0x0028,0x1203);
1071 // For the Palette Color Lookup Table UID
1072 Archive->Restore(0x0028,0x1203);
1074 // group 0002 may be pushed out for ACR-NEMA writting purposes
1075 Archive->Restore(0x0002,0x0000);
1076 Archive->Restore(0x0002,0x0001);
1077 Archive->Restore(0x0002,0x0002);
1078 Archive->Restore(0x0002,0x0003);
1079 Archive->Restore(0x0002,0x0010);
1080 Archive->Restore(0x0002,0x0012);
1081 Archive->Restore(0x0002,0x0013);
1082 Archive->Restore(0x0002,0x0016);
1083 Archive->Restore(0x0002,0x0100);
1084 Archive->Restore(0x0002,0x0102);
1089 * \brief Pushes out the whole group 0002
1090 * FIXME : better, set a flag to tell the writer not to write it ...
1091 * FIXME : method should probably have an other name !
1092 * SetWriteFileTypeToACR is NOT opposed to
1093 * SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1095 void FileHelper::SetWriteFileTypeToACR()
1097 Archive->Push(0x0002,0x0000);
1098 Archive->Push(0x0002,0x0001);
1099 Archive->Push(0x0002,0x0002);
1100 Archive->Push(0x0002,0x0003);
1101 Archive->Push(0x0002,0x0010);
1102 Archive->Push(0x0002,0x0012);
1103 Archive->Push(0x0002,0x0013);
1104 Archive->Push(0x0002,0x0016);
1105 Archive->Push(0x0002,0x0100);
1106 Archive->Push(0x0002,0x0102);
1110 * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1112 void FileHelper::SetWriteFileTypeToJPEG2000()
1114 std::string ts = Util::DicomString(
1115 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1117 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1125 * \brief Sets in the File the TransferSyntax to 'JPEG'
1127 void FileHelper::SetWriteFileTypeToJPEG()
1129 std::string ts = Util::DicomString(
1130 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGLosslessProcess14_1) );
1132 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1140 * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"
1142 void FileHelper::SetWriteFileTypeToExplicitVR()
1144 std::string ts = Util::DicomString(
1145 Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1147 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1154 * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"
1156 void FileHelper::SetWriteFileTypeToImplicitVR()
1158 std::string ts = Util::DicomString(
1159 Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1161 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1168 * \brief Set the Write not to Libido format
1170 void FileHelper::SetWriteToLibido()
1172 DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1173 DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1175 if ( oldRow && oldCol )
1177 std::string rows, columns;
1179 DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1180 DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1182 newRow->Copy(oldCol);
1183 newCol->Copy(oldRow);
1185 newRow->SetString(oldCol->GetString());
1186 newCol->SetString(oldRow->GetString());
1188 Archive->Push(newRow);
1189 Archive->Push(newCol);
1195 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1196 libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1197 Archive->Push(libidoCode);
1198 libidoCode->Delete();
1202 * \brief Set the Write not to No Libido format
1204 void FileHelper::SetWriteToNoLibido()
1206 DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1209 if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1211 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1212 libidoCode->SetString("");
1213 Archive->Push(libidoCode);
1214 libidoCode->Delete();
1220 * \brief Restore the Write format
1222 void FileHelper::RestoreWriteOfLibido()
1224 Archive->Restore(0x0028,0x0010);
1225 Archive->Restore(0x0028,0x0011);
1226 Archive->Restore(0x0008,0x0010);
1228 // Restore 'LibIDO-special' entries, if any
1229 Archive->Restore(0x0028,0x0015);
1230 Archive->Restore(0x0028,0x0016);
1231 Archive->Restore(0x0028,0x0017);
1232 Archive->Restore(0x0028,0x00199);
1236 * \brief Duplicates a DataEntry or creates it.
1237 * @param group Group number of the Entry
1238 * @param elem Element number of the Entry
1239 * @param vr Value Representation of the Entry
1240 * \return pointer to the new Bin Entry (NULL when creation failed).
1242 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1245 DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1248 if ( oldE && vr != GDCM_VRUNKNOWN )
1249 if ( oldE->GetVR() != vr )
1254 newE = DataEntry::New(group, elem, vr);
1259 newE = GetFile()->NewDataEntry(group, elem, vr);
1266 * \brief This method is called automatically, just before writting
1267 * in order to produce a 'True Dicom V3' image.
1269 * We cannot know *how* the user made the File :
1270 * (reading an old ACR-NEMA file or a not very clean DICOM file ...)
1271 * Just before writting :
1272 * - we check the Entries
1273 * - we create the mandatory entries if they are missing
1274 * - we modify the values if necessary
1275 * - we push the sensitive entries to the Archive
1276 * The writing process will restore the entries as they where before
1277 * entering FileHelper::CheckMandatoryElements, so the user will always
1278 * see the entries just as they were before he decided to write.
1281 * - Entries whose type is 1 are mandatory, with a mandatory value
1282 * - Entries whose type is 1c are mandatory-inside-a-Sequence,
1283 * with a mandatory value
1284 * - Entries whose type is 2 are mandatory, with an optional value
1285 * - Entries whose type is 2c are mandatory-inside-a-Sequence,
1286 * with an optional value
1287 * - Entries whose type is 3 are optional
1290 * - warn the user if we had to add some entries :
1291 * even if a mandatory entry is missing, we add it, with a default value
1292 * (we don't want to give up the writting process if user forgot to
1293 * specify Lena's Patient ID, for instance ...)
1294 * - read the whole PS 3.3 Part of DICOM (890 pages)
1295 * and write a *full* checker (probably one method per Modality ...)
1296 * Any contribution is welcome.
1297 * - write a user callable full checker, to allow post reading
1298 * and/or pre writting image consistency check.
1301 /* -------------------------------------------------------------------------------------
1302 To be moved to User's guide / WIKI ?
1304 We have to deal with 4 *very* different cases :
1305 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1307 -2) user modified the pixels of an existing image.
1309 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1311 -4) user modified/added some tags *without processing* the pixels (anonymization...)
1312 UNMODIFIED_PIXELS_IMAGE
1313 -Probabely some more to be added.
1315 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1318 0008 0012 Instance Creation Date
1319 0008 0013 Instance Creation Time
1320 0008 0018 SOP Instance UID
1321 are *always* created with the current values; user has *no* possible intervention on
1324 'Serie Instance UID'(0x0020,0x000e)
1325 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1326 created if it doesn't.
1327 The user is allowed to create his own Series/Studies,
1328 keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1330 The user shouldn't add any image to a 'Manufacturer Serie'
1331 but there is no way no to allow him to do that
1333 None of the 'shadow elements' are droped out.
1337 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1340 'Media Storage SOP Class UID' (0x0002,0x0002)
1341 'SOP Class UID' (0x0008,0x0016) are set to
1342 [Secondary Capture Image Storage]
1343 'Image Type' (0x0008,0x0008) is forced to "DERIVED\PRIMARY"
1344 Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1347 If 'SOP Class UID' exists in the native image ('true DICOM' image)
1348 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1349 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1350 whose value is the original 'SOP Class UID'
1351 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1352 whose value is the original 'SOP Class UID'
1354 3) TODO : find a trick to allow user to pass to the writter the list of the Dicom images
1355 or the Series, (or the Study ?) he used to created his image
1356 (MIP, MPR, cartography image, ...)
1357 These info should be stored (?)
1358 0008 1110 SQ 1 Referenced Study Sequence
1359 0008 1115 SQ 1 Referenced Series Sequence
1360 0008 1140 SQ 1 Referenced Image Sequence
1362 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1363 'Media Storage SOP Class UID' (0x0002,0x0002)
1364 'SOP Class UID' (0x0008,0x0016)
1365 'Image Type' (0x0008,0x0008)
1366 'Conversion Type' (0x0008,0x0064)
1369 Bellow follows the full description (hope so !) of the consistency checks performed
1370 by gdcm::FileHelper::CheckMandatoryElements()
1373 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1374 -->'SOP Class UID' (0x0008,0x0016) are defaulted to
1375 [Secondary Capture Image Storage]
1376 --> 'Image Type' (0x0008,0x0008)
1377 is forced to "DERIVED\PRIMARY"
1378 (The written image is no longer an 'ORIGINAL' one)
1379 Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1381 --> Conversion Type (0x0008,0x0064)
1382 is defaulted to 'SYN' (Synthetic Image)
1383 when *he* knows he created his own image ex nihilo
1385 --> 'Modality' (0x0008,0x0060)
1386 is defaulted to "OT" (other) if missing.
1387 (a fully user created image belongs to *no* modality)
1389 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1390 --> 'Implementation Class UID' (0x0002,0x0012)
1391 are automatically generated; no user intervention possible
1393 --> 'Serie Instance UID'(0x0020,0x000e)
1394 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1395 created if it doesn't.
1396 The user is allowed to create his own Series/Studies,
1397 keeping the same 'Serie Instance UID' / 'Study Instance UID'
1400 The user shouldn't add any image to a 'Manufacturer Serie'
1401 but there is no way no to allowed him to do that
1403 --> If 'SOP Class UID' exists in the native image ('true DICOM' image)
1404 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1406 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1407 whose value is the original 'SOP Class UID'
1408 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1409 whose value is the original 'SOP Class UID'
1411 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1412 --> Pixel Spacing (0x0028,0x0030) is defaulted to "1.0\1.0"
1413 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1415 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1417 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1419 --> Study Date, Study Time are defaulted to current Date and Time
1420 (they remain unchanged if they exist)
1422 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from
1423 Image Orientation (Patient) : (0020|0037) or from
1424 Image Orientation (RET) : (0020 0035)
1426 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1427 are created, with empty value if there are missing.
1429 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1430 are defaulted with a 'gdcm' value.
1432 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1433 --> Referring Physician's Name (Type 2)
1434 are created, with empty value if there are missing.
1436 -------------------------------------------------------------------------------------*/
1438 void FileHelper::CheckMandatoryElements()
1440 std::string sop = Util::CreateUniqueUID();
1442 // --------------------- For Meta Elements ---------------------
1443 // just to remember : 'official' 0002 group
1444 if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1446 // Group 000002 (Meta Elements) already pushed out
1448 //0002 0000 UL 1 Meta Group Length
1449 //0002 0001 OB 1 File Meta Information Version
1450 //0002 0002 UI 1 Media Storage SOP Class UID
1451 //0002 0003 UI 1 Media Storage SOP Instance UID
1452 //0002 0010 UI 1 Transfer Syntax UID
1453 //0002 0012 UI 1 Implementation Class UID
1454 //0002 0013 SH 1 Implementation Version Name
1455 //0002 0016 AE 1 Source Application Entity Title
1456 //0002 0100 UI 1 Private Information Creator
1457 //0002 0102 OB 1 Private Information
1459 // Push out 'ACR-NEMA-special' entries, if any
1460 Archive->Push(0x0008,0x0001); // Length to End
1461 Archive->Push(0x0008,0x0010); // Recognition Code
1462 Archive->Push(0x0028,0x0005); // Image Dimension
1464 // Create them if not found
1465 // Always modify the value
1466 // Push the entries to the archive.
1467 CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1469 DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1470 e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1472 e_0002_0001->SetLength(2);
1473 Archive->Push(e_0002_0001);
1474 e_0002_0001->Delete();
1476 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1478 // we keep the original 'Media Storage SOP Class UID', we default it if missing
1479 CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1483 // It's *not* an image comming straight from a source. We force
1484 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1485 CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1488 // 'Media Storage SOP Instance UID'
1489 CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1491 // 'Implementation Class UID'
1492 // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1493 // seems to be Root UID + 4 digits (?)
1494 CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1496 // 'Implementation Version Name'
1497 std::string version = "GDCM ";
1498 version += Util::GetVersion();
1499 CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1502 // --------------------- For DataSet ---------------------
1504 if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1507 gdcmDebugMacro( "USER_OWN_IMAGE (1)");
1508 // If 'SOP Class UID' exists ('true DICOM' image)
1509 // we create the 'Source Image Sequence' SeqEntry
1510 // to hold informations about the Source Image
1512 DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1515 // Create 'Source Image Sequence' SeqEntry
1516 // SeqEntry *sis = SeqEntry::New (
1517 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x2112) );
1518 SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1519 SQItem *sqi = SQItem::New(1);
1520 // (we assume 'SOP Instance UID' exists too)
1521 // create 'Referenced SOP Class UID'
1522 // DataEntry *e_0008_1150 = DataEntry::New(
1523 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1150) );
1524 DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1525 e_0008_1150->SetString( e_0008_0016->GetString());
1526 sqi->AddEntry(e_0008_1150);
1527 e_0008_1150->Delete();
1529 // create 'Referenced SOP Instance UID'
1530 DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1531 // DataEntry *e_0008_1155 = DataEntry::New(
1532 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1155) );
1533 DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1534 e_0008_1155->SetString( e_0008_0018->GetString());
1535 sqi->AddEntry(e_0008_1155);
1536 e_0008_1155->Delete();
1538 sis->AddSQItem(sqi,1);
1541 // temporarily replaces any previous 'Source Image Sequence'
1545 // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1546 if ( ContentType == FILTERED_IMAGE)
1547 // the user *knows* he just modified the pixels
1548 // the image is no longer an 'Original' one
1549 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1553 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1555 // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1556 CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");
1560 // It's *not* an image comming straight from a source. We force
1561 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1562 CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");
1565 Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1566 // Push out 'LibIDO-special' entries, if any
1567 Archive->Push(0x0028,0x0015);
1568 Archive->Push(0x0028,0x0016);
1569 Archive->Push(0x0028,0x0017);
1570 Archive->Push(0x0028,0x0198); // very old versions
1571 Archive->Push(0x0028,0x0199);
1573 // Replace deprecated 0028 0012 US Planes
1574 // by new 0028 0008 IS Number of Frames
1576 ///\todo : find if there is a rule!
1577 DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1580 CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1581 Archive->Push(0x0028,0x0012);
1584 // Deal with the pb of (Bits Stored = 12)
1585 // - we're gonna write the image as Bits Stored = 16
1586 if ( FileInternal->GetEntryString(0x0028,0x0100) == "12")
1588 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1591 // Check if user wasn't drunk ;-)
1593 std::ostringstream s;
1594 // check 'Bits Allocated' vs decent values
1595 int nbBitsAllocated = FileInternal->GetBitsAllocated();
1596 if ( nbBitsAllocated == 0 || nbBitsAllocated > 32
1597 || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1599 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1600 gdcmWarningMacro("(0028,0100) changed from "
1601 << nbBitsAllocated << " to 16 for consistency purpose");
1602 nbBitsAllocated = 16;
1604 // check 'Bits Stored' vs 'Bits Allocated'
1605 int nbBitsStored = FileInternal->GetBitsStored();
1606 if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1609 s << nbBitsAllocated;
1610 CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1611 gdcmWarningMacro("(0028,0101) changed from "
1612 << nbBitsStored << " to " << nbBitsAllocated
1613 << " for consistency purpose" );
1614 nbBitsStored = nbBitsAllocated;
1616 // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1617 int highBitPosition = FileInternal->GetHighBitPosition();
1618 if ( highBitPosition == 0 ||
1619 highBitPosition > nbBitsAllocated-1 ||
1620 highBitPosition < nbBitsStored-1 )
1623 s << nbBitsStored - 1;
1624 CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1625 gdcmWarningMacro("(0028,0102) changed from "
1626 << highBitPosition << " to " << nbBitsAllocated-1
1627 << " for consistency purpose");
1630 // check Pixel Representation (default it as 0 -unsigned-)
1632 DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1635 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1636 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1640 int sign = (int)e_0028_0103->GetValue(0);
1641 if (sign !=1 && sign !=0)
1643 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1644 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1648 std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1649 if ( pixelSpacing == GDCM_UNFOUND )
1651 pixelSpacing = "1.0\\1.0";
1652 // if missing, Pixel Spacing forced to "1.0\1.0"
1653 CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1656 // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1657 // --> This one is the *legal* one !
1658 if ( ContentType != USER_OWN_IMAGE)
1659 // we write it only when we are *sure* the image comes from
1660 // an imager (see also 0008,0x0064)
1661 CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1664 ///Exact meaning of RETired fields
1666 // See page 73 of ACR-NEMA_300-1988.pdf !
1668 // 0020,0020 : Patient Orientation :
1669 Patient direction of the first row and
1670 column of the images. The first entry id the direction of the raws, given by the
1671 direction of the last pixel in the first row from the first pixel in tha row.
1672 the second entry is the direction of the columns, given by the direction of the
1673 last pixel in the first column from the first pixel in that column.
1674 L : Left, F : Feet, A : Anterior, P : Posterior.
1675 Up to 3 letters can be used in combination to indicate oblique planes.
1677 //0020,0030 Image Position (RET)
1678 x,y,z coordinates im mm of the first pixel in the image
1680 // 0020,0035 Image Orientation (RET)
1681 Direction cosines of the R axis of the image system with respect to the
1682 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1683 the image system with respect to the same axes
1685 //0020,0050 Location
1686 An image location reference, standard for the modality (such as CT bed
1687 position), used to indicate position. Calculation of position for other purposes
1688 is only from (0020,0030) and (0020,0035)
1692 // if imagePositionPatient not found, default it with imagePositionRet, if any
1693 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1695 std::string imagePositionRet = FileInternal->GetEntryString(0x0020,0x0030);
1696 std::string imageOrientationRet = FileInternal->GetEntryString(0x0020,0x0035);
1697 std::string imagePositionPatient = FileInternal->GetEntryString(0x0020,0x0032);
1698 std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1700 if( imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1701 && imagePositionRet != GDCM_UNFOUND && imageOrientationRet != GDCM_UNFOUND)
1703 CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1704 Archive->Push(0x0020,0x0030);
1705 CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1706 Archive->Push(0x0020,0x0035);
1710 // Samples Per Pixel (type 1) : default to grayscale
1711 CheckMandatoryEntry(0x0028,0x0002,"1","US");
1713 // --- Check UID-related Entries ---
1715 // At the end, not to overwrite the original ones,
1716 // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
1717 // 'SOP Instance UID'
1718 CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1720 if ( ContentType == USER_OWN_IMAGE)
1722 gdcmDebugMacro( "USER_OWN_IMAGE (2)");
1724 // Other possible values are :
1725 // See PS 3.3, Page 408
1727 // DV = Digitized Video
1728 // DI = Digital Interface
1729 // DF = Digitized Film
1730 // WSD = Workstation
1731 // SD = Scanned Document
1732 // SI = Scanned Image
1734 // SYN = Synthetic Image
1736 CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1739 if ( ContentType == CREATED_IMAGE)
1741 /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1746 // ---- The user will never have to take any action on the following ----
1748 // new value for 'SOP Instance UID'
1749 //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1751 // Instance Creation Date
1752 const std::string &date = Util::GetCurrentDate();
1753 CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1755 // Instance Creation Time
1756 const std::string &time = Util::GetCurrentTime();
1757 CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1760 CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1762 CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1765 //CopyMandatoryEntry(0x0008,0x0050,"");
1766 CheckMandatoryEntry(0x0008,0x0050,"","SH");
1769 // ----- Add Mandatory Entries if missing ---
1770 // Entries whose type is 1 are mandatory, with a mandatory value
1771 // Entries whose type is 1c are mandatory-inside-a-Sequence,
1772 // with a mandatory value
1773 // Entries whose type is 2 are mandatory, with an optional value
1774 // Entries whose type is 2c are mandatory-inside-a-Sequence,
1775 // with an optional value
1776 // Entries whose type is 3 are optional
1778 // 'Study Instance UID'
1779 // Keep the value if exists
1780 // The user is allowed to create his own Study,
1781 // keeping the same 'Study Instance UID' for various images
1782 // The user may add images to a 'Manufacturer Study',
1783 // adding new Series to an already existing Study
1784 CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1786 // 'Serie Instance UID'
1787 // Keep the value if exists
1788 // The user is allowed to create his own Series,
1789 // keeping the same 'Serie Instance UID' for various images
1790 // The user shouldn't add any image to a 'Manufacturer Serie'
1791 // but there is no way no to prevent him for doing that
1792 CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1795 CheckMandatoryEntry(0x0020,0x0010,"","SH");
1798 CheckMandatoryEntry(0x0020,0x0011,"","IS");
1801 CheckMandatoryEntry(0x0020,0x0013,"","IS");
1803 // Patient Orientation
1804 // Can be computed from (0020|0037) : Image Orientation (Patient)
1805 GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1806 std::string ori = o->GetOrientation ( FileInternal );
1808 if (ori != "\\" && ori != GDCM_UNFOUND)
1809 CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1811 CheckMandatoryEntry(0x0020,0x0020,"","CS");
1813 // Default Patient Position to HFS
1814 CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1816 // Modality : if missing we set it to 'OTher'
1817 CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1819 // Manufacturer : if missing we set it to 'GDCM Factory'
1820 CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1822 // Institution Name : if missing we set it to 'GDCM Hospital'
1823 CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1825 // Patient's Name : if missing, we set it to 'GDCM^Patient'
1826 CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1828 // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1829 CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1831 // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1832 CheckMandatoryEntry(0x0010,0x0030,"","DA");
1834 // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1835 CheckMandatoryEntry(0x0010,0x0040,"","CS");
1837 // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1838 CheckMandatoryEntry(0x0008,0x0090,"","PN");
1841 // Deal with element 0x0000 (group length) of each group.
1842 // First stage : get all the different Groups
1845 DocEntry *d = FileInternal->GetFirstEntry();
1848 grHT[d->GetGroup()] = 0;
1849 d=FileInternal->GetNextEntry();
1851 // Second stage : add the missing ones (if any)
1852 for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)
1854 CheckMandatoryEntry(it->first, 0x0000, "0");
1856 // Third stage : update all 'zero level' groups length
1860 if (PhotometricInterpretation == 1)
1865 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1867 DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1870 //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1871 entry = DataEntry::New(group,elem,vr);
1872 entry->SetString(value);
1873 Archive->Push(entry);
1878 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
1879 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1881 //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1882 DataEntry *entry = DataEntry::New(group,elem,vr);
1883 entry->SetString(value);
1884 Archive->Push(entry);
1888 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1890 DataEntry *entry = CopyDataEntry(group,elem,vr);
1891 entry->SetString(value);
1892 Archive->Push(entry);
1897 * \brief Restore in the File the initial group 0002
1899 void FileHelper::RestoreWriteMandatory()
1901 // group 0002 may be pushed out for ACR-NEMA writting purposes
1902 Archive->Restore(0x0002,0x0000);
1903 Archive->Restore(0x0002,0x0001);
1904 Archive->Restore(0x0002,0x0002);
1905 Archive->Restore(0x0002,0x0003);
1906 Archive->Restore(0x0002,0x0010);
1907 Archive->Restore(0x0002,0x0012);
1908 Archive->Restore(0x0002,0x0013);
1909 Archive->Restore(0x0002,0x0016);
1910 Archive->Restore(0x0002,0x0100);
1911 Archive->Restore(0x0002,0x0102);
1913 // FIXME : Check if none is missing !
1915 Archive->Restore(0x0008,0x0012);
1916 Archive->Restore(0x0008,0x0013);
1917 Archive->Restore(0x0008,0x0016);
1918 Archive->Restore(0x0008,0x0018);
1919 Archive->Restore(0x0008,0x0060);
1920 Archive->Restore(0x0008,0x0070);
1921 Archive->Restore(0x0008,0x0080);
1922 Archive->Restore(0x0008,0x0090);
1923 Archive->Restore(0x0008,0x2112);
1925 Archive->Restore(0x0010,0x0010);
1926 Archive->Restore(0x0010,0x0030);
1927 Archive->Restore(0x0010,0x0040);
1929 Archive->Restore(0x0020,0x000d);
1930 Archive->Restore(0x0020,0x000e);
1934 * \brief CallStartMethod
1936 void FileHelper::CallStartMethod()
1940 CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1944 * \brief CallProgressMethod
1946 void FileHelper::CallProgressMethod()
1948 CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1952 * \brief CallEndMethod
1954 void FileHelper::CallEndMethod()
1957 CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1960 //-----------------------------------------------------------------------------
1963 * \brief Factorization for various forms of constructors.
1965 void FileHelper::Initialize()
1968 ContentType = USER_OWN_IMAGE;
1970 WriteMode = WMODE_RAW;
1971 WriteType = ExplicitVR;
1973 PhotometricInterpretation = 2; // Black = 0
1975 PixelReadConverter = new PixelReadConvert;
1976 PixelWriteConverter = new PixelWriteConvert;
1977 Archive = new DocEntryArchive( FileInternal );
1981 * \brief Reads/[decompresses] the pixels,
1982 * *without* making RGB from Palette Colors
1983 * @return the pixels area, whatever its type
1984 * (uint8_t is just for prototyping : feel free to Cast it)
1986 uint8_t *FileHelper::GetRaw()
1988 PixelReadConverter->SetUserFunction( UserFunction );
1990 uint8_t *raw = PixelReadConverter->GetRaw();
1993 // The Raw image migth not be loaded yet:
1994 std::ifstream *fp = FileInternal->OpenFile();
1995 PixelReadConverter->ReadAndDecompressPixelData( fp );
1997 FileInternal->CloseFile();
1999 raw = PixelReadConverter->GetRaw();
2002 gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
2010 * \brief Deal with Grey levels i.e. re-arange them
2011 * to have low values = dark, high values = bright
2013 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
2015 uint32_t i; // to please M$VC6
2018 // Number of Bits Allocated for storing a Pixel is defaulted to 16
2019 // when absent from the file.
2020 int bitsAllocated = FileInternal->GetBitsAllocated();
2021 if ( bitsAllocated == 0 )
2026 else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2030 // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2031 // when absent from the file.
2032 int bitsStored = FileInternal->GetBitsStored();
2033 if ( bitsStored == 0 )
2035 bitsStored = bitsAllocated;
2038 if (!FileInternal->IsSignedPixelData())
2040 if ( bitsAllocated == 8 )
2042 uint8_t *deb = (uint8_t *)raw;
2043 for (i=0; i<rawSize; i++)
2051 if ( bitsAllocated == 16 )
2054 for (j=0; j<bitsStored-1; j++)
2056 mask = (mask << 1) +1; // will be fff when BitsStored=12
2059 uint16_t *deb = (uint16_t *)raw;
2060 for (i=0; i<rawSize/2; i++)
2070 if ( bitsAllocated == 8 )
2072 uint8_t smask8 = 255;
2073 uint8_t *deb = (uint8_t *)raw;
2074 for (i=0; i<rawSize; i++)
2076 *deb = smask8 - *deb;
2081 if ( bitsAllocated == 16 )
2083 uint16_t smask16 = 65535;
2084 uint16_t *deb = (uint16_t *)raw;
2085 for (i=0; i<rawSize/2; i++)
2087 *deb = smask16 - *deb;
2095 //-----------------------------------------------------------------------------
2097 * \brief Prints the FileInternal + info on PixelReadConvertor
2098 * @param os ostream we want to print in
2099 * @param indent (unused)
2101 void FileHelper::Print(std::ostream &os, std::string const &)
2103 FileInternal->SetPrintLevel(PrintLevel);
2104 FileInternal->Print(os);
2106 if ( FileInternal->IsReadable() )
2108 PixelReadConverter->SetPrintLevel(PrintLevel);
2109 PixelReadConverter->Print(os);
2113 //-----------------------------------------------------------------------------
2114 } // end namespace gdcm
2117 /* Probabely something to be added to use Rescale Slope/Intercept
2118 Have a look ,at ITK code !
2120 // Internal function to rescale pixel according to Rescale Slope/Intercept
2121 template<class TBuffer, class TSource>
2122 void RescaleFunction(TBuffer* buffer, TSource *source,
2123 double slope, double intercept, size_t size)
2125 size /= sizeof(TSource);
2127 if (slope != 1.0 && intercept != 0.0)
2129 // Duff's device. Instead of this code:
2131 // for(unsigned int i=0; i<size; i++)
2133 // buffer[i] = (TBuffer)(source[i]*slope + intercept);
2136 // use Duff's device which exploits "fall through"
2137 register size_t n = (size + 7) / 8;
2140 case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2141 case 7: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2142 case 6: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2143 case 5: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2144 case 4: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2145 case 3: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2146 case 2: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2147 case 1: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2151 else if (slope == 1.0 && intercept != 0.0)
2153 // Duff's device. Instead of this code:
2155 // for(unsigned int i=0; i<size; i++)
2157 // buffer[i] = (TBuffer)(source[i] + intercept);
2160 // use Duff's device which exploits "fall through"
2161 register size_t n = (size + 7) / 8;
2164 case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2165 case 7: *buffer++ = (TBuffer)(*source++ + intercept);
2166 case 6: *buffer++ = (TBuffer)(*source++ + intercept);
2167 case 5: *buffer++ = (TBuffer)(*source++ + intercept);
2168 case 4: *buffer++ = (TBuffer)(*source++ + intercept);
2169 case 3: *buffer++ = (TBuffer)(*source++ + intercept);
2170 case 2: *buffer++ = (TBuffer)(*source++ + intercept);
2171 case 1: *buffer++ = (TBuffer)(*source++ + intercept);
2175 else if (slope != 1.0 && intercept == 0.0)
2177 // Duff's device. Instead of this code:
2179 // for(unsigned int i=0; i<size; i++)
2181 // buffer[i] = (TBuffer)(source[i]*slope);
2184 // use Duff's device which exploits "fall through"
2185 register size_t n = (size + 7) / 8;
2188 case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2189 case 7: *buffer++ = (TBuffer)((*source++)*slope);
2190 case 6: *buffer++ = (TBuffer)((*source++)*slope);
2191 case 5: *buffer++ = (TBuffer)((*source++)*slope);
2192 case 4: *buffer++ = (TBuffer)((*source++)*slope);
2193 case 3: *buffer++ = (TBuffer)((*source++)*slope);
2194 case 2: *buffer++ = (TBuffer)((*source++)*slope);
2195 case 1: *buffer++ = (TBuffer)((*source++)*slope);
2201 // Duff's device. Instead of this code:
2203 // for(unsigned int i=0; i<size; i++)
2205 // buffer[i] = (TBuffer)(source[i]);
2208 // use Duff's device which exploits "fall through"
2209 register size_t n = (size + 7) / 8;
2212 case 0: do { *buffer++ = (TBuffer)(*source++);
2213 case 7: *buffer++ = (TBuffer)(*source++);
2214 case 6: *buffer++ = (TBuffer)(*source++);
2215 case 5: *buffer++ = (TBuffer)(*source++);
2216 case 4: *buffer++ = (TBuffer)(*source++);
2217 case 3: *buffer++ = (TBuffer)(*source++);
2218 case 2: *buffer++ = (TBuffer)(*source++);
2219 case 1: *buffer++ = (TBuffer)(*source++);
2226 template<class TSource>
2227 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2228 void* buffer, TSource *source,
2229 double slope, double intercept, size_t size)
2233 case ImageIOBase::UCHAR:
2234 RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2236 case ImageIOBase::CHAR:
2237 RescaleFunction( (char *)buffer, source, slope, intercept, size);
2239 case ImageIOBase::USHORT:
2240 RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2242 case ImageIOBase::SHORT:
2243 RescaleFunction( (short *)buffer, source, slope, intercept, size);
2245 case ImageIOBase::UINT:
2246 RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2248 case ImageIOBase::INT:
2249 RescaleFunction( (int *)buffer, source, slope, intercept, size);
2251 case ImageIOBase::FLOAT:
2252 RescaleFunction( (float *)buffer, source, slope, intercept, size);
2254 case ImageIOBase::DOUBLE:
2255 RescaleFunction( (double *)buffer, source, slope, intercept, size);
2258 ::itk::OStringStream message;
2259 message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2260 ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);