1 /*=========================================================================
4 Module: $RCSfile: gdcmFile.cxx,v $
6 Date: $Date: 2004/12/07 09:32:24 $
7 Version: $Revision: 1.172 $
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 * user sets an a posteriori shadow dictionary (efficiency can be
48 * seen as a side effect).
49 * @param header already built Header
51 File::File(Header *header)
53 HeaderInternal = header;
59 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
60 * file (Header only deals with the ... header)
61 * Opens (in read only and when possible) an existing file and checks
62 * for DICOM compliance. Returns NULL on failure.
63 * It will be up to the user to load the pixels into memory
64 * (see GetImageData, GetImageDataRaw)
65 * \note the in-memory representation of all available tags found in
66 * the DICOM header is post-poned to first header information access.
67 * This avoid a double parsing of public part of the header when
68 * one sets an a posteriori shadow dictionary (efficiency can be
69 * seen as a side effect).
70 * @param filename file to be opened for parsing
72 File::File(std::string const & filename )
74 HeaderInternal = new Header( filename );
80 * \brief Factorization for various forms of constructors.
82 void File::Initialise()
84 WriteMode = WMODE_DECOMPRESSED;
85 WriteType = ImplicitVR;
87 PixelReadConverter = new PixelReadConvert;
88 PixelWriteConverter = new PixelWriteConvert;
89 Archive = new DocEntryArchive( HeaderInternal );
91 if ( HeaderInternal->IsReadable() )
93 PixelReadConverter->GrabInformationsFromHeader( HeaderInternal );
98 * \brief canonical destructor
99 * \note If the Header was created by the File constructor,
100 * it is destroyed by the File
104 if( PixelReadConverter )
106 delete PixelReadConverter;
108 if( PixelWriteConverter )
110 delete PixelWriteConverter;
119 delete HeaderInternal;
124 //-----------------------------------------------------------------------------
127 //-----------------------------------------------------------------------------
130 * \brief Get the size of the image data
132 * If the image can be RGB (with a lut or by default), the size
133 * corresponds to the RGB image
134 * @return The image size
136 size_t File::GetImageDataSize()
138 return PixelReadConverter->GetRGBSize();
142 * \brief Get the size of the image data
144 * If the image can be RGB by transformation in a LUT, this
145 * transformation isn't considered
146 * @return The raw image size
148 size_t File::GetImageDataRawSize()
150 return PixelReadConverter->GetDecompressedSize();
154 * \brief - Allocates necessary memory,
155 * - Reads the pixels from disk (uncompress if necessary),
156 * - Transforms YBR pixels, if any, into RGB pixels
157 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
158 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
159 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
160 * @return Pointer to newly allocated pixel data.
161 * NULL if alloc fails
163 uint8_t* File::GetImageData()
165 if ( ! GetDecompressed() )
167 // If the decompression failed nothing can be done.
171 if ( HeaderInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
173 return PixelReadConverter->GetRGB();
177 // When no LUT or LUT conversion fails, return the decompressed
178 return PixelReadConverter->GetDecompressed();
184 * Read the pixels from disk (uncompress if necessary),
185 * Transforms YBR pixels, if any, into RGB pixels
186 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
187 * Transforms single Grey plane + 3 Palettes into a RGB Plane
188 * Copies at most MaxSize bytes of pixel data to caller allocated
190 * \warning This function allows people that want to build a volume
191 * from an image stack *not to* have, first to get the image pixels,
192 * and then move them to the volume area.
193 * It's absolutely useless for any VTK user since vtk chooses
194 * to invert the lines of an image, that is the last line comes first
195 * (for some axis related reasons?). Hence he will have
196 * to load the image line by line, starting from the end.
197 * VTK users have to call GetImageData
199 * @param destination Address (in caller's memory space) at which the
200 * pixel data should be copied
201 * @param maxSize Maximum number of bytes to be copied. When MaxSize
202 * is not sufficient to hold the pixel data the copy is not
203 * executed (i.e. no partial copy).
204 * @return On success, the number of bytes actually copied. Zero on
205 * failure e.g. MaxSize is lower than necessary.
207 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
209 if ( ! GetDecompressed() )
211 // If the decompression failed nothing can be done.
215 if ( HeaderInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
217 if ( PixelReadConverter->GetRGBSize() > maxSize )
219 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
220 "than caller's expected MaxSize");
224 (void*)PixelReadConverter->GetRGB(),
225 PixelReadConverter->GetRGBSize() );
226 return PixelReadConverter->GetRGBSize();
229 // Either no LUT conversion necessary or LUT conversion failed
230 if ( PixelReadConverter->GetDecompressedSize() > maxSize )
232 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
233 "than caller's expected MaxSize");
237 (void*)PixelReadConverter->GetDecompressed(),
238 PixelReadConverter->GetDecompressedSize() );
239 return PixelReadConverter->GetDecompressedSize();
243 * \brief Allocates necessary memory,
244 * Transforms YBR pixels (if any) into RGB pixels
245 * Transforms 3 planes R, G, B (if any) into a single RGB Plane
246 * Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
247 * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
248 * @return Pointer to newly allocated pixel data.
249 * \ NULL if alloc fails
251 uint8_t* File::GetImageDataRaw ()
253 return GetDecompressed();
256 uint8_t* File::GetDecompressed()
258 uint8_t* decompressed = PixelReadConverter->GetDecompressed();
259 if ( ! decompressed )
261 // The decompressed image migth not be loaded yet:
262 std::ifstream* fp = HeaderInternal->OpenFile();
263 PixelReadConverter->ReadAndDecompressPixelData( fp );
265 HeaderInternal->CloseFile();
267 decompressed = PixelReadConverter->GetDecompressed();
268 if ( ! decompressed )
270 dbg.Verbose(0, "File::GetDecompressed: read/decompress of "
271 "pixel data apparently went wrong.");
280 * \brief Points the internal pointer to the callers inData
281 * image representation, BUT WITHOUT COPYING THE DATA.
282 * 'image' Pixels are presented as C-like 2D arrays : line per line.
283 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
284 * \warning Since the pixels are not copied, it is the caller's responsability
285 * not to deallocate it's data before gdcm uses them (e.g. with
286 * the Write() method.
287 * @param inData user supplied pixel area
288 * @param expectedSize total image size, in Bytes
292 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
294 PixelWriteConverter->SetUserData(inData,expectedSize);
300 * \brief Writes on disk A SINGLE Dicom file
301 * NO test is performed on processor "Endiannity".
302 * It's up to the user to call his Reader properly
303 * @param fileName name of the file to be created
304 * (any already existing file is over written)
305 * @return false if write fails
308 bool File::WriteRawData(std::string const & fileName)
310 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
313 dbg.Verbose(2, "Fail to open (write) file:", fileName.c_str());
317 if(PixelWriteConverter->GetUserData())
318 fp1.write((char*)PixelWriteConverter->GetUserData(), PixelWriteConverter->GetUserDataSize());
319 else if(PixelReadConverter->GetRGB())
320 fp1.write((char*)PixelReadConverter->GetRGB(), PixelReadConverter->GetRGBSize());
321 else if(PixelReadConverter->GetDecompressed())
322 fp1.write((char*)PixelReadConverter->GetDecompressed(), PixelReadConverter->GetDecompressedSize());
330 * \brief Writes on disk A SINGLE Dicom file,
331 * using the Implicit Value Representation convention
332 * NO test is performed on processor "Endiannity".
333 * @param fileName name of the file to be created
334 * (any already existing file is overwritten)
335 * @return false if write fails
338 bool File::WriteDcmImplVR (std::string const & fileName)
340 SetWriteTypeToDcmImplVR();
341 return Write(fileName);
345 * \brief Writes on disk A SINGLE Dicom file,
346 * using the Explicit Value Representation convention
347 * NO test is performed on processor "Endiannity". * @param fileName name of the file to be created
348 * (any already existing file is overwritten)
349 * @return false if write fails
352 bool File::WriteDcmExplVR (std::string const & fileName)
354 SetWriteTypeToDcmExplVR();
355 return Write(fileName);
359 * \brief Writes on disk A SINGLE Dicom file,
360 * using the ACR-NEMA convention
361 * NO test is performed on processor "Endiannity".
362 * (a l'attention des logiciels cliniques
363 * qui ne prennent en entrée QUE des images ACR ...
364 * \warning if a DICOM_V3 header is supplied,
365 * groups < 0x0008 and shadow groups are ignored
366 * \warning NO TEST is performed on processor "Endiannity".
367 * @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::WriteAcr (std::string const & fileName)
375 return Write(fileName);
378 bool File::Write(std::string const& fileName)
380 return WriteBase(fileName);
383 bool File::SetEntryByNumber(std::string const& content,
384 uint16_t group, uint16_t element)
386 HeaderInternal->SetEntryByNumber(content,group,element);
391 * \brief Access to the underlying \ref PixelReadConverter RGBA LUT
393 uint8_t* File::GetLutRGBA()
395 return PixelReadConverter->GetLutRGBA();
398 //-----------------------------------------------------------------------------
401 * \brief NOT a end user inteded function
402 * (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
403 * @param fileName name of the file to be created
404 * (any already existing file is overwritten)
405 * @param type file type (ExplicitVR, ImplicitVR, ...)
406 * @return false if write fails
408 bool File::WriteBase (std::string const & fileName)
413 SetWriteFileTypeToImplicitVR();
416 SetWriteFileTypeToExplicitVR();
420 SetWriteFileTypeToACR();
423 SetWriteFileTypeToExplicitVR();
426 // --------------------------------------------------------------
427 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
429 // if recognition code tells us we dealt with a LibIDO image
430 // we reproduce on disk the switch between lineNumber and columnNumber
431 // just before writting ...
432 /// \todo the best trick would be *change* the recognition code
433 /// but pb expected if user deals with, e.g. COMPLEX images
434 if( WriteType == ACR_LIBIDO )
440 SetWriteToNoLibido();
442 // ----------------- End of Special Patch ----------------
446 case WMODE_DECOMPRESSED :
447 SetWriteToDecompressed();
454 bool check = CheckWriteIntegrity();
457 check = HeaderInternal->Write(fileName,WriteType);
461 RestoreWriteFileType();
463 // --------------------------------------------------------------
464 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
466 // ...and we restore the Header to be Dicom Compliant again
467 // just after writting
468 RestoreWriteOfLibido();
469 // ----------------- End of Special Patch ----------------
475 * \brief Check the write integrity
477 * The tests made are :
478 * - verify the size of the image to write with the possible write
479 * when the user set an image data
480 * @return true if the check successfulls
482 bool File::CheckWriteIntegrity()
484 if(PixelWriteConverter->GetUserData())
486 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
487 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
489 numberBitsAllocated = 16;
492 size_t decSize = HeaderInternal->GetXSize()
493 * HeaderInternal->GetYSize()
494 * HeaderInternal->GetZSize()
495 * ( numberBitsAllocated / 8 )
496 * HeaderInternal->GetSamplesPerPixel();
497 size_t rgbSize = decSize;
498 if( HeaderInternal->HasLUT() )
499 rgbSize = decSize * 3;
503 case WMODE_DECOMPRESSED :
504 if( decSize!=PixelWriteConverter->GetUserDataSize() )
506 dbg.Verbose(0, "File::CheckWriteIntegrity: Data size is incorrect");
511 if( rgbSize!=PixelWriteConverter->GetUserDataSize() )
513 dbg.Verbose(0, "File::CheckWriteIntegrity: Data size is incorrect");
523 void File::SetWriteToDecompressed()
525 if(HeaderInternal->GetNumberOfScalarComponents()==3 && !HeaderInternal->HasLUT())
531 ValEntry* photInt = CopyValEntry(0x0028,0x0004);
532 if(HeaderInternal->HasLUT())
534 photInt->SetValue("PALETTE COLOR ");
535 photInt->SetLength(14);
539 photInt->SetValue("MONOCHROME1 ");
540 photInt->SetLength(12);
543 PixelWriteConverter->SetReadData(PixelReadConverter->GetDecompressed(),
544 PixelReadConverter->GetDecompressedSize());
546 BinEntry* pixel = CopyBinEntry(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
547 pixel->SetValue(GDCM_BINLOADED);
548 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
549 pixel->SetLength(PixelWriteConverter->GetDataSize());
551 Archive->Push(photInt);
552 Archive->Push(pixel);
556 void File::SetWriteToRGB()
558 if(HeaderInternal->GetNumberOfScalarComponents()==3)
560 PixelReadConverter->BuildRGBImage();
562 ValEntry* spp = CopyValEntry(0x0028,0x0002);
566 ValEntry* planConfig = CopyValEntry(0x0028,0x0006);
567 planConfig->SetValue("0 ");
568 planConfig->SetLength(2);
570 ValEntry* photInt = CopyValEntry(0x0028,0x0004);
571 photInt->SetValue("RGB ");
572 photInt->SetLength(4);
574 if(PixelReadConverter->GetRGB())
576 PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
577 PixelReadConverter->GetRGBSize());
579 else // Decompressed data
581 PixelWriteConverter->SetReadData(PixelReadConverter->GetDecompressed(),
582 PixelReadConverter->GetDecompressedSize());
585 BinEntry* pixel = CopyBinEntry(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
586 pixel->SetValue(GDCM_BINLOADED);
587 pixel->SetBinArea(PixelWriteConverter->GetData(),false);
588 pixel->SetLength(PixelWriteConverter->GetDataSize());
591 Archive->Push(planConfig);
592 Archive->Push(photInt);
593 Archive->Push(pixel);
596 Archive->Push(0x0028,0x1101);
597 Archive->Push(0x0028,0x1102);
598 Archive->Push(0x0028,0x1103);
599 Archive->Push(0x0028,0x1201);
600 Archive->Push(0x0028,0x1202);
601 Archive->Push(0x0028,0x1203);
604 // Thus, we have a RGB image and the bits allocated = 24 and
605 // samples per pixels = 1 (in the read file)
606 if(HeaderInternal->GetBitsAllocated()==24)
608 ValEntry* bitsAlloc = CopyValEntry(0x0028,0x0100);
609 bitsAlloc->SetValue("8 ");
610 bitsAlloc->SetLength(2);
612 ValEntry* bitsStored = CopyValEntry(0x0028,0x0101);
613 bitsStored->SetValue("8 ");
614 bitsStored->SetLength(2);
616 ValEntry* highBit = CopyValEntry(0x0028,0x0102);
617 highBit->SetValue("7 ");
618 highBit->SetLength(2);
620 Archive->Push(bitsAlloc);
621 Archive->Push(bitsStored);
622 Archive->Push(highBit);
627 SetWriteToDecompressed();
631 void File::RestoreWrite()
633 Archive->Restore(0x0028,0x0002);
634 Archive->Restore(0x0028,0x0004);
635 Archive->Restore(0x0028,0x0006);
636 Archive->Restore(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
638 // For old ACR-NEMA (24 bits problem)
639 Archive->Restore(0x0028,0x0100);
640 Archive->Restore(0x0028,0x0101);
641 Archive->Restore(0x0028,0x0102);
644 Archive->Restore(0x0028,0x1101);
645 Archive->Restore(0x0028,0x1102);
646 Archive->Restore(0x0028,0x1103);
647 Archive->Restore(0x0028,0x1201);
648 Archive->Restore(0x0028,0x1202);
649 Archive->Restore(0x0028,0x1203);
652 void File::SetWriteFileTypeToACR()
654 Archive->Push(0x0002,0x0010);
657 void File::SetWriteFileTypeToExplicitVR()
659 std::string ts = Util::DicomString(
660 Document::GetTransferSyntaxValue(ExplicitVRLittleEndian).c_str() );
662 ValEntry* tss = CopyValEntry(0x0002,0x0010);
664 tss->SetLength(ts.length());
669 void File::SetWriteFileTypeToImplicitVR()
671 std::string ts = Util::DicomString(
672 Document::GetTransferSyntaxValue(ImplicitVRLittleEndian).c_str() );
674 ValEntry* tss = CopyValEntry(0x0002,0x0010);
676 tss->SetLength(ts.length());
679 void File::RestoreWriteFileType()
681 Archive->Restore(0x0002,0x0010);
684 void File::SetWriteToLibido()
686 ValEntry *oldRow = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0010));
687 ValEntry *oldCol = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0011));
689 if( oldRow && oldCol )
691 std::string rows, columns;
693 ValEntry *newRow=new ValEntry(oldRow->GetDictEntry());
694 ValEntry *newCol=new ValEntry(oldCol->GetDictEntry());
696 newRow->Copy(oldCol);
697 newCol->Copy(oldRow);
699 newRow->SetValue(oldCol->GetValue());
700 newCol->SetValue(oldRow->GetValue());
702 Archive->Push(newRow);
703 Archive->Push(newCol);
706 ValEntry *libidoCode = CopyValEntry(0x0008,0x0010);
707 libidoCode->SetValue("ACRNEMA_LIBIDO_1.1");
708 libidoCode->SetLength(10);
709 Archive->Push(libidoCode);
712 void File::SetWriteToNoLibido()
714 ValEntry *recCode = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0008,0x0010));
717 if( recCode->GetValue() == "ACRNEMA_LIBIDO_1.1" )
719 ValEntry *libidoCode = CopyValEntry(0x0008,0x0010);
720 libidoCode->SetValue("");
721 libidoCode->SetLength(0);
722 Archive->Push(libidoCode);
727 void File::RestoreWriteOfLibido()
729 Archive->Restore(0x0028,0x0010);
730 Archive->Restore(0x0028,0x0011);
731 Archive->Restore(0x0008,0x0010);
734 ValEntry* File::CopyValEntry(uint16_t group,uint16_t element)
736 DocEntry* oldE = HeaderInternal->GetDocEntryByNumber(group, element);
741 newE = new ValEntry(oldE->GetDictEntry());
746 newE = GetHeader()->NewValEntryByNumber(group,element);
752 BinEntry* File::CopyBinEntry(uint16_t group,uint16_t element)
754 DocEntry* oldE = HeaderInternal->GetDocEntryByNumber(group, element);
759 newE = new BinEntry(oldE->GetDictEntry());
764 newE = GetHeader()->NewBinEntryByNumber(group,element);
770 //-----------------------------------------------------------------------------
773 //-----------------------------------------------------------------------------
774 } // end namespace gdcm