Ejemplo n.º 1
0
        /// <summary>
        /// This splits a structure into two. It creates a margin around the target,
        /// so that it approximately crosses the mass center of the structure, and then
        /// uses boolean operators to create two new structures. Note: due to the nature
        /// of the segment model in Eclipse, the new structures do not always perfectly cover
        /// the whole volume of the original structure.
        /// </summary>
        /// <param name="s"></param>
        /// <param name="ss"></param>
        /// <returns></returns>
        bool SplitStructure(StructureSet ss, Structure target, Structure roi)
        {
            if (ss.CanAddStructure(roi.DicomType, roi.Id + "_spl1"))
            {
                Structure newStr1 = ss.AddStructure(roi.DicomType, roi.Id + "_spl1");
                Structure newStr2 = ss.AddStructure(roi.DicomType, roi.Id + "_spl2");

                VVector targetCenter = target.CenterPoint;
                VVector roiCenter    = roi.CenterPoint;
                double  dist         = (targetCenter - roiCenter).Length;

                //figure out distance from target center to target surface
                System.Collections.BitArray buffer = new System.Collections.BitArray(100);
                SegmentProfile profile             = target.GetSegmentProfile(targetCenter, roiCenter, buffer);
                double         distToTargetSurface = 0;
                foreach (SegmentProfilePoint point in profile)
                {
                    if (point.Value == false)
                    {
                        //first point outside structure
                        distToTargetSurface = (point.Position - targetCenter).Length;
                        break;
                    }
                }
                //SegmentVolume seg = target.Margin(dist - distToTargetSurface);
                SegmentVolume seg = target.LargeMargin(dist - distToTargetSurface);
                newStr1.SegmentVolume = seg.And(roi);
                newStr2.SegmentVolume = roi.Sub(newStr1);
                return(true);
            }
            return(false);
        }
        public void Execute(ScriptContext context /*, System.Windows.Window window, ScriptEnvironment environment*/)
        {
            // TODO : Add here the code that is called when the script is launched from Eclipse.

            if (context.PlanSetup == null)
            {
                MessageBox.Show("Please load a plan before running this script.", SCRIPT_NAME, MessageBoxButton.OK, MessageBoxImage.Exclamation);
                return;
            }
            if (context.PlanSetup.Dose == null)
            {
                MessageBox.Show("Please complete dose calculation  before running this script.", SCRIPT_NAME, MessageBoxButton.OK, MessageBoxImage.Exclamation);
                return;
            }

            StructureSet ss      = context.StructureSet;
            Dose         dose    = context.PlanSetup.Dose;
            string       logtext = "";

            // enable writing with this script.
            context.Patient.BeginModifications();

            foreach (Isodose isodose in context.PlanSetup.Dose.Isodoses)
            {
                string isoStructureName = "";
                isoStructureName = context.PlanSetup.Id + "_"
                                   + string.Format("{0:f3}", Math.Round(context.PlanSetup.TotalDose.Dose * isodose.Level.Dose / 100.0, 3)) + "Gy";
                //isoStructureName = context.PlanSetup.Id + "_" + isodose.Level.ValueAsString + isodose.Level.UnitAsString;
                int    nameLength     = isoStructureName.Length;
                string isoStructureId = "";
                if (isoStructureName.Length > 16)
                {
                    isoStructureId = isoStructureName.Substring(0, 16);
                }
                else
                {
                    isoStructureId = isoStructureName;
                }

                if (ss.CanAddStructure("DOSE_REGION", isoStructureId) == true)
                {
                    Structure newIsoStructure = ss.Structures.FirstOrDefault(x => x.Id == isoStructureId);
                    if (newIsoStructure == null)
                    {
                        newIsoStructure = ss.AddStructure("DOSE_REGION", isoStructureId);
                    }
                    newIsoStructure.ConvertDoseLevelToStructure(dose, isodose.Level);
                    logtext += isoStructureName + "\n";
                }
            }
            MessageBox.Show(logtext + "\nDone.", SCRIPT_NAME);
        }
