#include "dicomUtils.h" //FUNCTION DECLARATION NECESSARY FOR COPY/PASTE FROM vtkGDCMImageReader // const char *gGetStringValueFromTag(const gdcm::Tag& t, const gdcm::DataSet& ds); const char *gGetStringValueFromTag(const gdcm::Tag& t, const gdcm::DataSet& ds) { static std::string buffer; buffer = ""; // cleanup previous call if( ds.FindDataElement( t ) ) { const gdcm::DataElement& de = ds.GetDataElement( t ); const gdcm::ByteValue *bv = de.GetByteValue(); if( bv ) // Can be Type 2 { buffer = std::string( bv->GetPointer(), bv->GetLength() ); // Will be padded with at least one \0 } } // Since return is a const char* the very first \0 will be considered return buffer.c_str(); }; //GENERAL INDEPENDENT FUNCTION TO CHECK FOR FILE DICOM MODALITY (PLAN, STRUCT, IMAGE) REQUIRES CORRECT PATH const char * gCheckDICOMModality(const char *filename) { gdcm::Reader gR; gR.SetFileName(filename);//str.toAscii().data()); if(!gR.Read()) return("NODCMFILE"); const gdcm::File &file = gR.GetFile(); const gdcm::DataSet &ds = file.GetDataSet(); return(gGetStringValueFromTag( gdcm::Tag(0x0008,0x0060), ds) ); //RETURN MODALITY }; int gCheckDICOMModalityToInt(const char *filename){ const char * tmp = gCheckDICOMModality(filename); //cout<< tmp< sqiPS = gSetupSequence.GetValueAsSQ(); const gdcm::DataSet& gPatSetupNest = sqiPS->GetItem(1).GetNestedDataSet(); //SET PATIENT ORIENTATION STRING strcpy(PatientOrientation, gGetStringValueFromTag(gdcm::Tag(0x0018, 0x5100),gPatSetupNest)); const gdcm::DataElement &gBeamSequence=ds.GetDataElement(gdcm::Tag(0x300a,(IsCarbon==false ? 0x03a2 : 0x00b0))); // (THESE ARE THE BEAMS) gdcm::SmartPointer sqi = gBeamSequence.GetValueAsSQ(); if(!sqi || !sqi->GetNumberOfItems()) { cout<<"usteria" <GetNumberOfItems(); //ALLOCATE BEAM STRUCTURES Beams=new IonBeamProperties* [NumberOfBeams]; memset(Beams,NULL,NumberOfBeams*sizeof(IonBeamProperties *)); //ITERATES ON BEAM NUMBER for(int i=0;iGetItem(i+1); //ITEM START FROM 1 const gdcm::DataSet& gBeamNestedds = item.GetNestedDataSet(); //THIS IS BEAM DATASET //ALLOCATE BEAM CLASS Beams[i]=new IonBeamProperties; //READ BEAM DATA Beams[i]->BeamNumber = (atoi(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x00c0),gBeamNestedds))); //BEAM NUMBER strcpy (Beams[i]->BeamName,(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x00c2),gBeamNestedds))); //BEAM NAME if(IsCarbon==false) strcpy (Beams[i]->SupportType,gGetStringValueFromTag(gdcm::Tag(0x300a, 0x0350),gBeamNestedds));//PATIENT SUPPORT TYPE IMPLEMENT TRICK FOR THE CHAIR else strcpy(Beams[i]->SupportType,"TABLE"); strcpy(Beams[i]->SupportId,gGetStringValueFromTag(gdcm::Tag(0x300a, 0x0352),gBeamNestedds));//PATIENT SUPPORT TYPE IMPLEMENT TRICK FOR THE CHAIR //gBeams[i]->gSetSupportType("CHAIR"); //READ GEOMETRY //READ SEQUENCE OF CONTROL POINTS //v04Build27 DO DIFFERENTLY IF PROTONS OR CARBONS const gdcm::DataElement &gCPSequence=gBeamNestedds.GetDataElement(gdcm::Tag(0x300a,IsCarbon==false ? 0x03a8 : 0x0111)); //FIND ITEM SEQUENCE (THESE ARE THE CONTROL POINTS) gdcm::SmartPointer sqicp = gCPSequence.GetValueAsSQ(); //READ DATA FROM ITEM 1 const gdcm::DataSet& gCPNestedds = sqicp->GetItem(1).GetNestedDataSet(); //GANTRY ANGLE (NEEDED FOR CHAIR ?) Beams[i]->GantryAngle=atoi(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x011e),gCPNestedds)); //ISOCENTER POSITION (THIS IS BEAM POSITION NOT PATIENT ALIGNMENT (VOLUME CENTER) POSITION) double iso[3]; char str[MAXSTRINGLENGHT]; strcpy(str,gGetStringValueFromTag(gdcm::Tag(0x300a, 0x012c),gCPNestedds)); Beams[i]->IsocenterPosition[0]=atof(strtok(str,"\\")); Beams[i]->IsocenterPosition[1]=atof(strtok(NULL,"\\")); Beams[i]->IsocenterPosition[2]=atof(strtok(NULL,"\\")); // Beams[i]->IsocenterPosition[](iso[0],iso[1],iso[2]); //SUPPORT ANGLE (YAW ANGLE OF SUPPORT WITH RESPECT TO FIXED REFERENCE SYSTEM) AROUND Z AXIS (VERTICAL) //POSITIVE COUNTER-CLOCKWISE WHEN VIEWED FROM TOP OF Z-Axis Beams[i]->TableYawAngle=atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x0122),gCPNestedds)); /*if(i==1) gBeams[i]->gSetTableYawAngle(0); */ //TABLE TOP PITCH (ROTATION AROUND X AXIS, i.e. LATERAL AXIS OF TABLE AND BEAM DIRECTION IN FIXED REF SYS) ////v04Build27 IF PROTONS READ IT FROM PLAN; IF CARBONS SET DEFAULT VALUE (0.0) AND FORGET CHAIR if(IsCarbon==false) { gdcm::Attribute <0x300a, 0x0140> pa; pa.SetFromDataElement(gCPNestedds.GetDataElement( pa.GetTag())); Beams[i]->TablePitchAngle =pa.GetValue(); } else (Beams[i]->TablePitchAngle)=(0.0); //v04 Build23 SET SUPPORT TYPE CORRECTLY IF PITCH IS 90 (IT IS CHAIR); DO IT FOR ALL BEAMS // if(i==0) // { if(Beams[i]->TablePitchAngle()>45) // strcpy (Beams[i]->SupportType,("CHAIR")); // } // else if (i>0) // { if(Beams[0]->SupportChair()==true) // Beams[i]->gSetSupportType("CHAIR"); // } //TABLE TOP ROLL (ROTATION AROUND Y AXIS, i.e. LONGITUDINAL AXIS OF TABLE) //v04Build27 IF PROTONS READ IT FROM PLAN; IF CARBONS SET DEFAULT VALUE (0.0) if(IsCarbon==false) { gdcm::Attribute <0x300a, 0x0144> ra; ra.SetFromDataElement(gCPNestedds.GetDataElement(ra.GetTag())); Beams[i]->TableRollAngle =(ra.GetValue()); } else (Beams[i]->TableRollAngle)=(0.0); //WHAT ABOUT TABLE TOP VERTICAL POSITION, LOGITUDINAL AND LATERAL POSITION ? USEFUL SOMEHOW ? // THEY ARE READ AND USED TO REALIZE VOLUME CENTER ALIGNMENT IN ROOM ISOCENTER //FIND PATIENT SETUP SEQUENCE (IT IS UNIQUE FOR ALL BEAMS BUT I DO IT BEAM BY BEAM) //v04Build27 DO NOT LOOK FOR PATIENT SETUPSEQUENCE /* if(!ds.FindDataElement(gdcm::Tag(0x300a,0x0180))) throw(gException("CRITICAL: PATIENT SETUP SEQUENCE NOT FOUND IN RT ION PLAN")); const gdcm::DataElement &gPatientSetupSequence=ds.GetDataElement(gdcm::Tag(0x300a,0x0180)); gdcm::SmartPointer sqpt = gPatientSetupSequence.GetValueAsSQ(); int psuseq=sqpt->GetNumberOfItems(); //CHECK NUMBER OF SETUPSEQUENCE VS BEAM NUMBER //READ DATA FROM ITEM 1 OR BEAM IS MORE PATIENT SETUP SEQUENCE ARE FOUND (ATTENTION !!!!) //v03 Build30: READ TABLE POSITIONS FROM SETUPSEQUENCE const gdcm::DataSet& gPSNestedds = sqpt->GetItem((i+1)<=psuseq ? (i+1) : 1).GetNestedDataSet(); //IF PSTIENT SETUP SEQUENCE < THAN BEAMS USE FIRST */ //v04Build27 READ BEAMS ABSOLUTE GEOMETRY NOT FROM SETUP SEQUENCE BUT FROM CONTROL POINT SEQUENCE (NEW VERSION OIS FOR CARBONS) /*float tablelat=atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x01d6),gPSNestedds)); float tablelong=atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x01d4),gPSNestedds)); float tablevert=atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x01d2),gPSNestedds));*/ float deltatablelat=atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x012a),gCPNestedds)); float deltatablelong=atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x0129),gCPNestedds)); float deltatablevert=atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x0128),gCPNestedds)); //v03 (Build 30) NOW ADD TO TREATMENT BEMA EVENTUAL RELATIVE SHIFTS BETWEEN SETUP SEQUENCE AND STORE IN BEAM STRUCTURE ONLY TO TREATMETN BEAMS // if(i==0) //THIS IS SETUP BEAM DO NOTHING //v04Build26 TREAT ALL BEAMS THE SAME WAY (SET-UP+BEAMS)(NEW VERSION OF OIS) //v04Build27 USE PPS GEOMETRY READ FROM POINT SEQUENCE { Beams[i]->TableLatDispl=(deltatablelat); Beams[i]->TableLongDispl=(deltatablelong); Beams[i]->TableVertDispl=(deltatablevert); } /* else { gBeams[i]->gSetTableLatDispl(tablelat); gBeams[i]->gSetTableLongDispl(tablelong); gBeams[i]->gSetTableVertDispl(tablevert); }*/ //v04Build26 DO NOT ADD DELTASHIFTS (NEW VERSION OF OIS) //v04Build1: CHECK FOR VIRTUAL ISO //ADD DELTASHIFTS TO TABLE DISPLACEMENTS //v04Build26 DO NOT ADD DELTASHIFTS (NEW VERSION OF OIS) /*gBeams[i]->gSetTableLatDispl(tablelat+atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x012a),gCPNestedds))); gBeams[i]->gSetTableLongDispl(tablelong+atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x0129),gCPNestedds))); gBeams[i]->gSetTableVertDispl(tablevert+atof(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x0128),gCPNestedds))); //UPDATE ISOCENTER POSITION ON TREATMENT BEAM USING CT REFERENCE (LONG IS Z COORDINATE; Y IS VERTICAL; Z I) //v04Build26 DO NOT ADD DELTASHIFTS (NEW VERSION OF OIS) gBeams[i]->gSetIsocenterPosition(iso[0]-deltatablelat,iso[1]-deltatablevert,iso[2]-deltatablelong); //SIGNS AND COORDINATE TO BE CHECKED (OK for LONG) } */ //v04Build28 CORRECT ISO POSITION IN CASE OF VIRTUAL ISO /* if(i<0) //DO THIS ONLY FOR TREATMENT BEAMS gBeams[i]->gSetIsocenterPosition(iso[0]-(gBeams[i]->gGetTableLatDispl()-gBeams[0]->gGetTableLatDispl()),iso[1]-(gBeams[i]->gGetTableVertDispl()-gBeams[0]->gGetTableVertDispl()),iso[2]-(gBeams[i]->gGetTableLongDispl()-gBeams[0]->gGetTableLongDispl())); //SIGNS AND COORDINATE TO BE CHECKED (OK for LONG) */ //UPDATE AT FIRST CNAO PATIENT SET TABLE POSITION FROM PLAN AT THE ISOCENTER POSITION FOR EVERY BEAM /* gBeams[i]->gSetTableLatDispl(gBeams[i]->gGetIsocenterPosition()[0]); gBeams[i]->gSetTableLongDispl(gBeams[i]->gGetIsocenterPosition()[1]);gui gBeams[i]->gSetTableVertDispl(gBeams[i]->gGetIsocenterPosition()[2]); */ // FILL gPPS CLASS WITH CURRENT PPS CONFIGURATION UPDATING CONVENTIONS FROM PLAN TO PPS (-90 FOR CONVENTION CT-PPS) // Beams[i]->gGetCurrentPPS()->gSetPPSXPlan(gBeams[i]->gGetTableLatDispl()); // Beams[i]->gGetCurrentPPS()->gSetPPSYPlan(gBeams[i]->gGetTableLongDispl()); // Beams[i]->gGetCurrentPPS()->gSetPPSYOrig(gBeams[i]->gGetTableLongDispl()); //SAVE ALSO AN ORIGINAL COPY FROM PLAN OF LONGITUFINAL POSITION (FOR INDEXING DIALOG) // Beams[i]->gGetCurrentPPS()->gSetPPSZPlan(gBeams[i]->gGetTableVertDispl()); // //v03 Build35: ADD TO TREATMENT BEAMS YAW THE YAW VALUE OF SETUP BEAM TO RECOVER ABSOLUTE VALUES FROM RELATIVE ONES AS SENT BY OIS if(i>0) // THIS IS TREATMENT BEAM { //v04Build03 SET PROPER DELTA ANGLES FOR BEAM ROTATION (IF DELTA IS NEGATIVE SET IT NEGATIVE) double yaw=Beams[i]->TableYawAngle; if(yaw>180) yaw=yaw-360; //V04Build26 NEW VERSION OF MOSAIQ OIS DO NOT USE RELATIVE ANGLES //yaw=yaw+gBeams[0]->gGetTableYawAngle(); Beams[i]->TableYawAngle=(yaw); double pitch=Beams[i]->TablePitchAngle; if(pitch>180) pitch=pitch-360; //V04Build26 NEW VERSION OF MOSAIQ OIS DO NOT USE RELATIVE ANGLES //pitch=pitch+gBeams[0]->gGetTablePitchAngle(); //v04Build23 SET ALL PITCH AT 90 (NOT STABLE) IT IS FOR THE CHAIR if(pitch>150) pitch=90; Beams[i]->TablePitchAngle=(pitch); double roll=Beams[i]->TableRollAngle; if(roll>180) roll=roll-360; //V04Build26 NEW VERSION OF MOSAIQ OIS DO NOT USE RELATIVE ANGLES //roll=roll+gBeams[0]->gGetTableRollAngle(); Beams[i]->TableRollAngle=(roll); } //HERE 90 DEGREES IS SUMMED IN ORDER TO SOLVE TEH AMBIGUITY BETWEEN TPS REFERENCE AND ROOM/ODEVIS REFERENCE //0 DEGREE YAW IN TPS BECOMES +90 YAW IN ROOM/PPS MEANING BEAM IS FROM LATERAL //THIS WE DO IT JUST FOR THE USER INTERFACE DISPLAYING DEGREES OF YAW ROTATION EXPECTED OR TO BE INSERTED IN THE PPS //v04Build24 ADJUST PPS YAW ANGLE FOR PPS AND CHAIR // double yaw=gBeams[0]->gIsSupportChair()==false ? gBeams[i]->gGetTableYawAngle()+90.0 : gBeams[i]->gGetTableYawAngle()-90.0; // //NOW SET +- 180 YAW ROTATION CONVENTION // if(yaw>180) // yaw=yaw-360; // gBeams[i]->gGetCurrentPPS()->gSetPPSRotateDegreePlan(yaw); //CONVERSION FROM CT BASE PLAN TO PPS (ANGLE RANGES -180 +180) // // //v04Build23 ATTENZIONE ! // if(gBeams[0]->gIsSupportChair()==false) gBeams[i]->gGetCurrentPPS()->gSetPPSPitchDegreePlan(gBeams[i]->gGetTablePitchAngle()); // else gBeams[i]->gGetCurrentPPS()->gSetPPSPitchDegreePlan(gBeams[i]->gGetTablePitchAngle()-90); //NEED TO SUBTRACT 90 DEGRESS FOR PPS CONFIGURATION : THERE IS THE CHAIR // // gBeams[i]->gGetCurrentPPS()->gSetPPSRollDegreePlan(gBeams[i]->gGetTableRollAngle()); // // // // //SET CURRENT PPS CONFIGURATION WITH PLAN CONFIGURATION (THERE I TAKE CARE OF THE CHAIR) // gBeams[i]->gGetCurrentPPS()->gSetPlanConfig(); /* //UPDATE TABLE DISPLACEMENT ACCORDING TO SIEMENS TPS USING THE ISOCENTER CO-ORDINATESIF DEFINE W.R.T. INDEXING POINT //MIGHT NEED TO SUBTRACT COORDINATES OF STRUCTURE VOLUMECENTER IF RELATIVE REFERENCE SYSTEM IS NOT DEFINED IN TPS gBeams[i]->gSetTableLatDispl(gBeams[i]->gGetIsocenterPosition()[0]); gBeams[i]->gSetTableLongDispl(gBeams[i]->gGetIsocenterPosition()[1]); gBeams[i]->gSetTableVertDispl(gBeams[i]->gGetIsocenterPosition()[2]);*/ // gBeams[i]->gSetIsocenterPosition(iso[0]- gBeams[i]->gGetCurrentPPS()->getPPSXPlan(),iso[1]- gBeams[i]->gGetCurrentPPS()->getPPSYPlan(),iso[2]- gBeams[i]->gGetCurrentPPS()->getPPSZPlan()); } return true; };