public void computeStats(IEnumerable <VVector> sourcePoints, IEnumerable <VVector> registeredPoints, IEnumerable <VVector> transformedPoints) { // ensure that all lists have same number of points. if (sourcePoints.Count() != registeredPoints.Count() || sourcePoints.Count() != transformedPoints.Count()) { throw new ApplicationException("source and registered match points don't have the same # of points!"); } // compute statistics double sum = 0; int numPoints = sourcePoints.Count(); for (int i = 0; i < numPoints; i++) { VVector source = sourcePoints.ElementAt(i); VVector registered = registeredPoints.ElementAt(i); VVector derived = transformedPoints.ElementAt(i); double distance = VVector.Distance(registered, derived); min = Math.Min(min, distance); max = Math.Max(max, distance); sum += distance; } mean = sum / (double)numPoints; }
public static int ProfileNumberOfIntervals(VVector start, VVector end, double res = 1.0) { double distance = VVector.Distance(start, end); int num = ((int)(distance / res)); return(num); }
public static VVector ProfileEndVVector(VVector start, VVector end, double res = 1.0) { double distance = VVector.Distance(start, end); int num = ProfileNumberOfIntervals(start, end, res); VVector truncatedEnd = start + ((num * res) / distance) * (end - start); return(truncatedEnd); }
public static bool[] ProfileStructureMask(VVector start, VVector end, Structure structure, double res = 1.0) { int numberOfIntervals = ProfileNumberOfIntervals(start, end, res); int numberOfPoints = numberOfIntervals + 1; VVector pointVVector; bool[] structureMask = new bool[numberOfPoints]; double distance = VVector.Distance(start, end); for (int i = 0; i < numberOfPoints; i++) { pointVVector = start + ((i * res) / distance) * (end - start); structureMask[i] = structure.IsPointInsideSegment(pointVVector); } return(structureMask); }
private DoseProfile CalculateProfile( Double x_start, Double y_start, Double z_start, Double x_stop, Double y_stop, Double z_stop, Double step, Dose dose ) { // 始点と終点を定義 var start = new VVector(x_start, y_start, z_start); var stop = new VVector(x_stop, y_stop, z_stop); // 始点・終点間距離を定義 var distance = VVector.Distance(start, stop); if (distance == 0) { throw new ApplicationException("Distance between two points is 0."); } // 距離をステップサイズで割ってサンプリング点数を決定(切り捨て) Double point = Math.Floor(distance / step); if (point < 1.0) { throw new ApplicationException("Step size is largar than distance between two points."); } // 始点から終点へ向かう長さ1のベクトルを得る var dir = stop - start; dir.ScaleToUnitLength(); // stepサイズに合うように終点位置を調整 var stop_new = start + dir * point * step; // DoseProfile用の配列を準備 double[] profile = new double[(int)(point + 1)]; // DoseProfileを取得 var doseProfile = dose.GetDoseProfile(start, stop_new, profile); return(doseProfile); }
public string GenerateReport(ScriptContext context, MIRSRegistration mirsReg, IEnumerable <VVector> sourcePoints, IEnumerable <VVector> registeredPoints, IEnumerable <VVector> transformedPoints, RegStats stats) { // userid {0}, report time {1}, patient {2}, source dataset {3}, registered dataset {4}. string htmlStartFmt = @" <HTML> <BODY text=""black"" bgColor=""white""> <H2>Registration Report</H2> Created by {0} on {1} <br/><br/> <table> <tr><td>Patient:</td><td>{2}</td></tr> <tr><td>Source Dataset:</td><td>{3}</td></tr> <tr><td>Registered Dataset:</td><td>{4}</td></tr> </table>"; string htmlEnd = "</BODY></HTML>"; // reg id {0}, reg type {1}, min {2}, max{3}, mean {4} string htmlRegHeaderFmt = @" <H3>Registration: {0} (type: {1})</H3> <H4>Error Statistics:</H4> <table> <tr><td>Min:</td><td>{2:0.0}</td><td>mm</td></tr> <tr><td>Max:</td><td>{3:0.0}</td><td>mm</td></tr> <tr><td>Mean:</td><td>{4:0.0}</td><td>mm</td></tr> </table> <table border=""1""> <tr> <th>point #</th> <th>Source Match Point</th> <th>Registered Match Point</th> <th>Derived Match Point</th> <th>Distance (mm)</th> </tr>"; string htmlRegFooter = "</table>"; // point # {0}, source match point {1}, registered match point {2}, derived match point {3}, distance {4} string htmlPointRowFmt = @" <tr> <td>{0}</td> <td>{1:0.0}</td> <td>{2:0.0}</td> <td>{3:0.0}</td> <td>{4:0.0}</td> </tr>"; // point.x {0}, point.y {1}, point.z {2} string htmlFirstIndividualPointFmt = @" <table> <tr><td>x: {0:0.0}</td></tr> <tr><td>y: {1:0.0}</td></tr> <tr><td>z: {2:0.0}</td></tr> </table>"; string htmlIndividualPointFmt = @" <table> <tr><td>{0:0.0}</td></tr> <tr><td>{1:0.0}</td></tr> <tr><td>{2:0.0}</td></tr> </table>"; string temp = System.Environment.GetEnvironmentVariable("TEMP"); string htmlPath = string.Format("{0}\\reg_report.html", temp); // open file "%temp%\points.html" using (System.IO.TextWriter writer = new System.IO.StreamWriter(htmlPath)) { // build the HTML header and write that // userid {0}, report time {1}, patient {2}, source dataset {3}, registered dataset {4}. writer.Write(string.Format(htmlStartFmt, context.CurrentUser.Name, DateTime.Now, context.Patient.Id, mirsReg.SourceImage.Id, mirsReg.RegisteredImage.Id)); // write the reg header writer.Write(string.Format(htmlRegHeaderFmt, mirsReg.Id, mirsReg.ToString(), stats.min, stats.max, stats.mean)); // list points for the registration int numPoints = sourcePoints.Count(); for (int i = 0; i < numPoints; i++) { VVector source = sourcePoints.ElementAt(i); VVector registered = registeredPoints.ElementAt(i); VVector derived = transformedPoints.ElementAt(i); double distance = VVector.Distance(registered, derived); string oneRow = string.Format(htmlPointRowFmt, i + 1, string.Format(htmlFirstIndividualPointFmt, source.x, source.y, source.z), string.Format(htmlIndividualPointFmt, registered.x, registered.y, registered.z), string.Format(htmlIndividualPointFmt, derived.x, derived.y, derived.z), distance.ToString(".0")); writer.Write(oneRow); } // write the registration footer. writer.Write(htmlRegFooter); // end the HTML document writer.Write(htmlEnd); } return(htmlPath); }
/// <summary> /// checkFieldFunc /// </summary> /// <param name="plan"></param> /// <returns></returns> static string CheckFieldFunc(PlanSetup plan) { //Initializes the variables string oText = ""; string checkName = ""; //////////////////////////////////////////////////////////////////////////////// // Check Treatment Machine // checkName = "Check treatment machine"; bool machineChkFlag = true; //TreatmentMachineName (the 1st field) string machine = plan.Beams.ElementAt(0).TreatmentUnit.Id; foreach (var beam in plan.Beams) { if (!beam.IsSetupField) { if (machine != beam.TreatmentUnit.Id) { //If false //flag -> false machineChkFlag = false; //add the parameters to the string oText += MakeFormatText(false, checkName, beam.Id + ": " + beam.TreatmentUnit.Id + " -> " + machine); } } } // If true if (machineChkFlag == true) { //add OK to the string oText += MakeFormatText(true, checkName, ""); } //////////////////////////////////////////////////////////////////////////////// // Check isocenter // Plan isocenter must be single point checkName = "Check single isocenter"; bool isoChkFlg = true; // Get 1st field isocenter position VVector isoCNT = plan.Beams.ElementAt(0).IsocenterPosition; foreach (var beam in plan.Beams) { // Compare isocenter position of 1st field with others if (VVector.Distance(beam.IsocenterPosition, isoCNT) != 0) { isoChkFlg = false; } } if (isoChkFlg == true) { // If true, add text[O] to the string oText += MakeFormatText(true, checkName, ""); } else { //If false, add the parameters and text[X] to the string oText += MakeFormatText(false, checkName, "multiple isocenter"); } //////////////////////////////////////////////////////////////////////////////// // Check MU checkName = "Check MU"; double minMU = 5.0; bool validFlag = true; string invalidMU = ""; foreach (var beam in plan.Beams) { if (!beam.IsSetupField) { if (beam.Meterset.Value < minMU) { validFlag = false; invalidMU += "(" + beam.Id + ":" + string.Format("{0:f1}", beam.Meterset.Value) + ")"; } } } if (validFlag == true) { // If true, add text[O] to the string oText += MakeFormatText(true, checkName, ""); } else { //If false, add the parameters and text[X] to the string oText += MakeFormatText(false, checkName, invalidMU); } /////////////////////// //Check Dose Rate checkName = "Check Dose Rate"; int defDoserate = 600; bool DRvalidFlag = true; string invalidDR = ""; foreach (var beam in plan.Beams) { if (!beam.IsSetupField) { if (beam.DoseRate != defDoserate) { DRvalidFlag = false; invalidDR += "(" + beam.Id + ":" + string.Format("{0}", beam.DoseRate) + ")"; } } } if (DRvalidFlag == true) { // If true, add text[O] to the string oText += MakeFormatText(true, checkName, ""); } else { //If false, add the parameters and text[X] to the string oText += MakeFormatText(false, checkName, invalidDR); } //////////////////////////////////////////////////////////////////////////////// // Check Jaw/MLC Position checkName = "Check Jaw/MLC position"; var checkJawMLCText = "\n"; double distJawMLCX = 0.0; // Jawともっとも開いているMLCとの規定距離(X方向) foreach (var beam in plan.Beams) { bool checkJawMLC = true; // MLCあり、かつStaticの場合のみ評価 if (beam.MLC != null && beam.MLCPlanType == 0) { var jawPositions = beam.ControlPoints.ElementAt(0).JawPositions; var leafPositions = beam.ControlPoints.ElementAt(0).LeafPositions; var leafPairs = leafPositions.GetLength(1); // Leaf対の数 float minX = 200; float maxX = -200; for (int i = 0; i < leafPairs; i++) { if (leafPositions[0, i] != leafPositions[1, i]) { minX = (minX > leafPositions[0, i]) ? leafPositions[0, i] : minX; maxX = (maxX < leafPositions[1, i]) ? leafPositions[1, i] : maxX; } } // X Jawの規定位置 var jawIdealX1 = minX - distJawMLCX; var jawIdealX2 = maxX + distJawMLCX; // 規定位置と2㎜以上ずれている場合にエラーを出す if (Math.Abs(jawIdealX1 - jawPositions.X1) > 2.0) { checkJawMLCText += string.Format("{0} : X1 jaw should be {1:f1} cm", beam.Id, jawIdealX1 / 10); checkJawMLC = false; } if (Math.Abs(jawIdealX2 - jawPositions.X2) > 2.0) { checkJawMLCText += string.Format("{0} : X2 jaw should be {1:f1} cm", beam.Id, jawIdealX2 / 10); checkJawMLC = false; } } if (checkJawMLC) { oText += MakeFormatText(true, checkName + "(" + beam.Id + ")", ""); } else { oText += MakeFormatText(false, checkName, checkJawMLCText); } } return(oText); }
public void Execute(ScriptContext context /*, System.Windows.Window window*/) { PlanSetup plan = context.PlanSetup; StructureSet ss = context.StructureSet; if (plan == null) { MessageBox.Show("Load a plan!"); return; } if (plan.IsDoseValid == false) { MessageBox.Show("plan has no dose calculated!"); return; } //User places point called "TopLeft" in top left corner where scan will start before running script Structure refMarker = (from s in ss.Structures where s.Id == "TopLeft" select s).FirstOrDefault(); if (refMarker == null) { MessageBox.Show("No marker point called TopLeft found."); return; } VVector topLeft = refMarker.CenterPoint; //User places point called "BottomRight" in bottom right corner where scan will stop before running script Structure refMarker2 = (from s in ss.Structures where s.Id == "BottomRight" select s).FirstOrDefault(); if (refMarker == null) { MessageBox.Show("No marker point called BottomRight found."); return; } VVector bottomRight = refMarker2.CenterPoint; VVector topRight = new VVector(bottomRight.x, topLeft.y, topLeft.z); VVector bottomLeft = new VVector(topLeft.x, topLeft.y, bottomRight.z); int column = 255; // arbitrarily make 255x255 (since that's what Excel can handle) int row = 255; double[] buffer = new double[column]; double xDist = (VVector.Distance(topLeft, topRight)) / (double)column; string filename = string.Format(@"c:\temp\doseplane.csv"); using (System.IO.StreamWriter sw = new System.IO.StreamWriter(filename, false, Encoding.ASCII)) { sw.Write("Y[cm]...X[cm],"); for (int i = 0; i < column; i++) { sw.Write("{0},", MMtoCM(Math.Round(topLeft.x + (xDist * i), 3))); } sw.WriteLine(""); double zDist = (VVector.Distance(topLeft, bottomLeft)) / (double)row; for (int j = 0; j < row; j++) { // figure out new start and stop points for the row we are scanning, then get the dose profile (scan it) double newZ = topLeft.z - (zDist * j); VVector newRowStart = topLeft; VVector newRowEnd = topRight; newRowStart.z = newZ; newRowEnd.z = newZ; // scan the row DoseProfile dp = plan.Dose.GetDoseProfile(newRowStart, newRowEnd, buffer); sw.Write("{0},", MMtoCM(Math.Round(newZ, 3))); foreach (var profilePt in dp) { sw.Write("{0},", Math.Round(profilePt.Value, 6)); } sw.WriteLine(""); } sw.Flush(); sw.Close(); MessageBox.Show(string.Format(@"File written to '{0}'", filename), "Varian Developer"); } // 'Start' generated CSV file to launch Excel window System.Diagnostics.Process.Start(filename); // Sleep for a few seconds to let Excel to start System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2)); }
protected override void RunTest(PlanSetup plan) { DisplayName = "Isocenter"; ResultDetails = ""; TestExplanation = "Checks that only a single isocenter exists in the plan\nAlso suggests using G180E if the isocenter is shifted >2cm to patient's right"; int isocenters = 1; VVector firstIso = plan.Beams.FirstOrDefault().IsocenterPosition; foreach (Beam field in plan.Beams) { //check for multiple isocenters if (field != plan.Beams.First()) { //if there's no distance between the two, they are equal if (VVector.Distance(field.IsocenterPosition, firstIso) != 0) { isocenters++; } } //check if 180E might be necessary if (!field.IsSetupField && field.ControlPoints.First().GantryAngle == 180) { try { Structure body = (from s in plan.StructureSet.Structures where (s.DicomType == "BODY" || s.DicomType == "EXTERNAL") select s).First(); //Looks for the isocenter position to be 20 mm to the left when facing the linac. Positions are in mm if (field.IsocenterPosition.x - body.CenterPoint.x < -20 && (plan.TreatmentOrientation == PatientOrientation.HeadFirstSupine || plan.TreatmentOrientation == PatientOrientation.FeetFirstProne)) { Result = "Warning"; ResultDetails += $"Isocenter is shifted to patients right, do you want to use {field.GantryAngleToUser(180)}E?\n"; DisplayColor = ResultColorChoices.Warn; } if (field.IsocenterPosition.x - body.CenterPoint.x > 20 && (plan.TreatmentOrientation == PatientOrientation.HeadFirstProne || plan.TreatmentOrientation == PatientOrientation.FeetFirstSupine)) { Result = "Warning"; ResultDetails += $"Isocenter is shifted to patients left, do you want to use {field.GantryAngleToUser(180)}E?\n"; DisplayColor = ResultColorChoices.Warn; } } catch { TestCouldNotComplete("CheckIsocenterPosition - No structure with type \"BODY\" or \"EXTERNAL\" found"); } } } if (isocenters > 1) { Result = "Warning"; ResultDetails += $"{isocenters} isocenters detected, please check plan\n"; DisplayColor = ResultColorChoices.Warn; } ResultDetails = ResultDetails.TrimEnd('\n'); //no problems found if (ResultDetails == "") { Result = "Pass"; DisplayColor = ResultColorChoices.Pass; } }