1 /*=========================================================================
4 Module: $RCSfile: gdcmFile.cxx,v $
6 Date: $Date: 2004/10/12 09:59:45 $
7 Version: $Revision: 1.140 $
9 Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10 l'Image). All rights reserved. See Doc/License.txt or
11 http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
13 This software is distributed WITHOUT ANY WARRANTY; without even
14 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 PURPOSE. See the above copyright notices for more information.
17 =========================================================================*/
20 #include "gdcmDebug.h"
24 typedef std::pair<TagDocEntryHT::iterator,TagDocEntryHT::iterator> IterHT;
26 //-------------------------------------------------------------------------
27 // Constructor / Destructor
29 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
30 * file (Header only deals with the ... header)
31 * Opens (in read only and when possible) an existing file and checks
32 * for DICOM compliance. Returns NULL on failure.
33 * It will be up to the user to load the pixels into memory
34 * (see GetImageData, GetImageDataRaw)
35 * \note the in-memory representation of all available tags found in
36 * the DICOM header is post-poned to first header information access.
37 * This avoid a double parsing of public part of the header when
38 * user sets an a posteriori shadow dictionary (efficiency can be
39 * seen as a side effect).
40 * @param header already built Header
42 File::File(Header *header)
44 HeaderInternal = header;
50 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
51 * file (Header only deals with the ... header)
52 * Opens (in read only and when possible) an existing file and checks
53 * for DICOM compliance. Returns NULL on failure.
54 * It will be up to the user to load the pixels into memory
55 * (see GetImageData, GetImageDataRaw)
56 * \note the in-memory representation of all available tags found in
57 * the DICOM header is post-poned to first header information access.
58 * This avoid a double parsing of public part of the header when
59 * one sets an a posteriori shadow dictionary (efficiency can be
60 * seen as a side effect).
61 * @param filename file to be opened for parsing
63 File::File(std::string const & filename )
65 HeaderInternal = new Header( filename );
71 * \brief Factorization for various forms of constructors.
73 void File::Initialise()
75 if ( HeaderInternal->IsReadable() )
77 ImageDataSizeRaw = ComputeDecompressedPixelDataSizeFromHeader();
78 if ( HeaderInternal->HasLUT() )
80 ImageDataSize = 3 * ImageDataSizeRaw;
84 ImageDataSize = ImageDataSizeRaw;
90 // Just in case some access to a Header element requires disk access:
91 FILE* fp = HeaderInternal->OpenFile();
92 // Number of Bits Allocated for storing a Pixel is defaulted to 16
93 // when absent from the header.
94 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
95 if ( numberBitsAllocated == 0 )
97 numberBitsAllocated = 16;
99 PixelConverter.SetBitsAllocated( numberBitsAllocated );
101 // Number of "Bits Stored" defaulted to number of "Bits Allocated"
102 // when absent from the header.
103 int numberBitsStored = HeaderInternal->GetBitsStored();
104 if ( numberBitsStored == 0 )
106 numberBitsStored = numberBitsAllocated;
108 PixelConverter.SetBitsStored( numberBitsStored );
111 int highBitPosition = HeaderInternal->GetHighBitPosition();
112 if ( highBitPosition == 0 )
114 highBitPosition = numberBitsAllocated - 1;
116 PixelConverter.SetHighBitPosition( highBitPosition );
119 PixelConverter.SetXSize( HeaderInternal->GetXSize() );
120 PixelConverter.SetYSize( HeaderInternal->GetYSize() );
121 PixelConverter.SetZSize( HeaderInternal->GetZSize() );
122 PixelConverter.SetSamplesPerPixel( HeaderInternal->GetSamplesPerPixel() );
123 PixelConverter.SetPixelSize( HeaderInternal->GetPixelSize() );
124 PixelConverter.SetPixelSign( HeaderInternal->IsSignedPixelData() );
125 PixelConverter.SetSwapCode( HeaderInternal->GetSwapCode() );
126 PixelConverter.SetIsUncompressed(
127 ! HeaderInternal->IsDicomV3()
128 || HeaderInternal->IsImplicitVRLittleEndianTransferSyntax()
129 || HeaderInternal->IsExplicitVRLittleEndianTransferSyntax()
130 || HeaderInternal->IsExplicitVRBigEndianTransferSyntax()
131 || HeaderInternal->IsDeflatedExplicitVRLittleEndianTransferSyntax() );
132 PixelConverter.SetIsJPEG2000( HeaderInternal->IsJPEG2000() );
133 PixelConverter.SetIsJPEGLossless( HeaderInternal->IsJPEGLossless() );
134 PixelConverter.SetIsRLELossless(
135 HeaderInternal->IsRLELossLessTransferSyntax() );
136 PixelConverter.SetPixelOffset( HeaderInternal->GetPixelOffset() );
137 PixelConverter.SetPixelDataLength( HeaderInternal->GetPixelAreaLength() );
138 PixelConverter.SetRLEInfo( &(HeaderInternal->RLEInfo) );
139 PixelConverter.SetJPEGInfo( &(HeaderInternal->JPEGInfo) );
140 PixelConverter.SetDecompressedSize( ImageDataSize );
142 HeaderInternal->CloseFile();
149 * \brief canonical destructor
150 * \note If the Header was created by the File constructor,
151 * it is destroyed by the File
157 delete HeaderInternal;
161 DeleteInitialValues();
165 * \brief Sets some initial values for the Constructor
166 * \warning not end user intended
168 void File::SaveInitialValues()
171 PixelRead = -1; // no ImageData read yet.
172 LastAllocatedPixelDataLength = 0;
177 InitialPlanConfig = "";
178 InitialBitsAllocated = "";
181 InitialRedLUTDescr = 0;
182 InitialGreenLUTDescr = 0;
183 InitialBlueLUTDescr = 0;
184 InitialRedLUTData = 0;
185 InitialGreenLUTData = 0;
186 InitialBlueLUTData = 0;
188 if ( HeaderInternal->IsReadable() )
190 // the following values *may* be modified
191 // by File::GetImageDataIntoVectorRaw
192 // we save their initial value.
193 InitialSpp = HeaderInternal->GetEntryByNumber(0x0028,0x0002);
194 InitialPhotInt = HeaderInternal->GetEntryByNumber(0x0028,0x0004);
195 InitialPlanConfig = HeaderInternal->GetEntryByNumber(0x0028,0x0006);
197 InitialBitsAllocated = HeaderInternal->GetEntryByNumber(0x0028,0x0100);
198 InitialHighBit = HeaderInternal->GetEntryByNumber(0x0028,0x0102);
200 // the following entries *may* be removed from the H table
201 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
202 // we keep a pointer on them.
203 InitialRedLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1101);
204 InitialGreenLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1102);
205 InitialBlueLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1103);
207 InitialRedLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1201);
208 InitialGreenLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1202);
209 InitialBlueLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1203);
214 * \brief restores some initial values
215 * \warning not end user intended
217 void File::RestoreInitialValues()
219 if ( HeaderInternal->IsReadable() )
221 // the following values *may* have been modified
222 // by File::GetImageDataIntoVectorRaw
223 // we restore their initial value.
224 if ( InitialSpp != "")
225 HeaderInternal->SetEntryByNumber(InitialSpp,0x0028,0x0002);
226 if ( InitialPhotInt != "")
227 HeaderInternal->SetEntryByNumber(InitialPhotInt,0x0028,0x0004);
228 if ( InitialPlanConfig != "")
230 HeaderInternal->SetEntryByNumber(InitialPlanConfig,0x0028,0x0006);
231 if ( InitialBitsAllocated != "")
232 HeaderInternal->SetEntryByNumber(InitialBitsAllocated,0x0028,0x0100);
233 if ( InitialHighBit != "")
234 HeaderInternal->SetEntryByNumber(InitialHighBit,0x0028,0x0102);
236 // the following entries *may* be have been removed from the H table
237 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
240 if (InitialRedLUTDescr)
241 HeaderInternal->AddEntry(InitialRedLUTDescr);
242 if (InitialGreenLUTDescr)
243 HeaderInternal->AddEntry(InitialGreenLUTDescr);
244 if (InitialBlueLUTDescr)
245 HeaderInternal->AddEntry(InitialBlueLUTDescr);
247 if (InitialRedLUTData)
248 HeaderInternal->AddEntry(InitialBlueLUTDescr);
249 if (InitialGreenLUTData)
250 HeaderInternal->AddEntry(InitialGreenLUTData);
251 if (InitialBlueLUTData)
252 HeaderInternal->AddEntry(InitialBlueLUTData);
257 * \brief delete initial values (il they were saved)
258 * of InitialLutDescriptors and InitialLutData
260 void File::DeleteInitialValues()
263 // InitialLutDescriptors and InitialLutData
264 // will have to be deleted if the don't belong any longer
265 // to the Header H table when the header is deleted...
267 if ( InitialRedLUTDescr )
268 delete InitialRedLUTDescr;
270 if ( InitialGreenLUTDescr )
271 delete InitialGreenLUTDescr;
273 if ( InitialBlueLUTDescr )
274 delete InitialBlueLUTDescr;
276 if ( InitialRedLUTData )
277 delete InitialRedLUTData;
279 if ( InitialGreenLUTData != NULL)
280 delete InitialGreenLUTData;
282 if ( InitialBlueLUTData != NULL)
283 delete InitialBlueLUTData;
286 //-----------------------------------------------------------------------------
289 //-----------------------------------------------------------------------------
293 * \brief computes the length (in bytes) we must ALLOCATE to receive the
294 * image(s) pixels (multiframes taken into account)
295 * \warning : it is NOT the group 7FE0 length
296 * (no interest for compressed images).
298 int File::ComputeDecompressedPixelDataSizeFromHeader()
300 // see PS 3.3-2003 : C.7.6.3.2.1
310 // YBR_FULL_422 (no LUT, no Palette)
316 // ex : gdcm-US-ALOKA-16.dcm
317 // 0028|1221 [OW] [Segmented Red Palette Color Lookup Table Data]
318 // 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
319 // 0028|1223 [OW] [Segmented Blue Palette Color Lookup Table Data]
321 // ex : OT-PAL-8-face.dcm
322 // 0028|1201 [US] [Red Palette Color Lookup Table Data]
323 // 0028|1202 [US] [Green Palette Color Lookup Table Data]
324 // 0028|1203 [US] [Blue Palette Color Lookup Table Data]
326 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
327 // Number of "Bits Allocated" is fixed to 16 when:
328 // - it is not defined (i.e. it's value is 0)
329 // - it's 12, since we will expand the image to 16 bits (see
330 // PixelConvert::ConvertDecompress12BitsTo16Bits() )
331 if ( ( numberBitsAllocated == 0 ) || ( numberBitsAllocated == 12 ) )
333 numberBitsAllocated = 16;
336 int DecompressedSize = HeaderInternal->GetXSize()
337 * HeaderInternal->GetYSize()
338 * HeaderInternal->GetZSize()
339 * ( numberBitsAllocated / 8 )
340 * HeaderInternal->GetSamplesPerPixel();
342 return DecompressedSize;
346 * \brief - Allocates necessary memory,
347 * - Reads the pixels from disk (uncompress if necessary),
348 * - Transforms YBR pixels, if any, into RGB pixels
349 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
350 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
351 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
352 * @return Pointer to newly allocated pixel data.
353 * NULL if alloc fails
355 uint8_t* File::GetImageData()
358 // I need to deallocate Pixel_Data before doing any allocation:
361 if ( LastAllocatedPixelDataLength != ImageDataSize )
364 Pixel_Data = new uint8_t[ImageDataSize];
368 LastAllocatedPixelDataLength = ImageDataSize;
370 // we load the pixels (and transform grey level + LUT into RGB)
371 GetImageDataIntoVector(Pixel_Data, ImageDataSize);
373 // We say the value *is* loaded.
374 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
375 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
377 // Will be 7fe0, 0010 in standard case
378 GetHeader()->SetEntryBinAreaByNumber( Pixel_Data,
379 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
381 PixelRead = 0; // no PixelRaw
388 * Read 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 at most MaxSize bytes of pixel data to caller allocated
394 * \warning This function allows people that want to build a volume
395 * from an image stack *not to* have, first to get the image pixels,
396 * and then move them to the volume area.
397 * It's absolutely useless for any VTK user since vtk chooses
398 * to invert the lines of an image, that is the last line comes first
399 * (for some axis related reasons?). Hence he will have
400 * to load the image line by line, starting from the end.
401 * VTK users have to call GetImageData
403 * @param destination Address (in caller's memory space) at which the
404 * pixel data should be copied
405 * @param maxSize Maximum number of bytes to be copied. When MaxSize
406 * is not sufficient to hold the pixel data the copy is not
407 * executed (i.e. no partial copy).
408 * @return On success, the number of bytes actually copied. Zero on
409 * failure e.g. MaxSize is lower than necessary.
411 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
413 GetImageDataIntoVectorRaw (destination, maxSize);
414 PixelRead = 0 ; // =0 : no ImageDataRaw
415 if ( !HeaderInternal->HasLUT() )
417 return ImageDataSize;
420 // from Lut R + Lut G + Lut B
421 uint8_t *newDest = new uint8_t[ImageDataSize];
422 uint8_t *a = (uint8_t *)destination;
423 uint8_t *lutRGBA = HeaderInternal->GetLUTRGBA();
428 // move Gray pixels to temp area
429 memmove(newDest, destination, ImageDataSizeRaw);
430 for (size_t i=0; i<ImageDataSizeRaw; ++i)
440 // now, it's an RGB image
441 // Lets's write it in the Header
443 // FIXME : Better use CreateOrReplaceIfExist ?
445 std::string spp = "3"; // Samples Per Pixel
446 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
447 std::string rgb = "RGB "; // Photometric Interpretation
448 HeaderInternal->SetEntryByNumber(rgb,0x0028,0x0004);
449 std::string planConfig = "0"; // Planar Configuration
450 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
453 else // GetLUTRGBA() failed
455 // (gdcm-US-ALOKA-16.dcm), contains Segmented xxx Palette Color
456 // that are *more* than 65535 long ?!?
457 // No idea how to manage such an image !
458 // Need to make RGB Pixels (?) from grey Pixels (?!) and Gray Lut (!?!)
459 // It seems that *no Dicom Viewer* has any idea :-(
461 std::string photomInterp = "MONOCHROME1 "; // Photometric Interpretation
462 HeaderInternal->SetEntryByNumber(photomInterp,0x0028,0x0004);
465 /// \todo Drop Palette Color out of the Header?
466 return ImageDataSize;
470 * \brief Allocates necessary memory,
471 * Transforms YBR pixels (if any) into RGB pixels
472 * Transforms 3 planes R, G, B (if any) into a single RGB Plane
473 * Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
474 * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
475 * @return Pointer to newly allocated pixel data.
476 * \ NULL if alloc fails
478 uint8_t* File::GetImageDataRaw ()
481 if ( HeaderInternal->HasLUT() )
482 /// \todo Let Header user a chance to get the right value
483 imgDataSize = ImageDataSizeRaw;
485 imgDataSize = ImageDataSize;
488 // I need to deallocate Pixel_Data before doing any allocation:
491 if ( LastAllocatedPixelDataLength != imgDataSize )
494 Pixel_Data = new uint8_t[imgDataSize];
498 LastAllocatedPixelDataLength = imgDataSize;
500 // we load the pixels ( grey level or RGB, but NO transformation)
501 GetImageDataIntoVectorRaw(Pixel_Data, imgDataSize);
503 // We say the value *is* loaded.
504 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
505 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
507 // will be 7fe0, 0010 in standard cases
508 GetHeader()->SetEntryBinAreaByNumber(Pixel_Data,
509 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
511 PixelRead = 1; // PixelRaw
517 * \brief Copies at most MaxSize bytes of pixel data to caller's
519 * \warning This function was designed to avoid people that want to build
520 * a volume from an image stack to need first to get the image pixels
521 * and then move them to the volume area.
522 * It's absolutely useless for any VTK user since vtk chooses
523 * to invert the lines of an image, that is the last line comes first
524 * (for some axis related reasons?). Hence he will have
525 * to load the image line by line, starting from the end.
526 * VTK users hace to call GetImageData
527 * \warning DOES NOT transform the Grey Plane + Palette Color (if any)
528 * into a single RGB Pixels Plane
529 * the (VTK) user will manage the palettes
531 * @param destination Address (in caller's memory space) at which the
532 * pixel data should be copied
533 * @param maxSize Maximum number of bytes to be copied. When MaxSize
534 * is not sufficient to hold the pixel data the copy is not
535 * executed (i.e. no partial copy).
536 * @return On success, the number of bytes actually copied. Zero on
537 * failure e.g. MaxSize is lower than necessary.
539 size_t File::GetImageDataIntoVectorRaw (void* destination, size_t maxSize)
541 // we save the initial values of the following
542 // in order to be able to restore the header in a disk-consistent state
543 // (if user asks twice to get the pixels from disk)
545 if ( PixelRead != -1 ) // File was "read" before
547 RestoreInitialValues();
550 PixelRead = 1 ; // PixelRaw
552 if ( ImageDataSize > maxSize )
554 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
555 "than caller's expected MaxSize");
559 FILE* fp = HeaderInternal->OpenFile();
560 PixelConverter.ReadAndDecompressPixelData( destination, fp );
561 HeaderInternal->CloseFile();
563 PixelConverter.ReorderEndianity( (uint8_t*) destination );
565 PixelConverter.ReArrangeBits( (uint8_t*) destination );
569 DebugFile = fopen( "SpuriousFile.RAW", "wb" );
570 fwrite( PixelConvertor.GetUncompressed(),
571 PixelConvertor.GetUncompressedsSize(),
577 //////////////////////////////////
578 // Deal with the color
580 // Monochrome pictures don't require color intervention
581 if ( HeaderInternal->IsMonochrome() )
583 return ImageDataSize;
586 // Planar configuration = 0 : Pixels are already RGB
587 // Planar configuration = 1 : 3 planes : R, G, B
588 // Planar configuration = 2 : 1 gray Plane + 3 LUT
590 // Well ... supposed to be !
591 // See US-PAL-8-10x-echo.dcm: PlanarConfiguration=0,
592 // PhotometricInterpretation=PALETTE COLOR
593 // and heuristic has to be found :-(
595 int planConf = HeaderInternal->GetPlanarConfiguration();
597 // Planar configuration = 2 ==> 1 gray Plane + 3 LUT
599 // whatever the Planar Configuration might be, "PALETTE COLOR "
600 // implies that we deal with the palette.
601 if ( ( planConf == 2 ) || HeaderInternal->IsPaletteColor() )
603 return ImageDataSize;
606 // When planConf is 0, pixels are allready in RGB
610 // Warning : YBR_FULL_422 acts as RGB
611 if ( HeaderInternal->IsYBRFull() )
613 PixelConverter.ConvertYcBcRPlanesToRGBPixels(
614 (uint8_t*)destination,
619 PixelConverter.ConvertRGBPlanesToRGBPixels(
620 (uint8_t*)destination,
626 ///////////////////////////////////////////////////
627 // now, it's an RGB image
628 // Lets's write it in the Header
630 // Droping Palette Color out of the Header
631 // has been moved to the Write process.
633 // TODO : move 'values' modification to the write process
634 // : save also (in order to be able to restore)
635 // : 'high bit' -when not equal to 'bits stored' + 1
636 // : 'bits allocated', when it's equal to 12 ?!
638 std::string spp = "3"; // Samples Per Pixel
639 std::string photInt = "RGB "; // Photometric Interpretation
640 std::string planConfig = "0"; // Planar Configuration
642 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
643 HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
644 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
646 return ImageDataSize;
650 * \brief Points the internal Pixel_Data pointer to the callers inData
651 * image representation, BUT WITHOUT COPYING THE DATA.
652 * 'image' Pixels are presented as C-like 2D arrays : line per line.
653 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
654 * \warning Since the pixels are not copied, it is the caller's responsability
655 * not to deallocate it's data before gdcm uses them (e.g. with
656 * the Write() method.
657 * @param inData user supplied pixel area
658 * @param expectedSize total image size, in Bytes
662 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
664 HeaderInternal->SetImageDataSize( expectedSize );
665 // FIXME : if already allocated, memory leak !
667 ImageDataSize = ImageDataSizeRaw = expectedSize;
669 // FIXME : 7fe0, 0010 IS NOT set ...
674 * \brief Writes on disk A SINGLE Dicom file
675 * NO test is performed on processor "Endiannity".
676 * It's up to the user to call his Reader properly
677 * @param fileName name of the file to be created
678 * (any already existing file is over written)
679 * @return false if write fails
682 bool File::WriteRawData(std::string const & fileName)
684 FILE* fp1 = fopen(fileName.c_str(), "wb");
687 printf("Fail to open (write) file [%s] \n", fileName.c_str());
690 fwrite (Pixel_Data, ImageDataSize, 1, fp1);
697 * \brief Writes on disk A SINGLE Dicom file,
698 * using the Implicit Value Representation convention
699 * NO test is performed on processor "Endiannity".
700 * @param fileName name of the file to be created
701 * (any already existing file is overwritten)
702 * @return false if write fails
705 bool File::WriteDcmImplVR (std::string const & fileName)
707 return WriteBase(fileName, ImplicitVR);
711 * \brief Writes on disk A SINGLE Dicom file,
712 * using the Explicit Value Representation convention
713 * NO test is performed on processor "Endiannity". * @param fileName name of the file to be created
714 * (any already existing file is overwritten)
715 * @return false if write fails
718 bool File::WriteDcmExplVR (std::string const & fileName)
720 return WriteBase(fileName, ExplicitVR);
724 * \brief Writes on disk A SINGLE Dicom file,
725 * using the ACR-NEMA convention
726 * NO test is performed on processor "Endiannity".
727 * (a l'attention des logiciels cliniques
728 * qui ne prennent en entrée QUE des images ACR ...
729 * \warning if a DICOM_V3 header is supplied,
730 * groups < 0x0008 and shadow groups are ignored
731 * \warning NO TEST is performed on processor "Endiannity".
732 * @param fileName name of the file to be created
733 * (any already existing file is overwritten)
734 * @return false if write fails
737 bool File::WriteAcr (std::string const & fileName)
739 return WriteBase(fileName, ACR);
742 //-----------------------------------------------------------------------------
745 * \brief NOT a end user inteded function
746 * (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
747 * @param fileName name of the file to be created
748 * (any already existing file is overwritten)
749 * @param type file type (ExplicitVR, ImplicitVR, ...)
750 * @return false if write fails
752 bool File::WriteBase (std::string const & fileName, FileType type)
754 if ( PixelRead == -1 && type != ExplicitVR)
759 FILE* fp1 = fopen(fileName.c_str(), "wb");
762 printf("Failed to open (write) File [%s] \n", fileName.c_str());
766 if ( type == ImplicitVR || type == ExplicitVR )
768 // writing Dicom File Preamble
769 uint8_t* filePreamble = new uint8_t[128];
770 memset(filePreamble, 0, 128);
771 fwrite(filePreamble, 128, 1, fp1);
772 fwrite("DICM", 4, 1, fp1);
774 delete[] filePreamble;
777 // --------------------------------------------------------------
778 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
780 // if recognition code tells us we dealt with a LibIDO image
781 // we reproduce on disk the switch between lineNumber and columnNumber
782 // just before writting ...
784 /// \todo the best trick would be *change* the recognition code
785 /// but pb expected if user deals with, e.g. COMPLEX images
787 std::string rows, columns;
788 if ( HeaderInternal->GetFileType() == ACR_LIBIDO)
790 rows = HeaderInternal->GetEntryByNumber(0x0028, 0x0010);
791 columns = HeaderInternal->GetEntryByNumber(0x0028, 0x0011);
793 HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0010);
794 HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0011);
796 // ----------------- End of Special Patch ----------------
798 uint16_t grPixel = HeaderInternal->GetGrPixel();
799 uint16_t numPixel = HeaderInternal->GetNumPixel();;
801 DocEntry* PixelElement =
802 GetHeader()->GetDocEntryByNumber(grPixel, numPixel);
804 if ( PixelRead == 1 )
806 // we read pixel 'as is' (no tranformation LUT -> RGB)
807 PixelElement->SetLength( ImageDataSizeRaw );
809 else if ( PixelRead == 0 )
811 // we tranformed GrayLevel pixels + LUT into RGB Pixel
812 PixelElement->SetLength( ImageDataSize );
815 HeaderInternal->Write(fp1, type);
817 // --------------------------------------------------------------
818 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
820 // ...and we restore the Header to be Dicom Compliant again
821 // just after writting
823 if ( HeaderInternal->GetFileType() == ACR_LIBIDO )
825 HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0010);
826 HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0011);
828 // ----------------- End of Special Patch ----------------
830 // fwrite(Pixel_Data, ImageDataSize, 1, fp1); // should be useless, now
836 } // end namespace gdcm