1 /*=========================================================================
4 Module: $RCSfile: gdcmFileHelper.cxx,v $
7 Date: $Date: 2007/07/05 10:37:53 $
8 Version: $Revision: 1.115 $
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 fh->SetWriteTypeToDcmExpl(); // he wants Explicit Value Representation
79 // Little Endian is the default
80 // no other value is allowed
81 (-->SetWriteType(ExplicitVR);)
82 -->WriteType = ExplicitVR;
83 fh->Write(newFileName); // overwrites the file, if any
86 fh->WriteDcmExplVR(newFileName);
89 // ----------------------------- WARNING -------------------------
92 These lines will be moved to the document-to-be 'Developer's Guide'
94 WriteMode : WMODE_RAW / WMODE_RGB
95 WriteType : ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO
96 PhotometricInterpretation : MONOCHROME2 (0=black), MONOCHROME2 (0=white)
99 fh->SetWriteMode(WMODE_RAW / WMODE_RGB)
101 fh->SetWriteType( ImplicitVR/ExplicitVR/ACR/ACR_LIBIDO/JPEG/JPEG2000)
103 fh->Write(newFileName);
104 CheckMandatoryElements(); // Checks existing ones / Add missing ones
105 Fix VR if unknown elements
106 SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR(); /
107 SetWriteFileTypeToACR() / SetWriteFileTypeToJPEG() / SetWriteFileTypeToJ2K()
108 (Modifies TransferSyntax if any; Pushes to the Archives old one)
109 SetWriteToRaw(); / SetWriteToRGB();
110 (Modifies and pushes to the Archive, when necessary : photochr. interp.,
111 samples per pixel, Planar configuration,
112 bits allocated, bits stored, high bit -ACR 24 bits-
113 Pixels element VR, pushes out the LUT )
115 Sets Photometric Interpretation
116 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
117 Sets VR, BinArea, Length for PixelData
120 Archive->Push(photInt);
121 Archive->Push(pixel);
125 if NumberOfScalarComponents==1
126 SetWriteToRaw(); return;
127 PixelReadConverter->BuildRGBImage()
128 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
129 Archives spp, planConfig,photInt, pixel
131 CheckWriteIntegrity();
132 (checks user given pixels length)
133 FileInternal->Write(fileName,WriteType)
134 fp = opens file(fileName); // out|binary
135 ComputeGroup0002Length( );
136 Document::WriteContent(fp, writetype);
137 writes Dicom File Preamble not ACR-NEMA
138 ElementSet::WriteContent(fp, writetype);
139 writes recursively all DataElements
141 (moves back to the gdcm::File all the archived elements)
147 namespace GDCM_NAME_SPACE
149 typedef std::map<uint16_t, int> GroupHT; // Hash Table
150 //-------------------------------------------------------------------------
151 // Constructor / Destructor
153 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
154 * file (gdcm::File only deals with the ... header)
155 * Opens (in read only and when possible) an existing file and checks
156 * for DICOM compliance. Returns NULL on failure.
157 * It will be up to the user to load the pixels into memory
158 * ( GetImageDataSize() + GetImageData() methods)
159 * \note the in-memory representation of all available tags found in
160 * the DICOM header is post-poned to first header information access.
161 * This avoid a double parsing of public part of the header when
162 * one sets an a posteriori shadow dictionary (efficiency can be
163 * seen as a side effect).
165 FileHelper::FileHelper( )
167 FileInternal = File::New( );
172 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
173 * file (File only deals with the ... header)
174 * Opens (in read only and when possible) an existing file and checks
175 * for DICOM compliance. Returns NULL on failure.
176 * It will be up to the user to load the pixels into memory
177 * ( GetImageDataSize() + GetImageData() methods)
178 * \note the in-memory representation of all available tags found in
179 * the DICOM header is post-poned to first header information access.
180 * This avoid a double parsing of public part of the header when
181 * user sets an a posteriori shadow dictionary (efficiency can be
182 * seen as a side effect).
183 * @param header already built Header
185 FileHelper::FileHelper(File *header)
187 gdcmAssertMacro(header);
189 FileInternal = header;
190 FileInternal->Register();
192 if ( FileInternal->IsReadable() )
194 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
199 * \brief canonical destructor
200 * \note If the header (gdcm::File) was created by the FileHelper constructor,
201 * it is destroyed by the FileHelper
203 FileHelper::~FileHelper()
205 if ( PixelReadConverter )
207 delete PixelReadConverter;
209 if ( PixelWriteConverter )
211 delete PixelWriteConverter;
218 FileInternal->Unregister();
221 //-----------------------------------------------------------------------------
225 * \brief Sets the LoadMode of the internal gdcm::File as a boolean string.
226 * NO_SEQ, NO_SHADOW, NO_SHADOWSEQ ... (nothing more, right now)
227 * WARNING : before using NO_SHADOW, be sure *all* your files
228 * contain accurate values in the 0x0000 element (if any)
229 * of *each* Shadow Group. The parser will fail if the size is wrong !
230 * @param loadMode Load mode to be used
232 void FileHelper::SetLoadMode(int loadMode)
234 GetFile()->SetLoadMode( loadMode );
237 * \brief Sets the LoadMode of the internal gdcm::File
238 * @param fileName name of the file to be open
240 void FileHelper::SetFileName(std::string const &fileName)
242 FileInternal->SetFileName( fileName );
247 * @return false if file cannot be open or no swap info was found,
248 * or no tag was found.
250 bool FileHelper::Load()
252 if ( !FileInternal->Load() )
255 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
260 * \brief Accesses an existing DataEntry through its (group, element)
261 * and modifies its content with the given value.
262 * @param content new value (string) to substitute with
263 * @param group group number of the Dicom Element to modify
264 * @param elem element number of the Dicom Element to modify
265 * \return false if DataEntry not found
267 bool FileHelper::SetEntryString(std::string const &content,
268 uint16_t group, uint16_t elem)
270 return FileInternal->SetEntryString(content, group, elem);
275 * \brief Accesses an existing DataEntry through its (group, element)
276 * and modifies its content with the given value.
277 * @param content new value (void* -> uint8_t*) to substitute with
278 * @param lgth new value length
279 * @param group group number of the Dicom Element to modify
280 * @param elem element number of the Dicom Element to modify
281 * \return false if DataEntry not found
283 bool FileHelper::SetEntryBinArea(uint8_t *content, int lgth,
284 uint16_t group, uint16_t elem)
286 return FileInternal->SetEntryBinArea(content, lgth, group, elem);
290 * \brief Modifies the value of a given DataEntry when it exists.
291 * Creates it with the given value when unexistant.
292 * @param content (string) value to be set
293 * @param group Group number of the Entry
294 * @param elem Element number of the Entry
295 * @param vr Value Representation of the DataElement to be inserted
296 * \return pointer to the modified/created DataEntry (NULL when creation
299 DataEntry *FileHelper::InsertEntryString(std::string const &content,
300 uint16_t group, uint16_t elem,
303 return FileInternal->InsertEntryString(content, group, elem, vr);
307 * \brief Modifies the value of a given DataEntry when it exists.
308 * Creates it with the given value when unexistant.
309 * A copy of the binArea is made to be kept in the Document.
310 * @param binArea (binary) value to be set
311 * @param lgth new value length
312 * @param group Group number of the Entry
313 * @param elem Element number of the Entry
314 * @param vr Value Representation of the DataElement to be inserted
315 * \return pointer to the modified/created DataEntry (NULL when creation
318 DataEntry *FileHelper::InsertEntryBinArea(uint8_t *binArea, int lgth,
319 uint16_t group, uint16_t elem,
322 return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem, vr);
326 * \brief Adds an empty SeqEntry
327 * (remove any existing entry with same group,elem)
328 * @param group Group number of the Entry
329 * @param elem Element number of the Entry
330 * \return pointer to the created SeqEntry (NULL when creation
333 SeqEntry *FileHelper::InsertSeqEntry(uint16_t group, uint16_t elem)
335 return FileInternal->InsertSeqEntry(group, elem);
339 * \brief Get the size of the image data
340 * If the image can be RGB (with a lut or by default), the size
341 * corresponds to the RGB image
342 * (use GetImageDataRawSize if you want to be sure to get *only*
343 * the size of the pixels)
344 * @return The image size
346 size_t FileHelper::GetImageDataSize()
348 if ( PixelWriteConverter->GetUserData() )
350 return PixelWriteConverter->GetUserDataSize();
352 return PixelReadConverter->GetRGBSize();
356 * \brief Get the size of the image data.
357 * If the image could be converted to RGB using a LUT,
358 * this transformation is not taken into account by GetImageDataRawSize
359 * (use GetImageDataSize if you wish)
360 * @return The raw image size
362 size_t FileHelper::GetImageDataRawSize()
364 if ( PixelWriteConverter->GetUserData() )
366 return PixelWriteConverter->GetUserDataSize();
368 return PixelReadConverter->GetRawSize();
372 * \brief brings pixels into memory :
373 * - Allocates necessary memory,
374 * - Reads the pixels from disk (uncompress if necessary),
375 * - Transforms YBR pixels, if any, into RGB pixels,
376 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
377 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
378 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
379 * @return Pointer to newly allocated pixel data.
380 * (uint8_t is just for prototyping. feel free to cast)
381 * NULL if alloc fails
383 uint8_t *FileHelper::GetImageData()
385 if ( PixelWriteConverter->GetUserData() )
387 return PixelWriteConverter->GetUserData();
392 // If the decompression failed nothing can be done.
396 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
398 return PixelReadConverter->GetRGB();
402 // When no LUT or LUT conversion fails, return the Raw
403 return PixelReadConverter->GetRaw();
408 * \brief brings pixels into memory :
409 * - Allocates necessary memory,
410 * - Transforms YBR pixels (if any) into RGB pixels
411 * - Transforms 3 planes R, G, B (if any) into a single RGB Plane
412 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
413 * - DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
414 * @return Pointer to newly allocated pixel data.
415 * (uint8_t is just for prototyping. feel free to cast)
416 * NULL if alloc fails
418 uint8_t *FileHelper::GetImageDataRaw ()
423 //#ifndef GDCM_LEGACY_REMOVE
425 * \brief Useless function, since PixelReadConverter forces us
426 * copy the Pixels anyway.
427 * Reads the pixels from disk (uncompress if necessary),
428 * Transforms YBR pixels, if any, into RGB pixels
429 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
430 * Transforms single Grey plane + 3 Palettes into a RGB Plane
431 * Copies at most MaxSize bytes of pixel data to caller allocated
433 * \warning This function allows people that want to build a volume
434 * from an image stack *not to* have, first to get the image pixels,
435 * and then move them to the volume area.
436 * It's absolutely useless for any VTK user since vtk chooses
437 * to invert the lines of an image, that is the last line comes first
438 * (for some axis related reasons?). Hence he will have
439 * to load the image line by line, starting from the end.
440 * VTK users have to call GetImageData
442 * @param destination Address (in caller's memory space) at which the
443 * pixel data should be copied
444 * @param maxSize Maximum number of bytes to be copied. When MaxSize
445 * is not sufficient to hold the pixel data the copy is not
446 * executed (i.e. no partial copy).
447 * @return On success, the number of bytes actually copied. Zero on
448 * failure e.g. MaxSize is lower than necessary.
451 size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
455 // If the decompression failed nothing can be done.
459 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
461 if ( PixelReadConverter->GetRGBSize() > maxSize )
463 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
467 (void*)PixelReadConverter->GetRGB(),
468 PixelReadConverter->GetRGBSize() );
469 return PixelReadConverter->GetRGBSize();
472 // Either no LUT conversion necessary or LUT conversion failed
473 if ( PixelReadConverter->GetRawSize() > maxSize )
475 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
479 (void *)PixelReadConverter->GetRaw(),
480 PixelReadConverter->GetRawSize() );
481 return PixelReadConverter->GetRawSize();
487 * \brief Points the internal pointer to the callers inData
488 * image representation, BUT WITHOUT COPYING THE DATA.
489 * 'image' Pixels are presented as C-like 2D arrays : line per line.
490 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
491 * \warning Since the pixels are not copied, it is the caller's responsability
492 * not to deallocate its data before gdcm uses them (e.g. with
493 * the Write() method )
494 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
495 * user is allowed to pass any kind of pixelsn since the size is
497 * @param expectedSize total image size, *in Bytes*
499 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
501 PixelWriteConverter->SetUserData(inData, expectedSize);
505 * \brief Set the image data defined by the user
506 * \warning When writting the file, this data are get as default data to write
507 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
508 * user is allowed to pass any kind of pixels since the size is
510 * @param expectedSize total image size, *in Bytes*
512 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
514 PixelWriteConverter->SetUserData(inData, expectedSize);
518 * \brief Get the image data defined by the user
519 * \warning When writting the file, this data are get as default data to write
521 uint8_t *FileHelper::GetUserData()
523 return PixelWriteConverter->GetUserData();
527 * \brief Get the image data size defined by the user
528 * \warning When writting the file, this data are get as default data to write
530 size_t FileHelper::GetUserDataSize()
532 return PixelWriteConverter->GetUserDataSize();
536 * \brief Get the image data from the file.
537 * If a LUT is found, the data are expanded to be RGB
539 uint8_t *FileHelper::GetRGBData()
541 return PixelReadConverter->GetRGB();
545 * \brief Get the image data size from the file.
546 * If a LUT is found, the data are expanded to be RGB
548 size_t FileHelper::GetRGBDataSize()
550 return PixelReadConverter->GetRGBSize();
554 * \brief Get the image data from the file.
555 * Even when a LUT is found, the data are not expanded to RGB!
557 uint8_t *FileHelper::GetRawData()
559 return PixelReadConverter->GetRaw();
563 * \brief Get the image data size from the file.
564 * Even when a LUT is found, the data are not expanded to RGB!
566 size_t FileHelper::GetRawDataSize()
568 return PixelReadConverter->GetRawSize();
572 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT
574 uint8_t* FileHelper::GetLutRGBA()
576 if ( PixelReadConverter->GetLutRGBA() ==0 )
577 PixelReadConverter->BuildLUTRGBA();
578 return PixelReadConverter->GetLutRGBA();
582 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Number
584 int FileHelper::GetLutItemNumber()
586 return PixelReadConverter->GetLutItemNumber();
590 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Size
592 int FileHelper::GetLutItemSize()
594 return PixelReadConverter->GetLutItemSize();
598 * \brief Writes on disk A SINGLE Dicom file
599 * NO test is performed on processor "Endiannity".
600 * It's up to the user to call his Reader properly
601 * @param fileName name of the file to be created
602 * (any already existing file is over written)
603 * @return false if write fails
605 bool FileHelper::WriteRawData(std::string const &fileName)
607 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
610 gdcmWarningMacro( "Fail to open (write) file:" << fileName.c_str());
614 if ( PixelWriteConverter->GetUserData() )
616 fp1.write( (char *)PixelWriteConverter->GetUserData(),
617 PixelWriteConverter->GetUserDataSize() );
619 else if ( PixelReadConverter->GetRGB() )
621 fp1.write( (char *)PixelReadConverter->GetRGB(),
622 PixelReadConverter->GetRGBSize());
624 else if ( PixelReadConverter->GetRaw() )
626 fp1.write( (char *)PixelReadConverter->GetRaw(),
627 PixelReadConverter->GetRawSize());
631 gdcmErrorMacro( "Nothing written." );
640 * \brief Writes on disk A SINGLE Dicom file,
641 * using the Implicit Value Representation convention
642 * NO test is performed on processor "Endianity".
643 * @param fileName name of the file to be created
644 * (any already existing file is overwritten)
645 * @return false if write fails
648 bool FileHelper::WriteDcmImplVR (std::string const &fileName)
650 SetWriteTypeToDcmImplVR();
651 return Write(fileName);
655 * \brief Writes on disk A SINGLE Dicom file,
656 * using the Explicit Value Representation convention
657 * NO test is performed on processor "Endiannity".
658 * @param fileName name of the file to be created
659 * (any already existing file is overwritten)
660 * @return false if write fails
663 bool FileHelper::WriteDcmExplVR (std::string const &fileName)
665 SetWriteTypeToDcmExplVR();
666 return Write(fileName);
670 * \brief Writes on disk A SINGLE Dicom file,
671 * using the ACR-NEMA convention
672 * NO test is performed on processor "Endiannity".
673 * (a l'attention des logiciels cliniques
674 * qui ne prennent en entrée QUE des images ACR ...
675 * \warning if a DICOM_V3 header is supplied,
676 * groups < 0x0008 and shadow groups are ignored
677 * \warning NO TEST is performed on processor "Endiannity".
678 * @param fileName name of the file to be created
679 * (any already existing file is overwritten)
680 * @return false if write fails
683 bool FileHelper::WriteAcr (std::string const &fileName)
686 return Write(fileName);
690 * \brief Writes on disk A SINGLE Dicom file,
691 * @param fileName name of the file to be created
692 * (any already existing file is overwritten)
693 * @return false if write fails
695 bool FileHelper::Write(std::string const &fileName)
698 CheckMandatoryElements(); //called once, here !
705 SetWriteFileTypeToImplicitVR();
708 case Unknown: // should never happen; ExplicitVR is the default value
711 // User should ask gdcm to write an image in Explicit VR mode
712 // only when he is sure *all* the VR of *all* the DataElements is known.
713 // i.e : when there are *only* Public Groups
714 // or *all* the Shadow Groups are fully described in the relevant Shadow
716 // Let's just *dream* about it; *never* trust a user !
717 // We turn to Implicit VR if at least the VR of one element is unknown.
720 e = FileInternal->GetFirstEntry();
723 if (e->GetVR() == " ")
725 SetWriteTypeToDcmImplVR();
726 SetWriteFileTypeToImplicitVR();
730 e = FileInternal->GetNextEntry();
735 SetWriteFileTypeToExplicitVR();
739 SetWriteFileTypeToExplicitVR(); // to see JPRx
743 // NOTHING is done here just for LibIDO.
744 // Just to avoid further trouble if user creates a file ex-nihilo,
745 // wants to write it as an ACR-NEMA file,
746 // and forgets to create any Entry belonging to group 0008
748 // We add Recognition Code (RET)
749 if ( ! FileInternal->GetDataEntry(0x0008, 0x0010) )
750 FileInternal->InsertEntryString("ACR-NEMA V1.0 ",
751 0x0008, 0x0010, "LO");
752 SetWriteFileTypeToACR();
753 // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
756 /// \todo FIXME : JPEG may be either ExplicitVR or ImplicitVR
758 SetWriteFileTypeToJPEG();
762 SetWriteFileTypeToJPEG2000();
766 // --------------------------------------------------------------
767 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
769 // if recognition code tells us we dealt with a LibIDO image
770 // we reproduce on disk the switch between lineNumber and columnNumber
771 // just before writting ...
772 /// \todo the best trick would be *change* the recognition code
773 /// but pb expected if user deals with, e.g. COMPLEX images
775 if ( WriteType == ACR_LIBIDO )
781 SetWriteToNoLibido();
783 // ----------------- End of Special Patch ----------------
788 SetWriteToRaw(); // modifies and pushes to the archive, when necessary
791 SetWriteToRGB(); // modifies and pushes to the archive, when necessary
795 bool check = CheckWriteIntegrity(); // verifies length
796 if (WriteType == JPEG || WriteType == JPEG2000) check = true;
799 check = FileInternal->Write(fileName,WriteType);
803 // RestoreWriteFileType();
804 // RestoreWriteMandatory();
807 // --------------------------------------------------------------
808 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
810 // ...and we restore the header to be Dicom Compliant again
811 // just after writting
812 RestoreWriteOfLibido();
813 // ----------------- End of Special Patch ----------------
818 //-----------------------------------------------------------------------------
821 * \brief Checks the write integrity
823 * The tests made are :
824 * - verify the size of the image to write with the possible write
825 * when the user set an image data
826 * @return true if check is successfull
828 bool FileHelper::CheckWriteIntegrity()
830 if ( PixelWriteConverter->GetUserData() )
832 int numberBitsAllocated = FileInternal->GetBitsAllocated();
833 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
835 gdcmWarningMacro( "numberBitsAllocated changed from "
836 << numberBitsAllocated << " to 16 "
837 << " for consistency purpose" );
838 numberBitsAllocated = 16;
841 size_t decSize = FileInternal->GetXSize()
842 * FileInternal->GetYSize()
843 * FileInternal->GetZSize()
844 * FileInternal->GetTSize()
845 * FileInternal->GetSamplesPerPixel()
846 * ( numberBitsAllocated / 8 );
847 size_t rgbSize = decSize;
848 if ( FileInternal->HasLUT() )
849 rgbSize = decSize * 3;
854 if ( decSize!=PixelWriteConverter->GetUserDataSize() )
856 gdcmWarningMacro( "Data size (Raw) is incorrect. Should be "
857 << decSize << " / Found :"
858 << PixelWriteConverter->GetUserDataSize() );
863 if ( rgbSize!=PixelWriteConverter->GetUserDataSize() )
865 gdcmWarningMacro( "Data size (RGB) is incorrect. Should be "
866 << decSize << " / Found "
867 << PixelWriteConverter->GetUserDataSize() );
878 * \brief Updates the File to write RAW data (as opposed to RGB data)
879 * (modifies, when necessary, photochromatic interpretation,
880 * bits allocated, Pixels element VR)
881 * WARNING : if SetPhotometricInterpretationToMonochrome1() was called before
882 * Pixel Elements if modified :-(
884 void FileHelper::SetWriteToRaw()
886 if ( FileInternal->GetNumberOfScalarComponents() == 3
887 && !FileInternal->HasLUT() )
893 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
894 if (FileInternal->HasLUT() )
896 photInt->SetString("PALETTE COLOR ");
900 if (GetPhotometricInterpretation() == 2)
901 photInt->SetString("MONOCHROME2 "); // 0 = Black
903 photInt->SetString("MONOCHROME1 "); // 0 = White !
906 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
907 PixelReadConverter->GetRawSize());
909 std::string vr = "OB";
910 if ( FileInternal->GetBitsAllocated()>8 )
912 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
914 // For non RAW data. Mainly JPEG
915 if( WriteType == JPEG || WriteType == JPEG2000)
921 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
922 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
923 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
924 pixel->SetLength(PixelWriteConverter->GetDataSize());
927 /// \TODO : fixme : I'm not too much happy with this feature :
928 /// It modifies the Pixel Data
929 /// If user calls twice the writer, images will not be equal !!!
930 if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
932 ConvertFixGreyLevels(PixelWriteConverter->GetData(),
933 PixelWriteConverter->GetDataSize());
936 Archive->Push(photInt);
937 Archive->Push(pixel);
945 * \brief Updates the File to write RGB data (as opposed to RAW data)
946 * (modifies, when necessary, photochromatic interpretation,
947 * samples per pixel, Planar configuration,
948 * bits allocated, bits stored, high bit -ACR 24 bits-
949 * Pixels element VR, pushes out the LUT, )
951 void FileHelper::SetWriteToRGB()
953 if ( FileInternal->GetNumberOfScalarComponents()==3 )
955 PixelReadConverter->BuildRGBImage();
957 DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
958 spp->SetString("3 ");
960 DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
961 planConfig->SetString("0 ");
963 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
964 photInt->SetString("RGB ");
966 if ( PixelReadConverter->GetRGB() )
968 PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
969 PixelReadConverter->GetRGBSize());
973 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
974 PixelReadConverter->GetRawSize());
977 std::string vr = "OB";
978 if ( FileInternal->GetBitsAllocated()>8 )
980 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
983 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
984 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
985 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
986 pixel->SetLength(PixelWriteConverter->GetDataSize());
989 Archive->Push(planConfig);
990 Archive->Push(photInt);
991 Archive->Push(pixel);
994 planConfig->Delete();
999 Archive->Push(0x0028,0x1101);
1000 Archive->Push(0x0028,0x1102);
1001 Archive->Push(0x0028,0x1103);
1002 Archive->Push(0x0028,0x1201);
1003 Archive->Push(0x0028,0x1202);
1004 Archive->Push(0x0028,0x1203);
1006 // push out Palette Color Lookup Table UID, if any
1007 Archive->Push(0x0028,0x1199);
1009 // For old '24 Bits' ACR-NEMA
1010 // Thus, we have a RGB image and the bits allocated = 24 and
1011 // samples per pixels = 1 (in the read file)
1012 if ( FileInternal->GetBitsAllocated()==24 )
1014 DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
1015 bitsAlloc->SetString("8 ");
1017 DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
1018 bitsStored->SetString("8 ");
1020 DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
1021 highBit->SetString("7 ");
1023 Archive->Push(bitsAlloc);
1024 Archive->Push(bitsStored);
1025 Archive->Push(highBit);
1027 bitsAlloc->Delete();
1028 bitsStored->Delete();
1039 * \brief Restore the File write mode
1041 void FileHelper::RestoreWrite()
1044 Archive->Restore(0x0028,0x0002);
1045 Archive->Restore(0x0028,0x0004);
1047 Archive->Restore(0x0028,0x0006);
1048 Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1050 // For old ACR-NEMA (24 bits problem)
1051 Archive->Restore(0x0028,0x0100);
1052 Archive->Restore(0x0028,0x0101);
1053 Archive->Restore(0x0028,0x0102);
1056 Archive->Restore(0x0028,0x1101);
1057 Archive->Restore(0x0028,0x1102);
1058 Archive->Restore(0x0028,0x1103);
1059 Archive->Restore(0x0028,0x1201);
1060 Archive->Restore(0x0028,0x1202);
1061 Archive->Restore(0x0028,0x1203);
1063 // For the Palette Color Lookup Table UID
1064 Archive->Restore(0x0028,0x1203);
1066 // group 0002 may be pushed out for ACR-NEMA writting purposes
1067 Archive->Restore(0x0002,0x0000);
1068 Archive->Restore(0x0002,0x0001);
1069 Archive->Restore(0x0002,0x0002);
1070 Archive->Restore(0x0002,0x0003);
1071 Archive->Restore(0x0002,0x0010);
1072 Archive->Restore(0x0002,0x0012);
1073 Archive->Restore(0x0002,0x0013);
1074 Archive->Restore(0x0002,0x0016);
1075 Archive->Restore(0x0002,0x0100);
1076 Archive->Restore(0x0002,0x0102);
1081 * \brief Pushes out the whole group 0002
1082 * FIXME : better, set a flag to tell the writer not to write it ...
1083 * FIXME : method should probably have an other name !
1084 * SetWriteFileTypeToACR is NOT opposed to
1085 * SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1087 void FileHelper::SetWriteFileTypeToACR()
1089 Archive->Push(0x0002,0x0000);
1090 Archive->Push(0x0002,0x0001);
1091 Archive->Push(0x0002,0x0002);
1092 Archive->Push(0x0002,0x0003);
1093 Archive->Push(0x0002,0x0010);
1094 Archive->Push(0x0002,0x0012);
1095 Archive->Push(0x0002,0x0013);
1096 Archive->Push(0x0002,0x0016);
1097 Archive->Push(0x0002,0x0100);
1098 Archive->Push(0x0002,0x0102);
1102 * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1104 void FileHelper::SetWriteFileTypeToJPEG2000()
1106 std::string ts = Util::DicomString(
1107 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1109 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1117 * \brief Sets in the File the TransferSyntax to 'JPEG'
1119 void FileHelper::SetWriteFileTypeToJPEG()
1121 std::string ts = Util::DicomString(
1122 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGBaselineProcess1) );
1124 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1132 * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"
1134 void FileHelper::SetWriteFileTypeToExplicitVR()
1136 std::string ts = Util::DicomString(
1137 Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1139 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1146 * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"
1148 void FileHelper::SetWriteFileTypeToImplicitVR()
1150 std::string ts = Util::DicomString(
1151 Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1153 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1161 * \brief Restore in the File the initial group 0002
1163 void FileHelper::RestoreWriteFileType()
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(oldRow->GetDictEntry());
1180 //DataEntry *newCol=DataEntry::New(oldCol->GetDictEntry());
1182 DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1183 DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1185 newRow->Copy(oldCol);
1186 newCol->Copy(oldRow);
1188 newRow->SetString(oldCol->GetString());
1189 newCol->SetString(oldRow->GetString());
1191 Archive->Push(newRow);
1192 Archive->Push(newCol);
1198 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1199 libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1200 Archive->Push(libidoCode);
1201 libidoCode->Delete();
1205 * \brief Set the Write not to No Libido format
1207 void FileHelper::SetWriteToNoLibido()
1209 DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1212 if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1214 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1215 libidoCode->SetString("");
1216 Archive->Push(libidoCode);
1217 libidoCode->Delete();
1223 * \brief Restore the Write format
1225 void FileHelper::RestoreWriteOfLibido()
1227 Archive->Restore(0x0028,0x0010);
1228 Archive->Restore(0x0028,0x0011);
1229 Archive->Restore(0x0008,0x0010);
1231 // Restore 'LibIDO-special' entries, if any
1232 Archive->Restore(0x0028,0x0015);
1233 Archive->Restore(0x0028,0x0016);
1234 Archive->Restore(0x0028,0x0017);
1235 Archive->Restore(0x0028,0x00199);
1239 * \brief Duplicates a DataEntry or creates it.
1240 * @param group Group number of the Entry
1241 * @param elem Element number of the Entry
1242 * @param vr Value Representation of the Entry
1243 * \return pointer to the new Bin Entry (NULL when creation failed).
1245 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1248 DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1251 if ( oldE && vr != GDCM_VRUNKNOWN )
1252 if ( oldE->GetVR() != vr )
1257 //newE = DataEntry::New(oldE->GetDictEntry());
1258 newE = DataEntry::New(group, elem, vr);
1263 newE = GetFile()->NewDataEntry(group, elem, vr);
1270 * \brief This method is called automatically, just before writting
1271 * in order to produce a 'True Dicom V3' image.
1273 * We cannot know *how* the user made the File :
1274 * (reading an old ACR-NEMA file or a not very clean DICOM file ...)
1275 * Just before writting :
1276 * - we check the Entries
1277 * - we create the mandatory entries if they are missing
1278 * - we modify the values if necessary
1279 * - we push the sensitive entries to the Archive
1280 * The writing process will restore the entries as they where before
1281 * entering FileHelper::CheckMandatoryElements, so the user will always
1282 * see the entries just as they were before he decided to write.
1285 * - Entries whose type is 1 are mandatory, with a mandatory value
1286 * - Entries whose type is 1c are mandatory-inside-a-Sequence,
1287 * with a mandatory value
1288 * - Entries whose type is 2 are mandatory, with an optional value
1289 * - Entries whose type is 2c are mandatory-inside-a-Sequence,
1290 * with an optional value
1291 * - Entries whose type is 3 are optional
1294 * - warn the user if we had to add some entries :
1295 * even if a mandatory entry is missing, we add it, with a default value
1296 * (we don't want to give up the writting process if user forgot to
1297 * specify Lena's Patient ID, for instance ...)
1298 * - read the whole PS 3.3 Part of DICOM (890 pages)
1299 * and write a *full* checker (probably one method per Modality ...)
1300 * Any contribution is welcome.
1301 * - write a user callable full checker, to allow post reading
1302 * and/or pre writting image consistency check.
1305 /* -------------------------------------------------------------------------------------
1306 To be moved to User's guide / WIKI ?
1308 We have to deal with 4 *very* different cases :
1309 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1311 -2) user modified the pixels of an existing image.
1313 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1315 -4) user modified/added some tags *without processing* the pixels (anonymization..
1316 UNMODIFIED_PIXELS_IMAGE
1317 -Probabely some more to be added
1319 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1322 0008 0012 Instance Creation Date
1323 0008 0013 Instance Creation Time
1324 0008 0018 SOP Instance UID
1325 are *always* created with the current values; user has *no* possible intervention on
1328 'Serie Instance UID'(0x0020,0x000e)
1329 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1330 created if it doesn't.
1331 The user is allowed to create his own Series/Studies,
1332 keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1334 The user shouldn't add any image to a 'Manufacturer Serie'
1335 but there is no way no to allow him to do that
1337 None of the 'shadow elements' are droped out.
1341 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1344 'Media Storage SOP Class UID' (0x0002,0x0002)
1345 'SOP Class UID' (0x0008,0x0016) are set to
1346 [Secondary Capture Image Storage]
1347 'Image Type' (0x0008,0x0008) is forced to "DERIVED\PRIMARY"
1348 Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1351 If 'SOP Class UID' exists in the native image ('true DICOM' image)
1352 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1353 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1354 whose value is the original 'SOP Class UID'
1355 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1356 whose value is the original 'SOP Class UID'
1358 3) TODO : find a trick to allow user to pass to the writter the list of the Dicom images
1359 or the Series, (or the Study ?) he used to created his image
1360 (MIP, MPR, cartography image, ...)
1361 These info should be stored (?)
1362 0008 1110 SQ 1 Referenced Study Sequence
1363 0008 1115 SQ 1 Referenced Series Sequence
1364 0008 1140 SQ 1 Referenced Image Sequence
1366 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1367 'Media Storage SOP Class UID' (0x0002,0x0002)
1368 'SOP Class UID' (0x0008,0x0016)
1369 'Image Type' (0x0008,0x0008)
1370 'Conversion Type' (0x0008,0x0064)
1373 Bellow follows the full description (hope so !) of the consistency checks performed
1374 by gdcm::FileHelper::CheckMandatoryElements()
1377 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1378 -->'SOP Class UID' (0x0008,0x0016) are defaulted to
1379 [Secondary Capture Image Storage]
1380 --> 'Image Type' (0x0008,0x0008)
1381 is forced to "DERIVED\PRIMARY"
1382 (The written image is no longer an 'ORIGINAL' one)
1383 Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1385 --> Conversion Type (0x0008,0x0064)
1386 is defaulted to 'SYN' (Synthetic Image)
1387 when *he* knows he created his own image ex nihilo
1389 --> 'Modality' (0x0008,0x0060)
1390 is defaulted to "OT" (other) if missing.
1391 (a fully user created image belongs to *no* modality)
1393 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1394 --> 'Implementation Class UID' (0x0002,0x0012)
1395 are automatically generated; no user intervention possible
1397 --> 'Serie Instance UID'(0x0020,0x000e)
1398 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1399 created if it doesn't.
1400 The user is allowed to create his own Series/Studies,
1401 keeping the same 'Serie Instance UID' / 'Study Instance UID'
1404 The user shouldn't add any image to a 'Manufacturer Serie'
1405 but there is no way no to allowed him to do that
1407 --> If 'SOP Class UID' exists in the native image ('true DICOM' image)
1408 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1410 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1411 whose value is the original 'SOP Class UID'
1412 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1413 whose value is the original 'SOP Class UID'
1415 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1416 --> Pixel Spacing (0x0028,0x0030) is defaulted to "1.0\1.0"
1417 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1419 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1421 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1423 --> Study Date, Study Time are defaulted to current Date and Time
1424 (they remain unchanged if they exist)
1426 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from
1427 Image Orientation (Patient) : (0020|0037) or from
1428 Image Orientation (RET) : (0020 0035)
1430 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1431 are created, with empty value if there are missing.
1433 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1434 are defaulted with a 'gdcm' value.
1436 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1437 --> Referring Physician's Name (Type 2)
1438 are created, with empty value if there are missing.
1440 -------------------------------------------------------------------------------------*/
1442 void FileHelper::CheckMandatoryElements()
1444 std::string sop = Util::CreateUniqueUID();
1446 // --------------------- For Meta Elements ---------------------
1447 // just to remember : 'official' 0002 group
1448 if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1450 // Group 000002 (Meta Elements) already pushed out
1452 //0002 0000 UL 1 Meta Group Length
1453 //0002 0001 OB 1 File Meta Information Version
1454 //0002 0002 UI 1 Media Stored SOP Class UID
1455 //0002 0003 UI 1 Media Stored SOP Instance UID
1456 //0002 0010 UI 1 Transfer Syntax UID
1457 //0002 0012 UI 1 Implementation Class UID
1458 //0002 0013 SH 1 Implementation Version Name
1459 //0002 0016 AE 1 Source Application Entity Title
1460 //0002 0100 UI 1 Private Information Creator
1461 //0002 0102 OB 1 Private Information
1463 // Push out 'ACR-NEMA-special' entries, if any
1464 Archive->Push(0x0008,0x0001); // Length to End
1465 Archive->Push(0x0008,0x0010); // Recognition Code
1466 Archive->Push(0x0028,0x0005); // Image Dimension
1468 // Create them if not found
1469 // Always modify the value
1470 // Push the entries to the archive.
1471 CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1473 DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1474 e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1476 e_0002_0001->SetLength(2);
1477 Archive->Push(e_0002_0001);
1478 e_0002_0001->Delete();
1480 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1482 // we keep the original 'Media Storage SOP Class UID', we default it if missing
1483 CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1487 // It's *not* an image comming straight from a source. We force
1488 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1489 CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1492 // 'Media Storage SOP Instance UID'
1493 CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1495 // 'Implementation Class UID'
1496 // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1497 // seems to be Root UID + 4 digits (?)
1498 CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1500 // 'Implementation Version Name'
1501 std::string version = "GDCM ";
1502 version += Util::GetVersion();
1503 CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1506 // --------------------- For DataSet ---------------------
1508 if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1511 gdcmDebugMacro( "USER_OWN_IMAGE (1)");
1512 // If 'SOP Class UID' exists ('true DICOM' image)
1513 // we create the 'Source Image Sequence' SeqEntry
1514 // to hold informations about the Source Image
1516 DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1519 // Create 'Source Image Sequence' SeqEntry
1520 // SeqEntry *sis = SeqEntry::New (
1521 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x2112) );
1522 SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1523 SQItem *sqi = SQItem::New(1);
1524 // (we assume 'SOP Instance UID' exists too)
1525 // create 'Referenced SOP Class UID'
1526 // DataEntry *e_0008_1150 = DataEntry::New(
1527 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1150) );
1528 DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1529 e_0008_1150->SetString( e_0008_0016->GetString());
1530 sqi->AddEntry(e_0008_1150);
1531 e_0008_1150->Delete();
1533 // create 'Referenced SOP Instance UID'
1534 DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1535 // DataEntry *e_0008_1155 = DataEntry::New(
1536 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1155) );
1537 DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1538 e_0008_1155->SetString( e_0008_0018->GetString());
1539 sqi->AddEntry(e_0008_1155);
1540 e_0008_1155->Delete();
1542 sis->AddSQItem(sqi,1);
1545 // temporarily replaces any previous 'Source Image Sequence'
1549 // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1550 if ( ContentType == FILTERED_IMAGE)
1551 // the user *knows* he just modified the pixels
1552 // the image is no longer an 'Original' one
1553 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1557 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1559 // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1560 CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");
1564 // It's *not* an image comming straight from a source. We force
1565 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1566 CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");
1569 Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1570 // Push out 'LibIDO-special' entries, if any
1571 Archive->Push(0x0028,0x0015);
1572 Archive->Push(0x0028,0x0016);
1573 Archive->Push(0x0028,0x0017);
1574 Archive->Push(0x0028,0x0198); // very old versions
1575 Archive->Push(0x0028,0x0199);
1577 // Replace deprecated 0028 0012 US Planes
1578 // by new 0028 0008 IS Number of Frames
1580 ///\todo : find if there is a rule!
1581 DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1584 CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1585 Archive->Push(0x0028,0x0012);
1588 // Deal with the pb of (Bits Stored = 12)
1589 // - we're gonna write the image as Bits Stored = 16
1590 if ( FileInternal->GetEntryString(0x0028,0x0100) == "12")
1592 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1595 // Check if user wasn't drunk ;-)
1597 std::ostringstream s;
1598 // check 'Bits Allocated' vs decent values
1599 int nbBitsAllocated = FileInternal->GetBitsAllocated();
1600 if ( nbBitsAllocated == 0 || nbBitsAllocated > 32
1601 || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1603 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1604 gdcmWarningMacro("(0028,0100) changed from "
1605 << nbBitsAllocated << " to 16 for consistency purpose");
1606 nbBitsAllocated = 16;
1608 // check 'Bits Stored' vs 'Bits Allocated'
1609 int nbBitsStored = FileInternal->GetBitsStored();
1610 if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1613 s << nbBitsAllocated;
1614 CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1615 gdcmWarningMacro("(0028,0101) changed from "
1616 << nbBitsStored << " to " << nbBitsAllocated
1617 << " for consistency purpose" );
1618 nbBitsStored = nbBitsAllocated;
1620 // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1621 int highBitPosition = FileInternal->GetHighBitPosition();
1622 if ( highBitPosition == 0 ||
1623 highBitPosition > nbBitsAllocated-1 ||
1624 highBitPosition < nbBitsStored-1 )
1627 s << nbBitsStored - 1;
1628 CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1629 gdcmWarningMacro("(0028,0102) changed from "
1630 << highBitPosition << " to " << nbBitsAllocated-1
1631 << " for consistency purpose");
1634 // check Pixel Representation (default it as 0 -unsigned-)
1636 DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1639 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1640 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1644 int sign = (int)e_0028_0103->GetValue(0);
1645 if (sign !=1 && sign !=0)
1647 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1648 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1652 std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1653 if ( pixelSpacing == GDCM_UNFOUND )
1655 pixelSpacing = "1.0\\1.0";
1656 // if missing, Pixel Spacing forced to "1.0\1.0"
1657 CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1660 // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1661 // --> This one is the *legal* one !
1662 if ( ContentType != USER_OWN_IMAGE)
1663 // we write it only when we are *sure* the image comes from
1664 // an imager (see also 0008,0x0064)
1665 CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1668 ///Exact meaning of RETired fields
1670 // See page 73 of ACR-NEMA_300-1988.pdf !
1672 // 0020,0020 : Patient Orientation :
1673 Patient direction of the first row and
1674 column of the images. The first entry id the direction of the raws, given by the
1675 direction of the last pixel in the first row from the first pixel in tha row.
1676 the second entry is the direction of the columns, given by the direction of the
1677 last pixel in the first column from the first pixel in that column.
1678 L : Left, F : Feet, A : Anterior, P : Posterior.
1679 Up to 3 letters can be used in combination to indicate oblique planes.
1681 //0020,0030 Image Position (RET)
1682 x,y,z coordinates im mm of the first pixel in the image
1684 // 0020,0035 Image Orientation (RET)
1685 Direction cosines of the R axis of the image system with respect to the
1686 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1687 the image system with respect to the same axes
1689 //0020,0050 Location
1690 An image location reference, standard for the modality (such as CT bed
1691 position), used to indicate position. Calculation of position for other purposes
1692 is only from (0020,0030) and (0020,0035)
1696 // if imagePositionPatient not found, default it with imagePositionRet, if any
1697 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1699 std::string imagePositionRet = FileInternal->GetEntryString(0x0020,0x0030);
1700 std::string imageOrientationRet = FileInternal->GetEntryString(0x0020,0x0035);
1701 std::string imagePositionPatient = FileInternal->GetEntryString(0x0020,0x0032);
1702 std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1704 if( imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1705 && imagePositionRet != GDCM_UNFOUND && imageOrientationRet != GDCM_UNFOUND)
1707 CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1708 Archive->Push(0x0020,0x0030);
1709 CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1710 Archive->Push(0x0020,0x0035);
1714 // Samples Per Pixel (type 1) : default to grayscale
1715 CheckMandatoryEntry(0x0028,0x0002,"1","US");
1717 // --- Check UID-related Entries ---
1719 // At the end, not to overwrite the original ones,
1720 // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
1721 // 'SOP Instance UID'
1722 CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1724 if ( ContentType == USER_OWN_IMAGE)
1726 gdcmDebugMacro( "USER_OWN_IMAGE (2)");
1728 // Other possible values are :
1729 // See PS 3.3, Page 408
1731 // DV = Digitized Video
1732 // DI = Digital Interface
1733 // DF = Digitized Film
1734 // WSD = Workstation
1735 // SD = Scanned Document
1736 // SI = Scanned Image
1738 // SYN = Synthetic Image
1740 CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1743 if ( ContentType == CREATED_IMAGE)
1745 /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1750 // ---- The user will never have to take any action on the following ----
1752 // new value for 'SOP Instance UID'
1753 //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1755 // Instance Creation Date
1756 const std::string &date = Util::GetCurrentDate();
1757 CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1759 // Instance Creation Time
1760 const std::string &time = Util::GetCurrentTime();
1761 CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1764 CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1766 CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1769 //CopyMandatoryEntry(0x0008,0x0050,"");
1770 CheckMandatoryEntry(0x0008,0x0050,"","SH");
1773 // ----- Add Mandatory Entries if missing ---
1774 // Entries whose type is 1 are mandatory, with a mandatory value
1775 // Entries whose type is 1c are mandatory-inside-a-Sequence,
1776 // with a mandatory value
1777 // Entries whose type is 2 are mandatory, with an optional value
1778 // Entries whose type is 2c are mandatory-inside-a-Sequence,
1779 // with an optional value
1780 // Entries whose type is 3 are optional
1782 // 'Study Instance UID'
1783 // Keep the value if exists
1784 // The user is allowed to create his own Study,
1785 // keeping the same 'Study Instance UID' for various images
1786 // The user may add images to a 'Manufacturer Study',
1787 // adding new Series to an already existing Study
1788 CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1790 // 'Serie Instance UID'
1791 // Keep the value if exists
1792 // The user is allowed to create his own Series,
1793 // keeping the same 'Serie Instance UID' for various images
1794 // The user shouldn't add any image to a 'Manufacturer Serie'
1795 // but there is no way no to prevent him for doing that
1796 CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1799 CheckMandatoryEntry(0x0020,0x0010,"","SH");
1802 CheckMandatoryEntry(0x0020,0x0011,"","IS");
1805 CheckMandatoryEntry(0x0020,0x0013,"","IS");
1807 // Patient Orientation
1808 // Can be computed from (0020|0037) : Image Orientation (Patient)
1809 GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1810 std::string ori = o->GetOrientation ( FileInternal );
1812 if (ori != "\\" && ori != GDCM_UNFOUND)
1813 CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1815 CheckMandatoryEntry(0x0020,0x0020,"","CS");
1817 // Default Patient Position to HFS
1818 CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1820 // Modality : if missing we set it to 'OTher'
1821 CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1823 // Manufacturer : if missing we set it to 'GDCM Factory'
1824 CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1826 // Institution Name : if missing we set it to 'GDCM Hospital'
1827 CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1829 // Patient's Name : if missing, we set it to 'GDCM^Patient'
1830 CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1832 // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1833 CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1835 // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1836 CheckMandatoryEntry(0x0010,0x0030,"","DA");
1838 // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1839 CheckMandatoryEntry(0x0010,0x0040,"","CS");
1841 // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1842 CheckMandatoryEntry(0x0008,0x0090,"","PN");
1845 // Deal with element 0x0000 (group length) of each group.
1846 // First stage : get all the different Groups
1849 DocEntry *d = FileInternal->GetFirstEntry();
1852 grHT[d->GetGroup()] = 0;
1853 d=FileInternal->GetNextEntry();
1855 // Second stage : add the missing ones (if any)
1856 for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)
1858 CheckMandatoryEntry(it->first, 0x0000, "0");
1860 // Third stage : update all 'zero level' groups length
1864 if (PhotometricInterpretation == 1)
1869 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1871 DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1874 //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1875 entry = DataEntry::New(group,elem,vr);
1876 entry->SetString(value);
1877 Archive->Push(entry);
1882 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
1883 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1885 //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1886 DataEntry *entry = DataEntry::New(group,elem,vr);
1887 entry->SetString(value);
1888 Archive->Push(entry);
1892 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1894 DataEntry *entry = CopyDataEntry(group,elem,vr);
1895 entry->SetString(value);
1896 Archive->Push(entry);
1901 * \brief Restore in the File the initial group 0002
1903 void FileHelper::RestoreWriteMandatory()
1905 // group 0002 may be pushed out for ACR-NEMA writting purposes
1906 Archive->Restore(0x0002,0x0000);
1907 Archive->Restore(0x0002,0x0001);
1908 Archive->Restore(0x0002,0x0002);
1909 Archive->Restore(0x0002,0x0003);
1910 Archive->Restore(0x0002,0x0010);
1911 Archive->Restore(0x0002,0x0012);
1912 Archive->Restore(0x0002,0x0013);
1913 Archive->Restore(0x0002,0x0016);
1914 Archive->Restore(0x0002,0x0100);
1915 Archive->Restore(0x0002,0x0102);
1917 // FIXME : Check if none is missing !
1919 Archive->Restore(0x0008,0x0012);
1920 Archive->Restore(0x0008,0x0013);
1921 Archive->Restore(0x0008,0x0016);
1922 Archive->Restore(0x0008,0x0018);
1923 Archive->Restore(0x0008,0x0060);
1924 Archive->Restore(0x0008,0x0070);
1925 Archive->Restore(0x0008,0x0080);
1926 Archive->Restore(0x0008,0x0090);
1927 Archive->Restore(0x0008,0x2112);
1929 Archive->Restore(0x0010,0x0010);
1930 Archive->Restore(0x0010,0x0030);
1931 Archive->Restore(0x0010,0x0040);
1933 Archive->Restore(0x0020,0x000d);
1934 Archive->Restore(0x0020,0x000e);
1939 * \brief CallStartMethod
1941 void FileHelper::CallStartMethod()
1945 CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1949 * \brief CallProgressMethod
1951 void FileHelper::CallProgressMethod()
1953 CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1957 * \brief CallEndMethod
1959 void FileHelper::CallEndMethod()
1962 CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1965 //-----------------------------------------------------------------------------
1968 * \brief Factorization for various forms of constructors.
1970 void FileHelper::Initialize()
1973 ContentType = USER_OWN_IMAGE;
1975 WriteMode = WMODE_RAW;
1976 WriteType = ExplicitVR;
1978 PhotometricInterpretation = 2; // Black = 0
1980 PixelReadConverter = new PixelReadConvert;
1981 PixelWriteConverter = new PixelWriteConvert;
1982 Archive = new DocEntryArchive( FileInternal );
1986 * \brief Reads/[decompresses] the pixels,
1987 * *without* making RGB from Palette Colors
1988 * @return the pixels area, whatever its type
1989 * (uint8_t is just for prototyping : feel free to Cast it)
1991 uint8_t *FileHelper::GetRaw()
1993 PixelReadConverter->SetUserFunction( UserFunction );
1995 uint8_t *raw = PixelReadConverter->GetRaw();
1998 // The Raw image migth not be loaded yet:
1999 std::ifstream *fp = FileInternal->OpenFile();
2000 PixelReadConverter->ReadAndDecompressPixelData( fp );
2002 FileInternal->CloseFile();
2004 raw = PixelReadConverter->GetRaw();
2007 gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
2016 * \brief Deal with Grey levels i.e. re-arange them
2017 * to have low values = dark, high values = bright
2019 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
2021 uint32_t i; // to please M$VC6
2024 // Number of Bits Allocated for storing a Pixel is defaulted to 16
2025 // when absent from the file.
2026 int bitsAllocated = FileInternal->GetBitsAllocated();
2027 if ( bitsAllocated == 0 )
2032 else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2036 // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2037 // when absent from the file.
2038 int bitsStored = FileInternal->GetBitsStored();
2039 if ( bitsStored == 0 )
2041 bitsStored = bitsAllocated;
2044 if (!FileInternal->IsSignedPixelData())
2046 if ( bitsAllocated == 8 )
2048 uint8_t *deb = (uint8_t *)raw;
2049 for (i=0; i<rawSize; i++)
2057 if ( bitsAllocated == 16 )
2060 for (j=0; j<bitsStored-1; j++)
2062 mask = (mask << 1) +1; // will be fff when BitsStored=12
2065 uint16_t *deb = (uint16_t *)raw;
2066 for (i=0; i<rawSize/2; i++)
2076 if ( bitsAllocated == 8 )
2078 uint8_t smask8 = 255;
2079 uint8_t *deb = (uint8_t *)raw;
2080 for (i=0; i<rawSize; i++)
2082 *deb = smask8 - *deb;
2087 if ( bitsAllocated == 16 )
2089 uint16_t smask16 = 65535;
2090 uint16_t *deb = (uint16_t *)raw;
2091 for (i=0; i<rawSize/2; i++)
2093 *deb = smask16 - *deb;
2101 //-----------------------------------------------------------------------------
2103 * \brief Prints the common part of DataEntry, SeqEntry
2104 * @param os ostream we want to print in
2105 * @param indent (unused)
2107 void FileHelper::Print(std::ostream &os, std::string const &)
2109 FileInternal->SetPrintLevel(PrintLevel);
2110 FileInternal->Print(os);
2112 if ( FileInternal->IsReadable() )
2114 PixelReadConverter->SetPrintLevel(PrintLevel);
2115 PixelReadConverter->Print(os);
2119 //-----------------------------------------------------------------------------
2120 } // end namespace gdcm
2123 /* Probabely something to be added to use Rescale Slope/Intercept
2124 Have a look ,at ITK code !
2126 // Internal function to rescale pixel according to Rescale Slope/Intercept
2127 template<class TBuffer, class TSource>
2128 void RescaleFunction(TBuffer* buffer, TSource *source,
2129 double slope, double intercept, size_t size)
2131 size /= sizeof(TSource);
2133 if (slope != 1.0 && intercept != 0.0)
2135 // Duff's device. Instead of this code:
2137 // for(unsigned int i=0; i<size; i++)
2139 // buffer[i] = (TBuffer)(source[i]*slope + intercept);
2142 // use Duff's device which exploits "fall through"
2143 register size_t n = (size + 7) / 8;
2146 case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2147 case 7: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2148 case 6: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2149 case 5: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2150 case 4: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2151 case 3: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2152 case 2: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2153 case 1: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2157 else if (slope == 1.0 && intercept != 0.0)
2159 // Duff's device. Instead of this code:
2161 // for(unsigned int i=0; i<size; i++)
2163 // buffer[i] = (TBuffer)(source[i] + intercept);
2166 // use Duff's device which exploits "fall through"
2167 register size_t n = (size + 7) / 8;
2170 case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2171 case 7: *buffer++ = (TBuffer)(*source++ + intercept);
2172 case 6: *buffer++ = (TBuffer)(*source++ + intercept);
2173 case 5: *buffer++ = (TBuffer)(*source++ + intercept);
2174 case 4: *buffer++ = (TBuffer)(*source++ + intercept);
2175 case 3: *buffer++ = (TBuffer)(*source++ + intercept);
2176 case 2: *buffer++ = (TBuffer)(*source++ + intercept);
2177 case 1: *buffer++ = (TBuffer)(*source++ + intercept);
2181 else if (slope != 1.0 && intercept == 0.0)
2183 // Duff's device. Instead of this code:
2185 // for(unsigned int i=0; i<size; i++)
2187 // buffer[i] = (TBuffer)(source[i]*slope);
2190 // use Duff's device which exploits "fall through"
2191 register size_t n = (size + 7) / 8;
2194 case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2195 case 7: *buffer++ = (TBuffer)((*source++)*slope);
2196 case 6: *buffer++ = (TBuffer)((*source++)*slope);
2197 case 5: *buffer++ = (TBuffer)((*source++)*slope);
2198 case 4: *buffer++ = (TBuffer)((*source++)*slope);
2199 case 3: *buffer++ = (TBuffer)((*source++)*slope);
2200 case 2: *buffer++ = (TBuffer)((*source++)*slope);
2201 case 1: *buffer++ = (TBuffer)((*source++)*slope);
2207 // Duff's device. Instead of this code:
2209 // for(unsigned int i=0; i<size; i++)
2211 // buffer[i] = (TBuffer)(source[i]);
2214 // use Duff's device which exploits "fall through"
2215 register size_t n = (size + 7) / 8;
2218 case 0: do { *buffer++ = (TBuffer)(*source++);
2219 case 7: *buffer++ = (TBuffer)(*source++);
2220 case 6: *buffer++ = (TBuffer)(*source++);
2221 case 5: *buffer++ = (TBuffer)(*source++);
2222 case 4: *buffer++ = (TBuffer)(*source++);
2223 case 3: *buffer++ = (TBuffer)(*source++);
2224 case 2: *buffer++ = (TBuffer)(*source++);
2225 case 1: *buffer++ = (TBuffer)(*source++);
2234 template<class TSource>
2235 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2236 void* buffer, TSource *source,
2237 double slope, double intercept, size_t size)
2241 case ImageIOBase::UCHAR:
2242 RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2244 case ImageIOBase::CHAR:
2245 RescaleFunction( (char *)buffer, source, slope, intercept, size);
2247 case ImageIOBase::USHORT:
2248 RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2250 case ImageIOBase::SHORT:
2251 RescaleFunction( (short *)buffer, source, slope, intercept, size);
2253 case ImageIOBase::UINT:
2254 RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2256 case ImageIOBase::INT:
2257 RescaleFunction( (int *)buffer, source, slope, intercept, size);
2259 case ImageIOBase::FLOAT:
2260 RescaleFunction( (float *)buffer, source, slope, intercept, size);
2262 case ImageIOBase::DOUBLE:
2263 RescaleFunction( (double *)buffer, source, slope, intercept, size);
2266 ::itk::OStringStream message;
2267 message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2268 ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);