1 /*=========================================================================
4 Module: $RCSfile: gdcmFile.cxx,v $
6 Date: $Date: 2004/11/25 10:24:34 $
7 Version: $Revision: 1.163 $
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 PixelConverter->GrabInformationsFromHeader( HeaderInternal );
94 * \brief canonical destructor
95 * \note If the Header was created by the File constructor,
96 * it is destroyed by the File
102 delete PixelConverter;
111 delete HeaderInternal;
116 //-----------------------------------------------------------------------------
119 //-----------------------------------------------------------------------------
122 * \brief Get the size of the image data
124 * If the image can be RGB (with a lut or by default), the size
125 * corresponds to the RGB image
126 * @return The image size
128 size_t File::GetImageDataSize()
130 return PixelConverter->GetRGBSize();
134 * \brief Get the size of the image data
136 * If the image can be RGB by transformation in a LUT, this
137 * transformation isn't considered
138 * @return The raw image size
140 size_t File::GetImageDataRawSize()
142 return PixelConverter->GetDecompressedSize();
146 * \brief - Allocates necessary memory,
147 * - Reads the pixels from disk (uncompress if necessary),
148 * - Transforms YBR pixels, if any, into RGB pixels
149 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
150 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
151 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
152 * @return Pointer to newly allocated pixel data.
153 * NULL if alloc fails
155 uint8_t* File::GetImageData()
157 if ( ! GetDecompressed() )
159 // If the decompression failed nothing can be done.
163 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
165 return PixelConverter->GetRGB();
169 // When no LUT or LUT conversion fails, return the decompressed
170 return PixelConverter->GetDecompressed();
176 * Read the pixels from disk (uncompress if necessary),
177 * Transforms YBR pixels, if any, into RGB pixels
178 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
179 * Transforms single Grey plane + 3 Palettes into a RGB Plane
180 * Copies at most MaxSize bytes of pixel data to caller allocated
182 * \warning This function allows people that want to build a volume
183 * from an image stack *not to* have, first to get the image pixels,
184 * and then move them to the volume area.
185 * It's absolutely useless for any VTK user since vtk chooses
186 * to invert the lines of an image, that is the last line comes first
187 * (for some axis related reasons?). Hence he will have
188 * to load the image line by line, starting from the end.
189 * VTK users have to call GetImageData
191 * @param destination Address (in caller's memory space) at which the
192 * pixel data should be copied
193 * @param maxSize Maximum number of bytes to be copied. When MaxSize
194 * is not sufficient to hold the pixel data the copy is not
195 * executed (i.e. no partial copy).
196 * @return On success, the number of bytes actually copied. Zero on
197 * failure e.g. MaxSize is lower than necessary.
199 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
201 if ( ! GetDecompressed() )
203 // If the decompression failed nothing can be done.
207 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
209 if ( PixelConverter->GetRGBSize() > maxSize )
211 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
212 "than caller's expected MaxSize");
216 (void*)PixelConverter->GetRGB(),
217 PixelConverter->GetRGBSize() );
218 return PixelConverter->GetRGBSize();
221 // Either no LUT conversion necessary or LUT conversion failed
222 if ( PixelConverter->GetDecompressedSize() > maxSize )
224 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
225 "than caller's expected MaxSize");
229 (void*)PixelConverter->GetDecompressed(),
230 PixelConverter->GetDecompressedSize() );
231 return PixelConverter->GetDecompressedSize();
235 * \brief Allocates necessary memory,
236 * Transforms YBR pixels (if any) into RGB pixels
237 * Transforms 3 planes R, G, B (if any) into a single RGB Plane
238 * Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
239 * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
240 * @return Pointer to newly allocated pixel data.
241 * \ NULL if alloc fails
243 uint8_t* File::GetImageDataRaw ()
245 return GetDecompressed();
248 uint8_t* File::GetDecompressed()
250 uint8_t* decompressed = PixelConverter->GetDecompressed();
251 if ( ! decompressed )
253 // The decompressed image migth not be loaded yet:
254 std::ifstream* fp = HeaderInternal->OpenFile();
255 PixelConverter->ReadAndDecompressPixelData( fp );
257 HeaderInternal->CloseFile();
259 decompressed = PixelConverter->GetDecompressed();
260 if ( ! decompressed )
262 dbg.Verbose(0, "File::GetDecompressed: read/decompress of "
263 "pixel data apparently went wrong.");
272 * \brief Points the internal Pixel_Data pointer to the callers inData
273 * image representation, BUT WITHOUT COPYING THE DATA.
274 * 'image' Pixels are presented as C-like 2D arrays : line per line.
275 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
276 * \warning Since the pixels are not copied, it is the caller's responsability
277 * not to deallocate it's data before gdcm uses them (e.g. with
278 * the Write() method.
279 * @param inData user supplied pixel area
280 * @param expectedSize total image size, in Bytes
284 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
286 // FIXME : if already allocated, memory leak !
288 ImageDataSize = expectedSize;
289 // FIXME : 7fe0, 0010 IS NOT set ...
294 * \brief Writes on disk A SINGLE Dicom file
295 * NO test is performed on processor "Endiannity".
296 * It's up to the user to call his Reader properly
297 * @param fileName name of the file to be created
298 * (any already existing file is over written)
299 * @return false if write fails
302 bool File::WriteRawData(std::string const & fileName)
304 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
307 dbg.Verbose(2, "Fail to open (write) file:", fileName.c_str());
310 fp1.write((char*)Pixel_Data, ImageDataSize);
317 * \brief Writes on disk A SINGLE Dicom file,
318 * using the Implicit Value Representation convention
319 * NO test is performed on processor "Endiannity".
320 * @param fileName name of the file to be created
321 * (any already existing file is overwritten)
322 * @return false if write fails
325 bool File::WriteDcmImplVR (std::string const & fileName)
327 SetWriteTypeToDcmImplVR();
328 return Write(fileName);
332 * \brief Writes on disk A SINGLE Dicom file,
333 * using the Explicit Value Representation convention
334 * NO test is performed on processor "Endiannity". * @param fileName name of the file to be created
335 * (any already existing file is overwritten)
336 * @return false if write fails
339 bool File::WriteDcmExplVR (std::string const & fileName)
341 SetWriteTypeToDcmExplVR();
342 return Write(fileName);
346 * \brief Writes on disk A SINGLE Dicom file,
347 * using the ACR-NEMA convention
348 * NO test is performed on processor "Endiannity".
349 * (a l'attention des logiciels cliniques
350 * qui ne prennent en entrée QUE des images ACR ...
351 * \warning if a DICOM_V3 header is supplied,
352 * groups < 0x0008 and shadow groups are ignored
353 * \warning NO TEST is performed on processor "Endiannity".
354 * @param fileName name of the file to be created
355 * (any already existing file is overwritten)
356 * @return false if write fails
359 bool File::WriteAcr (std::string const & fileName)
362 return Write(fileName);
365 bool File::Write(std::string const& fileName)
370 return WriteBase(fileName,ImplicitVR);
372 return WriteBase(fileName,ExplicitVR);
374 return WriteBase(fileName,ACR);
380 * \brief Access to the underlying \ref PixelConverter RGBA LUT
382 uint8_t* File::GetLutRGBA()
384 return PixelConverter->GetLutRGBA();
387 //-----------------------------------------------------------------------------
390 * \brief NOT a end user inteded function
391 * (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
392 * @param fileName name of the file to be created
393 * (any already existing file is overwritten)
394 * @param type file type (ExplicitVR, ImplicitVR, ...)
395 * @return false if write fails
397 bool File::WriteBase (std::string const & fileName, FileType type)
399 std::ofstream* fp1 = new std::ofstream(fileName.c_str(),
400 std::ios::out | std::ios::binary);
403 dbg.Verbose(2, "Failed to open (write) File: " , fileName.c_str());
412 case WMODE_DECOMPRESSED :
413 SetWriteToDecompressed();
420 // --------------------------------------------------------------
421 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
423 // if recognition code tells us we dealt with a LibIDO image
424 // we reproduce on disk the switch between lineNumber and columnNumber
425 // just before writting ...
426 /// \todo the best trick would be *change* the recognition code
427 /// but pb expected if user deals with, e.g. COMPLEX images
428 /* if ( HeaderInternal->GetFileType() == ACR_LIBIDO)
432 // ----------------- End of Special Patch ----------------
434 bool check=CheckWriteIntegrity();
437 HeaderInternal->Write(fp1,type);
440 // --------------------------------------------------------------
441 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
443 // ...and we restore the Header to be Dicom Compliant again
444 // just after writting
445 /* if ( HeaderInternal->GetFileType() == ACR_LIBIDO )
447 RestoreWriteFromLibido();
449 // ----------------- End of Special Patch ----------------
460 * \brief Check the write integrity
462 * The tests made are :
463 * - verify the size of the image to write with the possible write
464 * when the user set an image data
465 * @return true if the check successfulls
467 bool File::CheckWriteIntegrity()
475 case WMODE_DECOMPRESSED :
476 if(GetImageDataRawSize()!=ImageDataSize)
478 std::cerr<<"RAW : "<<GetImageDataRawSize()<<" / "<<ImageDataSize<<std::endl;
483 if(GetImageDataSize()!=ImageDataSize)
485 std::cerr<<"RGB : "<<GetImageDataSize()<<" / "<<ImageDataSize<<std::endl;
495 void File::SetWriteToNative()
499 BinEntry* pixel = CopyBinEntry(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
500 pixel->SetValue(GDCM_BINLOADED);
501 pixel->SetBinArea(Pixel_Data,false);
502 pixel->SetLength(ImageDataSize);
504 Archive->Push(pixel);
508 void File::SetWriteToDecompressed()
510 if(HeaderInternal->GetNumberOfScalarComponents()==3 && !HeaderInternal->HasLUT())
516 ValEntry* photInt = CopyValEntry(0x0028,0x0004);
517 if(HeaderInternal->HasLUT())
519 photInt->SetValue("PALETTE COLOR ");
520 photInt->SetLength(14);
524 photInt->SetValue("MONOCHROME1 ");
525 photInt->SetLength(12);
528 BinEntry* pixel = CopyBinEntry(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
529 pixel->SetValue(GDCM_BINLOADED);
532 pixel->SetBinArea(Pixel_Data,false);
533 pixel->SetLength(ImageDataSize);
537 pixel->SetBinArea(PixelConverter->GetDecompressed(),false);
538 pixel->SetLength(PixelConverter->GetDecompressedSize());
541 Archive->Push(photInt);
542 Archive->Push(pixel);
546 void File::SetWriteToRGB()
548 if(HeaderInternal->GetNumberOfScalarComponents()==3)
550 PixelConverter->BuildRGBImage();
552 ValEntry* spp = CopyValEntry(0x0028,0x0002);
556 ValEntry* planConfig = CopyValEntry(0x0028,0x0006);
557 planConfig->SetValue("0 ");
558 planConfig->SetLength(2);
560 ValEntry* photInt = CopyValEntry(0x0028,0x0004);
561 photInt->SetValue("RGB ");
562 photInt->SetLength(4);
564 BinEntry* pixel = CopyBinEntry(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
565 pixel->SetValue(GDCM_BINLOADED);
568 pixel->SetBinArea(Pixel_Data,false);
569 pixel->SetLength(ImageDataSize);
571 else if(PixelConverter->GetRGB())
573 pixel->SetBinArea(PixelConverter->GetRGB(),false);
574 pixel->SetLength(PixelConverter->GetRGBSize());
576 else // Decompressed data
578 pixel->SetBinArea(PixelConverter->GetDecompressed(),false);
579 pixel->SetLength(PixelConverter->GetDecompressedSize());
583 Archive->Push(planConfig);
584 Archive->Push(photInt);
585 Archive->Push(pixel);
588 Archive->Push(0x0028,0x1101);
589 Archive->Push(0x0028,0x1102);
590 Archive->Push(0x0028,0x1103);
591 Archive->Push(0x0028,0x1201);
592 Archive->Push(0x0028,0x1202);
593 Archive->Push(0x0028,0x1203);
596 // Thus, we have a RGB image and the bits allocated = 24 and
597 // samples per pixels = 1 (in the read file)
598 if(HeaderInternal->GetBitsAllocated()==24)
600 ValEntry* bitsAlloc = CopyValEntry(0x0028,0x0100);
601 bitsAlloc->SetValue("8 ");
602 bitsAlloc->SetLength(2);
604 ValEntry* bitsStored = CopyValEntry(0x0028,0x0101);
605 bitsStored->SetValue("8 ");
606 bitsStored->SetLength(2);
608 ValEntry* highBit = CopyValEntry(0x0028,0x0102);
609 highBit->SetValue("7 ");
610 highBit->SetLength(2);
612 Archive->Push(bitsAlloc);
613 Archive->Push(bitsStored);
614 Archive->Push(highBit);
619 SetWriteToDecompressed();
623 void File::RestoreWrite()
625 Archive->Restore(0x0028,0x0002);
626 Archive->Restore(0x0028,0x0004);
627 Archive->Restore(0x0028,0x0006);
628 Archive->Restore(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
630 // For old ACR-NEMA (24 bits problem)
631 Archive->Restore(0x0028,0x0100);
632 Archive->Restore(0x0028,0x0101);
633 Archive->Restore(0x0028,0x0102);
636 Archive->Restore(0x0028,0x1101);
637 Archive->Restore(0x0028,0x1102);
638 Archive->Restore(0x0028,0x1103);
639 Archive->Restore(0x0028,0x1201);
640 Archive->Restore(0x0028,0x1202);
641 Archive->Restore(0x0028,0x1203);
644 void File::SetWriteToLibido()
646 ValEntry *oldRow = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0010));
647 ValEntry *oldCol = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0011));
649 if( oldRow && oldCol )
651 std::string rows, columns;
653 ValEntry *newRow=new ValEntry(oldRow->GetDictEntry());
654 ValEntry *newCol=new ValEntry(oldCol->GetDictEntry());
656 newRow->Copy(oldCol);
657 newCol->Copy(oldRow);
659 newRow->SetValue(oldCol->GetValue());
660 newCol->SetValue(oldRow->GetValue());
662 Archive->Push(newRow);
663 Archive->Push(newCol);
667 void File::RestoreWriteFromLibido()
669 Archive->Restore(0x0028,0x0010);
670 Archive->Restore(0x0028,0x0011);
673 ValEntry* File::CopyValEntry(uint16_t group,uint16_t element)
675 DocEntry* oldE = HeaderInternal->GetDocEntryByNumber(group, element);
680 newE = new ValEntry(oldE->GetDictEntry());
685 newE = GetHeader()->NewValEntryByNumber(group,element);
691 BinEntry* File::CopyBinEntry(uint16_t group,uint16_t element)
693 DocEntry* oldE = HeaderInternal->GetDocEntryByNumber(group, element);
698 newE = new BinEntry(oldE->GetDictEntry());
703 newE = GetHeader()->NewBinEntryByNumber(group,element);
710 //-----------------------------------------------------------------------------
713 * \brief Set the pixel datas in the good entry of the Header
715 void File::SetPixelData(uint8_t* data)
717 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
718 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
720 // Will be 7fe0, 0010 in standard case
721 DocEntry* currentEntry = GetHeader()->GetDocEntryByNumber(GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
724 if ( BinEntry* binEntry = dynamic_cast<BinEntry *>(currentEntry) )
725 // Flag is to false because datas are kept in the gdcmPixelConvert
726 binEntry->SetBinArea( data, false );
730 //-----------------------------------------------------------------------------
731 } // end namespace gdcm