1 /*=========================================================================
4 Module: $RCSfile: gdcmFile.cxx,v $
6 Date: $Date: 2004/11/24 16:39:18 $
7 Version: $Revision: 1.162 $
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_DECOMPRESSED;
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;
194 /// Accessor to \ref ImageDataSize
195 size_t File::GetImageDataSize()
197 if ( ! GetDecompressed() )
199 // If the decompression failed nothing can be done.
203 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
205 return PixelConverter->GetRGBSize();
209 // When no LUT or LUT conversion fails, return the decompressed
210 return PixelConverter->GetDecompressedSize();
214 /// Accessor to \ref ImageDataSizeRaw
215 size_t File::GetImageDataSizeRaw()
217 if ( ! GetDecompressed() )
219 // If the decompression failed nothing can be done.
222 return PixelConverter->GetDecompressedSize();
226 * \brief - Allocates necessary memory,
227 * - Reads the pixels from disk (uncompress if necessary),
228 * - Transforms YBR pixels, if any, into RGB pixels
229 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
230 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
231 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
232 * @return Pointer to newly allocated pixel data.
233 * NULL if alloc fails
235 uint8_t* File::GetImageData()
237 if ( ! GetDecompressed() )
239 // If the decompression failed nothing can be done.
243 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
245 return PixelConverter->GetRGB();
249 // When no LUT or LUT conversion fails, return the decompressed
250 return PixelConverter->GetDecompressed();
256 * Read the pixels from disk (uncompress if necessary),
257 * Transforms YBR pixels, if any, into RGB pixels
258 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
259 * Transforms single Grey plane + 3 Palettes into a RGB Plane
260 * Copies at most MaxSize bytes of pixel data to caller allocated
262 * \warning This function allows people that want to build a volume
263 * from an image stack *not to* have, first to get the image pixels,
264 * and then move them to the volume area.
265 * It's absolutely useless for any VTK user since vtk chooses
266 * to invert the lines of an image, that is the last line comes first
267 * (for some axis related reasons?). Hence he will have
268 * to load the image line by line, starting from the end.
269 * VTK users have to call GetImageData
271 * @param destination Address (in caller's memory space) at which the
272 * pixel data should be copied
273 * @param maxSize Maximum number of bytes to be copied. When MaxSize
274 * is not sufficient to hold the pixel data the copy is not
275 * executed (i.e. no partial copy).
276 * @return On success, the number of bytes actually copied. Zero on
277 * failure e.g. MaxSize is lower than necessary.
279 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
281 if ( ! GetDecompressed() )
283 // If the decompression failed nothing can be done.
287 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
289 if ( PixelConverter->GetRGBSize() > maxSize )
291 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
292 "than caller's expected MaxSize");
296 (void*)PixelConverter->GetRGB(),
297 PixelConverter->GetRGBSize() );
298 return PixelConverter->GetRGBSize();
301 // Either no LUT conversion necessary or LUT conversion failed
302 if ( PixelConverter->GetDecompressedSize() > maxSize )
304 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
305 "than caller's expected MaxSize");
309 (void*)PixelConverter->GetDecompressed(),
310 PixelConverter->GetDecompressedSize() );
311 return PixelConverter->GetDecompressedSize();
315 * \brief Allocates necessary memory,
316 * Transforms YBR pixels (if any) into RGB pixels
317 * Transforms 3 planes R, G, B (if any) into a single RGB Plane
318 * Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
319 * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
320 * @return Pointer to newly allocated pixel data.
321 * \ NULL if alloc fails
323 uint8_t* File::GetImageDataRaw ()
325 return GetDecompressed();
328 uint8_t* File::GetDecompressed()
330 uint8_t* decompressed = PixelConverter->GetDecompressed();
331 if ( ! decompressed )
333 // The decompressed image migth not be loaded yet:
334 std::ifstream* fp = HeaderInternal->OpenFile();
335 PixelConverter->ReadAndDecompressPixelData( fp );
336 if(fp) HeaderInternal->CloseFile();
337 decompressed = PixelConverter->GetDecompressed();
338 if ( ! decompressed )
340 dbg.Verbose(0, "File::GetDecompressed: read/decompress of "
341 "pixel data apparently went wrong.");
350 * \brief Points the internal Pixel_Data pointer to the callers inData
351 * image representation, BUT WITHOUT COPYING THE DATA.
352 * 'image' Pixels are presented as C-like 2D arrays : line per line.
353 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
354 * \warning Since the pixels are not copied, it is the caller's responsability
355 * not to deallocate it's data before gdcm uses them (e.g. with
356 * the Write() method.
357 * @param inData user supplied pixel area
358 * @param expectedSize total image size, in Bytes
362 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
364 HeaderInternal->SetImageDataSize( expectedSize );
365 // FIXME : if already allocated, memory leak !
367 ImageDataSize = ImageDataSizeRaw = expectedSize;
369 // FIXME : 7fe0, 0010 IS NOT set ...
374 * \brief Writes on disk A SINGLE Dicom file
375 * NO test is performed on processor "Endiannity".
376 * It's up to the user to call his Reader properly
377 * @param fileName name of the file to be created
378 * (any already existing file is over written)
379 * @return false if write fails
382 bool File::WriteRawData(std::string const & fileName)
384 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
387 dbg.Verbose(2, "Fail to open (write) file:", fileName.c_str());
390 fp1.write((char*)Pixel_Data, ImageDataSize);
397 * \brief Writes on disk A SINGLE Dicom file,
398 * using the Implicit Value Representation convention
399 * NO test is performed on processor "Endiannity".
400 * @param fileName name of the file to be created
401 * (any already existing file is overwritten)
402 * @return false if write fails
405 bool File::WriteDcmImplVR (std::string const & fileName)
407 SetWriteTypeToDcmImplVR();
408 return Write(fileName);
412 * \brief Writes on disk A SINGLE Dicom file,
413 * using the Explicit Value Representation convention
414 * NO test is performed on processor "Endiannity". * @param fileName name of the file to be created
415 * (any already existing file is overwritten)
416 * @return false if write fails
419 bool File::WriteDcmExplVR (std::string const & fileName)
421 SetWriteTypeToDcmExplVR();
422 return Write(fileName);
426 * \brief Writes on disk A SINGLE Dicom file,
427 * using the ACR-NEMA convention
428 * NO test is performed on processor "Endiannity".
429 * (a l'attention des logiciels cliniques
430 * qui ne prennent en entrée QUE des images ACR ...
431 * \warning if a DICOM_V3 header is supplied,
432 * groups < 0x0008 and shadow groups are ignored
433 * \warning NO TEST is performed on processor "Endiannity".
434 * @param fileName name of the file to be created
435 * (any already existing file is overwritten)
436 * @return false if write fails
439 bool File::WriteAcr (std::string const & fileName)
442 return Write(fileName);
445 bool File::Write(std::string const& fileName)
450 return WriteBase(fileName,ImplicitVR);
452 return WriteBase(fileName,ExplicitVR);
454 return WriteBase(fileName,ACR);
459 //-----------------------------------------------------------------------------
462 * \brief NOT a end user inteded function
463 * (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
464 * @param fileName name of the file to be created
465 * (any already existing file is overwritten)
466 * @param type file type (ExplicitVR, ImplicitVR, ...)
467 * @return false if write fails
469 bool File::WriteBase (std::string const & fileName, FileType type)
471 /* if ( PixelRead == -1 && type != ExplicitVR)
476 std::ofstream* fp1 = new std::ofstream(fileName.c_str(),
477 std::ios::out | std::ios::binary);
480 dbg.Verbose(2, "Failed to open (write) File: " , fileName.c_str());
489 case WMODE_DECOMPRESSED :
490 SetWriteToDecompressed();
497 // --------------------------------------------------------------
498 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
500 // if recognition code tells us we dealt with a LibIDO image
501 // we reproduce on disk the switch between lineNumber and columnNumber
502 // just before writting ...
503 /// \todo the best trick would be *change* the recognition code
504 /// but pb expected if user deals with, e.g. COMPLEX images
505 /* if ( HeaderInternal->GetFileType() == ACR_LIBIDO)
509 // ----------------- End of Special Patch ----------------
511 HeaderInternal->Write(fp1, type);
513 // --------------------------------------------------------------
514 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
516 // ...and we restore the Header to be Dicom Compliant again
517 // just after writting
518 /* if ( HeaderInternal->GetFileType() == ACR_LIBIDO )
520 RestoreWriteFromLibido();
522 // ----------------- End of Special Patch ----------------
534 * \brief Access to the underlying \ref PixelConverter RGBA LUT
536 uint8_t* File::GetLutRGBA()
538 return PixelConverter->GetLutRGBA();
541 //-----------------------------------------------------------------------------
544 * \brief Set the pixel datas in the good entry of the Header
546 void File::SetPixelData(uint8_t* data)
548 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
549 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
551 // Will be 7fe0, 0010 in standard case
552 DocEntry* currentEntry = GetHeader()->GetDocEntryByNumber(GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
555 if ( BinEntry* binEntry = dynamic_cast<BinEntry *>(currentEntry) )
556 // Flag is to false because datas are kept in the gdcmPixelConvert
557 binEntry->SetBinArea( data, false );
561 void File::SetWriteToNative()
566 void File::SetWriteToDecompressed()
568 if(HeaderInternal->GetNumberOfScalarComponents()==3 && !HeaderInternal->HasLUT())
574 ValEntry* photInt = CopyValEntry(0x0028,0x0004);
575 if(HeaderInternal->HasLUT())
577 photInt->SetValue("PALETTE COLOR ");
578 photInt->SetLength(14);
582 photInt->SetValue("MONOCHROME1 ");
583 photInt->SetLength(12);
586 BinEntry* pixel = CopyBinEntry(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
587 pixel->SetValue(GDCM_BINLOADED);
590 pixel->SetBinArea(Pixel_Data,false);
591 pixel->SetLength(ImageDataSize);
595 pixel->SetBinArea(PixelConverter->GetDecompressed(),false);
596 pixel->SetLength(PixelConverter->GetDecompressedSize());
599 Archive->Push(photInt);
600 Archive->Push(pixel);
604 void File::SetWriteToRGB()
606 if(HeaderInternal->GetNumberOfScalarComponents()==3 && !HeaderInternal->HasLUT())
608 PixelConverter->BuildRGBImage();
610 ValEntry* spp = CopyValEntry(0x0028,0x0002);
614 ValEntry* planConfig = CopyValEntry(0x0028,0x0006);
615 planConfig->SetValue("0 ");
616 planConfig->SetLength(2);
618 ValEntry* photInt = CopyValEntry(0x0028,0x0004);
619 photInt->SetValue("RGB ");
620 photInt->SetLength(4);
622 BinEntry* pixel = CopyBinEntry(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
623 pixel->SetValue(GDCM_BINLOADED);
626 pixel->SetBinArea(Pixel_Data,false);
627 pixel->SetLength(ImageDataSize);
629 else if(PixelConverter->GetRGB())
631 pixel->SetBinArea(PixelConverter->GetRGB(),false);
632 pixel->SetLength(PixelConverter->GetRGBSize());
634 else // Decompressed data
636 pixel->SetBinArea(PixelConverter->GetDecompressed(),false);
637 pixel->SetLength(PixelConverter->GetDecompressedSize());
641 Archive->Push(planConfig);
642 Archive->Push(photInt);
643 Archive->Push(pixel);
646 // Thus, we have a RGB image and the bits allocated = 24 and
647 // samples per pixels = 1 (in the read file)
648 if(HeaderInternal->GetBitsAllocated()==24)
650 ValEntry* bitsAlloc = CopyValEntry(0x0028,0x0100);
651 bitsAlloc->SetValue("8 ");
652 bitsAlloc->SetLength(2);
654 ValEntry* bitsStored = CopyValEntry(0x0028,0x0101);
655 bitsStored->SetValue("8 ");
656 bitsStored->SetLength(2);
658 ValEntry* highBit = CopyValEntry(0x0028,0x0102);
659 highBit->SetValue("7 ");
660 highBit->SetLength(2);
662 Archive->Push(bitsAlloc);
663 Archive->Push(bitsStored);
664 Archive->Push(highBit);
669 SetWriteToDecompressed();
673 void File::RestoreWrite()
675 Archive->Restore(0x0028,0x0002);
676 Archive->Restore(0x0028,0x0004);
677 Archive->Restore(0x0028,0x0006);
678 Archive->Restore(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
680 // For old ACR-NEMA (24 bits problem)
681 Archive->Restore(0x0028,0x0100);
682 Archive->Restore(0x0028,0x0101);
683 Archive->Restore(0x0028,0x0102);
686 void File::SetWriteToLibido()
688 ValEntry *oldRow = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0010));
689 ValEntry *oldCol = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0011));
691 if( oldRow && oldCol )
693 std::string rows, columns;
695 ValEntry *newRow=new ValEntry(oldRow->GetDictEntry());
696 ValEntry *newCol=new ValEntry(oldCol->GetDictEntry());
698 newRow->Copy(oldCol);
699 newCol->Copy(oldRow);
701 newRow->SetValue(oldCol->GetValue());
702 newCol->SetValue(oldRow->GetValue());
704 Archive->Push(newRow);
705 Archive->Push(newCol);
709 void File::RestoreWriteFromLibido()
711 Archive->Restore(0x0028,0x0010);
712 Archive->Restore(0x0028,0x0011);
715 ValEntry* File::CopyValEntry(uint16_t group,uint16_t element)
717 DocEntry* oldE = HeaderInternal->GetDocEntryByNumber(group, element);
722 newE = new ValEntry(oldE->GetDictEntry());
727 newE = GetHeader()->NewValEntryByNumber(group,element);
733 BinEntry* File::CopyBinEntry(uint16_t group,uint16_t element)
735 DocEntry* oldE = HeaderInternal->GetDocEntryByNumber(group, element);
740 newE = new BinEntry(oldE->GetDictEntry());
745 newE = GetHeader()->NewBinEntryByNumber(group,element);
752 //-----------------------------------------------------------------------------
753 } // end namespace gdcm