1 /*=========================================================================
4 Module: $RCSfile: gdcmFile.cxx,v $
6 Date: $Date: 2004/12/07 17:28:50 $
7 Version: $Revision: 1.174 $
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 "gdcmDocument.h"
21 #include "gdcmDebug.h"
23 #include "gdcmBinEntry.h"
24 #include "gdcmHeader.h"
25 #include "gdcmPixelReadConvert.h"
26 #include "gdcmPixelWriteConvert.h"
27 #include "gdcmDocEntryArchive.h"
33 typedef std::pair<TagDocEntryHT::iterator,TagDocEntryHT::iterator> IterHT;
35 //-------------------------------------------------------------------------
36 // Constructor / Destructor
38 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
39 * file (Header only deals with the ... header)
40 * Opens (in read only and when possible) an existing file and checks
41 * for DICOM compliance. Returns NULL on failure.
42 * It will be up to the user to load the pixels into memory
43 * (see GetImageData, GetImageDataRaw)
44 * \note the in-memory representation of all available tags found in
45 * the DICOM header is post-poned to first header information access.
46 * This avoid a double parsing of public part of the header when
47 * one sets an a posteriori shadow dictionary (efficiency can be
48 * seen as a side effect).
52 HeaderInternal = new Header( );
58 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
59 * file (Header only deals with the ... header)
60 * Opens (in read only and when possible) an existing file and checks
61 * for DICOM compliance. Returns NULL on failure.
62 * It will be up to the user to load the pixels into memory
63 * (see GetImageData, GetImageDataRaw)
64 * \note the in-memory representation of all available tags found in
65 * the DICOM header is post-poned to first header information access.
66 * This avoid a double parsing of public part of the header when
67 * user sets an a posteriori shadow dictionary (efficiency can be
68 * seen as a side effect).
69 * @param header already built Header
71 File::File(Header *header)
73 HeaderInternal = header;
79 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
80 * file (Header only deals with the ... header)
81 * Opens (in read only and when possible) an existing file and checks
82 * for DICOM compliance. Returns NULL on failure.
83 * It will be up to the user to load the pixels into memory
84 * (see GetImageData, GetImageDataRaw)
85 * \note the in-memory representation of all available tags found in
86 * the DICOM header is post-poned to first header information access.
87 * This avoid a double parsing of public part of the header when
88 * one sets an a posteriori shadow dictionary (efficiency can be
89 * seen as a side effect).
90 * @param filename file to be opened for parsing
92 File::File(std::string const & filename )
94 HeaderInternal = new Header( filename );
100 * \brief Factorization for various forms of constructors.
102 void File::Initialise()
104 WriteMode = WMODE_DECOMPRESSED;
105 WriteType = ExplicitVR;
107 PixelReadConverter = new PixelReadConvert;
108 PixelWriteConverter = new PixelWriteConvert;
109 Archive = new DocEntryArchive( HeaderInternal );
111 if ( HeaderInternal->IsReadable() )
113 PixelReadConverter->GrabInformationsFromHeader( HeaderInternal );
118 * \brief canonical destructor
119 * \note If the Header was created by the File constructor,
120 * it is destroyed by the File
124 if( PixelReadConverter )
126 delete PixelReadConverter;
128 if( PixelWriteConverter )
130 delete PixelWriteConverter;
139 delete HeaderInternal;
144 //-----------------------------------------------------------------------------
147 //-----------------------------------------------------------------------------
150 * \brief Get the size of the image data
152 * If the image can be RGB (with a lut or by default), the size
153 * corresponds to the RGB image
154 * @return The image size
156 size_t File::GetImageDataSize()
158 return PixelReadConverter->GetRGBSize();
162 * \brief Get the size of the image data
164 * If the image can be RGB by transformation in a LUT, this
165 * transformation isn't considered
166 * @return The raw image size
168 size_t File::GetImageDataRawSize()
170 return PixelReadConverter->GetDecompressedSize();
174 * \brief - Allocates necessary memory,
175 * - Reads the pixels from disk (uncompress if necessary),
176 * - Transforms YBR pixels, if any, into RGB pixels
177 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
178 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
179 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
180 * @return Pointer to newly allocated pixel data.
181 * NULL if alloc fails
183 uint8_t* File::GetImageData()
185 if ( ! GetDecompressed() )
187 // If the decompression failed nothing can be done.
191 if ( HeaderInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
193 return PixelReadConverter->GetRGB();
197 // When no LUT or LUT conversion fails, return the decompressed
198 return PixelReadConverter->GetDecompressed();
204 * Read the pixels from disk (uncompress if necessary),
205 * Transforms YBR pixels, if any, into RGB pixels
206 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
207 * Transforms single Grey plane + 3 Palettes into a RGB Plane
208 * Copies at most MaxSize bytes of pixel data to caller allocated
210 * \warning This function allows people that want to build a volume
211 * from an image stack *not to* have, first to get the image pixels,
212 * and then move them to the volume area.
213 * It's absolutely useless for any VTK user since vtk chooses
214 * to invert the lines of an image, that is the last line comes first
215 * (for some axis related reasons?). Hence he will have
216 * to load the image line by line, starting from the end.
217 * VTK users have to call GetImageData
219 * @param destination Address (in caller's memory space) at which the
220 * pixel data should be copied
221 * @param maxSize Maximum number of bytes to be copied. When MaxSize
222 * is not sufficient to hold the pixel data the copy is not
223 * executed (i.e. no partial copy).
224 * @return On success, the number of bytes actually copied. Zero on
225 * failure e.g. MaxSize is lower than necessary.
227 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
229 if ( ! GetDecompressed() )
231 // If the decompression failed nothing can be done.
235 if ( HeaderInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
237 if ( PixelReadConverter->GetRGBSize() > maxSize )
239 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
240 "than caller's expected MaxSize");
244 (void*)PixelReadConverter->GetRGB(),
245 PixelReadConverter->GetRGBSize() );
246 return PixelReadConverter->GetRGBSize();
249 // Either no LUT conversion necessary or LUT conversion failed
250 if ( PixelReadConverter->GetDecompressedSize() > maxSize )
252 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
253 "than caller's expected MaxSize");
257 (void*)PixelReadConverter->GetDecompressed(),
258 PixelReadConverter->GetDecompressedSize() );
259 return PixelReadConverter->GetDecompressedSize();
263 * \brief Allocates necessary memory,
264 * Transforms YBR pixels (if any) into RGB pixels
265 * Transforms 3 planes R, G, B (if any) into a single RGB Plane
266 * Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
267 * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
268 * @return Pointer to newly allocated pixel data.
269 * \ NULL if alloc fails
271 uint8_t* File::GetImageDataRaw ()
273 return GetDecompressed();
276 uint8_t* File::GetDecompressed()
278 uint8_t* decompressed = PixelReadConverter->GetDecompressed();
279 if ( ! decompressed )
281 // The decompressed image migth not be loaded yet:
282 std::ifstream* fp = HeaderInternal->OpenFile();
283 PixelReadConverter->ReadAndDecompressPixelData( fp );
285 HeaderInternal->CloseFile();
287 decompressed = PixelReadConverter->GetDecompressed();
288 if ( ! decompressed )
290 dbg.Verbose(0, "File::GetDecompressed: read/decompress of "
291 "pixel data apparently went wrong.");
300 * \brief Points the internal pointer to the callers inData
301 * image representation, BUT WITHOUT COPYING THE DATA.
302 * 'image' Pixels are presented as C-like 2D arrays : line per line.
303 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
304 * \warning Since the pixels are not copied, it is the caller's responsability
305 * not to deallocate it's data before gdcm uses them (e.g. with
306 * the Write() method.
307 * @param inData user supplied pixel area
308 * @param expectedSize total image size, in Bytes
312 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
314 PixelWriteConverter->SetUserData(inData,expectedSize);
320 * \brief Writes on disk A SINGLE Dicom file
321 * NO test is performed on processor "Endiannity".
322 * It's up to the user to call his Reader properly
323 * @param fileName name of the file to be created
324 * (any already existing file is over written)
325 * @return false if write fails
328 bool File::WriteRawData(std::string const & fileName)
330 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
333 dbg.Verbose(2, "Fail to open (write) file:", fileName.c_str());
337 if(PixelWriteConverter->GetUserData())
338 fp1.write((char*)PixelWriteConverter->GetUserData(), PixelWriteConverter->GetUserDataSize());
339 else if(PixelReadConverter->GetRGB())
340 fp1.write((char*)PixelReadConverter->GetRGB(), PixelReadConverter->GetRGBSize());
341 else if(PixelReadConverter->GetDecompressed())
342 fp1.write((char*)PixelReadConverter->GetDecompressed(), PixelReadConverter->GetDecompressedSize());
350 * \brief Writes on disk A SINGLE Dicom file,
351 * using the Implicit Value Representation convention
352 * NO test is performed on processor "Endiannity".
353 * @param fileName name of the file to be created
354 * (any already existing file is overwritten)
355 * @return false if write fails
358 bool File::WriteDcmImplVR (std::string const & fileName)
360 SetWriteTypeToDcmImplVR();
361 return Write(fileName);
365 * \brief Writes on disk A SINGLE Dicom file,
366 * using the Explicit Value Representation convention
367 * NO test is performed on processor "Endiannity". * @param fileName name of the file to be created
368 * (any already existing file is overwritten)
369 * @return false if write fails
372 bool File::WriteDcmExplVR (std::string const & fileName)
374 SetWriteTypeToDcmExplVR();
375 return Write(fileName);
379 * \brief Writes on disk A SINGLE Dicom file,
380 * using the ACR-NEMA convention
381 * NO test is performed on processor "Endiannity".
382 * (a l'attention des logiciels cliniques
383 * qui ne prennent en entrée QUE des images ACR ...
384 * \warning if a DICOM_V3 header is supplied,
385 * groups < 0x0008 and shadow groups are ignored
386 * \warning NO TEST is performed on processor "Endiannity".
387 * @param fileName name of the file to be created
388 * (any already existing file is overwritten)
389 * @return false if write fails
392 bool File::WriteAcr (std::string const & fileName)
395 return Write(fileName);
398 bool File::Write(std::string const& fileName)
400 return WriteBase(fileName);
403 bool File::SetEntryByNumber(std::string const& content,
404 uint16_t group, uint16_t element)
406 return HeaderInternal->SetEntryByNumber(content,group,element);
409 bool File::SetEntryByNumber(uint8_t* content, int lgth,
410 uint16_t group, uint16_t element)
412 return HeaderInternal->SetEntryByNumber(content,lgth,group,element);
415 bool File::ReplaceOrCreateByNumber(std::string const& content,
416 uint16_t group, uint16_t element)
418 return HeaderInternal->ReplaceOrCreateByNumber(content,group,element) != NULL;
422 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT
424 uint8_t* File::GetLutRGBA()
426 return PixelReadConverter->GetLutRGBA();
429 //-----------------------------------------------------------------------------
432 * \brief NOT a end user inteded function
433 * (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
434 * @param fileName name of the file to be created
435 * (any already existing file is overwritten)
436 * @param type file type (ExplicitVR, ImplicitVR, ...)
437 * @return false if write fails
439 bool File::WriteBase (std::string const & fileName)
444 SetWriteFileTypeToImplicitVR();
447 SetWriteFileTypeToExplicitVR();
451 SetWriteFileTypeToACR();
454 SetWriteFileTypeToExplicitVR();
457 // --------------------------------------------------------------
458 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
460 // if recognition code tells us we dealt with a LibIDO image
461 // we reproduce on disk the switch between lineNumber and columnNumber
462 // just before writting ...
463 /// \todo the best trick would be *change* the recognition code
464 /// but pb expected if user deals with, e.g. COMPLEX images
465 if( WriteType == ACR_LIBIDO )
471 SetWriteToNoLibido();
473 // ----------------- End of Special Patch ----------------
477 case WMODE_DECOMPRESSED :
478 SetWriteToDecompressed();
485 bool check = CheckWriteIntegrity();
488 check = HeaderInternal->Write(fileName,WriteType);
492 RestoreWriteFileType();
494 // --------------------------------------------------------------
495 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
497 // ...and we restore the Header to be Dicom Compliant again
498 // just after writting
499 RestoreWriteOfLibido();
500 // ----------------- End of Special Patch ----------------
506 * \brief Check the write integrity
508 * The tests made are :
509 * - verify the size of the image to write with the possible write
510 * when the user set an image data
511 * @return true if the check successfulls
513 bool File::CheckWriteIntegrity()
515 if(PixelWriteConverter->GetUserData())
517 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
518 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
520 numberBitsAllocated = 16;
523 size_t decSize = HeaderInternal->GetXSize()
524 * HeaderInternal->GetYSize()
525 * HeaderInternal->GetZSize()
526 * ( numberBitsAllocated / 8 )
527 * HeaderInternal->GetSamplesPerPixel();
528 size_t rgbSize = decSize;
529 if( HeaderInternal->HasLUT() )
530 rgbSize = decSize * 3;
534 case WMODE_DECOMPRESSED :
535 if( decSize!=PixelWriteConverter->GetUserDataSize() )
537 dbg.Verbose(0, "File::CheckWriteIntegrity: Data size is incorrect");
542 if( rgbSize!=PixelWriteConverter->GetUserDataSize() )
544 dbg.Verbose(0, "File::CheckWriteIntegrity: Data size is incorrect");
554 void File::SetWriteToDecompressed()
556 if(HeaderInternal->GetNumberOfScalarComponents()==3 && !HeaderInternal->HasLUT())
562 ValEntry* photInt = CopyValEntry(0x0028,0x0004);
563 if(HeaderInternal->HasLUT())
565 photInt->SetValue("PALETTE COLOR ");
569 photInt->SetValue("MONOCHROME1 ");
572 PixelWriteConverter->SetReadData(PixelReadConverter->GetDecompressed(),
573 PixelReadConverter->GetDecompressedSize());
575 BinEntry* pixel = CopyBinEntry(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
576 pixel->SetValue(GDCM_BINLOADED);
577 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
578 pixel->SetLength(PixelWriteConverter->GetDataSize());
580 Archive->Push(photInt);
581 Archive->Push(pixel);
585 void File::SetWriteToRGB()
587 if(HeaderInternal->GetNumberOfScalarComponents()==3)
589 PixelReadConverter->BuildRGBImage();
591 ValEntry* spp = CopyValEntry(0x0028,0x0002);
594 ValEntry* planConfig = CopyValEntry(0x0028,0x0006);
595 planConfig->SetValue("0 ");
597 ValEntry* photInt = CopyValEntry(0x0028,0x0004);
598 photInt->SetValue("RGB ");
600 if(PixelReadConverter->GetRGB())
602 PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
603 PixelReadConverter->GetRGBSize());
605 else // Decompressed data
607 PixelWriteConverter->SetReadData(PixelReadConverter->GetDecompressed(),
608 PixelReadConverter->GetDecompressedSize());
611 BinEntry* pixel = CopyBinEntry(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
612 pixel->SetValue(GDCM_BINLOADED);
613 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
614 pixel->SetLength(PixelWriteConverter->GetDataSize());
617 Archive->Push(planConfig);
618 Archive->Push(photInt);
619 Archive->Push(pixel);
622 Archive->Push(0x0028,0x1101);
623 Archive->Push(0x0028,0x1102);
624 Archive->Push(0x0028,0x1103);
625 Archive->Push(0x0028,0x1201);
626 Archive->Push(0x0028,0x1202);
627 Archive->Push(0x0028,0x1203);
630 // Thus, we have a RGB image and the bits allocated = 24 and
631 // samples per pixels = 1 (in the read file)
632 if(HeaderInternal->GetBitsAllocated()==24)
634 ValEntry* bitsAlloc = CopyValEntry(0x0028,0x0100);
635 bitsAlloc->SetValue("8 ");
637 ValEntry* bitsStored = CopyValEntry(0x0028,0x0101);
638 bitsStored->SetValue("8 ");
640 ValEntry* highBit = CopyValEntry(0x0028,0x0102);
641 highBit->SetValue("7 ");
643 Archive->Push(bitsAlloc);
644 Archive->Push(bitsStored);
645 Archive->Push(highBit);
650 SetWriteToDecompressed();
654 void File::RestoreWrite()
656 Archive->Restore(0x0028,0x0002);
657 Archive->Restore(0x0028,0x0004);
658 Archive->Restore(0x0028,0x0006);
659 Archive->Restore(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
661 // For old ACR-NEMA (24 bits problem)
662 Archive->Restore(0x0028,0x0100);
663 Archive->Restore(0x0028,0x0101);
664 Archive->Restore(0x0028,0x0102);
667 Archive->Restore(0x0028,0x1101);
668 Archive->Restore(0x0028,0x1102);
669 Archive->Restore(0x0028,0x1103);
670 Archive->Restore(0x0028,0x1201);
671 Archive->Restore(0x0028,0x1202);
672 Archive->Restore(0x0028,0x1203);
675 void File::SetWriteFileTypeToACR()
677 Archive->Push(0x0002,0x0010);
680 void File::SetWriteFileTypeToExplicitVR()
682 std::string ts = Util::DicomString(
683 Document::GetTransferSyntaxValue(ExplicitVRLittleEndian).c_str() );
685 ValEntry* tss = CopyValEntry(0x0002,0x0010);
691 void File::SetWriteFileTypeToImplicitVR()
693 std::string ts = Util::DicomString(
694 Document::GetTransferSyntaxValue(ImplicitVRLittleEndian).c_str() );
696 ValEntry* tss = CopyValEntry(0x0002,0x0010);
700 void File::RestoreWriteFileType()
702 Archive->Restore(0x0002,0x0010);
705 void File::SetWriteToLibido()
707 ValEntry *oldRow = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0010));
708 ValEntry *oldCol = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0011));
710 if( oldRow && oldCol )
712 std::string rows, columns;
714 ValEntry *newRow=new ValEntry(oldRow->GetDictEntry());
715 ValEntry *newCol=new ValEntry(oldCol->GetDictEntry());
717 newRow->Copy(oldCol);
718 newCol->Copy(oldRow);
720 newRow->SetValue(oldCol->GetValue());
721 newCol->SetValue(oldRow->GetValue());
723 Archive->Push(newRow);
724 Archive->Push(newCol);
727 ValEntry *libidoCode = CopyValEntry(0x0008,0x0010);
728 libidoCode->SetValue("ACRNEMA_LIBIDO_1.1");
729 Archive->Push(libidoCode);
732 void File::SetWriteToNoLibido()
734 ValEntry *recCode = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0008,0x0010));
737 if( recCode->GetValue() == "ACRNEMA_LIBIDO_1.1" )
739 ValEntry *libidoCode = CopyValEntry(0x0008,0x0010);
740 libidoCode->SetValue("");
741 Archive->Push(libidoCode);
746 void File::RestoreWriteOfLibido()
748 Archive->Restore(0x0028,0x0010);
749 Archive->Restore(0x0028,0x0011);
750 Archive->Restore(0x0008,0x0010);
753 ValEntry* File::CopyValEntry(uint16_t group,uint16_t element)
755 DocEntry* oldE = HeaderInternal->GetDocEntryByNumber(group, element);
760 newE = new ValEntry(oldE->GetDictEntry());
765 newE = GetHeader()->NewValEntryByNumber(group,element);
771 BinEntry* File::CopyBinEntry(uint16_t group,uint16_t element)
773 DocEntry* oldE = HeaderInternal->GetDocEntryByNumber(group, element);
778 newE = new BinEntry(oldE->GetDictEntry());
783 newE = GetHeader()->NewBinEntryByNumber(group,element);
789 //-----------------------------------------------------------------------------
792 //-----------------------------------------------------------------------------
793 } // end namespace gdcm