1 /*=========================================================================
4 Module: $RCSfile: gdcmFileHelper.cxx,v $
7 Date: $Date: 2007/08/27 16:14:47 $
8 Version: $Revision: 1.122 $
10 Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
11 l'Image). All rights reserved. See Doc/License.txt or
12 http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
14 This software is distributed WITHOUT ANY WARRANTY; without even
15 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 PURPOSE. See the above copyright notices for more information.
18 =========================================================================*/
20 #include "gdcmFileHelper.h"
21 #include "gdcmGlobal.h"
23 #include "gdcmDocument.h"
24 #include "gdcmDebug.h"
26 #include "gdcmSeqEntry.h"
27 #include "gdcmSQItem.h"
28 #include "gdcmDataEntry.h"
29 #include "gdcmDocEntry.h"
31 #include "gdcmPixelReadConvert.h"
32 #include "gdcmPixelWriteConvert.h"
33 #include "gdcmDocEntryArchive.h"
34 #include "gdcmDictSet.h"
35 #include "gdcmOrientation.h"
37 #if defined(__BORLANDC__)
38 #include <mem.h> // for memset
44 // ----------------------------- WARNING -------------------------
46 These lines will be moved to the document-to-be 'User's Guide'
48 // To read an image, user needs a gdcm::File
49 gdcm::File *f = new gdcm::File(fileName);
51 // user may also decide he doesn't want to load some parts of the header
52 gdcm::File *f = new gdcm::File();
53 f->SetFileName(fileName);
54 f->SetLoadMode(LD_NOSEQ); // or
55 f->SetLoadMode(LD_NOSHADOW); // or
56 f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW); // or
57 f->SetLoadMode(LD_NOSHADOWSEQ);
60 // To decide whether it's an 'image of interest for him, or not,
61 // user can now check some values
62 std::string v = f->GetEntryValue(groupNb,ElementNb);
64 // to get the pixels, user needs a gdcm::FileHelper
65 gdcm::FileHelper *fh = new gdcm::FileHelper(f);
67 // user may ask not to convert Palette (if any) to RGB
68 uint8_t *pixels = fh->GetImageDataRaw();
69 int imageLength = fh->GetImageDataRawSize();
71 // He can now use the pixels, create a new image, ...
72 uint8_t *userPixels = ...
74 //To re-write the image, user re-uses the gdcm::FileHelper
75 gdcm::File *fh = new gdcm::FileHelper();
77 fh->SetTypeToRaw(); // Even if it was possible to convert Palette to RGB
80 // If user wants to write the file as MONOCHROME1 (0=white)
81 fh->SetPhotometricInterpretationToMonochrome1();
83 fh->SetWriteTypeToDcmExpl(); // he wants Explicit Value Representation
84 // Little Endian is the default,
85 // bigendian not supported for writting
86 (-->SetWriteType(ExplicitVR);)
87 -->WriteType = ExplicitVR;
88 fh->SetWriteTypeToJPEG(); // lossless compression
89 fh->SetWriteTypeToJPEG2000(); // lossless compression
91 fh->SetImageData( userPixels, userPixelsLength);
93 fh->SetUserData( userPixels, userPixelsLength); // this one performs compression, when required
95 fh->Write(newFileName); // overwrites the file, if any
101 These lines will be moved to the document-to-be 'Developer's Guide'
103 WriteMode : WMODE_RAW / WMODE_RGB
104 WriteType : ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO
105 PhotometricInterpretation : MONOCHROME2 (0=black), MONOCHROME2 (0=white)
107 fh->SetImageData( userPixels, userPixelsLength);
109 fh->SetUserData( userPixels, userPixelsLength);
110 PixelWriteConverter->SetUserData(inData, expectedSize);
113 fh->SetWriteMode(WMODE_RAW / WMODE_RGB)
115 fh->SetWriteType( ImplicitVR/ExplicitVR/ACR/ACR_LIBIDO/JPEG/JPEG2000)
117 fh->Write(newFileName);
118 CheckMandatoryElements(); // Checks existing ones / Add missing ones
119 Fix VR if unknown elements
120 SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR(); /
121 SetWriteFileTypeToACR() / SetWriteFileTypeToJPEG() / SetWriteFileTypeToJ2K()
122 (Modifies TransferSyntax if any; Pushes to the Archives old one)
123 SetWriteToRaw(); / SetWriteToRGB();
124 (Modifies and pushes to the Archive, when necessary : photochr. interp.,
125 samples per pixel, Planar configuration,
126 bits allocated, bits stored, high bit -ACR 24 bits-
127 Pixels element VR, pushes out the LUT )
129 Sets Photometric Interpretation
130 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
131 Sets VR, BinArea, Length for PixelData
134 Archive->Push(photInt);
135 Archive->Push(pixel);
139 if NumberOfScalarComponents==1
140 SetWriteToRaw(); return;
141 PixelReadConverter->BuildRGBImage()
142 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
143 Archives spp, planConfig,photInt, pixel
145 CheckWriteIntegrity();
146 (checks user given pixels length)
147 FileInternal->Write(fileName,WriteType)
148 fp = opens file(fileName); // out|binary
149 ComputeGroup0002Length( );
150 Document::WriteContent(fp, writetype);
151 writes Dicom File Preamble not ACR-NEMA
152 ElementSet::WriteContent(fp, writetype);
153 writes recursively all DataElements
155 (moves back to the gdcm::File all the archived elements)
161 namespace GDCM_NAME_SPACE
163 typedef std::map<uint16_t, int> GroupHT; // Hash Table
164 //-------------------------------------------------------------------------
165 // Constructor / Destructor
167 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
168 * file (gdcm::File only deals with the ... header)
169 * Opens (in read only and when possible) an existing file and checks
170 * for DICOM compliance. Returns NULL on failure.
171 * It will be up to the user to load the pixels into memory
172 * ( GetImageDataSize() + GetImageData() methods)
173 * \note the in-memory representation of all available tags found in
174 * the DICOM header is post-poned to first header information access.
175 * This avoid a double parsing of public part of the header when
176 * one sets an a posteriori shadow dictionary (efficiency can be
177 * seen as a side effect).
179 FileHelper::FileHelper( )
181 FileInternal = File::New( );
186 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
187 * file (File only deals with the ... header)
188 * Opens (in read only and when possible) an existing file and checks
189 * for DICOM compliance. Returns NULL on failure.
190 * It will be up to the user to load the pixels into memory
191 * ( GetImageDataSize() + GetImageData() methods)
192 * \note the in-memory representation of all available tags found in
193 * the DICOM header is post-poned to first header information access.
194 * This avoid a double parsing of public part of the header when
195 * user sets an a posteriori shadow dictionary (efficiency can be
196 * seen as a side effect).
197 * @param header already built Header
199 FileHelper::FileHelper(File *header)
201 gdcmAssertMacro(header);
203 FileInternal = header;
204 FileInternal->Register();
206 if ( FileInternal->IsReadable() )
208 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
213 * \brief canonical destructor
214 * \note If the header (gdcm::File) was created by the FileHelper constructor,
215 * it is destroyed by the FileHelper
217 FileHelper::~FileHelper()
219 if ( PixelReadConverter )
221 delete PixelReadConverter;
223 if ( PixelWriteConverter )
225 delete PixelWriteConverter;
232 FileInternal->Unregister();
235 //-----------------------------------------------------------------------------
239 * \brief Sets the LoadMode of the internal gdcm::File as a boolean string.
240 * NO_SEQ, NO_SHADOW, NO_SHADOWSEQ ... (nothing more, right now)
241 * WARNING : before using NO_SHADOW, be sure *all* your files
242 * contain accurate values in the 0x0000 element (if any)
243 * of *each* Shadow Group. The parser will fail if the size is wrong !
244 * @param loadMode Load mode to be used
246 void FileHelper::SetLoadMode(int loadMode)
248 GetFile()->SetLoadMode( loadMode );
251 * \brief Sets the LoadMode of the internal gdcm::File
252 * @param fileName name of the file to be open
254 void FileHelper::SetFileName(std::string const &fileName)
256 FileInternal->SetFileName( fileName );
261 * @return false if file cannot be open or no swap info was found,
262 * or no tag was found.
264 bool FileHelper::Load()
266 if ( !FileInternal->Load() )
269 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
274 * \brief Accesses an existing DataEntry through its (group, element)
275 * and modifies its content with the given value.
276 * @param content new value (string) to substitute with
277 * @param group group number of the Dicom Element to modify
278 * @param elem element number of the Dicom Element to modify
279 * \return false if DataEntry not found
281 bool FileHelper::SetEntryString(std::string const &content,
282 uint16_t group, uint16_t elem)
284 return FileInternal->SetEntryString(content, group, elem);
289 * \brief Accesses an existing DataEntry through its (group, element)
290 * and modifies its content with the given value.
291 * @param content new value (void* -> uint8_t*) to substitute with
292 * @param lgth new value length
293 * @param group group number of the Dicom Element to modify
294 * @param elem element number of the Dicom Element to modify
295 * \return false if DataEntry not found
297 bool FileHelper::SetEntryBinArea(uint8_t *content, int lgth,
298 uint16_t group, uint16_t elem)
300 return FileInternal->SetEntryBinArea(content, lgth, group, elem);
304 * \brief Modifies the value of a given DataEntry when it exists.
305 * Creates it with the given value when unexistant.
306 * @param content (string) value to be set
307 * @param group Group number of the Entry
308 * @param elem Element number of the Entry
309 * @param vr Value Representation of the DataElement to be inserted
310 * \return pointer to the modified/created DataEntry (NULL when creation
313 DataEntry *FileHelper::InsertEntryString(std::string const &content,
314 uint16_t group, uint16_t elem,
317 return FileInternal->InsertEntryString(content, group, elem, vr);
321 * \brief Modifies the value of a given DataEntry when it exists.
322 * Creates it with the given value when unexistant.
323 * A copy of the binArea is made to be kept in the Document.
324 * @param binArea (binary) value to be set
325 * @param lgth new value length
326 * @param group Group number of the Entry
327 * @param elem Element number of the Entry
328 * @param vr Value Representation of the DataElement to be inserted
329 * \return pointer to the modified/created DataEntry (NULL when creation
332 DataEntry *FileHelper::InsertEntryBinArea(uint8_t *binArea, int lgth,
333 uint16_t group, uint16_t elem,
336 return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem, vr);
340 * \brief Adds an empty SeqEntry
341 * (remove any existing entry with same group,elem)
342 * @param group Group number of the Entry
343 * @param elem Element number of the Entry
344 * \return pointer to the created SeqEntry (NULL when creation
347 SeqEntry *FileHelper::InsertSeqEntry(uint16_t group, uint16_t elem)
349 return FileInternal->InsertSeqEntry(group, elem);
353 * \brief Get the size of the image data
354 * If the image can be RGB (with a lut or by default), the size
355 * corresponds to the RGB image
356 * (use GetImageDataRawSize if you want to be sure to get *only*
357 * the size of the pixels)
358 * @return The image size
360 size_t FileHelper::GetImageDataSize()
362 if ( PixelWriteConverter->GetUserData() )
364 return PixelWriteConverter->GetUserDataSize();
366 return PixelReadConverter->GetRGBSize();
370 * \brief Get the size of the image data.
371 * If the image could be converted to RGB using a LUT,
372 * this transformation is not taken into account by GetImageDataRawSize
373 * (use GetImageDataSize if you wish)
374 * @return The raw image size
376 size_t FileHelper::GetImageDataRawSize()
378 if ( PixelWriteConverter->GetUserData() )
380 return PixelWriteConverter->GetUserDataSize();
382 return PixelReadConverter->GetRawSize();
386 * \brief brings pixels into memory :
387 * - Allocates necessary memory,
388 * - Reads the pixels from disk (uncompress if necessary),
389 * - Transforms YBR pixels, if any, into RGB pixels,
390 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
391 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
392 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
393 * @return Pointer to newly allocated pixel data.
394 * (uint8_t is just for prototyping. feel free to cast)
395 * NULL if alloc fails
397 uint8_t *FileHelper::GetImageData()
399 if ( PixelWriteConverter->GetUserData() )
401 return PixelWriteConverter->GetUserData();
406 // If the decompression failed nothing can be done.
410 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
412 return PixelReadConverter->GetRGB();
416 // When no LUT or LUT conversion fails, return the Raw
417 return PixelReadConverter->GetRaw();
422 * \brief brings pixels into memory :
423 * - Allocates necessary memory,
424 * - Transforms YBR pixels (if any) into RGB pixels
425 * - Transforms 3 planes R, G, B (if any) into a single RGB Plane
426 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
427 * - DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
428 * @return Pointer to newly allocated pixel data.
429 * (uint8_t is just for prototyping. feel free to cast)
430 * NULL if alloc fails
432 uint8_t *FileHelper::GetImageDataRaw ()
437 //#ifndef GDCM_LEGACY_REMOVE
439 * \brief Useless function, since PixelReadConverter forces us
440 * copy the Pixels anyway.
441 * Reads the pixels from disk (uncompress if necessary),
442 * Transforms YBR pixels, if any, into RGB pixels
443 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
444 * Transforms single Grey plane + 3 Palettes into a RGB Plane
445 * Copies at most MaxSize bytes of pixel data to caller allocated
447 * \warning This function allows people that want to build a volume
448 * from an image stack *not to* have, first to get the image pixels,
449 * and then move them to the volume area.
450 * It's absolutely useless for any VTK user since vtk chooses
451 * to invert the lines of an image, that is the last line comes first
452 * (for some axis related reasons?). Hence he will have
453 * to load the image line by line, starting from the end.
454 * VTK users have to call GetImageData
456 * @param destination Address (in caller's memory space) at which the
457 * pixel data should be copied
458 * @param maxSize Maximum number of bytes to be copied. When MaxSize
459 * is not sufficient to hold the pixel data the copy is not
460 * executed (i.e. no partial copy).
461 * @return On success, the number of bytes actually copied. Zero on
462 * failure e.g. MaxSize is lower than necessary.
465 size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
469 // If the decompression failed nothing can be done.
473 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
475 if ( PixelReadConverter->GetRGBSize() > maxSize )
477 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
481 (void*)PixelReadConverter->GetRGB(),
482 PixelReadConverter->GetRGBSize() );
483 return PixelReadConverter->GetRGBSize();
486 // Either no LUT conversion necessary or LUT conversion failed
487 if ( PixelReadConverter->GetRawSize() > maxSize )
489 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
493 (void *)PixelReadConverter->GetRaw(),
494 PixelReadConverter->GetRawSize() );
495 return PixelReadConverter->GetRawSize();
501 * \brief Points the internal pointer to the callers inData
502 * image representation, BUT WITHOUT COPYING THE DATA.
503 * 'image' Pixels are presented as C-like 2D arrays : line per line.
504 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
505 * \warning Since the pixels are not copied, it is the caller's responsability
506 * not to deallocate its data before gdcm uses them (e.g. with
507 * the Write() method )
508 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
509 * user is allowed to pass any kind of pixels since the size is
511 * @param expectedSize total image size, *in Bytes*
513 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
515 PixelWriteConverter->SetUserData(inData, expectedSize);
516 /// \todo : shouldn't we call SetCompressJPEGUserData/SetCompressJPEG2000UserData
521 * \brief Set the image data defined by the user
522 * \warning When writting the file, this data are get as default data to write
523 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
524 * user is allowed to pass any kind of pixels since the size is
526 * @param expectedSize total image size, *in Bytes*
528 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
530 // Shouldn't we move theese lines to FileHelper::Write()?
532 if( WriteType == JPEG2000 )
534 PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
536 else if( WriteType == JPEG )
538 PixelWriteConverter->SetCompressJPEGUserData(inData, expectedSize, FileInternal);
542 PixelWriteConverter->SetUserData(inData, expectedSize);
546 PixelWriteConverter->SetUserData(inData, expectedSize);
550 * \brief Get the image data defined by the user
551 * \warning When writting the file, this data are get as default data to write
553 uint8_t *FileHelper::GetUserData()
555 return PixelWriteConverter->GetUserData();
559 * \brief Get the image data size defined by the user
560 * \warning When writting the file, this data are get as default data to write
562 size_t FileHelper::GetUserDataSize()
564 return PixelWriteConverter->GetUserDataSize();
568 * \brief Get the image data from the file.
569 * If a LUT is found, the data are expanded to be RGB
571 uint8_t *FileHelper::GetRGBData()
573 return PixelReadConverter->GetRGB();
577 * \brief Get the image data size from the file.
578 * If a LUT is found, the data are expanded to be RGB
580 size_t FileHelper::GetRGBDataSize()
582 return PixelReadConverter->GetRGBSize();
586 * \brief Get the image data from the file.
587 * Even when a LUT is found, the data are not expanded to RGB!
589 uint8_t *FileHelper::GetRawData()
591 return PixelReadConverter->GetRaw();
595 * \brief Get the image data size from the file.
596 * Even when a LUT is found, the data are not expanded to RGB!
598 size_t FileHelper::GetRawDataSize()
600 return PixelReadConverter->GetRawSize();
604 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT
606 uint8_t* FileHelper::GetLutRGBA()
608 if ( PixelReadConverter->GetLutRGBA() ==0 )
609 PixelReadConverter->BuildLUTRGBA();
610 return PixelReadConverter->GetLutRGBA();
614 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Number
616 int FileHelper::GetLutItemNumber()
618 return PixelReadConverter->GetLutItemNumber();
622 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Size
624 int FileHelper::GetLutItemSize()
626 return PixelReadConverter->GetLutItemSize();
630 * \brief Writes on disk A SINGLE Dicom file
631 * NO test is performed on processor "Endiannity".
632 * It's up to the user to call his Reader properly
633 * @param fileName name of the file to be created
634 * (any already existing file is over written)
635 * @return false if write fails
637 bool FileHelper::WriteRawData(std::string const &fileName)
639 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
642 gdcmWarningMacro( "Fail to open (write) file:" << fileName.c_str());
646 if ( PixelWriteConverter->GetUserData() )
648 fp1.write( (char *)PixelWriteConverter->GetUserData(),
649 PixelWriteConverter->GetUserDataSize() );
651 else if ( PixelReadConverter->GetRGB() )
653 fp1.write( (char *)PixelReadConverter->GetRGB(),
654 PixelReadConverter->GetRGBSize());
656 else if ( PixelReadConverter->GetRaw() )
658 fp1.write( (char *)PixelReadConverter->GetRaw(),
659 PixelReadConverter->GetRawSize());
663 gdcmErrorMacro( "Nothing written." );
672 * \brief Writes on disk A SINGLE Dicom file,
673 * using the Implicit Value Representation convention
674 * NO test is performed on processor "Endianity".
675 * @param fileName name of the file to be created
676 * (any already existing file is overwritten)
677 * @return false if write fails
680 bool FileHelper::WriteDcmImplVR (std::string const &fileName)
682 SetWriteTypeToDcmImplVR();
683 return Write(fileName);
687 * \brief Writes on disk A SINGLE Dicom file,
688 * using the Explicit Value Representation convention
689 * NO test is performed on processor "Endiannity".
690 * @param fileName name of the file to be created
691 * (any already existing file is overwritten)
692 * @return false if write fails
695 bool FileHelper::WriteDcmExplVR (std::string const &fileName)
697 SetWriteTypeToDcmExplVR();
698 return Write(fileName);
702 * \brief Writes on disk A SINGLE Dicom file,
703 * using the ACR-NEMA convention
704 * NO test is performed on processor "Endiannity".
705 * (a l'attention des logiciels cliniques
706 * qui ne prennent en entrée QUE des images ACR ...
707 * \warning if a DICOM_V3 header is supplied,
708 * groups < 0x0008 and shadow groups are ignored
709 * \warning NO TEST is performed on processor "Endiannity".
710 * @param fileName name of the file to be created
711 * (any already existing file is overwritten)
712 * @return false if write fails
715 bool FileHelper::WriteAcr (std::string const &fileName)
718 return Write(fileName);
722 * \brief Writes on disk A SINGLE Dicom file,
723 * @param fileName name of the file to be created
724 * (any already existing file is overwritten)
725 * @return false if write fails
727 bool FileHelper::Write(std::string const &fileName)
729 CheckMandatoryElements(); //called once, here !
736 SetWriteFileTypeToImplicitVR();
739 case Unknown: // should never happen; ExplicitVR is the default value
742 // We let DocEntry::WriteContent to put vr=UN for undocumented Shadow Groups !
743 SetWriteFileTypeToExplicitVR();
748 // NOTHING is done here just for LibIDO.
749 // Just to avoid further trouble if user creates a file ex-nihilo,
750 // wants to write it as an ACR-NEMA file,
751 // and forgets to create any Entry belonging to group 0008
753 // We add Recognition Code (RET)
754 if ( ! FileInternal->GetDataEntry(0x0008, 0x0010) )
755 FileInternal->InsertEntryString("ACR-NEMA V1.0 ",
756 0x0008, 0x0010, "LO");
757 SetWriteFileTypeToACR();
758 // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
761 /// \todo FIXME : JPEG/JPEG2000 may be either ExplicitVR or ImplicitVR
763 SetWriteFileTypeToJPEG();
765 //PixelWriteConverter->SetCompressJPEGUserData(
766 // inData, expectedSize, FileInternal);
767 PixelWriteConverter->SetCompressJPEGUserData(
768 PixelWriteConverter->GetUserData(),
769 PixelWriteConverter->GetUserDataSize(),FileInternal);
773 /// \TODO Maybe we should consider doing the compression here !
774 // PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
776 SetWriteFileTypeToJPEG2000();
777 PixelWriteConverter->SetCompressJPEG2000UserData(
778 PixelWriteConverter->GetUserData(),
779 PixelWriteConverter->GetUserDataSize(),
785 // --------------------------------------------------------------
786 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
788 // if recognition code tells us we dealt with a LibIDO image
789 // we reproduce on disk the switch between lineNumber and columnNumber
790 // just before writting ...
791 /// \todo the best trick would be *change* the recognition code
792 /// but pb expected if user deals with, e.g. COMPLEX images
794 if ( WriteType == ACR_LIBIDO )
800 SetWriteToNoLibido();
802 // ----------------- End of Special Patch ----------------
807 SetWriteToRaw(); // modifies and pushes to the archive, when necessary
810 SetWriteToRGB(); // modifies and pushes to the archive, when necessary
815 if (WriteType == JPEG || WriteType == JPEG2000)
818 check = CheckWriteIntegrity(); // verifies length
822 check = FileInternal->Write(fileName,WriteType);
826 // RestoreWriteFileType();
827 // RestoreWriteMandatory();
829 // --------------------------------------------------------------
830 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
832 // ...and we restore the header to be Dicom Compliant again
833 // just after writting
834 RestoreWriteOfLibido();
835 // ----------------- End of Special Patch ----------------
840 //-----------------------------------------------------------------------------
843 * \brief Verifies the size of the user given PixelData
844 * @return true if check is successfull
846 bool FileHelper::CheckWriteIntegrity()
848 if ( PixelWriteConverter->GetUserData() )
850 int numberBitsAllocated = FileInternal->GetBitsAllocated();
851 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
853 gdcmWarningMacro( "numberBitsAllocated changed from "
854 << numberBitsAllocated << " to 16 "
855 << " for consistency purpose" );
856 numberBitsAllocated = 16;
859 size_t decSize = FileInternal->GetXSize()
860 * FileInternal->GetYSize()
861 * FileInternal->GetZSize()
862 * FileInternal->GetTSize()
863 * FileInternal->GetSamplesPerPixel()
864 * ( numberBitsAllocated / 8 );
865 size_t rgbSize = decSize;
866 if ( FileInternal->HasLUT() )
867 rgbSize = decSize * 3;
872 if ( decSize!=PixelWriteConverter->GetUserDataSize() )
874 gdcmWarningMacro( "Data size (Raw) is incorrect. Should be "
875 << decSize << " / Found :"
876 << PixelWriteConverter->GetUserDataSize() );
881 if ( rgbSize!=PixelWriteConverter->GetUserDataSize() )
883 gdcmWarningMacro( "Data size (RGB) is incorrect. Should be "
884 << decSize << " / Found "
885 << PixelWriteConverter->GetUserDataSize() );
895 * \brief Updates the File to write RAW data (as opposed to RGB data)
896 * (modifies, when necessary, photochromatic interpretation,
897 * bits allocated, Pixels element VR)
898 * WARNING : if SetPhotometricInterpretationToMonochrome1() was called
899 * before Pixel Elements is modified :-(
901 void FileHelper::SetWriteToRaw()
903 if ( FileInternal->GetNumberOfScalarComponents() == 3
904 && !FileInternal->HasLUT() )
910 // 0x0028,0x0004 : Photometric Interpretation
911 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
912 if (FileInternal->HasLUT() )
914 photInt->SetString("PALETTE COLOR ");
918 if (GetPhotometricInterpretation() == 2)
919 photInt->SetString("MONOCHROME2 "); // 0 = Black
921 photInt->SetString("MONOCHROME1 "); // 0 = White !
924 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
925 PixelReadConverter->GetRawSize());
927 std::string vr = "OB";
928 if ( FileInternal->GetBitsAllocated()>8 )
930 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
932 // For non RAW data. Mainly JPEG/JPEG2000
933 if( WriteType == JPEG || WriteType == JPEG2000)
939 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
940 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
941 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
943 static_cast< uint32_t >(PixelWriteConverter->GetDataSize()) );
945 if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
947 ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );
950 Archive->Push(photInt);
951 Archive->Push(pixel);
959 * \brief Updates the File to write RGB data (as opposed to RAW data)
960 * (modifies, when necessary, photochromatic interpretation,
961 * samples per pixel, Planar configuration,
962 * bits allocated, bits stored, high bit -ACR 24 bits-
963 * Pixels element VR, pushes out the LUT, )
965 void FileHelper::SetWriteToRGB()
967 if ( FileInternal->GetNumberOfScalarComponents()==3 )
969 PixelReadConverter->BuildRGBImage();
971 DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
972 spp->SetString("3 "); // Don't drop trailing space
974 DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
975 planConfig->SetString("0 "); // Don't drop trailing space
977 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
978 photInt->SetString("RGB "); // Don't drop trailing space
980 if ( PixelReadConverter->GetRGB() )
982 PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
983 PixelReadConverter->GetRGBSize());
987 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
988 PixelReadConverter->GetRawSize());
991 std::string vr = "OB";
992 if ( FileInternal->GetBitsAllocated()>8 )
994 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
997 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
998 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
999 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
1000 pixel->SetLength(PixelWriteConverter->GetDataSize());
1003 Archive->Push(planConfig);
1004 Archive->Push(photInt);
1005 Archive->Push(pixel);
1008 planConfig->Delete();
1013 Archive->Push(0x0028,0x1101);
1014 Archive->Push(0x0028,0x1102);
1015 Archive->Push(0x0028,0x1103);
1016 Archive->Push(0x0028,0x1201);
1017 Archive->Push(0x0028,0x1202);
1018 Archive->Push(0x0028,0x1203);
1020 // push out Palette Color Lookup Table UID, if any
1021 Archive->Push(0x0028,0x1199);
1023 // For old '24 Bits' ACR-NEMA
1024 // Thus, we have a RGB image and the bits allocated = 24 and
1025 // samples per pixels = 1 (in the read file)
1026 if ( FileInternal->GetBitsAllocated()==24 )
1028 DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
1029 bitsAlloc->SetString("8 ");
1031 DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
1032 bitsStored->SetString("8 ");
1034 DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
1035 highBit->SetString("7 ");
1037 Archive->Push(bitsAlloc);
1038 Archive->Push(bitsStored);
1039 Archive->Push(highBit);
1041 bitsAlloc->Delete();
1042 bitsStored->Delete();
1053 * \brief Restore the File write mode
1055 void FileHelper::RestoreWrite()
1057 Archive->Restore(0x0028,0x0002);
1058 Archive->Restore(0x0028,0x0004);
1060 Archive->Restore(0x0028,0x0006);
1061 Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1063 // For old ACR-NEMA (24 bits problem)
1064 Archive->Restore(0x0028,0x0100);
1065 Archive->Restore(0x0028,0x0101);
1066 Archive->Restore(0x0028,0x0102);
1069 Archive->Restore(0x0028,0x1101);
1070 Archive->Restore(0x0028,0x1102);
1071 Archive->Restore(0x0028,0x1103);
1072 Archive->Restore(0x0028,0x1201);
1073 Archive->Restore(0x0028,0x1202);
1074 Archive->Restore(0x0028,0x1203);
1076 // For the Palette Color Lookup Table UID
1077 Archive->Restore(0x0028,0x1203);
1079 // group 0002 may be pushed out for ACR-NEMA writting purposes
1080 Archive->Restore(0x0002,0x0000);
1081 Archive->Restore(0x0002,0x0001);
1082 Archive->Restore(0x0002,0x0002);
1083 Archive->Restore(0x0002,0x0003);
1084 Archive->Restore(0x0002,0x0010);
1085 Archive->Restore(0x0002,0x0012);
1086 Archive->Restore(0x0002,0x0013);
1087 Archive->Restore(0x0002,0x0016);
1088 Archive->Restore(0x0002,0x0100);
1089 Archive->Restore(0x0002,0x0102);
1094 * \brief Pushes out the whole group 0002
1095 * FIXME : better, set a flag to tell the writer not to write it ...
1096 * FIXME : method should probably have an other name !
1097 * SetWriteFileTypeToACR is NOT opposed to
1098 * SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1100 void FileHelper::SetWriteFileTypeToACR()
1102 Archive->Push(0x0002,0x0000);
1103 Archive->Push(0x0002,0x0001);
1104 Archive->Push(0x0002,0x0002);
1105 Archive->Push(0x0002,0x0003);
1106 Archive->Push(0x0002,0x0010);
1107 Archive->Push(0x0002,0x0012);
1108 Archive->Push(0x0002,0x0013);
1109 Archive->Push(0x0002,0x0016);
1110 Archive->Push(0x0002,0x0100);
1111 Archive->Push(0x0002,0x0102);
1115 * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1117 void FileHelper::SetWriteFileTypeToJPEG2000()
1119 std::string ts = Util::DicomString(
1120 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1122 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1130 * \brief Sets in the File the TransferSyntax to 'JPEG'
1132 void FileHelper::SetWriteFileTypeToJPEG()
1134 std::string ts = Util::DicomString(
1135 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGLosslessProcess14_1) );
1137 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1145 * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"
1147 void FileHelper::SetWriteFileTypeToExplicitVR()
1149 std::string ts = Util::DicomString(
1150 Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1152 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1159 * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"
1161 void FileHelper::SetWriteFileTypeToImplicitVR()
1163 std::string ts = Util::DicomString(
1164 Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1166 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1173 * \brief Set the Write not to Libido format
1175 void FileHelper::SetWriteToLibido()
1177 DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1178 DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1180 if ( oldRow && oldCol )
1182 std::string rows, columns;
1184 DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1185 DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1187 newRow->Copy(oldCol);
1188 newCol->Copy(oldRow);
1190 newRow->SetString(oldCol->GetString());
1191 newCol->SetString(oldRow->GetString());
1193 Archive->Push(newRow);
1194 Archive->Push(newCol);
1200 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1201 libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1202 Archive->Push(libidoCode);
1203 libidoCode->Delete();
1207 * \brief Set the Write not to No Libido format
1209 void FileHelper::SetWriteToNoLibido()
1211 DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1214 if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1216 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1217 libidoCode->SetString("");
1218 Archive->Push(libidoCode);
1219 libidoCode->Delete();
1225 * \brief Restore the Write format
1227 void FileHelper::RestoreWriteOfLibido()
1229 Archive->Restore(0x0028,0x0010);
1230 Archive->Restore(0x0028,0x0011);
1231 Archive->Restore(0x0008,0x0010);
1233 // Restore 'LibIDO-special' entries, if any
1234 Archive->Restore(0x0028,0x0015);
1235 Archive->Restore(0x0028,0x0016);
1236 Archive->Restore(0x0028,0x0017);
1237 Archive->Restore(0x0028,0x00199);
1241 * \brief Duplicates a DataEntry or creates it.
1242 * @param group Group number of the Entry
1243 * @param elem Element number of the Entry
1244 * @param vr Value Representation of the Entry
1245 * \return pointer to the new Bin Entry (NULL when creation failed).
1247 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1250 DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1253 if ( oldE && vr != GDCM_VRUNKNOWN )
1254 if ( oldE->GetVR() != vr )
1259 newE = DataEntry::New(group, elem, vr);
1264 newE = GetFile()->NewDataEntry(group, elem, vr);
1271 * \brief This method is called automatically, just before writting
1272 * in order to produce a 'True Dicom V3' image.
1274 * We cannot know *how* the user made the File :
1275 * (reading an old ACR-NEMA file or a not very clean DICOM file ...)
1276 * Just before writting :
1277 * - we check the Entries
1278 * - we create the mandatory entries if they are missing
1279 * - we modify the values if necessary
1280 * - we push the sensitive entries to the Archive
1281 * The writing process will restore the entries as they where before
1282 * entering FileHelper::CheckMandatoryElements, so the user will always
1283 * see the entries just as they were before he decided to write.
1286 * - Entries whose type is 1 are mandatory, with a mandatory value
1287 * - Entries whose type is 1c are mandatory-inside-a-Sequence,
1288 * with a mandatory value
1289 * - Entries whose type is 2 are mandatory, with an optional value
1290 * - Entries whose type is 2c are mandatory-inside-a-Sequence,
1291 * with an optional value
1292 * - Entries whose type is 3 are optional
1295 * - warn the user if we had to add some entries :
1296 * even if a mandatory entry is missing, we add it, with a default value
1297 * (we don't want to give up the writting process if user forgot to
1298 * specify Lena's Patient ID, for instance ...)
1299 * - read the whole PS 3.3 Part of DICOM (890 pages)
1300 * and write a *full* checker (probably one method per Modality ...)
1301 * Any contribution is welcome.
1302 * - write a user callable full checker, to allow post reading
1303 * and/or pre writting image consistency check.
1306 /* -------------------------------------------------------------------------------------
1307 To be moved to User's guide / WIKI ?
1309 We have to deal with 4 *very* different cases :
1310 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1312 -2) user modified the pixels of an existing image.
1314 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1316 -4) user modified/added some tags *without processing* the pixels (anonymization...)
1317 UNMODIFIED_PIXELS_IMAGE
1318 -Probabely some more to be added.
1320 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1323 0008 0012 Instance Creation Date
1324 0008 0013 Instance Creation Time
1325 0008 0018 SOP Instance UID
1326 are *always* created with the current values; user has *no* possible intervention on
1329 'Serie Instance UID'(0x0020,0x000e)
1330 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1331 created if it doesn't.
1332 The user is allowed to create his own Series/Studies,
1333 keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1335 The user shouldn't add any image to a 'Manufacturer Serie'
1336 but there is no way no to allow him to do that
1338 None of the 'shadow elements' are droped out.
1342 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1345 'Media Storage SOP Class UID' (0x0002,0x0002)
1346 'SOP Class UID' (0x0008,0x0016) are set to
1347 [Secondary Capture Image Storage]
1348 'Image Type' (0x0008,0x0008) is forced to "DERIVED\PRIMARY"
1349 Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1352 If 'SOP Class UID' exists in the native image ('true DICOM' image)
1353 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1354 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1355 whose value is the original 'SOP Class UID'
1356 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1357 whose value is the original 'SOP Class UID'
1359 3) TODO : find a trick to allow user to pass to the writter the list of the Dicom images
1360 or the Series, (or the Study ?) he used to created his image
1361 (MIP, MPR, cartography image, ...)
1362 These info should be stored (?)
1363 0008 1110 SQ 1 Referenced Study Sequence
1364 0008 1115 SQ 1 Referenced Series Sequence
1365 0008 1140 SQ 1 Referenced Image Sequence
1367 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1368 'Media Storage SOP Class UID' (0x0002,0x0002)
1369 'SOP Class UID' (0x0008,0x0016)
1370 'Image Type' (0x0008,0x0008)
1371 'Conversion Type' (0x0008,0x0064)
1374 Bellow follows the full description (hope so !) of the consistency checks performed
1375 by gdcm::FileHelper::CheckMandatoryElements()
1378 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1379 -->'SOP Class UID' (0x0008,0x0016) are defaulted to
1380 [Secondary Capture Image Storage]
1381 --> 'Image Type' (0x0008,0x0008)
1382 is forced to "DERIVED\PRIMARY"
1383 (The written image is no longer an 'ORIGINAL' one)
1384 Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1386 --> Conversion Type (0x0008,0x0064)
1387 is defaulted to 'SYN' (Synthetic Image)
1388 when *he* knows he created his own image ex nihilo
1390 --> 'Modality' (0x0008,0x0060)
1391 is defaulted to "OT" (other) if missing.
1392 (a fully user created image belongs to *no* modality)
1394 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1395 --> 'Implementation Class UID' (0x0002,0x0012)
1396 are automatically generated; no user intervention possible
1398 --> 'Serie Instance UID'(0x0020,0x000e)
1399 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1400 created if it doesn't.
1401 The user is allowed to create his own Series/Studies,
1402 keeping the same 'Serie Instance UID' / 'Study Instance UID'
1405 The user shouldn't add any image to a 'Manufacturer Serie'
1406 but there is no way no to allowed him to do that
1408 --> If 'SOP Class UID' exists in the native image ('true DICOM' image)
1409 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1411 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1412 whose value is the original 'SOP Class UID'
1413 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1414 whose value is the original 'SOP Class UID'
1416 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1417 --> Pixel Spacing (0x0028,0x0030) is defaulted to "1.0\1.0"
1418 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1420 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1422 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1424 --> Study Date, Study Time are defaulted to current Date and Time
1425 (they remain unchanged if they exist)
1427 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from
1428 Image Orientation (Patient) : (0020|0037) or from
1429 Image Orientation (RET) : (0020 0035)
1431 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1432 are created, with empty value if there are missing.
1434 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1435 are defaulted with a 'gdcm' value.
1437 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1438 --> Referring Physician's Name (Type 2)
1439 are created, with empty value if there are missing.
1441 -------------------------------------------------------------------------------------*/
1443 void FileHelper::CheckMandatoryElements()
1445 std::string sop = Util::CreateUniqueUID();
1447 // --------------------- For Meta Elements ---------------------
1448 // just to remember : 'official' 0002 group
1449 if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1451 // Group 000002 (Meta Elements) already pushed out
1453 //0002 0000 UL 1 Meta Group Length
1454 //0002 0001 OB 1 File Meta Information Version
1455 //0002 0002 UI 1 Media Storage SOP Class UID
1456 //0002 0003 UI 1 Media Storage SOP Instance UID
1457 //0002 0010 UI 1 Transfer Syntax UID
1458 //0002 0012 UI 1 Implementation Class UID
1459 //0002 0013 SH 1 Implementation Version Name
1460 //0002 0016 AE 1 Source Application Entity Title
1461 //0002 0100 UI 1 Private Information Creator
1462 //0002 0102 OB 1 Private Information
1464 // Push out 'ACR-NEMA-special' entries, if any
1465 Archive->Push(0x0008,0x0001); // Length to End
1466 Archive->Push(0x0008,0x0010); // Recognition Code
1467 Archive->Push(0x0028,0x0005); // Image Dimension
1469 // Create them if not found
1470 // Always modify the value
1471 // Push the entries to the archive.
1472 CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1474 DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1475 e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1477 e_0002_0001->SetLength(2);
1478 Archive->Push(e_0002_0001);
1479 e_0002_0001->Delete();
1481 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1483 // we keep the original 'Media Storage SOP Class UID', we default it if missing
1484 CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1488 // It's *not* an image comming straight from a source. We force
1489 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1490 CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1493 // 'Media Storage SOP Instance UID'
1494 CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1496 // 'Implementation Class UID'
1497 // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1498 // seems to be Root UID + 4 digits (?)
1499 CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1501 // 'Implementation Version Name'
1502 std::string version = "GDCM ";
1503 version += Util::GetVersion();
1504 CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1507 // --------------------- For DataSet ---------------------
1509 if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1512 gdcmDebugMacro( "USER_OWN_IMAGE (1)");
1513 // If 'SOP Class UID' exists ('true DICOM' image)
1514 // we create the 'Source Image Sequence' SeqEntry
1515 // to hold informations about the Source Image
1517 DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1520 // Create 'Source Image Sequence' SeqEntry
1521 // SeqEntry *sis = SeqEntry::New (
1522 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x2112) );
1523 SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1524 SQItem *sqi = SQItem::New(1);
1525 // (we assume 'SOP Instance UID' exists too)
1526 // create 'Referenced SOP Class UID'
1527 // DataEntry *e_0008_1150 = DataEntry::New(
1528 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1150) );
1529 DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1530 e_0008_1150->SetString( e_0008_0016->GetString());
1531 sqi->AddEntry(e_0008_1150);
1532 e_0008_1150->Delete();
1534 // create 'Referenced SOP Instance UID'
1535 DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1536 // DataEntry *e_0008_1155 = DataEntry::New(
1537 // Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1155) );
1538 DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1539 e_0008_1155->SetString( e_0008_0018->GetString());
1540 sqi->AddEntry(e_0008_1155);
1541 e_0008_1155->Delete();
1543 sis->AddSQItem(sqi,1);
1546 // temporarily replaces any previous 'Source Image Sequence'
1550 // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1551 if ( ContentType == FILTERED_IMAGE)
1552 // the user *knows* he just modified the pixels
1553 // the image is no longer an 'Original' one
1554 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1558 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1560 // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1561 CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");
1565 // It's *not* an image comming straight from a source. We force
1566 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1567 CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");
1570 Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1571 // Push out 'LibIDO-special' entries, if any
1572 Archive->Push(0x0028,0x0015);
1573 Archive->Push(0x0028,0x0016);
1574 Archive->Push(0x0028,0x0017);
1575 Archive->Push(0x0028,0x0198); // very old versions
1576 Archive->Push(0x0028,0x0199);
1578 // Replace deprecated 0028 0012 US Planes
1579 // by new 0028 0008 IS Number of Frames
1581 ///\todo : find if there is a rule!
1582 DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1585 CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1586 Archive->Push(0x0028,0x0012);
1589 // Deal with the pb of (Bits Stored = 12)
1590 // - we're gonna write the image as Bits Stored = 16
1591 if ( FileInternal->GetEntryString(0x0028,0x0100) == "12")
1593 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1596 // Check if user wasn't drunk ;-)
1598 std::ostringstream s;
1599 // check 'Bits Allocated' vs decent values
1600 int nbBitsAllocated = FileInternal->GetBitsAllocated();
1601 if ( (nbBitsAllocated == 0 || nbBitsAllocated > 32)
1602 || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1604 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1605 gdcmWarningMacro("(0028,0100) changed from "
1606 << nbBitsAllocated << " to 16 for consistency purpose");
1607 nbBitsAllocated = 16;
1609 // check 'Bits Stored' vs 'Bits Allocated'
1610 int nbBitsStored = FileInternal->GetBitsStored();
1611 if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1614 s << nbBitsAllocated;
1615 CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1616 gdcmWarningMacro("(0028,0101) changed from "
1617 << nbBitsStored << " to " << nbBitsAllocated
1618 << " for consistency purpose" );
1619 nbBitsStored = nbBitsAllocated;
1621 // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1622 int highBitPosition = FileInternal->GetHighBitPosition();
1623 if ( highBitPosition == 0 ||
1624 highBitPosition > nbBitsAllocated-1 ||
1625 highBitPosition < nbBitsStored-1 )
1628 s << nbBitsStored - 1;
1629 CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1630 gdcmWarningMacro("(0028,0102) changed from "
1631 << highBitPosition << " to " << nbBitsAllocated-1
1632 << " for consistency purpose");
1635 // check Pixel Representation (default it as 0 -unsigned-)
1637 DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1640 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1641 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1645 int sign = (int)e_0028_0103->GetValue(0);
1646 if (sign !=1 && sign !=0)
1648 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1649 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1653 std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1654 if ( pixelSpacing == GDCM_UNFOUND )
1656 pixelSpacing = "1.0\\1.0";
1657 // if missing, Pixel Spacing forced to "1.0\1.0"
1658 CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1661 // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1662 // --> This one is the *legal* one !
1663 if ( ContentType != USER_OWN_IMAGE)
1664 // we write it only when we are *sure* the image comes from
1665 // an imager (see also 0008,0x0064)
1666 CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1669 ///Exact meaning of RETired fields
1671 // See page 73 of ACR-NEMA_300-1988.pdf !
1673 // 0020,0020 : Patient Orientation :
1674 Patient direction of the first row and
1675 column of the images. The first entry id the direction of the raws, given by the
1676 direction of the last pixel in the first row from the first pixel in tha row.
1677 the second entry is the direction of the columns, given by the direction of the
1678 last pixel in the first column from the first pixel in that column.
1679 L : Left, F : Feet, A : Anterior, P : Posterior.
1680 Up to 3 letters can be used in combination to indicate oblique planes.
1682 //0020,0030 Image Position (RET)
1683 x,y,z coordinates im mm of the first pixel in the image
1685 // 0020,0035 Image Orientation (RET)
1686 Direction cosines of the R axis of the image system with respect to the
1687 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1688 the image system with respect to the same axes
1690 //0020,0050 Location
1691 An image location reference, standard for the modality (such as CT bed position),
1692 used to indicate position. Calculation of position for other purposes
1693 is only from (0020,0030) and (0020,0035)
1697 // if imagePositionPatient not found, default it with imagePositionRet, if any
1698 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1700 std::string imagePositionRet = FileInternal->GetEntryString(0x0020,0x0030);
1701 std::string imageOrientationRet = FileInternal->GetEntryString(0x0020,0x0035);
1702 std::string imagePositionPatient = FileInternal->GetEntryString(0x0020,0x0032);
1703 std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1705 if( imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1706 && imagePositionRet != GDCM_UNFOUND && imageOrientationRet != GDCM_UNFOUND)
1708 CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1709 Archive->Push(0x0020,0x0030);
1710 CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1711 Archive->Push(0x0020,0x0035);
1715 // Samples Per Pixel (type 1) : default to grayscale
1716 CheckMandatoryEntry(0x0028,0x0002,"1","US");
1718 // --- Check UID-related Entries ---
1720 // At the end, not to overwrite the original ones,
1721 // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
1722 // 'SOP Instance UID'
1723 CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1725 if ( ContentType == USER_OWN_IMAGE)
1727 gdcmDebugMacro( "USER_OWN_IMAGE (2)");
1729 // Other possible values are :
1730 // See PS 3.3, Page 408
1732 // DV = Digitized Video
1733 // DI = Digital Interface
1734 // DF = Digitized Film
1735 // WSD = Workstation
1736 // SD = Scanned Document
1737 // SI = Scanned Image
1739 // SYN = Synthetic Image
1741 CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1744 if ( ContentType == CREATED_IMAGE)
1746 /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1751 // ---- The user will never have to take any action on the following ----
1753 // new value for 'SOP Instance UID'
1754 //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1756 // Instance Creation Date
1757 const std::string &date = Util::GetCurrentDate();
1758 CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1760 // Instance Creation Time
1761 const std::string &time = Util::GetCurrentTime();
1762 CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1765 CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1767 CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1770 //CopyMandatoryEntry(0x0008,0x0050,"");
1771 CheckMandatoryEntry(0x0008,0x0050,"","SH");
1774 // ----- Add Mandatory Entries if missing ---
1775 // Entries whose type is 1 are mandatory, with a mandatory value
1776 // Entries whose type is 1c are mandatory-inside-a-Sequence,
1777 // with a mandatory value
1778 // Entries whose type is 2 are mandatory, with an optional value
1779 // Entries whose type is 2c are mandatory-inside-a-Sequence,
1780 // with an optional value
1781 // Entries whose type is 3 are optional
1783 // 'Study Instance UID'
1784 // Keep the value if exists
1785 // The user is allowed to create his own Study,
1786 // keeping the same 'Study Instance UID' for various images
1787 // The user may add images to a 'Manufacturer Study',
1788 // adding new Series to an already existing Study
1789 CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1791 // 'Serie Instance UID'
1792 // Keep the value if exists
1793 // The user is allowed to create his own Series,
1794 // keeping the same 'Serie Instance UID' for various images
1795 // The user shouldn't add any image to a 'Manufacturer Serie'
1796 // but there is no way no to prevent him for doing that
1797 CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1800 CheckMandatoryEntry(0x0020,0x0010,"","SH");
1803 CheckMandatoryEntry(0x0020,0x0011,"","IS");
1806 CheckMandatoryEntry(0x0020,0x0013,"","IS");
1808 // Patient Orientation
1809 // Can be computed from (0020|0037) : Image Orientation (Patient)
1810 GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1811 std::string ori = o->GetOrientation ( FileInternal );
1813 if (ori != "\\" && ori != GDCM_UNFOUND)
1814 CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1816 CheckMandatoryEntry(0x0020,0x0020,"","CS");
1818 // Default Patient Position to HFS
1819 CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1821 // Modality : if missing we set it to 'OTher'
1822 CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1824 // Manufacturer : if missing we set it to 'GDCM Factory'
1825 CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1827 // Institution Name : if missing we set it to 'GDCM Hospital'
1828 CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1830 // Patient's Name : if missing, we set it to 'GDCM^Patient'
1831 CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1833 // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1834 CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1836 // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1837 CheckMandatoryEntry(0x0010,0x0030,"","DA");
1839 // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1840 CheckMandatoryEntry(0x0010,0x0040,"","CS");
1842 // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1843 CheckMandatoryEntry(0x0008,0x0090,"","PN");
1846 // Deal with element 0x0000 (group length) of each group.
1847 // First stage : get all the different Groups
1850 DocEntry *d = FileInternal->GetFirstEntry();
1853 grHT[d->GetGroup()] = 0;
1854 d=FileInternal->GetNextEntry();
1856 // Second stage : add the missing ones (if any)
1857 for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)
1859 CheckMandatoryEntry(it->first, 0x0000, "0");
1861 // Third stage : update all 'zero level' groups length
1865 if (PhotometricInterpretation == 1)
1870 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1872 DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1875 //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1876 entry = DataEntry::New(group,elem,vr);
1877 entry->SetString(value);
1878 Archive->Push(entry);
1883 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
1884 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1886 //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1887 DataEntry *entry = DataEntry::New(group,elem,vr);
1888 entry->SetString(value);
1889 Archive->Push(entry);
1893 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1895 DataEntry *entry = CopyDataEntry(group,elem,vr);
1896 entry->SetString(value);
1897 Archive->Push(entry);
1902 * \brief Restore in the File the initial group 0002
1904 void FileHelper::RestoreWriteMandatory()
1906 // group 0002 may be pushed out for ACR-NEMA writting purposes
1907 Archive->Restore(0x0002,0x0000);
1908 Archive->Restore(0x0002,0x0001);
1909 Archive->Restore(0x0002,0x0002);
1910 Archive->Restore(0x0002,0x0003);
1911 Archive->Restore(0x0002,0x0010);
1912 Archive->Restore(0x0002,0x0012);
1913 Archive->Restore(0x0002,0x0013);
1914 Archive->Restore(0x0002,0x0016);
1915 Archive->Restore(0x0002,0x0100);
1916 Archive->Restore(0x0002,0x0102);
1918 // FIXME : Check if none is missing !
1920 Archive->Restore(0x0008,0x0012);
1921 Archive->Restore(0x0008,0x0013);
1922 Archive->Restore(0x0008,0x0016);
1923 Archive->Restore(0x0008,0x0018);
1924 Archive->Restore(0x0008,0x0060);
1925 Archive->Restore(0x0008,0x0070);
1926 Archive->Restore(0x0008,0x0080);
1927 Archive->Restore(0x0008,0x0090);
1928 Archive->Restore(0x0008,0x2112);
1930 Archive->Restore(0x0010,0x0010);
1931 Archive->Restore(0x0010,0x0030);
1932 Archive->Restore(0x0010,0x0040);
1934 Archive->Restore(0x0020,0x000d);
1935 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.");
2015 * \brief Deal with Grey levels i.e. re-arange them
2016 * to have low values = dark, high values = bright
2018 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
2020 uint32_t i; // to please M$VC6
2023 // Number of Bits Allocated for storing a Pixel is defaulted to 16
2024 // when absent from the file.
2025 int bitsAllocated = FileInternal->GetBitsAllocated();
2026 if ( bitsAllocated == 0 )
2031 else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2035 // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2036 // when absent from the file.
2037 int bitsStored = FileInternal->GetBitsStored();
2038 if ( bitsStored == 0 )
2040 bitsStored = bitsAllocated;
2043 if (!FileInternal->IsSignedPixelData())
2045 if ( bitsAllocated == 8 )
2047 uint8_t *deb = (uint8_t *)raw;
2048 for (i=0; i<rawSize; i++)
2056 if ( bitsAllocated == 16 )
2059 for (j=0; j<bitsStored-1; j++)
2061 mask = (mask << 1) +1; // will be fff when BitsStored=12
2064 uint16_t *deb = (uint16_t *)raw;
2065 for (i=0; i<rawSize/2; i++)
2075 if ( bitsAllocated == 8 )
2077 uint8_t smask8 = 255;
2078 uint8_t *deb = (uint8_t *)raw;
2079 for (i=0; i<rawSize; i++)
2081 *deb = smask8 - *deb;
2086 if ( bitsAllocated == 16 )
2088 uint16_t smask16 = 65535;
2089 uint16_t *deb = (uint16_t *)raw;
2090 for (i=0; i<rawSize/2; i++)
2092 *deb = smask16 - *deb;
2100 //-----------------------------------------------------------------------------
2102 * \brief Prints the FileInternal + info on PixelReadConvertor
2103 * @param os ostream we want to print in
2104 * @param indent (unused)
2106 void FileHelper::Print(std::ostream &os, std::string const &)
2108 FileInternal->SetPrintLevel(PrintLevel);
2109 FileInternal->Print(os);
2111 if ( FileInternal->IsReadable() )
2113 PixelReadConverter->SetPrintLevel(PrintLevel);
2114 PixelReadConverter->Print(os);
2118 //-----------------------------------------------------------------------------
2119 } // end namespace gdcm
2122 /* Probabely something to be added to use Rescale Slope/Intercept
2123 Have a look ,at ITK code !
2125 // Internal function to rescale pixel according to Rescale Slope/Intercept
2126 template<class TBuffer, class TSource>
2127 void RescaleFunction(TBuffer* buffer, TSource *source,
2128 double slope, double intercept, size_t size)
2130 size /= sizeof(TSource);
2132 if (slope != 1.0 && intercept != 0.0)
2134 // Duff's device. Instead of this code:
2136 // for(unsigned int i=0; i<size; i++)
2138 // buffer[i] = (TBuffer)(source[i]*slope + intercept);
2141 // use Duff's device which exploits "fall through"
2142 register size_t n = (size + 7) / 8;
2145 case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2146 case 7: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2147 case 6: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2148 case 5: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2149 case 4: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2150 case 3: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2151 case 2: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2152 case 1: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2156 else if (slope == 1.0 && intercept != 0.0)
2158 // Duff's device. Instead of this code:
2160 // for(unsigned int i=0; i<size; i++)
2162 // buffer[i] = (TBuffer)(source[i] + intercept);
2165 // use Duff's device which exploits "fall through"
2166 register size_t n = (size + 7) / 8;
2169 case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2170 case 7: *buffer++ = (TBuffer)(*source++ + intercept);
2171 case 6: *buffer++ = (TBuffer)(*source++ + intercept);
2172 case 5: *buffer++ = (TBuffer)(*source++ + intercept);
2173 case 4: *buffer++ = (TBuffer)(*source++ + intercept);
2174 case 3: *buffer++ = (TBuffer)(*source++ + intercept);
2175 case 2: *buffer++ = (TBuffer)(*source++ + intercept);
2176 case 1: *buffer++ = (TBuffer)(*source++ + intercept);
2180 else if (slope != 1.0 && intercept == 0.0)
2182 // Duff's device. Instead of this code:
2184 // for(unsigned int i=0; i<size; i++)
2186 // buffer[i] = (TBuffer)(source[i]*slope);
2189 // use Duff's device which exploits "fall through"
2190 register size_t n = (size + 7) / 8;
2193 case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2194 case 7: *buffer++ = (TBuffer)((*source++)*slope);
2195 case 6: *buffer++ = (TBuffer)((*source++)*slope);
2196 case 5: *buffer++ = (TBuffer)((*source++)*slope);
2197 case 4: *buffer++ = (TBuffer)((*source++)*slope);
2198 case 3: *buffer++ = (TBuffer)((*source++)*slope);
2199 case 2: *buffer++ = (TBuffer)((*source++)*slope);
2200 case 1: *buffer++ = (TBuffer)((*source++)*slope);
2206 // Duff's device. Instead of this code:
2208 // for(unsigned int i=0; i<size; i++)
2210 // buffer[i] = (TBuffer)(source[i]);
2213 // use Duff's device which exploits "fall through"
2214 register size_t n = (size + 7) / 8;
2217 case 0: do { *buffer++ = (TBuffer)(*source++);
2218 case 7: *buffer++ = (TBuffer)(*source++);
2219 case 6: *buffer++ = (TBuffer)(*source++);
2220 case 5: *buffer++ = (TBuffer)(*source++);
2221 case 4: *buffer++ = (TBuffer)(*source++);
2222 case 3: *buffer++ = (TBuffer)(*source++);
2223 case 2: *buffer++ = (TBuffer)(*source++);
2224 case 1: *buffer++ = (TBuffer)(*source++);
2231 template<class TSource>
2232 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2233 void* buffer, TSource *source,
2234 double slope, double intercept, size_t size)
2238 case ImageIOBase::UCHAR:
2239 RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2241 case ImageIOBase::CHAR:
2242 RescaleFunction( (char *)buffer, source, slope, intercept, size);
2244 case ImageIOBase::USHORT:
2245 RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2247 case ImageIOBase::SHORT:
2248 RescaleFunction( (short *)buffer, source, slope, intercept, size);
2250 case ImageIOBase::UINT:
2251 RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2253 case ImageIOBase::INT:
2254 RescaleFunction( (int *)buffer, source, slope, intercept, size);
2256 case ImageIOBase::FLOAT:
2257 RescaleFunction( (float *)buffer, source, slope, intercept, size);
2259 case ImageIOBase::DOUBLE:
2260 RescaleFunction( (double *)buffer, source, slope, intercept, size);
2263 ::itk::OStringStream message;
2264 message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2265 ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);
2271 ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);