Ejemplo n.º 3
0
        public static Structure CreateStructureIfNotExists(this StructureSet ss, string id, string dicomType)
        {
            var match = ss.Structures.FirstOrDefault(s => s.Id == id);

            if (match != null)
            {
                return(match);
            }

            if (!ss.CanAddStructure(dicomType, id))
            {
                return(null);
            }

            return(ss.AddStructure(dicomType, id));
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Select a section of body structure that is X mm up and down from an existing structure's
        /// top and bottom slices. Make it into a new structure.
        /// </summary>
        /// <param name="s">The structure to enlarge</param>
        /// <param name="ss">The structure set where to add</param>
        /// <param name="upMm">The margin up in mm</param>
        /// <param name="downMm">The margin down in mm</param>
        /// <returns>The new structure. Null if new structure cannot be added.</returns>
        Structure EnlargeStructure(Structure s, StructureSet ss, double upMm, double downMm)
        {
            if (ss.CanAddStructure(s.DicomType, s.Id + "_enl"))
            {
                var    newStr     = ss.AddStructure(s.DicomType, s.Id + "_enl");
                var    image      = ss.Image;
                double res        = image.ZRes;
                int    startSlice = 0;
                for (int z = 0; z < image.ZSize; z++)
                {
                    if (s.GetContoursOnImagePlane(z).Count() > 0)
                    {
                        startSlice = (int)(z - downMm / res);
                        break;
                    }
                }
                int stopSlice = image.ZSize - 1;
                for (int z = image.ZSize - 1; z >= 0; z--)
                {
                    if (s.GetContoursOnImagePlane(z).Count() > 0)
                    {
                        stopSlice = (int)(z + upMm / res);
                        break;
                    }
                }
                startSlice = Math.Max(startSlice, 0);
                stopSlice  = Math.Min(stopSlice, image.ZSize - 1);
                var contour = new VVector[4]
                {
                    // these are real coordinates, not voxel coordinates
                    new VVector(-10000, -10000, 0),
                    new VVector(10000, -10000, 0),
                    new VVector(10000, 10000, 0),
                    new VVector(-10000, 10000, 0)
                };

                for (int z = startSlice; z <= stopSlice; z++)
                {
                    newStr.AddContourOnImagePlane(contour, z);
                }
                var body = ss.Structures.First(st => st.DicomType == "EXTERNAL");
                newStr.SegmentVolume = newStr.And(body);
                return(newStr);
            }
            return(null);
        }
Ejemplo n.º 5
0
        private bool preliminaryChecks()
        {
            //check if user origin was set
            //get the points collection for the Body (used for calculating number of isocenters)
            Point3DCollection pts = selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "body").MeshGeometry.Positions;

            if (!selectedSS.Image.HasUserOrigin || !(selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "body").IsPointInsideSegment(selectedSS.Image.UserOrigin)))
            {
                MessageBox.Show("Did you forget to set the user origin? \nUser origin is NOT inside body contour! \nPlease fix and try again!");
                return(true);
            }

            //check if patient length is > 116cm, if so, check for matchline contour
            if ((pts.Max(p => p.Z) - pts.Min(p => p.Z)) > 1160.0 && !(selectedSS.Structures.Where(x => x.Id.ToLower() == "matchline").Any()))
            {
                //check to see if the user wants to proceed even though there is no matchplane contour or the matchplane contour exists, but is not filled
                VMATTBIautoPlan.confirmUI CUI = new VMATTBIautoPlan.confirmUI();
                CUI.message.Text = "No matchplane contour found even though patient length > 116.0 cm!" + Environment.NewLine + Environment.NewLine + "Continue?!";
                CUI.ShowDialog();
                if (!CUI.confirm)
                {
                    return(true);
                }

                //checks for LA16 couch and spinning manny couch/bolt will be performed at optimization stage
            }

            //calculate number of required isocenters
            if (!(selectedSS.Structures.Where(x => x.Id.ToLower() == "matchline").Any()))
            {
                //no matchline implying that this patient will be treated with VMAT only. For these cases the maximum number of allowed isocenters is 3.
                //the reason for the explicit statements calculating the number of isos and then truncating them to 3 was to account for patients requiring < 3 isos and if, later on, we want to remove the restriction of 3 isos
                numIsos = numVMATIsos = (int)Math.Ceiling(((pts.Max(p => p.Z) - pts.Min(p => p.Z)) / (400.0 - 20.0)));
                if (numIsos > 3)
                {
                    numIsos = numVMATIsos = 3;
                }
            }
            else
            {
                //matchline structure is present, but empty
                if (selectedSS.Structures.First(x => x.Id.ToLower() == "matchline").IsEmpty)
                {
                    VMATTBIautoPlan.confirmUI CUI = new VMATTBIautoPlan.confirmUI();
                    CUI.message.Text = "I found a matchline structure in the structure set, but it's empty!" + Environment.NewLine + Environment.NewLine + "Do you want to continue without using the matchline structure?!";
                    CUI.ShowDialog();
                    if (!CUI.confirm)
                    {
                        return(true);
                    }

                    //continue and ignore the empty matchline structure (same calculation as VMAT only)
                    numIsos = numVMATIsos = (int)Math.Ceiling(((pts.Max(p => p.Z) - pts.Min(p => p.Z)) / (400.0 - 20.0)));
                    if (numIsos > 3)
                    {
                        numIsos = numVMATIsos = 3;
                    }
                }
                //matchline structure is present and not empty
                else
                {
                    //get number of isos for PTV superior to matchplane (always truncate this value to a maximum of 3 isocenters)
                    numVMATIsos = (int)Math.Ceiling(((pts.Max(p => p.Z) - selectedSS.Structures.First(x => x.Id.ToLower() == "matchline").CenterPoint.z) / (400.0 - 20.0)));
                    if (numVMATIsos > 3)
                    {
                        numVMATIsos = 3;
                    }

                    //get number of iso for PTV inferior to matchplane
                    //if (selectedSS.Structures.First(x => x.Id.ToLower() == "matchline").CenterPoint.z - pts.Min(p => p.Z) - 3.0 <= (400.0 - 20.0)) numIsos = numVMATIsos + 1;

                    //5-20-2020 Nataliya said to only add a second legs iso if the extent of the TS_PTV_LEGS is > 40.0 cm
                    if (selectedSS.Structures.First(x => x.Id.ToLower() == "matchline").CenterPoint.z - pts.Min(p => p.Z) - 3.0 <= (400.0 - 0.0))
                    {
                        numIsos = numVMATIsos + 1;
                    }
                    else
                    {
                        numIsos = numVMATIsos + 2;
                    }
                    //MessageBox.Show(String.Format("{0}", selectedSS.Structures.First(x => x.Id.ToLower() == "matchline").CenterPoint.z - pts.Min(p => p.Z) - 3.0));
                }
            }

            //set isocenter names based on numIsos and numVMATIsos (determined these names from prior cases). Need to implement a more clever way to name the isocenters
            isoNames.Add("Head");
            if (numVMATIsos == 2)
            {
                isoNames.Add("Pelvis");
            }
            else if (numVMATIsos == 3 && numIsos > numVMATIsos)
            {
                isoNames.Add("Chest"); isoNames.Add("Pelvis");
            }
            //this could technically be an else statement, but I left it as an else-if statement so it's explicit what situation is being considered here
            else if (numVMATIsos == 3 && numIsos == numVMATIsos)
            {
                isoNames.Add("Pelvis"); isoNames.Add("Legs");
            }
            if (numIsos > numVMATIsos)
            {
                isoNames.Add("AP / PA upper legs");
                if (numIsos == numVMATIsos + 2)
                {
                    isoNames.Add("AP / PA lower legs");
                }
            }

            //check if selected structures are empty or of high-resolution (i.e., no operations can be performed on high-resolution structures)
            string           output            = "The following structures are high-resolution:" + System.Environment.NewLine;
            List <Structure> highResStructList = new List <Structure> {
            };
            List <Tuple <string, string, double> > highResSpareList = new List <Tuple <string, string, double> > {
            };

            foreach (Tuple <string, string, double> itr in spareStructList)
            {
                if (itr.Item2 == "Mean Dose < Rx Dose")
                {
                    if (selectedSS.Structures.First(x => x.Id == itr.Item1).IsEmpty)
                    {
                        MessageBox.Show(String.Format("Error! \nThe selected structure that will be subtracted from PTV_Body and TS_PTV_VMAT is empty! \nContour the structure and try again."));
                        return(true);
                    }
                    else if (selectedSS.Structures.First(x => x.Id == itr.Item1).IsHighResolution)
                    {
                        highResStructList.Add(selectedSS.Structures.First(x => x.Id == itr.Item1));
                        highResSpareList.Add(itr);
                        output += String.Format("{0}", itr.Item1) + System.Environment.NewLine;
                    }
                }
            }
            //if there are high resolution structures, they will need to be converted to default resolution.
            if (highResStructList.Count() > 0)
            {
                //ask user if they are ok with converting the relevant high resolution structures to default resolution
                output += "They must be converted to default resolution before proceeding!";
                VMATTBIautoPlan.confirmUI CUI = new VMATTBIautoPlan.confirmUI();
                CUI.message.Text = output + Environment.NewLine + Environment.NewLine + "Continue?!";
                CUI.message.Font = new System.Drawing.Font("Microsoft Sans Serif", 11F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
                CUI.ShowDialog();
                if (!CUI.confirm)
                {
                    return(true);
                }

                int count = 0;
                foreach (Structure s in highResStructList)
                {
                    //get the high res structure mesh geometry
                    MeshGeometry3D mesh = s.MeshGeometry;
                    //get the start and stop image planes for this structure
                    int startSlice = (int)((mesh.Bounds.Z - selectedSS.Image.Origin.z) / selectedSS.Image.ZRes);
                    int stopSlice  = (int)(((mesh.Bounds.Z + mesh.Bounds.SizeZ) - selectedSS.Image.Origin.z) / selectedSS.Image.ZRes) + 1;
                    //create an Id for the low resolution struture that will be created. The name will be '_lowRes' appended to the current structure Id
                    string newName = s.Id + "_lowRes";
                    if (newName.Length > 16)
                    {
                        newName = newName.Substring(0, 16);
                    }
                    //add a new structure (default resolution by default)
                    Structure lowRes = null;
                    if (selectedSS.CanAddStructure("CONTROL", newName))
                    {
                        lowRes = selectedSS.AddStructure("CONTROL", newName);
                    }
                    else
                    {
                        MessageBox.Show(String.Format("Error! Cannot add new structure: {0}!\nCorrect this issue and try again!", newName.Substring(0, 16)));
                        return(true);
                    }

                    //foreach slice that contains contours, get the contours, and determine if you need to add or subtract the contours on the given image plane for the new low resolution structure. You need to subtract contours if the points lie INSIDE the current structure contour.
                    //We can sample three points (first, middle, and last points in array) to see if they are inside the current contour. If any of them are, subtract the set of contours from the image plane. Otherwise, add the contours to the image plane. NOTE: THIS LOGIC ASSUMES
                    //THAT YOU DO NOT OBTAIN THE CUTOUT CONTOUR POINTS BEFORE THE OUTER CONTOUR POINTS (it seems that ESAPI generally passes the main structure contours first before the cutout contours, but more testing is needed)
                    //string data = "";
                    for (int slice = startSlice; slice < stopSlice; slice++)
                    {
                        VVector[][] points = s.GetContoursOnImagePlane(slice);
                        for (int i = 0; i < points.GetLength(0); i++)
                        {
                            //for (int j = 0; j < points[i].GetLength(0); j++) data += String.Format("{0}, {1}, {2}", points[i][j].x, points[i][j].y, points[i][j].z) + System.Environment.NewLine;
                            if (lowRes.IsPointInsideSegment(points[i][0]) || lowRes.IsPointInsideSegment(points[i][points[i].GetLength(0) - 1]) || lowRes.IsPointInsideSegment(points[i][(int)(points[i].GetLength(0) / 2)]))
                            {
                                lowRes.SubtractContourOnImagePlane(points[i], slice);
                            }
                            else
                            {
                                lowRes.AddContourOnImagePlane(points[i], slice);
                            }
                            //data += System.Environment.NewLine;
                        }
                    }

                    //get the index of the high resolution structure in the structure sparing list and repace this entry with the newly created low resolution structure
                    int index = spareStructList.IndexOf(highResSpareList.ElementAt(count));
                    spareStructList.RemoveAt(index);
                    spareStructList.Insert(index, new Tuple <string, string, double>(newName, highResSpareList.ElementAt(count).Item2, highResSpareList.ElementAt(count).Item3));
                    count++;

                    //data += System.Environment.NewLine;
                    //points = lowRes.GetContoursOnImagePlane((int)((stopSlice + startSlice) / 2));
                    //for (int i = 0; i < points.GetLength(0); i++)
                    //{
                    //    for (int j = 0; j < points[i].GetLength(0); j++) data += String.Format("{0}, {1}, {2}", points[i][j].x, points[i][j].y, points[i][j].z) + System.Environment.NewLine;
                    //    //data += System.Environment.NewLine;
                    //}

                    //File.WriteAllText(@"\\enterprise.stanfordmed.org\depts\RadiationTherapy\Public\Users\ESimiele\vvectorData.txt", data);
                }
                //inform the main UI class that the UI needs to be updated
                updateSparingList = true;

                //string mod = "";
                //foreach (Tuple<string, string, double> itr in spareStructList) mod += String.Format("{0}, {1}, {2}", itr.Item1, itr.Item2, itr.Item3) + System.Environment.NewLine;
                //MessageBox.Show(test);
                //MessageBox.Show(mod);
            }
            return(false);
        }