1 /*=========================================================================
4 Module: $RCSfile: gdcmFile.cxx,v $
6 Date: $Date: 2004/10/12 04:35:46 $
7 Version: $Revision: 1.139 $
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"
21 #include "gdcmPixelConvert.h"
25 typedef std::pair<TagDocEntryHT::iterator,TagDocEntryHT::iterator> IterHT;
27 //-------------------------------------------------------------------------
28 // Constructor / Destructor
30 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
31 * file (Header only deals with the ... header)
32 * Opens (in read only and when possible) an existing file and checks
33 * for DICOM compliance. Returns NULL on failure.
34 * It will be up to the user to load the pixels into memory
35 * (see GetImageData, GetImageDataRaw)
36 * \note the in-memory representation of all available tags found in
37 * the DICOM header is post-poned to first header information access.
38 * This avoid a double parsing of public part of the header when
39 * user sets an a posteriori shadow dictionary (efficiency can be
40 * seen as a side effect).
41 * @param header already built Header
43 File::File(Header *header)
45 HeaderInternal = header;
51 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
52 * file (Header only deals with the ... header)
53 * Opens (in read only and when possible) an existing file and checks
54 * for DICOM compliance. Returns NULL on failure.
55 * It will be up to the user to load the pixels into memory
56 * (see GetImageData, GetImageDataRaw)
57 * \note the in-memory representation of all available tags found in
58 * the DICOM header is post-poned to first header information access.
59 * This avoid a double parsing of public part of the header when
60 * one sets an a posteriori shadow dictionary (efficiency can be
61 * seen as a side effect).
62 * @param filename file to be opened for parsing
64 File::File(std::string const & filename )
66 HeaderInternal = new Header( filename );
72 * \brief Factorization for various forms of constructors.
74 void File::Initialise()
76 if ( HeaderInternal->IsReadable() )
78 ImageDataSizeRaw = ComputeDecompressedPixelDataSizeFromHeader();
79 if ( HeaderInternal->HasLUT() )
81 ImageDataSize = 3 * ImageDataSizeRaw;
85 ImageDataSize = ImageDataSizeRaw;
92 * \brief canonical destructor
93 * \note If the Header was created by the File constructor,
94 * it is destroyed by the File
100 delete HeaderInternal;
104 DeleteInitialValues();
108 * \brief Sets some initial values for the Constructor
109 * \warning not end user intended
111 void File::SaveInitialValues()
114 PixelRead = -1; // no ImageData read yet.
115 LastAllocatedPixelDataLength = 0;
120 InitialPlanConfig = "";
121 InitialBitsAllocated = "";
124 InitialRedLUTDescr = 0;
125 InitialGreenLUTDescr = 0;
126 InitialBlueLUTDescr = 0;
127 InitialRedLUTData = 0;
128 InitialGreenLUTData = 0;
129 InitialBlueLUTData = 0;
131 if ( HeaderInternal->IsReadable() )
133 // the following values *may* be modified
134 // by File::GetImageDataIntoVectorRaw
135 // we save their initial value.
136 InitialSpp = HeaderInternal->GetEntryByNumber(0x0028,0x0002);
137 InitialPhotInt = HeaderInternal->GetEntryByNumber(0x0028,0x0004);
138 InitialPlanConfig = HeaderInternal->GetEntryByNumber(0x0028,0x0006);
140 InitialBitsAllocated = HeaderInternal->GetEntryByNumber(0x0028,0x0100);
141 InitialHighBit = HeaderInternal->GetEntryByNumber(0x0028,0x0102);
143 // the following entries *may* be removed from the H table
144 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
145 // we keep a pointer on them.
146 InitialRedLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1101);
147 InitialGreenLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1102);
148 InitialBlueLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1103);
150 InitialRedLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1201);
151 InitialGreenLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1202);
152 InitialBlueLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1203);
157 * \brief restores some initial values
158 * \warning not end user intended
160 void File::RestoreInitialValues()
162 if ( HeaderInternal->IsReadable() )
164 // the following values *may* have been modified
165 // by File::GetImageDataIntoVectorRaw
166 // we restore their initial value.
167 if ( InitialSpp != "")
168 HeaderInternal->SetEntryByNumber(InitialSpp,0x0028,0x0002);
169 if ( InitialPhotInt != "")
170 HeaderInternal->SetEntryByNumber(InitialPhotInt,0x0028,0x0004);
171 if ( InitialPlanConfig != "")
173 HeaderInternal->SetEntryByNumber(InitialPlanConfig,0x0028,0x0006);
174 if ( InitialBitsAllocated != "")
175 HeaderInternal->SetEntryByNumber(InitialBitsAllocated,0x0028,0x0100);
176 if ( InitialHighBit != "")
177 HeaderInternal->SetEntryByNumber(InitialHighBit,0x0028,0x0102);
179 // the following entries *may* be have been removed from the H table
180 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
183 if (InitialRedLUTDescr)
184 HeaderInternal->AddEntry(InitialRedLUTDescr);
185 if (InitialGreenLUTDescr)
186 HeaderInternal->AddEntry(InitialGreenLUTDescr);
187 if (InitialBlueLUTDescr)
188 HeaderInternal->AddEntry(InitialBlueLUTDescr);
190 if (InitialRedLUTData)
191 HeaderInternal->AddEntry(InitialBlueLUTDescr);
192 if (InitialGreenLUTData)
193 HeaderInternal->AddEntry(InitialGreenLUTData);
194 if (InitialBlueLUTData)
195 HeaderInternal->AddEntry(InitialBlueLUTData);
200 * \brief delete initial values (il they were saved)
201 * of InitialLutDescriptors and InitialLutData
203 void File::DeleteInitialValues()
206 // InitialLutDescriptors and InitialLutData
207 // will have to be deleted if the don't belong any longer
208 // to the Header H table when the header is deleted...
210 if ( InitialRedLUTDescr )
211 delete InitialRedLUTDescr;
213 if ( InitialGreenLUTDescr )
214 delete InitialGreenLUTDescr;
216 if ( InitialBlueLUTDescr )
217 delete InitialBlueLUTDescr;
219 if ( InitialRedLUTData )
220 delete InitialRedLUTData;
222 if ( InitialGreenLUTData != NULL)
223 delete InitialGreenLUTData;
225 if ( InitialBlueLUTData != NULL)
226 delete InitialBlueLUTData;
229 //-----------------------------------------------------------------------------
232 //-----------------------------------------------------------------------------
236 * \brief computes the length (in bytes) we must ALLOCATE to receive the
237 * image(s) pixels (multiframes taken into account)
238 * \warning : it is NOT the group 7FE0 length
239 * (no interest for compressed images).
241 int File::ComputeDecompressedPixelDataSizeFromHeader()
243 // see PS 3.3-2003 : C.7.6.3.2.1
253 // YBR_FULL_422 (no LUT, no Palette)
259 // ex : gdcm-US-ALOKA-16.dcm
260 // 0028|1221 [OW] [Segmented Red Palette Color Lookup Table Data]
261 // 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
262 // 0028|1223 [OW] [Segmented Blue Palette Color Lookup Table Data]
264 // ex : OT-PAL-8-face.dcm
265 // 0028|1201 [US] [Red Palette Color Lookup Table Data]
266 // 0028|1202 [US] [Green Palette Color Lookup Table Data]
267 // 0028|1203 [US] [Blue Palette Color Lookup Table Data]
269 // Number of "Bits Allocated"
270 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
271 if ( ( numberBitsAllocated == 0 ) || ( numberBitsAllocated == 12 ) )
273 numberBitsAllocated = 16;
276 int DecompressedSize = HeaderInternal->GetXSize()
277 * HeaderInternal->GetYSize()
278 * HeaderInternal->GetZSize()
279 * ( numberBitsAllocated / 8 )
280 * HeaderInternal->GetSamplesPerPixel();
282 return DecompressedSize;
286 * \brief - Allocates necessary memory,
287 * - Reads the pixels from disk (uncompress if necessary),
288 * - Transforms YBR pixels, if any, into RGB pixels
289 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
290 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
291 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
292 * @return Pointer to newly allocated pixel data.
293 * NULL if alloc fails
295 uint8_t* File::GetImageData()
298 // I need to deallocate Pixel_Data before doing any allocation:
301 if ( LastAllocatedPixelDataLength != ImageDataSize )
304 Pixel_Data = new uint8_t[ImageDataSize];
308 LastAllocatedPixelDataLength = ImageDataSize;
310 // we load the pixels (and transform grey level + LUT into RGB)
311 GetImageDataIntoVector(Pixel_Data, ImageDataSize);
313 // We say the value *is* loaded.
314 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
315 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
317 // Will be 7fe0, 0010 in standard case
318 GetHeader()->SetEntryBinAreaByNumber( Pixel_Data,
319 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
321 PixelRead = 0; // no PixelRaw
328 * Read the pixels from disk (uncompress if necessary),
329 * Transforms YBR pixels, if any, into RGB pixels
330 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
331 * Transforms single Grey plane + 3 Palettes into a RGB Plane
332 * Copies at most MaxSize bytes of pixel data to caller allocated
334 * \warning This function allows people that want to build a volume
335 * from an image stack *not to* have, first to get the image pixels,
336 * and then move them to the volume area.
337 * It's absolutely useless for any VTK user since vtk chooses
338 * to invert the lines of an image, that is the last line comes first
339 * (for some axis related reasons?). Hence he will have
340 * to load the image line by line, starting from the end.
341 * VTK users have to call GetImageData
343 * @param destination Address (in caller's memory space) at which the
344 * pixel data should be copied
345 * @param maxSize Maximum number of bytes to be copied. When MaxSize
346 * is not sufficient to hold the pixel data the copy is not
347 * executed (i.e. no partial copy).
348 * @return On success, the number of bytes actually copied. Zero on
349 * failure e.g. MaxSize is lower than necessary.
351 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
353 GetImageDataIntoVectorRaw (destination, maxSize);
354 PixelRead = 0 ; // =0 : no ImageDataRaw
355 if ( !HeaderInternal->HasLUT() )
357 return ImageDataSize;
360 // from Lut R + Lut G + Lut B
361 uint8_t *newDest = new uint8_t[ImageDataSize];
362 uint8_t *a = (uint8_t *)destination;
363 uint8_t *lutRGBA = HeaderInternal->GetLUTRGBA();
368 // move Gray pixels to temp area
369 memmove(newDest, destination, ImageDataSizeRaw);
370 for (size_t i=0; i<ImageDataSizeRaw; ++i)
380 // now, it's an RGB image
381 // Lets's write it in the Header
383 // FIXME : Better use CreateOrReplaceIfExist ?
385 std::string spp = "3"; // Samples Per Pixel
386 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
387 std::string rgb = "RGB "; // Photometric Interpretation
388 HeaderInternal->SetEntryByNumber(rgb,0x0028,0x0004);
389 std::string planConfig = "0"; // Planar Configuration
390 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
393 else // GetLUTRGBA() failed
395 // (gdcm-US-ALOKA-16.dcm), contains Segmented xxx Palette Color
396 // that are *more* than 65535 long ?!?
397 // No idea how to manage such an image !
398 // Need to make RGB Pixels (?) from grey Pixels (?!) and Gray Lut (!?!)
399 // It seems that *no Dicom Viewer* has any idea :-(
401 std::string photomInterp = "MONOCHROME1 "; // Photometric Interpretation
402 HeaderInternal->SetEntryByNumber(photomInterp,0x0028,0x0004);
405 /// \todo Drop Palette Color out of the Header?
406 return ImageDataSize;
410 * \brief Allocates necessary memory,
411 * Transforms YBR pixels (if any) into RGB pixels
412 * Transforms 3 planes R, G, B (if any) into a single RGB Plane
413 * Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
414 * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
415 * @return Pointer to newly allocated pixel data.
416 * \ NULL if alloc fails
418 uint8_t* File::GetImageDataRaw ()
421 if ( HeaderInternal->HasLUT() )
422 /// \todo Let Header user a chance to get the right value
423 imgDataSize = ImageDataSizeRaw;
425 imgDataSize = ImageDataSize;
428 // I need to deallocate Pixel_Data before doing any allocation:
431 if ( LastAllocatedPixelDataLength != imgDataSize )
434 Pixel_Data = new uint8_t[imgDataSize];
438 LastAllocatedPixelDataLength = imgDataSize;
440 // we load the pixels ( grey level or RGB, but NO transformation)
441 GetImageDataIntoVectorRaw(Pixel_Data, imgDataSize);
443 // We say the value *is* loaded.
444 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
445 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
447 // will be 7fe0, 0010 in standard cases
448 GetHeader()->SetEntryBinAreaByNumber(Pixel_Data,
449 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
451 PixelRead = 1; // PixelRaw
457 * \brief Copies at most MaxSize bytes of pixel data to caller's
459 * \warning This function was designed to avoid people that want to build
460 * a volume from an image stack to need first to get the image pixels
461 * and then move them to the volume area.
462 * It's absolutely useless for any VTK user since vtk chooses
463 * to invert the lines of an image, that is the last line comes first
464 * (for some axis related reasons?). Hence he will have
465 * to load the image line by line, starting from the end.
466 * VTK users hace to call GetImageData
467 * \warning DOES NOT transform the Grey Plane + Palette Color (if any)
468 * into a single RGB Pixels Plane
469 * the (VTK) user will manage the palettes
471 * @param destination Address (in caller's memory space) at which the
472 * pixel data should be copied
473 * @param maxSize Maximum number of bytes to be copied. When MaxSize
474 * is not sufficient to hold the pixel data the copy is not
475 * executed (i.e. no partial copy).
476 * @return On success, the number of bytes actually copied. Zero on
477 * failure e.g. MaxSize is lower than necessary.
479 size_t File::GetImageDataIntoVectorRaw (void* destination, size_t maxSize)
481 // we save the initial values of the following
482 // in order to be able to restore the header in a disk-consistent state
483 // (if user asks twice to get the pixels from disk)
485 if ( PixelRead != -1 ) // File was "read" before
487 RestoreInitialValues();
490 PixelRead = 1 ; // PixelRaw
492 if ( ImageDataSize > maxSize )
494 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
495 "than caller's expected MaxSize");
499 ReadPixelData( destination );
501 // Number of Bits Allocated for storing a Pixel
502 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
503 if ( numberBitsAllocated == 0 )
505 numberBitsAllocated = 16;
508 // Number of Bits actually used
509 int numberBitsStored = HeaderInternal->GetBitsStored();
510 if ( numberBitsStored == 0 )
512 numberBitsStored = numberBitsAllocated;
516 int highBitPosition = HeaderInternal->GetHighBitPosition();
517 if ( highBitPosition == 0 )
519 highBitPosition = numberBitsAllocated - 1;
522 bool signedPixel = HeaderInternal->IsSignedPixelData();
524 PixelConvert::ConvertReorderEndianity(
525 (uint8_t*) destination,
529 HeaderInternal->GetSwapCode(),
532 PixelConvert::ConvertReArrangeBits(
533 (uint8_t*) destination,
541 DebugFile = fopen( "SpuriousFile.RAW", "wb" );
542 fwrite( PixelConvertor.GetUncompressed(),
543 PixelConvertor.GetUncompressedsSize(),
549 //////////////////////////////////
550 // Deal with the color
552 // Monochrome pictures don't require color intervention
553 if ( HeaderInternal->IsMonochrome() )
555 return ImageDataSize;
558 // Planar configuration = 0 : Pixels are already RGB
559 // Planar configuration = 1 : 3 planes : R, G, B
560 // Planar configuration = 2 : 1 gray Plane + 3 LUT
562 // Well ... supposed to be !
563 // See US-PAL-8-10x-echo.dcm: PlanarConfiguration=0,
564 // PhotometricInterpretation=PALETTE COLOR
565 // and heuristic has to be found :-(
567 int planConf = HeaderInternal->GetPlanarConfiguration();
569 // Planar configuration = 2 ==> 1 gray Plane + 3 LUT
571 // whatever the Planar Configuration might be, "PALETTE COLOR "
572 // implies that we deal with the palette.
573 if ( ( planConf == 2 ) || HeaderInternal->IsPaletteColor() )
575 return ImageDataSize;
578 // When planConf is 0, pixels are allready in RGB
582 uint8_t* newDest = new uint8_t[ImageDataSize];
583 // Warning : YBR_FULL_422 acts as RGB
584 if ( HeaderInternal->IsYBRFull() )
586 ConvertYcBcRPlanesToRGBPixels((uint8_t*)destination, newDest);
590 ConvertRGBPlanesToRGBPixels((uint8_t*)destination, newDest);
592 memmove(destination, newDest, ImageDataSize);
596 ///////////////////////////////////////////////////
597 // now, it's an RGB image
598 // Lets's write it in the Header
600 // Droping Palette Color out of the Header
601 // has been moved to the Write process.
603 // TODO : move 'values' modification to the write process
604 // : save also (in order to be able to restore)
605 // : 'high bit' -when not equal to 'bits stored' + 1
606 // : 'bits allocated', when it's equal to 12 ?!
608 std::string spp = "3"; // Samples Per Pixel
609 std::string photInt = "RGB "; // Photometric Interpretation
610 std::string planConfig = "0"; // Planar Configuration
612 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
613 HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
614 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
616 return ImageDataSize;
620 * \brief Convert (Y plane, cB plane, cR plane) to RGB pixels
621 * \warning Works on all the frames at a time
623 void File::ConvertYcBcRPlanesToRGBPixels(uint8_t* source,
624 uint8_t* destination)
626 // to see the tricks about YBR_FULL, YBR_FULL_422,
627 // YBR_PARTIAL_422, YBR_ICT, YBR_RCT have a look at :
628 // ftp://medical.nema.org/medical/dicom/final/sup61_ft.pdf
629 // and be *very* affraid
631 int l = HeaderInternal->GetXSize() * HeaderInternal->GetYSize();
632 int nbFrames = HeaderInternal->GetZSize();
635 uint8_t* b = source + l;
636 uint8_t* c = source + l + l;
639 /// \todo : Replace by the 'well known' integer computation
640 /// counterpart. Refer to
641 /// http://lestourtereaux.free.fr/papers/data/yuvrgb.pdf
642 /// for code optimisation.
644 for (int i = 0; i < nbFrames; i++)
646 for (int j = 0; j < l; j++)
648 R = 1.164 *(*a-16) + 1.596 *(*c -128) + 0.5;
649 G = 1.164 *(*a-16) - 0.813 *(*c -128) - 0.392 *(*b -128) + 0.5;
650 B = 1.164 *(*a-16) + 2.017 *(*b -128) + 0.5;
652 if (R < 0.0) R = 0.0;
653 if (G < 0.0) G = 0.0;
654 if (B < 0.0) B = 0.0;
655 if (R > 255.0) R = 255.0;
656 if (G > 255.0) G = 255.0;
657 if (B > 255.0) B = 255.0;
659 *(destination++) = (uint8_t)R;
660 *(destination++) = (uint8_t)G;
661 *(destination++) = (uint8_t)B;
670 * \brief Convert (Red plane, Green plane, Blue plane) to RGB pixels
671 * \warning Works on all the frames at a time
673 void File::ConvertRGBPlanesToRGBPixels(uint8_t* source,
674 uint8_t* destination)
676 int l = HeaderInternal->GetXSize() * HeaderInternal->GetYSize() * HeaderInternal->GetZSize();
679 uint8_t* b = source + l;
680 uint8_t* c = source + l + l;
682 for (int j = 0; j < l; j++)
684 *(destination++) = *(a++);
685 *(destination++) = *(b++);
686 *(destination++) = *(c++);
691 * \brief Points the internal Pixel_Data pointer to the callers inData
692 * image representation, BUT WITHOUT COPYING THE DATA.
693 * 'image' Pixels are presented as C-like 2D arrays : line per line.
694 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
695 * \warning Since the pixels are not copied, it is the caller's responsability
696 * not to deallocate it's data before gdcm uses them (e.g. with
697 * the Write() method.
698 * @param inData user supplied pixel area
699 * @param expectedSize total image size, in Bytes
703 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
705 HeaderInternal->SetImageDataSize( expectedSize );
706 // FIXME : if already allocated, memory leak !
708 ImageDataSize = ImageDataSizeRaw = expectedSize;
710 // FIXME : 7fe0, 0010 IS NOT set ...
715 * \brief Writes on disk A SINGLE Dicom file
716 * NO test is performed on processor "Endiannity".
717 * It's up to the user to call his Reader properly
718 * @param fileName name of the file to be created
719 * (any already existing file is over written)
720 * @return false if write fails
723 bool File::WriteRawData(std::string const & fileName)
725 FILE* fp1 = fopen(fileName.c_str(), "wb");
728 printf("Fail to open (write) file [%s] \n", fileName.c_str());
731 fwrite (Pixel_Data, ImageDataSize, 1, fp1);
738 * \brief Writes on disk A SINGLE Dicom file,
739 * using the Implicit Value Representation convention
740 * NO test is performed on processor "Endiannity".
741 * @param fileName name of the file to be created
742 * (any already existing file is overwritten)
743 * @return false if write fails
746 bool File::WriteDcmImplVR (std::string const & fileName)
748 return WriteBase(fileName, ImplicitVR);
752 * \brief Writes on disk A SINGLE Dicom file,
753 * using the Explicit Value Representation convention
754 * NO test is performed on processor "Endiannity". * @param fileName name of the file to be created
755 * (any already existing file is overwritten)
756 * @return false if write fails
759 bool File::WriteDcmExplVR (std::string const & fileName)
761 return WriteBase(fileName, ExplicitVR);
765 * \brief Writes on disk A SINGLE Dicom file,
766 * using the ACR-NEMA convention
767 * NO test is performed on processor "Endiannity".
768 * (a l'attention des logiciels cliniques
769 * qui ne prennent en entrée QUE des images ACR ...
770 * \warning if a DICOM_V3 header is supplied,
771 * groups < 0x0008 and shadow groups are ignored
772 * \warning NO TEST is performed on processor "Endiannity".
773 * @param fileName name of the file to be created
774 * (any already existing file is overwritten)
775 * @return false if write fails
778 bool File::WriteAcr (std::string const & fileName)
780 return WriteBase(fileName, ACR);
783 //-----------------------------------------------------------------------------
786 * \brief NOT a end user inteded function
787 * (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
788 * @param fileName name of the file to be created
789 * (any already existing file is overwritten)
790 * @param type file type (ExplicitVR, ImplicitVR, ...)
791 * @return false if write fails
793 bool File::WriteBase (std::string const & fileName, FileType type)
795 if ( PixelRead == -1 && type != ExplicitVR)
800 FILE* fp1 = fopen(fileName.c_str(), "wb");
803 printf("Failed to open (write) File [%s] \n", fileName.c_str());
807 if ( type == ImplicitVR || type == ExplicitVR )
809 // writing Dicom File Preamble
810 uint8_t* filePreamble = new uint8_t[128];
811 memset(filePreamble, 0, 128);
812 fwrite(filePreamble, 128, 1, fp1);
813 fwrite("DICM", 4, 1, fp1);
815 delete[] filePreamble;
818 // --------------------------------------------------------------
819 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
821 // if recognition code tells us we dealt with a LibIDO image
822 // we reproduce on disk the switch between lineNumber and columnNumber
823 // just before writting ...
825 /// \todo the best trick would be *change* the recognition code
826 /// but pb expected if user deals with, e.g. COMPLEX images
828 std::string rows, columns;
829 if ( HeaderInternal->GetFileType() == ACR_LIBIDO)
831 rows = HeaderInternal->GetEntryByNumber(0x0028, 0x0010);
832 columns = HeaderInternal->GetEntryByNumber(0x0028, 0x0011);
834 HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0010);
835 HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0011);
837 // ----------------- End of Special Patch ----------------
839 uint16_t grPixel = HeaderInternal->GetGrPixel();
840 uint16_t numPixel = HeaderInternal->GetNumPixel();;
842 DocEntry* PixelElement =
843 GetHeader()->GetDocEntryByNumber(grPixel, numPixel);
845 if ( PixelRead == 1 )
847 // we read pixel 'as is' (no tranformation LUT -> RGB)
848 PixelElement->SetLength( ImageDataSizeRaw );
850 else if ( PixelRead == 0 )
852 // we tranformed GrayLevel pixels + LUT into RGB Pixel
853 PixelElement->SetLength( ImageDataSize );
856 HeaderInternal->Write(fp1, type);
858 // --------------------------------------------------------------
859 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
861 // ...and we restore the Header to be Dicom Compliant again
862 // just after writting
864 if ( HeaderInternal->GetFileType() == ACR_LIBIDO )
866 HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0010);
867 HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0011);
869 // ----------------- End of Special Patch ----------------
871 // fwrite(Pixel_Data, ImageDataSize, 1, fp1); // should be useless, now
877 //-----------------------------------------------------------------------------
880 * \brief Read pixel data from disk (optionaly decompressing) into the
881 * caller specified memory location.
882 * @param destination where the pixel data should be stored.
885 bool File::ReadPixelData(void* destination)
887 FILE* fp = HeaderInternal->OpenFile();
893 if ( fseek(fp, HeaderInternal->GetPixelOffset(), SEEK_SET) == -1 )
895 HeaderInternal->CloseFile();
899 if ( HeaderInternal->GetBitsAllocated() == 12 )
901 PixelConvert::ConvertDecompress12BitsTo16Bits(
902 (uint8_t*)destination,
903 HeaderInternal->GetXSize(),
904 HeaderInternal->GetYSize(),
906 HeaderInternal->CloseFile();
910 // ---------------------- Uncompressed File
911 if ( !HeaderInternal->IsDicomV3() ||
912 HeaderInternal->IsImplicitVRLittleEndianTransferSyntax() ||
913 HeaderInternal->IsExplicitVRLittleEndianTransferSyntax() ||
914 HeaderInternal->IsExplicitVRBigEndianTransferSyntax() ||
915 HeaderInternal->IsDeflatedExplicitVRLittleEndianTransferSyntax() )
917 size_t ItemRead = fread(destination, HeaderInternal->GetPixelAreaLength(), 1, fp);
918 HeaderInternal->CloseFile();
929 // ---------------------- Run Length Encoding
930 if ( HeaderInternal->IsRLELossLessTransferSyntax() )
932 bool res = PixelConvert::ReadAndDecompressRLEFile(
934 HeaderInternal->GetXSize(),
935 HeaderInternal->GetYSize(),
936 HeaderInternal->GetZSize(),
937 HeaderInternal->GetBitsAllocated(),
938 &(HeaderInternal->RLEInfo),
940 HeaderInternal->CloseFile();
944 // --------------- SingleFrame/Multiframe JPEG Lossless/Lossy/2000
945 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
946 if ( ( numberBitsAllocated == 0 ) || ( numberBitsAllocated == 12 ) )
948 numberBitsAllocated = 16;
951 bool res = PixelConvert::ReadAndDecompressJPEGFile(
952 (uint8_t*)destination,
953 HeaderInternal->GetXSize(),
954 HeaderInternal->GetYSize(),
955 HeaderInternal->GetBitsAllocated(),
956 HeaderInternal->GetBitsStored(),
957 HeaderInternal->GetSamplesPerPixel(),
958 HeaderInternal->GetPixelSize(),
959 HeaderInternal->IsJPEG2000(),
960 HeaderInternal->IsJPEGLossless(),
961 &(HeaderInternal->JPEGInfo),
963 HeaderInternal->CloseFile();
967 } // end namespace gdcm