1 /*=========================================================================
4 Module: $RCSfile: gdcmFile.cxx,v $
6 Date: $Date: 2004/11/05 21:23:46 $
7 Version: $Revision: 1.152 $
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"
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 PixelConverter = NULL; //just in case
77 if ( HeaderInternal->IsReadable() )
79 ImageDataSizeRaw = ComputeDecompressedPixelDataSizeFromHeader();
80 if ( HeaderInternal->HasLUT() )
82 ImageDataSize = 3 * ImageDataSizeRaw;
86 ImageDataSize = ImageDataSizeRaw;
89 PixelConverter = new PixelConvert; //LEAK !
90 PixelConverter->GrabInformationsFromHeader( HeaderInternal );
96 * \brief canonical destructor
97 * \note If the Header was created by the File constructor,
98 * it is destroyed by the File
104 delete HeaderInternal;
108 DeleteInitialValues();
111 //delete PixelConverter;
117 * \brief Sets some initial values for the Constructor
118 * \warning not end user intended
120 void File::SaveInitialValues()
123 PixelRead = -1; // no ImageData read yet.
124 LastAllocatedPixelDataLength = 0;
129 InitialPlanConfig = "";
130 InitialBitsAllocated = "";
133 InitialRedLUTDescr = 0;
134 InitialGreenLUTDescr = 0;
135 InitialBlueLUTDescr = 0;
136 InitialRedLUTData = 0;
137 InitialGreenLUTData = 0;
138 InitialBlueLUTData = 0;
140 if ( HeaderInternal->IsReadable() )
142 // the following values *may* be modified
143 // by File::GetImageDataIntoVectorRaw
144 // we save their initial value.
145 InitialSpp = HeaderInternal->GetEntryByNumber(0x0028,0x0002);
146 InitialPhotInt = HeaderInternal->GetEntryByNumber(0x0028,0x0004);
147 InitialPlanConfig = HeaderInternal->GetEntryByNumber(0x0028,0x0006);
149 InitialBitsAllocated = HeaderInternal->GetEntryByNumber(0x0028,0x0100);
150 InitialHighBit = HeaderInternal->GetEntryByNumber(0x0028,0x0102);
152 // the following entries *may* be removed from the H table
153 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
154 // we keep a pointer on them.
155 InitialRedLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1101);
156 InitialGreenLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1102);
157 InitialBlueLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1103);
159 InitialRedLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1201);
160 InitialGreenLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1202);
161 InitialBlueLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1203);
166 * \brief restores some initial values
167 * \warning not end user intended
169 void File::RestoreInitialValues()
171 if ( HeaderInternal->IsReadable() )
173 // the following values *may* have been modified
174 // by File::GetImageDataIntoVectorRaw
175 // we restore their initial value.
176 if ( InitialSpp != "")
177 HeaderInternal->SetEntryByNumber(InitialSpp,0x0028,0x0002);
178 if ( InitialPhotInt != "")
179 HeaderInternal->SetEntryByNumber(InitialPhotInt,0x0028,0x0004);
180 if ( InitialPlanConfig != "")
182 HeaderInternal->SetEntryByNumber(InitialPlanConfig,0x0028,0x0006);
183 if ( InitialBitsAllocated != "")
184 HeaderInternal->SetEntryByNumber(InitialBitsAllocated,0x0028,0x0100);
185 if ( InitialHighBit != "")
186 HeaderInternal->SetEntryByNumber(InitialHighBit,0x0028,0x0102);
188 // the following entries *may* be have been removed from the H table
189 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
192 if (InitialRedLUTDescr)
193 HeaderInternal->AddEntry(InitialRedLUTDescr);
194 if (InitialGreenLUTDescr)
195 HeaderInternal->AddEntry(InitialGreenLUTDescr);
196 if (InitialBlueLUTDescr)
197 HeaderInternal->AddEntry(InitialBlueLUTDescr);
199 if (InitialRedLUTData)
200 HeaderInternal->AddEntry(InitialBlueLUTDescr);
201 if (InitialGreenLUTData)
202 HeaderInternal->AddEntry(InitialGreenLUTData);
203 if (InitialBlueLUTData)
204 HeaderInternal->AddEntry(InitialBlueLUTData);
209 * \brief delete initial values (il they were saved)
210 * of InitialLutDescriptors and InitialLutData
212 void File::DeleteInitialValues()
215 // InitialLutDescriptors and InitialLutData
216 // will have to be deleted if the don't belong any longer
217 // to the Header H table when the header is deleted...
219 if ( InitialRedLUTDescr )
220 delete InitialRedLUTDescr;
222 if ( InitialGreenLUTDescr )
223 delete InitialGreenLUTDescr;
225 if ( InitialBlueLUTDescr )
226 delete InitialBlueLUTDescr;
228 if ( InitialRedLUTData )
229 delete InitialRedLUTData;
231 if ( InitialGreenLUTData != NULL)
232 delete InitialGreenLUTData;
234 if ( InitialBlueLUTData != NULL)
235 delete InitialBlueLUTData;
238 //-----------------------------------------------------------------------------
241 //-----------------------------------------------------------------------------
245 * \brief computes the length (in bytes) we must ALLOCATE to receive the
246 * image(s) pixels (multiframes taken into account)
247 * \warning : it is NOT the group 7FE0 length
248 * (no interest for compressed images).
250 int File::ComputeDecompressedPixelDataSizeFromHeader()
252 // see PS 3.3-2003 : C.7.6.3.2.1
262 // YBR_FULL_422 (no LUT, no Palette)
268 // ex : gdcm-US-ALOKA-16.dcm
269 // 0028|1221 [OW] [Segmented Red Palette Color Lookup Table Data]
270 // 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
271 // 0028|1223 [OW] [Segmented Blue Palette Color Lookup Table Data]
273 // ex : OT-PAL-8-face.dcm
274 // 0028|1201 [US] [Red Palette Color Lookup Table Data]
275 // 0028|1202 [US] [Green Palette Color Lookup Table Data]
276 // 0028|1203 [US] [Blue Palette Color Lookup Table Data]
278 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
279 // Number of "Bits Allocated" is fixed to 16 when:
280 // - it is not defined (i.e. it's value is 0)
281 // - it's 12, since we will expand the image to 16 bits (see
282 // PixelConvert::ConvertDecompress12BitsTo16Bits() )
283 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
285 numberBitsAllocated = 16;
288 int DecompressedSize = HeaderInternal->GetXSize()
289 * HeaderInternal->GetYSize()
290 * HeaderInternal->GetZSize()
291 * ( numberBitsAllocated / 8 )
292 * HeaderInternal->GetSamplesPerPixel();
294 return DecompressedSize;
298 * \brief - Allocates necessary memory,
299 * - Reads the pixels from disk (uncompress if necessary),
300 * - Transforms YBR pixels, if any, into RGB pixels
301 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
302 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
303 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
304 * @return Pointer to newly allocated pixel data.
305 * NULL if alloc fails
307 uint8_t* File::GetImageData()
309 if ( ! GetDecompressed() )
311 // If the decompression failed nothing can be done.
316 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
318 pixelData = PixelConverter->GetRGB();
322 // When no LUT or LUT conversion fails, return the decompressed
323 pixelData = PixelConverter->GetDecompressed();
326 // PIXELCONVERT CLEANME
327 // Restore the header in a disk-consistent state
328 // (if user asks twice to get the pixels from disk)
329 if ( PixelRead != -1 ) // File was "read" before
331 RestoreInitialValues();
333 if ( PixelConverter->GetRGB() )
335 // now, it's an RGB image
336 // Lets's write it in the Header
337 std::string spp = "3"; // Samples Per Pixel
338 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
339 std::string rgb = "RGB "; // Photometric Interpretation
340 HeaderInternal->SetEntryByNumber(rgb,0x0028,0x0004);
341 std::string planConfig = "0"; // Planar Configuration
342 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
343 PixelRead = 0; // no PixelRaw
347 if ( HeaderInternal->HasLUT() )
349 // The LUT interpretation failed
350 std::string photometricInterpretation = "MONOCHROME1 ";
351 HeaderInternal->SetEntryByNumber( photometricInterpretation,
353 PixelRead = 0; // no PixelRaw
357 if ( PixelConverter->IsDecompressedRGB() )
359 ///////////////////////////////////////////////////
360 // now, it's an RGB image
361 // Lets's write it in the Header
362 // Droping Palette Color out of the Header
363 // has been moved to the Write process.
364 // TODO : move 'values' modification to the write process
365 // : save also (in order to be able to restore)
366 // : 'high bit' -when not equal to 'bits stored' + 1
367 // : 'bits allocated', when it's equal to 12 ?!
368 std::string spp = "3"; // Samples Per Pixel
369 std::string photInt = "RGB "; // Photometric Interpretation
370 std::string planConfig = "0"; // Planar Configuration
371 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
372 HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
373 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
375 PixelRead = 1; // PixelRaw
379 // We say the value *is* loaded.
380 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
381 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
383 // Will be 7fe0, 0010 in standard case
384 GetHeader()->SetEntryBinAreaByNumber( pixelData,
385 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
386 // END PIXELCONVERT CLEANME
393 * Read 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 at most MaxSize bytes of pixel data to caller allocated
399 * \warning This function allows people that want to build a volume
400 * from an image stack *not to* have, first to get the image pixels,
401 * and then move them to the volume area.
402 * It's absolutely useless for any VTK user since vtk chooses
403 * to invert the lines of an image, that is the last line comes first
404 * (for some axis related reasons?). Hence he will have
405 * to load the image line by line, starting from the end.
406 * VTK users have to call GetImageData
408 * @param destination Address (in caller's memory space) at which the
409 * pixel data should be copied
410 * @param maxSize Maximum number of bytes to be copied. When MaxSize
411 * is not sufficient to hold the pixel data the copy is not
412 * executed (i.e. no partial copy).
413 * @return On success, the number of bytes actually copied. Zero on
414 * failure e.g. MaxSize is lower than necessary.
416 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
418 if ( ! GetDecompressed() )
420 // If the decompression failed nothing can be done.
424 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
426 if ( PixelConverter->GetRGBSize() > maxSize )
428 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
429 "than caller's expected MaxSize");
432 memmove( destination,
433 (void*)PixelConverter->GetRGB(),
434 PixelConverter->GetRGBSize() );
435 return PixelConverter->GetRGBSize();
438 // Either no LUT conversion necessary or LUT conversion failed
439 if ( PixelConverter->GetDecompressedSize() > maxSize )
441 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
442 "than caller's expected MaxSize");
445 memmove( destination,
446 (void*)PixelConverter->GetDecompressed(),
447 PixelConverter->GetDecompressedSize() );
448 return PixelConverter->GetDecompressedSize();
452 * \brief Allocates necessary memory,
453 * Transforms YBR pixels (if any) into RGB pixels
454 * Transforms 3 planes R, G, B (if any) into a single RGB Plane
455 * Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
456 * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
457 * @return Pointer to newly allocated pixel data.
458 * \ NULL if alloc fails
460 uint8_t* File::GetImageDataRaw ()
462 uint8_t* decompressed = GetDecompressed();
463 if ( ! decompressed )
468 // PIXELCONVERT CLEANME
469 // Restore the header in a disk-consistent state
470 // (if user asks twice to get the pixels from disk)
471 if ( PixelRead != -1 ) // File was "read" before
473 RestoreInitialValues();
475 if ( PixelConverter->IsDecompressedRGB() )
477 ///////////////////////////////////////////////////
478 // now, it's an RGB image
479 // Lets's write it in the Header
480 // Droping Palette Color out of the Header
481 // has been moved to the Write process.
482 // TODO : move 'values' modification to the write process
483 // : save also (in order to be able to restore)
484 // : 'high bit' -when not equal to 'bits stored' + 1
485 // : 'bits allocated', when it's equal to 12 ?!
486 std::string spp = "3"; // Samples Per Pixel
487 std::string photInt = "RGB "; // Photometric Interpretation
488 std::string planConfig = "0"; // Planar Configuration
489 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
490 HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
491 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
494 // We say the value *is* loaded.
495 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
496 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
498 // will be 7fe0, 0010 in standard cases
499 GetHeader()->SetEntryBinAreaByNumber( decompressed,
500 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
502 PixelRead = 1; // PixelRaw
503 // END PIXELCONVERT CLEANME
508 uint8_t* File::GetDecompressed()
510 uint8_t* decompressed = PixelConverter->GetDecompressed();
511 if ( ! decompressed )
513 // The decompressed image migth not be loaded yet:
514 std::ifstream* fp = HeaderInternal->OpenFile();
515 PixelConverter->ReadAndDecompressPixelData( fp );
516 HeaderInternal->CloseFile();
517 decompressed = PixelConverter->GetDecompressed();
518 if ( ! decompressed )
520 dbg.Verbose(0, "File::GetDecompressed: read/decompress of "
521 "pixel data apparently went wrong.");
530 * \brief Points the internal Pixel_Data pointer to the callers inData
531 * image representation, BUT WITHOUT COPYING THE DATA.
532 * 'image' Pixels are presented as C-like 2D arrays : line per line.
533 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
534 * \warning Since the pixels are not copied, it is the caller's responsability
535 * not to deallocate it's data before gdcm uses them (e.g. with
536 * the Write() method.
537 * @param inData user supplied pixel area
538 * @param expectedSize total image size, in Bytes
542 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
544 HeaderInternal->SetImageDataSize( expectedSize );
545 // FIXME : if already allocated, memory leak !
547 ImageDataSize = ImageDataSizeRaw = expectedSize;
549 // FIXME : 7fe0, 0010 IS NOT set ...
554 * \brief Writes on disk A SINGLE Dicom file
555 * NO test is performed on processor "Endiannity".
556 * It's up to the user to call his Reader properly
557 * @param fileName name of the file to be created
558 * (any already existing file is over written)
559 * @return false if write fails
562 bool File::WriteRawData(std::string const & fileName)
564 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
567 dbg.Verbose(2, "Fail to open (write) file:", fileName.c_str());
570 fp1.write((char*)Pixel_Data, ImageDataSize);
577 * \brief Writes on disk A SINGLE Dicom file,
578 * using the Implicit Value Representation convention
579 * NO test is performed on processor "Endiannity".
580 * @param fileName name of the file to be created
581 * (any already existing file is overwritten)
582 * @return false if write fails
585 bool File::WriteDcmImplVR (std::string const & fileName)
587 return WriteBase(fileName, ImplicitVR);
591 * \brief Writes on disk A SINGLE Dicom file,
592 * using the Explicit Value Representation convention
593 * NO test is performed on processor "Endiannity". * @param fileName name of the file to be created
594 * (any already existing file is overwritten)
595 * @return false if write fails
598 bool File::WriteDcmExplVR (std::string const & fileName)
600 return WriteBase(fileName, ExplicitVR);
604 * \brief Writes on disk A SINGLE Dicom file,
605 * using the ACR-NEMA convention
606 * NO test is performed on processor "Endiannity".
607 * (a l'attention des logiciels cliniques
608 * qui ne prennent en entrée QUE des images ACR ...
609 * \warning if a DICOM_V3 header is supplied,
610 * groups < 0x0008 and shadow groups are ignored
611 * \warning NO TEST is performed on processor "Endiannity".
612 * @param fileName name of the file to be created
613 * (any already existing file is overwritten)
614 * @return false if write fails
617 bool File::WriteAcr (std::string const & fileName)
619 return WriteBase(fileName, ACR);
622 //-----------------------------------------------------------------------------
625 * \brief NOT a end user inteded function
626 * (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
627 * @param fileName name of the file to be created
628 * (any already existing file is overwritten)
629 * @param type file type (ExplicitVR, ImplicitVR, ...)
630 * @return false if write fails
632 bool File::WriteBase (std::string const & fileName, FileType type)
634 if ( PixelRead == -1 && type != ExplicitVR)
639 std::ofstream* fp1 = new std::ofstream(fileName.c_str(),
640 std::ios::out | std::ios::binary);
643 dbg.Verbose(2, "Failed to open (write) File: " , fileName.c_str());
647 if ( type == ImplicitVR || type == ExplicitVR )
649 // writing Dicom File Preamble
650 char filePreamble[128];
651 memset(filePreamble, 0, 128);
652 fp1->write(filePreamble, 128);
653 fp1->write("DICM", 4);
656 // --------------------------------------------------------------
657 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
659 // if recognition code tells us we dealt with a LibIDO image
660 // we reproduce on disk the switch between lineNumber and columnNumber
661 // just before writting ...
663 /// \todo the best trick would be *change* the recognition code
664 /// but pb expected if user deals with, e.g. COMPLEX images
666 std::string rows, columns;
667 if ( HeaderInternal->GetFileType() == ACR_LIBIDO)
669 rows = HeaderInternal->GetEntryByNumber(0x0028, 0x0010);
670 columns = HeaderInternal->GetEntryByNumber(0x0028, 0x0011);
672 HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0010);
673 HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0011);
675 // ----------------- End of Special Patch ----------------
677 uint16_t grPixel = HeaderInternal->GetGrPixel();
678 uint16_t numPixel = HeaderInternal->GetNumPixel();;
680 DocEntry* PixelElement =
681 GetHeader()->GetDocEntryByNumber(grPixel, numPixel);
683 if ( PixelRead == 1 )
685 // we read pixel 'as is' (no tranformation LUT -> RGB)
686 PixelElement->SetLength( ImageDataSizeRaw );
688 else if ( PixelRead == 0 )
690 // we tranformed GrayLevel pixels + LUT into RGB Pixel
691 PixelElement->SetLength( ImageDataSize );
694 HeaderInternal->Write(fp1, type);
696 // --------------------------------------------------------------
697 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
699 // ...and we restore the Header to be Dicom Compliant again
700 // just after writting
702 if ( HeaderInternal->GetFileType() == ACR_LIBIDO )
704 HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0010);
705 HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0011);
707 // ----------------- End of Special Patch ----------------
715 * \brief Access to the underlying \ref PixelConverter RGBA LUT
717 uint8_t* File::GetLutRGBA()
719 return PixelConverter->GetLutRGBA();
722 } // end namespace gdcm