├── CHANGELOG.md ├── README.md ├── Velocity Python Guide.pdf └── examples ├── csharp ├── BED.cs ├── DoseSummation.cs ├── ImportLiver.cs ├── PetCtDeformable.cs └── StructureMetrics.cs └── python ├── BED.py ├── dose_summation.py ├── jacobian_structure_mask.py ├── liver_import.py ├── lowDeformHighDiff.py ├── pet_ct_registrations.py ├── print_patient.py └── structure_metrics.py /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [4.1.4.1217] - 2022-03-08 4 | ### Added 5 | PlanOperations: 6 | - Container plan creation. 7 | 8 | StructureOperations: 9 | - Structure delete. 10 | - Structure set creation with External contour. 11 | 12 | PatientDataOperations 13 | - Delete volumes, registrations, structure sets, plans. 14 | 15 | ### Changed 16 | - Fix Issue #15 - crop and smooth parameter documentation. 17 | - Fix Issue #19 - enforce volume match for load registration. 18 | - ROI box fix - don't require a particular order for the corner points. 19 | - Mac build for Python 3.9. 20 | 21 | ## [4.1.4.1216] - 2021-06-02 22 | ### Added 23 | - Dose summation example script. 24 | 25 | ### Changed 26 | - Build against released Velocity 4.1.4. 27 | 28 | ## [4.1.2.1199] - 2020-10-09 29 | 30 | ### Added 31 | - Adaptive plan creation. 32 | - Chain registration loading. 33 | - Default Settings for rigid and deformable registrations. 34 | 35 | ### Changed 36 | - BED functions, examples, and documentation updated. 37 | - Fix Issue #8 - structure mask for secondary structure. 38 | - Fix Issue #7 - hang on script exit for Windows. 39 | - C# example fixes. 40 | - Python guide updates. 41 | 42 | ## [4.1.2.1197] - 2020-06-10 43 | ### Changed 44 | - Fixed missing dependency DLL for C#. 45 | 46 | ## [4.1.2.1194] - 2020-03-12 47 | ### Changed 48 | - Build against released Velocity 4.1.2. 49 | 50 | ## [4.1.1.1184] - 2020-03-11 51 | ### Changed 52 | - Build against released Velocity 4.1.1. 53 | 54 | ## [4.1.0.1145] - 2020-03-11 55 | ### Added 56 | - Python 3 support. 57 | 58 | ### Changed 59 | - Build against released Velocity 4.1.0. 60 | - Examples converted to Python 3. 61 | 62 | ### Removed 63 | - Python 2 support. 64 | 65 | ## [4.0.1.822] - 2019-06-13 66 | ### Added 67 | - Functions to get bounds of structures and intersections between volumes. Can be used as ROI for registration operations. 68 | 69 | ### Changed 70 | - Updated inline documentation. 71 | - Updated Python user guide. 72 | - Updated examples to use `DICOM` versions of registration operations, see removals below. 73 | 74 | ### Removed 75 | - `RegistrationOperations` which use Velocity-internal coordinate systems: 76 | - `performRigidRegistration` 77 | - `performBSplineRegistration` 78 | - `performManualAlignment` 79 | - Use the `DICOM` suffixed versions of the above. 80 | - Removed unused type for structure guided. 81 | 82 | 83 | ## [4.0.1.820] - 2019-05-09 84 | ### Added 85 | - Support for burn-in structure-guided deformable image registration. 86 | - Python user guide. 87 | 88 | ### Changed 89 | - Build against released Velocity 4.0.1. 90 | 91 | 92 | ## [4.0.0.793] - 2018-07-27 93 | ### Added 94 | - Build of scripting APIs against Velocity 4.0.0. 95 | - Examples in Python and C#. 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VelocityEngine 2 | 3 | ## Requirements 4 | VelocityEngine requires a Velocity system and valid license. Use 5 | of the VelocityEngine is governed by the Velocity license. VelocityEngine may be 6 | used with either a local Velocity workstation system, in a single-user fashion, 7 | or the VelocityGRID server system, remotely. Users of the VelocityEngine must 8 | have a user of type "script" created on the Velocity system. Only one login per 9 | script user is possible on a workstation system. Users of the VelocityGRID 10 | system may have unlimited logins using a single script user - however each login 11 | will consume a license. 12 | 13 | ## Non-Clinical Use 14 | VelocityEngine has not been tested and validated for 15 | clinical use on humans. It requires a non-clinical system. VelocityEngine checks 16 | at login to verify the system is a non-clinical database. 17 | 18 | ## License 19 | Copyright 2021 Varian Medical Systems, inc. Use of the VelocityEngine 20 | is governed by existing license of Velocity software. No additional warranties 21 | are provided. Use at your own risk. 22 | -------------------------------------------------------------------------------- /Velocity Python Guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VarianAPIs/VelocityEngine/3849619838ea2821281549da62d40ebf25ffb8c0/Velocity Python Guide.pdf -------------------------------------------------------------------------------- /examples/csharp/BED.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Linq.Expressions; 7 | 8 | namespace Velocity.Examples 9 | { 10 | class BED 11 | { 12 | const string USER = "script"; 13 | const string PASS = "script"; 14 | 15 | const string WORKSTATION_PATH = @"C:\Velocity\Databases\vscDatabase"; 16 | 17 | const string GRID_IP = "127.0.0.1"; 18 | const int GRID_PORT = 57000; 19 | const string GRID_DB = "vscDatabase"; 20 | 21 | const string PATIENT_ID = "10052012"; 22 | const string PRIMARY_UID = "1.2.840.113619.2.55.3.278435321.302.1313753121.582"; 23 | const string SECONDARY_UID = "1.2.246.352.71.7.1873493392.2486330.20110829091331"; 24 | const string REG_NAME = "DICOM"; 25 | const string STRUCT_1 = "Bladder"; 26 | const string STRUCTSET_1UID = "1.2.276.0.7230010.3.1.4.1492057408.5156.1349471628.10"; 27 | const int DEFAULT_ALPHABETA_TISSUE = 3; 28 | 29 | static void OrThrow(bool result, dynamic source) { 30 | if (!result) 31 | throw new Exception(source.getErrorMessage()); 32 | } 33 | 34 | static void ValidOrThrow(PatientDataItem result, dynamic source) { 35 | if (!result.isValid()) 36 | throw new System.Exception(source.getErrorMessage()); 37 | } 38 | 39 | static void Main(string[] args) { 40 | // connect to the grid 41 | var engine = new VelocityEngine(); 42 | Action orThrow = result => OrThrow(result, engine); 43 | //orThrow(engine.loginToGrid(USER, PASS, GRID_IP, GRID_PORT, GRID_DB)); 44 | orThrow(engine.loginToWorkstation(USER, PASS, WORKSTATION_PATH, true)); 45 | AppDomain.CurrentDomain.ProcessExit += (source, data) => { engine.logout(); }; 46 | 47 | ValidOrThrow(engine.loadPatientByPatientId(PATIENT_ID), engine); 48 | Console.WriteLine("Loaded patient: {0}", PATIENT_ID); 49 | ValidOrThrow(engine.loadPrimaryVolumeByUID(PRIMARY_UID), engine); 50 | Console.WriteLine("Loaded primary volume: {0}", PRIMARY_UID); 51 | ValidOrThrow(engine.loadSecondaryVolumeByUID(SECONDARY_UID), engine); 52 | Console.WriteLine("Loaded secondary volume: {0}", SECONDARY_UID); 53 | 54 | // load registration 55 | ValidOrThrow(engine.loadRegistrationByName(REG_NAME), engine); 56 | Console.WriteLine("Loaded registration: {0}", REG_NAME); 57 | 58 | var structure = engine.loadStructureByName(STRUCT_1, STRUCTSET_1UID); 59 | ValidOrThrow(structure, engine); 60 | Console.WriteLine("Loading existing structure: {0}", STRUCT_1); 61 | 62 | var bedStructure = new BedStructureVariables(); 63 | bedStructure.structureId = structure.getVelocityId(); 64 | bedStructure.alphaBetaRatio = 2; 65 | bedStructure.structureName = structure.getName(); 66 | 67 | var calculationData = new BedDoseCalculationData(); 68 | calculationData.bedVariablesByStructure = new BedStructureVariablesList(new BedStructureVariables[] {bedStructure}); 69 | calculationData.fractions = 25; 70 | 71 | var backgroundData = new BedBackgroundData(); 72 | backgroundData.alphaBetaRatio = DEFAULT_ALPHABETA_TISSUE; 73 | 74 | var vo = engine.getVolumeOperations(); 75 | OrThrow(vo.createBEDose(calculationData,backgroundData, false ) != -1, vo); 76 | Console.WriteLine("Biological Effective Dose created"); 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /examples/csharp/DoseSummation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Velocity.Examples { 8 | class DoseSummation { 9 | static void OrThrow(bool result, dynamic source) { 10 | if (!result) 11 | throw new Exception(source.getErrorMessage()); 12 | } 13 | 14 | static void ValidOrThrow(PatientDataItem result, dynamic source) { 15 | if (!result.isValid()) 16 | throw new System.Exception(source.getErrorMessage()); 17 | } 18 | 19 | static Registration makeAutoReg(VelocityEngine e, Volume pVol, Volume sVol, String regName) { 20 | ValidOrThrow(e.loadPrimaryVolume(pVol.getVelocityId()), e); 21 | Console.WriteLine("Loaded primary volume: {0}", pVol.getName()); 22 | ValidOrThrow(e.loadSecondaryVolume(sVol.getVelocityId()), e); 23 | Console.WriteLine("Loaded secondary volume: {0}", sVol.getName()); 24 | 25 | // create registration 26 | var regOps = e.getRegistrationOperations(); 27 | Console.WriteLine("Creating registration object: {0}", regName); 28 | var registration = regOps.createNewRegistration(regName); 29 | ValidOrThrow(registration, regOps); 30 | ValidOrThrow(e.loadRegistration(registration.getVelocityId()), e); 31 | 32 | Console.WriteLine("Running rigid registration ..."); 33 | var rigidSettings = new DefaultRigidRegistrationSettings(); 34 | OrThrow(regOps.performRigidRegistrationDICOM(rigidSettings), regOps); 35 | 36 | // save the changes 37 | ValidOrThrow(regOps.saveRegistration(), regOps); 38 | return registration; 39 | } 40 | 41 | static void Main(string[] args) { 42 | const string USER = "script"; 43 | const string PASS = "script"; 44 | const string WORKSTATION_PATH = @"C:\Velocity\Databases\WKS414"; 45 | const string GRID_IP = "127.0.0.1"; 46 | const int GRID_PORT = 57000; 47 | const string GRID_DB = "vscDatabase"; 48 | 49 | const string PATIENT_ID = "hyperarc"; 50 | 51 | // connect to db 52 | var engine = new VelocityEngine(); 53 | Action orThrow = result => OrThrow(result, engine); 54 | //orThrow(engine.loginToGrid(USER, PASS, GRID_IP, GRID_PORT, GRID_DB)); 55 | orThrow(engine.loginToWorkstation(USER, PASS, WORKSTATION_PATH, true)); 56 | AppDomain.CurrentDomain.ProcessExit += (source, data) => { engine.logout(); }; 57 | 58 | var volOps = engine.getVolumeOperations(); 59 | var patient = engine.loadPatientByPatientId(PATIENT_ID); 60 | 61 | ValidOrThrow(patient, engine); 62 | Console.WriteLine("Loaded patient: {0}", PATIENT_ID); 63 | 64 | //sort CTs ascending by AcquisitionDate 65 | var cts = patient.getVolumes("CT").OrderBy(o => o.getAcquisitionDate()); 66 | var doses = patient.getVolumes("RTDOSE"); 67 | 68 | // find all CT-DS in the same frame of reference 69 | //if multiple DS for same CT pick first one that has a plan 70 | var ctDosePair = new Dictionary(); 71 | Console.WriteLine("Finding CT-DS pairs to be resampled") ; 72 | foreach (Volume ct in cts) { 73 | var plans = ct.getLinkedPlans().ToList(); 74 | foreach (Volume d in doses) { 75 | if (d.getName() == "auto resampled" || d.getName() == "SUM RTDOSE") 76 | continue; 77 | if (ct.getFrameOfReferenceUID() == d.getFrameOfReferenceUID()) { 78 | ctDosePair[ct] = d; 79 | bool hasPlan = plans.Exists(p => p.getInstanceUID() == d.getPlanUID()); 80 | if (hasPlan) break; 81 | } 82 | } 83 | } 84 | 85 | //resample doses onto the most recent CT 86 | Console.WriteLine("Resampling DS onto most recent CT"); 87 | var primaryCT = cts.Last(); // most recent by Acq Date 88 | var resampledDoses = new IntList(); // holds the ids of all doses to be summed 89 | var primaryDoseId = -1; 90 | 91 | foreach (Volume ct in ctDosePair.Keys) { 92 | var dose = ctDosePair[ct]; 93 | if (ct.getVelocityId() != primaryCT.getVelocityId()) { 94 | //need to load CT-DS to activate/create DICOM registration needed for chain 95 | ValidOrThrow(engine.loadPrimaryVolume(ct.getVelocityId()), engine); 96 | Console.WriteLine("Loaded CT {0} as primary", ct.getName()); 97 | ValidOrThrow(engine.loadSecondaryVolume(dose.getVelocityId()), engine); 98 | Console.WriteLine("Loaded dose {0} as secondary", dose.getName()); 99 | var dicomReg = engine.loadRegistrationByName("DICOM"); 100 | ValidOrThrow(dicomReg, engine); 101 | 102 | // make new reg for resampling between primary CT and secondary CT 103 | var ct2ctReg = makeAutoReg(engine, primaryCT, ct, "resample_reg"); 104 | 105 | //now load primary CT and dose, load chain reg and resample onto primary CT 106 | ValidOrThrow(engine.loadPrimaryVolume(primaryCT.getVelocityId()), engine); 107 | Console.WriteLine("Loaded CT {0} as primary volume", primaryCT.getName()); 108 | ValidOrThrow(engine.loadSecondaryVolume(dose.getVelocityId()), engine); 109 | Console.WriteLine("Loaded Dose id {0} as secondary volume", dose.getName()); 110 | //load registration resample_reg + DICOM 111 | orThrow(engine.loadChainRegistration(ct2ctReg.getVelocityId(), dicomReg.getVelocityId())); 112 | Console.WriteLine("Loaded registration resample_reg + DICOM"); 113 | Console.WriteLine("Creating resampled dose..."); 114 | 115 | var id = volOps.createResampledVolume(VolumeResampleOperation.VolumeResampleReplace, "auto resampled"); 116 | if (id == -1) { 117 | Console.WriteLine("Error creating resampled dose {0}", volOps.getErrorMessage()); 118 | continue; 119 | } 120 | 121 | Console.WriteLine("Resampled dose created with id {0}", (id)); 122 | resampledDoses.Add(id); 123 | } 124 | else { 125 | //this dose does not require resample, same FOR as primary CT 126 | primaryDoseId = dose.getVelocityId(); 127 | resampledDoses.Add(primaryDoseId); 128 | } 129 | } 130 | 131 | Console.WriteLine("Summing {0} dose volumes ...", resampledDoses.Count()); 132 | ValidOrThrow(engine.loadPrimaryVolume(primaryDoseId), engine); 133 | //load primary dose as anchor 134 | Console.WriteLine("Loaded DS id {0} as primary volume", primaryDoseId); 135 | int sumId = volOps.createResampledVolumeAggregate(VolumeResampleOperation.VolumeResampleAdd, resampledDoses, (int)ResampleFlags.Flags.AllowNone); // sum 136 | if (sumId == -1) { 137 | Console.WriteLine("Error creating sum dose {0}", volOps.getErrorMessage()); 138 | } 139 | else { 140 | Console.WriteLine("Sum dose created with id {0}", sumId); 141 | } 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /examples/csharp/ImportLiver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Velocity.Examples { 8 | class Program { 9 | const string USER = "script"; 10 | const string PASS = "script"; 11 | const string WORKSTATION_PATH = @"C:\Velocity\Databases\vscDatabase"; 12 | 13 | const string GRID_IP = "127.0.0.1"; 14 | const int GRID_PORT = 57000; 15 | const string GRID_DB = "vscDatabase"; 16 | 17 | const string LIVER_PATIENT_ID = "0045"; 18 | const string LIVER_IMPORT_DIR = @"C:\demodata\SPECT, Liver"; 19 | 20 | /** 21 | * Convenience function to test loading a patient and listing the associated volume UIDS. 22 | */ 23 | static void loadAndListVolumes(VelocityEngine engine, string patientId) { 24 | if (!engine.loadPatientByPatientId(patientId).isValid()) { 25 | Console.WriteLine("Could not load patient id {0}, error: {1}", patientId, engine.getErrorMessage()); 26 | } else { 27 | var volumeIds = engine.getPatientVolumeUIDs(patientId); 28 | Console.WriteLine("Patient id {0} has volume UIDs: {1}", patientId, String.Join(", ", volumeIds)); 29 | } 30 | } 31 | 32 | static void Main(string[] args) { 33 | // connect to the grid 34 | var engine = new VelocityEngine(); 35 | if (!engine.loginToWorkstation(USER, PASS, WORKSTATION_PATH, true)) { 36 | //if (!engine.loginToGrid(USER, PASS, GRID_IP, GRID_PORT, GRID_DB)) { 37 | throw new System.Exception(engine.getErrorMessage()); 38 | } 39 | AppDomain.CurrentDomain.ProcessExit += (source, data) => { engine.logout(); }; 40 | 41 | // try to list liver patient volumes 42 | loadAndListVolumes(engine, LIVER_PATIENT_ID); 43 | 44 | var importOps = engine.getImportOperations(); 45 | 46 | Console.WriteLine("Importing SPECT, Liver ..."); 47 | if (importOps.importDirectory(LIVER_IMPORT_DIR, true)) { 48 | Console.WriteLine("Successfully imported."); 49 | } else { 50 | Console.WriteLine("Import error: {0}", importOps.getErrorMessage()); 51 | } 52 | 53 | // try again to list the volumes for the liver patient 54 | loadAndListVolumes(engine, LIVER_PATIENT_ID); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /examples/csharp/PetCtDeformable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Linq.Expressions; 7 | 8 | namespace Velocity.Examples { 9 | class PetCtDeformable { 10 | const string USER = "script"; 11 | const string PASS = "script"; 12 | 13 | const string WORKSTATION_PATH = @"C:\Velocity\Databases\vscDatabase"; 14 | 15 | const string GRID_IP = "127.0.0.1"; 16 | const int GRID_PORT = 57000; 17 | const string GRID_DB = "vscDatabase"; 18 | 19 | const string PATIENT_ID = "H&N 1"; 20 | const string PRIMARY_UID = "1.3.12.2.1107.5.1.4.1031.30000009080711125237500008699"; 21 | const string SECONDARY_UID = "1.2.840.113704.1.111.3200.1253806429.28"; 22 | const string REG_NAME = "csharp_registration"; 23 | 24 | static void OrThrow(bool result, dynamic source) { 25 | if (!result) 26 | throw new Exception(source.getErrorMessage()); 27 | } 28 | 29 | static void ValidOrThrow(PatientDataItem result, dynamic source) { 30 | if (!result.isValid()) 31 | throw new System.Exception(source.getErrorMessage()); 32 | } 33 | 34 | static void Main(string[] args) { 35 | 36 | // connect to the grid 37 | var engine = new VelocityEngine(); 38 | Action orThrow = result => OrThrow(result, engine); 39 | //orThrow(engine.loginToGrid(USER, PASS, GRID_IP, GRID_PORT, GRID_DB)); 40 | orThrow(engine.loginToWorkstation(USER, PASS, WORKSTATION_PATH, true)); 41 | AppDomain.CurrentDomain.ProcessExit += (source, data) => { engine.logout(); }; 42 | 43 | ValidOrThrow(engine.loadPatientByPatientId(PATIENT_ID), engine); 44 | Console.WriteLine("Loaded patient: {0}", PATIENT_ID); 45 | ValidOrThrow(engine.loadPrimaryVolumeByUID(PRIMARY_UID), engine); 46 | Console.WriteLine("Loaded primary volume: {0}", PRIMARY_UID); 47 | ValidOrThrow(engine.loadSecondaryVolumeByUID(SECONDARY_UID), engine); 48 | Console.WriteLine("Loaded secondary volume: {0}", SECONDARY_UID); 49 | 50 | // create registration 51 | var regOps = engine.getRegistrationOperations(); 52 | Console.WriteLine("Creating registration object: {0}", REG_NAME); 53 | var registration = regOps.createNewRegistration(REG_NAME); 54 | ValidOrThrow(registration, regOps); 55 | ValidOrThrow(engine.loadRegistration(registration.getVelocityId()), engine); 56 | 57 | // first move the head into the right general area 58 | var manualSettings = new ManualRegistrationSettingsStructure(); 59 | manualSettings.registrationMatrix = new MatrixR44d(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -8.1947, -275.0845, -394.5001, 1.0); 60 | orThrow(regOps.performManualAlignmentDICOM(manualSettings)); 61 | 62 | Console.WriteLine("Running rigid registration ..."); 63 | var rigidSettings = new RigidRegistrationSettingsStructure(); 64 | 65 | rigidSettings.roiStart[0] = -102.021; 66 | rigidSettings.roiStart[1] = -74.7234; 67 | rigidSettings.roiStart[2] = -5.49291; 68 | rigidSettings.roiEnd[0] = 100.532; 69 | rigidSettings.roiEnd[1] = -410.341; 70 | rigidSettings.roiEnd[2] = -309.819; 71 | 72 | rigidSettings.primaryStartLevel = -110.02; 73 | rigidSettings.primaryEndLevel = 189.974; 74 | rigidSettings.secondaryStartLevel = -125.014; 75 | rigidSettings.secondaryEndLevel = 224.998; 76 | 77 | rigidSettings.preprocessingMethod = PreprocessingFilterMethod.NoFilter; 78 | rigidSettings.performInitialAutoAlignment = true; 79 | rigidSettings.disableRotationsX = false; 80 | rigidSettings.disableRotationsY = false; 81 | rigidSettings.disableRotationsZ = false; 82 | rigidSettings.maximumNumberOfIterations = 45; 83 | rigidSettings.minimumStepLength = 0.0001; 84 | rigidSettings.maximumStepLength = 17.0; 85 | rigidSettings.samplesDenominator = 10; 86 | rigidSettings.numberOfHistogramBins = 25; 87 | 88 | OrThrow(regOps.performRigidRegistrationDICOM(rigidSettings), regOps); 89 | Console.WriteLine("done"); 90 | 91 | // perform a deformable registration in the same area 92 | Console.WriteLine("Performing deformable registration..."); 93 | var bsplineSettings = new BSplineDeformableRegistrationSettingsStructure(); 94 | bsplineSettings.roiStart[0] = -102.021; 95 | bsplineSettings.roiStart[1] = -74.7234; 96 | bsplineSettings.roiStart[2] = -5.49291; 97 | bsplineSettings.roiEnd[0] = 100.532; 98 | bsplineSettings.roiEnd[1] = -410.341; 99 | bsplineSettings.roiEnd[2] = -309.819; 100 | 101 | bsplineSettings.primaryStartLevel = -110.02; 102 | bsplineSettings.primaryEndLevel = 189.974; 103 | bsplineSettings.secondaryStartLevel = -125.014; 104 | bsplineSettings.secondaryEndLevel = 224.998; 105 | 106 | bsplineSettings.preprocessingMethod = PreprocessingFilterMethod.NoFilter; 107 | bsplineSettings.numberOfMultiResolutionLevels = 3; // # so each List setting should be length 3 108 | bsplineSettings.applyBoundaryContinuityConstraints = new BoolList(new bool[] { false, false, false }); 109 | bsplineSettings.applyTopologicalRegularizer = new BoolList(new bool[] { false, false, false }); 110 | var r3dZeroes = new VectorR3d(0.0); 111 | bsplineSettings.topologicalRegularizerDistanceLimitingCoefficient = new VectorR3dList(new VectorR3d[] { r3dZeroes, r3dZeroes, r3dZeroes }); 112 | bsplineSettings.numberOfHistogramBins = new IntList(new int[] { 50, 50, 50 }); 113 | bsplineSettings.maximumNumberOfIterations = new IntList(new int[] { 30, 30, 30 }); 114 | bsplineSettings.maximumNumberOfConsecutiveOptimizerAttempts = new IntList(new int[] { 10, 10, 10 }); 115 | bsplineSettings.metricValuePercentageDifference = new DoubleList(new double[] { 0.0, 0.0, 0.0 }); 116 | bsplineSettings.minimumStepLength = new DoubleList(new double[] { 0.000001, 0.000001, 0.000001 }); 117 | bsplineSettings.maximumStepLength = new DoubleList(new double[] { 100.0, 100.0, 100.0 }); 118 | bsplineSettings.samplesDenominator = new IntList(new int[] { 5, 5, 5 }); 119 | bsplineSettings.relaxationFactor = new DoubleList(new double[] { 0.9, 0.9, 0.9 }); 120 | bsplineSettings.gradientMagnitudeTolerance = new DoubleList(Enumerable.Repeat(0.000000000000000000005, 3).ToArray()); 121 | bsplineSettings.gridCellSize = new VectorR3dList(new VectorR3d[] { new VectorR3d(5.0), new VectorR3d(10.0), new VectorR3d(15.0) }); 122 | bsplineSettings.gridCellSizeType = new CharList(new char[] { 'n', 'n', 'n' }); 123 | 124 | OrThrow(regOps.performBsplineRegistrationDICOM(bsplineSettings), regOps); 125 | Console.WriteLine("done"); 126 | 127 | // save the changes 128 | ValidOrThrow(regOps.saveRegistration(), regOps); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /examples/csharp/StructureMetrics.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Linq.Expressions; 7 | 8 | namespace Velocity.Examples 9 | { 10 | class StructureMetrics 11 | { 12 | const string USER = "script"; 13 | const string PASS = "script"; 14 | 15 | const string WORKSTATION_PATH = @"C:\Velocity\Databases\vscDatabase"; 16 | 17 | const string GRID_IP = "127.0.0.1"; 18 | const int GRID_PORT = 57000; 19 | const string GRID_DB = "vscDatabase"; 20 | 21 | const string PATIENT_ID = "AW3Y6TA684"; 22 | const string PRIMARY_UID = "1.3.12.2.1107.5.1.4.54841.30000011071412175920300003025"; 23 | const string SECONDARY_UID = "1.2.246.352.61.2.4621874044879001489.17114159699319862401"; 24 | const string DEFORMABLE_NAME = "CBCT MULTI"; 25 | const string RIGID_NAME = "RIGID"; 26 | 27 | static void OrThrow(bool result, dynamic source) { 28 | if (!result) 29 | throw new Exception(source.getErrorMessage()); 30 | } 31 | 32 | static void ValidOrThrow(PatientDataItem result, dynamic source) { 33 | if (!result.isValid()) 34 | throw new System.Exception(source.getErrorMessage()); 35 | } 36 | 37 | static void Main(string[] args) { 38 | // connect to the grid 39 | var engine = new VelocityEngine(); 40 | Action orThrow = result => OrThrow(result, engine); 41 | //orThrow(engine.loginToGrid(USER, PASS, GRID_IP, GRID_PORT, GRID_DB)); 42 | orThrow(engine.loginToWorkstation(USER, PASS, WORKSTATION_PATH, true)); 43 | AppDomain.CurrentDomain.ProcessExit += (source, data) => { engine.logout(); }; 44 | 45 | ValidOrThrow(engine.loadPatientByPatientId(PATIENT_ID), engine); 46 | Console.WriteLine("Loaded patient: {0}", PATIENT_ID); 47 | ValidOrThrow(engine.loadPrimaryVolumeByUID(PRIMARY_UID), engine); 48 | Console.WriteLine("Loaded primary volume: {0}", PRIMARY_UID); 49 | ValidOrThrow(engine.loadSecondaryVolumeByUID(SECONDARY_UID), engine); 50 | Console.WriteLine("Loaded secondary volume: {0}", SECONDARY_UID); 51 | ValidOrThrow(engine.loadRegistrationByName(DEFORMABLE_NAME), engine); 52 | Console.WriteLine("Loaded registration: {0}", DEFORMABLE_NAME); 53 | 54 | 55 | var rops = engine.getRegistrationOperations(); 56 | var sops = engine.getStructureOperations(); 57 | 58 | var primaryVolume = engine.getPrimaryVolume(); 59 | var secondaryVolume = engine.getSecondaryVolume(); 60 | 61 | // find an external structure 62 | StructureSet primarySet = primaryVolume.getStructureSets().Where(ss => ss.getName() == "Original SIM").First(); 63 | Structure structure = primarySet.getStructures().Where(s => s.getName() == "Mandible").First(); 64 | Console.WriteLine("Using structure '{0}' from structure set '{1}'", structure.getName(), primarySet.getName()); 65 | 66 | // create a new structure set on the secondary volume 67 | string targetSetName = DateTime.UtcNow.ToString("s", System.Globalization.CultureInfo.InvariantCulture).Substring(0, 16); 68 | var targetSet = sops.createStructureSet(targetSetName, false); 69 | ValidOrThrow(targetSet, sops); 70 | 71 | // copy the external to the new structure set 72 | Console.WriteLine("Copying structure to secondary..."); 73 | var structureIds = new IntList(new int[] { structure.getVelocityId() }); 74 | var newStructures = sops.copyStructuresToSecondary(structureIds, targetSet.getVelocityId()); 75 | 76 | // IMPORTANT: call save after finishing a set of modifications to a structure set 77 | targetSet = sops.saveStructureSet(targetSet.getVelocityId()); 78 | 79 | OrThrow(newStructures.Count == 1, sops); 80 | var newStructure = newStructures.First().Value; 81 | 82 | Action metrics = delegate () { 83 | var c = sops.conformality(structure.getVelocityId(), newStructure.getVelocityId()); 84 | Console.WriteLine("Conformality: {0}", c); 85 | if (c < 0.0) { 86 | Console.WriteLine("Error: {0}", sops.getErrorMessage()); 87 | } 88 | 89 | var mets = sops.surfaceDistanceMetrics(structure.getVelocityId(), newStructure.getVelocityId()); 90 | if (!mets.isValid) { 91 | Console.WriteLine("Error: {0}", sops.getErrorMessage()); 92 | } else { 93 | Console.WriteLine("Metrics: Hausdorff={0}, min={1}, median={2}, mean={3}, stddev={4}", 94 | mets.hausdorffDistance, mets.min, mets.median, mets.mean, mets.standardDeviation); 95 | } 96 | }; 97 | 98 | // show metrics on registration used for copying 99 | metrics(); 100 | 101 | // now on an alternative registration 102 | ValidOrThrow(engine.loadRegistrationByName(RIGID_NAME), engine); 103 | Console.WriteLine("Loaded registration: {0}", RIGID_NAME); 104 | metrics(); 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /examples/python/BED.py: -------------------------------------------------------------------------------- 1 | 2 | import velocity 3 | import atexit 4 | 5 | USER = 'script' 6 | PASS = 'script' 7 | 8 | #grid settings 9 | GRID_IP = '127.0.0.1' 10 | GRID_PORT = 57000 11 | GRID_DB = 'jakedb' 12 | 13 | #workstation settings 14 | DB_PATH = '/Velocity/Databases/vscDatabase' 15 | 16 | # requires patient 'external DR sum' already imported 17 | PATIENT_ID = '10052012' 18 | PRIMARY_UID = '1.2.840.113619.2.55.3.278435321.302.1313753121.582' 19 | SECONDARY_UID = '1.2.246.352.71.7.1873493392.2486330.20110829091331' 20 | REG_NAME = 'DICOM' 21 | STRUCT_1 = 'Bladder' 22 | STRUCTSET_1UID = '1.2.276.0.7230010.3.1.4.1492057408.5156.1349471628.10' 23 | DEFAULT_ALPHABETA_TISSUE = 3 24 | 25 | e = velocity.VelocityEngine() 26 | def orThrow(c, e=e): 27 | if not c or (hasattr(c, 'isValid') and not c.isValid()): 28 | raise RuntimeError(e.getErrorMessage()) 29 | 30 | orThrow(e.loginToGrid(USER, PASS, GRID_IP, GRID_PORT, GRID_DB)) 31 | #orThrow(e.loginToWorkstation(USER, PASS, DB_PATH, True)) 32 | orThrow(e.loadPatientByPatientId(PATIENT_ID)) 33 | atexit.register(e.logout) 34 | 35 | print('Loaded patient: {}'.format(PATIENT_ID)) 36 | orThrow(e.loadPrimaryVolumeByUID(PRIMARY_UID)) 37 | print('Loaded primary volume: {}'.format(PRIMARY_UID)) 38 | orThrow(e.loadSecondaryVolumeByUID(SECONDARY_UID)) 39 | print('Loaded secondary volume: {}'.format(SECONDARY_UID)) 40 | 41 | # create registration if it doesn't exist 42 | regOps = e.getRegistrationOperations() 43 | orThrow (e.loadRegistrationByName(REG_NAME)) 44 | print('Loaded existing registration object: {}'.format(REG_NAME)) 45 | 46 | structure = e.loadStructureByName(STRUCT_1, STRUCTSET_1UID) 47 | orThrow(structure) 48 | print('Loaded existing structure: {}'.format(STRUCT_1)) 49 | 50 | bedStructure = velocity.BedStructureVariables() 51 | bedStructure.structureId = structure.getVelocityId() 52 | bedStructure.alphaBetaRatio = 2 53 | bedStructure.structureName = structure.getName() 54 | 55 | calculationData = velocity.BedDoseCalculationData() 56 | calculationData.bedVariablesByStructure = velocity.BedStructureVariablesList([bedStructure]) 57 | calculationData.fractions = 25 58 | 59 | backgroundData = velocity.BedBackgroundData() 60 | backgroundData.alphaBetaRatio = DEFAULT_ALPHABETA_TISSUE 61 | 62 | vo = e.getVolumeOperations() 63 | orThrow(vo.createBEDose(calculationData, backgroundData, False) != -1, vo) 64 | print ('Biological Effective Dose created') 65 | -------------------------------------------------------------------------------- /examples/python/dose_summation.py: -------------------------------------------------------------------------------- 1 | import velocity 2 | import sys 3 | import atexit 4 | from datetime import datetime 5 | 6 | DB_USER = 'script' 7 | DB_PASS = 'script' 8 | DB_PATH = 'C:\Velocity\Databases\WKS414' 9 | PATIENT_ID = 'hyperarc' 10 | 11 | #grid settings 12 | GRID_IP = '127.0.0.1' 13 | GRID_PORT = 57000 14 | GRID_DB = 'vscDatabase' 15 | 16 | # optional patient id argument 17 | patientId = sys.argv[1] if len(sys.argv) > 1 else PATIENT_ID 18 | 19 | e = velocity.VelocityEngine() 20 | def orThrow(c, e=e): 21 | if not c or (hasattr(c, 'isValid') and not c.isValid()): 22 | raise RuntimeError(e.getErrorMessage()) 23 | 24 | orThrow(e.loginToWorkstation(DB_USER, DB_PASS, DB_PATH, True)) 25 | #orThrow(e.loginToGrid(USER, PASS, GRID_IP, GRID_PORT, GRID_DB)) 26 | atexit.register(e.logout) # ensure we log out when the script ends 27 | 28 | def makeAutoReg(pVol, sVol, regName): 29 | orThrow(e.loadPrimaryVolume(pVol.getVelocityId())) 30 | print('Loaded {} as primary volume'.format(pVol.getName())) 31 | orThrow(e.loadSecondaryVolume(sVol.getVelocityId())) 32 | print('Loaded {} as secondary volume'.format(sVol.getName())) 33 | print('Creating registration object: {}'.format(regName)) 34 | registration = regOps.createNewRegistration(regName) 35 | orThrow(registration, regOps) 36 | orThrow(e.loadRegistration(registration.getVelocityId())) 37 | # now run rigid registration 38 | print('Running rigid registration...') 39 | regSettings = velocity.DefaultRigidRegistrationSettings() 40 | orThrow(regOps.performRigidRegistrationDICOM(regSettings), regOps) 41 | print('reg done.') 42 | orThrow(regOps.saveRegistration(), regOps) 43 | return registration 44 | 45 | patient = e.loadPatientByPatientId(patientId) 46 | regOps = e.getRegistrationOperations() 47 | volOps = e.getVolumeOperations() 48 | orThrow(patient) 49 | print('Loaded patient: {}'.format(patientId)) 50 | 51 | #sort CTs by AcquisitionDate 52 | cts = list(patient.getVolumes('CT')) 53 | cts.sort(key=lambda x: x.getAcquisitionDate()) 54 | doses = list(patient.getVolumes('RTDOSE')) 55 | 56 | # find all CT-DS in the same frame of reference 57 | # if multiple DS for same CT pick first one that has a plan 58 | print ('-- Finding CT-DS pairs to be resampled --') 59 | ctDosePair = {} 60 | for ct in cts: 61 | plans = list(ct.getLinkedPlans()) 62 | for d in doses: 63 | if d.getName() == 'auto resampled' or d.getName() == 'Sum RTDOSE': continue 64 | if ct.getFrameOfReferenceUID() == d.getFrameOfReferenceUID(): 65 | ctDosePair[ct] = d 66 | if next((p for p in plans if p.getInstanceUID() == d.getPlanUID()), None) != None: 67 | break 68 | 69 | 70 | # resample doses onto the most recent CT 71 | print ('-- Resampling DS onto most recent CT --') 72 | primaryCT = cts[-1] # most recent by Acq Date 73 | resampledDoses = [] 74 | primaryDoseId = -1 75 | 76 | for ct in ctDosePair: 77 | dose = ctDosePair.get(ct) 78 | if ct.getVelocityId() != primaryCT.getVelocityId(): 79 | # need to load CT-DS to activate/create DICOM registration needed for chain 80 | orThrow(e.loadPrimaryVolume(ct.getVelocityId())) 81 | print('Loaded CT {} as primary volume'.format(ct.getName())) 82 | orThrow(e.loadSecondaryVolume(dose.getVelocityId())) 83 | print('Loaded Dose {} as secondary volume'.format(dose.getName())) 84 | dicomReg = e.loadRegistrationByName('DICOM') 85 | orThrow(dicomReg) 86 | 87 | #make new reg for resampling between primary CT and secondary CT 88 | ct2ctReg = makeAutoReg(primaryCT, ct, 'resample_reg') 89 | 90 | # now load primary CT and dose, load chain reg and resample onto primary CT 91 | orThrow(e.loadPrimaryVolume(primaryCT.getVelocityId())) 92 | print('Loaded CT {} as primary volume'.format(primaryCT.getName())) 93 | orThrow(e.loadSecondaryVolume(dose.getVelocityId())) 94 | print('Loaded Dose id {} as secondary volume'.format(dose.getName())) 95 | #load registration resample_reg + DICOM 96 | orThrow(e.loadChainRegistration(ct2ctReg.getVelocityId(), dicomReg.getVelocityId())) 97 | print('Loaded registration: {}'.format('resample_reg + DICOM')) 98 | print('Creating resampled dose...') 99 | id = volOps.createResampledVolume(velocity.VolumeResampleReplace, "auto resampled") 100 | if id == -1: 101 | print('Error creating resampled dose ' + volOps.getErrorMessage()) 102 | continue 103 | print('Resampled dose created with id {}'.format(id)) 104 | resampledDoses.append(id) 105 | else: 106 | #this dose does not require resample, same FOR as primary CT 107 | primaryDoseId = dose.getVelocityId() 108 | resampledDoses.append(primaryDoseId) 109 | 110 | print('Summing {} dose volumes ...'.format(len(resampledDoses))) 111 | orThrow(e.loadPrimaryVolume(primaryDoseId)) # load primary dose as anchor 112 | print('Loaded DS id {} as primary volume'. format(primaryDoseId)) 113 | sumId = volOps.createResampledVolumeAggregate(velocity.VolumeResampleAdd, resampledDoses, velocity.ResampleFlags.AllowNone) # sum 114 | if sumId == -1: 115 | print('Error creating sum dose ' + volOps.getErrorMessage()) 116 | else: 117 | print('Sum dose created with id {}'.format(sumId)) 118 | -------------------------------------------------------------------------------- /examples/python/jacobian_structure_mask.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import velocity 4 | import atexit 5 | import datetime 6 | import math 7 | 8 | DB_NAME = r'vscDatabase' 9 | DB_USER = 'script' 10 | DB_PASS = 'script' 11 | # if workstation 12 | DB_PATH = r'/Velocity/Databases/vscDatabase' 13 | # if grid 14 | DB_IP = '127.0.0.1' 15 | DB_PORT = 57000 16 | 17 | # requires "OPT 3, CBCT adaptive" data 18 | IMPORT_DIR = "C:\demodata\OPT 3, CBCT adaptive" 19 | PATIENT_ID = "H&N 3" 20 | REG_NAME = 'python_registration' 21 | 22 | # create the engine and a helper to check errors 23 | e = velocity.VelocityEngine() 24 | def orThrow(c, e=e): 25 | if not c or (hasattr(c, 'isValid') and not c.isValid()): 26 | raise RuntimeError(e.getErrorMessage()) 27 | 28 | orThrow(e.loginToWorkstation(DB_USER, DB_PASS, DB_PATH, True)) 29 | #orThrow(e.loginToGrid(DB_USER, DB_PASS, DB_IP, DB_PORT, DB_NAME)) 30 | atexit.register(e.logout) # ensure we log out when the script ends 31 | 32 | patient = e.loadPatientByPatientId(PATIENT_ID) 33 | if not patient.isValid(): 34 | print('Importing CBCT and CTSIM...') 35 | iops = e.getImportOperations() 36 | orThrow(iops.importDirectory(IMPORT_DIR, True)) 37 | patient = e.loadPatientByPatientId(PATIENT_ID) 38 | orThrow(patient) 39 | print('Loaded patient: {}'.format(PATIENT_ID)) 40 | 41 | #get and load volumes 42 | volumes = patient.getVolumes() 43 | 44 | primaryVol = next(v for v in volumes if v.getName() == 'CTSIM') 45 | secondaryVol = next(v for v in volumes if v.getName() == 'CBCT') 46 | 47 | orThrow(e.loadPrimaryVolumeByUID(primaryVol.getVolumeUID())) 48 | print('Loaded primary volume: {}'.format(primaryVol.getVolumeUID())) 49 | orThrow(e.loadSecondaryVolumeByUID(secondaryVol.getVolumeUID())) 50 | print('Loaded secondary volume: {}'.format(secondaryVol.getVolumeUID())) 51 | 52 | # create registration 53 | regOps = e.getRegistrationOperations() 54 | print('Creating registration object: {}'.format(REG_NAME)) 55 | registration = regOps.createNewRegistration(REG_NAME) 56 | orThrow(registration, regOps) 57 | orThrow(e.loadRegistration(registration.getVelocityId())) 58 | 59 | 60 | # now run rigid registration 61 | print('Running rigid registration...') 62 | regSettings = velocity.RigidRegistrationSettingsStructure() 63 | orThrow(regOps.performRigidRegistrationDICOM(regSettings), regOps) 64 | print('done.') 65 | 66 | print('Performing deformable registration...') 67 | bsplineSettings = velocity.BSplineDeformableRegistrationSettingsStructure() 68 | orThrow(regOps.performBsplineRegistrationDICOM(bsplineSettings), regOps) 69 | print('done') 70 | 71 | # changes are just in memory, save changes to the database 72 | orThrow(regOps.saveRegistration(), regOps) 73 | 74 | print('Computing Jacobian...') 75 | jacobian = regOps.computeJacobian() 76 | orThrow(jacobian, regOps) 77 | print('done') 78 | 79 | # create mask from volumetric data 80 | thresholdValue = 800 #HU 81 | vops = e.getVolumeOperations() 82 | primaryVol = e.getPrimaryVolume() 83 | volumetricData = vops.getVolumetricData(primaryVol.getVelocityId()) 84 | orThrow(volumetricData, vops) 85 | 86 | # mask should be voxel-aligned with the volume 87 | mask = velocity.StructureMask(volumetricData.size) 88 | 89 | print('Creating mask from threshold... ') 90 | for i in range(0,volumetricData.size[0]*volumetricData.size[1]*volumetricData.size[2]): 91 | isDense = volumetricData.data[i] >= thresholdValue 92 | isNonRigid = abs(math.log(abs(jacobian.data[i]))) >= 0.5 93 | 94 | # find dense material like bone that had non-rigid deformation 95 | mask.data[i] = isDense and isNonRigid 96 | print('done') 97 | 98 | # create a new structure set on the primary volume 99 | sops = e.getStructureOperations() 100 | setName = datetime.datetime.now().isoformat()[:16] 101 | set = sops.createStructureSet(setName) 102 | orThrow(set, sops) 103 | 104 | print('Creating structure from mask ... ') 105 | s1 = sops.createStructureFromMask(set.getVelocityId(), mask, "mask_API") 106 | orThrow(s1, sops) 107 | 108 | # create a new structure by applying a margin 109 | s2 = sops.createStructure(set.getVelocityId(), "mask_API + Margin") 110 | orThrow(s2, sops) 111 | 112 | marginSettings = velocity.MarginSettings(5) 113 | 114 | print('Applying margin ... ') 115 | s2 = sops.margin(set.getVelocityId(), s1.getVelocityId(), s2.getVelocityId(), marginSettings) 116 | orThrow(s2, sops) 117 | 118 | # IMPORTANT: call save after finishing a set of modifications to the structure set 119 | sops.saveStructureSet(set.getVelocityId()) 120 | 121 | print('Done.') 122 | 123 | 124 | -------------------------------------------------------------------------------- /examples/python/liver_import.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import velocity 3 | import atexit 4 | 5 | DB_NAME = r'vscDatabase' 6 | DB_USER = 'script' 7 | DB_PASS = 'script' 8 | # if workstation 9 | DB_PATH = r'/Velocity/Databases/vscDatabase' 10 | # if grid 11 | DB_IP = '127.0.0.1' 12 | DB_PORT = 57000 13 | 14 | IMPORT_DIR = '/demodata/SPECT, Liver' 15 | 16 | LIVER_PATIENT_ID = '0045' 17 | 18 | # create the engine and a helper to check errors 19 | e = velocity.VelocityEngine() 20 | def orThrow(c, e=e): 21 | if not c or (hasattr(c, 'isValid') and not c.isValid()): 22 | raise RuntimeError(e.getErrorMessage()) 23 | 24 | #orThrow(e.loginToWorkstation(DB_USER, DB_PASS, DB_PATH, True)) 25 | orThrow(e.loginToGrid(DB_USER, DB_PASS, DB_IP, DB_PORT, DB_NAME)) 26 | atexit.register(e.logout) # ensure we log out when the script ends 27 | 28 | # a python function to load a patient and list the volume uids 29 | def loadAndListVolumes(patientId): 30 | patient = e.loadPatientByPatientId(patientId) 31 | if not patient.isValid(): 32 | print('Could not load patient id {}, error: {}'.format(patientId, e.getErrorMessage())) 33 | else: 34 | volIds = e.getPatientVolumeUIDs(patientId) 35 | print('Patient id {} has volume UIDs: {}'.format(patientId, volIds)) 36 | 37 | 38 | # try to list liver patient volumes 39 | loadAndListVolumes(LIVER_PATIENT_ID) 40 | 41 | # import the liver patient 42 | iops = e.getImportOperations() 43 | 44 | print('Importing SPECT, Liver ...') 45 | if iops.importDirectory(IMPORT_DIR, True): 46 | print('Successfully imported.') 47 | else: 48 | print('Import error: {}'.format(iops.getErrorMessage())) 49 | 50 | # try again to list the volumes for the liver patient 51 | loadAndListVolumes(LIVER_PATIENT_ID) 52 | 53 | -------------------------------------------------------------------------------- /examples/python/lowDeformHighDiff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import velocity 4 | import atexit 5 | import datetime 6 | import math 7 | 8 | 9 | ''' 10 | Database and login handling. We are setting variables for later 11 | usage. 12 | ''' 13 | # Database login variables. Change as needed. 14 | DB_USER = 'script' 15 | DB_PASS = 'script' # Replace the password here. 16 | 17 | 18 | # Options for connecting to either a Workstation or GRID installation. Modify the fields as necessary. 19 | # if workstation 20 | WKST_DB_PATH = r'C:/Velocity/Databases/vscDatabase' 21 | # if grid 22 | GRID_DB_NAME = r'vscDatabase' 23 | DB_IP = '127.0.0.1' # Assumes same computer as GRID 24 | DB_PORT = 57000 25 | 26 | 27 | # Set desired patient to load, as well as image volume names and registration name. 28 | # Note: The cone-beam CT is set as primary so that the registration 29 | # does not escape the bounds of the smaller image. 30 | PATIENT_ID = "Velocity_1" 31 | PRIMVOL= "CBCT" 32 | SECVOL="CTSIM" 33 | REG_NAME = "deform_01" 34 | 35 | 36 | ''' 37 | Create the Velocity engine client and login. From here, we can load 38 | patients and volumes using velengine as if it were a client. 39 | ''' 40 | # Create the engine and a helper to check errors 41 | e = velocity.VelocityEngine() 42 | def orThrow(c, e=e): 43 | if not c or (hasattr(c, 'isValid') and not c.isValid()): 44 | raise RuntimeError(e.getErrorMessage()) 45 | 46 | 47 | #Login to Velocity user the script user credentials. The current configuration 48 | #is to log into a Grid. In order to log into a Workstation, comment out 49 | #the following line, and uncomment the next line. 50 | orThrow(e.loginToGrid(DB_USER, DB_PASS, DB_IP, DB_PORT, GRID_DB_NAME)) 51 | #orThrow(e.loginToWorkstation(DB_USER, DB_PASS, WKST_DB_PATH, True)) 52 | atexit.register(e.logout) # ensure we log out when the script ends 53 | 54 | 55 | # Load the patient and import operations 56 | patient = e.loadPatientByPatientId(PATIENT_ID) 57 | if not patient.isValid(): 58 | print('Importing CBCT and CTSIM...') 59 | iops = e.getImportOperations() 60 | orThrow(iops.importDirectory(IMPORT_DIR, True)) 61 | patient = e.loadPatientByPatientId(PATIENT_ID) 62 | orThrow(patient) 63 | print('Loaded patient: {}'.format(PATIENT_ID)) 64 | 65 | 66 | # Get and load volumes 67 | volumes = patient.getVolumes() 68 | 69 | 70 | # Assign and set the primary and secondary volumes 71 | primaryVol = next(v for v in volumes if v.getName() == PRIMVOL) 72 | orThrow(e.loadPrimaryVolumeByUID(primaryVol.getVolumeUID())) 73 | print('Loaded primary volume: {}'.format(primaryVol.getVolumeUID())) 74 | secondaryVol = next(v for v in volumes if v.getName() == SECVOL) 75 | orThrow(e.loadSecondaryVolumeByUID(secondaryVol.getVolumeUID())) 76 | print('Loaded secondary volume: {}'.format(secondaryVol.getVolumeUID())) 77 | 78 | 79 | # Load the registration and assign a variable to registration operations 80 | orThrow(e.loadRegistrationByName(REG_NAME)) 81 | print('Loaded registration: {}'.format(REG_NAME)) 82 | regOps = e.getRegistrationOperations() 83 | #orThrow(regOps.saveRegistration(), regOps) 84 | 85 | 86 | ''' 87 | Create subtract from primary resample volume utilizing createResampledVolume(). 88 | The elementOperation of '2' corresponds to VolumeResampleSubtract operation. 89 | The result is a volume with name 'Diff'. 90 | Then, we print the size of the image. 91 | ''' 92 | print('Creating createResampledVolume...') 93 | volOps = e.getVolumeOperations() 94 | SubVolID = volOps.createResampledVolume(2,'Diff') 95 | print('Resampled Volume ID: {}'.format(SubVolID)) 96 | orThrow(e.loadPrimaryVolume(SubVolID)) 97 | print('Load resampled volume: {}'.format(SubVolID)) 98 | diffVolumetricData = volOps.getVolumetricData(SubVolID) 99 | orThrow(diffVolumetricData, volOps) 100 | diffVolumetricSize = diffVolumetricData.size[0]*diffVolumetricData.size[1]*diffVolumetricData.size[2] 101 | print('Calculate createResampledVolume size: {}'.format(diffVolumetricSize)) 102 | 103 | 104 | ''' 105 | Reload CBCT as primary and registration in order to calc Jacobian 106 | Note: Loading a new primary unseats the secondary, which is why 107 | we need to reload the secondary and the registration as well. 108 | ''' 109 | orThrow(e.loadPrimaryVolumeByUID(primaryVol.getVolumeUID())) 110 | print('Loaded primary volume: {}'.format(primaryVol.getVolumeUID())) 111 | orThrow(e.loadSecondaryVolumeByUID(secondaryVol.getVolumeUID())) 112 | print('Loaded secondary volume: {}'.format(secondaryVol.getVolumeUID())) 113 | orThrow(e.loadRegistrationByName(REG_NAME)) 114 | print('Loaded registration: {}'.format(REG_NAME)) 115 | 116 | 117 | # Calculate the Jacobian determinants, which helps us determine the extent of 118 | # volume changes in each voxel. 119 | print('Computing Jacobian...') 120 | jacobian = regOps.computeJacobian() 121 | orThrow(jacobian, regOps) 122 | jacobianSize = jacobian.size[0]*jacobian.size[1]*jacobian.size[2] 123 | print('Calculated Jacobian size: {}'.format(jacobianSize)) 124 | print('Done') 125 | 126 | 127 | ''' 128 | Create mask from volumetric data in order to determine if there are regions 129 | of high difference between the primary volume and deformed secondary volume, 130 | but if there also was a lack of deformation in those regions. 131 | ''' 132 | vops = e.getVolumeOperations() 133 | primaryVol = e.getPrimaryVolume() 134 | volumetricData = vops.getVolumetricData(primaryVol.getVelocityId()) 135 | orThrow(volumetricData, vops) 136 | print('Creating mask using Diff and Jacobian...') 137 | 138 | diffMask = velocity.StructureMask(diffVolumetricData.size) 139 | for i in range(0, diffVolumetricData.size[0]*diffVolumetricData.size[1]*diffVolumetricData.size[2]): 140 | isDiff = abs(diffVolumetricData.data[i]) >= 700 141 | isRigid = abs(math.log(abs(jacobian.data[i]))) <= 0.5 142 | # Create a mask in which there is a significant difference between primary 143 | # and deformed secondary volume but not a significant change in volume 144 | diffMask.data[i] = isDiff and isRigid 145 | print('Done') 146 | 147 | 148 | # Mask should be voxel-aligned with the primary volume 149 | mask = velocity.StructureMask(volumetricData.size) 150 | 151 | 152 | # Create a new structure set on the primary volume 153 | sops = e.getStructureOperations() 154 | setName = datetime.datetime.now().isoformat()[5:16] 155 | set = sops.createStructureSet(setName) 156 | orThrow(set, sops) 157 | 158 | print('Creating structure from mask...') 159 | s1 = sops.createStructureFromMask(set.getVelocityId(), diffMask, "LowDeformHighDiff") 160 | orThrow(s1, sops) 161 | 162 | 163 | # IMPORTANT: call save after finishing a set of modifications to the structure set 164 | sops.saveStructureSet(set.getVelocityId()) 165 | 166 | print('Done.') 167 | -------------------------------------------------------------------------------- /examples/python/pet_ct_registrations.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import velocity 4 | import atexit 5 | 6 | DB_NAME = r'vscDatabase' 7 | DB_USER = 'script' 8 | DB_PASS = 'script' 9 | # if workstation 10 | DB_PATH = r'/Velocity/Databases/vscDatabase' 11 | # if grid 12 | DB_IP = '127.0.0.1' 13 | DB_PORT = 57000 14 | 15 | # requires "PETCT, Deformable" data already imported 16 | PATIENT_ID = "H&N 1" 17 | PRIMARY_UID = '1.3.12.2.1107.5.1.4.1031.30000009080711125237500008699' 18 | SECONDARY_UID = '1.2.840.113704.1.111.3200.1253806429.28' 19 | REG_NAME = 'python_registration' 20 | 21 | # create the engine and a helper to check errors 22 | e = velocity.VelocityEngine() 23 | def orThrow(c, e=e): 24 | if not c or (hasattr(c, 'isValid') and not c.isValid()): 25 | raise RuntimeError(e.getErrorMessage()) 26 | 27 | #orThrow(e.loginToWorkstation(DB_USER, DB_PASS, DB_PATH, True)) 28 | orThrow(e.loginToGrid(DB_USER, DB_PASS, DB_IP, DB_PORT, DB_NAME)) 29 | atexit.register(e.logout) # ensure we log out when the script ends 30 | 31 | orThrow(e.loadPatientByPatientId(PATIENT_ID)) 32 | print('Loaded patient: {}'.format(PATIENT_ID)) 33 | orThrow(e.loadPrimaryVolumeByUID(PRIMARY_UID)) 34 | print('Loaded primary volume: {}'.format(PRIMARY_UID)) 35 | orThrow(e.loadSecondaryVolumeByUID(SECONDARY_UID)) 36 | print('Loaded secondary volume: {}'.format(SECONDARY_UID)) 37 | 38 | # create registration 39 | regOps = e.getRegistrationOperations() 40 | print('Creating registration object: {}'.format(REG_NAME)) 41 | registration = regOps.createNewRegistration(REG_NAME) 42 | orThrow(registration, regOps) 43 | orThrow(e.loadRegistration(registration.getVelocityId())) 44 | 45 | # first move the head into the right general area 46 | print('Running manual registration...') 47 | manualSettings = velocity.ManualRegistrationSettingsStructure() 48 | manualSettings.registrationMatrix = ( 49 | 1.0, 0.0, 0.0, 0.0, 50 | 0.0, 1.0, 0.0, 0.0, 51 | 0.0, 0.0, 1.0, 0.0, 52 | -8.1947, -275.0845, -394.5001, 1.0) 53 | orThrow(regOps.performManualAlignmentDICOM(manualSettings), regOps) 54 | print('done.') 55 | 56 | # now run rigid registration 57 | print('Running rigid registration...') 58 | regSettings = velocity.RigidRegistrationSettingsStructure() 59 | regSettings.roiStart[0] = -102.021; 60 | regSettings.roiStart[1] = -74.7234; 61 | regSettings.roiStart[2] = -5.49291; 62 | regSettings.roiEnd[0] = 100.532; 63 | regSettings.roiEnd[1] = -410.341; 64 | regSettings.roiEnd[2] = -309.819; 65 | 66 | regSettings.primaryStartLevel = -110.02; 67 | regSettings.primaryEndLevel = 189.974; 68 | regSettings.secondaryStartLevel = -125.014; 69 | regSettings.secondaryEndLevel = 224.998; 70 | 71 | regSettings.preProcessingMethod = 0 72 | regSettings.performInitialAutoAlignment = True 73 | regSettings.disableRotations = False 74 | regSettings.maximumNumberOfIterations = 45 75 | regSettings.minimumStepLength = 0.0001 76 | regSettings.maximumStepLength = 17.0 77 | regSettings.samplesDenominator = 10 78 | regSettings.numberOfHistogramBins = 25 79 | orThrow(regOps.performRigidRegistrationDICOM(regSettings), regOps) 80 | print('done.') 81 | 82 | print('Performing deformable registration...') 83 | bsplineSettings = velocity.BSplineDeformableRegistrationSettingsStructure() 84 | bsplineSettings.roiStart[0] = -102.021 85 | bsplineSettings.roiStart[1] = -74.7234 86 | bsplineSettings.roiStart[2] = -5.49291 87 | bsplineSettings.roiEnd[0] = 100.532; 88 | bsplineSettings.roiEnd[1] = -410.341; 89 | bsplineSettings.roiEnd[2] = -309.819; 90 | 91 | bsplineSettings.primaryStartLevel = -110.02 92 | bsplineSettings.primaryEndLevel = 189.974 93 | bsplineSettings.secondaryStartLevel = -125.014 94 | bsplineSettings.secondaryEndLevel = 224.998 95 | 96 | bsplineSettings.preprocessingMethod = 0 97 | bsplineSettings.numberOfMultiResolutionLevels = 3 # so each vector setting should be length 3 98 | bsplineSettings.applyBoundaryContinuityConstraints = velocity.BoolList([False]*3) 99 | bsplineSettings.applyTopologicalRegularizer = velocity.BoolList([False]*3) 100 | r3dZeroes = velocity.VectorR3d(0.0) 101 | bsplineSettings.topologicalRegularizerDistanceLimitingCoefficient = velocity.VectorR3dList([r3dZeroes]*3) 102 | bsplineSettings.numberOfHistogramBins = velocity.IntList([50]*3) 103 | bsplineSettings.maximumNumberOfIterations = velocity.IntList([30]*3) 104 | bsplineSettings.maximumNumberOfConsecutiveOptimizerAttempts = velocity.IntList([10]*3) 105 | bsplineSettings.metricValuePercentageDifference = velocity.DoubleList([0.0]*3) 106 | bsplineSettings.minimumStepLength = velocity.DoubleList([0.000001]*3) 107 | bsplineSettings.maximumStepLength = velocity.DoubleList([100.0]*3) 108 | bsplineSettings.samplesDenominator = velocity.IntList([5]*3) 109 | bsplineSettings.relaxationFactor = velocity.DoubleList([0.9]*3) 110 | bsplineSettings.gradientMagnitudeTolerance = velocity.DoubleList([0.000000000000000000005]*3) 111 | bsplineSettings.gridCellSize = velocity.VectorR3dList(( velocity.VectorR3d(5.0), velocity.VectorR3d(10.0), velocity.VectorR3d(15.0) )) 112 | bsplineSettings.gridCellSizeType = velocity.CharList([ord('n')]*3) 113 | 114 | orThrow(regOps.performBsplineRegistrationDICOM(bsplineSettings), regOps) 115 | print('done') 116 | 117 | # changes are just in memory, save changes to the database 118 | orThrow(regOps.saveRegistration(), regOps) 119 | -------------------------------------------------------------------------------- /examples/python/print_patient.py: -------------------------------------------------------------------------------- 1 | 2 | import velocity 3 | import sys 4 | import atexit 5 | 6 | DB_NAME = r'vscDatabase' 7 | DB_USER = 'script' 8 | DB_PASS = 'script' 9 | # if workstation 10 | DB_PATH = r'/Velocity/Databases/vscDatabase' 11 | # if grid 12 | DB_IP = '127.0.0.1' 13 | DB_PORT = 57000 14 | 15 | # assumes "sixCBCT, AdaptiveMonitoring" patient loaded by default 16 | PATIENT_ID = 'AW3Y6TA684' 17 | 18 | # optional patient id argument 19 | patientId = sys.argv[1] if len(sys.argv) > 1 else PATIENT_ID 20 | 21 | e = velocity.VelocityEngine() 22 | def orThrow(c, e=e): 23 | if not c or (hasattr(c, 'isValid') and not c.isValid()): 24 | raise RuntimeError(e.getErrorMessage()) 25 | 26 | #orThrow(e.loginToWorkstation(DB_USER, DB_PASS, DB_PATH, True)) 27 | orThrow(e.loginToGrid(DB_USER, DB_PASS, DB_IP, DB_PORT, DB_NAME)) 28 | atexit.register(e.logout) # ensure we log out when the script ends 29 | 30 | pops = e.getPatientDataOperations() 31 | patient = pops.getPatientByPatientId(patientId) 32 | orThrow(patient, pops) 33 | 34 | def compare_key(obj_and_name): 35 | """Compare objects for printing. We consider 36 | wrapped lists (having size()) to come after anything else, 37 | otherwise sort by name. 38 | """ 39 | obj, obj_name = obj_and_name 40 | return (hasattr(obj, 'size'), obj_name) 41 | 42 | def print_object(obj, obj_name, indent=0, seen_ids=None): 43 | """Recursively prints an object. 44 | @param obj the object to print 45 | @param obj_name the object name used when printing 46 | @param indent indentation level, for internal use 47 | @param seen_ids infinite recursion prevention, for internal use 48 | """ 49 | pre = '-' * indent 50 | if seen_ids is None: 51 | seen_ids = set() 52 | # for std::vector wrappers 53 | if hasattr(obj, 'size'): 54 | # print the name, then each child, e.g. 'obj', then 'obj[0]', 'obj[1]', etc 55 | print('{}{} [{} elements]:'.format(pre, obj_name, obj.size())) 56 | for i, child in enumerate(obj): 57 | child_name = obj_name + '[' + str(i) + ']' 58 | print_object(child, child_name, indent + 1, seen_ids) 59 | # for non-API types 60 | elif not hasattr(obj, 'this'): 61 | # print the name and value 62 | print('{}{}: {}'.format(pre, obj_name, obj)) 63 | # for all other API types 64 | else: 65 | # prevent infinite loop by checking for already seen objects 66 | seen_id = (repr(type(obj)), obj.getVelocityId()) 67 | if seen_id in seen_ids: 68 | print('{}{}: {}'.format(pre, obj_name, "[ALREADY SEEN]")) 69 | else: 70 | # remember that we saw this one 71 | seen_ids.add(seen_id) 72 | 73 | # get all the data inspection functions and call them 74 | try: 75 | children = [ (getattr(obj, attr)(), attr) for attr in dir(obj) if (attr.startswith('get') or attr.startswith('is')) and attr not in ('this', 'getRecord') ] 76 | # sort to get primitives before child lists 77 | children.sort(key=compare_key) 78 | # now recurse to all the children 79 | for child, attr in children: 80 | child_name = obj_name + '.' + attr + '()' 81 | print_object(child, child_name, indent + 1, seen_ids) 82 | except: 83 | print('Problem with object: {} (attrs={})'.format(obj, dir(obj))) 84 | 85 | 86 | print_object(patient, 'patient') 87 | -------------------------------------------------------------------------------- /examples/python/structure_metrics.py: -------------------------------------------------------------------------------- 1 | 2 | import velocity 3 | import sys 4 | import atexit 5 | import uuid 6 | import datetime 7 | 8 | DB_NAME = r'vscDatabase' 9 | DB_USER = 'script' 10 | DB_PASS = 'script' 11 | # if workstation 12 | DB_PATH = r'/Velocity/Databases/vscDatabase' 13 | # if grid 14 | DB_IP = '127.0.0.1' 15 | DB_PORT = 57000 16 | 17 | # requires "sixCBCT, AdaptiveMonitoring" data already imported 18 | PATIENT_ID = 'AW3Y6TA684' 19 | PRIMARY_UID = '1.3.12.2.1107.5.1.4.54841.30000011071412175920300003025' 20 | SECONDARY_UID = '1.2.246.352.61.2.4621874044879001489.17114159699319862401' 21 | REGISTRATION_NAME = 'CBCT MULTI' 22 | RIGID_NAME = "RIGID" 23 | 24 | e = velocity.VelocityEngine() 25 | def orThrow(c, e=e): 26 | if not c or (hasattr(c, 'isValid') and not c.isValid()): 27 | raise RuntimeError(e.getErrorMessage()) 28 | 29 | #orThrow(e.loginToWorkstation(DB_USER, DB_PASS, DB_PATH, True)) 30 | orThrow(e.loginToGrid(DB_USER, DB_PASS, DB_IP, DB_PORT, DB_NAME)) 31 | atexit.register(e.logout) 32 | orThrow(e.loadPatientByPatientId(PATIENT_ID)) 33 | print('Loaded patient: {}'.format(PATIENT_ID)) 34 | orThrow(e.loadPrimaryVolumeByUID(PRIMARY_UID)) 35 | print('Loaded primary volume: {}'.format(PRIMARY_UID)) 36 | orThrow(e.loadSecondaryVolumeByUID(SECONDARY_UID)) 37 | print('Loaded secondary volume: {}'.format(SECONDARY_UID)) 38 | orThrow(e.loadRegistrationByName(REGISTRATION_NAME)) 39 | print('Loaded registration: {}'.format(REGISTRATION_NAME)) 40 | 41 | rops = e.getRegistrationOperations() 42 | sops = e.getStructureOperations() 43 | 44 | primaryVol = e.getPrimaryVolume() 45 | secondaryVol = e.getSecondaryVolume() 46 | 47 | 48 | # find an external structure, throws StopIteration if not found 49 | primarySet = next(s for s in primaryVol.getStructureSets() if s.getName() == 'Original SIM') 50 | structure = next(s for s in primarySet.getStructures() if s.getName() == 'Mandible') 51 | print('Using structure "{}" from structure set "{}"'.format(structure.getName(), primarySet.getName())) 52 | 53 | # create a new structure set on the secondary volume 54 | targetSetName = datetime.datetime.now().isoformat()[:16] 55 | targetSet = sops.createStructureSet(targetSetName, False) 56 | orThrow(targetSet, sops) 57 | 58 | # copy the external to the new structure set 59 | print('Copying structure to secondary...') 60 | newStructures = sops.copyStructuresToSecondary([structure.getVelocityId()], targetSet.getVelocityId()) 61 | 62 | # IMPORTANT: call save after finishing a set of modifications to a structure set 63 | targetSet = sops.saveStructureSet(targetSet.getVelocityId()) 64 | 65 | orThrow(len(newStructures) == 1, sops) 66 | newStructureId = next(iter(newStructures)) 67 | newStructure = newStructures[newStructureId] 68 | print('Structure copied to secondary, computing metrics...') 69 | 70 | def metrics(): 71 | # compute metrics on the copied external structure 72 | c = sops.conformality(structure.getVelocityId(), newStructure.getVelocityId()) 73 | print('Conformality: {}'.format(c)) 74 | if c < 0.0: 75 | print('Error: {}'.format(sops.getErrorMessage())) 76 | 77 | mets = sops.surfaceDistanceMetrics(structure.getVelocityId(), newStructure.getVelocityId()) 78 | if not mets.isValid: 79 | print('Error: {}'.format(sops.getErrorMessage())) 80 | else: 81 | print('Metrics: Hausdorff={}, min={}, median={}, mean={}, stddev={}'.format(mets.hausdorffDistance, mets.min, mets.median, mets.mean, mets.standardDeviation)) 82 | 83 | # show metrics on registration used for copying 84 | metrics() 85 | 86 | # now on an alternative registration 87 | orThrow(e.loadRegistrationByName(RIGID_NAME)) 88 | print('Loaded registration: {}'.format(RIGID_NAME)) 89 | metrics() 90 | --------------------------------------------------------------------------------