/*--------------------------------------------------------------------------*/ /* dcmodules.h : description of the DcmModules class */ /*--------------------------------------------------------------------------*/ /* Author : Thomas DESODT */ /*--------------------------------------------------------------------------*/ /* */ /* Copyright (C) Park Medical Systems (UK) Ltd. */ /* */ /* This software is the sole property of Park Medical Systems (UK) Ltd. */ /* and may not be copied or reproduced in any way without prior written */ /* permission from Park Medical Systems (UK) Ltd. This software is */ /* intended for use in systems produced by Park Medical Systems (UK) Ltd. */ /* */ /* This software or any other copies thereof may not be provided or */ /* otherwise made available to any other person without written permission */ /* from Park Medical Systems (UK) Ltd. No title to and ownership of the */ /* software is hereby transferred. */ /* */ /* The information in this software is subject to change without notice */ /* and should not be construed as a commitment by Park Medical Systems */ /* (UK) Ltd. */ /* */ /*--------------------------------------------------------------------------*/ /* SCCS Information */ // #ifndef lint // static char sccsid[] = "@(#)dcmodules.cc 1.6 02/07/12 (C) Park Medical Systems (UK) Ltd."; // static char sccsrev[] = "1.6"; // static char sccsmod[] = "dcmodules.cc"; // static char sccsdate[] = "02/07/12"; // #endif #include "osconfig.h" /* OS specific configuration */ #include "dcversions.h" /* the current file format versions */ #include "dcmodules.h" /* .h of this file... */ #include "dcdatset.h" /* for class DcmDataSet */ #include "ofstring.h" /* for class OFString */ #include "dcuid.h" /* for UID numbers... */ #include "dcdebug.h" /* for debug level, etc... */ /* includes for all the element to be put in the dataset */ #include "dcvrpn.h" /* for class DcmPersonName */ #include "dcvrlo.h" /* for class DcmLongString */ #include "dcvrlt.h" /* for class DcmLongText */ #include "dcvrda.h" /* for class DcmDate */ #include "dcvrcs.h" /* for class DcmCodeString */ #include "dcvrsh.h" /* for class DcmShortString */ #include "dcvrds.h" /* for class DcmDecimalString */ #include "dcvrtm.h" /* for class DcmTime */ #include "dcvris.h" /* for class DcmIntegerString */ #include "dcvrus.h" /* for class DcmUnsignedShort */ #include "dcvrul.h" /* for class DcmUnsignedLong */ #include "dcpixel.h" /* for class DcmPixelData */ #include "dcstack.h" /* for class DcmStack */ #include "dcfilefo.h" /* for class DcmFileFormat */ #include "dcvrat.h" /* for class DcmAttributeTag */ #include "dcdeftag.h" /* for definitions of tags, like DCM_PixelData */ /* specific data includes */ #include "DB_global.h" /* to get the standard #define-ed values for study type, organ, etc... */ /* general purpose includes */ #include /* for function 'isdigit' */ /*------------------------------------------------------------------*/ /*** local functions */ /*------------------------------------------------------------------*/ E_Condition load_colour_map(DataStore *ds, short *block, Uint8 **A, int *overlayMapSize, long *imageMapSize); E_Condition create_palette(E_ColourFormat input_colourFormat, const Uint8* pixelsArray, Uint8** R, Uint8** G, Uint8** B, long* colourMapSize); E_Condition normalise_16bits_to_8bits(Uint8* pixelsArray, long size); char *buildStudyId( const char *date, const char *time, int studyType, int studyMode, int fileType, int organ ); /*------------------------------------------------------------------*/ /*** local macros */ /*------------------------------------------------------------------*/ #define DS_CRNT_SLICE -1 #define DI_ENTIRE_ITEM -1 #define DI_FROM_FIRST_ELEM 0 #define SHOW_WARNING 1 #define HIDE_WARNING 0 #define SC_NO_COLOUR_MAP -1 #define NB_COLOURS_MONO 255 #define DEFAULT_DATE "18720814" #define DEFAULT_TIME "010101" #define LOAD_DI(dataitem) \ di_loadi(dataitem, DS_CRNT_SLICE, DI_ENTIRE_ITEM, DI_FROM_FIRST_ELEM); //the following macro looks for the item 'item' in the current DataBlock, //and stores its content as a string into the str_buf variable. If it //cannot find it, then it sets it to the default, and optionally shows //a warning message. // //requirements: // - a variable DataBlock* db must be declared and loaded on the correct // block // - a variable DataItem* di must be declared // - a variable OFString must be declared // //arguments: // - item: string givin the name of the item in the block // - default: default value to return (NB : must be of correct type, no // checks are made) // - show_warning: boolean. =true => show the warning. // //results: // - di now points to the item (if found) // - str_buf contains the new value #define LOAD_STR_BUF(item,default,show_warning) \ di = db_findItem(db,item); \ if (di) \ { \ LOAD_DI(di); \ str_buf = (const char*)di_getBuf(di); \ } \ else \ { \ if (show_warning) \ { \ debug_cpp(1,"Warning: cannot find item "< create int*) // - item_target: name to give to the pointer // - default: default value to return (NB : must be of correct type, no // checks are made) // - show_warning: boolean. =true => show the warning. // //results: // - a variable item_type* item_target is created, still avialable after // the macro, and contains the value of the item (if found) // - di now points to the item (if found) #define LOAD_ITEM(item_str,item_type,item_target,default,show_warning) \ item_type* item_target; \ di = db_findItem(db,item_str); \ if (di) \ { \ LOAD_DI(di); \ item_target = (item_type*)di_getBuf(di); \ } \ else \ { \ if (show_warning) \ { \ debug_cpp(1,"Warning: cannot fing item " \ <insertfunction(value); \ dset->insert(element,OFTrue); \ } \ else \ { debug_cpp(4,"not enough memory to create element "<insertfunction(array,size); \ dset->insert(element,OFTrue); \ } \ else \ { debug_cpp(4,"not enough memory to create element "< Add_Curve_Module // - Frame Of Reference -> Add_FrameOfReference_Module // - General Equipment -> Add_GeneralEquipment_Module // - General Image -> Add_GeneralImage_Module // - General Series -> Add_GeneralSeries_Module // - General Study -> Add_GeneralStudy_Module // - Image Pixel -> Add_ImagePixel_Module // - Multi Frame -> Add_MultiFrame_Module // - Multi Frame Overlay -> Add_MultiFrameOverlay_Module // - NM Detector -> Add_NMDetector_Module // - NM Image -> Add_NMImage_Module // - NM Image Pixel -> Add_NMImagePixel_Module // - NM Isotope -> Add_NMIsotope_Module // - NM Multi Frame -> Add_NMMultiFrame_Module // - NM Multi Gated Acquisition -> Add_NMMultiGatedAcquisition_Module // - NM/PET Patient Orientation -> Add_NMPETPatientOrientation_Module // - NM Phase -> Add_NMPhase_Module // - NM Reconstruction -> Add_NMReconstruction_Module // - NM TOMO Acquisition -> Add_NMTOMOAcquisition_Module // - Overlay Plane -> Add_OverlayPlane_Module // - Patient -> Add_Patient_Module // - Patient Study -> Add_PatientStudy_Module // - SC Equipment -> Add_SCEquipment_Module // - SOP Common -> Add_SOPCommon_Module // - VOI LUT -> Add_VOILUT_Module /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_Curve_Module **------------------------------------------------------------------------ ** Summary : do nothing at the moment... ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** EC_Normal **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_Curve_Module (DcmDataset *dset, DataStore *ds) { // Curve module contains: // ??? //This is not a mandatory module, and so far it has no meaning for //us, so i leave it empty for the time being. debug_cpp(3,"Adding Curve Module"); debug_cpp(4,"not needed non-mandatory module..."); debug_cpp(4," => Curve module not created"); return EC_Normal; } /* End of "DcmModules::Add_Curve_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_FrameOfReference_Module **------------------------------------------------------------------------ ** Summary : do nothing at the moment... ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** EC_Normal **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_FrameOfReference_Module (DcmDataset *dset, DataStore *ds) { // NM Frame Of Reference module contains: // * - Frame of Reference UID - (0020,0052) - UI - 1 // - Position Reference Indicator - (0020,1040) - LO - 2 //This is not a mandatory module, and so far it has no meaning for //us, so i leave it empty for the time being. debug_cpp(3,"Adding NM Frame of Reference Module"); debug_cpp(4,"not needed non-mandatory module..."); debug_cpp(4," => NM Frame of Reference module not created"); return EC_Normal; } /* End of "DcmModules::Add_FrameOfReference_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_GeneralEquipment_Module **------------------------------------------------------------------------ ** Summary : adds the General Equipment Module to the given dataset, ** taking the data in the given datastore. The type of iod is used to ** write in the dataset the file format version, used to allow backward ** compatibility when the file format gets modified. ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** type I the type of iod we are writting (NM, SC...) ** ** Return Value : ** - EC_MemoryExhausted if an element to insert could not be created ** - EC_Normal otherwise **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_GeneralEquipment_Module(DcmDataset *dset, DataStore *ds, E_IODtype type) { //General Equipment Module contains: // * - Manufacturer - (0008,0070) - LO - 2 // - Institution Name - (0008,0080) - LO - 3 // - Institution Address - (0008,0081) - ST - 3 // - Station Name - (0008,1010) - SH - 3 // - Institutional Department Name - (0008,1040) - LO - 3 // - Manufacturer's Model Name - (0008,1090) - LO - 3 // - Device Serial Number - (0018,1000) - LO - 3 // * - Software Versions - (0018,1020) - LO - 3 // * - Spatial Resolution - (0018,1050) - DS - 3 // - Date of Last Calibration - (0018,1200) - DA - 3 // - Time of Last Calibration - (0018,1201) - TM - 3 // - Pixel Padding Value - (0028,0120) - xs - 3 debug_cpp(3,"Adding General Equipment Module"); DataBlock* db=NULL; DataItem* di=NULL; OFString str_buf=""; // Manufacturer - (0008,0070) - LO - 2 // debug_cpp(7,"dataset <- manufacturer ("<< PARK_MANUFACTURER_NAME <<")"); INSERT_INTO_DATASET("Manufacturer",DcmLongString, putString,PARK_MANUFACTURER_NAME); // Software Versions - (0018,1020) - LO - 3 // debug_cpp(7,"dataset <- software versions ("<< getCurrentFileFormat(type) <<")"); INSERT_INTO_DATASET("SoftwareVersions",DcmLongString, putString,getCurrentFileFormat(type)); //Spatial Resolution - (0018,1050) - DS - 3 db = ds_findBlock(ds,"Head"); if (db) { TEST_ITEM("resolution",float,resolution,0.0,HIDE_WARNING); if (*resolution!=0.0) { OFString resolutionStr = ""; resolutionStr<<*resolution; debug_cpp(7,"dataset <- spatial resolution ("<findOFString(DcmTag("StudyID"),studyID,0) != EC_Normal) { db = ds_findBlock(ds,"Head"); if (db) { // load Study Date { LOAD_STR_BUF("date",DEFAULT_DATE,SHOW_WARNING); if (format_date(str_buf) != EC_Normal) { show_warning_message("date","StudyDate"); debug_cpp(4,"defaulting StudyDate to '"< yyyy/mmdd date.insert(7,1,'/'); //yyyy/mmdd -> yyyy/mm/dd str_buf.erase(); } // load Study Time { LOAD_STR_BUF("time",DEFAULT_TIME,SHOW_WARNING); if (format_time(str_buf) != EC_Normal) { show_warning_message("time","StudyTime"); debug_cpp(4,"defaulting StudyTime to '"< hh/mmss time.insert(5,1,':'); //hh/mmss -> hh/mm/ss str_buf.erase(); } } else //could not find block "Head" { show_error_message_block("Head"); return_value = EC_ValueDefaulted; debug_cpp(8,"while building Series Instance UID:"); debug_cpp(9,"defaulting study date to 1901/01/01"); date = "1901/01/01"; debug_cpp(9,"defaulting study time to 01:01:01"); time = "01:01:01"; } db = ds_findBlock(ds,"Study"); if (db) { LOAD_ITEM("organ",short,organ,11,HIDE_WARNING); //11=OTHER LOAD_ITEM("studyMode",short,mode,1,HIDE_WARNING); //1=NONE LOAD_ITEM("studyType",short,type,1,HIDE_WARNING); //1=NONE studyID = buildStudyId(date.c_str(),time.c_str(),*type, *mode,0,*organ); } else //could not find block "Study" { show_error_message_block("Study"); return_value = EC_ValueDefaulted; debug_cpp(8,"while building Series Instance UID:"); debug_cpp(9,"defaulting study type to 1 (NONE)"); debug_cpp(9,"defaulting study mode to 1 (NONE)"); debug_cpp(9,"defaulting organ to 11 (Other)"); studyID = buildStudyId(date.c_str(),time.c_str(),1,1,0,11); } } seriesInstanceUID = dcmGetSeriesUIDPrefix(temp_str); seriesInstanceUID += "." + studyID + ".1"; debug_cpp(7,"dataset <- series instance UID ("<< seriesInstanceUID <<")"); INSERT_INTO_DATASET("SeriesInstanceUID",DcmUniqueIdentifier, putString,seriesInstanceUID.c_str()); } db = ds_findBlock(ds,"PatStudy"); if (db) { // Series Description : (0008,103E) - LO - 3 // /* { LOAD_STR_BUF("description","(unknown)",SHOW_WARNING); debug_cpp(7,"dataset <- series description"); INSERT_INTO_DATASET("SeriesDescription",DcmLongString, putString,str_buf.c_str()); str_buf.erase(); } */ // Protocol Name : (0018,1030) - LO - 3 // { //(protocol name is used to store datastore's 'Study Name' entry) LOAD_STR_BUF("studyName","(unknown)",SHOW_WARNING); debug_cpp(7,"dataset <- Protocol Name ("<< str_buf <<")"); INSERT_INTO_DATASET("ProtocolName",DcmLongString, putString,str_buf.c_str()); str_buf.erase(); } // Performing Physicians' Name : (0008,1050) - PN - 3 // { OFString phdName=""; LOAD_STR_BUF("phdName2","",HIDE_WARNING); //surname phdName = str_buf + "^"; LOAD_STR_BUF("phdName1","",HIDE_WARNING); //forename phdName += str_buf + "^^^"; //could be omitted, but clearer to keep it if (phdName != "^^^^") //i.e the entry exists in the datastore { debug_cpp(7,"dataset <- performing phys.'s name ("<< phdName <<")"); INSERT_INTO_DATASET("PerformingPhysiciansName",DcmPersonName, putString,phdName.c_str()); } str_buf.erase(); } } return return_value; } /* End of "DcmModules::Add_GeneralSeries_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_GeneralStudy_Module **------------------------------------------------------------------------ ** Summary : adds the General Study Module to the given dataset, ** taking the data in the given datastore. ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** acc_num I the accession number ** ** Return Value : ** - EC_MemoryExhausted if an element to insert could not be created ** - EC_ValueDefaulted if a value could not be read where we SHOULD have ** found a value, and we had to default it ** - EC_Normal otherwise **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_GeneralStudy_Module(DcmDataset *dset, DataStore *ds, OFString acc_num) { //General Study Contains: // * - Study Instance UID - (0020,000D) - UI - 1 // * - Study Date - (0008,0020) - DA - 2 // * - Study Time - (0008,0030) - TM - 2 // * - Referring Physician's Name - (0008,0090) - PN - 2 // * - Accession Number - (0008,0050) - SH - 2 // * - Study ID - (0020,0010) - SH - 2 // * - Study Description - (0008,1030) - LO - 3 // - Procedure Code Sequence - (0008,1032) - SQ - 3 // * - Physician(s) of Record - (0008,1048) - PN - 3 // - Name of Physician(s) Reading Study - (0008,1060) - PN - 3 // - Referenced Study Sequence - (0008,1110) - SQ - 3 // - Referenced SOP Class UID - (0008,1150) - UI - 1C (Referenced Study Sequence) // - Referenced SOP Instance UID - (0008,1155) - UI - 1C (Referenced Study Sequence) debug_cpp(3,"Adding General Study Module"); OFString str_buf="", date="", time="", studyid_str_buf=""; DataBlock* db=NULL; DataItem* di=NULL; E_Condition return_value = EC_Normal; db = ds_findBlock(ds,"PatStudy"); if (db) { // Study Description : (0008,1030) - LO - 3 // { LOAD_STR_BUF("description","(unknown)",SHOW_WARNING); debug_cpp(7,"dataset <- study description ("<< str_buf <<")"); INSERT_INTO_DATASET("StudyDescription",DcmLongString, putString,str_buf.c_str()); str_buf.erase(); } // Referring physician's name : (0008,0090) - PN - 2 // { OFString phdName=""; LOAD_STR_BUF("phdRefName2","",HIDE_WARNING); //surname phdName = str_buf + "^"; LOAD_STR_BUF("phdRefName1","",HIDE_WARNING); //forename phdName += str_buf + "^^^"; //could be omitted, but clearer to keep it debug_cpp(7,"dataset <- referring phys. name ("<< phdName <<")"); INSERT_INTO_DATASET("ReferringPhysiciansName",DcmPersonName, putString,phdName.c_str()); str_buf.erase(); } // Physician(s) of Record : (0008,1048) - PN - 3 // // Note : Physician(s) of Record seems to be the closest concept to // the 'phdName' entries in a datastore... { OFString phdName=""; LOAD_STR_BUF("phdName2","",HIDE_WARNING); //surname phdName = str_buf + "^"; LOAD_STR_BUF("phdName1","",HIDE_WARNING); //forename phdName += str_buf + "^^^"; //could be omitted, but clearer to keep it if (phdName != "^^^^") //i.e the entry exists in the datastore { debug_cpp(7,"dataset <- phys. of record ("<< phdName <<")"); INSERT_INTO_DATASET("PhysiciansOfRecord",DcmPersonName, putString,phdName.c_str()); } str_buf.erase(); } } else //could not find block PatStudy { show_error_message_block("PatStudy"); return_value = EC_ValueDefaulted; debug_cpp(7,"dataset <- referring phys. name (no value)"); INSERT_INTO_DATASET("ReferringPhysiciansName",DcmPersonName, putString,"^^^^"); } db = ds_findBlock(ds,"Head"); if (db) { // Study Date : (0008,0020) - DA - 2 // { LOAD_STR_BUF("date",DEFAULT_DATE,SHOW_WARNING); if (format_date(str_buf) != EC_Normal) { show_warning_message("date","StudyDate"); debug_cpp(4,"defaulting StudyDate to '"< yyyy/mmdd date.insert(7,1,'/'); //yyyy/mmdd -> yyyy/mm/dd debug_cpp(7,"dataset <- study date ("<< str_buf <<")"); INSERT_INTO_DATASET("StudyDate",DcmDate, putString,str_buf.c_str()); str_buf.erase(); } // Study Time : (0008,0030) - TM - 2 // { LOAD_STR_BUF("time",DEFAULT_TIME,SHOW_WARNING); if (format_time(str_buf) != EC_Normal) { show_warning_message("time","StudyTime"); debug_cpp(4,"defaulting StudyTime to '"< hh/mmss time.insert(5,1,':'); //hh/mmss -> hh/mm/ss if (str_buf != "") str_buf += ".000000"; debug_cpp(7,"dataset <- study time ("<< str_buf <<")"); INSERT_INTO_DATASET("StudyTime",DcmTime, putString,str_buf.c_str()); str_buf.erase(); } } else //could not find block "Head" { show_error_message_block("Head"); return_value = EC_ValueDefaulted; debug_cpp(7,"dataset <- study date (defaulted to "< 1) // - Pixel Aspect Ratio - (0028,0034) - IS - 1C (aspect ratio of image != 1/1 and Image Plane Module not applicable to this image) // * - Red Palette Color Lookup Table Descriptor - (0028,1101) - xs - 1C (Photometric Interpretation == COLOR PALETTE or ARGB) // * - Green Palette Color Lookup Table Descriptor - (0028,1102) - xs - 1C (Photometric Interpretation == COLOR PALETTE or ARGB) // * - Blue Palette Color Lookup Table Descriptor - (0028,1103) - xs - 1C (Photometric Interpretation == COLOR PALETTE or ARGB) // * - Red Palette Color Lookup Table Data - (0028,1201) - OW - 1C (Photometric Interpretation == COLOR PALETTE or ARGB) // * - Green Palette Color Lookup Table Data - (0028,1202) - OW - 1C (Photometric Interpretation == COLOR PALETTE or ARGB) // * - Blue Palette Color Lookup Table Data - (0028,1203) - OW - 1C (Photometric Interpretation == COLOR PALETTE or ARGB) int overlayMapSize=0; long imageMapSize=0,size=0; Uint8 *A=NULL, *R=NULL, *G=NULL, *B=NULL; const Uint8* pixelsArray=NULL; Uint16 r=0,c=0; OFString str_buf=""; DataItem *di=NULL; DataBlock *db=NULL; E_ColourFormat input_colourFormat = ECF_Monochrome2; debug_cpp(3,"Adding Image Pixel Module"); db = ds_findBlock(ds,"Data"); if (!db) return show_error_message_block("Data"); di = db_findItem(db,"image"); // Rows and Columns : (0028,0010) & (0028,0011) - US - 1 // if (di) { long *dims; di_getSize(di,&dims); c = (Uint16)dims[0]; //r & c declared at start of function r = (Uint16)dims[1]; size = (long)r * (long)c; //total number of pixels... //NOTE : the colours are never stored in RGB inside MX+, so there is //no worries about multiplying the size by 3... } else return show_error_message_item("Data.image"); debug_cpp(7,"dataset <- rows ("<< r <<")"); INSERT_INTO_DATASET("Rows", DcmUnsignedShort,putUint16,r); debug_cpp(7,"dataset <- columns ("<< c <<")"); INSERT_INTO_DATASET("Columns",DcmUnsignedShort,putUint16,c); switch (imgType) { case EIOD_NM: { //for each isotope in each datablock, simply read the data //and write it into the dataset. //We do not consider the colour map even if there is one, as //NM images are always gray levels. //NOTE : at that stage, db points to the block 'Data', and di //to the item 'image' int num_dims; long* dims; Uint16 numDataBlocks, numIsotopes, numFrames; long totalSize; num_dims = db_getSize(db,&dims); numDataBlocks = (Uint16)dims[0]; num_dims = di_getSize(di,&dims); numFrames = (Uint16)dims[2]; numIsotopes = (Uint16)dims[3]; totalSize = (long)size*numDataBlocks*numIsotopes*numFrames; //prepare a big structure to receive all the data !!! Uint16* bigPixelsArray= new Uint16[totalSize]; memset(bigPixelsArray,0,totalSize); long suffix=0; for (int block=0;blocksetVR(EVR_OW); pixelData->putUint16Array(bigPixelsArray,totalSize); dset->insert(pixelData); } break; case EIOD_SC: //this is much more complex !!! { //-------------------------------------// // WRITE BASIC INFO, RELATED TO SC // //-------------------------------------// // bits allocated - (0028,0100) - US - 1 // debug_cpp(7,"dataset <- bits allocated (=8)"); INSERT_INTO_DATASET("BitsAllocated",DcmUnsignedShort,putUint16,8); // bits stored - (0028,0101) - US - 1 // debug_cpp(7,"dataset <- bits stored (=8)"); INSERT_INTO_DATASET("BitsStored",DcmUnsignedShort,putUint16,8); // high bit - (0028,0102) - US - 1 // debug_cpp(7,"dataset <- high bit (=7)"); INSERT_INTO_DATASET("HighBit",DcmUnsignedShort,putUint16,7); // Pixel representation - (0028,0103) - US - 1 // debug_cpp(7,"dataset <- pixel representation (=0)"); INSERT_INTO_DATASET("PixelRepresentation",DcmUnsignedShort,putUint16,0); //-------------------------------------// // READ THE COLOUR PALETTE (if needed) // //-------------------------------------// //di_loadi(di, 0, size, 0,0,0,0); TEST_ITEM("colourMapNo",short,input_colourBlock,SC_NO_COLOUR_MAP,HIDE_WARNING); //input_colourBlock will then contain the block number of the colour map... if (*input_colourBlock == SC_NO_COLOUR_MAP) { input_colourFormat = ECF_Monochrome2; debug_cpp(4,"input colour format : Monochrome2"); str_buf = "MONOCHROME2"; } else { input_colourFormat = ECF_ColourPalette; debug_cpp(4,"input colour format : Colour Palette"); str_buf = "PALETTE COLOR"; } if (input_colourFormat == ECF_ColourPalette) { E_Condition result=load_colour_map(ds,input_colourBlock,&A,&overlayMapSize,&imageMapSize); if (result==EC_MissingDatastoreBlock || result==EC_MissingDatastoreItem) return result; } R = A; G = A+1; B = A+2; //---------------------------// // READ PIXEL VALUES // //---------------------------// db = ds_findBlock(ds,"Data"); if (db) { di = db_findItem(db,"image"); if (di) { di_loadi(di, 0, size, 0,0,0,0); pixelsArray = (Uint8*)di_getBuf(di); if ( input_colourFormat == ECF_Monochrome1 || input_colourFormat == ECF_Monochrome2) normalise_16bits_to_8bits((Uint8*)pixelsArray,size); //NB : this because MX+ can use 16 bits values for monochrome //images... But i have tried everything to write 16 bits pixels //in DICOM files, and it never worked, so i scale it down to 8 //bits for the time being... This might be changed later. //NOTE : of course, for the colour palette colours, it is the //opposite: to try to write them in 8 bits doesn't work ! } else return show_error_message_item("Data.image"); } else return show_error_message_block("Data"); //------------------------------------------------------// // WRITE THE COLOUR PALETTE FOR THE DATASET (if needed) // //------------------------------------------------------// if ( output_colourFormat == ECF_ColourPalette || ( output_colourFormat == ECF_Default && input_colourFormat == ECF_ColourPalette)) { //------------------------------------------------// // --- create palette if not existing already --- // if (input_colourFormat != ECF_ColourPalette) { E_Condition result; if ((result=create_palette(input_colourFormat,pixelsArray,&R,&G,&B,&imageMapSize))!=EC_Normal) { debug_cpp(4,"error in create_palette. Error returned: '"<< dcmErrorConditionToString(result)<<"'"); debug_cpp(0,"warning: impossible to generate colour palette.\n" "Switching output colour format to 'No Change'."); output_colourFormat = ECF_Default; } else A = R; //just for the coherence of the code... } //-----------------------------------// // --- write palette descriptors --- // Uint16 descValues[3]; // -- create values for the palette descriptors -- // descValues[0] = (Uint16)imageMapSize; //obvious... descValues[1] = (Uint16)0; //in MicasXplus, the first entry in the //colour map is always the value 0... descValues[2] = (Uint16)16;//colours in 16 bits, for some reason it //doesn't work with 8 bits.... // -- red descriptors -- 0x0028,0x1101// debug_cpp(7,"dataset <- red palette color descriptor"); INSERT_ARRAY_INTO_DATASET("RedPaletteColorLookupTableDescriptor", DcmUnsignedShort,putUint16Array, &descValues[0],3); // -- green descriptors -- 0x0028,0x1102// debug_cpp(7,"dataset <- green palette color descriptor"); INSERT_ARRAY_INTO_DATASET("GreenPaletteColorLookupTableDescriptor", DcmUnsignedShort,putUint16Array, &descValues[0],3); // -- blue descriptors -- 0x0028,0x1103// debug_cpp(7,"dataset <- blue palette color descriptor"); INSERT_ARRAY_INTO_DATASET("BluePaletteColorLookupTableDescriptor", DcmUnsignedShort,putUint16Array, &descValues[0],3); //------------------------------// // --- write palette values --- // { // -- building local arrays of values -- // Uint16* r = (Uint16*)malloc(imageMapSize*sizeof(Uint16)); Uint16* g = (Uint16*)malloc(imageMapSize*sizeof(Uint16)); Uint16* b = (Uint16*)malloc(imageMapSize*sizeof(Uint16)); for(int i=0;ifindOFString(DcmTag("ImageComments"),comment,0); if (comment!="") debug_cpp(8,"found existing image comment: '"<"; comment += temp_comment; //NOTE : we write the overlay for colour palette AND monochrome, since // the latter can use them as well (on re-import the colour // palette is re-created as gray-level, and it is usefull to be // able to change the colour palette in ManProcess without // changing the overlays ! debug_cpp(7,"dataset <- image comments ("<< comment <<")"); INSERT_INTO_DATASET("ImageComments",DcmLongText, putString,comment.c_str()); } //-----------------------------------------------// // WRITE PIXEL VALUES (convert if needed) // //-----------------------------------------------// DcmTag *tag = new DcmTag("PixelData");//(7FE0,0010) DcmPixelData *pixelData = new DcmPixelData(*tag); if (output_colourFormat != ECF_RGB || (output_colourFormat == input_colourFormat)) { if (input_colourFormat != ECF_RGB || output_colourFormat == ECF_Default) { //then we write the data without changing it, because the only //thing to change when RGB is not involved is the colour palette //(which has to be created or deleted) and this is dealt with in //the previous step ! debug_cpp(7,"dataset <- pixel data (8bits)"); pixelData->putUint8Array(pixelsArray,size); pixelData->setVR(EVR_OB); dset->insert(pixelData); //Note : cannot use macro INSERT_ARRAY_INTO_DATASET because we have //to set the VR to OB manually (the default value would be OW //otherwise). } else { //not implemented yet, and can't happen anyway since micasXplus //does not use RGB... but i leave this test here in case we //update it to RGB later on ! } } else //output == RGB { Uint8 *RGBpixelsArray = (Uint8*)malloc((size*3)*sizeof(Uint8)); //we reach here only if input data is not RGB, so we have to convert //the pixel data... switch(input_colourFormat) { case ECF_Monochrome2: for (int i=0;i3*8bits)"); pixelData->putUint8Array(RGBpixelsArray,size*3); pixelData->setVR(EVR_OB); dset->insert(pixelData); //Note : cannot use macro INSERT_ARRAY_INTO_DATASET because we have //to set the VR to OB manually (the default value would be OW //otherwise). } if(A) delete[] A; //we can (and have to) delete it, since the data put in //the dataset was a copy of A, and not A itself ! delete tag; } break; //end of EIOD_SC case... default: break; }//switch (imgType)... E_ColourFormat real_colourFormat = (output_colourFormat == ECF_Default) ? input_colourFormat : output_colourFormat; // Photometric interpretation : (0028,0004) - CS - 1 // debug_cpp(7,"dataset <- photometric interpretation ("<< dcmColourFormatToString(real_colourFormat) <<")"); INSERT_INTO_DATASET("PhotometricInterpretation",DcmCodeString, putString,dcmColourFormatToString(real_colourFormat)); // Samples per pixel : (0028,0002) - US - 1 // if (real_colourFormat == ECF_RGB) { debug_cpp(7,"dataset <- samples per pixel (=3)"); INSERT_INTO_DATASET("SamplesPerPixel",DcmUnsignedShort,putString,"3"); } else { debug_cpp(7,"dataset <- samples per pixel (=1)"); INSERT_INTO_DATASET("SamplesPerPixel",DcmUnsignedShort,putString,"1"); } //planar configuration : (0028,0006) - US - 1C // if (real_colourFormat == ECF_RGB) { debug_cpp(7,"dataset <- planar configuration (=0)"); INSERT_INTO_DATASET("PlanarConfiguration",DcmUnsignedShort, putString,"0"); //data written R1G1B1R2G2B2... } return EC_Normal; } /* End of "DcmModules::Add_ImagePixel_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_MultiFrame_Module **------------------------------------------------------------------------ ** Summary : adds the Multiframe Module to the given dataset, ** taking the data in the given datastore. ** ** (see note below) ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** EC_Normal (always) ** ** NOTE : ** This module is not involved in the creation of a Secondary Capture, ** and its data is always written in the NM Multiframe module, so it ** is not needed either when building NM images. ** So for the moment we leave it empty, and we will fill it whenever ** and if we need to build other types of DICOM images where it is not ** superceeded by another module. **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_MultiFrame_Module (DcmDataset *dset, DataStore *ds) { // Multi Frame module contains: // - Number of Frames - (0028,0008) - IS - 1 // - Frame Increment Pointer - (0028,0009) - AT - 1 debug_cpp(3,"Adding Multi Frame Module"); // Number of Frames : (0028,0008) - IS - 1 // // debug_cpp(7,"dataset <- number of frames"); // INSERT_INTO_DATASET("SamplesPerPixel",DcmUnsignedShort,putString,"1"); // Frame Increment Pointer : (0028,0009) - AT - 1 // // debug_cpp(7,"dataset <- frame increment pointer"); // INSERT_INTO_DATASET("PhotometricInterpretation",DcmAttributeTag, // putString,"(0000,0000)"); return EC_Normal; } /* End of "DcmModules::Add_MultiFrame_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_MultiFrameOverlay_Module **------------------------------------------------------------------------ ** Summary : do nothing at the moment... ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** EC_Normal **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_MultiFrameOverlay_Module (DcmDataset *dset, DataStore *ds) { // MultiFrameOverlay module contains: // ??? //This is not a mandatory module, and so far it has no meaning for //us, so i leave it empty for the time being. debug_cpp(3,"Adding MultiFrameOverlay Module"); debug_cpp(4,"not needed non-mandatory module..."); debug_cpp(4," => MultiFrameOverlay module not created"); return EC_Normal; } /* End of "DcmModules::Add_MultiFrameOverlay_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_NMDetector_Module **------------------------------------------------------------------------ ** IMPORTANT : call this function AFTER Add_NMMultiFrame_Module. ** This function uses the data written in the dataset by the ** function Add_NMMultiFrame_Module, hence it has to be called ** AFTER Add_NMMultiFrame_Module. ** ** Summary : adds the NM Detector Module to the given dataset, ** taking the data in the given datastore. ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset I/O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** - EC_MemoryExhausted if an element to insert could not be created ** - EC_IllegalCall if the module NM MultiFrame was not created before ** - EC_MissingDataStoreBlock if block "Collimator" could not be found ** - EC_MissingDataStoreItem if items "id" or "name" could not be found ** - EC_Normal otherwise **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_NMDetector_Module (DcmDataset *dset, DataStore *ds) { // NM Detector module contains: // * - Detector Information Sequence - (0054,0022) - SQ - 2 // * - >Collimator/Grid Name - (0018,1180) - SH - 3 // * - >Collimator Type - (0018,1181) - CS - 2C (sequence is present) // - >Field of View Shape - (0018,1147) - CS - 3 // - >Field of View Dimension(s) - (0018,1149) - IS - 3 // * - >Focal Distance - (0018,1182) - IS - 2C (sequence is present) // - >X Focus Center - (0018,1183) - DS - 3 // - >Y Focus Center - (0018,1184) - DS - 3 // - >Zoom Center - (0028,0032) - DS - 3 // * - >Zoom Factor - (0028,0031) - DS - 3 // - >Center of Rotation Offset - (0018,1145) - DS - 3 // - >Gantry/Detector Tilt - (0018,1120) - DS - 3 // * - >Distance Source to Detector - (0018,1110) - DS - 2C (sequence is present) // - >Start Angle - (0054,0200) - DS - 3 // - > Radial Position - (0018,1142) - DS - 3 // * - >Image Orientation (Patient) - (0020,0037) - DS - 2C (sequence is present) // * - >Image Position (Patient) - (0020,0032) - DS - 2C (sequence is present) // - >View Code Sequence - (0054,0220) - SQ - 3 // - >>(see below) // - >>View Angulation Modifier Code Sequence - (0054,0222) - SQ - 2C (sequence is present) // - >>>(see below) // NOTE : // ! - Each code sequence should include // ! - >>Code Value - (0008,0100) - SH - 1C - See Section 3-8.1 // ! - >>Coding Scheme Designator - (0008,0102) - SH - 1C - See Section 3-8.2 // ! - >>Coding Scheme Version - (0008,0103) - SH - 1C - See Section 3-8.2 - required if Coding Scheme Designator (0008,0102) is not sufficient to identify the Code Value (0008,0100) unambiguously. // ! - >>Code Meaning - (0008,0104) - LO - 1C - See Section 3-8.3 DataItem *di=NULL; DataBlock *db=NULL; E_Condition return_value = EC_Normal; long num_detectors=0; OFString zoomFactor=""; short *zoom=NULL; debug_cpp(3,"Adding NM Detector Module"); // first, find zoom factor is there is one // note : in DICOM, zoom is "row_factor\col_factor" // in MX+, zoom[0]=x_factor=col_factor... if ( ((db = ds_findBlock(ds,"Data"))!=NULL) && ((di = db_findItem(db,"zoom"))!=NULL) ) { di_loadi(di, 0, 2, 0); zoom = (short*)di_getBuf(di); zoomFactor << zoom[1] << "\\" << zoom[0]; if (zoom[0]==0 || zoom[1]==0) debug_cpp(8,"WARNING: found a zoom factor of 0... This might not be correct"); } else zoomFactor << 1.0 << "\\" << 1.0; // Detector Information Sequence - (0054,0022) - SQ - 2 DcmSequenceOfItems *detector_info_seq = new DcmSequenceOfItems(DcmTag("DetectorInformationSequence")); if (detector_info_seq) { //check if the value (0054,0021) exists, to find out the //number of detectors. if(dset->findIntegerNumber(DcmTag("NumberOfDetectors"), num_detectors,0) == EC_Normal) { debug_cpp(7,"dataset <- detector info sequence"); if (!(db = ds_findBlock(ds,"Collimator"))) { debug_cpp(4,"block Collimator... Defaulting all values to '0'..."); return_value = EC_ValueDefaulted; for(int i=0; iputString("NONE"); debug_cpp(9,"dataset <- collimator name (NONE)"); new_detector->insert(coll_name); // Collimator Type - (0018,1181) - CS - 2C DcmCodeString *coll_type = new DcmCodeString(DcmTag("CollimatorType")); coll_type->putString("NONE"); debug_cpp(9,"dataset <- collimator type (NONE)"); new_detector->insert(coll_type); // Focal Distance - (0018,1182) - IS - 2C DcmIntegerString *fd = new DcmIntegerString(DcmTag("FocalDistance")); debug_cpp(9,"dataset <- focal distance (=empty)"); new_detector->insert(fd); // Zoom Factor - (0028,0031) - DS - 3 DcmDecimalString *zfact = new DcmDecimalString(DcmTag("ZoomFactor")); debug_cpp(9,"dataset <- zoom factor (=0\\0)"); new_detector->insert(zfact); // Distance Source to Detector - (0018,1110) - DS - 2C DcmDecimalString *dstd = new DcmDecimalString(DcmTag("DistanceSourceToDetector")); debug_cpp(9,"dataset <- distance source->detector (=empty)"); new_detector->insert(dstd); // Image Orientation (Patient) - (0020,0037) - DS - 2C DcmDecimalString *io = new DcmDecimalString(DcmTag("ImageOrientationPatient")); debug_cpp(9,"dataset <- image orientation (=empty)"); new_detector->insert(io); // Image Position (Patient) - (0020,0032) - DS - 2C DcmDecimalString *ip = new DcmDecimalString(DcmTag("ImagePositionPatient")); debug_cpp(9,"dataset <- image position (=empty)"); new_detector->insert(ip); detector_info_seq->insert(new_detector); }//loop over all detectors dset->insert(detector_info_seq,OFTrue); }//block 'Collimator' could not be found else //block collimator could be found { for(int i=0; iputString("NONE"); coll_type->putString("NONE"); debug1 = debug2 = "NONE"; break; default: coll_name->putString(name.c_str()); debug1 = name; coll_type->putString("UNKN"); debug2 = "UNKN"; break; } debug_cpp(9,"dataset <- collimator name ("<< debug1 <<")"); new_detector->insert(coll_name); debug_cpp(9,"dataset <- collimator type ("<< debug2 <<")"); new_detector->insert(coll_type); // Focal Distance - (0018,1182) - IS - 2C DcmIntegerString *fd = new DcmIntegerString(DcmTag("FocalDistance")); debug_cpp(9,"dataset <- focal distance (=empty)"); new_detector->insert(fd); //Zoom Factor - (0028,0031) - DS - 3 DcmDecimalString *zfact = new DcmDecimalString(DcmTag("ZoomFactor")); zfact->putString(zoomFactor.c_str()); debug_cpp(9,"dataset <- zoom factor ("<insert(zfact); // Distance Source to Detector - (0018,1110) - DS - 2C DcmDecimalString *dstd = new DcmDecimalString(DcmTag("DistanceSourceToDetector")); debug_cpp(9,"dataset <- distance source->detector (=empty)"); new_detector->insert(dstd); // Image Orientation (Patient) - (0020,0037) - DS - 2C DcmDecimalString *io = new DcmDecimalString(DcmTag("ImageOrientationPatient")); debug_cpp(9,"dataset <- image orientation (=empty)"); new_detector->insert(io); // Image Position (Patient) - (0020,0032) - DS - 2C DcmDecimalString *ip = new DcmDecimalString(DcmTag("ImagePositionPatient")); debug_cpp(9,"dataset <- image position (=empty)"); new_detector->insert(ip); detector_info_seq->insert(new_detector); }//loop over all detectors dset->insert(detector_info_seq,OFTrue); }//block 'collimator' could be found }//check if tag (0054,0021) exists else { debug_cpp(4,"Warning : tag (0054,0021) could not be found."); debug_cpp(4," Detector Info Sequence not created."); debug_cpp(5,"Possible reason: NM Detector module build before NM MultiFrame"); return_value = EC_IllegalCall; } } // check if memory not exhausted else { debug_cpp(4,"Error: not enough memory to create element DetectorInformationSequence"); delete dset; return EC_MemoryExhausted; } return return_value; } /* End of "DcmModules::Add_NMDetector_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_NMImage_Module **------------------------------------------------------------------------ ** Summary : adds the NM Image Module to the given dataset, ** taking the data in the given datastore. ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** - EC_MemoryExhausted if an element to insert could not be created ** - EC_MissingDatastoreBlock if the "Study" or "data" blocks cannot ** be found ** - EC_IllegalCall if the image is not a standard NM image (but ** is a screen dump instead, for instance) ** - EC_Normal otherwise ** ** IMPORTANT : ** The NM Image Module contains data which decides of the existence ** of some modules, as well as determines the content of some data ** in some other modules... Hence this function has to be called ** before the following: ** - Add_NMTOMOAcquisition_Module ** - ... **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_NMImage_Module (DcmDataset *dset, DataStore *ds) { // NM Image module contains: // * - Image Type - (0008,0008) - CS - 1 // * - Actual Frame Duration - (0018,1242) - IS - 1C (Image type (0008,0008) Value3 = WholeBody or Static) // - Lossy Image Compression - (0028,2110) - CS - 1C (lossy compression done on image) // * - Counts Accumulated - (0018,0070) - IS - 2 // * - Scan Velocity - (0018,1300) - DS - 2C (Image type (0008,0008) value3 = WholeBody) // * - Scan Length - (0018,1302) - IS - 2C (Image type (0008,0008) value3 = WholeBody) // - Anatomic Region Sequence - (0008,2218) - SQ - 3 // - >Anatomic Region Modifier Sequence - (0008,2220) - SQ - 3 // - Primary Anatomic Structure Sequence - (0008,2228) - SQ - 3 // - >Primary Anatomic Structure Modifier Sequence - (0008,2230) - SQ - 3 //(*)- Acquisition Termination Condition - (0018,0071) - CS - 3 // - Trigger Source or Type - (0018,1061) - LO - 3 // - Table Height - (0018,1130) - DS - 3 // - Table Traverse - (0018,1131) - DS - 3 // - Count Rate - (0018,1243) - IS - 3 // - Whole Body Technique - (0018,1301) - CS - 3 // - Processing Function - (0018,5020) - LO - 3 // - Corrected Image - (0028,0051) - CS - 3 // - Image ID - (0054,0400) - SH - 3 // - Referenced Overlay Sequence - (0008,1130) - SQ - 3 // - >Referenced SOP Class UID - (0008,1150) - UI - 1C (Referenced Overlay Sequence) // - >Referenced SOP Instance UID - (0008,1155) - UI - 1C (Referenced Overlay Sequence) // - Referenced Curve Sequence - (0008,1145) - SQ - 3 // - >Referenced SOP Class UID - (0008,1150) - UI - 1C (Referenced Curve Sequence) // - >Referenced SOP Instance UID - (0008,1155) - UI - 1C (Referenced Curve Sequence DataItem *di=NULL; DataBlock *db=NULL; OFString imageType=""; E_Condition return_value=EC_Normal; debug_cpp(3,"Adding NM Image Module"); //first, find what type of image we have db = ds_findBlock(ds,"Study"); if (!db) return show_error_message_block("Study"); //LOAD_ITEM("organ", short,organ,UI_NONE,HIDE_WARNING); //LOAD_ITEM("studyMode",short,mode, UI_NONE,HIDE_WARNING); LOAD_ITEM("studyType",short,type, UI_NONE,HIDE_WARNING); // Image Type : (0008,0008) - CS - 1 // // Image Type contains 4 values, separated by back-slashes. // For Nuclear Medicine, these values should be: // * Value 1: // - ORIGINAL for raw and reconstructed images // - DERIVED for some other results (??... ORIGINAL it is, then !) // * Value 2: // - PRIMARY (is the only 'choice' !) // * Value 3: (here is the one that matters...) // - STATIC // - DYNAMIC // - GATED // - WHOLE BODY // - TOMO // - GATED TOMO // - RECON TOMO // - RECON GATED TOMO // * Value 4: // - EMISSION // - TRANSMISSION // NOTE : MX+ does not use transmission, or at least does not know // about it (so if it is used, it will have to be notified for // instance in a label or study description which will keep on // showing whatever happens...). Hence in here, we always write // EMISSION. imageType = "ORIGINAL\\PRIMARY\\"; switch(*type) { case UI_STATIC: imageType += "STATIC"; break; case UI_DYNAMIC: imageType += "DYNAMIC"; break; case UI_GATED: imageType += "GATED"; break; case UI_WHOLE_BODY: imageType += "WHOLE BODY"; break; case UI_SPECT: imageType += "TOMO"; break; case UI_GATED_SPECT: imageType += "GATED TOMO"; break; case UI_AXIAL: imageType += "RECON TOMO"; break; case UI_GATED_AXIAL: imageType += "RECON GATED TOMO"; break; default : //might apply if type = NONE, ROI/Profile or Screen Dump debug_cpp(4,"Error : this image is not a standard NM image..."); debug_cpp(4,"Trying to export as Static..."); return_value = EC_ValueDefaulted; *type=UI_NONE; imageType += "STATIC"; break; } imageType += "\\EMISSION"; debug_cpp(7,"dataset <- image type ("<< imageType <<")"); INSERT_INTO_DATASET("ImageType",DcmCodeString,putString,imageType.c_str()); debug_cpp(7,"dataset <- counts accumulated (=0)"); INSERT_INTO_DATASET("CountsAccumulated",DcmIntegerString,putString,"0"); db = ds_findBlock(ds,"Data"); if (!db) return show_error_message_block("Data"); // Actual Frame Duration : (0018,1242) - IS - 1C (Image type (0008,0008) Value3 = WholeBody or Static) if (*type == UI_STATIC || *type == UI_WHOLE_BODY) { OFString tempVal; long* duration=NULL; // we put the time of the first image, but this does not make much sense // for multiple statics... if (!(di = db_findItem(db,"expDurations"))) return show_error_message_item("Data.expDurations"); di_loadi(di, 0, 1, 0, 0); duration = (long*)di_getBuf(di); tempVal << *duration; debug_cpp(7,"dataset <- actual frame duration ("<< tempVal <<")"); INSERT_INTO_DATASET("ActualFrameDuration",DcmIntegerString,putString,tempVal.c_str()); } if (*type == UI_WHOLE_BODY) { // Scan Velocity : (0018,1300) - DS - 2C debug_cpp(7,"dataset <- scan velocity (=empty)"); INSERT_INTO_DATASET("ScanVelocity",DcmIntegerString,putString,""); // Scan Length : (0018,1302) - IS - 2C debug_cpp(7,"dataset <- scan length (=empty)"); INSERT_INTO_DATASET("ScanLength",DcmIntegerString,putString,""); } //termination criteria... does it get put in the sd file ? return EC_Normal; } /* End of "DcmModules::Add_NMImage_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_NMImagePixel_Module **------------------------------------------------------------------------ ** Summary : adds the NM Image Pixel Module to the given dataset, ** taking the data in the given datastore. ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** colourFormat I the output colour format... used only to write ** the photometric interpretation ** ** Return Value : ** EC_MemoryExhausted if an element to insert could not be created ** EC_Normal otherwise ** ** Note : ** Most of the data in this mocule is added 'properly' (i.e. with ** proper checking of their values) in the Add_ImagePixel_Module ** function. It is then better to call that function first, and on ** that case this function here will add nothing else (except an ** 'empty' value for pixel spacing...) **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_NMImagePixel_Module (DcmDataset *dset, DataStore *ds, E_ColourFormat colourFormat) { // NM Image Pixel module contains: // * - Samples per Pixel - (0028,0002) - US - 1 // * - Photometric Interpretation - (0028,0004) - CS - 1 // * - Bits Allocated - (0028,0100) - US - 1 // * - Bits Stored - (0028,0101) - US - 1 // * - High Bit - (0028,0102) - US - 1 // * - Pixel Spacing - (0028,0030) - DS - 2 DcmStack stack; debug_cpp(3,"Adding NM Image Pixel Module"); //NB : by now, normally, the Image Pixel module should be already built, so we // should not have to do it again... // Samples per pixel : (0028,0002) - US - 1 // if (dset->search(DcmTag("SamplesPerPixel"),stack,ESM_fromHere,OFFalse) != EC_Normal) { debug_cpp(7,"dataset <- samples per pixel (=1)"); INSERT_INTO_DATASET("SamplesPerPixel",DcmUnsignedShort,putString,"1"); } // Photometric interpretation : (0028,0004) - CS - 1 // if (dset->search(DcmTag("PhotometricInterpretation"),stack,ESM_fromHere,OFFalse) != EC_Normal) { debug_cpp(7,"dataset <- photometric interpretation ("<< dcmColourFormatToString(colourFormat) <<")"); INSERT_INTO_DATASET("PhotometricInterpretation",DcmCodeString, putString,dcmColourFormatToString(colourFormat)); } // bits allocated : (0028,0100) - US - 1 // if (dset->search(DcmTag("BitsAllocated"),stack,ESM_fromHere,OFFalse) != EC_Normal) { debug_cpp(7,"dataset <- bits allocated (=8)"); INSERT_INTO_DATASET("BitsAllocated",DcmUnsignedShort,putUint16,8); } // bits stored : (0028,0101) - US - 1 // if (dset->search(DcmTag("BitsStored"),stack,ESM_fromHere,OFFalse) != EC_Normal) { debug_cpp(7,"dataset <- bits stored (=8)"); INSERT_INTO_DATASET("BitsStored",DcmUnsignedShort,putUint16,8); } // high bit : (0028,0102) - US - 1 // if (dset->search(DcmTag("HighBit"),stack,ESM_fromHere,OFFalse) != EC_Normal) { debug_cpp(7,"dataset <- high bit (=7)"); INSERT_INTO_DATASET("HighBit",DcmUnsignedShort,putUint16,7); } // Pixel Spacing : (0028,0030) - DS - 2 // if (dset->search(DcmTag("PixelSpacing"),stack,ESM_fromHere,OFFalse) != EC_Normal) { debug_cpp(7,"dataset <- pixel spacing (=empty)"); INSERT_INTO_DATASET("PixelSpacing",DcmDecimalString,putString,""); } return EC_Normal; } /* End of "DcmModules::Add_NMImagePixel_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_NMIsotope_Module **------------------------------------------------------------------------ ** IMPORTANT : call this function AFTER Add_NMMultiFrame_Module ** this function uses the data written in the dataset by the ** function Add_NMMultiFrame_Module, hence it has to be called ** AFTER Add_NMMultiFrame_Module. ** ** Summary : adds the NM Isotope Module to the given dataset, ** taking the data in the given datastore. ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset I/O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** - EC_MemoryExhausted if an element to insert could not be created ** - EC_IllegalCall if the module NM MultiFrame was not created before ** - EC_MissingDataStoreBlock if blocks "Study" or "Data" could not be ** found ** - EC_MissingDataStoreItem if items "Isotope", "lowWindow" or ** "highWindow" could not be found ** - EC_ValueDefaulted if there is a pbme with the num of isotopes ** - EC_Normal otherwise **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_NMIsotope_Module (DcmDataset *dset, DataStore *ds) { // NM Isotope module contains: // * - Energy Window Information Sequence - (0054,0012) - SQ - 2 // * - >Energy Window Name - (0054,0018) - SH - 3 // * - >Energy Window Range Sequence - (0054,0013) - SQ - 3 // * - >>Energy Window Lower Limit - (0054,0014) - DS - 3 // * - >>Energy Window Upper Limit - (0054,0015) - DS - 3 // * - Radiopharmaceutical Information Sequence - (0054,0016) - SQ - 2 // - >Radionuclide Code Sequence - (0054,0300) - SQ - 2C // - >>(see below) // - >Radiopharmaceutical Route - (0018,1070) - LO - 3 // - >Administration Route Code Sequence - (0054,0302) - SQ - 3 // - >>(see below) // - >Radiopharmaceutical Volume - (0018,1071) - DS - 3 // - >Radiopharmaceutical Start Time - (0018,1072) - TM - 3 // - >Radiopharmaceutical Stop Time - (0018,1073) - TM - 3 // * - >Radionuclide Total Dose - (0018,1074) - DS - 3 // - >Calibration Data Sequence - (0054,0306) - SQ - 3 // - >>Energy Window Number - (0054,0308) - US - 3 // - >>Syringe Counts - (0018,1045) - IS - 1C // - >>Residual Syringe Counts - (0054,0017) - IS - 3 // * - >Radiopharmaceutical - (0018,0031) - LO - 3 // - >Radiopharmaceutical Code Sequence - (0054,0304) - SQ - 3 // - >>(see below) // - Intervention Drug Information Sequence - (0018,0026) - SQ - 3 // - >Intervention Drug Name - (0018,0034) - LO - 3 // - >Intervention Drug Code Sequence - (0018,0029) - SQ - 3 // - >>(see below) // - >Administration Route Code Sequence - (0054,0302) - SQ - 3 // - >>(see below) // - >Intervention Drug Start Time - (0018,0035) - TM - 3 // - >Intervention Drug Stop Time - (0018,0027) - TM - 3 // - >Intervention Drug Dose - (0018,0028) - DS - 3 // NOTE : // ! - Each code sequence should include // ! - >>Code Value - (0008,0100) - SH - 1C - See Section 3-8.1 // ! - >>Coding Scheme Designator - (0008,0102) - SH - 1C - See Section 3-8.2 // ! - >>Coding Scheme Version - (0008,0103) - SH - 1C - See Section 3-8.2 - required if Coding Scheme Designator (0008,0102) is not sufficient to identify the Code Value (0008,0100) unambiguously. // ! - >>Code Meaning - (0008,0104) - LO - 1C - See Section 3-8.3 DataItem *di1=NULL,*di2=NULL,*di3=NULL; DataBlock *db1=NULL,*db2=NULL; E_Condition return_value=EC_Normal; long num_isotopes=0, *dims=NULL, temp=0; short **lowWindow=NULL, **highWindow=NULL, numWindows=0; char **isotopes_names=NULL; debug_cpp(3,"Adding NM Isotope Module"); //Energy Window Information Sequence - (0054,0012) - SQ - 2 if(dset->findIntegerNumber(DcmTag("NumberOfEnergyWindows"), num_isotopes,0) == EC_Normal) { DcmSequenceOfItems *energy_wind_info_seq = new DcmSequenceOfItems(DcmTag("EnergyWindowInformationSequence")); if (energy_wind_info_seq) { //check if the value (0054,0011) exists, to find out the //number of isotopes. debug_cpp(7,"dataset <- energy window info sequence"); //get the the number of isotopes and load the corrsponding item if (!(db1 = ds_findBlock(ds,"Study"))) return show_error_message_block("Study"); if (!(di1 = db_findItem(db1,"isotope"))) { debug_cpp(4,"item Study.isotope not present... Defaulting all Values to empty strings..."); return_value = EC_ValueDefaulted; } else { di_getSize(di1,&dims); temp = dims[1]; if (temp!=num_isotopes) { debug_cpp(4,"Warning: conflict in the number of isotopes"); debug_cpp(4," assuming "<putString(isotopes_names[i]); debug_cpp(9,"dataset <- energy window name ("<< isotopes_names[i] <<")"); new_isotope->insert(name); // insert isotope's upper and lower limits debug_cpp(9,"dataset <- energy window range sequence"); DcmSequenceOfItems *windows_sequence = new DcmSequenceOfItems(DcmTag("EnergyWindowRangeSequence")); //loop over all the energy windows: for (short j=0; jputString(low.c_str()); higher->putString(high.c_str()); debug_cpp(11,"dataset <- energy window lower limit ("<< low <<")"); new_window->insert(lower); debug_cpp(11,"dataset <- energy window upper limit ("<< high <<")"); new_window->insert(higher); windows_sequence->insert(new_window); } //if one limit is not 0 } //loop over all the energy windows for the current isotope new_isotope->insert(windows_sequence); energy_wind_info_seq->insert(new_isotope); } //loop over all the isotopes dset->insert(energy_wind_info_seq,OFTrue); } // if memory not exhausted else { debug_cpp(4,"Error: not enough memory to create element EnergyWindowInformationSequence"); delete dset; return EC_MemoryExhausted; } } // if tag (0054,0011) exists else { debug_cpp(4,"Warning : tag (0054,0011) could not be found."); debug_cpp(4," Energy Window Info Sequence not created."); debug_cpp(5,"Possible reason: NM Isotope module build before NM MultiFrame"); return_value = EC_IllegalCall; } // Radiopharmaceutical Information Sequence - (0054,0016) - SQ - 2 if (num_isotopes != 0) { DcmSequenceOfItems *radiopharma_info_seq = new DcmSequenceOfItems(DcmTag("RadiopharmaceuticalInformationSequence")); if (radiopharma_info_seq) { debug_cpp(7,"dataset <- Radiopharmaceutical Information Sequence"); //find and load the items for pharmaceutical's name and dose if (!(db1 = ds_findBlock(ds,"Study"))) return show_error_message_block("Study"); if (!(di1 = db_findItem(db1,"pharmaceutical"))) { debug_cpp(4,"item Study.pharmaceutical not present... Defaulting all values to empty strings..."); return_value = EC_ValueDefaulted; } if (!(db2 = ds_findBlock(ds,"PatStudy"))) return show_error_message_block("PatStudy"); if (!(di2 = db_findItem(db2,"dose"))) { debug_cpp(4,"item PatStudy.dose not present... Defaulting all values to '0'..."); return_value = EC_ValueDefaulted; } // loop over all the pharmaceuticals. OFString pharma_name="", pharma_dose=""; float dose_float=0.; for (long i=0; iputString(pharma_name.c_str()); debug_cpp(9,"dataset <- radiopharmaceutical ("<< pharma_name <<")"); new_pharma->insert(name); // load the dose for the current pharmaceutical. if (di2) { di_loadi(di2,0,1,i,0); dose_float= *(float*)di_getBuf(di2); pharma_dose << dose_float; } else pharma_dose = "0"; DcmDecimalString *dose = new DcmDecimalString(DcmTag("RadionuclideTotalDose")); dose->putString(pharma_dose.c_str()); debug_cpp(9,"dataset <- radionuclide total dose ("<< pharma_dose.c_str() <<")"); new_pharma->insert(dose); pharma_dose.clear(); radiopharma_info_seq->insert(new_pharma); }// loop over all the pharmaceuticals dset->insert(radiopharma_info_seq); } // if memory not exhausted else { debug_cpp(4,"Error: not enough memory to create element RadiopharmaceuticalInformationSequence"); delete dset; return EC_MemoryExhausted; } } return return_value; } /* End of "DcmModules::Add_NMIsotope_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_NMMultiFrame_Module **------------------------------------------------------------------------ ** Summary : adds the NM MultiFrame Module to the given dataset, ** taking the data in the given datastore. ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** - EC_MissingDatastoreBlock if blocks "Data" or "Camera" cannot be ** found ** - EC_MissingDatastoreItem if items "Data:lowWindow" or ** "Camera:heads" cannot be found ** - EC_CorruptedData if wrong dimensions for 'lowWindow' ** - EC_MemoryExhausted if an element to insert could not be created ** - EC_ValueDefaulted if at least one value was wrong or missing but ** has been set to a default value. ** - EC_Normal otherwise ** ** IMPORTANT : ** The NM MultiFrame Module contains data which decides of the existence ** of some modules, as well as determines the content of some data ** in some other modules... Hence this function has to be called ** before the following: ** - Add_NMIsotope_Module ** - Add_NMDetector_Module ** - Add_NMPhase_Module ** ** IMPORTANT : ** On the case of DYNAMICS, the phase vector is actually written by the ** Add_NMPhase_Module function. In here, we just make sure that the ** phase vector is put in the frame increment pointer... In the case of ** static and whole body, however, the phase vector is written in here. **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_NMMultiFrame_Module (DcmDataset *dset, DataStore *ds) { // NM Multi Frame module contains: // * - Frame Increment Pointer (0028,0009) - AT - 1 // * - Energy Window Vector (0054,0010) - US - 1C // * - Number of Energy Windows (0054,0011) - US - 1 // * - Detector Vector (0054,0020) - US - 1C // * - Number of Detectors (0054,0021) - US - 1 // * - Phase Vector (0054,0030) - US - 1C // * - Number of Phases (0054,0031) - US - 1C // - Rotation Vector (0054,0050) - US - 1C // - Number of Rotations (0054,0051) - US - 1C // - R-R Interval Vector (0054,0060) - US - 1C // - Number of R-R Intervals (0054,0061) - US - 1C // - Time Slot Vector (0054,0070) - US - 1C // - Number of Time Slots (0054,0071) - US - 1C // - Slice Vector (0054,0080) - US - 1C // - Number of Slices (0054,0081) - US - 1C // - Angular View Vector (0054,0090) - US - 1C // - Time Slice Vector (0054,0100) - US - 1C //Other tag written in this module (although is does not belong here): // * - Number of Frames (0028,0008) - IS - 1 (see Add_MultiFrame_Module) // * - Images in Acquisition (0020,1002) - IS - 3 DataItem *di=NULL; DataBlock *db=NULL; short studyMode=1, studyType=1, organ=11, *headNumber=NULL; Uint16 num_dims=0, numFrames=0, numIsotopes=0, numFramesPerImage=0, numImages=0, numPhases=0, numDetectors=0, numDataBlocks=0, numImages2=0, *detectorVector=NULL, *energyWindVector=NULL, *phaseVector=NULL; long *dims; OFString attributeTags=""; E_Condition return_value = EC_Normal; debug_cpp(3,"Adding NM MultiFrame Module"); //first, find out what kind of study we have db = ds_findBlock(ds,"Study"); if (db) { LOAD_ITEM("organ",short,organ2,11,SHOW_WARNING); //11=OTHER LOAD_ITEM("studyMode",short,mode,1,SHOW_WARNING); // 1=NONE LOAD_ITEM("studyType",short,type,1,SHOW_WARNING); // 1=NONE studyMode=*mode; studyType=*type; organ=*organ2; } else //could not find block "Study" { show_error_message_block("Study"); return_value = EC_ValueDefaulted; debug_cpp(8,"while building NM Multi Frame Module:"); debug_cpp(9,"defaulting study type to 1 (NONE)"); debug_cpp(9,"defaulting study mode to 1 (NONE)"); debug_cpp(9,"defaulting organ to 11 (Other)"); } // Frame Increment Pointer (0028,0009) - AT - 1 // // This basically is the complete list of all the elements that contain // multi frames. The list of multiframe elements contained in this module // is the exhaustive list of all possible elements. // // Energy Window Vector (0054,0010) - US - 1C // Number of Energy Windows (0054,0011) - US - 1 // There seems to be a clash between what we call 'energy window' in MX+, // and what it is in DICOM. I think that in DICOM, the numer of energy // windows will be the number of isotopes, because each energy window // seems to be able to contain different lower and higher limits (whereas // in MX+, one isotope can contain up to 3 energy windows, each having // only one lower and one higher limit.) // find the number of isotopes: // if (studyMode == 3) //3=Dual Isotope numIsotopes = 2; else numIsotopes = 1; db = ds_findBlock(ds,"Data"); if (!db) return show_error_message_block("Data"); //find out the number of energy windows per isotope // di = db_findItem(db,"lowWindow"); // if (!di) // return show_error_message_item("Data.lowWindow"); // // num_dims = di_getSize(di,&dims); // if (num_dims!=2) // { // debug_cpp(1,"Error: wrong dimensions for item 'lowWindow'"); // return(EC_CorruptedData); // } // else // numEnergyWind = (Uint16)dims[1]; // find the number of data blocks: num_dims = db_getSize(db,&dims); numDataBlocks = (Uint16)dims[0]; // deduct the number of frames numFrames = numDataBlocks*numIsotopes; //NB : i think that for several heads, each head will generate a new data //block, so we do not have to take that information into consideration. //find the number of detectors //NB : If we have an even number of datablocks, acquired with a camera // that physically has 2 heads, there is very little we can do // to know if the acquisitions have all been made with the same // head, or if the 2 heads were used at the same time. Normally, // the item headNumber should give a precise and reliable, but it // is often 0 (that should be fixed soon, though)... In any case, // we look at the headNumber value for the first 2 Data blocks, // and set the number of heads used to 2 only if they have different // values. if (numDataBlocks==1) { numDetectors = 1; headNumber = new short[1]; headNumber[0]=1; } else { OFBool warningAlreadyGiven=OFFalse, needToIncreaseValues=OFFalse; headNumber = new short[numDataBlocks]; //read all the headnumbers, i.e. in each Data block. di = db_findItem(db,"headNumber"); if (di) { for (int i=0;i 1) ? numDataBlocks : 0; numFramesPerImage = 1; numImages = numDataBlocks*numIsotopes; numFrames = numFramesPerImage*numImages; break; case UI_DYNAMIC: db = ds_findBlock(ds,"Data"); di = db_findItem(db,"image"); if (!di) return show_error_message_item("Data.image"); num_dims = di_getSize(di,&dims); numFramesPerImage = (Uint16)dims[2]; numImages = numDataBlocks*numIsotopes; numFrames = numFramesPerImage*numImages; numPhases = 0; // because it actually gets written in // the Add_NMPhase_Module function... break; default: numPhases = 0; numFrames = numDataBlocks*numIsotopes; numImages = numFrames; numFramesPerImage = 1; break; } //NOTE : This numImages2 variable is used only to avoid a compiler // internal error ! When inserting the number of Images in // Acquisition a few lines below, if we put numimg << numImages // then the compilation breaks. But if we use this little numImages2 // instead, it works... VERY STRANGE !!!!!! numImages2 = numImages; //create the vectors detectorVector = new Uint16[numFrames]; energyWindVector= new Uint16[numFrames]; if (numPhases) phaseVector= new Uint16[numFrames]; Uint8 frame = 0; for (Uint8 i=0; ifindOFString(DcmTag("ImageType"),image_type,3) != EC_Normal) { debug_cpp(4,"Warning : ImageType could not be found."); debug_cpp(4," NM Multi Gated Acquisition Module not created."); debug_cpp(5,"Possible reason: NM Multi Gated Acquisition Module build before NM Image"); return_value = EC_IllegalCall; } else { if ( image_type == "GATED" || image_type == "GATED TOMO" || image_type == "RECON GATED TOMO" ) { debug_cpp(3,"Adding NM Multi Gated Acquisition Module"); //then we would have to build the module here !!!! return_value = EC_Normal; } else { debug_cpp(3,"Adding NM Multi Gated Acquisition Module"); debug_cpp(4,"image type not gated"); debug_cpp(4," => NM Multi Gated Acquisition module not needed"); return_value = EC_Normal; } } return return_value; } /* End of "DcmModules::Add_NMMultiGatedAcquisition_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_NMPETPatientOrientation_Module **------------------------------------------------------------------------ ** Summary : Add the NM/PET Patient Orientation module elements to the ** given dataset... ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset I/O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** EC_Normal **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_NMPETPatientOrientation_Module (DcmDataset *dset, DataStore *ds) { // NM/PET Patient Orientation module contains: // * - Patient Orientation Code Sequence - (0054,0410) - SQ - 2 // * - >Patient Orientation Modifier Code Sequence - (0054,0412) - SQ - 2C // * - Patient Gantry Relationship Code Sequence - (0054,0414) - SQ - 2 debug_cpp(3,"Adding NM/PET Patient Orientation Module"); debug_cpp(7,"dataset <- patient orientation code sequence (=empty)"); DcmSequenceOfItems *patient_orient_code_seq = new DcmSequenceOfItems(DcmTag("PatientOrientationCodeSequence")); dset->insert(patient_orient_code_seq); debug_cpp(7,"dataset <- patient gantry relationship code sequence (=empty)"); DcmSequenceOfItems *patient_gantry_relation_code_seq = new DcmSequenceOfItems(DcmTag("PatientGantryRelationshipCodeSequence")); dset->insert(patient_gantry_relation_code_seq); return EC_Normal; } /* End of "DcmModules::Add_NMPETPatientOrientation_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_NMPhase_Module **------------------------------------------------------------------------ ** IMPORTANT : call this function AFTER: - Add_NMImage_Module ** IMPORTANT : - Add_NMMultiFrame_Module ** This function uses the data written in the dataset by these two ** functions, hence it has to be called AFTER them... ** ** Summary : Tis function looks in the given datastore if the Image ** Type (0008,0008) (created in Add_NMImage_Module) is DYNAMIC or ** STATIC. If yes, then it looks to see if the frame increment pointer ** (created in Add_NMMultiFrame_Module) contains the tag for Phase ** Vector. If yes, then the Phase module is created, otherwise not. ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset I/O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** - EC_IllegalCall if the image type or the frame increment pointer ** do not already exist in the dset (i.e. this function is ** probably called before the two other mentionned above... ** - EC_MemoryExhausted if some items could not be allocated ** - EC_ValueDefaulted if the number of phases does not match the ** datastore, and was defaulted to do so. ** - EC_MissingDataStoreBlock if block "Data" could not be found ** - EC_MissingDataStoreItem if items "expTimes" or "expDurations" ** could not be found ** - EC_Normal otherwise **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_NMPhase_Module (DcmDataset *dset, DataStore *ds) { E_Condition return_value=EC_Normal; DataItem *di_delay=NULL, *di_duration=NULL, *di_image=NULL; DataBlock *db=NULL; Uint16 num_dims=0, *phaseVector=NULL; long *dims, *phasesList, numDataBlocks=0, numIsotopes=0, numPhases=0, numFramesPerImage=0, numFrames=0; short **frame2phase, block=0,isotope=0,frame=0,phase=0,i=0; // find out the type of the image OFString image_type=""; if (dset->findOFString(DcmTag("ImageType"),image_type,2) != EC_Normal) { debug_cpp(4,"Warning : ImageType could not be found."); debug_cpp(4," NM Phase Module not created."); debug_cpp(5,"Possible reason: NM Phase Module build before NM Image"); return_value = EC_IllegalCall; } else { if ( image_type == "DYNAMIC" ) { debug_cpp(3,"Adding NM phase Module -- DYNAMIC"); //then we would have to build the module here !!!! // check if frame increment pointer includes phase vector DcmStack fr_inc_pointer_stack; if (dset->search(DcmTag("FrameIncrementPointer"),fr_inc_pointer_stack,ESM_fromHere, OFFalse) != EC_Normal) { debug_cpp(4,"Warning : FrameIncrementPointer could not be found."); debug_cpp(4," NM Phase Module not created."); debug_cpp(5,"Possible reason: NM Phase Module build before NM Multi Frame"); return_value = EC_IllegalCall; } else { // Arriving here, we have a datastore in which the frame increment // pointer cointains the phase vector, but this latter and the // number of phases value have not been set... // the phase concept is not used in MX+ as such. Therefore, we // have to adapt to translate this information into a dicom // object. There could be 3 ways to do so: // - count one phase per frame // - count one phase per different type of frame (based on duration // only) (e.g. frames of 1 sec => phase 1, etc...) // - count one frame per group of frames, just as defined in the // acquisition window (e.g. 4 frames @ 1 sec => phase 1, etc...) // The first choice is the easiest and most error-proof, but too // messy, and would involve too much useless data written in the // file. // The third choice is too complicated to implement, as the groups // of frames can be cut if the acquisition was interrupted. // The second choice seems to make the more sense, considering... //--------------------------------------------------------------// // first, we need to find out how many different durations exist //--------------------------------------------------------------// //find num of datablocks db = ds_findBlock(ds,"Data"); if (!db) return show_error_message_block("Data"); num_dims = db_getSize(db,&dims); numDataBlocks = dims[0]; //get pointers to expTimes and expDurations di_delay = db_findItem(db,"expTimes"); if (!di_delay) return show_error_message_item("Data.expTimes"); di_duration = db_findItem(db,"expDurations"); if (!di_duration) return show_error_message_item("Data.expDurations"); //find num of isotopes and frames per image di_image = db_findItem(db,"image"); if (!di_image) return show_error_message_item("Data.image"); num_dims = di_getSize(di_image,&dims); numIsotopes = dims[3]; numFramesPerImage = dims[2]; numFrames = numDataBlocks*numIsotopes*numFramesPerImage; //store all the durations that are in the datastore long **duration = new long*[numDataBlocks]; if (!duration) { DEBUG_EXIT_MEMORYEXHAUSTED } for (block=0;blockputString("0"); debug_cpp(9,"dataset <- phase delay (=0)"); new_phase->insert(phaseDelay); // insert actual frame duration DcmIntegerString *phaseDuration = new DcmIntegerString(DcmTag("ActualFrameDuration")); if (!phaseDuration) { DEBUG_EXIT_MEMORYEXHAUSTED } duration_str << phasesList[phase]; phaseDuration->putString(duration_str.c_str()); debug_cpp(9,"dataset <- phase duration ("<< duration_str <<")"); new_phase->insert(phaseDuration); // insert pause between frames DcmIntegerString *pause = new DcmIntegerString(DcmTag("PauseBetweenFrames")); if (!pause) { DEBUG_EXIT_MEMORYEXHAUSTED } pause->putString("0"); debug_cpp(9,"dataset <- pause btwn frames (=0)"); new_phase->insert(pause); // insert number of frames DcmUnsignedShort *numFrames = new DcmUnsignedShort(DcmTag("NumberOfFramesInPhase")); if (!numFrames) { DEBUG_EXIT_MEMORYEXHAUSTED } numFrames->putString("1"); debug_cpp(9,"dataset <- num of frames in phase (=1)"); new_phase->insert(numFrames); // insert this new phase item in the phase sequence phase_info_seq->insert(new_phase); } dset->insert(phase_info_seq); //--------------------------------------------------------------// // then we need to create the phase vector, matching the actual // frames with the phases in the sequence. //--------------------------------------------------------------// phaseVector = new Uint16[numFrames]; if (!phaseVector) { DEBUG_EXIT_MEMORYEXHAUSTED } short vector_entry=0; for (block=0;blocksearch(DcmTag("FrameIncrementPointer"),fr_inc_pointer_stack,ESM_fromHere, OFFalse) != EC_Normal) { debug_cpp(3,"Adding NM phase Module -- STATIC"); debug_cpp(4,"Warning : FrameIncrementPointer could not be found."); debug_cpp(4," NM Phase Module not created."); debug_cpp(5,"Possible reason: NM Phase Module build before NM Multi Frame"); return_value = EC_IllegalCall; } else { Uint16 *attributes=NULL; Uint32 i=0; DcmAttributeTag *attTag = (DcmAttributeTag*) fr_inc_pointer_stack.top(); attTag->getUint16Array(attributes); // see if phase vector is in frame_increment_pointer for (i=0;igetVM();i++) if (attributes[i*2]==0x0054 && attributes[i*2+1]==0x0030) break; if (i==attTag->getVM()) //phase vector not found { debug_cpp(3,"Adding NM phase Module"); debug_cpp(4,"image type is STATIC, but not multiple"); debug_cpp(4," => NM phase module not needed"); return_value = EC_Normal; } else { // multiple static => we use the phase module to store each // frame's individual time characteristics... debug_cpp(3,"Adding NM phase Module -- Mult STATIC"); OFString num_frames_in_phases=""; DcmSequenceOfItems *phase_info_seq = new DcmSequenceOfItems(DcmTag("PhaseInformationSequence")); if (phase_info_seq) { long diloadSuffices[2]={ 0,0 }; //find the number of datablocks db = ds_findBlock(ds,"Data"); if (!db) return show_error_message_block("Data"); num_dims = db_getSize(db,&dims); numDataBlocks = dims[0]; //find the relevant items in the datastore di_delay = db_findItem(db,"expTimes"); if (!di_delay) return show_error_message_item("Data.expTimes"); di_duration = db_findItem(db,"expDurations"); if (!di_duration) return show_error_message_item("Data.expDurations"); di_image = db_findItem(db,"image"); if (!di_image) return show_error_message_item("Data.image"); num_dims = di_getSize(di_image,&dims); numIsotopes = dims[3]; num_frames_in_phases << numIsotopes; //check if the value (0054,0031) exists, to find out the //number of phases. dset->findIntegerNumber(DcmTag("NumberOfPhases"),numPhases,0); if (numPhases != numDataBlocks) { debug_cpp(4,"Warning: number of phases different from number of blocks."); debug_cpp(4," Assuming "<putString(delay_str.c_str()); debug_cpp(9,"dataset <- phase delay ("<< delay_str <<")"); new_phase->insert(phaseDelay); // insert actual frame duration di_load(di_duration, i, 1, diloadSuffices); duration=(long*)di_getBuf(di_duration); duration_str << *duration; DcmIntegerString *phaseDuration = new DcmIntegerString(DcmTag("ActualFrameDuration")); phaseDuration->putString(duration_str.c_str()); debug_cpp(9,"dataset <- phase duration ("<< duration_str <<")"); new_phase->insert(phaseDuration); // insert pause between frames DcmIntegerString *pause = new DcmIntegerString(DcmTag("PauseBetweenFrames")); pause->putString("0"); debug_cpp(9,"dataset <- pause btwn frames (=0)"); new_phase->insert(pause); // insert number of frames DcmUnsignedShort *numFrames = new DcmUnsignedShort(DcmTag("NumberOfFramesInPhase")); numFrames->putString(num_frames_in_phases.c_str()); debug_cpp(9,"dataset <- num of frames in phase ("<< num_frames_in_phases <<")"); new_phase->insert(numFrames); // insert this new phase item in the phase sequence phase_info_seq->insert(new_phase); } //loop over all the datablocks (=all the phases for statics) dset->insert(phase_info_seq); } else // check if memory not exhausted { debug_cpp(4,"Error: not enough memory to create element PhaseInformationSequence"); delete dset; return_value = EC_MemoryExhausted; } } } } else { debug_cpp(3,"Adding NM phase Module"); debug_cpp(4,"image type not dynamic (nor multiple static)"); debug_cpp(4," => NM phase module not needed"); return_value = EC_Normal; } } return return_value; } /* End of "DcmModules::Add_NMPhase_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_NMReconstruction_Module **------------------------------------------------------------------------ ** IMPORTANT : call this function AFTER Add_NMImage_Module. ** This function uses the data written in the dataset by the function ** Add_NMImage_Module, hence it has to be called AFTER ** Add_NMImage_Module. ** ** Summary : This function looks in the given datastore if the Image ** Type (0008,0008) is RECON in any way, and if yes it adds the NM ** Reconstruction Module to it, taking the data in the given datastore. ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset I/O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** - EC_IllegalCall if the image type does not already exist in the dset ** - EC_Normal otherwise **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_NMReconstruction_Module (DcmDataset *dset, DataStore *ds) { E_Condition return_value=EC_Normal; // find out the type of the image OFString image_type=""; if (dset->findOFString(DcmTag("ImageType"),image_type,3) != EC_Normal) { debug_cpp(4,"Warning : ImageType could not be found."); debug_cpp(4," NM Reconstruction Module not created."); debug_cpp(5,"Possible reason: NM Reconstruction Module build before NM Image"); return_value = EC_IllegalCall; } else { if ( image_type == "RECON TOMO" || image_type == "RECON GATED TOMO" ) { debug_cpp(3,"Adding NM Reconstruction Module"); //then we would have to build the module here !!!! return_value = EC_Normal; } else { debug_cpp(3,"Adding NM Reconstruction Module"); debug_cpp(4,"image type not RECON"); debug_cpp(4," => NM Reconstruction module not needed"); return_value = EC_Normal; } } return return_value; } /* End of "DcmModules::Add_NMReconstruction_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_NMTOMOAcquisition_Module **------------------------------------------------------------------------ ** IMPORTANT : call this function AFTER Add_NMImage_Module. ** This function uses the data written in the dataset by the function ** Add_NMImage_Module, hence it has to be called AFTER ** Add_NMImage_Module. ** ** Summary : This function looks in the given datastore if the Image ** Type (0008,0008) is TOMO in any way, and if yes it adds the NM TOMO ** Acquisition Module to it, taking the data in the given datastore. ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset I/O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** - EC_IllegalCall if the image type does not already exist in the dset ** - EC_Normal otherwise **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_NMTOMOAcquisition_Module (DcmDataset *dset, DataStore *ds) { E_Condition return_value=EC_Normal; // find out the type of the image OFString image_type=""; if (dset->findOFString(DcmTag("ImageType"),image_type,3) != EC_Normal) { debug_cpp(4,"Warning : ImageType could not be found."); debug_cpp(4," NM TOMO Acquisition Module not created."); debug_cpp(5,"Possible reason: NM TOMO Acquisition Module build before NM Image"); return_value = EC_IllegalCall; } else { if ( image_type == "TOMO" || image_type == "GATED TOMO" || image_type == "RECON TOMO" || image_type == "RECON GATED TOMO" ) { debug_cpp(3,"Adding NM TOMO Acquisition Module"); //then we would have to build the module here !!!! return_value = EC_Normal; } else { debug_cpp(3,"Adding NM TOMO Acquisition Module"); debug_cpp(4,"image type not tomo"); debug_cpp(4," => NM TOMO Acquisition module not needed"); return_value = EC_Normal; } } return return_value; } /* End of "DcmModules::Add_NMTOMOAcquisition_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_OverlayPlane_Module **------------------------------------------------------------------------ ** Summary : do nothing at the moment... ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** EC_Normal **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_OverlayPlane_Module (DcmDataset *dset, DataStore *ds) { // OverlayPlane module contains: // ??? //This is not a mandatory module, and so far it has no meaning for //us, so i leave it empty for the time being. debug_cpp(3,"Adding OverlayPlane Module"); debug_cpp(4,"not needed non-mandatory module..."); debug_cpp(4," => OverlayPlane module not created"); return EC_Normal; } /* End of "DcmModules::Add_OverlayPlane_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_Patient_Module **------------------------------------------------------------------------ ** Summary : adds the Patient Module to the given dataset, ** taking the data in the given datastore. ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** - EC_ValueDefaulted if a value could not be read where we SHOULD have ** found a value, and we had to default it ** - EC_MemoryExhausted if an element to insert could not be created ** - EC_Normal otherwise **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_Patient_Module (DcmDataset *dset, DataStore *ds) { //patient module contains: // * - Patient's name - (0010,0010) - PN - 2 // * - Patient's id - (0010,0020) - LO - 2 // * - Patient's birth date - (0010,0030) - DA - 2 // * - Patient's sex - (0010,0040) - CS - 2 // - Referenced Patient Sequence - (0008,1120) - SQ - 3 // - Referenced SOP Class UID - (0008,1150) - UI - 1C (Referenced Patient Sequence) // - Referenced SOP Instance UID - (0008,1155) - UI - 1C (Referenced Patient Sequence) // - Patient's Birth Time - (0010,0032) - TM - 3 // - Other Patient IDs - (0010,1000) - LO - 3 // - Other Patient Names - (0010,1001) - PN - 3 // - Ethnic Group - (0010,2160) - SH - 3 // - Patient Comments - (0010,4000) - LT - 3 debug_cpp(3,"Adding Patient Module"); OFString str_buf=""; DataBlock* db=NULL; DataItem* di=NULL; E_Condition return_value = EC_Normal; db = ds_findBlock(ds,"Head"); if (db) { // Patient Name : (0010,0010) - PN - 2 // { // person name in the form 'surname^forename^middlename^prefix^suffix' OFString patName=""; LOAD_STR_BUF("patName2","",HIDE_WARNING); //surname patName = str_buf + "^"; LOAD_STR_BUF("patName1","",HIDE_WARNING); //forename patName += str_buf + "^^^"; //could be omitted, but clearer to keep it debug_cpp(7,"dataset <- patient name ("<< patName <<")"); INSERT_INTO_DATASET("PatientsName",DcmPersonName, putString,patName.c_str()); str_buf.erase(); } // Patient ID : (0010,0020) - LO - 2 // { LOAD_STR_BUF("patId","(unknown)",SHOW_WARNING); debug_cpp(7,"dataset <- patient ID ("<< str_buf <<")"); INSERT_INTO_DATASET("PatientID",DcmLongString, putString,str_buf.c_str()); str_buf.erase(); } } else //could not find block 'Head' { show_error_message_block("Head"); return_value = EC_ValueDefaulted; debug_cpp(7,"dataset <- patient name (no value)"); INSERT_INTO_DATASET("PatientsName",DcmPersonName, putString,"^^^^"); debug_cpp(7,"dataset <- patient ID (no value)"); INSERT_INTO_DATASET("PatientID",DcmLongString, putString,""); } db = ds_findBlock(ds,"Patient"); if (db) { // Patient's Birth Date : (0010,0030) - DA - 2 // { LOAD_STR_BUF("birthDate","",HIDE_WARNING); if (format_date(str_buf) != EC_Normal) { show_warning_message("date","PatientsBirthDate"); debug_cpp(4,"keeping date '"<findOFString(DcmTag("StudyID"),studyID,0) != EC_Normal) { DataBlock* db; DataItem* di; OFString date="", time=""; db = ds_findBlock(ds,"Head"); if (db) { // load Study Date { LOAD_STR_BUF("date",DEFAULT_DATE,SHOW_WARNING); if (format_date(str_buf) != EC_Normal) { show_warning_message("date","StudyDate"); debug_cpp(4,"defaulting StudyDate to '"< yyyy/mmdd date.insert(7,1,'/'); //yyyy/mmdd -> yyyy/mm/dd str_buf.erase(); } // load Study Time { LOAD_STR_BUF("time",DEFAULT_TIME,SHOW_WARNING); if (format_time(str_buf) != EC_Normal) { show_warning_message("time","StudyTime"); debug_cpp(4,"defaulting StudyTime to '"< hh/mmss time.insert(5,1,':'); //hh/mmss -> hh/mm/ss str_buf.erase(); } } else //could not find block "Head" { show_error_message_block("Head"); return_value = EC_ValueDefaulted; debug_cpp(8,"while building SOP Instance UID:"); debug_cpp(9,"defaulting study date to 1901/01/01"); date = "1901/01/01"; debug_cpp(9,"defaulting study time to 01:01:01"); time = "01:01:01"; } db = ds_findBlock(ds,"Study"); if (db) { LOAD_ITEM("organ",short,organ,11,HIDE_WARNING); //11=OTHER LOAD_ITEM("studyMode",short,mode,1,HIDE_WARNING); //1=NONE LOAD_ITEM("studyType",short,type,1,HIDE_WARNING); //1=NONE studyID = buildStudyId(date.c_str(),time.c_str(),*type, *mode,0,*organ); } else //could not find block "Study" { show_error_message_block("Study"); return_value = EC_ValueDefaulted; debug_cpp(8,"while building SOP Instance UID:"); debug_cpp(9,"defaulting study type to 1 (NONE)"); debug_cpp(9,"defaulting study mode to 1 (NONE)"); debug_cpp(9,"defaulting organ to 11 (Other)"); studyID = buildStudyId(date.c_str(),time.c_str(),1,1,0,11); } } SOPInstanceUID = dcmGetInstanceUIDPrefix(temp_str); SOPInstanceUID += "." + studyID + ".1"; debug_cpp(7,"dataset <- SOP instance UID"); INSERT_INTO_DATASET("SOPInstanceUID",DcmUniqueIdentifier, putString,SOPInstanceUID.c_str()); str_buf.erase(); } return return_value; } /* End of "DcmModules::Add_SOPCommon_Module" */ /* **------------------------------------------------------------------------ ** Function : DcmModules::Add_VOILUT_Module **------------------------------------------------------------------------ ** Summary : do nothing at the moment... ** ** Arguments : ** Name I/O Explanation ** ---- --- ----------- ** dset O the dataset into which we will add the data ** ds I the datastore from where to extract the data ** ** Return Value : ** EC_Normal **------------------------------------------------------------------------ */ E_Condition DcmModules::Add_VOILUT_Module (DcmDataset *dset, DataStore *ds) { // VOILUT module contains: // ??? //This is not a mandatory module, and so far it has no meaning for //us, so i leave it empty for the time being. debug_cpp(3,"Adding VOI LUT Module"); debug_cpp(4,"not needed non-mandatory module..."); debug_cpp(4," => VOI LUT module not created"); return EC_Normal; } /* End of "DcmModules::Add_VOILUT_Module" */ /*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/ /*** Support functions */ /*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/ /* **------------------------------------------------------------------------ ** Function : format_date **------------------------------------------------------------------------ ** Summary : function to take an OFString containing a date in a ** relatively unknown format (that is, we know that the order it ** year-month-day, but we mainly don't know how the year is written (2 ** or 4 digits ?), and we are not sure about seperators...), and ** changing it to the normal DICOM format, i.e. yyyymmdd. ** ** Arguments : Name I/O Explanation ** date I/O OFString sturcture containing the date. ** It gets modified to contain the corrected ** date afterwards. ** Return Value : ** - EC_IllegalCall if date is empty or does not contain enough nubmers ** - EC_CorruptedData if the number of digits it contains is non ** standard, or if the format is strange (1, or more ** than 2 separators, for example...) ** - EC_OutOfRange if one of the values is out of range ** - EC_Normal otherwise ** ** Note : ** This function expects 0 or 2 separators (optional non-digit ** characters before and after date are discarded). If 0 separator, the ** input format can be yyyymmdd or yymmdd. Anything else will raise an ** error. If 2 separators, the input format can be Y/M/D, where Y can ** contain 1,2 or 4 digits, M and D 1 or 2 digits. Anything else will ** raise an error. ** There can be any series of non-digits characters used as separators, ** or preceding/following the date. ** The day has to be in the range [1-31], the month in the range [1-12] ** and the year can be anything ! It is voluntarily that values of 0 ** do not raise errors, as they are commonly used to symbolise 'no date'. ** ** NB: this function will try to retreive data as much as it can, but in ** the case that it gets completely confused, or can't retreive any date ** at all (EC_IllegalCall or EC_CorruptedData cases), then it defaults ** the date to DEFAULT_DATE, in order to provide an opportunity to the ** program to keep on running... **------------------------------------------------------------------------ */ E_Condition format_date(OFString & date) { int i = 0; int length = date.length(); //nb: apparently, size_t = int... int num_series = 0; int num_digits = 0; int suffix[6] = {0,0,0,0,0,0}; OFString year; OFString month; OFString day; OFString temp; E_Condition return_value = EC_Normal; //check value not existing if (date=="" || date.find("00/00/00")!=OFString_npos) { date = ""; return EC_Normal; } //check minimal string if (length <5) { debug_cpp(4,"date '"< 19yymmdd { year = date.substr(suffix[0] ,2); month = date.substr(suffix[0]+2,2); day = date.substr(suffix[0]+4,2); } else if (num_digits==8) //then date must be yyyymmdd { year = date.substr(suffix[0] ,4); month = date.substr(suffix[0]+4,2); day = date.substr(suffix[0]+6,2); } else { debug_cpp(4,"cannot recognise date '"<12) { debug_cpp(4,"error in date '"<31) { if (atoi(year.c_str())<=31 && atoi(month.c_str())<=12) { //then it means that for a reason or another the date is //written in reverse order than what we expect... temp="19"+day+month+year; //==year+month+day } else { debug_cpp(4,"error in date '"< 123000) ** - abc093541cvb (=>093541) ** - xx10xx40xx28xxx (=>104028) ** - xx2:4:6xx (=>020406) ** If some values are out of range, they will be defaulted to 00 to ** to avoid stopping the program, and an error message will be raised. ** ** NB : incase the time is completely ununderstandable (EC_IllegalCall ** or EC_CorruptedData cases), then it will be defaulted to ** DEFAULT_TIME... It is important to carry that error to the top... **------------------------------------------------------------------------ */ E_Condition format_time(OFString & time) { int i = 0; int length = time.length(); int num_series = 0; int num_digits = 0; int suffix[6] = {0,0,0,0,0,0}; OFString hour; OFString min; OFString sec; OFString temp; E_Condition return_value = EC_Normal; //check existing time and prevent times with value 000000 //if (time == "" || time.find("00:00:00")!=OFString_npos) if (time == "") { return EC_Normal; } // check miminum string if (length <3) //minimum string= h:m { debug_cpp(4,"time '"< separated hour minutes and seconds { hour = time.substr(suffix[0],suffix[1]-suffix[0]+1); min = time.substr(suffix[2],suffix[3]-suffix[2]+1); sec = time.substr(suffix[4],suffix[5]-suffix[4]+1); } // now check the semantic, and derive the hhmmss format... //process the hour if (hour.length()==1) temp = "0" + hour; else if(atoi(hour.c_str())>24) { debug_cpp(4,"error in time '"<59) { debug_cpp(4,"error in time '"<59) { debug_cpp(4,"error in time '"< block "< item "< Assuming dimension "< max_colour) max_colour = temp; } if(max_colour>=1) { //normalise the pixels for(i=0; i11} ** studyMode I study mode (single isotope, etc...) {1->4} ** fileType I (????... always use 0 in here) {0} ** organ I (cardiac, pulmonary, etc...) {1->11} ** ** Return Value : ** A string (char*) containing the study id. This string is allocated ** allocated here, and owned by this function (static...). ** ** Notes : ** This function is basically a copy/paste of DB_BuildImageName in ** DB_init.c. The idea is to make it have the same form, to make ** things a little more standard. AND it also provides a usefull ** way to retreive study type, mode and organ !!! ** NB: for SC objects, this is available from file format version ** 1.00 onwards. **----------------------------------------------------------------------- */ char *buildStudyId( const char *date, const char *time, int studyType, int studyMode, int fileType, int organ ) { int yr, mon, day, hr, min, sec ; static char studyID[64] ; if(sscanf(date, "%d/%d/%d", &yr, &mon, &day ) != 3) return NULL ; if(sscanf(time, "%d:%d:%d", &hr, &min, &sec ) != 3) return NULL ; sprintf( studyID,"%02d%02d%02d%02d%02d%02d%02d%02d%02d%02d", yr, mon, day, hr, min, sec, studyType, studyMode, fileType, organ ); return studyID ; } /* End of "buildStudyId" */