Module: $RCSfile: gdcmFileHelper.cxx,v $
Language: C++
- Date: $Date: 2007/04/12 13:22:49 $
- Version: $Revision: 1.111 $
+ Date: $Date: 2007/07/26 08:36:49 $
+ Version: $Revision: 1.119 $
Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
l'Image). All rights reserved. See Doc/License.txt or
// 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, ...
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
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->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<uint16_t, int> GroupHT; // Hash Table
//-------------------------------------------------------------------------
* @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).
*/
* \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 Represenation of the DataElement to be inserted
+ * @param vr Value Representation of the DataElement to be inserted
* \return pointer to the modified/created DataEntry (NULL when creation
* failed).
*/
*/
void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
{
- SetUserData(inData, expectedSize);
+ PixelWriteConverter->SetUserData(inData, expectedSize);
}
/**
*/
void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
{
- PixelWriteConverter->SetUserData(inData, expectedSize);
+ if( WriteType == JPEG2000 )
+ {
+ PixelWriteConverter->SetCompressJPEG2000UserData(inData, expectedSize, FileInternal);
+ }
+ else if( WriteType == JPEG )
+ {
+ PixelWriteConverter->SetCompressJPEGUserData(inData, expectedSize, FileInternal);
+ }
+ else
+ {
+ PixelWriteConverter->SetUserData(inData, expectedSize);
+ }
}
/**
*/
bool FileHelper::Write(std::string const &fileName)
{
-
CheckMandatoryElements(); //called once, here !
bool flag = false;
// 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.
-
+ // Better we let DocEntry::WriteContent to put vr=UN for undocumented Shadow Groups !
+
+/*
e = FileInternal->GetFirstEntry();
while (e != 0)
{
if (e->GetVR() == " ")
{
+
SetWriteTypeToDcmImplVR();
SetWriteFileTypeToImplicitVR();
flag = true;
SetWriteFileTypeToExplicitVR();
}
break;
+*/
+
+ SetWriteFileTypeToExplicitVR();
- SetWriteFileTypeToExplicitVR(); // to see JPRx
break;
case ACR:
case ACR_LIBIDO:
SetWriteFileTypeToJPEG();
break;
- case JPEG2000:
+ case JPEG2000:
SetWriteFileTypeToJPEG2000();
break;
}
}
bool check = CheckWriteIntegrity(); // verifies length
- if (WriteType == JPEG || WriteType == JPEG2000) check = true;
+ if (WriteType == JPEG || WriteType == JPEG2000)
+ check = true;
+
if (check)
{
check = FileInternal->Write(fileName,WriteType);
}
- RestoreWrite();
+ RestoreWrite();
// RestoreWriteFileType();
// RestoreWriteMandatory();
-
// --------------------------------------------------------------
// Special Patch to allow gdcm to re-write ACR-LibIDO formated images
//-----------------------------------------------------------------------------
// 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()
break;
}
}
-
return true;
}
* \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 if modified :-(
*/
void FileHelper::SetWriteToRaw()
{
}
else
{
+ // 0x0028,0x0004 : Photometric Interpretation
DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
if (FileInternal->HasLUT() )
{
photInt->SetString("PALETTE COLOR ");
}
else
- {
- photInt->SetString("MONOCHROME2 ");
+ {
+ if (GetPhotometricInterpretation() == 2)
+ photInt->SetString("MONOCHROME2 "); // 0 = Black
+ else
+ photInt->SetString("MONOCHROME1 "); // 0 = White !
}
-
+
PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
PixelReadConverter->GetRawSize());
{
vr = "OW";
}
+
DataEntry *pixel =
CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
pixel->SetBinArea(PixelWriteConverter->GetData(),false);
pixel->SetLength(PixelWriteConverter->GetDataSize());
+
+ if (!FileInternal->HasLUT() && GetPhotometricInterpretation() == 1)
+ {
+ ConvertFixGreyLevels( pixel->GetBinArea(), pixel->GetLength() );
+ }
Archive->Push(photInt);
Archive->Push(pixel);
*/
void FileHelper::RestoreWrite()
{
-
Archive->Restore(0x0028,0x0002);
Archive->Restore(0x0028,0x0004);
Archive->Push(0x0002,0x0102);
}
- /**
- * \brief Sets in the File the TransferSyntax to 'JPEG2000'
- */
+/**
+ * \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);
/**
* \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);
tss->Delete();
}
-
-/**
- * \brief Restore in the File the initial group 0002
- */
-void FileHelper::RestoreWriteFileType()
-{
-}
-
/**
* \brief Set the Write not to Libido format
*/
if ( oldRow && oldCol )
{
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");
if ( oldE )
{
- //newE = DataEntry::New(oldE->GetDictEntry());
newE = DataEntry::New(group, elem, vr);
newE->Copy(oldE);
}
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.
gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
//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
-
+ Archive->Push(0x0028,0x0005); // Image Dimension
+
// Create them if not found
// Always modify the value
// Push the entries to the archive.
// an imager (see also 0008,0x0064)
CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
-
-
/*
///Exact meaning of RETired fields
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
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");
// See PS 3.3, Page 408
// DV = Digitized Video
- // DI = Digital Interface
+ // DI = Digital Interface
// DF = Digitized Film
// WSD = Workstation
// SD = Scanned Document
}
*/
-
+
// ---- The user will never have to take any action on the following ----
// new value for 'SOP Instance UID'
// 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");
// 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
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 )
Archive->Restore(0x0020,0x000e);
}
-
/**
* \brief CallStartMethod
*/
{
UserFunction = 0;
ContentType = USER_OWN_IMAGE;
-
+
WriteMode = WMODE_RAW;
WriteType = ExplicitVR;
+
+ PhotometricInterpretation = 2; // Black = 0
PixelReadConverter = new PixelReadConvert;
PixelWriteConverter = new PixelWriteConvert;
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<rawSize; i++)
+ {
+ *deb = 255 - *deb;
+ deb++;
+ }
+ return;
+ }
+
+ if ( bitsAllocated == 16 )
+ {
+ uint16_t mask =1;
+ for (j=0; j<bitsStored-1; j++)
+ {
+ mask = (mask << 1) +1; // will be fff when BitsStored=12
+ }
+
+ uint16_t *deb = (uint16_t *)raw;
+ for (i=0; i<rawSize/2; i++)
+ {
+ *deb = mask - *deb;
+ deb++;
+ }
+ return;
+ }
+ }
+ else
+ {
+ if ( bitsAllocated == 8 )
+ {
+ uint8_t smask8 = 255;
+ uint8_t *deb = (uint8_t *)raw;
+ for (i=0; i<rawSize; i++)
+ {
+ *deb = smask8 - *deb;
+ deb++;
+ }
+ return;
+ }
+ if ( bitsAllocated == 16 )
+ {
+ uint16_t smask16 = 65535;
+ uint16_t *deb = (uint16_t *)raw;
+ for (i=0; i<rawSize/2; i++)
+ {
+ *deb = smask16 - *deb;
+ deb++;
+ }
+ return;
+ }
+ }
+}
+
//-----------------------------------------------------------------------------
/**
- * \brief Prints the common part of DataEntry, SeqEntry
+ * \brief Prints the FileInternal + info on PixelReadConvertor
* @param os ostream we want to print in
* @param indent (unused)
*/
case 1: *buffer++ = (TBuffer)(*source++);
} while (--n > 0);
}
- }
-
-
+ }
}