X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmFileHelper.cxx;h=f05197d19a62d09a28f21419b044b170ffa4d17a;hb=add6c88b334e9cf4ddb0ae1849f1380f9844f99c;hp=62c0374010cd07b5e0f043fa57d3fac6ba34b8e8;hpb=1c4a6671c995962804086936a409bd0c8cb2b985;p=gdcm.git diff --git a/src/gdcmFileHelper.cxx b/src/gdcmFileHelper.cxx index 62c03740..f05197d1 100644 --- a/src/gdcmFileHelper.cxx +++ b/src/gdcmFileHelper.cxx @@ -4,8 +4,8 @@ Module: $RCSfile: gdcmFileHelper.cxx,v $ Language: C++ - Date: $Date: 2006/05/30 08:14:50 $ - Version: $Revision: 1.104 $ + Date: $Date: 2007/10/08 15:20:17 $ + Version: $Revision: 1.132 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -51,73 +51,113 @@ gdcm::File *f = new gdcm::File(fileName); // user may also decide he doesn't want to load some parts of the header gdcm::File *f = new gdcm::File(); f->SetFileName(fileName); - f->SetLoadMode(LD_NOSEQ); // or - f->SetLoadMode(LD_NOSHADOW); // or + f->SetLoadMode(LD_NOSEQ); // or + f->SetLoadMode(LD_NOSHADOW); // or f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW); // or f->SetLoadMode(LD_NOSHADOWSEQ); f->Load(); +// To decide whether it's an 'image of interest for him, or not, // user can now check some values std::string v = f->GetEntryValue(groupNb,ElementNb); // to get the pixels, user needs a gdcm::FileHelper gdcm::FileHelper *fh = new gdcm::FileHelper(f); -// user may ask not to convert Palette to RGB + +// user may ask not to convert Palette (if any) to RGB uint8_t *pixels = fh->GetImageDataRaw(); int imageLength = fh->GetImageDataRawSize(); + // He can now use the pixels, create a new image, ... uint8_t *userPixels = ... -To re-write the image, user re-uses the gdcm::FileHelper +//To re-write the image, user re-uses the gdcm::FileHelper +gdcm::File *fh = new gdcm::FileHelper(); -fh->SetImageData( userPixels, userPixelsLength); fh->SetTypeToRaw(); // Even if it was possible to convert Palette to RGB - // (WriteMode is set) - + // (WriteMode is set) + +// If user wants to write the file as MONOCHROME1 (0=white) +fh->SetPhotometricInterpretationToMonochrome1(); + fh->SetWriteTypeToDcmExpl(); // he wants Explicit Value Representation - // Little Endian is the default - // no other value is allowed + // Little Endian is the default, + // bigendian not supported for writting (-->SetWriteType(ExplicitVR);) -->WriteType = ExplicitVR; +fh->SetWriteTypeToJPEG(); // lossless compression +fh->SetWriteTypeToJPEG2000(); // lossless compression + +fh->SetImageData( userPixels, userPixelsLength); +or +fh->SetUserData( userPixels, userPixelsLength); // this one performs compression, when required + fh->Write(newFileName); // overwrites the file, if any -// or : -fh->WriteDcmExplVR(newFileName); -// ----------------------------- WARNING ------------------------- These lines will be moved to the document-to-be 'Developer's Guide' WriteMode : WMODE_RAW / WMODE_RGB WriteType : ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO +PhotometricInterpretation : MONOCHROME2 (0=black), MONOCHROME2 (0=white) -fh1->Write(newFileName); - SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR(); - (modifies TransferSyntax) +fh->SetImageData( userPixels, userPixelsLength); +or +fh->SetUserData( userPixels, userPixelsLength); + PixelWriteConverter->SetUserData(inData, expectedSize); + + +fh->SetWriteMode(WMODE_RAW / WMODE_RGB) + +fh->SetWriteType( ImplicitVR/ExplicitVR/ACR/ACR_LIBIDO/JPEG/JPEG2000) + +fh->Write(newFileName); + CheckMandatoryElements(); // Checks existing ones / Add missing ones + Fix VR if unknown elements + SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR(); / + SetWriteFileTypeToACR() / SetWriteFileTypeToJPEG() / SetWriteFileTypeToJ2K() + (Modifies TransferSyntax if any; Pushes to the Archives old one) SetWriteToRaw(); / SetWriteToRGB(); - (modifies, when necessary : photochromatic interpretation, - samples per pixel, Planar configuration, - bits allocated, bits stored, high bit -ACR 24 bits- - Pixels element VR, pushes out the LUT ) + (Modifies and pushes to the Archive, when necessary : photochr. interp., + samples per pixel, Planar configuration, + bits allocated, bits stored, high bit -ACR 24 bits- + Pixels element VR, pushes out the LUT ) + SetWriteToRaw() + Sets Photometric Interpretation + DataEntry *pixel =CopyDataEntry(7fe0,0010,VR) + Sets VR, BinArea, Length for PixelData + if MONOCHROME1 + ConvertFixGreyLevels + Archive->Push(photInt); + Archive->Push(pixel); + photInt->Delete(); + pixel->Delete(); + SetWriteToRGB() + if NumberOfScalarComponents==1 + SetWriteToRaw(); return; + PixelReadConverter->BuildRGBImage() + DataEntry *pixel =CopyDataEntry(7fe0,0010,VR) + Archives spp, planConfig,photInt, pixel + Pushes out any LUT CheckWriteIntegrity(); (checks user given pixels length) FileInternal->Write(fileName,WriteType) - fp = opens file(fileName); - ComputeGroup0002Length( ); - BitsAllocated 12->16 - RemoveEntry(palettes, etc) + fp = opens file(fileName); // out|binary + ComputeGroup0002Length( ); Document::WriteContent(fp, writetype); + writes Dicom File Preamble not ACR-NEMA + ElementSet::WriteContent(fp, writetype); + writes recursively all DataElements RestoreWrite(); - (moves back to the File all the archived elements) - RestoreWriteFileType(); - (pushes back group 0002, with TransferSyntax) + (moves back to the gdcm::File all the archived elements) */ -namespace gdcm +namespace GDCM_NAME_SPACE { typedef std::map GroupHT; // Hash Table //------------------------------------------------------------------------- @@ -230,8 +270,8 @@ bool FileHelper::Load() } /** - * \brief Accesses an existing DataEntry through it's (group, element) - * and modifies it's content with the given value. + * \brief Accesses an existing DataEntry through its (group, element) + * and modifies its content with the given value. * @param content new value (string) to substitute with * @param group group number of the Dicom Element to modify * @param elem element number of the Dicom Element to modify @@ -245,8 +285,8 @@ bool FileHelper::SetEntryString(std::string const &content, /** - * \brief Accesses an existing DataEntry through it's (group, element) - * and modifies it's content with the given value. + * \brief Accesses an existing DataEntry through its (group, element) + * and modifies its content with the given value. * @param content new value (void* -> uint8_t*) to substitute with * @param lgth new value length * @param group group number of the Dicom Element to modify @@ -265,6 +305,7 @@ bool FileHelper::SetEntryBinArea(uint8_t *content, int lgth, * @param content (string) value to be set * @param group Group number of the Entry * @param elem Element number of the Entry + * @param vr Value Representation of the DataElement to be inserted * \return pointer to the modified/created DataEntry (NULL when creation * failed). */ @@ -279,10 +320,11 @@ DataEntry *FileHelper::InsertEntryString(std::string const &content, * \brief Modifies the value of a given DataEntry when it exists. * Creates it with the given value when unexistant. * A copy of the binArea is made to be kept in the Document. - * @param binArea (binary)value to be set + * @param binArea (binary) value to be set * @param lgth new value length * @param group Group number of the Entry * @param elem Element number of the Entry + * @param vr Value Representation of the DataElement to be inserted * \return pointer to the modified/created DataEntry (NULL when creation * failed). */ @@ -463,13 +505,15 @@ size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize) * not to deallocate its data before gdcm uses them (e.g. with * the Write() method ) * @param inData user supplied pixel area (uint8_t* is just for the compiler. - * user is allowed to pass any kind of pixelsn since the size is + * user is allowed to pass any kind of pixels since the size is * given in bytes) * @param expectedSize total image size, *in Bytes* */ void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize) { - SetUserData(inData, expectedSize); + PixelWriteConverter->SetUserData(inData, expectedSize); + /// \todo : shouldn't we call SetCompressJPEGUserData/SetCompressJPEG2000UserData + /// here, too? } /** @@ -482,6 +526,22 @@ void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize) */ void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize) { + // Shouldn't we move theese lines to FileHelper::Write()? +/* + if( WriteType == JPEG2000 ) + { + PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal); + } + else if( WriteType == JPEG ) + { + PixelWriteConverter->SetCompressJPEGUserData(inData, expectedSize, FileInternal); + } + else + { + PixelWriteConverter->SetUserData(inData, expectedSize); + } + */ + // Just try! PixelWriteConverter->SetUserData(inData, expectedSize); } @@ -540,7 +600,7 @@ size_t FileHelper::GetRawDataSize() } /** - * \brief Access to the underlying \ref PixelReadConverter RGBA LUT + * \brief Access to the underlying PixelReadConverter RGBA LUT */ uint8_t* FileHelper::GetLutRGBA() { @@ -550,7 +610,7 @@ uint8_t* FileHelper::GetLutRGBA() } /** - * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Number + * \brief Access to the underlying PixelReadConverter RGBA LUT Item Number */ int FileHelper::GetLutItemNumber() { @@ -558,7 +618,7 @@ int FileHelper::GetLutItemNumber() } /** - * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Size + * \brief Access to the underlying PixelReadConverter RGBA LUT Item Size */ int FileHelper::GetLutItemSize() { @@ -664,12 +724,9 @@ bool FileHelper::WriteAcr (std::string const &fileName) * @return false if write fails */ bool FileHelper::Write(std::string const &fileName) -{ - +{ CheckMandatoryElements(); //called once, here ! - bool flag = false; - DocEntry *e; switch(WriteType) { case ImplicitVR: @@ -678,36 +735,10 @@ bool FileHelper::Write(std::string const &fileName) case Unknown: // should never happen; ExplicitVR is the default value case ExplicitVR: - - // User should ask gdcm to write an image in Explicit VR mode - // only when he is sure *all* the VR of *all* the DataElements is known. - // i.e : when there are *only* Public Groups - // or *all* the Shadow Groups are fully described in the relevant Shadow - // Dictionnary - // Let's just *dream* about it; *never* trust a user ! - // We turn to Implicit VR if at least the VR of one element is unknown. - - e = FileInternal->GetFirstEntry(); - while (e != 0) - { - if (e->GetVR() == " ") - { - SetWriteTypeToDcmImplVR(); - SetWriteFileTypeToImplicitVR(); - flag = true; - break; - } - e = FileInternal->GetNextEntry(); - } - - if (!flag) - { - SetWriteFileTypeToExplicitVR(); - } - break; + // We let DocEntry::WriteContent to put vr=UN for undocumented Shadow Groups ! + SetWriteFileTypeToExplicitVR(); - SetWriteFileTypeToExplicitVR(); // to see JPRx break; case ACR: case ACR_LIBIDO: @@ -724,12 +755,29 @@ bool FileHelper::Write(std::string const &fileName) // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR ! break; - /// \todo FIXME : JPEG may be either ExplicitVR or ImplicitVR + /// \todo FIXME : JPEG/JPEG2000 may be either ExplicitVR or ImplicitVR case JPEG: SetWriteFileTypeToJPEG(); + // was : + //PixelWriteConverter->SetCompressJPEGUserData( + // inData, expectedSize, FileInternal); + PixelWriteConverter->SetCompressJPEGUserData( + PixelWriteConverter->GetUserData(), + PixelWriteConverter->GetUserDataSize(),FileInternal); + break; + + case JPEG2000: + /// \todo Maybe we should consider doing the compression here ! + // PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal); + + SetWriteFileTypeToJPEG2000(); + PixelWriteConverter->SetCompressJPEG2000UserData( + PixelWriteConverter->GetUserData(), + PixelWriteConverter->GetUserDataSize(), + FileInternal); break; } - + // -------------------------------------------------------------- // Special Patch to allow gdcm to re-write ACR-LibIDO formated images // @@ -759,17 +807,20 @@ bool FileHelper::Write(std::string const &fileName) break; } - bool check = CheckWriteIntegrity(); // verifies length - if (WriteType == JPEG ) check = true; + bool check; + if (WriteType == JPEG || WriteType == JPEG2000) + check = true; + else + check = CheckWriteIntegrity(); // verifies length + if (check) { check = FileInternal->Write(fileName,WriteType); } - RestoreWrite(); + RestoreWrite(); // RestoreWriteFileType(); // RestoreWriteMandatory(); - // -------------------------------------------------------------- // Special Patch to allow gdcm to re-write ACR-LibIDO formated images @@ -785,11 +836,7 @@ bool FileHelper::Write(std::string const &fileName) //----------------------------------------------------------------------------- // Protected /** - * \brief Checks the write integrity - * - * The tests made are : - * - verify the size of the image to write with the possible write - * when the user set an image data + * \brief Verifies the size of the user given PixelData * @return true if check is successfull */ bool FileHelper::CheckWriteIntegrity() @@ -799,14 +846,14 @@ bool FileHelper::CheckWriteIntegrity() int numberBitsAllocated = FileInternal->GetBitsAllocated(); if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 ) { - gdcmWarningMacro( "numberBitsAllocated changed from " - << numberBitsAllocated << " to 16 " + gdcmWarningMacro( "numberBitsAllocated changed from " + << numberBitsAllocated << " to 16 " << " for consistency purpose" ); numberBitsAllocated = 16; } size_t decSize = FileInternal->GetXSize() - * FileInternal->GetYSize() + * FileInternal->GetYSize() * FileInternal->GetZSize() * FileInternal->GetTSize() * FileInternal->GetSamplesPerPixel() @@ -815,29 +862,29 @@ bool FileHelper::CheckWriteIntegrity() if ( FileInternal->HasLUT() ) rgbSize = decSize * 3; + size_t userDataSize = PixelWriteConverter->GetUserDataSize(); switch(WriteMode) { case WMODE_RAW : - if ( decSize!=PixelWriteConverter->GetUserDataSize() ) + if ( abs((long)(decSize-userDataSize))>1) // ignore padding zero { gdcmWarningMacro( "Data size (Raw) is incorrect. Should be " << decSize << " / Found :" - << PixelWriteConverter->GetUserDataSize() ); + << userDataSize ); return false; } break; case WMODE_RGB : - if ( rgbSize!=PixelWriteConverter->GetUserDataSize() ) + if ( abs((long)(rgbSize-userDataSize))>1) // ignore padding zero { gdcmWarningMacro( "Data size (RGB) is incorrect. Should be " - << decSize << " / Found " - << PixelWriteConverter->GetUserDataSize() ); + << rgbSize << " / Found " + << userDataSize ); return false; } break; } } - return true; } @@ -845,6 +892,8 @@ bool FileHelper::CheckWriteIntegrity() * \brief Updates the File to write RAW data (as opposed to RGB data) * (modifies, when necessary, photochromatic interpretation, * bits allocated, Pixels element VR) + * WARNING : if SetPhotometricInterpretationToMonochrome1() was called + * before Pixel Elements is modified :-( */ void FileHelper::SetWriteToRaw() { @@ -855,6 +904,7 @@ void FileHelper::SetWriteToRaw() } else { + // 0x0028,0x0004 : Photometric Interpretation DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS"); if (FileInternal->HasLUT() ) { @@ -862,7 +912,10 @@ void FileHelper::SetWriteToRaw() } else { - photInt->SetString("MONOCHROME2 "); + if (GetPhotometricInterpretation() == 2) + photInt->SetString("MONOCHROME2 "); // 0 = Black + else + photInt->SetString("MONOCHROME1 "); // 0 = White ! } PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(), @@ -873,11 +926,23 @@ void FileHelper::SetWriteToRaw() vr = "OW"; if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files vr = "OB"; + // For non RAW data. Mainly JPEG/JPEG2000 + if( WriteType == JPEG || WriteType == JPEG2000) + { + vr = "OW"; + } + DataEntry *pixel = CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr); pixel->SetFlag(DataEntry::FLAG_PIXELDATA); pixel->SetBinArea(PixelWriteConverter->GetData(),false); - pixel->SetLength(PixelWriteConverter->GetDataSize()); + pixel->SetLength( + static_cast< uint32_t >(PixelWriteConverter->GetDataSize()) ); + + if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1) + { + ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() ); + } Archive->Push(photInt); Archive->Push(pixel); @@ -901,13 +966,13 @@ void FileHelper::SetWriteToRGB() PixelReadConverter->BuildRGBImage(); DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US"); - spp->SetString("3 "); + spp->SetString("3 "); // Don't drop trailing space DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US"); - planConfig->SetString("0 "); + planConfig->SetString("0 "); // Don't drop trailing space DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS"); - photInt->SetString("RGB "); + photInt->SetString("RGB "); // Don't drop trailing space if ( PixelReadConverter->GetRGB() ) { @@ -986,7 +1051,6 @@ void FileHelper::SetWriteToRGB() */ void FileHelper::RestoreWrite() { - Archive->Restore(0x0028,0x0002); Archive->Restore(0x0028,0x0004); @@ -1044,13 +1108,28 @@ void FileHelper::SetWriteFileTypeToACR() Archive->Push(0x0002,0x0102); } +/** + * \brief Sets in the File the TransferSyntax to 'JPEG2000' + */ +void FileHelper::SetWriteFileTypeToJPEG2000() +{ + std::string ts = Util::DicomString( + Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) ); + + DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI"); + tss->SetString(ts); + + Archive->Push(tss); + tss->Delete(); +} + /** * \brief Sets in the File the TransferSyntax to 'JPEG' - */ + */ void FileHelper::SetWriteFileTypeToJPEG() { - std::string ts = Util::DicomString( - Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGBaselineProcess1) ); + std::string ts = Util::DicomString( + Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGLosslessProcess14_1) ); DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI"); tss->SetString(ts); @@ -1087,14 +1166,6 @@ void FileHelper::SetWriteFileTypeToImplicitVR() tss->Delete(); } - -/** - * \brief Restore in the File the initial group 0002 - */ -void FileHelper::RestoreWriteFileType() -{ -} - /** * \brief Set the Write not to Libido format */ @@ -1107,12 +1178,9 @@ void FileHelper::SetWriteToLibido() { std::string rows, columns; - //DataEntry *newRow=DataEntry::New(oldRow->GetDictEntry()); - //DataEntry *newCol=DataEntry::New(oldCol->GetDictEntry()); - DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US"); DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US"); - + newRow->Copy(oldCol); newCol->Copy(oldRow); @@ -1185,7 +1253,6 @@ DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem, if ( oldE ) { - //newE = DataEntry::New(oldE->GetDictEntry()); newE = DataEntry::New(group, elem, vr); newE->Copy(oldE); } @@ -1243,9 +1310,10 @@ We have to deal with 4 *very* different cases : FILTERED_IMAGE -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image) CREATED_IMAGE --4) user modified/added some tags *without processing* the pixels (anonymization.. +-4) user modified/added some tags *without processing* the pixels (anonymization...) UNMODIFIED_PIXELS_IMAGE --Probabely some more to be added +-Probabely some more to be added. + --> Set it with FileHelper::SetContentType(int); gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases. @@ -1286,7 +1354,7 @@ If 'SOP Class UID' exists in the native image ('true DICOM' image) --> 'Referenced SOP Instance UID' (0x0008, 0x1155) whose value is the original 'SOP Class UID' -3) TODO : find a trick to allow user to pass to the writter the list of the Dicom images +3) TO DO : find a trick to allow user to pass to the writter the list of the Dicom images or the Series, (or the Study ?) he used to created his image (MIP, MPR, cartography image, ...) These info should be stored (?) @@ -1382,15 +1450,20 @@ void FileHelper::CheckMandatoryElements() //0002 0000 UL 1 Meta Group Length //0002 0001 OB 1 File Meta Information Version - //0002 0002 UI 1 Media Stored SOP Class UID - //0002 0003 UI 1 Media Stored SOP Instance UID + //0002 0002 UI 1 Media Storage SOP Class UID + //0002 0003 UI 1 Media Storage SOP Instance UID //0002 0010 UI 1 Transfer Syntax UID //0002 0012 UI 1 Implementation Class UID //0002 0013 SH 1 Implementation Version Name //0002 0016 AE 1 Source Application Entity Title //0002 0100 UI 1 Private Information Creator //0002 0102 OB 1 Private Information - + + // Push out 'ACR-NEMA-special' entries, if any + Archive->Push(0x0008,0x0001); // Length to End + Archive->Push(0x0008,0x0010); // Recognition Code + Archive->Push(0x0028,0x0005); // Image Dimension + // Create them if not found // Always modify the value // Push the entries to the archive. @@ -1414,10 +1487,10 @@ void FileHelper::CheckMandatoryElements() // 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage] CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI"); } - - // 'Media Storage SOP Instance UID' + + // 'Media Storage SOP Instance UID' CopyMandatoryEntry(0x0002,0x0003,sop,"UI"); - + // 'Implementation Class UID' // FIXME : in all examples we have, 0x0002,0x0012 is not so long : // seems to be Root UID + 4 digits (?) @@ -1433,45 +1506,39 @@ void FileHelper::CheckMandatoryElements() if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image { - - gdcmDebugMacro( "USER_OWN_IMAGE (1)"); - // If 'SOP Class UID' exists ('true DICOM' image) + // If 'SOP Class UID' and 'SOP Instance UID' exist ('true DICOM' image) // we create the 'Source Image Sequence' SeqEntry // to hold informations about the Source Image DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016); - if ( e_0008_0016 ) + DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018); + if ( e_0008_0016 && e_0008_0018) { // Create 'Source Image Sequence' SeqEntry -// SeqEntry *sis = SeqEntry::New ( -// Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x2112) ); SeqEntry *sis = SeqEntry::New (0x0008, 0x2112); SQItem *sqi = SQItem::New(1); - // (we assume 'SOP Instance UID' exists too) - // create 'Referenced SOP Class UID' -// DataEntry *e_0008_1150 = DataEntry::New( -// Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1150) ); + + // create 'Referenced SOP Class UID' from 'SOP Class UID' + DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI"); e_0008_1150->SetString( e_0008_0016->GetString()); sqi->AddEntry(e_0008_1150); e_0008_1150->Delete(); - // create 'Referenced SOP Instance UID' + // create 'Referenced SOP Instance UID' from 'SOP Instance UID' DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018); -// DataEntry *e_0008_1155 = DataEntry::New( -// Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1155) ); - DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI"); + + DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI"); e_0008_1155->SetString( e_0008_0018->GetString()); sqi->AddEntry(e_0008_1155); e_0008_1155->Delete(); - + sis->AddSQItem(sqi,1); sqi->Delete(); // temporarily replaces any previous 'Source Image Sequence' Archive->Push(sis); sis->Delete(); - // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'? if ( ContentType == FILTERED_IMAGE) // the user *knows* he just modified the pixels @@ -1499,10 +1566,10 @@ void FileHelper::CheckMandatoryElements() Archive->Push(0x0028,0x0017); Archive->Push(0x0028,0x0198); // very old versions Archive->Push(0x0028,0x0199); - + // Replace deprecated 0028 0012 US Planes // by new 0028 0008 IS Number of Frames - + ///\todo : find if there is a rule! DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012); if ( e_0028_0012 ) @@ -1523,7 +1590,8 @@ void FileHelper::CheckMandatoryElements() std::ostringstream s; // check 'Bits Allocated' vs decent values int nbBitsAllocated = FileInternal->GetBitsAllocated(); - if ( nbBitsAllocated == 0 || nbBitsAllocated > 32) + if ( (nbBitsAllocated == 0 || nbBitsAllocated > 32) + || ( nbBitsAllocated > 8 && nbBitsAllocated <16) ) { CopyMandatoryEntry(0x0028,0x0100,"16","US"); gdcmWarningMacro("(0028,0100) changed from " @@ -1556,29 +1624,48 @@ void FileHelper::CheckMandatoryElements() << " for consistency purpose"); } - std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030); - if ( pixelSpacing == GDCM_UNFOUND ) + // check Pixel Representation (default it as 0 -unsigned-) + + DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103); + if ( !e_0028_0103 ) { - pixelSpacing = "1.0\\1.0"; - // if missing, Pixel Spacing forced to "1.0\1.0" - CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS"); + gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory"); + CopyMandatoryEntry(0x0028, 0x0103,"0","US"); + } + else + { + int sign = (int)e_0028_0103->GetValue(0); + if (sign !=1 && sign !=0) + { + gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0"); + CopyMandatoryEntry(0x0028, 0x0103,"0","US"); + } } - - // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing' - // --> This one is the *legal* one ! - if ( ContentType != USER_OWN_IMAGE) - // we write it only when we are *sure* the image comes from - // an imager (see also 0008,0x0064) - CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS"); - - + std::string pixelAspectRatio = FileInternal->GetEntryString(0x0028,0x0034); + if ( pixelAspectRatio == GDCM_UNFOUND ) // avoid conflict with pixelSpacing ! + { + std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030); + if ( pixelSpacing == GDCM_UNFOUND ) + { + pixelSpacing = "1.0\\1.0"; + // if missing, Pixel Spacing forced to "1.0\1.0" + CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS"); + } + + // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing' + // --> This one is the *legal* one ! + if ( ContentType != USER_OWN_IMAGE) + // we write it only when we are *sure* the image comes from + // an imager (see also 0008,0x0064) + CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS"); + } /* ///Exact meaning of RETired fields // See page 73 of ACR-NEMA_300-1988.pdf ! -// 0020,0020 : Patient Orientation : +// 0020,0020 : Patient Orientation : Patient direction of the first row and column of the images. The first entry id the direction of the raws, given by the direction of the last pixel in the first row from the first pixel in tha row. @@ -1596,11 +1683,11 @@ equipment coordinate axes x,y,z, followed by direction cosines of the C axis of the image system with respect to the same axes //0020,0050 Location -An image location reference, standard for the modality (such as CT bed -position), used to indicate position. Calculation of position for other purposes +An image location reference, standard for the modality (such as CT bed position), +used to indicate position. Calculation of position for other purposes is only from (0020,0030) and (0020,0035) */ - + /* // if imagePositionPatient not found, default it with imagePositionRet, if any // if imageOrientationPatient not found, default it with imageOrientationRet, if any @@ -1609,43 +1696,43 @@ is only from (0020,0030) and (0020,0035) std::string imageOrientationRet = FileInternal->GetEntryString(0x0020,0x0035); std::string imagePositionPatient = FileInternal->GetEntryString(0x0020,0x0032); std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037); - + if( imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND && imagePositionRet != GDCM_UNFOUND && imageOrientationRet != GDCM_UNFOUND) { CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS"); Archive->Push(0x0020,0x0030); CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS"); - Archive->Push(0x0020,0x0035); - } + Archive->Push(0x0020,0x0035); + } */ - - // Samples Per Pixel (type 1) : default to grayscale + + // Samples Per Pixel (type 1) : default to grayscale CheckMandatoryEntry(0x0028,0x0002,"1","US"); // --- Check UID-related Entries --- // At the end, not to overwrite the original ones, - // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID' + // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID' // 'SOP Instance UID' CopyMandatoryEntry(0x0008,0x0018,sop,"UI"); if ( ContentType == USER_OWN_IMAGE) { - gdcmDebugMacro( "USER_OWN_IMAGE (2)"); + gdcmDebugMacro( "USER_OWN_IMAGE (2)"); // Conversion Type. // Other possible values are : // See PS 3.3, Page 408 - + // DV = Digitized Video - // DI = Digital Interface + // DI = Digital Interface // DF = Digitized Film // WSD = Workstation // SD = Scanned Document // SI = Scanned Image // DRW = Drawing // SYN = Synthetic Image - + CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not? } /* @@ -1655,7 +1742,7 @@ is only from (0020,0030) and (0020,0035) } */ - + // ---- The user will never have to take any action on the following ---- // new value for 'SOP Instance UID' @@ -1664,7 +1751,7 @@ is only from (0020,0030) and (0020,0035) // Instance Creation Date const std::string &date = Util::GetCurrentDate(); CopyMandatoryEntry(0x0008,0x0012,date,"DA"); - + // Instance Creation Time const std::string &time = Util::GetCurrentTime(); CopyMandatoryEntry(0x0008,0x0013,time,"TM"); @@ -1712,15 +1799,15 @@ is only from (0020,0030) and (0020,0035) // Instance Number CheckMandatoryEntry(0x0020,0x0013,"","IS"); - + // Patient Orientation // Can be computed from (0020|0037) : Image Orientation (Patient) - gdcm::Orientation *o = gdcm::Orientation::New(); + GDCM_NAME_SPACE::Orientation *o = GDCM_NAME_SPACE::Orientation::New(); std::string ori = o->GetOrientation ( FileInternal ); o->Delete(); if (ori != "\\" && ori != GDCM_UNFOUND) CheckMandatoryEntry(0x0020,0x0020,ori,"CS"); - else + else CheckMandatoryEntry(0x0020,0x0020,"","CS"); // Default Patient Position to HFS @@ -1753,7 +1840,7 @@ is only from (0020,0030) and (0020,0035) /* // Deal with element 0x0000 (group length) of each group. // First stage : get all the different Groups - + GroupHT grHT; DocEntry *d = FileInternal->GetFirstEntry(); while(d) @@ -1767,8 +1854,12 @@ is only from (0020,0030) and (0020,0035) CheckMandatoryEntry(it->first, 0x0000, "0"); } // Third stage : update all 'zero level' groups length -*/ +*/ + + if (PhotometricInterpretation == 1) + { + } } void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr ) @@ -1784,7 +1875,7 @@ void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string va } } -/// \todo : what is it used for ? (FileHelper::SetMandatoryEntry) +/// \todo : what is it used for ? (FileHelper::SetMandatoryEntry) void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr) { //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem)); @@ -1839,7 +1930,6 @@ void FileHelper::RestoreWriteMandatory() Archive->Restore(0x0020,0x000e); } - /** * \brief CallStartMethod */ @@ -1876,13 +1966,17 @@ void FileHelper::Initialize() { UserFunction = 0; ContentType = USER_OWN_IMAGE; - + WriteMode = WMODE_RAW; WriteType = ExplicitVR; + + PhotometricInterpretation = 2; // Black = 0 PixelReadConverter = new PixelReadConvert; PixelWriteConverter = new PixelWriteConvert; Archive = new DocEntryArchive( FileInternal ); + + KeepOverlays = false; } /** @@ -1914,9 +2008,95 @@ uint8_t *FileHelper::GetRaw() return raw; } +/** + * \brief Deal with Grey levels i.e. re-arange them + * to have low values = dark, high values = bright + */ +void FileHelper::ConvertFixGreyLevels(uint8_t *raw, size_t rawSize) +{ + uint32_t i; // to please M$VC6 + int16_t j; + + // Number of Bits Allocated for storing a Pixel is defaulted to 16 + // when absent from the file. + int bitsAllocated = FileInternal->GetBitsAllocated(); + if ( bitsAllocated == 0 ) + { + bitsAllocated = 16; + } + + else if (bitsAllocated > 8 && bitsAllocated < 16 && bitsAllocated != 12) + { + bitsAllocated = 16; + } + // Number of "Bits Stored", defaulted to number of "Bits Allocated" + // when absent from the file. + int bitsStored = FileInternal->GetBitsStored(); + if ( bitsStored == 0 ) + { + bitsStored = bitsAllocated; + } + + if (!FileInternal->IsSignedPixelData()) + { + if ( bitsAllocated == 8 ) + { + uint8_t *deb = (uint8_t *)raw; + for (i=0; i +void RescaleFunction(TBuffer* buffer, TSource *source, + double slope, double intercept, size_t size) +{ + size /= sizeof(TSource); + + if (slope != 1.0 && intercept != 0.0) + { + // Duff's device. Instead of this code: + // + // for(unsigned int i=0; i 0); + } + } + else if (slope == 1.0 && intercept != 0.0) + { + // Duff's device. Instead of this code: + // + // for(unsigned int i=0; i 0); + } + } + else if (slope != 1.0 && intercept == 0.0) + { + // Duff's device. Instead of this code: + // + // for(unsigned int i=0; i 0); + } + } + else + { + // Duff's device. Instead of this code: + // + // for(unsigned int i=0; i 0); + } + } +} + + +template +void RescaleFunction(ImageIOBase::IOComponentType bufferType, + void* buffer, TSource *source, + double slope, double intercept, size_t size) +{ + switch (bufferType) + { + case ImageIOBase::UCHAR: + RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size); + break; + case ImageIOBase::CHAR: + RescaleFunction( (char *)buffer, source, slope, intercept, size); + break; + case ImageIOBase::USHORT: + RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size); + break; + case ImageIOBase::SHORT: + RescaleFunction( (short *)buffer, source, slope, intercept, size); + break; + case ImageIOBase::UINT: + RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size); + break; + case ImageIOBase::INT: + RescaleFunction( (int *)buffer, source, slope, intercept, size); + break; + case ImageIOBase::FLOAT: + RescaleFunction( (float *)buffer, source, slope, intercept, size); + break; + case ImageIOBase::DOUBLE: + RescaleFunction( (double *)buffer, source, slope, intercept, size); + break; + default: + ::itk::OStringStream message; + message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType; + ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION); + throw e; + } +} +*/