1 /*=========================================================================
4 Module: $RCSfile: gdcmFileHelper.cxx,v $
7 Date: $Date: 2009/05/19 15:07:58 $
8 Version: $Revision: 1.139 $
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"
39 #include <algorithm> // for transform?
41 #if defined(__BORLANDC__)
42 #include <mem.h> // for memset
43 #include <ctype.h> //for toupper
50 // ----------------------------- WARNING -------------------------
52 These lines will be moved to the document-to-be 'User's Guide'
54 // To read an image, user needs a GDCM_NAME_SPACE::File
55 GDCM_NAME_SPACE::File *f = new GDCM_NAME_SPACE::File(fileName);
57 // user may also decide he doesn't want to load some parts of the header
58 GDCM_NAME_SPACE::File *f = new GDCM_NAME_SPACE::File();
59 f->SetFileName(fileName);
60 f->SetLoadMode(LD_NOSEQ); // or
61 f->SetLoadMode(LD_NOSHADOW); // or
62 f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW); // or
63 f->SetLoadMode(LD_NOSHADOWSEQ);
66 // To decide whether it's an 'image of interest for him, or not,
67 // user can now check some values
68 std::string v = f->GetEntryValue(groupNb,ElementNb);
70 // to get the pixels, user needs a GDCM_NAME_SPACE::FileHelper
71 GDCM_NAME_SPACE::FileHelper *fh = new GDCM_NAME_SPACE::FileHelper(f);
73 // user may ask not to convert Palette (if any) to RGB
74 uint8_t *pixels = fh->GetImageDataRaw();
75 int imageLength = fh->GetImageDataRawSize();
77 // He can now use the pixels, create a new image, ...
78 uint8_t *userPixels = ...
80 //To re-write the image, user re-uses the GDCM_NAME_SPACE::FileHelper
81 GDCM_NAME_SPACE::File *fh = new GDCM_NAME_SPACE::FileHelper();
83 fh->SetTypeToRaw(); // Even if it was possible to convert Palette to RGB
86 // If user wants to write the file as MONOCHROME1 (0=white)
87 fh->SetPhotometricInterpretationToMonochrome1();
89 fh->SetWriteTypeToDcmExpl(); // he wants Explicit Value Representation
90 // Little Endian is the default,
91 // bigendian not supported for writting
92 (-->SetWriteType(ExplicitVR);)
93 -->WriteType = ExplicitVR;
94 fh->SetWriteTypeToJPEG(); // lossless compression
95 fh->SetWriteTypeToJPEG2000(); // lossless compression
97 fh->SetImageData( userPixels, userPixelsLength);
99 fh->SetUserData( userPixels, userPixelsLength); // this one performs compression, when required
101 fh->Write(newFileName); // overwrites the file, if any
106 These lines will be moved to the document-to-be 'Developer's Guide'
108 WriteMode : WMODE_RAW / WMODE_RGB
109 WriteType : ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO
110 PhotometricInterpretation : MONOCHROME2 (0=black), MONOCHROME2 (0=white)
112 fh->SetImageData( userPixels, userPixelsLength);
114 fh->SetUserData( userPixels, userPixelsLength);
115 PixelWriteConverter->SetUserData(inData, expectedSize);
118 fh->SetWriteMode(WMODE_RAW / WMODE_RGB)
120 fh->SetWriteType( ImplicitVR/ExplicitVR/ACR/ACR_LIBIDO/JPEG/JPEG2000)
122 fh->Write(newFileName);
123 CheckMandatoryElements(); // Checks existing ones / Add missing ones
124 Fix VR if unknown elements
125 SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR(); /
126 SetWriteFileTypeToACR() / SetWriteFileTypeToJPEG() / SetWriteFileTypeToJ2K()
127 (Modifies TransferSyntax if any; Pushes to the Archives old one)
128 SetWriteToRaw(); / SetWriteToRGB();
129 (Modifies and pushes to the Archive, when necessary : photochr. interp.,
130 samples per pixel, Planar configuration,
131 bits allocated, bits stored, high bit -ACR 24 bits-
132 Pixels element VR, pushes out the LUT )
134 Sets Photometric Interpretation
135 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
136 Sets VR, BinArea, Length for PixelData
139 Archive->Push(photInt);
140 Archive->Push(pixel);
144 if NumberOfScalarComponents==1
145 SetWriteToRaw(); return;
146 PixelReadConverter->BuildRGBImage()
147 DataEntry *pixel =CopyDataEntry(7fe0,0010,VR)
148 Archives spp, planConfig,photInt, pixel
150 CheckWriteIntegrity();
151 (checks user given pixels length)
152 FileInternal->Write(fileName,WriteType)
153 fp = opens file(fileName); // out|binary
154 ComputeGroup0002Length( );
155 Document::WriteContent(fp, writetype);
156 writes Dicom File Preamble not ACR-NEMA
157 ElementSet::WriteContent(fp, writetype);
158 writes recursively all DataElements
160 (moves back to the GDCM_NAME_SPACE::File all the archived elements)
166 namespace GDCM_NAME_SPACE
168 typedef std::map<uint16_t, int> GroupHT; // Hash Table
169 //-------------------------------------------------------------------------
170 // Constructor / Destructor
172 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
173 * file (GDCM_NAME_SPACE::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 * one sets an a posteriori shadow dictionary (efficiency can be
182 * seen as a side effect).
184 FileHelper::FileHelper( )
186 FileInternal = File::New( );
191 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
192 * file (File only deals with the ... header)
193 * Opens (in read only and when possible) an existing file and checks
194 * for DICOM compliance. Returns NULL on failure.
195 * It will be up to the user to load the pixels into memory
196 * ( GetImageDataSize() + GetImageData() methods)
197 * \note the in-memory representation of all available tags found in
198 * the DICOM header is post-poned to first header information access.
199 * This avoid a double parsing of public part of the header when
200 * user sets an a posteriori shadow dictionary (efficiency can be
201 * seen as a side effect).
202 * @param header already built Header
204 FileHelper::FileHelper(File *header)
206 gdcmAssertMacro(header);
208 FileInternal = header;
209 FileInternal->Register();
211 if ( FileInternal->IsReadable() )
213 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
218 * \brief canonical destructor
219 * \note If the header (GDCM_NAME_SPACE::File) was created by the FileHelper constructor,
220 * it is destroyed by the FileHelper
222 FileHelper::~FileHelper()
224 if ( PixelReadConverter )
226 delete PixelReadConverter;
228 if ( PixelWriteConverter )
230 delete PixelWriteConverter;
237 FileInternal->Unregister();
240 //-----------------------------------------------------------------------------
244 * \brief Sets the LoadMode of the internal GDCM_NAME_SPACE::File as a boolean string.
245 * NO_SEQ, NO_SHADOW, NO_SHADOWSEQ ... (nothing more, right now)
246 * WARNING : before using NO_SHADOW, be sure *all* your files
247 * contain accurate values in the 0x0000 element (if any)
248 * of *each* Shadow Group. The parser will fail if the size is wrong !
249 * @param loadMode Load mode to be used
251 void FileHelper::SetLoadMode(int loadMode)
253 GetFile()->SetLoadMode( loadMode );
256 * \brief Sets the LoadMode of the internal GDCM_NAME_SPACE::File
257 * @param fileName name of the file to be open
259 void FileHelper::SetFileName(std::string const &fileName)
261 FileInternal->SetFileName( fileName );
266 * @return false if file cannot be open or no swap info was found,
267 * or no tag was found.
269 bool FileHelper::Load()
271 if ( !FileInternal->Load() )
274 PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
279 * \brief Accesses an existing DataEntry through its (group, element)
280 * and modifies its content with the given value.
281 * @param content new value (string) to substitute with
282 * @param group group number of the Dicom Element to modify
283 * @param elem element number of the Dicom Element to modify
284 * \return false if DataEntry not found
286 bool FileHelper::SetEntryString(std::string const &content,
287 uint16_t group, uint16_t elem)
289 return FileInternal->SetEntryString(content, group, elem);
294 * \brief Accesses an existing DataEntry through its (group, element)
295 * and modifies its content with the given value.
296 * @param content new value (void* -> uint8_t*) to substitute with
297 * @param lgth new value length
298 * @param group group number of the Dicom Element to modify
299 * @param elem element number of the Dicom Element to modify
300 * \return false if DataEntry not found
302 bool FileHelper::SetEntryBinArea(uint8_t *content, int lgth,
303 uint16_t group, uint16_t elem)
305 return FileInternal->SetEntryBinArea(content, lgth, group, elem);
309 * \brief Modifies the value of a given DataEntry when it exists.
310 * Creates it with the given value when unexistant.
311 * @param content (string) value to be set
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::InsertEntryString(std::string const &content,
319 uint16_t group, uint16_t elem,
322 return FileInternal->InsertEntryString(content, group, elem, vr);
326 * \brief Modifies the value of a given DataEntry when it exists.
327 * Creates it with the given value when unexistant.
328 * A copy of the binArea is made to be kept in the Document.
329 * @param binArea (binary) value to be set
330 * @param lgth new value length
331 * @param group Group number of the Entry
332 * @param elem Element number of the Entry
333 * @param vr Value Representation of the DataElement to be inserted
334 * \return pointer to the modified/created DataEntry (NULL when creation
337 DataEntry *FileHelper::InsertEntryBinArea(uint8_t *binArea, int lgth,
338 uint16_t group, uint16_t elem,
341 return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem, vr);
345 * \brief Adds an empty SeqEntry
346 * (remove any existing entry with same group,elem)
347 * @param group Group number of the Entry
348 * @param elem Element number of the Entry
349 * \return pointer to the created SeqEntry (NULL when creation
352 SeqEntry *FileHelper::InsertSeqEntry(uint16_t group, uint16_t elem)
354 return FileInternal->InsertSeqEntry(group, elem);
358 * \brief Get the size of the image data
359 * If the image can be RGB (with a lut or by default), the size
360 * corresponds to the RGB image
361 * (use GetImageDataRawSize if you want to be sure to get *only*
362 * the size of the pixels)
363 * @return The image size
365 size_t FileHelper::GetImageDataSize()
367 if ( PixelWriteConverter->GetUserData() )
369 return PixelWriteConverter->GetUserDataSize();
371 return PixelReadConverter->GetRGBSize();
375 * \brief Get the size of the image data.
376 * If the image could be converted to RGB using a LUT,
377 * this transformation is not taken into account by GetImageDataRawSize
378 * (use GetImageDataSize if you wish)
379 * @return The raw image size
381 size_t FileHelper::GetImageDataRawSize()
383 if ( PixelWriteConverter->GetUserData() )
385 return PixelWriteConverter->GetUserDataSize();
387 return PixelReadConverter->GetRawSize();
391 * \brief brings pixels into memory :
392 * - Allocates necessary memory,
393 * - Reads the pixels from disk (uncompress if necessary),
394 * - Transforms YBR pixels, if any, into RGB pixels,
395 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
396 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
397 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
398 * @return Pointer to newly allocated pixel data.
399 * (uint8_t is just for prototyping. feel free to cast)
400 * NULL if alloc fails
402 uint8_t *FileHelper::GetImageData()
404 if ( PixelWriteConverter->GetUserData() )
406 return PixelWriteConverter->GetUserData();
411 // If the decompression failed nothing can be done.
415 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
417 return PixelReadConverter->GetRGB();
421 // When no LUT or LUT conversion fails, return the Raw
422 return PixelReadConverter->GetRaw();
427 * \brief brings pixels into memory :
428 * - Allocates necessary memory,
429 * - Transforms YBR pixels (if any) into RGB pixels
430 * - Transforms 3 planes R, G, B (if any) into a single RGB Plane
431 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
432 * - DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
433 * @return Pointer to newly allocated pixel data.
434 * (uint8_t is just for prototyping. feel free to cast)
435 * NULL if alloc fails
437 uint8_t *FileHelper::GetImageDataRaw ()
442 //#ifndef GDCM_LEGACY_REMOVE
444 * \brief Useless function, since PixelReadConverter forces us
445 * copy the Pixels anyway.
446 * Reads the pixels from disk (uncompress if necessary),
447 * Transforms YBR pixels, if any, into RGB pixels
448 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
449 * Transforms single Grey plane + 3 Palettes into a RGB Plane
450 * Copies at most MaxSize bytes of pixel data to caller allocated
452 * \warning This function allows people that want to build a volume
453 * from an image stack *not to* have, first to get the image pixels,
454 * and then move them to the volume area.
455 * It's absolutely useless for any VTK user since vtk chooses
456 * to invert the lines of an image, that is the last line comes first
457 * (for some axis related reasons?). Hence he will have
458 * to load the image line by line, starting from the end.
459 * VTK users have to call GetImageData
461 * @param destination Address (in caller's memory space) at which the
462 * pixel data should be copied
463 * @param maxSize Maximum number of bytes to be copied. When MaxSize
464 * is not sufficient to hold the pixel data the copy is not
465 * executed (i.e. no partial copy).
466 * @return On success, the number of bytes actually copied. Zero on
467 * failure e.g. MaxSize is lower than necessary.
470 size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
474 // If the decompression failed nothing can be done.
478 if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
480 if ( PixelReadConverter->GetRGBSize() > maxSize )
482 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
486 (void*)PixelReadConverter->GetRGB(),
487 PixelReadConverter->GetRGBSize() );
488 return PixelReadConverter->GetRGBSize();
491 // Either no LUT conversion necessary or LUT conversion failed
492 if ( PixelReadConverter->GetRawSize() > maxSize )
494 gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
498 (void *)PixelReadConverter->GetRaw(),
499 PixelReadConverter->GetRawSize() );
500 return PixelReadConverter->GetRawSize();
506 * \brief Points the internal pointer to the callers inData
507 * image representation, BUT WITHOUT COPYING THE DATA.
508 * 'image' Pixels are presented as C-like 2D arrays : line per line.
509 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
510 * \warning Since the pixels are not copied, it is the caller's responsability
511 * not to deallocate its data before gdcm uses them (e.g. with
512 * the Write() method )
513 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
514 * user is allowed to pass any kind of pixels since the size is
516 * @param expectedSize total image size, *in Bytes*
518 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
520 PixelWriteConverter->SetUserData(inData, expectedSize);
521 /// \todo : shouldn't we call SetCompressJPEGUserData/SetCompressJPEG2000UserData
526 * \brief Set the image data defined by the user
527 * \warning When writting the file, this data are get as default data to write
528 * @param inData user supplied pixel area (uint8_t* is just for the compiler.
529 * user is allowed to pass any kind of pixels since the size is
531 * @param expectedSize total image size, *in Bytes*
533 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
535 // Shouldn't we move theese lines to FileHelper::Write()?
537 if( WriteType == JPEG2000 )
539 PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
541 else if( WriteType == JPEG )
543 PixelWriteConverter->SetCompressJPEGUserData(inData, expectedSize, FileInternal);
547 PixelWriteConverter->SetUserData(inData, expectedSize);
551 PixelWriteConverter->SetUserData(inData, expectedSize);
555 * \brief Get the image data defined by the user
556 * \warning When writting the file, this data are get as default data to write
558 uint8_t *FileHelper::GetUserData()
560 return PixelWriteConverter->GetUserData();
564 * \brief Get the image data size defined by the user
565 * \warning When writting the file, this data are get as default data to write
567 size_t FileHelper::GetUserDataSize()
569 return PixelWriteConverter->GetUserDataSize();
573 * \brief Get the image data from the file.
574 * If a LUT is found, the data are expanded to be RGB
576 uint8_t *FileHelper::GetRGBData()
578 return PixelReadConverter->GetRGB();
582 * \brief Get the image data size from the file.
583 * If a LUT is found, the data are expanded to be RGB
585 size_t FileHelper::GetRGBDataSize()
587 return PixelReadConverter->GetRGBSize();
591 * \brief Get the image data from the file.
592 * Even when a LUT is found, the data are not expanded to RGB!
594 uint8_t *FileHelper::GetRawData()
596 return PixelReadConverter->GetRaw();
600 * \brief Get the image data size from the file.
601 * Even when a LUT is found, the data are not expanded to RGB!
603 size_t FileHelper::GetRawDataSize()
605 return PixelReadConverter->GetRawSize();
609 * \brief Access to the underlying PixelReadConverter RGBA LUT
611 uint8_t* FileHelper::GetLutRGBA()
613 if ( PixelReadConverter->GetLutRGBA() ==0 )
614 PixelReadConverter->BuildLUTRGBA();
615 return PixelReadConverter->GetLutRGBA();
619 * \brief Access to the underlying PixelReadConverter RGBA LUT Item Number
621 int FileHelper::GetLutItemNumber()
623 return PixelReadConverter->GetLutItemNumber();
627 * \brief Access to the underlying PixelReadConverter RGBA LUT Item Size
629 int FileHelper::GetLutItemSize()
631 return PixelReadConverter->GetLutItemSize();
635 * \brief Writes on disk A SINGLE Dicom file
636 * NO test is performed on processor "Endiannity".
637 * It's up to the user to call his Reader properly
638 * @param fileName name of the file to be created
639 * (any already existing file is over written)
640 * @return false if write fails
642 bool FileHelper::WriteRawData(std::string const &fileName)
644 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
647 gdcmWarningMacro( "Fail to open (write) file:" << fileName.c_str());
651 if ( PixelWriteConverter->GetUserData() )
653 fp1.write( (char *)PixelWriteConverter->GetUserData(),
654 PixelWriteConverter->GetUserDataSize() );
656 else if ( PixelReadConverter->GetRGB() )
658 fp1.write( (char *)PixelReadConverter->GetRGB(),
659 PixelReadConverter->GetRGBSize());
661 else if ( PixelReadConverter->GetRaw() )
663 fp1.write( (char *)PixelReadConverter->GetRaw(),
664 PixelReadConverter->GetRawSize());
668 gdcmErrorMacro( "Nothing written." );
677 * \brief Writes on disk A SINGLE Dicom file,
678 * using the Implicit Value Representation convention
679 * NO test is performed on processor "Endianity".
680 * @param fileName name of the file to be created
681 * (any already existing file is overwritten)
682 * @return false if write fails
685 bool FileHelper::WriteDcmImplVR (std::string const &fileName)
687 SetWriteTypeToDcmImplVR();
688 return Write(fileName);
692 * \brief Writes on disk A SINGLE Dicom file,
693 * using the Explicit Value Representation convention
694 * NO test is performed on processor "Endiannity".
695 * @param fileName name of the file to be created
696 * (any already existing file is overwritten)
697 * @return false if write fails
700 bool FileHelper::WriteDcmExplVR (std::string const &fileName)
702 SetWriteTypeToDcmExplVR();
703 return Write(fileName);
707 * \brief Writes on disk A SINGLE Dicom file,
708 * using the ACR-NEMA convention
709 * NO test is performed on processor "Endiannity".
710 * (a l'attention des logiciels cliniques
711 * qui ne prennent en entrée QUE des images ACR ...
712 * \warning if a DICOM_V3 header is supplied,
713 * groups < 0x0008 and shadow groups are ignored
714 * \warning NO TEST is performed on processor "Endiannity".
715 * @param fileName name of the file to be created
716 * (any already existing file is overwritten)
717 * @return false if write fails
720 bool FileHelper::WriteAcr (std::string const &fileName)
723 return Write(fileName);
727 * \brief Writes on disk A SINGLE Dicom file,
728 * @param fileName name of the file to be created
729 * (any already existing file is overwritten)
730 * @return false if write fails
732 bool FileHelper::Write(std::string const &fileName)
734 CheckMandatoryElements(); //called once, here !
739 SetWriteFileTypeToImplicitVR();
742 case Unknown: // should never happen; ExplicitVR is the default value
745 // We let DocEntry::WriteContent to put vr=UN for undocumented Shadow Groups !
746 SetWriteFileTypeToExplicitVR();
751 // NOTHING is done here just for LibIDO.
752 // Just to avoid further trouble if user creates a file ex-nihilo,
753 // wants to write it as an ACR-NEMA file,
754 // and forgets to create any Entry belonging to group 0008
756 // We add Recognition Code (RET)
757 if ( ! FileInternal->GetDataEntry(0x0008, 0x0010) )
758 FileInternal->InsertEntryString("ACR-NEMA V1.0 ",
759 0x0008, 0x0010, "LO");
760 SetWriteFileTypeToACR();
761 // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
764 /// \todo FIXME : JPEG/JPEG2000 may be either ExplicitVR or ImplicitVR
766 SetWriteFileTypeToJPEG();
768 //PixelWriteConverter->SetCompressJPEGUserData(
769 // inData, expectedSize, FileInternal);
770 PixelWriteConverter->SetCompressJPEGUserData(
771 PixelWriteConverter->GetUserData(),
772 PixelWriteConverter->GetUserDataSize(),FileInternal);
776 /// \todo Maybe we should consider doing the compression here !
777 // PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
779 SetWriteFileTypeToJPEG2000();
780 PixelWriteConverter->SetCompressJPEG2000UserData(
781 PixelWriteConverter->GetUserData(),
782 PixelWriteConverter->GetUserDataSize(),
787 // --------------------------------------------------------------
788 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
790 // if recognition code tells us we dealt with a LibIDO image
791 // we reproduce on disk the switch between lineNumber and columnNumber
792 // just before writting ...
793 /// \todo the best trick would be *change* the recognition code
794 /// but pb expected if user deals with, e.g. COMPLEX images
796 if ( WriteType == ACR_LIBIDO )
802 SetWriteToNoLibido();
804 // ----------------- End of Special Patch ----------------
809 SetWriteToRaw(); // modifies and pushes to the archive, when necessary
812 SetWriteToRGB(); // modifies and pushes to the archive, when necessary
817 if (WriteType == JPEG || WriteType == JPEG2000)
820 check = CheckWriteIntegrity(); // verifies length
824 check = FileInternal->Write(fileName,WriteType);
828 // RestoreWriteFileType();
829 // RestoreWriteMandatory();
831 // --------------------------------------------------------------
832 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
834 // ...and we restore the header to be Dicom Compliant again
835 // just after writting
836 RestoreWriteOfLibido();
837 // ----------------- End of Special Patch ----------------
842 //-----------------------------------------------------------------------------
845 * \brief Verifies the size of the user given PixelData
846 * @return true if check is successfull
848 bool FileHelper::CheckWriteIntegrity()
850 if ( PixelWriteConverter->GetUserData() )
852 int numberBitsAllocated = FileInternal->GetBitsAllocated();
853 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
855 gdcmWarningMacro( "numberBitsAllocated changed from "
856 << numberBitsAllocated << " to 16 "
857 << " for consistency purpose" );
858 numberBitsAllocated = 16;
861 size_t decSize = FileInternal->GetXSize()
862 * FileInternal->GetYSize()
863 * FileInternal->GetZSize()
864 * FileInternal->GetTSize()
865 * FileInternal->GetSamplesPerPixel()
866 * ( numberBitsAllocated / 8 );
867 size_t rgbSize = decSize;
868 if ( FileInternal->HasLUT() )
869 rgbSize = decSize * 3;
871 size_t userDataSize = PixelWriteConverter->GetUserDataSize();
875 if ( abs((long)(decSize-userDataSize))>1) // ignore padding zero
877 gdcmWarningMacro( "Data size (Raw) is incorrect. Should be "
879 << FileInternal->GetXSize() << " * "
880 << FileInternal->GetYSize() << " * "
881 << FileInternal->GetZSize() << " * "
882 << FileInternal->GetTSize() << " * "
883 << FileInternal->GetSamplesPerPixel() << " * "
884 << numberBitsAllocated / 8
891 if ( abs((long)(rgbSize-userDataSize))>1) // ignore padding zero
893 gdcmWarningMacro( "Data size (RGB) is incorrect. Should be "
894 << rgbSize << " / Found "
905 * \brief Updates the File to write RAW data (as opposed to RGB data)
906 * (modifies, when necessary, photochromatic interpretation,
907 * bits allocated, Pixels element VR)
908 * WARNING : if SetPhotometricInterpretationToMonochrome1() was called
909 * before Pixel Elements is modified :-(
911 void FileHelper::SetWriteToRaw()
913 if ( FileInternal->GetNumberOfScalarComponents() == 3
914 && !FileInternal->HasLUT() )
920 // 0x0028,0x0004 : Photometric Interpretation
921 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
922 if (FileInternal->HasLUT() )
924 photInt->SetString("PALETTE COLOR ");
928 if (GetPhotometricInterpretation() == 2)
929 photInt->SetString("MONOCHROME2 "); // 0 = Black
931 photInt->SetString("MONOCHROME1 "); // 0 = White !
934 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
935 PixelReadConverter->GetRawSize());
937 std::string vr = "OB";
938 if ( FileInternal->GetBitsAllocated()>8 )
940 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
942 // For non RAW data. Mainly JPEG/JPEG2000
943 if( WriteType == JPEG || WriteType == JPEG2000)
949 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
950 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
951 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
953 static_cast< uint32_t >(PixelWriteConverter->GetDataSize()) );
955 if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
957 ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );
960 Archive->Push(photInt);
961 Archive->Push(pixel);
969 * \brief Updates the File to write RGB data (as opposed to RAW data)
970 * (modifies, when necessary, photochromatic interpretation,
971 * samples per pixel, Planar configuration,
972 * bits allocated, bits stored, high bit -ACR 24 bits-
973 * Pixels element VR, pushes out the LUT, )
975 void FileHelper::SetWriteToRGB()
977 if ( FileInternal->GetNumberOfScalarComponents()==3 )
979 PixelReadConverter->BuildRGBImage();
981 DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
982 spp->SetString("3 "); // Don't drop trailing space
984 DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
985 planConfig->SetString("0 "); // Don't drop trailing space
987 DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
988 photInt->SetString("RGB "); // Don't drop trailing space
990 if ( PixelReadConverter->GetRGB() )
992 PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
993 PixelReadConverter->GetRGBSize());
997 PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
998 PixelReadConverter->GetRawSize());
1001 std::string vr = "OB";
1002 if ( FileInternal->GetBitsAllocated()>8 )
1004 if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files
1007 CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
1008 pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
1009 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
1010 pixel->SetLength(PixelWriteConverter->GetDataSize());
1013 Archive->Push(planConfig);
1014 Archive->Push(photInt);
1015 Archive->Push(pixel);
1018 planConfig->Delete();
1023 Archive->Push(0x0028,0x1101);
1024 Archive->Push(0x0028,0x1102);
1025 Archive->Push(0x0028,0x1103);
1026 Archive->Push(0x0028,0x1201);
1027 Archive->Push(0x0028,0x1202);
1028 Archive->Push(0x0028,0x1203);
1030 // push out Palette Color Lookup Table UID, if any
1031 Archive->Push(0x0028,0x1199);
1033 // For old '24 Bits' ACR-NEMA
1034 // Thus, we have a RGB image and the bits allocated = 24 and
1035 // samples per pixels = 1 (in the read file)
1036 if ( FileInternal->GetBitsAllocated()==24 )
1038 DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
1039 bitsAlloc->SetString("8 ");
1041 DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
1042 bitsStored->SetString("8 ");
1044 DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
1045 highBit->SetString("7 ");
1047 Archive->Push(bitsAlloc);
1048 Archive->Push(bitsStored);
1049 Archive->Push(highBit);
1051 bitsAlloc->Delete();
1052 bitsStored->Delete();
1063 * \brief Restore the File write mode
1065 void FileHelper::RestoreWrite()
1067 Archive->Restore(0x0028,0x0002);
1068 Archive->Restore(0x0028,0x0004);
1070 Archive->Restore(0x0028,0x0006);
1071 Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1073 // For old ACR-NEMA (24 bits problem)
1074 Archive->Restore(0x0028,0x0100);
1075 Archive->Restore(0x0028,0x0101);
1076 Archive->Restore(0x0028,0x0102);
1079 Archive->Restore(0x0028,0x1101);
1080 Archive->Restore(0x0028,0x1102);
1081 Archive->Restore(0x0028,0x1103);
1082 Archive->Restore(0x0028,0x1201);
1083 Archive->Restore(0x0028,0x1202);
1084 Archive->Restore(0x0028,0x1203);
1086 // For the Palette Color Lookup Table UID
1087 Archive->Restore(0x0028,0x1203);
1089 // group 0002 may be pushed out for ACR-NEMA writting purposes
1090 Archive->Restore(0x0002,0x0000);
1091 Archive->Restore(0x0002,0x0001);
1092 Archive->Restore(0x0002,0x0002);
1093 Archive->Restore(0x0002,0x0003);
1094 Archive->Restore(0x0002,0x0010);
1095 Archive->Restore(0x0002,0x0012);
1096 Archive->Restore(0x0002,0x0013);
1097 Archive->Restore(0x0002,0x0016);
1098 Archive->Restore(0x0002,0x0100);
1099 Archive->Restore(0x0002,0x0102);
1104 * \brief Pushes out the whole group 0002
1105 * FIXME : better, set a flag to tell the writer not to write it ...
1106 * FIXME : method should probably have an other name !
1107 * SetWriteFileTypeToACR is NOT opposed to
1108 * SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1110 void FileHelper::SetWriteFileTypeToACR()
1112 Archive->Push(0x0002,0x0000);
1113 Archive->Push(0x0002,0x0001);
1114 Archive->Push(0x0002,0x0002);
1115 Archive->Push(0x0002,0x0003);
1116 Archive->Push(0x0002,0x0010);
1117 Archive->Push(0x0002,0x0012);
1118 Archive->Push(0x0002,0x0013);
1119 Archive->Push(0x0002,0x0016);
1120 Archive->Push(0x0002,0x0100);
1121 Archive->Push(0x0002,0x0102);
1125 * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1127 void FileHelper::SetWriteFileTypeToJPEG2000()
1129 std::string ts = Util::DicomString(
1130 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1132 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1140 * \brief Sets in the File the TransferSyntax to 'JPEG'
1142 void FileHelper::SetWriteFileTypeToJPEG()
1144 std::string ts = Util::DicomString(
1145 Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGLosslessProcess14_1) );
1147 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1155 * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"
1157 void FileHelper::SetWriteFileTypeToExplicitVR()
1159 std::string ts = Util::DicomString(
1160 Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1162 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1169 * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"
1171 void FileHelper::SetWriteFileTypeToImplicitVR()
1173 std::string ts = Util::DicomString(
1174 Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1176 DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1183 * \brief Set the Write not to Libido format
1185 void FileHelper::SetWriteToLibido()
1187 DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1188 DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1190 if ( oldRow && oldCol )
1192 std::string rows, columns;
1194 DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1195 DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1197 newRow->Copy(oldCol);
1198 newCol->Copy(oldRow);
1200 newRow->SetString(oldCol->GetString());
1201 newCol->SetString(oldRow->GetString());
1203 Archive->Push(newRow);
1204 Archive->Push(newCol);
1210 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1211 libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1212 Archive->Push(libidoCode);
1213 libidoCode->Delete();
1217 * \brief Set the Write not to No Libido format
1219 void FileHelper::SetWriteToNoLibido()
1221 DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1224 if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1226 DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1227 libidoCode->SetString("");
1228 Archive->Push(libidoCode);
1229 libidoCode->Delete();
1235 * \brief Restore the Write format
1237 void FileHelper::RestoreWriteOfLibido()
1239 Archive->Restore(0x0028,0x0010);
1240 Archive->Restore(0x0028,0x0011);
1241 Archive->Restore(0x0008,0x0010);
1243 // Restore 'LibIDO-special' entries, if any
1244 Archive->Restore(0x0028,0x0015);
1245 Archive->Restore(0x0028,0x0016);
1246 Archive->Restore(0x0028,0x0017);
1247 Archive->Restore(0x0028,0x00199);
1251 * \brief Duplicates a DataEntry or creates it.
1252 * @param group Group number of the Entry
1253 * @param elem Element number of the Entry
1254 * @param vr Value Representation of the Entry
1255 * \return pointer to the new Bin Entry (NULL when creation failed).
1257 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1260 DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1263 if ( oldE && vr != GDCM_VRUNKNOWN )
1264 if ( oldE->GetVR() != vr )
1269 newE = DataEntry::New(group, elem, vr);
1274 newE = GetFile()->NewDataEntry(group, elem, vr);
1281 * \brief This method is called automatically, just before writting
1282 * in order to produce a 'True Dicom V3' image.
1284 * We cannot know *how* the user made the File :
1285 * (reading an old ACR-NEMA file or a not very clean DICOM file ...)
1286 * Just before writting :
1287 * - we check the Entries
1288 * - we create the mandatory entries if they are missing
1289 * - we modify the values if necessary
1290 * - we push the sensitive entries to the Archive
1291 * The writing process will restore the entries as they where before
1292 * entering FileHelper::CheckMandatoryElements, so the user will always
1293 * see the entries just as they were before he decided to write.
1296 * - Entries whose type is 1 are mandatory, with a mandatory value
1297 * - Entries whose type is 1c are mandatory-inside-a-Sequence,
1298 * with a mandatory value
1299 * - Entries whose type is 2 are mandatory, with an optional value
1300 * - Entries whose type is 2c are mandatory-inside-a-Sequence,
1301 * with an optional value
1302 * - Entries whose type is 3 are optional
1305 * - warn the user if we had to add some entries :
1306 * even if a mandatory entry is missing, we add it, with a default value
1307 * (we don't want to give up the writting process if user forgot to
1308 * specify Lena's Patient ID, for instance ...)
1309 * - read the whole PS 3.3 Part of DICOM (890 pages)
1310 * and write a *full* checker (probably one method per Modality ...)
1311 * Any contribution is welcome.
1312 * - write a user callable full checker, to allow post reading
1313 * and/or pre writting image consistency check.
1316 /* -------------------------------------------------------------------------------------
1317 To be moved to User's guide / WIKI ?
1319 We have to deal with 4 *very* different cases :
1320 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1322 -2) user modified the pixels of an existing image.
1324 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1326 -4) user modified/added some tags *without processing* the pixels (anonymization...)
1327 UNMODIFIED_PIXELS_IMAGE
1328 -Probabely some more to be added.
1329 --> Set it with FileHelper::SetContentType(int);
1331 GDCM_NAME_SPACE::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1334 0008 0012 Instance Creation Date
1335 0008 0013 Instance Creation Time
1336 0008 0018 SOP Instance UID
1337 are *always* created with the current values; user has *no* possible intervention on
1340 'Serie Instance UID'(0x0020,0x000e)
1341 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1342 created if it doesn't.
1343 The user is allowed to create his own Series/Studies,
1344 keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1346 The user shouldn't add any image to a 'Manufacturer Serie'
1347 but there is no way no to allow him to do that
1349 None of the 'shadow elements' are droped out.
1353 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1356 'Media Storage SOP Class UID' (0x0002,0x0002)
1357 'SOP Class UID' (0x0008,0x0016) are set to
1358 [Secondary Capture Image Storage]
1359 'Image Type' (0x0008,0x0008) is forced to "DERIVED\PRIMARY"
1360 Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1363 If 'SOP Class UID' exists in the native image ('true DICOM' image)
1364 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1365 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1366 whose value is the original 'SOP Class UID'
1367 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1368 whose value is the original 'SOP Class UID'
1370 3) TO DO : find a trick to allow user to pass to the writter the list of the Dicom images
1371 or the Series, (or the Study ?) he used to created his image
1372 (MIP, MPR, cartography image, ...)
1373 These info should be stored (?)
1374 0008 1110 SQ 1 Referenced Study Sequence
1375 0008 1115 SQ 1 Referenced Series Sequence
1376 0008 1140 SQ 1 Referenced Image Sequence
1378 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1379 'Media Storage SOP Class UID' (0x0002,0x0002)
1380 'SOP Class UID' (0x0008,0x0016)
1381 'Image Type' (0x0008,0x0008)
1382 'Conversion Type' (0x0008,0x0064)
1385 Bellow follows the full description (hope so !) of the consistency checks performed
1386 by GDCM_NAME_SPACE::FileHelper::CheckMandatoryElements()
1389 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1390 -->'SOP Class UID' (0x0008,0x0016) are defaulted to
1391 [Secondary Capture Image Storage]
1392 --> 'Image Type' (0x0008,0x0008)
1393 is forced to "DERIVED\PRIMARY"
1394 (The written image is no longer an 'ORIGINAL' one)
1395 Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1397 --> Conversion Type (0x0008,0x0064)
1398 is defaulted to 'SYN' (Synthetic Image)
1399 when *he* knows he created his own image ex nihilo
1401 --> 'Modality' (0x0008,0x0060)
1402 is defaulted to "OT" (other) if missing.
1403 (a fully user created image belongs to *no* modality)
1405 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1406 --> 'Implementation Class UID' (0x0002,0x0012)
1407 are automatically generated; no user intervention possible
1409 --> 'Serie Instance UID'(0x0020,0x000e)
1410 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1411 created if it doesn't.
1412 The user is allowed to create his own Series/Studies,
1413 keeping the same 'Serie Instance UID' / 'Study Instance UID'
1416 The user shouldn't add any image to a 'Manufacturer Serie'
1417 but there is no way no to allowed him to do that
1419 --> If 'SOP Class UID' exists in the native image ('true DICOM' image)
1420 we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1422 --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1423 whose value is the original 'SOP Class UID'
1424 --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1425 whose value is the original 'SOP Class UID'
1427 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1428 --> Pixel Spacing (0x0028,0x0030) is defaulted to "1.0\1.0"
1429 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1431 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1433 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1435 --> Study Date, Study Time are defaulted to current Date and Time
1436 (they remain unchanged if they exist)
1438 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from
1439 Image Orientation (Patient) : (0020|0037) or from
1440 Image Orientation (RET) : (0020 0035)
1442 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1443 are created, with empty value if there are missing.
1445 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1446 are defaulted with a 'gdcm' value.
1448 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1449 --> Referring Physician's Name (Type 2)
1450 are created, with empty value if there are missing.
1452 -------------------------------------------------------------------------------------*/
1454 void FileHelper::CheckMandatoryElements()
1456 std::string sop = Util::CreateUniqueUID();
1458 // --------------------- For Meta Elements ---------------------
1459 // just to remember : 'official' 0002 group
1460 if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1462 // Group 000002 (Meta Elements) already pushed out
1464 //0002 0000 UL 1 Meta Group Length
1465 //0002 0001 OB 1 File Meta Information Version
1466 //0002 0002 UI 1 Media Storage SOP Class UID
1467 //0002 0003 UI 1 Media Storage SOP Instance UID
1468 //0002 0010 UI 1 Transfer Syntax UID
1469 //0002 0012 UI 1 Implementation Class UID
1470 //0002 0013 SH 1 Implementation Version Name
1471 //0002 0016 AE 1 Source Application Entity Title
1472 //0002 0100 UI 1 Private Information Creator
1473 //0002 0102 OB 1 Private Information
1475 // Push out 'ACR-NEMA-special' entries, if any
1476 Archive->Push(0x0008,0x0001); // Length to End
1477 Archive->Push(0x0008,0x0010); // Recognition Code
1478 Archive->Push(0x0028,0x0005); // Image Dimension
1480 // Create them if not found
1481 // Always modify the value
1482 // Push the entries to the archive.
1483 CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1485 DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1486 e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1488 e_0002_0001->SetLength(2);
1489 Archive->Push(e_0002_0001);
1490 e_0002_0001->Delete();
1492 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1494 // we keep the original 'Media Storage SOP Class UID', we default it if missing
1495 CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1499 // It's *not* an image comming straight from a source. We force
1500 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1501 CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1504 // 'Media Storage SOP Instance UID'
1505 CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1507 // 'Implementation Class UID'
1508 // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1509 // seems to be Root UID + 4 digits (?)
1510 CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1512 // 'Implementation Version Name'
1513 std::string version = "GDCM ";
1514 version += Util::GetVersion();
1515 CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1518 // --------------------- For DataSet ---------------------
1520 // check whether 0018|0015 [CS] [Body Part Examined] value is UPPER CASE
1521 // (avoid dciodvfy to complain!)
1522 DataEntry *e_0018_0015 = FileInternal->GetDataEntry(0x0018, 0x0015);
1525 std::string bodyPartExamined = e_0018_0015->GetString();
1526 std::transform(bodyPartExamined.begin(), bodyPartExamined.end(), bodyPartExamined.begin(),
1527 (int(*)(int)) toupper);
1528 CopyMandatoryEntry(0x0018,0x0015,bodyPartExamined,"CS");
1531 if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1533 // If 'SOP Class UID' and 'SOP Instance UID' exist ('true DICOM' image)
1534 // we create the 'Source Image Sequence' SeqEntry
1535 // to hold informations about the Source Image
1537 // 'SOP Instance UID'
1538 DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1540 DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1541 if ( e_0008_0016 && e_0008_0018)
1543 // Create 'Source Image Sequence' SeqEntry
1544 SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1545 SQItem *sqi = SQItem::New(1);
1547 // create 'Referenced SOP Class UID' from 'SOP Class UID'
1549 DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1550 e_0008_1150->SetString( e_0008_0016->GetString());
1551 sqi->AddEntry(e_0008_1150);
1552 e_0008_1150->Delete();
1554 // create 'Referenced SOP Instance UID' from 'SOP Instance UID'
1555 // DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1557 DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1558 e_0008_1155->SetString( e_0008_0018->GetString());
1559 sqi->AddEntry(e_0008_1155);
1560 e_0008_1155->Delete();
1562 sis->AddSQItem(sqi,1);
1565 // temporarily replaces any previous 'Source Image Sequence'
1568 // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1570 if ( ContentType == FILTERED_IMAGE) // the user *knows* he just modified the pixels
1572 DataEntry *e_0008_0008 = FileInternal->GetDataEntry(0x0008, 0x0008);
1575 std::string imageType = e_0008_0008->GetString();
1576 std::string::size_type p = imageType.find("ORIGINAL");
1577 if (p == 0) // image is ORIGINAL one
1579 // the image is no longer an 'Original' one
1580 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1582 // if Image Type was not ORIGINAL\..., we keep it.
1584 else // 0008_0008 was missing, wee add it.
1586 CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");
1592 if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1594 // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1595 CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");
1599 // It's *not* an image comming straight from a source. We force
1600 // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
1601 CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");
1604 Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1605 // Push out 'LibIDO-special' entries, if any
1606 Archive->Push(0x0028,0x0015);
1607 Archive->Push(0x0028,0x0016);
1608 Archive->Push(0x0028,0x0017);
1609 Archive->Push(0x0028,0x0198); // very old versions
1610 Archive->Push(0x0028,0x0199);
1612 // Replace deprecated 0028 0012 US Planes
1613 // by new 0028 0008 IS Number of Frames
1615 ///\todo : find if there is a rule!
1616 DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1619 CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1620 Archive->Push(0x0028,0x0012);
1623 // Deal with the pb of (Bits Stored = 12)
1624 // - we're gonna write the image as Bits Stored = 16
1625 if ( FileInternal->GetEntryString(0x0028,0x0100) == "12")
1627 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1630 // Check if user wasn't drunk ;-)
1632 std::ostringstream s;
1633 // check 'Bits Allocated' vs decent values
1634 int nbBitsAllocated = FileInternal->GetBitsAllocated();
1636 // We allow now to deal with 'non standard' 64 bits 'real' values
1638 if ( (nbBitsAllocated == 0 || nbBitsAllocated > 64) // was 32
1639 || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1641 CopyMandatoryEntry(0x0028,0x0100,"16","US");
1642 gdcmWarningMacro("(0028,0100) changed from "
1643 << nbBitsAllocated << " to 16 for consistency purpose");
1644 nbBitsAllocated = 16;
1646 // check 'Bits Stored' vs 'Bits Allocated'
1647 int nbBitsStored = FileInternal->GetBitsStored();
1648 if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1651 s << nbBitsAllocated;
1652 CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1653 gdcmWarningMacro("(0028,0101) changed from "
1654 << nbBitsStored << " to " << nbBitsAllocated
1655 << " for consistency purpose" );
1656 nbBitsStored = nbBitsAllocated;
1658 // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1659 int highBitPosition = FileInternal->GetHighBitPosition();
1660 if ( highBitPosition == 0 ||
1661 highBitPosition > nbBitsAllocated-1 ||
1662 highBitPosition < nbBitsStored-1 )
1665 s << nbBitsStored - 1;
1666 CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1667 gdcmWarningMacro("(0028,0102) changed from "
1668 << highBitPosition << " to " << nbBitsAllocated-1
1669 << " for consistency purpose");
1672 // check Pixel Representation (default it as 0 -unsigned-)
1674 DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1677 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1678 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1682 int sign = (int)e_0028_0103->GetValue(0);
1683 if (sign !=1 && sign !=0)
1685 gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1686 CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1690 std::string pixelAspectRatio = FileInternal->GetEntryString(0x0028,0x0034);
1691 if ( pixelAspectRatio == GDCM_UNFOUND ) // avoid conflict with pixelSpacing !
1693 std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1694 if ( pixelSpacing == GDCM_UNFOUND )
1696 pixelSpacing = "1.0\\1.0";
1697 // if missing, Pixel Spacing forced to "1.0\1.0"
1698 CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1701 // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1702 // --> This one is the *legal* one !
1703 if ( ContentType != USER_OWN_IMAGE)
1704 // we write it only when we are *sure* the image comes from
1705 // an imager (see also 0008,0x0064)
1706 CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1709 ///Exact meaning of RETired fields
1711 // See page 73 of ACR-NEMA_300-1988.pdf !
1713 // 0020,0020 : Patient Orientation :
1714 Patient direction of the first row and
1715 column of the images. The first entry id the direction of the raws, given by the
1716 direction of the last pixel in the first row from the first pixel in tha row.
1717 the second entry is the direction of the columns, given by the direction of the
1718 last pixel in the first column from the first pixel in that column.
1719 L : Left, F : Feet, A : Anterior, P : Posterior.
1720 Up to 3 letters can be used in combination to indicate oblique planes.
1722 //0020,0030 Image Position (RET)
1723 x,y,z coordinates im mm of the first pixel in the image
1725 // 0020,0035 Image Orientation (RET)
1726 Direction cosines of the R axis of the image system with respect to the
1727 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1728 the image system with respect to the same axes
1730 //0020,0050 Location
1731 An image location reference, standard for the modality (such as CT bed position),
1732 used to indicate position. Calculation of position for other purposes
1733 is only from (0020,0030) and (0020,0035)
1737 // if imagePositionPatient not found, default it with imagePositionRet, if any
1738 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1740 std::string imagePositionRet = FileInternal->GetEntryString(0x0020,0x0030);
1741 std::string imageOrientationRet = FileInternal->GetEntryString(0x0020,0x0035);
1742 std::string imagePositionPatient = FileInternal->GetEntryString(0x0020,0x0032);
1743 std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1745 if( imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1746 && imagePositionRet != GDCM_UNFOUND && imageOrientationRet != GDCM_UNFOUND)
1748 CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1749 Archive->Push(0x0020,0x0030);
1750 CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1751 Archive->Push(0x0020,0x0035);
1755 // Samples Per Pixel (type 1) : default to grayscale
1756 CheckMandatoryEntry(0x0028,0x0002,"1","US");
1758 // --- Check UID-related Entries ---
1760 // At the end, not to overwrite the original ones,
1761 // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
1762 // 'SOP Instance UID'
1763 CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1765 if ( ContentType == USER_OWN_IMAGE)
1767 gdcmDebugMacro( "USER_OWN_IMAGE (2)");
1769 // Other possible values are :
1770 // See PS 3.3, Page 408
1772 // DV = Digitized Video
1773 // DI = Digital Interface
1774 // DF = Digitized Film
1775 // WSD = Workstation
1776 // SD = Scanned Document
1777 // SI = Scanned Image
1779 // SYN = Synthetic Image
1781 CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1784 if ( ContentType == CREATED_IMAGE)
1786 /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1791 // ---- The user will never have to take any action on the following ----
1793 // new value for 'SOP Instance UID'
1794 //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1796 // Instance Creation Date
1797 const std::string &date = Util::GetCurrentDate();
1798 CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1800 // Instance Creation Time
1801 const std::string &time = Util::GetCurrentTime();
1802 CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1805 CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1807 CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1810 //CopyMandatoryEntry(0x0008,0x0050,"");
1811 CheckMandatoryEntry(0x0008,0x0050,"","SH");
1814 // ----- Add Mandatory Entries if missing ---
1815 // Entries whose type is 1 are mandatory, with a mandatory value
1816 // Entries whose type is 1c are mandatory-inside-a-Sequence,
1817 // with a mandatory value
1818 // Entries whose type is 2 are mandatory, with an optional value
1819 // Entries whose type is 2c are mandatory-inside-a-Sequence,
1820 // with an optional value
1821 // Entries whose type is 3 are optional
1823 // 'Study Instance UID'
1824 // Keep the value if exists
1825 // The user is allowed to create his own Study,
1826 // keeping the same 'Study Instance UID' for various images
1827 // The user may add images to a 'Manufacturer Study',
1828 // adding new Series to an already existing Study
1829 CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1831 // 'Serie Instance UID'
1832 // Keep the value if exists
1833 // The user is allowed to create his own Series,
1834 // keeping the same 'Serie Instance UID' for various images
1835 // The user shouldn't add any image to a 'Manufacturer Serie'
1836 // but there is no way no to prevent him for doing that
1837 CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1840 CheckMandatoryEntry(0x0020,0x0010,"","SH");
1843 CheckMandatoryEntry(0x0020,0x0011,"","IS");
1846 CheckMandatoryEntry(0x0020,0x0013,"","IS");
1848 // Patient Orientation
1849 // Can be computed from (0020|0037) : Image Orientation (Patient)
1850 GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New();
1851 std::string ori = o->GetOrientation ( FileInternal );
1853 if (ori != "\\" && ori != GDCM_UNFOUND)
1854 CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1856 CheckMandatoryEntry(0x0020,0x0020,"","CS");
1858 // Default Patient Position to HFS
1859 CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1861 // Modality : if missing we set it to 'OTher'
1862 CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1864 // Manufacturer : if missing we set it to 'GDCM Factory'
1865 CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1867 // Institution Name : if missing we set it to 'GDCM Hospital'
1868 CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1870 // Patient's Name : if missing, we set it to 'GDCM^Patient'
1871 CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1873 // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1874 CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1876 // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1877 CheckMandatoryEntry(0x0010,0x0030,"","DA");
1879 // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1880 CheckMandatoryEntry(0x0010,0x0040,"","CS");
1882 // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1883 CheckMandatoryEntry(0x0008,0x0090,"","PN");
1886 // Deal with element 0x0000 (group length) of each group.
1887 // First stage : get all the different Groups
1890 DocEntry *d = FileInternal->GetFirstEntry();
1893 grHT[d->GetGroup()] = 0;
1894 d=FileInternal->GetNextEntry();
1896 // Second stage : add the missing ones (if any)
1897 for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)
1899 CheckMandatoryEntry(it->first, 0x0000, "0");
1901 // Third stage : update all 'zero level' groups length
1905 if (PhotometricInterpretation == 1)
1910 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1912 DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1915 //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1916 entry = DataEntry::New(group,elem,vr);
1917 entry->SetString(value);
1918 Archive->Push(entry);
1923 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
1924 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1926 //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1927 DataEntry *entry = DataEntry::New(group,elem,vr);
1928 entry->SetString(value);
1929 Archive->Push(entry);
1933 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1935 DataEntry *entry = CopyDataEntry(group,elem,vr);
1936 entry->SetString(value);
1937 Archive->Push(entry);
1942 * \brief Restore in the File the initial group 0002
1944 void FileHelper::RestoreWriteMandatory()
1946 // group 0002 may be pushed out for ACR-NEMA writting purposes
1947 Archive->Restore(0x0002,0x0000);
1948 Archive->Restore(0x0002,0x0001);
1949 Archive->Restore(0x0002,0x0002);
1950 Archive->Restore(0x0002,0x0003);
1951 Archive->Restore(0x0002,0x0010);
1952 Archive->Restore(0x0002,0x0012);
1953 Archive->Restore(0x0002,0x0013);
1954 Archive->Restore(0x0002,0x0016);
1955 Archive->Restore(0x0002,0x0100);
1956 Archive->Restore(0x0002,0x0102);
1958 // FIXME : Check if none is missing !
1960 Archive->Restore(0x0008,0x0012);
1961 Archive->Restore(0x0008,0x0013);
1962 Archive->Restore(0x0008,0x0016);
1963 Archive->Restore(0x0008,0x0018);
1964 Archive->Restore(0x0008,0x0060);
1965 Archive->Restore(0x0008,0x0070);
1966 Archive->Restore(0x0008,0x0080);
1967 Archive->Restore(0x0008,0x0090);
1968 Archive->Restore(0x0008,0x2112);
1970 Archive->Restore(0x0010,0x0010);
1971 Archive->Restore(0x0010,0x0030);
1972 Archive->Restore(0x0010,0x0040);
1974 Archive->Restore(0x0020,0x000d);
1975 Archive->Restore(0x0020,0x000e);
1979 * \brief CallStartMethod
1981 void FileHelper::CallStartMethod()
1985 CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1989 * \brief CallProgressMethod
1991 void FileHelper::CallProgressMethod()
1993 CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1997 * \brief CallEndMethod
1999 void FileHelper::CallEndMethod()
2002 CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
2005 //-----------------------------------------------------------------------------
2008 * \brief Factorization for various forms of constructors.
2010 void FileHelper::Initialize()
2013 ContentType = USER_OWN_IMAGE;
2015 WriteMode = WMODE_RAW;
2016 WriteType = ExplicitVR;
2018 PhotometricInterpretation = 2; // Black = 0
2020 PixelReadConverter = new PixelReadConvert;
2021 PixelWriteConverter = new PixelWriteConvert;
2022 Archive = new DocEntryArchive( FileInternal );
2024 KeepOverlays = false;
2028 * \brief Reads/[decompresses] the pixels,
2029 * *without* making RGB from Palette Colors
2030 * @return the pixels area, whatever its type
2031 * (uint8_t is just for prototyping : feel free to Cast it)
2033 uint8_t *FileHelper::GetRaw()
2035 PixelReadConverter->SetUserFunction( UserFunction );
2037 uint8_t *raw = PixelReadConverter->GetRaw();
2040 // The Raw image migth not be loaded yet:
2041 std::ifstream *fp = FileInternal->OpenFile();
2042 PixelReadConverter->ReadAndDecompressPixelData( fp );
2044 FileInternal->CloseFile();
2046 raw = PixelReadConverter->GetRaw();
2049 gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
2057 * \brief Deal with Grey levels i.e. re-arange them
2058 * to have low values = dark, high values = bright
2060 void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize)
2062 uint32_t i; // to please M$VC6
2065 // Number of Bits Allocated for storing a Pixel is defaulted to 16
2066 // when absent from the file.
2067 int bitsAllocated = FileInternal->GetBitsAllocated();
2068 if ( bitsAllocated == 0 )
2073 else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12)
2077 // Number of "Bits Stored", defaulted to number of "Bits Allocated"
2078 // when absent from the file.
2079 int bitsStored = FileInternal->GetBitsStored();
2080 if ( bitsStored == 0 )
2082 bitsStored = bitsAllocated;
2085 if (!FileInternal->IsSignedPixelData())
2087 if ( bitsAllocated == 8 )
2089 uint8_t *deb = (uint8_t *)raw;
2090 for (i=0; i<rawSize; i++)
2098 if ( bitsAllocated == 16 )
2101 for (j=0; j<bitsStored-1; j++)
2103 mask = (mask << 1) +1; // will be fff when BitsStored=12
2106 uint16_t *deb = (uint16_t *)raw;
2107 for (i=0; i<rawSize/2; i++)
2117 if ( bitsAllocated == 8 )
2119 uint8_t smask8 = 255;
2120 uint8_t *deb = (uint8_t *)raw;
2121 for (i=0; i<rawSize; i++)
2123 *deb = smask8 - *deb;
2128 if ( bitsAllocated == 16 )
2130 uint16_t smask16 = 65535;
2131 uint16_t *deb = (uint16_t *)raw;
2132 for (i=0; i<rawSize/2; i++)
2134 *deb = smask16 - *deb;
2142 //-----------------------------------------------------------------------------
2144 * \brief Prints the FileInternal + info on PixelReadConvertor
2145 * @param os ostream we want to print in
2146 * @param indent (unused)
2148 void FileHelper::Print(std::ostream &os, std::string const &)
2150 FileInternal->SetPrintLevel(PrintLevel);
2151 FileInternal->Print(os);
2153 if ( FileInternal->IsReadable() )
2155 PixelReadConverter->SetPrintLevel(PrintLevel);
2156 PixelReadConverter->Print(os);
2160 //-----------------------------------------------------------------------------
2161 } // end namespace gdcm
2164 /* Probabely something to be added to use Rescale Slope/Intercept
2165 Have a look at ITK code !
2167 // Internal function to rescale pixel according to Rescale Slope/Intercept
2168 template<class TBuffer, class TSource>
2169 void RescaleFunction(TBuffer* buffer, TSource *source,
2170 double slope, double intercept, size_t size)
2172 size /= sizeof(TSource);
2174 if (slope != 1.0 && intercept != 0.0)
2176 // Duff's device. Instead of this code:
2178 // for(unsigned int i=0; i<size; i++)
2180 // buffer[i] = (TBuffer)(source[i]*slope + intercept);
2183 // use Duff's device which exploits "fall through"
2184 register size_t n = (size + 7) / 8;
2187 case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2188 case 7: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2189 case 6: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2190 case 5: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2191 case 4: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2192 case 3: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2193 case 2: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2194 case 1: *buffer++ = (TBuffer)((*source++)*slope + intercept);
2198 else if (slope == 1.0 && intercept != 0.0)
2200 // Duff's device. Instead of this code:
2202 // for(unsigned int i=0; i<size; i++)
2204 // buffer[i] = (TBuffer)(source[i] + intercept);
2207 // use Duff's device which exploits "fall through"
2208 register size_t n = (size + 7) / 8;
2211 case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2212 case 7: *buffer++ = (TBuffer)(*source++ + intercept);
2213 case 6: *buffer++ = (TBuffer)(*source++ + intercept);
2214 case 5: *buffer++ = (TBuffer)(*source++ + intercept);
2215 case 4: *buffer++ = (TBuffer)(*source++ + intercept);
2216 case 3: *buffer++ = (TBuffer)(*source++ + intercept);
2217 case 2: *buffer++ = (TBuffer)(*source++ + intercept);
2218 case 1: *buffer++ = (TBuffer)(*source++ + intercept);
2222 else if (slope != 1.0 && intercept == 0.0)
2224 // Duff's device. Instead of this code:
2226 // for(unsigned int i=0; i<size; i++)
2228 // buffer[i] = (TBuffer)(source[i]*slope);
2231 // use Duff's device which exploits "fall through"
2232 register size_t n = (size + 7) / 8;
2235 case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2236 case 7: *buffer++ = (TBuffer)((*source++)*slope);
2237 case 6: *buffer++ = (TBuffer)((*source++)*slope);
2238 case 5: *buffer++ = (TBuffer)((*source++)*slope);
2239 case 4: *buffer++ = (TBuffer)((*source++)*slope);
2240 case 3: *buffer++ = (TBuffer)((*source++)*slope);
2241 case 2: *buffer++ = (TBuffer)((*source++)*slope);
2242 case 1: *buffer++ = (TBuffer)((*source++)*slope);
2248 // Duff's device. Instead of this code:
2250 // for(unsigned int i=0; i<size; i++)
2252 // buffer[i] = (TBuffer)(source[i]);
2255 // use Duff's device which exploits "fall through"
2256 register size_t n = (size + 7) / 8;
2259 case 0: do { *buffer++ = (TBuffer)(*source++);
2260 case 7: *buffer++ = (TBuffer)(*source++);
2261 case 6: *buffer++ = (TBuffer)(*source++);
2262 case 5: *buffer++ = (TBuffer)(*source++);
2263 case 4: *buffer++ = (TBuffer)(*source++);
2264 case 3: *buffer++ = (TBuffer)(*source++);
2265 case 2: *buffer++ = (TBuffer)(*source++);
2266 case 1: *buffer++ = (TBuffer)(*source++);
2273 template<class TSource>
2274 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2275 void* buffer, TSource *source,
2276 double slope, double intercept, size_t size)
2280 case ImageIOBase::UCHAR:
2281 RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2283 case ImageIOBase::CHAR:
2284 RescaleFunction( (char *)buffer, source, slope, intercept, size);
2286 case ImageIOBase::USHORT:
2287 RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2289 case ImageIOBase::SHORT:
2290 RescaleFunction( (short *)buffer, source, slope, intercept, size);
2292 case ImageIOBase::UINT:
2293 RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2295 case ImageIOBase::INT:
2296 RescaleFunction( (int *)buffer, source, slope, intercept, size);
2298 case ImageIOBase::FLOAT:
2299 RescaleFunction( (float *)buffer, source, slope, intercept, size);
2301 case ImageIOBase::DOUBLE:
2302 RescaleFunction( (double *)buffer, source, slope, intercept, size);
2305 ::itk::OStringStream message;
2306 message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2307 ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);