1 /*=========================================================================
4 Module: $RCSfile: gdcmFile.cxx,v $
6 Date: $Date: 2004/11/16 02:54:35 $
7 Version: $Revision: 1.155 $
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;
116 * \brief Sets some initial values for the Constructor
117 * \warning not end user intended
119 void File::SaveInitialValues()
122 PixelRead = -1; // no ImageData read yet.
123 LastAllocatedPixelDataLength = 0;
128 InitialPlanConfig = "";
129 InitialBitsAllocated = "";
132 InitialRedLUTDescr = 0;
133 InitialGreenLUTDescr = 0;
134 InitialBlueLUTDescr = 0;
135 InitialRedLUTData = 0;
136 InitialGreenLUTData = 0;
137 InitialBlueLUTData = 0;
139 if ( HeaderInternal->IsReadable() )
141 // the following values *may* be modified
142 // by File::GetImageDataIntoVectorRaw
143 // we save their initial value.
144 InitialSpp = HeaderInternal->GetEntryByNumber(0x0028,0x0002);
145 InitialPhotInt = HeaderInternal->GetEntryByNumber(0x0028,0x0004);
146 InitialPlanConfig = HeaderInternal->GetEntryByNumber(0x0028,0x0006);
148 InitialBitsAllocated = HeaderInternal->GetEntryByNumber(0x0028,0x0100);
149 InitialHighBit = HeaderInternal->GetEntryByNumber(0x0028,0x0102);
151 // the following entries *may* be removed from the H table
152 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
153 // we keep a pointer on them.
154 InitialRedLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1101);
155 InitialGreenLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1102);
156 InitialBlueLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1103);
158 InitialRedLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1201);
159 InitialGreenLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1202);
160 InitialBlueLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1203);
165 * \brief restores some initial values
166 * \warning not end user intended
168 void File::RestoreInitialValues()
170 if ( HeaderInternal->IsReadable() )
172 // the following values *may* have been modified
173 // by File::GetImageDataIntoVectorRaw
174 // we restore their initial value.
175 if ( InitialSpp != "")
176 HeaderInternal->SetEntryByNumber(InitialSpp,0x0028,0x0002);
177 if ( InitialPhotInt != "")
178 HeaderInternal->SetEntryByNumber(InitialPhotInt,0x0028,0x0004);
179 if ( InitialPlanConfig != "")
181 HeaderInternal->SetEntryByNumber(InitialPlanConfig,0x0028,0x0006);
182 if ( InitialBitsAllocated != "")
183 HeaderInternal->SetEntryByNumber(InitialBitsAllocated,0x0028,0x0100);
184 if ( InitialHighBit != "")
185 HeaderInternal->SetEntryByNumber(InitialHighBit,0x0028,0x0102);
187 // the following entries *may* be have been removed from the H table
188 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
191 if (InitialRedLUTDescr)
192 HeaderInternal->AddEntry(InitialRedLUTDescr);
193 if (InitialGreenLUTDescr)
194 HeaderInternal->AddEntry(InitialGreenLUTDescr);
195 if (InitialBlueLUTDescr)
196 HeaderInternal->AddEntry(InitialBlueLUTDescr);
198 if (InitialRedLUTData)
199 HeaderInternal->AddEntry(InitialBlueLUTDescr);
200 if (InitialGreenLUTData)
201 HeaderInternal->AddEntry(InitialGreenLUTData);
202 if (InitialBlueLUTData)
203 HeaderInternal->AddEntry(InitialBlueLUTData);
208 * \brief delete initial values (il they were saved)
209 * of InitialLutDescriptors and InitialLutData
211 void File::DeleteInitialValues()
214 // InitialLutDescriptors and InitialLutData
215 // will have to be deleted if the don't belong any longer
216 // to the Header H table when the header is deleted...
218 if ( InitialRedLUTDescr )
219 delete InitialRedLUTDescr;
221 if ( InitialGreenLUTDescr )
222 delete InitialGreenLUTDescr;
224 if ( InitialBlueLUTDescr )
225 delete InitialBlueLUTDescr;
227 if ( InitialRedLUTData )
228 delete InitialRedLUTData;
230 if ( InitialGreenLUTData != NULL)
231 delete InitialGreenLUTData;
233 if ( InitialBlueLUTData != NULL)
234 delete InitialBlueLUTData;
237 //-----------------------------------------------------------------------------
240 //-----------------------------------------------------------------------------
244 * \brief computes the length (in bytes) we must ALLOCATE to receive the
245 * image(s) pixels (multiframes taken into account)
246 * \warning : it is NOT the group 7FE0 length
247 * (no interest for compressed images).
249 int File::ComputeDecompressedPixelDataSizeFromHeader()
251 // see PS 3.3-2003 : C.7.6.3.2.1
261 // YBR_FULL_422 (no LUT, no Palette)
267 // ex : gdcm-US-ALOKA-16.dcm
268 // 0028|1221 [OW] [Segmented Red Palette Color Lookup Table Data]
269 // 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
270 // 0028|1223 [OW] [Segmented Blue Palette Color Lookup Table Data]
272 // ex : OT-PAL-8-face.dcm
273 // 0028|1201 [US] [Red Palette Color Lookup Table Data]
274 // 0028|1202 [US] [Green Palette Color Lookup Table Data]
275 // 0028|1203 [US] [Blue Palette Color Lookup Table Data]
277 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
278 // Number of "Bits Allocated" is fixed to 16 when:
279 // - it is not defined (i.e. it's value is 0)
280 // - it's 12, since we will expand the image to 16 bits (see
281 // PixelConvert::ConvertDecompress12BitsTo16Bits() )
282 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
284 numberBitsAllocated = 16;
287 int DecompressedSize = HeaderInternal->GetXSize()
288 * HeaderInternal->GetYSize()
289 * HeaderInternal->GetZSize()
290 * ( numberBitsAllocated / 8 )
291 * HeaderInternal->GetSamplesPerPixel();
293 return DecompressedSize;
297 * \brief - Allocates necessary memory,
298 * - Reads the pixels from disk (uncompress if necessary),
299 * - Transforms YBR pixels, if any, into RGB pixels
300 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
301 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
302 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
303 * @return Pointer to newly allocated pixel data.
304 * NULL if alloc fails
306 uint8_t* File::GetImageData()
308 if ( ! GetDecompressed() )
310 // If the decompression failed nothing can be done.
315 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
317 pixelData = PixelConverter->GetRGB();
321 // When no LUT or LUT conversion fails, return the decompressed
322 pixelData = PixelConverter->GetDecompressed();
325 // PIXELCONVERT CLEANME
326 // Restore the header in a disk-consistent state
327 // (if user asks twice to get the pixels from disk)
328 if ( PixelRead != -1 ) // File was "read" before
330 RestoreInitialValues();
332 if ( PixelConverter->GetRGB() )
334 // now, it's an RGB image
335 // Lets's write it in the Header
336 std::string spp = "3"; // Samples Per Pixel
337 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
338 std::string rgb = "RGB "; // Photometric Interpretation
339 HeaderInternal->SetEntryByNumber(rgb,0x0028,0x0004);
340 std::string planConfig = "0"; // Planar Configuration
341 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
342 PixelRead = 0; // no PixelRaw
346 if ( HeaderInternal->HasLUT() )
348 // The LUT interpretation failed
349 std::string photometricInterpretation = "MONOCHROME1 ";
350 HeaderInternal->SetEntryByNumber( photometricInterpretation,
352 PixelRead = 0; // no PixelRaw
356 if ( PixelConverter->IsDecompressedRGB() )
358 ///////////////////////////////////////////////////
359 // now, it's an RGB image
360 // Lets's write it in the Header
361 // Droping Palette Color out of the Header
362 // has been moved to the Write process.
363 // TODO : move 'values' modification to the write process
364 // : save also (in order to be able to restore)
365 // : 'high bit' -when not equal to 'bits stored' + 1
366 // : 'bits allocated', when it's equal to 12 ?!
367 std::string spp = "3"; // Samples Per Pixel
368 std::string photInt = "RGB "; // Photometric Interpretation
369 std::string planConfig = "0"; // Planar Configuration
370 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
371 HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
372 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
374 PixelRead = 1; // PixelRaw
378 // We say the value *is* loaded.
379 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
380 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
382 // Will be 7fe0, 0010 in standard case
383 GetHeader()->SetEntryBinAreaByNumber( pixelData,
384 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
385 // END PIXELCONVERT CLEANME
392 * Read the pixels from disk (uncompress if necessary),
393 * Transforms YBR pixels, if any, into RGB pixels
394 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
395 * Transforms single Grey plane + 3 Palettes into a RGB Plane
396 * Copies at most MaxSize bytes of pixel data to caller allocated
398 * \warning This function allows people that want to build a volume
399 * from an image stack *not to* have, first to get the image pixels,
400 * and then move them to the volume area.
401 * It's absolutely useless for any VTK user since vtk chooses
402 * to invert the lines of an image, that is the last line comes first
403 * (for some axis related reasons?). Hence he will have
404 * to load the image line by line, starting from the end.
405 * VTK users have to call GetImageData
407 * @param destination Address (in caller's memory space) at which the
408 * pixel data should be copied
409 * @param maxSize Maximum number of bytes to be copied. When MaxSize
410 * is not sufficient to hold the pixel data the copy is not
411 * executed (i.e. no partial copy).
412 * @return On success, the number of bytes actually copied. Zero on
413 * failure e.g. MaxSize is lower than necessary.
415 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
417 if ( ! GetDecompressed() )
419 // If the decompression failed nothing can be done.
423 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
425 if ( PixelConverter->GetRGBSize() > maxSize )
427 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
428 "than caller's expected MaxSize");
431 memmove( destination,
432 (void*)PixelConverter->GetRGB(),
433 PixelConverter->GetRGBSize() );
434 return PixelConverter->GetRGBSize();
437 // Either no LUT conversion necessary or LUT conversion failed
438 if ( PixelConverter->GetDecompressedSize() > maxSize )
440 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
441 "than caller's expected MaxSize");
444 memmove( destination,
445 (void*)PixelConverter->GetDecompressed(),
446 PixelConverter->GetDecompressedSize() );
447 return PixelConverter->GetDecompressedSize();
451 * \brief Allocates necessary memory,
452 * Transforms YBR pixels (if any) into RGB pixels
453 * Transforms 3 planes R, G, B (if any) into a single RGB Plane
454 * Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
455 * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
456 * @return Pointer to newly allocated pixel data.
457 * \ NULL if alloc fails
459 uint8_t* File::GetImageDataRaw ()
461 uint8_t* decompressed = GetDecompressed();
462 if ( ! decompressed )
467 // PIXELCONVERT CLEANME
468 // Restore the header in a disk-consistent state
469 // (if user asks twice to get the pixels from disk)
470 if ( PixelRead != -1 ) // File was "read" before
472 RestoreInitialValues();
474 if ( PixelConverter->IsDecompressedRGB() )
476 ///////////////////////////////////////////////////
477 // now, it's an RGB image
478 // Lets's write it in the Header
479 // Droping Palette Color out of the Header
480 // has been moved to the Write process.
481 // TODO : move 'values' modification to the write process
482 // : save also (in order to be able to restore)
483 // : 'high bit' -when not equal to 'bits stored' + 1
484 // : 'bits allocated', when it's equal to 12 ?!
485 std::string spp = "3"; // Samples Per Pixel
486 std::string photInt = "RGB "; // Photometric Interpretation
487 std::string planConfig = "0"; // Planar Configuration
488 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
489 HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
490 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
493 // We say the value *is* loaded.
494 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
495 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
497 // will be 7fe0, 0010 in standard cases
498 GetHeader()->SetEntryBinAreaByNumber( decompressed,
499 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
501 PixelRead = 1; // PixelRaw
502 // END PIXELCONVERT CLEANME
507 uint8_t* File::GetDecompressed()
509 uint8_t* decompressed = PixelConverter->GetDecompressed();
510 if ( ! decompressed )
512 // The decompressed image migth not be loaded yet:
513 std::ifstream* fp = HeaderInternal->OpenFile();
514 PixelConverter->ReadAndDecompressPixelData( fp );
515 if(fp) HeaderInternal->CloseFile();
516 decompressed = PixelConverter->GetDecompressed();
517 if ( ! decompressed )
519 dbg.Verbose(0, "File::GetDecompressed: read/decompress of "
520 "pixel data apparently went wrong.");
529 * \brief Points the internal Pixel_Data pointer to the callers inData
530 * image representation, BUT WITHOUT COPYING THE DATA.
531 * 'image' Pixels are presented as C-like 2D arrays : line per line.
532 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
533 * \warning Since the pixels are not copied, it is the caller's responsability
534 * not to deallocate it's data before gdcm uses them (e.g. with
535 * the Write() method.
536 * @param inData user supplied pixel area
537 * @param expectedSize total image size, in Bytes
541 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
543 HeaderInternal->SetImageDataSize( expectedSize );
544 // FIXME : if already allocated, memory leak !
546 ImageDataSize = ImageDataSizeRaw = expectedSize;
548 // FIXME : 7fe0, 0010 IS NOT set ...
553 * \brief Writes on disk A SINGLE Dicom file
554 * NO test is performed on processor "Endiannity".
555 * It's up to the user to call his Reader properly
556 * @param fileName name of the file to be created
557 * (any already existing file is over written)
558 * @return false if write fails
561 bool File::WriteRawData(std::string const & fileName)
563 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
566 dbg.Verbose(2, "Fail to open (write) file:", fileName.c_str());
569 fp1.write((char*)Pixel_Data, ImageDataSize);
576 * \brief Writes on disk A SINGLE Dicom file,
577 * using the Implicit Value Representation convention
578 * NO test is performed on processor "Endiannity".
579 * @param fileName name of the file to be created
580 * (any already existing file is overwritten)
581 * @return false if write fails
584 bool File::WriteDcmImplVR (std::string const & fileName)
586 return WriteBase(fileName, ImplicitVR);
590 * \brief Writes on disk A SINGLE Dicom file,
591 * using the Explicit Value Representation convention
592 * NO test is performed on processor "Endiannity". * @param fileName name of the file to be created
593 * (any already existing file is overwritten)
594 * @return false if write fails
597 bool File::WriteDcmExplVR (std::string const & fileName)
599 return WriteBase(fileName, ExplicitVR);
603 * \brief Writes on disk A SINGLE Dicom file,
604 * using the ACR-NEMA convention
605 * NO test is performed on processor "Endiannity".
606 * (a l'attention des logiciels cliniques
607 * qui ne prennent en entrée QUE des images ACR ...
608 * \warning if a DICOM_V3 header is supplied,
609 * groups < 0x0008 and shadow groups are ignored
610 * \warning NO TEST is performed on processor "Endiannity".
611 * @param fileName name of the file to be created
612 * (any already existing file is overwritten)
613 * @return false if write fails
616 bool File::WriteAcr (std::string const & fileName)
618 return WriteBase(fileName, ACR);
621 //-----------------------------------------------------------------------------
624 * \brief NOT a end user inteded function
625 * (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
626 * @param fileName name of the file to be created
627 * (any already existing file is overwritten)
628 * @param type file type (ExplicitVR, ImplicitVR, ...)
629 * @return false if write fails
631 bool File::WriteBase (std::string const & fileName, FileType type)
633 if ( PixelRead == -1 && type != ExplicitVR)
638 std::ofstream* fp1 = new std::ofstream(fileName.c_str(),
639 std::ios::out | std::ios::binary);
642 dbg.Verbose(2, "Failed to open (write) File: " , fileName.c_str());
646 if ( type == ImplicitVR || type == ExplicitVR )
648 // writing Dicom File Preamble
649 char filePreamble[128];
650 memset(filePreamble, 0, 128);
651 fp1->write(filePreamble, 128);
652 fp1->write("DICM", 4);
655 // --------------------------------------------------------------
656 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
658 // if recognition code tells us we dealt with a LibIDO image
659 // we reproduce on disk the switch between lineNumber and columnNumber
660 // just before writting ...
662 /// \todo the best trick would be *change* the recognition code
663 /// but pb expected if user deals with, e.g. COMPLEX images
665 std::string rows, columns;
666 if ( HeaderInternal->GetFileType() == ACR_LIBIDO)
668 rows = HeaderInternal->GetEntryByNumber(0x0028, 0x0010);
669 columns = HeaderInternal->GetEntryByNumber(0x0028, 0x0011);
671 HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0010);
672 HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0011);
674 // ----------------- End of Special Patch ----------------
676 uint16_t grPixel = HeaderInternal->GetGrPixel();
677 uint16_t numPixel = HeaderInternal->GetNumPixel();;
679 DocEntry* PixelElement =
680 GetHeader()->GetDocEntryByNumber(grPixel, numPixel);
682 if ( PixelRead == 1 )
684 // we read pixel 'as is' (no tranformation LUT -> RGB)
685 PixelElement->SetLength( ImageDataSizeRaw );
687 else if ( PixelRead == 0 )
689 // we tranformed GrayLevel pixels + LUT into RGB Pixel
690 PixelElement->SetLength( ImageDataSize );
693 HeaderInternal->Write(fp1, type);
695 // --------------------------------------------------------------
696 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
698 // ...and we restore the Header to be Dicom Compliant again
699 // just after writting
701 if ( HeaderInternal->GetFileType() == ACR_LIBIDO )
703 HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0010);
704 HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0011);
706 // ----------------- End of Special Patch ----------------
714 * \brief Access to the underlying \ref PixelConverter RGBA LUT
716 uint8_t* File::GetLutRGBA()
718 return PixelConverter->GetLutRGBA();
721 } // end namespace gdcm