1 /*=========================================================================
4 Module: $RCSfile: gdcmFile.cxx,v $
6 Date: $Date: 2004/10/13 04:05:04 $
7 Version: $Revision: 1.141 $
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();
93 // Number of Bits Allocated for storing a Pixel is defaulted to 16
94 // when absent from the header.
95 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
96 if ( numberBitsAllocated == 0 )
98 numberBitsAllocated = 16;
100 PixelConverter.SetBitsAllocated( numberBitsAllocated );
102 // Number of "Bits Stored" defaulted to number of "Bits Allocated"
103 // when absent from the header.
104 int numberBitsStored = HeaderInternal->GetBitsStored();
105 if ( numberBitsStored == 0 )
107 numberBitsStored = numberBitsAllocated;
109 PixelConverter.SetBitsStored( numberBitsStored );
112 int highBitPosition = HeaderInternal->GetHighBitPosition();
113 if ( highBitPosition == 0 )
115 highBitPosition = numberBitsAllocated - 1;
117 PixelConverter.SetHighBitPosition( highBitPosition );
120 PixelConverter.SetXSize( HeaderInternal->GetXSize() );
121 PixelConverter.SetYSize( HeaderInternal->GetYSize() );
122 PixelConverter.SetZSize( HeaderInternal->GetZSize() );
123 PixelConverter.SetSamplesPerPixel( HeaderInternal->GetSamplesPerPixel() );
124 PixelConverter.SetPixelSize( HeaderInternal->GetPixelSize() );
125 PixelConverter.SetPixelSign( HeaderInternal->IsSignedPixelData() );
126 PixelConverter.SetSwapCode( HeaderInternal->GetSwapCode() );
127 PixelConverter.SetIsUncompressed(
128 ! HeaderInternal->IsDicomV3()
129 || HeaderInternal->IsImplicitVRLittleEndianTransferSyntax()
130 || HeaderInternal->IsExplicitVRLittleEndianTransferSyntax()
131 || HeaderInternal->IsExplicitVRBigEndianTransferSyntax()
132 || HeaderInternal->IsDeflatedExplicitVRLittleEndianTransferSyntax() );
133 PixelConverter.SetIsJPEG2000( HeaderInternal->IsJPEG2000() );
134 PixelConverter.SetIsJPEGLossless( HeaderInternal->IsJPEGLossless() );
135 PixelConverter.SetIsRLELossless(
136 HeaderInternal->IsRLELossLessTransferSyntax() );
137 PixelConverter.SetPixelOffset( HeaderInternal->GetPixelOffset() );
138 PixelConverter.SetPixelDataLength( HeaderInternal->GetPixelAreaLength() );
139 PixelConverter.SetRLEInfo( &(HeaderInternal->RLEInfo) );
140 PixelConverter.SetJPEGInfo( &(HeaderInternal->JPEGInfo) );
141 PixelConverter.SetDecompressedSize( ImageDataSize );
143 HeaderInternal->CloseFile();
150 * \brief canonical destructor
151 * \note If the Header was created by the File constructor,
152 * it is destroyed by the File
158 delete HeaderInternal;
162 DeleteInitialValues();
166 * \brief Sets some initial values for the Constructor
167 * \warning not end user intended
169 void File::SaveInitialValues()
172 PixelRead = -1; // no ImageData read yet.
173 LastAllocatedPixelDataLength = 0;
178 InitialPlanConfig = "";
179 InitialBitsAllocated = "";
182 InitialRedLUTDescr = 0;
183 InitialGreenLUTDescr = 0;
184 InitialBlueLUTDescr = 0;
185 InitialRedLUTData = 0;
186 InitialGreenLUTData = 0;
187 InitialBlueLUTData = 0;
189 if ( HeaderInternal->IsReadable() )
191 // the following values *may* be modified
192 // by File::GetImageDataIntoVectorRaw
193 // we save their initial value.
194 InitialSpp = HeaderInternal->GetEntryByNumber(0x0028,0x0002);
195 InitialPhotInt = HeaderInternal->GetEntryByNumber(0x0028,0x0004);
196 InitialPlanConfig = HeaderInternal->GetEntryByNumber(0x0028,0x0006);
198 InitialBitsAllocated = HeaderInternal->GetEntryByNumber(0x0028,0x0100);
199 InitialHighBit = HeaderInternal->GetEntryByNumber(0x0028,0x0102);
201 // the following entries *may* be removed from the H table
202 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
203 // we keep a pointer on them.
204 InitialRedLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1101);
205 InitialGreenLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1102);
206 InitialBlueLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1103);
208 InitialRedLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1201);
209 InitialGreenLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1202);
210 InitialBlueLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1203);
215 * \brief restores some initial values
216 * \warning not end user intended
218 void File::RestoreInitialValues()
220 if ( HeaderInternal->IsReadable() )
222 // the following values *may* have been modified
223 // by File::GetImageDataIntoVectorRaw
224 // we restore their initial value.
225 if ( InitialSpp != "")
226 HeaderInternal->SetEntryByNumber(InitialSpp,0x0028,0x0002);
227 if ( InitialPhotInt != "")
228 HeaderInternal->SetEntryByNumber(InitialPhotInt,0x0028,0x0004);
229 if ( InitialPlanConfig != "")
231 HeaderInternal->SetEntryByNumber(InitialPlanConfig,0x0028,0x0006);
232 if ( InitialBitsAllocated != "")
233 HeaderInternal->SetEntryByNumber(InitialBitsAllocated,0x0028,0x0100);
234 if ( InitialHighBit != "")
235 HeaderInternal->SetEntryByNumber(InitialHighBit,0x0028,0x0102);
237 // the following entries *may* be have been removed from the H table
238 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
241 if (InitialRedLUTDescr)
242 HeaderInternal->AddEntry(InitialRedLUTDescr);
243 if (InitialGreenLUTDescr)
244 HeaderInternal->AddEntry(InitialGreenLUTDescr);
245 if (InitialBlueLUTDescr)
246 HeaderInternal->AddEntry(InitialBlueLUTDescr);
248 if (InitialRedLUTData)
249 HeaderInternal->AddEntry(InitialBlueLUTDescr);
250 if (InitialGreenLUTData)
251 HeaderInternal->AddEntry(InitialGreenLUTData);
252 if (InitialBlueLUTData)
253 HeaderInternal->AddEntry(InitialBlueLUTData);
258 * \brief delete initial values (il they were saved)
259 * of InitialLutDescriptors and InitialLutData
261 void File::DeleteInitialValues()
264 // InitialLutDescriptors and InitialLutData
265 // will have to be deleted if the don't belong any longer
266 // to the Header H table when the header is deleted...
268 if ( InitialRedLUTDescr )
269 delete InitialRedLUTDescr;
271 if ( InitialGreenLUTDescr )
272 delete InitialGreenLUTDescr;
274 if ( InitialBlueLUTDescr )
275 delete InitialBlueLUTDescr;
277 if ( InitialRedLUTData )
278 delete InitialRedLUTData;
280 if ( InitialGreenLUTData != NULL)
281 delete InitialGreenLUTData;
283 if ( InitialBlueLUTData != NULL)
284 delete InitialBlueLUTData;
287 //-----------------------------------------------------------------------------
290 //-----------------------------------------------------------------------------
294 * \brief computes the length (in bytes) we must ALLOCATE to receive the
295 * image(s) pixels (multiframes taken into account)
296 * \warning : it is NOT the group 7FE0 length
297 * (no interest for compressed images).
299 int File::ComputeDecompressedPixelDataSizeFromHeader()
301 // see PS 3.3-2003 : C.7.6.3.2.1
311 // YBR_FULL_422 (no LUT, no Palette)
317 // ex : gdcm-US-ALOKA-16.dcm
318 // 0028|1221 [OW] [Segmented Red Palette Color Lookup Table Data]
319 // 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
320 // 0028|1223 [OW] [Segmented Blue Palette Color Lookup Table Data]
322 // ex : OT-PAL-8-face.dcm
323 // 0028|1201 [US] [Red Palette Color Lookup Table Data]
324 // 0028|1202 [US] [Green Palette Color Lookup Table Data]
325 // 0028|1203 [US] [Blue Palette Color Lookup Table Data]
327 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
328 // Number of "Bits Allocated" is fixed to 16 when:
329 // - it is not defined (i.e. it's value is 0)
330 // - it's 12, since we will expand the image to 16 bits (see
331 // PixelConvert::ConvertDecompress12BitsTo16Bits() )
332 if ( ( numberBitsAllocated == 0 ) || ( numberBitsAllocated == 12 ) )
334 numberBitsAllocated = 16;
337 int DecompressedSize = HeaderInternal->GetXSize()
338 * HeaderInternal->GetYSize()
339 * HeaderInternal->GetZSize()
340 * ( numberBitsAllocated / 8 )
341 * HeaderInternal->GetSamplesPerPixel();
343 return DecompressedSize;
347 * \brief - Allocates necessary memory,
348 * - Reads the pixels from disk (uncompress if necessary),
349 * - Transforms YBR pixels, if any, into RGB pixels
350 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
351 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
352 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
353 * @return Pointer to newly allocated pixel data.
354 * NULL if alloc fails
356 uint8_t* File::GetImageData()
359 // I need to deallocate Pixel_Data before doing any allocation:
362 if ( LastAllocatedPixelDataLength != ImageDataSize )
365 Pixel_Data = new uint8_t[ImageDataSize];
369 LastAllocatedPixelDataLength = ImageDataSize;
371 // we load the pixels (and transform grey level + LUT into RGB)
372 GetImageDataIntoVector(Pixel_Data, ImageDataSize);
374 // We say the value *is* loaded.
375 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
376 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
378 // Will be 7fe0, 0010 in standard case
379 GetHeader()->SetEntryBinAreaByNumber( Pixel_Data,
380 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
382 PixelRead = 0; // no PixelRaw
389 * Read the pixels from disk (uncompress if necessary),
390 * Transforms YBR pixels, if any, into RGB pixels
391 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
392 * Transforms single Grey plane + 3 Palettes into a RGB Plane
393 * Copies at most MaxSize bytes of pixel data to caller allocated
395 * \warning This function allows people that want to build a volume
396 * from an image stack *not to* have, first to get the image pixels,
397 * and then move them to the volume area.
398 * It's absolutely useless for any VTK user since vtk chooses
399 * to invert the lines of an image, that is the last line comes first
400 * (for some axis related reasons?). Hence he will have
401 * to load the image line by line, starting from the end.
402 * VTK users have to call GetImageData
404 * @param destination Address (in caller's memory space) at which the
405 * pixel data should be copied
406 * @param maxSize Maximum number of bytes to be copied. When MaxSize
407 * is not sufficient to hold the pixel data the copy is not
408 * executed (i.e. no partial copy).
409 * @return On success, the number of bytes actually copied. Zero on
410 * failure e.g. MaxSize is lower than necessary.
412 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
414 GetImageDataIntoVectorRaw (destination, maxSize);
415 PixelRead = 0 ; // =0 : no ImageDataRaw
416 if ( !HeaderInternal->HasLUT() )
418 return ImageDataSize;
421 // from Lut R + Lut G + Lut B
422 uint8_t *newDest = new uint8_t[ImageDataSize];
423 uint8_t *a = (uint8_t *)destination;
424 uint8_t *lutRGBA = HeaderInternal->GetLUTRGBA();
429 // move Gray pixels to temp area
430 memmove(newDest, destination, ImageDataSizeRaw);
431 for (size_t i=0; i<ImageDataSizeRaw; ++i)
441 // now, it's an RGB image
442 // Lets's write it in the Header
444 // FIXME : Better use CreateOrReplaceIfExist ?
446 std::string spp = "3"; // Samples Per Pixel
447 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
448 std::string rgb = "RGB "; // Photometric Interpretation
449 HeaderInternal->SetEntryByNumber(rgb,0x0028,0x0004);
450 std::string planConfig = "0"; // Planar Configuration
451 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
454 else // GetLUTRGBA() failed
456 // (gdcm-US-ALOKA-16.dcm), contains Segmented xxx Palette Color
457 // that are *more* than 65535 long ?!?
458 // No idea how to manage such an image !
459 // Need to make RGB Pixels (?) from grey Pixels (?!) and Gray Lut (!?!)
460 // It seems that *no Dicom Viewer* has any idea :-(
462 std::string photomInterp = "MONOCHROME1 "; // Photometric Interpretation
463 HeaderInternal->SetEntryByNumber(photomInterp,0x0028,0x0004);
466 /// \todo Drop Palette Color out of the Header?
467 return ImageDataSize;
471 * \brief Allocates necessary memory,
472 * Transforms YBR pixels (if any) into RGB pixels
473 * Transforms 3 planes R, G, B (if any) into a single RGB Plane
474 * Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
475 * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
476 * @return Pointer to newly allocated pixel data.
477 * \ NULL if alloc fails
479 uint8_t* File::GetImageDataRaw ()
482 if ( HeaderInternal->HasLUT() )
483 /// \todo Let Header user a chance to get the right value
484 imgDataSize = ImageDataSizeRaw;
486 imgDataSize = ImageDataSize;
489 // I need to deallocate Pixel_Data before doing any allocation:
492 if ( LastAllocatedPixelDataLength != imgDataSize )
495 Pixel_Data = new uint8_t[imgDataSize];
499 LastAllocatedPixelDataLength = imgDataSize;
501 // we load the pixels ( grey level or RGB, but NO transformation)
502 GetImageDataIntoVectorRaw(Pixel_Data, imgDataSize);
504 // We say the value *is* loaded.
505 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
506 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
508 // will be 7fe0, 0010 in standard cases
509 GetHeader()->SetEntryBinAreaByNumber(Pixel_Data,
510 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
512 PixelRead = 1; // PixelRaw
518 * \brief Copies at most MaxSize bytes of pixel data to caller's
520 * \warning This function was designed to avoid people that want to build
521 * a volume from an image stack to need first to get the image pixels
522 * and then move them to the volume area.
523 * It's absolutely useless for any VTK user since vtk chooses
524 * to invert the lines of an image, that is the last line comes first
525 * (for some axis related reasons?). Hence he will have
526 * to load the image line by line, starting from the end.
527 * VTK users hace to call GetImageData
528 * \warning DOES NOT transform the Grey Plane + Palette Color (if any)
529 * into a single RGB Pixels Plane
530 * the (VTK) user will manage the palettes
532 * @param destination Address (in caller's memory space) at which the
533 * pixel data should be copied
534 * @param maxSize Maximum number of bytes to be copied. When MaxSize
535 * is not sufficient to hold the pixel data the copy is not
536 * executed (i.e. no partial copy).
537 * @return On success, the number of bytes actually copied. Zero on
538 * failure e.g. MaxSize is lower than necessary.
540 size_t File::GetImageDataIntoVectorRaw (void* destination, size_t maxSize)
542 // we save the initial values of the following
543 // in order to be able to restore the header in a disk-consistent state
544 // (if user asks twice to get the pixels from disk)
546 if ( PixelRead != -1 ) // File was "read" before
548 RestoreInitialValues();
551 PixelRead = 1 ; // PixelRaw
553 if ( ImageDataSize > maxSize )
555 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
556 "than caller's expected MaxSize");
560 FILE* fp = HeaderInternal->OpenFile();
561 PixelConverter.ReadAndDecompressPixelData( destination, fp );
562 HeaderInternal->CloseFile();
564 PixelConverter.ReorderEndianity( (uint8_t*) destination );
566 PixelConverter.ReArrangeBits( (uint8_t*) destination );
570 DebugFile = fopen( "SpuriousFile.RAW", "wb" );
571 fwrite( PixelConvertor.GetUncompressed(),
572 PixelConvertor.GetUncompressedsSize(),
578 //////////////////////////////////
579 // Deal with the color
581 // Monochrome pictures don't require color intervention
582 if ( HeaderInternal->IsMonochrome() )
584 return ImageDataSize;
587 // Planar configuration = 0 : Pixels are already RGB
588 // Planar configuration = 1 : 3 planes : R, G, B
589 // Planar configuration = 2 : 1 gray Plane + 3 LUT
591 // Well ... supposed to be !
592 // See US-PAL-8-10x-echo.dcm: PlanarConfiguration=0,
593 // PhotometricInterpretation=PALETTE COLOR
594 // and heuristic has to be found :-(
596 int planConf = HeaderInternal->GetPlanarConfiguration();
598 // Planar configuration = 2 ==> 1 gray Plane + 3 LUT
600 // whatever the Planar Configuration might be, "PALETTE COLOR "
601 // implies that we deal with the palette.
602 if ( ( planConf == 2 ) || HeaderInternal->IsPaletteColor() )
604 return ImageDataSize;
607 // When planConf is 0, pixels are allready in RGB
611 // Warning : YBR_FULL_422 acts as RGB
612 if ( HeaderInternal->IsYBRFull() )
614 PixelConverter.ConvertYcBcRPlanesToRGBPixels(
615 (uint8_t*)destination,
620 PixelConverter.ConvertRGBPlanesToRGBPixels(
621 (uint8_t*)destination,
627 ///////////////////////////////////////////////////
628 // now, it's an RGB image
629 // Lets's write it in the Header
631 // Droping Palette Color out of the Header
632 // has been moved to the Write process.
634 // TODO : move 'values' modification to the write process
635 // : save also (in order to be able to restore)
636 // : 'high bit' -when not equal to 'bits stored' + 1
637 // : 'bits allocated', when it's equal to 12 ?!
639 std::string spp = "3"; // Samples Per Pixel
640 std::string photInt = "RGB "; // Photometric Interpretation
641 std::string planConfig = "0"; // Planar Configuration
643 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
644 HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
645 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
647 return ImageDataSize;
651 * \brief Points the internal Pixel_Data pointer to the callers inData
652 * image representation, BUT WITHOUT COPYING THE DATA.
653 * 'image' Pixels are presented as C-like 2D arrays : line per line.
654 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
655 * \warning Since the pixels are not copied, it is the caller's responsability
656 * not to deallocate it's data before gdcm uses them (e.g. with
657 * the Write() method.
658 * @param inData user supplied pixel area
659 * @param expectedSize total image size, in Bytes
663 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
665 HeaderInternal->SetImageDataSize( expectedSize );
666 // FIXME : if already allocated, memory leak !
668 ImageDataSize = ImageDataSizeRaw = expectedSize;
670 // FIXME : 7fe0, 0010 IS NOT set ...
675 * \brief Writes on disk A SINGLE Dicom file
676 * NO test is performed on processor "Endiannity".
677 * It's up to the user to call his Reader properly
678 * @param fileName name of the file to be created
679 * (any already existing file is over written)
680 * @return false if write fails
683 bool File::WriteRawData(std::string const & fileName)
685 FILE* fp1 = fopen(fileName.c_str(), "wb");
688 printf("Fail to open (write) file [%s] \n", fileName.c_str());
691 fwrite (Pixel_Data, ImageDataSize, 1, fp1);
698 * \brief Writes on disk A SINGLE Dicom file,
699 * using the Implicit Value Representation convention
700 * NO test is performed on processor "Endiannity".
701 * @param fileName name of the file to be created
702 * (any already existing file is overwritten)
703 * @return false if write fails
706 bool File::WriteDcmImplVR (std::string const & fileName)
708 return WriteBase(fileName, ImplicitVR);
712 * \brief Writes on disk A SINGLE Dicom file,
713 * using the Explicit Value Representation convention
714 * NO test is performed on processor "Endiannity". * @param fileName name of the file to be created
715 * (any already existing file is overwritten)
716 * @return false if write fails
719 bool File::WriteDcmExplVR (std::string const & fileName)
721 return WriteBase(fileName, ExplicitVR);
725 * \brief Writes on disk A SINGLE Dicom file,
726 * using the ACR-NEMA convention
727 * NO test is performed on processor "Endiannity".
728 * (a l'attention des logiciels cliniques
729 * qui ne prennent en entrée QUE des images ACR ...
730 * \warning if a DICOM_V3 header is supplied,
731 * groups < 0x0008 and shadow groups are ignored
732 * \warning NO TEST is performed on processor "Endiannity".
733 * @param fileName name of the file to be created
734 * (any already existing file is overwritten)
735 * @return false if write fails
738 bool File::WriteAcr (std::string const & fileName)
740 return WriteBase(fileName, ACR);
743 //-----------------------------------------------------------------------------
746 * \brief NOT a end user inteded function
747 * (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
748 * @param fileName name of the file to be created
749 * (any already existing file is overwritten)
750 * @param type file type (ExplicitVR, ImplicitVR, ...)
751 * @return false if write fails
753 bool File::WriteBase (std::string const & fileName, FileType type)
755 if ( PixelRead == -1 && type != ExplicitVR)
760 FILE* fp1 = fopen(fileName.c_str(), "wb");
763 printf("Failed to open (write) File [%s] \n", fileName.c_str());
767 if ( type == ImplicitVR || type == ExplicitVR )
769 // writing Dicom File Preamble
770 uint8_t* filePreamble = new uint8_t[128];
771 memset(filePreamble, 0, 128);
772 fwrite(filePreamble, 128, 1, fp1);
773 fwrite("DICM", 4, 1, fp1);
775 delete[] filePreamble;
778 // --------------------------------------------------------------
779 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
781 // if recognition code tells us we dealt with a LibIDO image
782 // we reproduce on disk the switch between lineNumber and columnNumber
783 // just before writting ...
785 /// \todo the best trick would be *change* the recognition code
786 /// but pb expected if user deals with, e.g. COMPLEX images
788 std::string rows, columns;
789 if ( HeaderInternal->GetFileType() == ACR_LIBIDO)
791 rows = HeaderInternal->GetEntryByNumber(0x0028, 0x0010);
792 columns = HeaderInternal->GetEntryByNumber(0x0028, 0x0011);
794 HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0010);
795 HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0011);
797 // ----------------- End of Special Patch ----------------
799 uint16_t grPixel = HeaderInternal->GetGrPixel();
800 uint16_t numPixel = HeaderInternal->GetNumPixel();;
802 DocEntry* PixelElement =
803 GetHeader()->GetDocEntryByNumber(grPixel, numPixel);
805 if ( PixelRead == 1 )
807 // we read pixel 'as is' (no tranformation LUT -> RGB)
808 PixelElement->SetLength( ImageDataSizeRaw );
810 else if ( PixelRead == 0 )
812 // we tranformed GrayLevel pixels + LUT into RGB Pixel
813 PixelElement->SetLength( ImageDataSize );
816 HeaderInternal->Write(fp1, type);
818 // --------------------------------------------------------------
819 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
821 // ...and we restore the Header to be Dicom Compliant again
822 // just after writting
824 if ( HeaderInternal->GetFileType() == ACR_LIBIDO )
826 HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0010);
827 HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0011);
829 // ----------------- End of Special Patch ----------------
831 // fwrite(Pixel_Data, ImageDataSize, 1, fp1); // should be useless, now
837 } // end namespace gdcm