1 /*=========================================================================
4 Module: $RCSfile: gdcmFile.cxx,v $
6 Date: $Date: 2004/11/16 16:20:23 $
7 Version: $Revision: 1.157 $
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"
22 #include "gdcmBinEntry.h"
27 typedef std::pair<TagDocEntryHT::iterator,TagDocEntryHT::iterator> IterHT;
29 //-------------------------------------------------------------------------
30 // Constructor / Destructor
32 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
33 * file (Header only deals with the ... header)
34 * Opens (in read only and when possible) an existing file and checks
35 * for DICOM compliance. Returns NULL on failure.
36 * It will be up to the user to load the pixels into memory
37 * (see GetImageData, GetImageDataRaw)
38 * \note the in-memory representation of all available tags found in
39 * the DICOM header is post-poned to first header information access.
40 * This avoid a double parsing of public part of the header when
41 * user sets an a posteriori shadow dictionary (efficiency can be
42 * seen as a side effect).
43 * @param header already built Header
45 File::File(Header *header)
47 HeaderInternal = header;
53 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
54 * file (Header only deals with the ... header)
55 * Opens (in read only and when possible) an existing file and checks
56 * for DICOM compliance. Returns NULL on failure.
57 * It will be up to the user to load the pixels into memory
58 * (see GetImageData, GetImageDataRaw)
59 * \note the in-memory representation of all available tags found in
60 * the DICOM header is post-poned to first header information access.
61 * This avoid a double parsing of public part of the header when
62 * one sets an a posteriori shadow dictionary (efficiency can be
63 * seen as a side effect).
64 * @param filename file to be opened for parsing
66 File::File(std::string const & filename )
68 HeaderInternal = new Header( filename );
74 * \brief Factorization for various forms of constructors.
76 void File::Initialise()
78 PixelConverter = NULL; //just in case
79 if ( HeaderInternal->IsReadable() )
81 ImageDataSizeRaw = ComputeDecompressedPixelDataSizeFromHeader();
82 if ( HeaderInternal->HasLUT() )
84 ImageDataSize = 3 * ImageDataSizeRaw;
88 ImageDataSize = ImageDataSizeRaw;
91 PixelConverter = new PixelConvert;
92 PixelConverter->GrabInformationsFromHeader( HeaderInternal );
98 * \brief canonical destructor
99 * \note If the Header was created by the File constructor,
100 * it is destroyed by the File
106 delete HeaderInternal;
110 DeleteInitialValues();
113 delete PixelConverter;
118 * \brief Sets some initial values for the Constructor
119 * \warning not end user intended
121 void File::SaveInitialValues()
124 PixelRead = -1; // no ImageData read yet.
125 LastAllocatedPixelDataLength = 0;
130 InitialPlanConfig = "";
131 InitialBitsAllocated = "";
134 InitialRedLUTDescr = 0;
135 InitialGreenLUTDescr = 0;
136 InitialBlueLUTDescr = 0;
137 InitialRedLUTData = 0;
138 InitialGreenLUTData = 0;
139 InitialBlueLUTData = 0;
141 if ( HeaderInternal->IsReadable() )
143 // the following values *may* be modified
144 // by File::GetImageDataIntoVectorRaw
145 // we save their initial value.
146 InitialSpp = HeaderInternal->GetEntryByNumber(0x0028,0x0002);
147 InitialPhotInt = HeaderInternal->GetEntryByNumber(0x0028,0x0004);
148 InitialPlanConfig = HeaderInternal->GetEntryByNumber(0x0028,0x0006);
150 InitialBitsAllocated = HeaderInternal->GetEntryByNumber(0x0028,0x0100);
151 InitialHighBit = HeaderInternal->GetEntryByNumber(0x0028,0x0102);
153 // the following entries *may* be removed from the H table
154 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
155 // we keep a pointer on them.
156 InitialRedLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1101);
157 InitialGreenLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1102);
158 InitialBlueLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1103);
160 InitialRedLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1201);
161 InitialGreenLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1202);
162 InitialBlueLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1203);
167 * \brief restores some initial values
168 * \warning not end user intended
170 void File::RestoreInitialValues()
172 if ( HeaderInternal->IsReadable() )
174 // the following values *may* have been modified
175 // by File::GetImageDataIntoVectorRaw
176 // we restore their initial value.
177 if ( InitialSpp != "")
178 HeaderInternal->SetEntryByNumber(InitialSpp,0x0028,0x0002);
179 if ( InitialPhotInt != "")
180 HeaderInternal->SetEntryByNumber(InitialPhotInt,0x0028,0x0004);
181 if ( InitialPlanConfig != "")
183 HeaderInternal->SetEntryByNumber(InitialPlanConfig,0x0028,0x0006);
184 if ( InitialBitsAllocated != "")
185 HeaderInternal->SetEntryByNumber(InitialBitsAllocated,0x0028,0x0100);
186 if ( InitialHighBit != "")
187 HeaderInternal->SetEntryByNumber(InitialHighBit,0x0028,0x0102);
189 // the following entries *may* be have been removed from the H table
190 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
193 if (InitialRedLUTDescr)
194 HeaderInternal->AddEntry(InitialRedLUTDescr);
195 if (InitialGreenLUTDescr)
196 HeaderInternal->AddEntry(InitialGreenLUTDescr);
197 if (InitialBlueLUTDescr)
198 HeaderInternal->AddEntry(InitialBlueLUTDescr);
200 if (InitialRedLUTData)
201 HeaderInternal->AddEntry(InitialBlueLUTDescr);
202 if (InitialGreenLUTData)
203 HeaderInternal->AddEntry(InitialGreenLUTData);
204 if (InitialBlueLUTData)
205 HeaderInternal->AddEntry(InitialBlueLUTData);
210 * \brief delete initial values (il they were saved)
211 * of InitialLutDescriptors and InitialLutData
213 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...
220 // We don't know if the InitialLutData are still in the header or not !
221 /* if ( InitialRedLUTDescr )
222 delete InitialRedLUTDescr;
224 if ( InitialGreenLUTDescr )
225 delete InitialGreenLUTDescr;
227 if ( InitialBlueLUTDescr )
228 delete InitialBlueLUTDescr;
230 if ( InitialRedLUTData )
231 delete InitialRedLUTData;
233 if ( InitialGreenLUTData )
234 delete InitialGreenLUTData;
236 if ( InitialBlueLUTData )
237 delete InitialBlueLUTData;*/
240 //-----------------------------------------------------------------------------
243 //-----------------------------------------------------------------------------
247 * \brief computes the length (in bytes) we must ALLOCATE to receive the
248 * image(s) pixels (multiframes taken into account)
249 * \warning : it is NOT the group 7FE0 length
250 * (no interest for compressed images).
252 int File::ComputeDecompressedPixelDataSizeFromHeader()
254 // see PS 3.3-2003 : C.7.6.3.2.1
264 // YBR_FULL_422 (no LUT, no Palette)
270 // ex : gdcm-US-ALOKA-16.dcm
271 // 0028|1221 [OW] [Segmented Red Palette Color Lookup Table Data]
272 // 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
273 // 0028|1223 [OW] [Segmented Blue Palette Color Lookup Table Data]
275 // ex : OT-PAL-8-face.dcm
276 // 0028|1201 [US] [Red Palette Color Lookup Table Data]
277 // 0028|1202 [US] [Green Palette Color Lookup Table Data]
278 // 0028|1203 [US] [Blue Palette Color Lookup Table Data]
280 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
281 // Number of "Bits Allocated" is fixed to 16 when:
282 // - it is not defined (i.e. it's value is 0)
283 // - it's 12, since we will expand the image to 16 bits (see
284 // PixelConvert::ConvertDecompress12BitsTo16Bits() )
285 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
287 numberBitsAllocated = 16;
290 int DecompressedSize = HeaderInternal->GetXSize()
291 * HeaderInternal->GetYSize()
292 * HeaderInternal->GetZSize()
293 * ( numberBitsAllocated / 8 )
294 * HeaderInternal->GetSamplesPerPixel();
296 return DecompressedSize;
300 * \brief - Allocates necessary memory,
301 * - Reads the pixels from disk (uncompress if necessary),
302 * - Transforms YBR pixels, if any, into RGB pixels
303 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
304 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
305 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
306 * @return Pointer to newly allocated pixel data.
307 * NULL if alloc fails
309 uint8_t* File::GetImageData()
311 if ( ! GetDecompressed() )
313 // If the decompression failed nothing can be done.
318 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
320 pixelData = PixelConverter->GetRGB();
324 // When no LUT or LUT conversion fails, return the decompressed
325 pixelData = PixelConverter->GetDecompressed();
328 // PIXELCONVERT CLEANME
329 // Restore the header in a disk-consistent state
330 // (if user asks twice to get the pixels from disk)
331 if ( PixelRead != -1 ) // File was "read" before
333 RestoreInitialValues();
335 if ( PixelConverter->GetRGB() )
337 // now, it's an RGB image
338 // Lets's write it in the Header
339 std::string spp = "3"; // Samples Per Pixel
340 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
341 std::string rgb = "RGB "; // Photometric Interpretation
342 HeaderInternal->SetEntryByNumber(rgb,0x0028,0x0004);
343 std::string planConfig = "0"; // Planar Configuration
344 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
345 PixelRead = 0; // no PixelRaw
349 if ( HeaderInternal->HasLUT() )
351 // The LUT interpretation failed
352 std::string photometricInterpretation = Util::DicomString("MONOCHROME1");
353 HeaderInternal->SetEntryByNumber( photometricInterpretation,
355 PixelRead = 0; // no PixelRaw
359 if ( PixelConverter->IsDecompressedRGB() )
361 ///////////////////////////////////////////////////
362 // now, it's an RGB image
363 // Lets's write it in the Header
364 // Droping Palette Color out of the Header
365 // has been moved to the Write process.
366 // TODO : move 'values' modification to the write process
367 // : save also (in order to be able to restore)
368 // : 'high bit' -when not equal to 'bits stored' + 1
369 // : 'bits allocated', when it's equal to 12 ?!
370 std::string spp = "3"; // Samples Per Pixel
371 std::string photInt = "RGB "; // Photometric Interpretation
372 std::string planConfig = "0"; // Planar Configuration
373 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
374 HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
375 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
377 PixelRead = 1; // PixelRaw
381 // We say the value *is* loaded.
382 SetPixelData(pixelData);
383 // END PIXELCONVERT CLEANME
390 * Read the pixels from disk (uncompress if necessary),
391 * Transforms YBR pixels, if any, into RGB pixels
392 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
393 * Transforms single Grey plane + 3 Palettes into a RGB Plane
394 * Copies at most MaxSize bytes of pixel data to caller allocated
396 * \warning This function allows people that want to build a volume
397 * from an image stack *not to* have, first to get the image pixels,
398 * and then move them to the volume area.
399 * It's absolutely useless for any VTK user since vtk chooses
400 * to invert the lines of an image, that is the last line comes first
401 * (for some axis related reasons?). Hence he will have
402 * to load the image line by line, starting from the end.
403 * VTK users have to call GetImageData
405 * @param destination Address (in caller's memory space) at which the
406 * pixel data should be copied
407 * @param maxSize Maximum number of bytes to be copied. When MaxSize
408 * is not sufficient to hold the pixel data the copy is not
409 * executed (i.e. no partial copy).
410 * @return On success, the number of bytes actually copied. Zero on
411 * failure e.g. MaxSize is lower than necessary.
413 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
415 if ( ! GetDecompressed() )
417 // If the decompression failed nothing can be done.
421 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
423 if ( PixelConverter->GetRGBSize() > maxSize )
425 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
426 "than caller's expected MaxSize");
430 (void*)PixelConverter->GetRGB(),
431 PixelConverter->GetRGBSize() );
432 return PixelConverter->GetRGBSize();
435 // Either no LUT conversion necessary or LUT conversion failed
436 if ( PixelConverter->GetDecompressedSize() > maxSize )
438 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
439 "than caller's expected MaxSize");
443 (void*)PixelConverter->GetDecompressed(),
444 PixelConverter->GetDecompressedSize() );
445 return PixelConverter->GetDecompressedSize();
449 * \brief Allocates necessary memory,
450 * Transforms YBR pixels (if any) into RGB pixels
451 * Transforms 3 planes R, G, B (if any) into a single RGB Plane
452 * Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
453 * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
454 * @return Pointer to newly allocated pixel data.
455 * \ NULL if alloc fails
457 uint8_t* File::GetImageDataRaw ()
459 uint8_t* decompressed = GetDecompressed();
460 if ( ! decompressed )
465 // PIXELCONVERT CLEANME
466 // Restore the header in a disk-consistent state
467 // (if user asks twice to get the pixels from disk)
468 if ( PixelRead != -1 ) // File was "read" before
470 RestoreInitialValues();
472 if ( PixelConverter->IsDecompressedRGB() )
474 ///////////////////////////////////////////////////
475 // now, it's an RGB image
476 // Lets's write it in the Header
477 // Droping Palette Color out of the Header
478 // has been moved to the Write process.
479 // TODO : move 'values' modification to the write process
480 // : save also (in order to be able to restore)
481 // : 'high bit' -when not equal to 'bits stored' + 1
482 // : 'bits allocated', when it's equal to 12 ?!
483 std::string spp = "3"; // Samples Per Pixel
484 std::string photInt = "RGB "; // Photometric Interpretation
485 std::string planConfig = "0"; // Planar Configuration
486 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
487 HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
488 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
491 // We say the value *is* loaded.
492 /* GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
493 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
495 // will be 7fe0, 0010 in standard cases
496 GetHeader()->SetEntryBinAreaByNumber( decompressed,
497 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());*/
498 SetPixelData(decompressed);
500 PixelRead = 1; // PixelRaw
501 // END PIXELCONVERT CLEANME
506 uint8_t* File::GetDecompressed()
508 uint8_t* decompressed = PixelConverter->GetDecompressed();
509 if ( ! decompressed )
511 // The decompressed image migth not be loaded yet:
512 std::ifstream* fp = HeaderInternal->OpenFile();
513 PixelConverter->ReadAndDecompressPixelData( fp );
514 if(fp) HeaderInternal->CloseFile();
515 decompressed = PixelConverter->GetDecompressed();
516 if ( ! decompressed )
518 dbg.Verbose(0, "File::GetDecompressed: read/decompress of "
519 "pixel data apparently went wrong.");
528 * \brief Points the internal Pixel_Data pointer to the callers inData
529 * image representation, BUT WITHOUT COPYING THE DATA.
530 * 'image' Pixels are presented as C-like 2D arrays : line per line.
531 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
532 * \warning Since the pixels are not copied, it is the caller's responsability
533 * not to deallocate it's data before gdcm uses them (e.g. with
534 * the Write() method.
535 * @param inData user supplied pixel area
536 * @param expectedSize total image size, in Bytes
540 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
542 HeaderInternal->SetImageDataSize( expectedSize );
543 // FIXME : if already allocated, memory leak !
545 ImageDataSize = ImageDataSizeRaw = expectedSize;
547 // FIXME : 7fe0, 0010 IS NOT set ...
552 * \brief Writes on disk A SINGLE Dicom file
553 * NO test is performed on processor "Endiannity".
554 * It's up to the user to call his Reader properly
555 * @param fileName name of the file to be created
556 * (any already existing file is over written)
557 * @return false if write fails
560 bool File::WriteRawData(std::string const & fileName)
562 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
565 dbg.Verbose(2, "Fail to open (write) file:", fileName.c_str());
568 fp1.write((char*)Pixel_Data, ImageDataSize);
575 * \brief Writes on disk A SINGLE Dicom file,
576 * using the Implicit Value Representation convention
577 * NO test is performed on processor "Endiannity".
578 * @param fileName name of the file to be created
579 * (any already existing file is overwritten)
580 * @return false if write fails
583 bool File::WriteDcmImplVR (std::string const & fileName)
585 return WriteBase(fileName, ImplicitVR);
589 * \brief Writes on disk A SINGLE Dicom file,
590 * using the Explicit Value Representation convention
591 * NO test is performed on processor "Endiannity". * @param fileName name of the file to be created
592 * (any already existing file is overwritten)
593 * @return false if write fails
596 bool File::WriteDcmExplVR (std::string const & fileName)
598 return WriteBase(fileName, ExplicitVR);
602 * \brief Writes on disk A SINGLE Dicom file,
603 * using the ACR-NEMA convention
604 * NO test is performed on processor "Endiannity".
605 * (a l'attention des logiciels cliniques
606 * qui ne prennent en entrée QUE des images ACR ...
607 * \warning if a DICOM_V3 header is supplied,
608 * groups < 0x0008 and shadow groups are ignored
609 * \warning NO TEST is performed on processor "Endiannity".
610 * @param fileName name of the file to be created
611 * (any already existing file is overwritten)
612 * @return false if write fails
615 bool File::WriteAcr (std::string const & fileName)
617 return WriteBase(fileName, ACR);
620 //-----------------------------------------------------------------------------
623 * \brief NOT a end user inteded function
624 * (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
625 * @param fileName name of the file to be created
626 * (any already existing file is overwritten)
627 * @param type file type (ExplicitVR, ImplicitVR, ...)
628 * @return false if write fails
630 bool File::WriteBase (std::string const & fileName, FileType type)
632 if ( PixelRead == -1 && type != ExplicitVR)
637 std::ofstream* fp1 = new std::ofstream(fileName.c_str(),
638 std::ios::out | std::ios::binary);
641 dbg.Verbose(2, "Failed to open (write) File: " , fileName.c_str());
645 if ( type == ImplicitVR || type == ExplicitVR )
647 // writing Dicom File Preamble
648 char filePreamble[128];
649 memset(filePreamble, 0, 128);
650 fp1->write(filePreamble, 128);
651 fp1->write("DICM", 4);
654 // --------------------------------------------------------------
655 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
657 // if recognition code tells us we dealt with a LibIDO image
658 // we reproduce on disk the switch between lineNumber and columnNumber
659 // just before writting ...
661 /// \todo the best trick would be *change* the recognition code
662 /// but pb expected if user deals with, e.g. COMPLEX images
664 std::string rows, columns;
665 if ( HeaderInternal->GetFileType() == ACR_LIBIDO)
667 rows = HeaderInternal->GetEntryByNumber(0x0028, 0x0010);
668 columns = HeaderInternal->GetEntryByNumber(0x0028, 0x0011);
670 HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0010);
671 HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0011);
673 // ----------------- End of Special Patch ----------------
675 uint16_t grPixel = HeaderInternal->GetGrPixel();
676 uint16_t numPixel = HeaderInternal->GetNumPixel();;
678 DocEntry* PixelElement =
679 GetHeader()->GetDocEntryByNumber(grPixel, numPixel);
681 if ( PixelRead == 1 )
683 // we read pixel 'as is' (no tranformation LUT -> RGB)
684 PixelElement->SetLength( ImageDataSizeRaw );
686 else if ( PixelRead == 0 )
688 // we tranformed GrayLevel pixels + LUT into RGB Pixel
689 PixelElement->SetLength( ImageDataSize );
692 HeaderInternal->Write(fp1, type);
694 // --------------------------------------------------------------
695 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
697 // ...and we restore the Header to be Dicom Compliant again
698 // just after writting
700 if ( HeaderInternal->GetFileType() == ACR_LIBIDO )
702 HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0010);
703 HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0011);
705 // ----------------- End of Special Patch ----------------
713 * \brief Access to the underlying \ref PixelConverter RGBA LUT
715 uint8_t* File::GetLutRGBA()
717 return PixelConverter->GetLutRGBA();
720 //-----------------------------------------------------------------------------
722 void File::SetPixelData(uint8_t* data)
724 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
725 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
727 // Will be 7fe0, 0010 in standard case
728 DocEntry* currentEntry = GetHeader()->GetDocEntryByNumber(GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
731 if ( BinEntry* binEntry = dynamic_cast<BinEntry *>(currentEntry) )
732 // Flag is to false because datas are kept in the gdcmPixelConvert
733 binEntry->SetBinArea( data, false );
737 //-----------------------------------------------------------------------------
738 } // end namespace gdcm