diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100755 new mode 100644 index 08d26a1..3cae699 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,132 +1,164 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.22) -IF (NOT CMAKE_BUILD_TYPE) - SET (CMAKE_BUILD_TYPE "Release" CACHE STRING - "Choose the type of build, options are: Debug Release - RelWithDebInfo MinSizeRel." FORCE) -ENDIF () - -project(gLocalize) +project(gLocalize LANGUAGES CXX) set(TRG gLocalize) - -SET(SOURCE_FILES ${CMAKE_SOURCE_DIR}) - -INCLUDE_DIRECTORIES( - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_SOURCE_DIR} - ${SOURCE_FILES} - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - ${CMAKE_BINARY_DIR} - ) - - -set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(CMAKE_AUTOMOC ON) - -find_package(HDF5 REQUIRED COMPONENTS C CXX HL) -INCLUDE_DIRECTORIES(${HDF5_INCLUDE_DIRS}) - -find_package(Qt6Core REQUIRED) -find_package(Qt6Gui REQUIRED) -find_package(Qt6Widgets REQUIRED) -find_package(Qt6Network REQUIRED) -find_package(Qt6Xml REQUIRED) -find_package(Qt6Svg REQUIRED) - -include_directories( - ${Qt6Core_INCLUDE_DIRS} - ${Qt65Gui_INCLUDE_DIRS} - ${Qt6Widgets_INCLUDE_DIRS} - ${Qt6Network_INCLUDE_DIRS} - ${Qt6Xml_INCLUDE_DIRS} - ${Qt6Svg_INCLUDE_DIRS} - ) - - -find_package(ITK REQUIRED) -include(${ITK_USE_FILE}) -INCLUDE_DIRECTORIES(${ITK_INCLUDE_DIRS}) - -find_package(VTK REQUIRED) -INCLUDE_DIRECTORIES(${VTK_INCLUDE_DIRS}) - -FIND_PACKAGE(GDCM REQUIRED) -include(${GDCM_USE_FILE}) -INCLUDE_DIRECTORIES(${GDCM_INCLUDE_DIRS}) - - -set(SRCS - ${SOURCE_FILES}/main.cpp - ${SOURCE_FILES}/mainw.cpp - ${SOURCE_FILES}/mainw.h - ${SOURCE_FILES}/connectITKVTK.h - ${SOURCE_FILES}/dicomUtils.cpp - ${SOURCE_FILES}/dicomUtils.h - ${SOURCE_FILES}/gPatientRTGeneralInfos.h - ${SOURCE_FILES}/gRen.h - ${SOURCE_FILES}/gRen.cpp - ${SOURCE_FILES}/gLoadPatient.h - ${SOURCE_FILES}/gLoadPatient.cpp - ${SOURCE_FILES}/gLocalize.h - ${SOURCE_FILES}/gLocalize.cpp - ${SOURCE_FILES}/gSkullRemoval.h - ${SOURCE_FILES}/gSkullRemoval.cpp - ${SOURCE_FILES}/itkQtAdaptor.h - ) - -set(HDR - ${SOURCE_FILES}/mainw.h - ${SOURCE_FILES}/connectITKVTK.h - ${SOURCE_FILES}/dicomUtils.h - ${SOURCE_FILES}/gPatientRTGeneralInfos.h - ${SOURCE_FILES}/gRen.h - ${SOURCE_FILES}/gLoadPatient.h - ${SOURCE_FILES}/gLocalize.h - ${SOURCE_FILES}/gSkullRemoval.h - ${SOURCE_FILES}/itkQtAdaptor.h - ) - - -SET(RES ${PROJECT_SOURCE_DIR}/images.qrc) -QT6_ADD_RESOURCES(RES_RCC ${RES}) - - -add_executable(${TRG} - ${SRCS} - ${RES_RCC} -) - -set(GDCM_PREFIX "/usr/local/gdcm" CACHE PATH "GDCM install prefix") -set(GDCM_LIBDIR "${GDCM_PREFIX}/lib") -if(EXISTS "${GDCM_PREFIX}/lib64") - set(GDCM_LIBDIR "${GDCM_PREFIX}/lib64") +# --- Build type default --- +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) endif() -set_property(TARGET gLocalize PROPERTY BUILD_RPATH "${GDCM_LIBDIR}") -set_property(TARGET gLocalize PROPERTY INSTALL_RPATH "${GDCM_LIBDIR}") +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) -target_link_options(gLocalize PRIVATE "-Wl,-rpath,/usr/local/gdcm/lib") +# Qt: enable automoc/uic/rcc +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) -target_link_libraries(${TRG} - PRIVATE - Qt6::Network +# Prefer sane RPATH behavior without hardcoding /usr/local paths. +# If dependencies are properly installed in system paths, no RPATH is needed. +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +set(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE) + +# --- Dependencies --- +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Network Xml Svg OpenGL OpenGLWidgets) + +# HDF5: use imported targets if provided, otherwise fall back to variables. +find_package(HDF5 REQUIRED COMPONENTS C CXX HL) + +# ITK config package generally exports ITK_LIBRARIES as imported targets. +find_package(ITK REQUIRED) + +# VTK: list the modules actually used by the code (Qt widget + volume rendering + widgets + imaging). +find_package(VTK REQUIRED + COMPONENTS + CommonCore + CommonDataModel + CommonTransforms + FiltersCore + FiltersGeneral + FiltersGeometry + FiltersModeling + FiltersSources + ImagingCore + ImagingGeneral + ImagingHybrid + InteractionStyle + InteractionWidgets + RenderingCore + RenderingOpenGL2 + RenderingVolumeOpenGL2 + RenderingAnnotation + RenderingFreeType + RenderingImage + RenderingQt + GUISupportQt +) + +# GDCM: prefer config targets when available. +find_package(GDCM REQUIRED) + +# --- Sources --- +set(SRCS + main.cpp + mainw.cpp + dicomUtils.cpp + gRen.cpp + gLoadPatient.cpp + gLocalize.cpp + gSkullRemoval.cpp +) + +set(HDR + mainw.h + connectITKVTK.h + dicomUtils.h + gPatientRTGeneralInfos.h + gRen.h + gLoadPatient.h + gLocalize.h + gSkullRemoval.h + itkQtAdaptor.h +) + +qt_add_resources(RES_RCC images.qrc) + +add_executable(${TRG} + ${SRCS} + ${HDR} + ${RES_RCC} +) + +# Do NOT define QT_NO_KEYWORDS here: this project uses Qt's 'signals/slots/emit' +# keywords in headers, and disabling them breaks moc. + +target_include_directories(${TRG} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) + +# --- Link libraries --- +target_link_libraries(${TRG} + PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets + Qt6::OpenGL + Qt6::OpenGLWidgets Qt6::Network + Qt6::Xml Qt6::Svg - ${VTK_LIBRARIES} - ${ITK_LIBRARIES} - ${GDCM_LIBRARIES} - m - ${HDF5_CXX_LIBRARIES} - ${HDF5_LIBRARIES} - ) +# ITK: imported targets are typically in ITK_LIBRARIES +if(DEFINED ITK_LIBRARIES) + target_link_libraries(${TRG} PRIVATE ${ITK_LIBRARIES}) +endif() + +# VTK +target_link_libraries(${TRG} PRIVATE ${VTK_LIBRARIES}) +# VTK 9.x + --as-needed: ensure GUISupportQt is explicitly linked when using QVTKOpenGLNativeWidget +if(TARGET VTK::GUISupportQt) + target_link_libraries(${TRG} PRIVATE VTK::GUISupportQt) +endif() +# VTK module auto-init (needed especially when VTK is built static or with some build options) +if(COMMAND vtk_module_autoinit) + vtk_module_autoinit( + TARGETS ${TRG} + MODULES ${VTK_LIBRARIES} + ) +endif() +# GDCM (prefer targets; otherwise fallback to variables) +if(TARGET GDCM::gdcmMSFF) + target_link_libraries(${TRG} PRIVATE GDCM::gdcmMSFF) +elseif(DEFINED GDCM_LIBRARIES) + target_link_libraries(${TRG} PRIVATE ${GDCM_LIBRARIES}) +endif() +# HDF5 (prefer targets; otherwise fallback to variables) +if(TARGET hdf5::hdf5_cpp) + target_link_libraries(${TRG} PRIVATE hdf5::hdf5_cpp) +endif() +if(TARGET hdf5::hdf5_hl_cpp) + target_link_libraries(${TRG} PRIVATE hdf5::hdf5_hl_cpp) +endif() +if(DEFINED HDF5_LIBRARIES) + target_link_libraries(${TRG} PRIVATE ${HDF5_LIBRARIES}) +endif() +if(DEFINED HDF5_CXX_LIBRARIES) + target_link_libraries(${TRG} PRIVATE ${HDF5_CXX_LIBRARIES}) +endif() + +# math lib (Linux) +target_link_libraries(${TRG} PRIVATE m) + +# Helpful warnings (tweak as you like) +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(${TRG} PRIVATE -Wall -Wextra -Wpedantic) +endif() diff --git a/connectITKVTK.h b/connectITKVTK.h old mode 100755 new mode 100644 diff --git a/dicomUtils.cpp b/dicomUtils.cpp old mode 100755 new mode 100644 index b33d25b..2784a87 --- a/dicomUtils.cpp +++ b/dicomUtils.cpp @@ -1,26 +1,118 @@ #include "dicomUtils.h" +#include +#include +#include + +using std::cout; +using std::endl; + +namespace { + +static inline std::string Trim(std::string s) +{ + auto notSpace = [](unsigned char c) { return !std::isspace(c); }; + s.erase(s.begin(), std::find_if(s.begin(), s.end(), notSpace)); + s.erase(std::find_if(s.rbegin(), s.rend(), notSpace).base(), s.end()); + return s; +} + +static inline int ParseIntOr(const std::string& s, int fallback) +{ + const std::string t = Trim(s); + if (t.empty()) return fallback; + try + { + size_t idx = 0; + int v = std::stoi(t, &idx); + (void)idx; + return v; + } + catch (...) + { + return fallback; + } +} + +static inline double ParseDoubleOr(const std::string& s, double fallback) +{ + const std::string t = Trim(s); + if (t.empty()) return fallback; + try + { + size_t idx = 0; + double v = std::stod(t, &idx); + (void)idx; + return v; + } + catch (...) + { + return fallback; + } +} + +static inline int GetTagIntOr(const gdcm::Tag& t, const gdcm::DataSet& ds, int fallback) +{ + return ParseIntOr(gGetStringValueFromTagStr(t, ds), fallback); +} + +static inline double GetTagDoubleOr(const gdcm::Tag& t, const gdcm::DataSet& ds, double fallback) +{ + return ParseDoubleOr(gGetStringValueFromTagStr(t, ds), fallback); +} + +// Parse a DICOM "DS" multi-value string with 3 components separated by '\\'. +// Returns {BADVALUE,BADVALUE,BADVALUE} if parsing fails. +std::array Parse3Doubles(const std::string& s) +{ + std::array out{BADVALUE, BADVALUE, BADVALUE}; + if (s.empty()) return out; + + std::stringstream ss(s); + std::string token; + for (int i = 0; i < 3; ++i) + { + if (!std::getline(ss, token, '\\')) return out; + try + { + out[static_cast(i)] = std::stod(token); + } + catch (...) + { + return {BADVALUE, BADVALUE, BADVALUE}; + } + } + return out; +} + +} // namespace + //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) +const char *gGetStringValueFromTag(const gdcm::Tag& t, const gdcm::DataSet& ds) { + // NOTE: legacy API returning pointer into a static buffer. + // Keep for older call sites. 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 + buffer = gGetStringValueFromTagStr(t, ds); return buffer.c_str(); -}; +} + +std::string gGetStringValueFromTagStr(const gdcm::Tag& t, const gdcm::DataSet& ds) +{ + if (!ds.FindDataElement(t)) return {}; + const gdcm::DataElement& de = ds.GetDataElement(t); + const gdcm::ByteValue* bv = de.GetByteValue(); + if (!bv) return {}; + + std::string s(bv->GetPointer(), bv->GetLength()); + // DICOM strings are often space/\0 padded. + while (!s.empty() && (s.back() == '\0' || s.back() == ' ' || s.back() == '\r' || s.back() == '\n' || s.back() == '\t')) + s.pop_back(); + return s; +} //GENERAL INDEPENDENT FUNCTION TO CHECK FOR FILE DICOM MODALITY (PLAN, STRUCT, IMAGE) REQUIRES CORRECT PATH const char * gCheckDICOMModality(const char *filename) @@ -55,27 +147,20 @@ int gCheckDICOMModalityToInt(const char *filename){ -IonBeamProperties::IonBeamProperties(){ - BeamNumber = BADVALUE; - BeamName = new char [MAXSTRINGLENGHT]; - SupportId = new char [MAXSTRINGLENGHT]; - SupportType = new char [MAXSTRINGLENGHT]; - GantryAngle = BADVALUE; - IsocenterPosition = new double [3]; - TablePitchAngle= BADVALUE; +IonBeamProperties::IonBeamProperties() +{ + BeamNumber = BADVALUE; + BeamName.clear(); + SupportId.clear(); + SupportType.clear(); + GantryAngle = BADVALUE; + IsocenterPosition = {BADVALUE, BADVALUE, BADVALUE}; + TablePitchAngle = BADVALUE; TableRollAngle = BADVALUE; - TableYawAngle= BADVALUE; - TableLatDispl = BADVALUE; - TableLongDispl= BADVALUE; - TableVertDispl= BADVALUE; - -} - -IonBeamProperties::~IonBeamProperties(){ -delete[] BeamName; -delete[] SupportId; -delete[] IsocenterPosition; -delete[] SupportType; + TableYawAngle = BADVALUE; + TableLatDispl = BADVALUE; + TableLongDispl = BADVALUE; + TableVertDispl = BADVALUE; } @@ -86,9 +171,9 @@ void IonBeamProperties::PrintSelf(){ cout<<" |_ SupportType = " < sqiPS = gSetupSequence.GetValueAsSQ(); - const gdcm::DataSet& gPatSetupNest = sqiPS->GetItem(1).GetNestedDataSet(); - //SET PATIENT ORIENTATION STRING - strcpy(PatientOrientation, gGetStringValueFromTag(gdcm::Tag(0x0018, 0x5100),gPatSetupNest)); + // Patient orientation comes from Patient Setup Sequence, when present. + PatientOrientation.clear(); + if (ds.FindDataElement(gdcm::Tag(0x300a,0x0180))) + { + const gdcm::DataElement &gSetupSequence=ds.GetDataElement(gdcm::Tag(0x300a,0x0180)); + gdcm::SmartPointer sqiPS = gSetupSequence.GetValueAsSQ(); + if (sqiPS && sqiPS->GetNumberOfItems() >= 1) + { + const gdcm::DataSet& gPatSetupNest = sqiPS->GetItem(1).GetNestedDataSet(); + PatientOrientation = gGetStringValueFromTagStr(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 *)); - memset(Beams, 0, NumberOfBeams * sizeof(IonBeamProperties*)); + const int expectedBeams = static_cast(sqi->GetNumberOfItems()); + NumberOfBeams = expectedBeams; + + // Own and manage beams with RAII. + Beams.clear(); + Beams.reserve(static_cast(expectedBeams)); //ITERATES ON BEAM NUMBER - for(int i=0;iGetItem(i+1); //ITEM START FROM 1 + for(int i = 0; i < expectedBeams; i++) + { + const gdcm::Item & item = sqi->GetItem(i+1); //ITEM START FROM 1 const gdcm::DataSet& gBeamNestedds = item.GetNestedDataSet(); //THIS IS BEAM DATASET - //ALLOCATE BEAM CLASS - Beams[i]=new IonBeamProperties; + auto beam = std::make_unique(); + IonBeamProperties* b = beam.get(); + //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 + b->BeamNumber = GetTagIntOr(gdcm::Tag(0x300a, 0x00c0), gBeamNestedds, 0); + b->BeamName = gGetStringValueFromTagStr(gdcm::Tag(0x300a, 0x00c2), gBeamNestedds); - 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"); + if(IsCarbon == false) + b->SupportType = gGetStringValueFromTagStr(gdcm::Tag(0x300a, 0x0350), gBeamNestedds); + else + b->SupportType = "TABLE"; - strcpy(Beams[i]->SupportId,gGetStringValueFromTag(gdcm::Tag(0x300a, 0x0352),gBeamNestedds));//PATIENT SUPPORT TYPE IMPLEMENT TRICK FOR THE CHAIR + b->SupportId = gGetStringValueFromTagStr(gdcm::Tag(0x300a, 0x0352), gBeamNestedds); //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) + const gdcm::Tag cpTag(0x300a, IsCarbon==false ? 0x03a8 : 0x0111); + if (!gBeamNestedds.FindDataElement(cpTag)) + { + // Skip this beam if control points are missing. + continue; + } + const gdcm::DataElement &gCPSequence=gBeamNestedds.GetDataElement(cpTag); gdcm::SmartPointer sqicp = gCPSequence.GetValueAsSQ(); - - //READ DATA FROM ITEM 1 + if (!sqicp || sqicp->GetNumberOfItems() < 1) + { + continue; + } const gdcm::DataSet& gCPNestedds = sqicp->GetItem(1).GetNestedDataSet(); //GANTRY ANGLE (NEEDED FOR CHAIR ?) - Beams[i]->GantryAngle=atoi(gGetStringValueFromTag(gdcm::Tag(0x300a, 0x011e),gCPNestedds)); + b->GantryAngle = GetTagIntOr(gdcm::Tag(0x300a, 0x011e), gCPNestedds, 0); - //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,"\\")); + // ISOCENTER POSITION (this is beam position, not patient/volume center). + // DICOM tag (300A,012C) is a multi-value DS string: "x\\y\\z". + { + const std::string isoStr = gGetStringValueFromTagStr(gdcm::Tag(0x300a, 0x012c), gCPNestedds); + const auto iso = Parse3Doubles(isoStr); + b->IsocenterPosition[0] = iso[0]; + b->IsocenterPosition[1] = iso[1]; + b->IsocenterPosition[2] = iso[2]; + } // 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)); + b->TableYawAngle = static_cast(GetTagDoubleOr(gdcm::Tag(0x300a, 0x0122), gCPNestedds, 0.0)); /*if(i==1) gBeams[i]->gSetTableYawAngle(0); */ @@ -322,10 +408,10 @@ RTPlan::~RTPlan(){ if(IsCarbon==false) { gdcm::Attribute <0x300a, 0x0140> pa; pa.SetFromDataElement(gCPNestedds.GetDataElement( pa.GetTag())); - Beams[i]->TablePitchAngle =pa.GetValue(); + b->TablePitchAngle = pa.GetValue(); } else - (Beams[i]->TablePitchAngle)=(0.0); + b->TablePitchAngle = 0.0; //v04 Build23 SET SUPPORT TYPE CORRECTLY IF PITCH IS 90 (IT IS CHAIR); DO IT FOR ALL BEAMS // if(i==0) @@ -342,10 +428,10 @@ RTPlan::~RTPlan(){ if(IsCarbon==false) { gdcm::Attribute <0x300a, 0x0144> ra; ra.SetFromDataElement(gCPNestedds.GetDataElement(ra.GetTag())); - Beams[i]->TableRollAngle =(ra.GetValue()); + b->TableRollAngle = ra.GetValue(); } else - (Beams[i]->TableRollAngle)=(0.0); + b->TableRollAngle = 0.0; @@ -371,17 +457,17 @@ RTPlan::~RTPlan(){ 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)); + float deltatablelat = static_cast(GetTagDoubleOr(gdcm::Tag(0x300a, 0x012a), gCPNestedds, 0.0)); + float deltatablelong = static_cast(GetTagDoubleOr(gdcm::Tag(0x300a, 0x0129), gCPNestedds, 0.0)); + float deltatablevert = static_cast(GetTagDoubleOr(gdcm::Tag(0x300a, 0x0128), gCPNestedds, 0.0)); //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); + { b->TableLatDispl = deltatablelat; + b->TableLongDispl = deltatablelong; + b->TableVertDispl = deltatablevert; } /* else { gBeams[i]->gSetTableLatDispl(tablelat); @@ -426,29 +512,29 @@ RTPlan::~RTPlan(){ //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; + double yaw = b->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); + b->TableYawAngle = yaw; - double pitch=Beams[i]->TablePitchAngle; + double pitch = b->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); + b->TablePitchAngle = pitch; - double roll=Beams[i]->TableRollAngle; + double roll = b->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); + b->TableRollAngle = roll; } //HERE 90 DEGREES IS SUMMED IN ORDER TO SOLVE TEH AMBIGUITY BETWEEN TPS REFERENCE AND ROOM/ODEVIS REFERENCE @@ -480,6 +566,11 @@ RTPlan::~RTPlan(){ // gBeams[i]->gSetIsocenterPosition(iso[0]- gBeams[i]->gGetCurrentPPS()->getPPSXPlan(),iso[1]- gBeams[i]->gGetCurrentPPS()->getPPSYPlan(),iso[2]- gBeams[i]->gGetCurrentPPS()->getPPSZPlan()); - } - return true; + // Store the parsed beam. + Beams.push_back(std::move(beam)); + } + + // Keep legacy field in sync with actual parsed beams. + NumberOfBeams = static_cast(Beams.size()); + return true; }; diff --git a/dicomUtils.h b/dicomUtils.h old mode 100755 new mode 100644 index cbfdd3c..d50fa4b --- a/dicomUtils.h +++ b/dicomUtils.h @@ -11,7 +11,12 @@ #include #include -using namespace std; +#include +#include +#include +#include + +// Avoid `using namespace` in headers; keep symbols qualified. #define MAXSTRINGLENGHT 255 #define BADVALUE -9999 @@ -23,7 +28,10 @@ enum{ NODCMFILE }; +// Legacy helper: returns a pointer to an internal static buffer. +// Prefer gGetStringValueFromTagStr() in new code. const char *gGetStringValueFromTag(const gdcm::Tag& t, const gdcm::DataSet& ds); +std::string gGetStringValueFromTagStr(const gdcm::Tag& t, const gdcm::DataSet& ds); const char * gCheckDICOMModality(const char *filename); int gCheckDICOMModalityToInt(const char *filename); @@ -32,20 +40,20 @@ class IonBeamProperties{ public: IonBeamProperties(); -~IonBeamProperties(); +~IonBeamProperties() = default; -int BeamNumber; -char* BeamName; -char* SupportType; -char* SupportId; -int GantryAngle; -double* IsocenterPosition; -float TableYawAngle; -float TablePitchAngle; -float TableRollAngle; -float TableLatDispl; -float TableLongDispl; -float TableVertDispl; +int BeamNumber; +std::string BeamName; +std::string SupportType; +std::string SupportId; +int GantryAngle; +std::array IsocenterPosition; +float TableYawAngle; +float TablePitchAngle; +float TableRollAngle; +float TableLatDispl; +float TableLongDispl; +float TableVertDispl; void PrintSelf(); }; @@ -55,41 +63,45 @@ void PrintSelf(); class RTPlan { public: - char* PatientName; - char* PatientID; - char* PatientSex; - char* PatientAge; - char* PatientBirthDate; - char* SeriesNumber; - char* SeriesDescription; - char* StudyID; - char* StudyDescription; - char* Modality; + std::string PatientName; + std::string PatientID; + std::string PatientSex; + std::string PatientAge; + std::string PatientBirthDate; + std::string SeriesNumber; + std::string SeriesDescription; + std::string StudyID; + std::string StudyDescription; + std::string Modality; - double* IsocenterPosition; - double* VolumeCenter; - int BeamNumber; - char* BeamName; - double TableRollAngle; - double TableYawAngle; - double TablePitchAngle; - double TableLatDispl; - double TableVertDispl; - double TableLongDispl; - char* SupportType; - char* SupportId; - char* PatientOrientation; - int NumberOfBeams; + std::array IsocenterPosition; + std::array VolumeCenter; + int BeamNumber; + std::string BeamName; + double TableRollAngle; + double TableYawAngle; + double TablePitchAngle; + double TableLatDispl; + double TableVertDispl; + double TableLongDispl; + std::string SupportType; + std::string SupportId; + std::string PatientOrientation; + int NumberOfBeams; - IonBeamProperties** Beams; + // Owns all beams. + std::vector> Beams; + + // Last error message set by fillRTPlan() when it returns false. + std::string LastError; - ~RTPlan(); + ~RTPlan() = default; RTPlan(); void PrintSelf(); - bool fillRTPlan( char *filertionplan); + bool fillRTPlan(const char *filertionplan); }; #endif \ No newline at end of file diff --git a/gLoadPatient.cpp b/gLoadPatient.cpp old mode 100755 new mode 100644 index 25bd8d7..e4edba7 --- a/gLoadPatient.cpp +++ b/gLoadPatient.cpp @@ -1,436 +1,441 @@ -#include "gLoadPatient.h" - - -/*_________________________________________________________- - -Patient loader for gLocalize. -no wrkDir behaviour. just load data -____________________________________________________________*/ - - -gLoadPatient::gLoadPatient(){ - patientInfos = new gPatientRTGeneralInfos; - readRT=0; - m_rtIsocenter=0; - initialized=false; - virtualIso=false; - //cout<< "gLoadPatient::gLoadPatient" <parse(p_loadDir); - - if(patientInfos->CTfiles .size()!=0){ - cout<< "this->load2VTK(patientInfos)" <rtIonPlanPath.isEmpty()){ - readRT = new RTPlan; - readRT->fillRTPlan(patientInfos->rtIonPlanPath.toLatin1().data() ); - m_rtIsocenter = new double[3]; - if (readRT->NumberOfBeams > 0){ - memcpy(m_rtIsocenter, readRT->Beams[0]->IsocenterPosition, 3* sizeof(double) ); - if (strncmp (readRT->PatientOrientation,"HFS",3) == 0 ) - m_patientOrientation = SUPINE; - else if (strncmp (readRT->PatientOrientation,"HFP",3) == 0 ) - m_patientOrientation = PRONE; - else m_patientOrientation = NOTDEFINED; - - emit loadedRTIso(m_rtIsocenter); - //for (int ii=0;iiNumberOfBeams; ii++){ - // cout<< readRT->Beams[ii]->IsocenterPosition[0]<<" "; - // cout<< readRT->Beams[ii]->IsocenterPosition[1]<<" "; - // cout<< readRT->Beams[ii]->IsocenterPosition[2]<<" "<NumberOfBeams <Beams[0]->IsocenterPosition[0] <Beams[2]->IsocenterPosition[0]<Beams[0]->IsocenterPosition[1] <Beams[2]->IsocenterPosition[1]<Beams[0]->IsocenterPosition[2] <Beams[2]->IsocenterPosition[2]<NumberOfBeams > 2 && - (readRT->Beams[0]->IsocenterPosition[0] != readRT->Beams[2]->IsocenterPosition[0] || - readRT->Beams[0]->IsocenterPosition[1] != readRT->Beams[2]->IsocenterPosition[1] || - readRT->Beams[0]->IsocenterPosition[2] != readRT->Beams[2]->IsocenterPosition[2]) - ) - virtualIso=true; - else - virtualIso=false; - //cout << "virtual Iso: "<loadDICOM(patientInfos->CTfiles); - this->connectToVTK(); -// this->changeRef(GOTSREF); - /* use DCM as default */ - this->changeRef(DCMREF); - //trueOffset_rot_prev=trueOffset_rot; - - - //this->load2VTK(patientInfos); - } else { - //DO NOTHING. THE folderisempty signal was already emitted. - } - cout<< " gLoadPatient::load _ END" <SetDirection( /*imageDir*/directionToWCS ); - myImage->SetOrigin( trueOffset_rot ); - myImage->Update(); - - in->SetInput(myImage); - this->connectToVTK(); - this->actualizeOut(); - - /*calculate deltas for marker representation update*/ - if(initialized){ - emit referenceChange( - trueOffset_rot_prev[0]-trueOffset_rot[0], - trueOffset_rot_prev[1]-trueOffset_rot[1], - trueOffset_rot_prev[2]-trueOffset_rot[2]); - trueOffset_rot_prev=trueOffset_rot; - } else { - trueOffset_rot_prev=trueOffset_rot; - initialized=true; - } - -} - -void gLoadPatient::loadDICOM(std::vector CTfilenames){ - - myImage = myImageType::New(); - rDICOM = itk::ImageSeriesReader::New(); - iGDCMimage = itk::GDCMImageIO::New(); - myDICOMseries = itk::GDCMSeriesFileNames::New(); - - rDICOM->SetImageIO(iGDCMimage); - rDICOM->SetFileNames(CTfilenames); - // rDICOM->SetReverseOrder(true); - try{ - rDICOM->Update(); - } - catch (itk::ExceptionObject &ex){ - std::cout << ex << std::endl; - return; // error! - } - - myImage=rDICOM->GetOutput(); - imageDir = myImage->GetDirection( ); - origin = myImage->GetOrigin( ); - sizeOfImage = myImage->GetLargestPossibleRegion().GetSize(); - -} - -void gLoadPatient::connectToVTK(){ - - - out = vtkSmartPointer::New(); - - in = itk::VTKImageExport ::New(); - in->SetInput(myImage); - - out->SetUpdateInformationCallback(in->GetUpdateInformationCallback()); - out->SetPipelineModifiedCallback(in->GetPipelineModifiedCallback()); - out->SetWholeExtentCallback(in->GetWholeExtentCallback()); - out->SetSpacingCallback(in->GetSpacingCallback()); - out->SetOriginCallback(in->GetOriginCallback()); - out->SetScalarTypeCallback(in->GetScalarTypeCallback()); - out->SetNumberOfComponentsCallback(in->GetNumberOfComponentsCallback()); - out->SetPropagateUpdateExtentCallback(in->GetPropagateUpdateExtentCallback()); - out->SetUpdateDataCallback(in->GetUpdateDataCallback()); - out->SetDataExtentCallback(in->GetDataExtentCallback()); - out->SetBufferPointerCallback(in->GetBufferPointerCallback()); - out->SetCallbackUserData(in->GetCallbackUserData()); - -} - -void gLoadPatient::actualizeOut(){ - - //in->Update(); - out->Update(); - //vol3D = vtkSmartPointer ::New(); - //vol3D->DeepCopy(out->GetOutput()); - //vol3D->Update(); - vol3D = out->GetOutput(); - cout<GetSpacing()[0] <<" "<GetSpacing()[1] <<" "<GetSpacing()[2] <clearInfo() " <clearInfo(); - - std::cout<<"parsing dir: "<c_str()).fileName().split(".").last() != QString("raw") ) - patientInfos->filenames.push_back( it->c_str() ); - } - patientInfos->filenames.size() == 0 ? result = PARSER_FOLDER_EMPTY : result = NOERRORS; - } else { - result=PARSER_FOLDER_NOTEXISTING; - }; - - cout << " patientInfos->filenames.size() "<filenames.size() <filenames.size()-1);i>-1;i--) - { - //cout << gCheckDICOMModalityToInt(patientInfos->filenames.at(i).c_str()) <SetFileName (patientInfos->filenames.at(i).c_str()); - - if( !fileReader->Read() ) { - cout<< "Error reading file: "<< patientInfos->filenames.at(i).data() <filenames.erase(patientInfos->filenames.begin()+i); - continue; - } - - //msStructRTIonPlan.SetFromFile( fileReader->GetFile() ); - //if( msStructRTIonPlan == gdcm::MediaStorage::RTIonPlanStorage ) - if( gCheckDICOMModalityToInt(patientInfos->filenames.at(i).c_str()) == RTPLAN ) - { - if(!patientInfos->rtIonPlanPath.isEmpty()) { -// result=PARSER_RTPLAN_NOTUNIQUE; - continue; - } else { - const gdcm::File &file = fileReader->GetFile(); - const gdcm::DataSet &ds = file.GetDataSet(); - //PATIENT NAME - strcpy( patientInfos->PatientName,gGetStringValueFromTag( gdcm::Tag(0x0010,0x0010), ds)); - // // // PATIENT ID For ex: DICOM (0010,0020) = 1933197 - strcpy( patientInfos->PatientID , gGetStringValueFromTag( gdcm::Tag(0x0010,0x0020), ds)); - // // // PATIENT AGE For ex: DICOM (0010,1010) = 031Y - strcpy( patientInfos->PatientAge , gGetStringValueFromTag( gdcm::Tag(0x0010,0x1010), ds)) ; - // // // PATIENT SEX For ex: DICOM (0010,0040) = M - strcpy( patientInfos->PatientSex, gGetStringValueFromTag( gdcm::Tag(0x0010,0x0040), ds) ); - // // // PATIENT BIRTHDATE For ex: DICOM (0010,0030) = 19680427 - strcpy( patientInfos->PatientBirthDate, gGetStringValueFromTag( gdcm::Tag(0x0010,0x0030), ds) ); - // // // SERIES NUMBER For ex: DICOM (0020,0011) = 902 - strcpy( patientInfos->SeriesNumber, gGetStringValueFromTag( gdcm::Tag(0x0020,0x0011), ds) ); - // // // SERIES DESCRIPTION For ex: DICOM (0008,103e) = SCOUT - strcpy( patientInfos->SeriesDescription , gGetStringValueFromTag( gdcm::Tag(0x0008,0x103e), ds) ); - // // // STUDY ID For ex: DICOM (0020,0010) = 37481 - strcpy( patientInfos->StudyID, gGetStringValueFromTag( gdcm::Tag(0x0020,0x0010), ds) ); - // // // STUDY DESCRIPTION For ex: DICOM (0008,1030) = BRAIN/C-SP/FACIAL - strcpy( patientInfos->StudyDescription, gGetStringValueFromTag( gdcm::Tag(0x0008,0x1030), ds) ); - - const gdcm::DataElement &gSetupSequence=ds.GetDataElement(gdcm::Tag(0x300a,0x0180)); - //LOOK FOR PATIENT ORIENTATION - gdcm::SmartPointer sqiPS = gSetupSequence.GetValueAsSQ(); - const gdcm::DataSet& gPatSetupNest = sqiPS->GetItem(1).GetNestedDataSet(); - //SET PATIENT ORIENTATION STRING - strcpy(patientInfos->PatientOrientation, gGetStringValueFromTag(gdcm::Tag(0x0018, 0x5100),gPatSetupNest)); - - - - patientInfos->rtIonPlanPath=QString(patientInfos->filenames.at(i).c_str()); - cout<< "RTPlan import success" <filenames.erase(patientInfos->filenames.begin()+i); - delete fileReader; - continue; - } - } - - // msStructRTStruct.SetFromFile( fileReader->GetFile() ); - // if( msStructRTStruct == gdcm::MediaStorage::RTStructureSetStorage ) { - if( gCheckDICOMModalityToInt(patientInfos->filenames.at(i).c_str()) == RTSTRUCT ){ - if(!patientInfos->rtStructurePath.isEmpty()) { - patientInfos->rtStructurePath.clear(); -// result=PARSER_RTSTRUCT_NOTUNIQUE; - continue; - } else { - patientInfos->rtStructurePath=QString(patientInfos->filenames.at(i).c_str()); - cout<< "RTStruct import success" <filenames.erase(patientInfos->filenames.begin()+i); - delete fileReader; - continue; - } - } - delete fileReader; - } - - - //OK - //if(result == NOERRORS && patientInfos->rtIonPlanPath.isEmpty() ) - // result= PARSER_RTPLAN_MISSING; - //if(result == NOERRORS && patientInfos->rtStructurePath.isEmpty()) - // result=PARSER_RTSTRUCT_MISSING; - - s.SetComputeZSpacing( true ); - s.SetZSpacingTolerance( 1e-2 ); - - if(result==NOERRORS) - if(!s.Sort( patientInfos->filenames ) ) { - result=PARSER_CT_FAILED_SORTING; - } else { - s.GetFilenames().size() == 0 ? result = PARSER_CT_FILES_MISSING : result=NOERRORS; - } - - if(result == NOERRORS){ - patientInfos->CTfiles=s.GetFilenames (); - //cout<< "CT files sorting succeeded" <parse(p_loadDir); + + if(patientInfos->CTfiles .size()!=0){ + cout<< "this->load2VTK(patientInfos)" <rtIonPlanPath.isEmpty()){ + readRT = new RTPlan; + readRT->fillRTPlan(patientInfos->rtIonPlanPath.toLatin1().data() ); + m_rtIsocenter = new double[3]; + if (readRT->NumberOfBeams > 0){ + { + const auto& iso = readRT->Beams.at(0)->IsocenterPosition; + m_rtIsocenter[0] = iso[0]; + m_rtIsocenter[1] = iso[1]; + m_rtIsocenter[2] = iso[2]; + } + if (readRT->PatientOrientation.rfind("HFS", 0) == 0) + m_patientOrientation = SUPINE; + else if (readRT->PatientOrientation.rfind("HFP", 0) == 0) + m_patientOrientation = PRONE; + else m_patientOrientation = NOTDEFINED; + + emit loadedRTIso(m_rtIsocenter); + //for (int ii=0;iiNumberOfBeams; ii++){ + // cout<< readRT->Beams[ii]->IsocenterPosition[0]<<" "; + // cout<< readRT->Beams[ii]->IsocenterPosition[1]<<" "; + // cout<< readRT->Beams[ii]->IsocenterPosition[2]<<" "<NumberOfBeams <Beams.at(0)->IsocenterPosition[0] <Beams.at(2)->IsocenterPosition[0]<Beams.at(0)->IsocenterPosition[1] <Beams.at(2)->IsocenterPosition[1]<Beams.at(0)->IsocenterPosition[2] <Beams.at(2)->IsocenterPosition[2]<NumberOfBeams > 2 && + (readRT->Beams.at(0)->IsocenterPosition[0] != readRT->Beams.at(2)->IsocenterPosition[0] || + readRT->Beams.at(0)->IsocenterPosition[1] != readRT->Beams.at(2)->IsocenterPosition[1] || + readRT->Beams.at(0)->IsocenterPosition[2] != readRT->Beams.at(2)->IsocenterPosition[2]) + ) + virtualIso=true; + else + virtualIso=false; + //cout << "virtual Iso: "<loadDICOM(patientInfos->CTfiles); + this->connectToVTK(); +// this->changeRef(GOTSREF); + /* use DCM as default */ + this->changeRef(DCMREF); + //trueOffset_rot_prev=trueOffset_rot; + + + //this->load2VTK(patientInfos); + } else { + //DO NOTHING. THE folderisempty signal was already emitted. + } + cout<< " gLoadPatient::load _ END" <SetDirection( /*imageDir*/directionToWCS ); + myImage->SetOrigin( trueOffset_rot ); + myImage->Update(); + + in->SetInput(myImage); + this->connectToVTK(); + this->actualizeOut(); + + /*calculate deltas for marker representation update*/ + if(initialized){ + emit referenceChange( + trueOffset_rot_prev[0]-trueOffset_rot[0], + trueOffset_rot_prev[1]-trueOffset_rot[1], + trueOffset_rot_prev[2]-trueOffset_rot[2]); + trueOffset_rot_prev=trueOffset_rot; + } else { + trueOffset_rot_prev=trueOffset_rot; + initialized=true; + } + +} + +void gLoadPatient::loadDICOM(std::vector CTfilenames){ + + myImage = myImageType::New(); + rDICOM = itk::ImageSeriesReader::New(); + iGDCMimage = itk::GDCMImageIO::New(); + myDICOMseries = itk::GDCMSeriesFileNames::New(); + + rDICOM->SetImageIO(iGDCMimage); + rDICOM->SetFileNames(CTfilenames); + // rDICOM->SetReverseOrder(true); + try{ + rDICOM->Update(); + } + catch (itk::ExceptionObject &ex){ + std::cout << ex << std::endl; + return; // error! + } + + myImage=rDICOM->GetOutput(); + imageDir = myImage->GetDirection( ); + origin = myImage->GetOrigin( ); + sizeOfImage = myImage->GetLargestPossibleRegion().GetSize(); + +} + +void gLoadPatient::connectToVTK(){ + + + out = vtkSmartPointer::New(); + + in = itk::VTKImageExport ::New(); + in->SetInput(myImage); + + out->SetUpdateInformationCallback(in->GetUpdateInformationCallback()); + out->SetPipelineModifiedCallback(in->GetPipelineModifiedCallback()); + out->SetWholeExtentCallback(in->GetWholeExtentCallback()); + out->SetSpacingCallback(in->GetSpacingCallback()); + out->SetOriginCallback(in->GetOriginCallback()); + out->SetScalarTypeCallback(in->GetScalarTypeCallback()); + out->SetNumberOfComponentsCallback(in->GetNumberOfComponentsCallback()); + out->SetPropagateUpdateExtentCallback(in->GetPropagateUpdateExtentCallback()); + out->SetUpdateDataCallback(in->GetUpdateDataCallback()); + out->SetDataExtentCallback(in->GetDataExtentCallback()); + out->SetBufferPointerCallback(in->GetBufferPointerCallback()); + out->SetCallbackUserData(in->GetCallbackUserData()); + +} + +void gLoadPatient::actualizeOut(){ + + //in->Update(); + out->Update(); + //vol3D = vtkSmartPointer ::New(); + //vol3D->DeepCopy(out->GetOutput()); + //vol3D->Update(); + vol3D = out->GetOutput(); + cout<GetSpacing()[0] <<" "<GetSpacing()[1] <<" "<GetSpacing()[2] <clearInfo() " <clearInfo(); + + std::cout<<"parsing dir: "<c_str()).fileName().split(".").last() != QString("raw") ) + patientInfos->filenames.push_back( it->c_str() ); + } + patientInfos->filenames.size() == 0 ? result = PARSER_FOLDER_EMPTY : result = NOERRORS; + } else { + result=PARSER_FOLDER_NOTEXISTING; + }; + + cout << " patientInfos->filenames.size() "<filenames.size() <filenames.size()-1);i>-1;i--) + { + //cout << gCheckDICOMModalityToInt(patientInfos->filenames.at(i).c_str()) <SetFileName (patientInfos->filenames.at(i).c_str()); + + if( !fileReader->Read() ) { + cout<< "Error reading file: "<< patientInfos->filenames.at(i).data() <filenames.erase(patientInfos->filenames.begin()+i); + continue; + } + + //msStructRTIonPlan.SetFromFile( fileReader->GetFile() ); + //if( msStructRTIonPlan == gdcm::MediaStorage::RTIonPlanStorage ) + if( gCheckDICOMModalityToInt(patientInfos->filenames.at(i).c_str()) == RTPLAN ) + { + if(!patientInfos->rtIonPlanPath.isEmpty()) { +// result=PARSER_RTPLAN_NOTUNIQUE; + continue; + } else { + const gdcm::File &file = fileReader->GetFile(); + const gdcm::DataSet &ds = file.GetDataSet(); + //PATIENT NAME + strcpy( patientInfos->PatientName,gGetStringValueFromTag( gdcm::Tag(0x0010,0x0010), ds)); + // // // PATIENT ID For ex: DICOM (0010,0020) = 1933197 + strcpy( patientInfos->PatientID , gGetStringValueFromTag( gdcm::Tag(0x0010,0x0020), ds)); + // // // PATIENT AGE For ex: DICOM (0010,1010) = 031Y + strcpy( patientInfos->PatientAge , gGetStringValueFromTag( gdcm::Tag(0x0010,0x1010), ds)) ; + // // // PATIENT SEX For ex: DICOM (0010,0040) = M + strcpy( patientInfos->PatientSex, gGetStringValueFromTag( gdcm::Tag(0x0010,0x0040), ds) ); + // // // PATIENT BIRTHDATE For ex: DICOM (0010,0030) = 19680427 + strcpy( patientInfos->PatientBirthDate, gGetStringValueFromTag( gdcm::Tag(0x0010,0x0030), ds) ); + // // // SERIES NUMBER For ex: DICOM (0020,0011) = 902 + strcpy( patientInfos->SeriesNumber, gGetStringValueFromTag( gdcm::Tag(0x0020,0x0011), ds) ); + // // // SERIES DESCRIPTION For ex: DICOM (0008,103e) = SCOUT + strcpy( patientInfos->SeriesDescription , gGetStringValueFromTag( gdcm::Tag(0x0008,0x103e), ds) ); + // // // STUDY ID For ex: DICOM (0020,0010) = 37481 + strcpy( patientInfos->StudyID, gGetStringValueFromTag( gdcm::Tag(0x0020,0x0010), ds) ); + // // // STUDY DESCRIPTION For ex: DICOM (0008,1030) = BRAIN/C-SP/FACIAL + strcpy( patientInfos->StudyDescription, gGetStringValueFromTag( gdcm::Tag(0x0008,0x1030), ds) ); + + const gdcm::DataElement &gSetupSequence=ds.GetDataElement(gdcm::Tag(0x300a,0x0180)); + //LOOK FOR PATIENT ORIENTATION + gdcm::SmartPointer sqiPS = gSetupSequence.GetValueAsSQ(); + const gdcm::DataSet& gPatSetupNest = sqiPS->GetItem(1).GetNestedDataSet(); + //SET PATIENT ORIENTATION STRING + strcpy(patientInfos->PatientOrientation, gGetStringValueFromTag(gdcm::Tag(0x0018, 0x5100),gPatSetupNest)); + + + + patientInfos->rtIonPlanPath=QString(patientInfos->filenames.at(i).c_str()); + cout<< "RTPlan import success" <filenames.erase(patientInfos->filenames.begin()+i); + delete fileReader; + continue; + } + } + + // msStructRTStruct.SetFromFile( fileReader->GetFile() ); + // if( msStructRTStruct == gdcm::MediaStorage::RTStructureSetStorage ) { + if( gCheckDICOMModalityToInt(patientInfos->filenames.at(i).c_str()) == RTSTRUCT ){ + if(!patientInfos->rtStructurePath.isEmpty()) { + patientInfos->rtStructurePath.clear(); +// result=PARSER_RTSTRUCT_NOTUNIQUE; + continue; + } else { + patientInfos->rtStructurePath=QString(patientInfos->filenames.at(i).c_str()); + cout<< "RTStruct import success" <filenames.erase(patientInfos->filenames.begin()+i); + delete fileReader; + continue; + } + } + delete fileReader; + } + + + //OK + //if(result == NOERRORS && patientInfos->rtIonPlanPath.isEmpty() ) + // result= PARSER_RTPLAN_MISSING; + //if(result == NOERRORS && patientInfos->rtStructurePath.isEmpty()) + // result=PARSER_RTSTRUCT_MISSING; + + s.SetComputeZSpacing( true ); + s.SetZSpacingTolerance( 1e-2 ); + + if(result==NOERRORS) + if(!s.Sort( patientInfos->filenames ) ) { + result=PARSER_CT_FAILED_SORTING; + } else { + s.GetFilenames().size() == 0 ? result = PARSER_CT_FILES_MISSING : result=NOERRORS; + } + + if(result == NOERRORS){ + patientInfos->CTfiles=s.GetFilenames (); + //cout<< "CT files sorting succeeded" < +#include gRen::gRen(QVTKOpenGLNativeWidget *m_qvtk){ @@ -8,12 +8,9 @@ gRen::gRen(QVTKOpenGLNativeWidget *m_qvtk){ qvtk= m_qvtk; nmarker=0; - markerSources = 0; - markerMappers = 0; - markerActors = 0; - - - + markerSources.clear(); + markerMappers.clear(); + markerActors.clear(); return; } @@ -136,20 +133,20 @@ void gRen::loadVolume(vtkImageData* volCT){ void gRen::createRenderers(){ - ren1 = vtkRenderer::New(); + ren1 = vtkSmartPointer::New(); ren1->SetBackground(0.13, 0.25, 0.32); qvtk->renderWindow()->AddRenderer(ren1); ren1->AddActor(assi); ren1->AddActor( outlineActor); - boxWidget_NEW = vtkBoxWidget::New(); + boxWidget_NEW = vtkSmartPointer::New(); boxWidget_NEW->SetInteractor(qvtk->interactor()); boxWidget_NEW->SetPlaceFactor(1.0); // - - - - - - - - - - - - - - - - - - - - - - boxCallback_NEW->rcm=volumeMapper; + boxCallback_NEW->rcm=volumeMapper.GetPointer(); boxWidget_NEW->AddObserver(vtkCommand::InteractionEvent, boxCallback_NEW); return; @@ -163,23 +160,23 @@ void gRen::buildRenderPipe(){ qDebug()<<"buildRenderPipe: outlineMapper"; - vtkPolyDataMapper* outlineMapper = vtkPolyDataMapper::New(); + vtkNew outlineMapper; outlineMapper->SetInputConnection(outline->GetOutputPort()); outlineActor = vtkSmartPointer ::New(); - outlineActor->SetMapper( outlineMapper); + outlineActor->SetMapper(outlineMapper); qDebug()<<"buildRenderPipe: picker"; - vtkCellPicker* picker = vtkCellPicker::New(); + vtkNew picker; picker->SetTolerance(0.005); - vtkProperty* ipwProp = vtkProperty::New(); + vtkNew ipwProp; //// //assign default props to the ipw's texture plane actor qDebug()<<"buildRenderPipe: planeX"; - planeWidgetX = vtkImagePlaneWidget::New(); + planeWidgetX = vtkSmartPointer::New(); planeWidgetX->SetInteractor( qvtk->interactor()); planeWidgetX->SetKeyPressActivationValue('x'); planeWidgetX->SetPicker(picker); @@ -195,7 +192,7 @@ void gRen::buildRenderPipe(){ qDebug()<<"buildRenderPipe: planeY"; - planeWidgetY = vtkImagePlaneWidget::New(); + planeWidgetY = vtkSmartPointer::New(); planeWidgetY->SetInteractor( qvtk->interactor()); planeWidgetY->SetKeyPressActivationValue('y'); planeWidgetY->SetPicker(picker); @@ -210,7 +207,7 @@ void gRen::buildRenderPipe(){ qDebug()<<"buildRenderPipe: planeZ"; - planeWidgetZ = vtkImagePlaneWidget::New(); + planeWidgetZ = vtkSmartPointer::New(); planeWidgetZ->SetInteractor( qvtk->interactor()); planeWidgetZ->SetKeyPressActivationValue('z'); planeWidgetZ->SetPicker(picker); @@ -225,7 +222,7 @@ void gRen::buildRenderPipe(){ qDebug()<<"buildRenderPipe: assi"; - assi= vtkAxesActor::New(); + assi = vtkSmartPointer::New(); assi->SetOrigin(0,0,0); assi->AxisLabelsOff(); assi->SetTotalLength(15,15,15); @@ -233,7 +230,7 @@ void gRen::buildRenderPipe(){ qDebug()<<"buildRenderPipe: volMapper"; // Create mapper (as vtkVolumeMapper smart pointer) - auto gpuMapper = vtkSmartPointer::New(); + auto gpuMapper = vtkSmartPointer::New(); gpuMapper->SetBlendModeToComposite(); if (!gpuMapper) { @@ -309,7 +306,7 @@ void gRen::buildRenderPipe(){ qDebug()<<"buildRenderPipe: orthoplanes"; - orthoPlanes = vtkImageOrthoPlanes::New(); + orthoPlanes = vtkSmartPointer::New(); return; } @@ -318,11 +315,17 @@ void gRen::onSingleMarkerChange(int idx, bool state){ cout<< "onSingleMarkerChange : "<GetProperty()->SetColor(0,1,0); - } else { - markerActors[idx]->GetProperty()->SetColor(1,1,1); - } + if (idx < 0 || idx >= static_cast(markerActors.size())) + return; + + auto &act = markerActors.at(static_cast(idx)); + if (!act) + return; + + if(state) + act->GetProperty()->SetColor(0,1,0); + else + act->GetProperty()->SetColor(1,1,1); qvtk->renderWindow()->Render(); return; @@ -333,26 +336,25 @@ void gRen::renderMarkers(QList marker_list){ cout << "renderMarkers" << endl; - if(markerSources != 0) { - for(int ii=0;iiRemoveActor(markerActors[ii]); - markerActors[ii]->RemoveAllObservers(); - markerMappers[ii]->RemoveAllObservers(); - markerSources[ii]->RemoveAllObservers(); - markerSources[ii]->Delete(); - markerActors[ii]->Delete(); - markerMappers[ii]->Delete(); - } - delete [] markerActors; - delete [] markerMappers; - delete [] markerSources; + // Remove previous marker actors/labels + for (auto &a : markerActors) + { + if (a) + ren1->RemoveActor(a); + } + markerActors.clear(); + markerMappers.clear(); + markerSources.clear(); + if (pointLabels) + { ren1->RemoveActor(pointLabels); + pointLabels = nullptr; } nmarker = marker_list.size(); - markerSources = new vtkSphereSource* [nmarker]; - markerMappers = new vtkPolyDataMapper* [nmarker]; - markerActors = new vtkActor* [nmarker]; + markerSources.reserve(static_cast(nmarker)); + markerMappers.reserve(static_cast(nmarker)); + markerActors.reserve(static_cast(nmarker)); modelPoints = vtkSmartPointer::New(); modelPoints->Reset(); @@ -367,19 +369,22 @@ void gRen::renderMarkers(QList marker_list){ stringData->InsertNextValue(QString::number(i).toLatin1().constData()); modelPoints->InsertPoint(i,marker_list.at(i).centroid.x*1000,marker_list.at(i).centroid.y*1000,marker_list.at(i).centroid.z*1000); - markerSources[i] = vtkSphereSource::New(); - markerSources[i]->SetCenter(marker_list.at(i).centroid.x*1000,marker_list.at(i).centroid.y*1000,marker_list.at(i).centroid.z*1000); - markerSources[i]->SetRadius(10); - // markerSources[i]->SetRadius(2); + auto src = vtkSmartPointer::New(); + src->SetCenter(marker_list.at(i).centroid.x*1000,marker_list.at(i).centroid.y*1000,marker_list.at(i).centroid.z*1000); + src->SetRadius(10); - markerMappers[i]= vtkPolyDataMapper::New(); - markerMappers[i]->SetInputConnection (markerSources[i]->GetOutputPort()); + auto map = vtkSmartPointer::New(); + map->SetInputConnection(src->GetOutputPort()); - markerActors[i]= vtkActor::New(); - markerActors[i]->SetMapper(markerMappers[i]); - markerActors[i]->GetProperty()->SetRepresentationToWireframe(); - markerActors[i]->GetProperty()->SetColor(0,1,0); - ren1->AddActor(markerActors[i]); + auto act = vtkSmartPointer::New(); + act->SetMapper(map); + act->GetProperty()->SetRepresentationToWireframe(); + act->GetProperty()->SetColor(0,1,0); + ren1->AddActor(act); + + markerSources.push_back(src); + markerMappers.push_back(map); + markerActors.push_back(act); } cout << "modelPoints" << endl; @@ -415,20 +420,17 @@ void gRen::renderMarkers(QList marker_list){ void gRen::onReferenceChange(double dx, double dy, double dz){ - if(markerSources == 0) + if (markerSources.empty()) return; for(int i=0;iSetCenter( - markerSources[i]->GetCenter()[0]-dx, - markerSources[i]->GetCenter()[1]-dy, - markerSources[i]->GetCenter()[2]-dz); - markerSources[i]->Update(); + auto &src = markerSources.at(static_cast(i)); + const double* c = src->GetCenter(); + src->SetCenter(c[0]-dx, c[1]-dy, c[2]-dz); + src->Update(); - modelPoints->SetPoint(i, - modelPoints->GetPoint(i)[0]-dx, - modelPoints->GetPoint(i)[1]-dy, - modelPoints->GetPoint(i)[2]-dz); + const double* p = modelPoints->GetPoint(i); + modelPoints->SetPoint(i, p[0]-dx, p[1]-dy, p[2]-dz); } modelPoints->Modified(); diff --git a/gRen.h b/gRen.h old mode 100755 new mode 100644 index 19cf807..5e3be03 --- a/gRen.h +++ b/gRen.h @@ -1,6 +1,7 @@ #ifndef _GREN_H_ #define _GREN_H_ +#include #include #include @@ -100,13 +101,17 @@ public: ); - /*clip the vol ren*/ - vtkPlanes *planes = vtkPlanes::New(); + /* clip the vol ren */ + // Keep the vtkPlanes object alive for as long as the callback lives. + if (!this->Planes) + { + this->Planes = vtkSmartPointer::New(); + } // The implicit function vtkPlanes is used in conjunction with the // volume ray cast mapper to limit which portion of the volume is // volume rendered. - boxWidget->GetPlanes(planes); - this->rcm->SetClippingPlanes(planes); + boxWidget->GetPlanes(this->Planes); + this->rcm->SetClippingPlanes(this->Planes); } void setNominalGeometry(int* extents, double* bounds, double* spacing){ @@ -124,6 +129,7 @@ private: double nominalBounds[6]; int nominalExtent[6]; double VolSpacing[3]; + vtkSmartPointer Planes; }; @@ -237,19 +243,19 @@ private: vtkSmartPointer CTvolume; vtkSmartPointer outline; vtkSmartPointer outlineActor; - vtkImageMapToColors* colorMap_X; - vtkImagePlaneWidget* planeWidgetX; - vtkImagePlaneWidget* planeWidgetY; - vtkImagePlaneWidget* planeWidgetZ; + vtkSmartPointer colorMap_X; + vtkSmartPointer planeWidgetX; + vtkSmartPointer planeWidgetY; + vtkSmartPointer planeWidgetZ; QVTKOpenGLNativeWidget* qvtk; // vtkSmartPointer myrenderWindow; - vtkImageOrthoPlanes* orthoPlanes; - vtkAxesActor * assi; - vtkRenderer* ren1; + vtkSmartPointer orthoPlanes; + vtkSmartPointer assi; + vtkSmartPointer ren1; - //vtkSmartPointer volumeMapper; - //vtkSmartPointer volumeMapper; - vtkSmartPointer volumeMapper; + // NOTE: vtkGPUVolumeRayCastMapper is used at runtime, but we keep a + // concrete smart pointer for stability and to avoid nullptr issues. + vtkSmartPointer volumeMapper; vtkSmartPointer color; vtkSmartPointer compositeOpacity; @@ -260,11 +266,11 @@ private: vtkSmartPointer boxCallback ; vtkSmartPointer boxCallback_NEW; vtkSmartPointer extractVOI; - vtkBoxWidget *boxWidget_NEW; + vtkSmartPointer boxWidget_NEW; - vtkSphereSource** markerSources; - vtkPolyDataMapper** markerMappers; - vtkActor** markerActors; + std::vector> markerSources; + std::vector> markerMappers; + std::vector> markerActors; vtkSmartPointer modelPoints; vtkSmartPointer pointLabels; int nmarker; diff --git a/gSkullRemoval.cpp b/gSkullRemoval.cpp old mode 100755 new mode 100644 diff --git a/gSkullRemoval.h b/gSkullRemoval.h old mode 100755 new mode 100644 diff --git a/icons/Thumbs.db b/icons/Thumbs.db old mode 100755 new mode 100644 diff --git a/icons/ortoIco.png b/icons/ortoIco.png old mode 100755 new mode 100644 diff --git a/icons/pb_load.png b/icons/pb_load.png old mode 100755 new mode 100644 diff --git a/icons/pb_quit.png b/icons/pb_quit.png old mode 100755 new mode 100644 diff --git a/icons/volrenIco.png b/icons/volrenIco.png old mode 100755 new mode 100644 diff --git a/images.qrc b/images.qrc old mode 100755 new mode 100644 diff --git a/itkQtAdaptor.h b/itkQtAdaptor.h old mode 100755 new mode 100644 diff --git a/main.cpp b/main.cpp old mode 100755 new mode 100644 diff --git a/mainw.cpp b/mainw.cpp old mode 100755 new mode 100644 diff --git a/mainw.h b/mainw.h old mode 100755 new mode 100644