1 /*=========================================================================
4 Module: $RCSfile: gdcmFile.cxx,v $
6 Date: $Date: 2004/11/24 10:23:47 $
7 Version: $Revision: 1.160 $
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 WriteMode = WMODE_RGB;
79 WriteType = WTYPE_IMPL_VR;
81 PixelConverter = new PixelConvert;
82 Archive = new DocEntryArchive( HeaderInternal );
84 if ( HeaderInternal->IsReadable() )
86 ImageDataSizeRaw = ComputeDecompressedPixelDataSizeFromHeader();
87 if ( HeaderInternal->HasLUT() )
89 ImageDataSize = 3 * ImageDataSizeRaw;
93 ImageDataSize = ImageDataSizeRaw;
96 PixelConverter->GrabInformationsFromHeader( HeaderInternal );
103 * \brief canonical destructor
104 * \note If the Header was created by the File constructor,
105 * it is destroyed by the File
111 delete PixelConverter;
120 delete HeaderInternal;
126 * \brief Sets some initial values for the Constructor
127 * \warning not end user intended
129 void File::SaveInitialValues()
131 PixelRead = -1; // no ImageData read yet.
135 //-----------------------------------------------------------------------------
138 //-----------------------------------------------------------------------------
142 * \brief computes the length (in bytes) we must ALLOCATE to receive the
143 * image(s) pixels (multiframes taken into account)
144 * \warning : it is NOT the group 7FE0 length
145 * (no interest for compressed images).
147 int File::ComputeDecompressedPixelDataSizeFromHeader()
149 // see PS 3.3-2003 : C.7.6.3.2.1
159 // YBR_FULL_422 (no LUT, no Palette)
165 // ex : gdcm-US-ALOKA-16.dcm
166 // 0028|1221 [OW] [Segmented Red Palette Color Lookup Table Data]
167 // 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
168 // 0028|1223 [OW] [Segmented Blue Palette Color Lookup Table Data]
170 // ex : OT-PAL-8-face.dcm
171 // 0028|1201 [US] [Red Palette Color Lookup Table Data]
172 // 0028|1202 [US] [Green Palette Color Lookup Table Data]
173 // 0028|1203 [US] [Blue Palette Color Lookup Table Data]
175 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
176 // Number of "Bits Allocated" is fixed to 16 when:
177 // - it is not defined (i.e. it's value is 0)
178 // - it's 12, since we will expand the image to 16 bits (see
179 // PixelConvert::ConvertDecompress12BitsTo16Bits() )
180 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
182 numberBitsAllocated = 16;
185 int DecompressedSize = HeaderInternal->GetXSize()
186 * HeaderInternal->GetYSize()
187 * HeaderInternal->GetZSize()
188 * ( numberBitsAllocated / 8 )
189 * HeaderInternal->GetSamplesPerPixel();
191 return DecompressedSize;
195 * \brief - Allocates necessary memory,
196 * - Reads the pixels from disk (uncompress if necessary),
197 * - Transforms YBR pixels, if any, into RGB pixels
198 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
199 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
200 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
201 * @return Pointer to newly allocated pixel data.
202 * NULL if alloc fails
204 uint8_t* File::GetImageData()
206 if ( ! GetDecompressed() )
208 // If the decompression failed nothing can be done.
213 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
215 pixelData = PixelConverter->GetRGB();
219 // When no LUT or LUT conversion fails, return the decompressed
220 pixelData = PixelConverter->GetDecompressed();
223 /*// PIXELCONVERT CLEANME
224 // Restore the header in a disk-consistent state
225 // (if user asks twice to get the pixels from disk)
226 if ( PixelRead != -1 ) // File was "read" before
228 RestoreInitialValues();
230 if ( PixelConverter->GetRGB() )
232 // now, it's an RGB image
233 // Lets's write it in the Header
234 std::string spp = "3"; // Samples Per Pixel
235 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
236 std::string rgb = "RGB "; // Photometric Interpretation
237 HeaderInternal->SetEntryByNumber(rgb,0x0028,0x0004);
238 std::string planConfig = "0"; // Planar Configuration
239 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
240 PixelRead = 0; // no PixelRaw
244 if ( HeaderInternal->HasLUT() )
246 // The LUT interpretation failed
247 std::string photometricInterpretation = Util::DicomString("MONOCHROME1");
248 HeaderInternal->SetEntryByNumber( photometricInterpretation,
250 PixelRead = 0; // no PixelRaw
254 if ( PixelConverter->IsDecompressedRGB() )
256 ///////////////////////////////////////////////////
257 // now, it's an RGB image
258 // Lets's write it in the Header
259 // Droping Palette Color out of the Header
260 // has been moved to the Write process.
261 // TODO : move 'values' modification to the write process
262 // : save also (in order to be able to restore)
263 // : 'high bit' -when not equal to 'bits stored' + 1
264 // : 'bits allocated', when it's equal to 12 ?!
265 std::string spp = "3"; // Samples Per Pixel
266 std::string photInt = "RGB "; // Photometric Interpretation
267 std::string planConfig = "0"; // Planar Configuration
268 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
269 HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
270 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
272 PixelRead = 1; // PixelRaw
276 // We say the value *is* loaded.
277 SetPixelData(pixelData);
278 // END PIXELCONVERT CLEANME*/
285 * Read the pixels from disk (uncompress if necessary),
286 * Transforms YBR pixels, if any, into RGB pixels
287 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
288 * Transforms single Grey plane + 3 Palettes into a RGB Plane
289 * Copies at most MaxSize bytes of pixel data to caller allocated
291 * \warning This function allows people that want to build a volume
292 * from an image stack *not to* have, first to get the image pixels,
293 * and then move them to the volume area.
294 * It's absolutely useless for any VTK user since vtk chooses
295 * to invert the lines of an image, that is the last line comes first
296 * (for some axis related reasons?). Hence he will have
297 * to load the image line by line, starting from the end.
298 * VTK users have to call GetImageData
300 * @param destination Address (in caller's memory space) at which the
301 * pixel data should be copied
302 * @param maxSize Maximum number of bytes to be copied. When MaxSize
303 * is not sufficient to hold the pixel data the copy is not
304 * executed (i.e. no partial copy).
305 * @return On success, the number of bytes actually copied. Zero on
306 * failure e.g. MaxSize is lower than necessary.
308 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
310 if ( ! GetDecompressed() )
312 // If the decompression failed nothing can be done.
316 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
318 if ( PixelConverter->GetRGBSize() > maxSize )
320 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
321 "than caller's expected MaxSize");
325 (void*)PixelConverter->GetRGB(),
326 PixelConverter->GetRGBSize() );
327 return PixelConverter->GetRGBSize();
330 // Either no LUT conversion necessary or LUT conversion failed
331 if ( PixelConverter->GetDecompressedSize() > maxSize )
333 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
334 "than caller's expected MaxSize");
338 (void*)PixelConverter->GetDecompressed(),
339 PixelConverter->GetDecompressedSize() );
340 return PixelConverter->GetDecompressedSize();
344 * \brief Allocates necessary memory,
345 * Transforms YBR pixels (if any) into RGB pixels
346 * Transforms 3 planes R, G, B (if any) into a single RGB Plane
347 * Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
348 * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
349 * @return Pointer to newly allocated pixel data.
350 * \ NULL if alloc fails
352 uint8_t* File::GetImageDataRaw ()
354 uint8_t* decompressed = GetDecompressed();
355 if ( ! decompressed )
360 /*// PIXELCONVERT CLEANME
361 // Restore the header in a disk-consistent state
362 // (if user asks twice to get the pixels from disk)
363 if ( PixelRead != -1 ) // File was "read" before
365 RestoreInitialValues();
367 if ( PixelConverter->IsDecompressedRGB() )
369 ///////////////////////////////////////////////////
370 // now, it's an RGB image
371 // Lets's write it in the Header
372 // Droping Palette Color out of the Header
373 // has been moved to the Write process.
374 // TODO : move 'values' modification to the write process
375 // : save also (in order to be able to restore)
376 // : 'high bit' -when not equal to 'bits stored' + 1
377 // : 'bits allocated', when it's equal to 12 ?!
378 std::string spp = "3"; // Samples Per Pixel
379 std::string photInt = "RGB "; // Photometric Interpretation
380 std::string planConfig = "0"; // Planar Configuration
381 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
382 HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
383 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
386 // We say the value *is* loaded.
387 SetPixelData(decompressed);
389 PixelRead = 1; // PixelRaw
390 // END PIXELCONVERT CLEANME*/
395 uint8_t* File::GetDecompressed()
397 uint8_t* decompressed = PixelConverter->GetDecompressed();
398 if ( ! decompressed )
400 // The decompressed image migth not be loaded yet:
401 std::ifstream* fp = HeaderInternal->OpenFile();
402 PixelConverter->ReadAndDecompressPixelData( fp );
403 if(fp) HeaderInternal->CloseFile();
404 decompressed = PixelConverter->GetDecompressed();
405 if ( ! decompressed )
407 dbg.Verbose(0, "File::GetDecompressed: read/decompress of "
408 "pixel data apparently went wrong.");
417 * \brief Points the internal Pixel_Data pointer to the callers inData
418 * image representation, BUT WITHOUT COPYING THE DATA.
419 * 'image' Pixels are presented as C-like 2D arrays : line per line.
420 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
421 * \warning Since the pixels are not copied, it is the caller's responsability
422 * not to deallocate it's data before gdcm uses them (e.g. with
423 * the Write() method.
424 * @param inData user supplied pixel area
425 * @param expectedSize total image size, in Bytes
429 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
431 HeaderInternal->SetImageDataSize( expectedSize );
432 // FIXME : if already allocated, memory leak !
434 ImageDataSize = ImageDataSizeRaw = expectedSize;
436 // FIXME : 7fe0, 0010 IS NOT set ...
441 * \brief Writes on disk A SINGLE Dicom file
442 * NO test is performed on processor "Endiannity".
443 * It's up to the user to call his Reader properly
444 * @param fileName name of the file to be created
445 * (any already existing file is over written)
446 * @return false if write fails
449 bool File::WriteRawData(std::string const & fileName)
451 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
454 dbg.Verbose(2, "Fail to open (write) file:", fileName.c_str());
457 fp1.write((char*)Pixel_Data, ImageDataSize);
464 * \brief Writes on disk A SINGLE Dicom file,
465 * using the Implicit Value Representation convention
466 * NO test is performed on processor "Endiannity".
467 * @param fileName name of the file to be created
468 * (any already existing file is overwritten)
469 * @return false if write fails
472 bool File::WriteDcmImplVR (std::string const & fileName)
474 SetWriteTypeToDcmImplVR();
475 return Write(fileName);
479 * \brief Writes on disk A SINGLE Dicom file,
480 * using the Explicit Value Representation convention
481 * NO test is performed on processor "Endiannity". * @param fileName name of the file to be created
482 * (any already existing file is overwritten)
483 * @return false if write fails
486 bool File::WriteDcmExplVR (std::string const & fileName)
488 SetWriteTypeToDcmExplVR();
489 return Write(fileName);
493 * \brief Writes on disk A SINGLE Dicom file,
494 * using the ACR-NEMA convention
495 * NO test is performed on processor "Endiannity".
496 * (a l'attention des logiciels cliniques
497 * qui ne prennent en entrée QUE des images ACR ...
498 * \warning if a DICOM_V3 header is supplied,
499 * groups < 0x0008 and shadow groups are ignored
500 * \warning NO TEST is performed on processor "Endiannity".
501 * @param fileName name of the file to be created
502 * (any already existing file is overwritten)
503 * @return false if write fails
506 bool File::WriteAcr (std::string const & fileName)
509 return Write(fileName);
512 bool File::Write(std::string const& fileName)
517 return WriteBase(fileName,ImplicitVR);
519 return WriteBase(fileName,ExplicitVR);
521 return WriteBase(fileName,ACR);
526 //-----------------------------------------------------------------------------
529 * \brief NOT a end user inteded function
530 * (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
531 * @param fileName name of the file to be created
532 * (any already existing file is overwritten)
533 * @param type file type (ExplicitVR, ImplicitVR, ...)
534 * @return false if write fails
536 bool File::WriteBase (std::string const & fileName, FileType type)
538 /* if ( PixelRead == -1 && type != ExplicitVR)
543 std::ofstream* fp1 = new std::ofstream(fileName.c_str(),
544 std::ios::out | std::ios::binary);
547 dbg.Verbose(2, "Failed to open (write) File: " , fileName.c_str());
551 if ( type == ImplicitVR || type == ExplicitVR )
553 // writing Dicom File Preamble
554 char filePreamble[128];
555 memset(filePreamble, 0, 128);
556 fp1->write(filePreamble, 128);
557 fp1->write("DICM", 4);
565 case WMODE_DECOMPRESSED :
566 SetWriteToDecompressed();
573 // --------------------------------------------------------------
574 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
576 // if recognition code tells us we dealt with a LibIDO image
577 // we reproduce on disk the switch between lineNumber and columnNumber
578 // just before writting ...
580 /// \todo the best trick would be *change* the recognition code
581 /// but pb expected if user deals with, e.g. COMPLEX images
583 std::string rows, columns;
584 if ( HeaderInternal->GetFileType() == ACR_LIBIDO)
588 // ----------------- End of Special Patch ----------------
590 /* uint16_t grPixel = HeaderInternal->GetGrPixel();
591 uint16_t numPixel = HeaderInternal->GetNumPixel();;
593 DocEntry* PixelElement =
594 GetHeader()->GetDocEntryByNumber(grPixel, numPixel);
596 if ( PixelRead == 1 )
598 // we read pixel 'as is' (no tranformation LUT -> RGB)
599 PixelElement->SetLength( ImageDataSizeRaw );
601 else if ( PixelRead == 0 )
603 // we tranformed GrayLevel pixels + LUT into RGB Pixel
604 PixelElement->SetLength( ImageDataSize );
607 HeaderInternal->Write(fp1, type);
609 // --------------------------------------------------------------
610 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
612 // ...and we restore the Header to be Dicom Compliant again
613 // just after writting
615 if ( HeaderInternal->GetFileType() == ACR_LIBIDO )
617 RestoreWriteFromLibido();
619 // ----------------- End of Special Patch ----------------
631 * \brief Access to the underlying \ref PixelConverter RGBA LUT
633 uint8_t* File::GetLutRGBA()
635 return PixelConverter->GetLutRGBA();
638 //-----------------------------------------------------------------------------
641 * \brief Set the pixel datas in the good entry of the Header
643 void File::SetPixelData(uint8_t* data)
645 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
646 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
648 // Will be 7fe0, 0010 in standard case
649 DocEntry* currentEntry = GetHeader()->GetDocEntryByNumber(GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
652 if ( BinEntry* binEntry = dynamic_cast<BinEntry *>(currentEntry) )
653 // Flag is to false because datas are kept in the gdcmPixelConvert
654 binEntry->SetBinArea( data, false );
658 void File::SetWriteToNative()
663 void File::SetWriteToDecompressed()
665 // if (( !HeaderInternal->HasLUT() ) || (!PixelConverter->BuildRGBImage()))
666 if(HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage())
672 ValEntry* photInt = CopyValEntry(0x0028,0x0004);
673 photInt->SetValue("MONOCHROME1 ");
674 photInt->SetLength(12);
676 BinEntry* pixel = CopyBinEntry(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
677 pixel->SetValue(GDCM_BINLOADED);
678 pixel->SetBinArea(PixelConverter->GetDecompressed(),false);
679 pixel->SetLength(PixelConverter->GetDecompressedSize());
681 Archive->Push(photInt);
682 Archive->Push(pixel);
690 void File::SetWriteToRGB()
692 if(PixelConverter->BuildRGBImage())
694 ValEntry* spp = CopyValEntry(0x0028,0x0002);
698 ValEntry* photInt = CopyValEntry(0x0028,0x0004);
699 photInt->SetValue("RGB ");
700 photInt->SetLength(4);
702 ValEntry* planConfig = CopyValEntry(0x0028,0x0006);
703 planConfig->SetValue("0 ");
704 planConfig->SetLength(2);
706 BinEntry* pixel = CopyBinEntry(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
707 pixel->SetValue(GDCM_BINLOADED);
708 pixel->SetBinArea(PixelConverter->GetRGB(),false);
709 pixel->SetLength(PixelConverter->GetRGBSize());
712 Archive->Push(photInt);
713 Archive->Push(planConfig);
714 Archive->Push(pixel);
718 SetWriteToDecompressed();
722 void File::RestoreWrite()
724 Archive->Restore(0x0028,0x0002);
725 Archive->Restore(0x0028,0x0004);
726 Archive->Restore(0x0028,0x0006);
727 Archive->Restore(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
730 void File::SetWriteToLibido()
732 ValEntry *oldRow = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0010));
733 ValEntry *oldCol = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0011));
735 if( oldRow && oldCol )
737 std::string rows, columns;
739 ValEntry *newRow=new ValEntry(oldRow->GetDictEntry());
740 ValEntry *newCol=new ValEntry(oldCol->GetDictEntry());
742 newRow->Copy(oldCol);
743 newCol->Copy(oldRow);
745 newRow->SetValue(oldCol->GetValue());
746 newCol->SetValue(oldRow->GetValue());
748 Archive->Push(newRow);
749 Archive->Push(newCol);
753 void File::RestoreWriteFromLibido()
755 Archive->Restore(0x0028,0x0010);
756 Archive->Restore(0x0028,0x0011);
759 ValEntry* File::CopyValEntry(uint16_t group,uint16_t element)
761 DocEntry* oldE = HeaderInternal->GetDocEntryByNumber(group, element);
766 newE = new ValEntry(oldE->GetDictEntry());
771 newE = GetHeader()->NewValEntryByNumber(group,element);
777 BinEntry* File::CopyBinEntry(uint16_t group,uint16_t element)
779 DocEntry* oldE = HeaderInternal->GetDocEntryByNumber(group, element);
784 newE = new BinEntry(oldE->GetDictEntry());
789 newE = GetHeader()->NewBinEntryByNumber(group,element);
796 //-----------------------------------------------------------------------------
797 } // end namespace gdcm