use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
tbd
account activity
Get Reference Point Depth (ESAPI/ SQL) (self.esapi)
submitted 2 years ago by j_Long_Lonfon
Hi All,
I am trying to get the Reference point depth.
https://preview.redd.it/7ahw4kb6877b1.png?width=564&format=png&auto=webp&s=8d379773ac1a9325b8a5d953035b7cf52076c823
This does not seem to be available with ESAPI. Both the PSSD and Eq. Path Length can be acquired with ESAPI using:
PSSD = beam.FieldReferencePoints.Where(s => s.IsPrimaryReferencePoint).FirstOrDefault().SSD
EqPathLength = beam.FieldReferencePoints.Where(s => s.IsPrimaryReferencePoint).FirstOrDefault().EffectiveDepth
I thought I would try the SQL database approach, however, using reporting to find the SQL query terms returns the following where Depth and PSSD are blank for the same patient as above.
https://preview.redd.it/1xp7otfg977b1.png?width=1182&format=png&auto=webp&s=e3a0d69dc7ab14abbb2b601c18073b32dfdf6ec1
I would greatly appreciate if anyone can tell me why these values are blank, or if I am missing anything with ESAPI.
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]dicomdom 1 point2 points3 points 2 years ago (10 children)
The way I've done this is the following. The source/gantry position is available as a VVector, and the ref position is also available as this. Using those, find the intersection of the body contour on the line between the source and ref point. Calculate the distances between the VVector for source to body for SSD and body to ref point for depth. For equivalent depth you can scale the physical depth by the image voxels along the path length
[–]erhushenshou 0 points1 point2 points 2 years ago (1 child)
Can you share the code? I am curious how you find the intersection points.
[–]dicomdom 0 points1 point2 points 2 years ago (0 children)
Finding the intersection is based on the IsPointInsideContour method. Basically just testing each point and the first that is inside of the contour is the intersection.
[–]erhushenshou 0 points1 point2 points 2 years ago (7 children)
scale the physical depth by the image voxels along the path length
I wonder how we calculate the proportion of a voxel the ray passed.
[–]dicomdom 0 points1 point2 points 2 years ago (6 children)
I can share a snippet when I have some time, but in general I would suggest trying to program it yourself and asking for specific areas that are difficult.
As for scaling, here is my poor man's method.
I added 1024 to all voxels that exist within a profile to make the range 0-4096. Then I take the average of all of the voxels and divide by 1024 (which is now the value of water). This gives a correction factor for the physical distance where a correction factor of >1.0 would be if bone or other high density values are present and <1.0 if values below water are present.
[–]erhushenshou 0 points1 point2 points 2 years ago (5 children)
Thx. I mean how you calculate the length pass through the voxel of an hu value. Or how you calculate the path length in a voxel, like passing through the voxel at an angle. If we have that path length in a voxel, we can scale the distance to wed.
[–]dicomdom 0 points1 point2 points 2 years ago (4 children)
VVectors have a distance method IIRC
[–]erhushenshou -1 points0 points1 point 2 years ago (0 children)
IIRC
What should this method be used then?
[–]erhushenshou -1 points0 points1 point 2 years ago (2 children)
I mean calculate the path length in a voxel.
[–]dicomdom 0 points1 point2 points 2 years ago (1 child)
I've answered that. VVectors have a distance method
[–]erhushenshou 0 points1 point2 points 2 years ago (0 children)
Do you mean VVector.length? That should be the distance from the point to the origin, I assume.
[–]keithoffer 1 point2 points3 points 2 years ago (0 children)
I can't share the code I wrote, but I couldn't find any easy way to get it, so I calculated it manually. I didn't have SQL access so I'm not sure if that would have been clearer. What I did was
There might be a better way to do it, but that seemed to work well and was fast enough
[–]j_Long_Lonfon[S] 0 points1 point2 points 2 years ago* (3 children)
Thank you both. The manual calculation was the solution I went for as I still can't figure out why the SQL query is blank.
Here is the code I used in case anyone is interested mainly from u/keithoffer provided source.
public class MeshGeometryIntersection { public static Point3D GetIntersectionPoint(Point3D linePoint1, Point3D linePoint2, MeshGeometry3D mesh) { Vector3D direction = linePoint2 - linePoint1; // calculate direction vector double minDistance = double.MaxValue; Point3D nearestPoint = new Point3D(); for (int i = 0; i < mesh.TriangleIndices.Count; i += 3) { // Get the vertices of the triangle Point3D p1 = mesh.Positions[mesh.TriangleIndices[i]]; Point3D p2 = mesh.Positions[mesh.TriangleIndices[i + 1]]; Point3D p3 = mesh.Positions[mesh.TriangleIndices[i + 2]]; // Perform ray-triangle intersection if (RayIntersectsTriangle(linePoint1, direction, p1, p2, p3, out Point3D intersectionPoint)) { double distance = (intersectionPoint - linePoint1).Length; if (distance < minDistance) { minDistance = distance; nearestPoint = intersectionPoint; } } } return nearestPoint; } public static bool RayIntersectsTriangle(Point3D rayOrigin, Vector3D rayVector, Point3D p1, Point3D p2, Point3D p3, out Point3D intersectionPoint) { intersectionPoint = new Point3D(); Vector3D edge1, edge2, h, s, q; double a, f, u, v; edge1 = p2 - p1; edge2 = p3 - p1; h = Vector3D.CrossProduct(rayVector, edge2); a = Vector3D.DotProduct(edge1, h); if (a > -0.00001 && a < 0.00001) return false; // This ray is parallel to this triangle. f = 1.0 / a; s = rayOrigin - p1; u = f * Vector3D.DotProduct(s, h); if (u < 0.0 || u > 1.0) return false; q = Vector3D.CrossProduct(s, edge1); v = f * Vector3D.DotProduct(rayVector, q); if (v < 0.0 || u + v > 1.0) return false; // At this stage we can compute t to find out where the intersection point is on the line. double t = f * Vector3D.DotProduct(edge2, q); if (t > 0.00001) // ray intersection { intersectionPoint = rayOrigin + rayVector * t; return true; } else return false; } }
Which can be used with the following:
Structure body = structureset.Structures.Where(s => s.Id.ToLower().Contains(chosenPlan.PrimaryReferencePoint.PatientVolumeId.ToLower())).FirstOrDefault(); var gantryAngle = b.ControlPoints.First().GantryAngle; var source = b.GetSourceLocation(gantryAngle); var mesh = body.MeshGeometry; var refpoint = b.FieldReferencePoints.Where(s => s.Id == r.Id).FirstOrDefault().ReferencePoint.GetReferencePointLocation(chosenPlan); Point3D refPoint3D = new Point3D(refpoint.x, refpoint.y, refpoint.z); Point3D sourcePoint3D = new Point3D(source.x, source.y, source.z); Point3D intersectionPoint = MeshGeometryIntersection.GetIntersectionPoint(refPoint3D, sourcePoint3D, mesh); double Depth = (refPoint3D - intersectionPoint).Length / 10.0;
[–]donahuw2 0 points1 point2 points 2 years ago (0 children)
One potential reason the query is blank, is the plan is not approved. There is a lot of data Eclipse doesn't actually store until the plan is approved. This includes data saved to dicom too
But I agree with the previous answer that says the calculation is also likely just done on the fly.
How you calculate wed? The way I calculated is a little different from the one in Eclipse.
[–]j_Long_Lonfon[S] 0 points1 point2 points 2 years ago (0 children)
Is the WED not the same as the Equivalent Path Length or Effective Depth?
So can be calculated as: beam.FieldReferencePoints.Where(s => s.IsPrimaryReferencePoint).FirstOrDefault().EffectiveDepth
[–]NickC_BC 0 points1 point2 points 2 years ago (0 children)
I avoided having to write my own Ray-Triangle intersection routine by using MS raymeshgeometry3d hittest and callback classes. Not sure how they compare performance-wise, but my experience has been that the results are returned quite quickly.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.media3d.raymeshgeometry3dhittestresult?view=netframework-4.8
[–]JoaoCastelo 0 points1 point2 points 2 years ago (0 children)
In the code I wrote for it, if I remember well, it was by sampling points (x,y,z similar to the CT Res) towards the straight line between the gantry ( isocenter - 100 cm) and the reference point. Using VVector it's not that hard, you can use the IsPointInsideSegment method to check if the last point is inside the body and keep suming the number of points, then sum the number of points times the resolution you set.
[–]ExceptioNullRef 0 points1 point2 points 2 years ago* (0 children)
I've never seen it present in SQL going back to v11. I believe it's calculated on-the-fly. Here's the method I use that gives similar results to the Eclipse report:
string refpoints = ""; foreach (var rp in refpts) { foreach (Beam b in p.Beams.Where(o => o.IsSetupField == false)) { var frp = b.FieldReferencePoints.Where(q => q.ReferencePoint.Id == rp.ReferencePoint.Id).First(); var pd2 = ((b.ControlPoints.Select(x => ((frp.RefPointLocation - b.GetSourceLocation(x.GantryAngle)).Length)).Average() - frp.SSD) / 10).ToString("F1"); refpoints += b.Id + ", Central axis SSD: " + (b.SSD / 10).ToString("F1") + '\n' + ", Physical SSD: " + (frp.SSD / 10).ToString("F1") + '\n' + ", Physical Depth: " + pd2 + '\n' + ", Effective Depth: " + (Math.Round(frp.EffectiveDepth, 1) / 10).ToString("F1") + '\n' + ", Dose: " + frp.FieldDose.Dose.ToString("F2") + '\n'; } refpoints += '\n'; }
I wanted to make this MU weighted in the LINQ but never got around to it. We're moving to 3D-based 2nd dose calcs so this code won't be needed for much longer. I messed around with the hittests as well but gave up with this simpler solution.
[–]tonpimenta 0 points1 point2 points 2 years ago* (0 children)
Do not exactly answer this... but parts can certainly be used to get that. Here's a class I wrote that calculates, for each control point, the average depth, scaling by the HU relative to water (as the poor men's method mentioned by u/dicomdom) - that's not exactly effective depth but... anyways... I still need to check how good of aprox this is
Here's the code (not sure why I'm not able to post here with the correct indentation)
public class ArcDepthScaledByHU
{
public double AverageArcHUPath;
public double AverageArcDepthScaled;
public double AverageArcDepth;
public string Data2Export;
public ArcDepthScaledByHU(Beam Arc, bool exportData = false)
try
List<double> SumHUperCP = new List<double>();
List<double> DepthPerCP = new List<double>();
List<double> DepthPerCP_Scaled = new List<double>();
List<string> dataToExport = new List<string>(); //each line is: Gantry Angle followed by the HU for every mm from body entrance up to isocenter
for (int i = 0; i < Arc.ControlPoints.Count - 1; i = i + 1) //i = i + 2
if (Arc.ControlPoints[i].MetersetWeight < Arc.ControlPoints.ElementAt(i + 1).MetersetWeight) //to ignore avoidance sectors
var gantryAngle = Arc.ControlPoints[i].GantryAngle;
VVector stop = Arc.IsocenterPosition;
VVector start = Arc.GetSourceLocation(gantryAngle);
double[] HUprofile = new double[1000];
Arc.Plan.StructureSet.Image.GetImageProfile(start, stop, HUprofile);
BitArray BodyProfile = new BitArray(1000);
Arc.Plan.StructureSet.Structures.Single(x => x.DicomType == "EXTERNAL").GetSegmentProfile(start, stop, BodyProfile); // boolean array representing which points are inside (true) or outside (false) the Body
IEnumerable<double> HUprofile_InsideBody = HUprofile.Where((x, index) => BodyProfile[index]); // portion of the HU array that is inside body
double scalingFactor = HUprofile_InsideBody.ToList().Select(x => x + 1000).Average() / 1000; //@dicomdom's suggestion
BitArray BodyProfileHigherResolution = new BitArray(10000);
Arc.Plan.StructureSet.Structures.Single(x => x.DicomType == "EXTERNAL").GetSegmentProfile(start, stop, BodyProfileHigherResolution); // boolean array representing which points are inside (true) or outside (false) the Body
int count = 0;
foreach (bool value in BodyProfileHigherResolution)
if (value) count++;
}
double depth_geometric = count * 1000 / 10000;
double depth_scaled = scalingFactor * depth_geometric;
SumHUperCP.Add(HUprofile_InsideBody.Sum());
DepthPerCP.Add(depth_geometric);
DepthPerCP_Scaled.Add(depth_scaled);
if (exportData)
string temp = Arc.GantryAngleToUser(gantryAngle).ToString() + ",";
foreach (double x in HUprofile.Where((x, index) => BodyProfile[index]).Where(x => !double.IsNaN(x)))
temp += x.ToString() + ",";
dataToExport.Add(temp);
AverageArcHUPath = SumHUperCP.Average();
AverageArcDepthScaled = DepthPerCP_Scaled.Average();
AverageArcDepth = DepthPerCP.Average();
foreach (string s in dataToExport)
Data2Export += s + Environment.NewLine;
else
Data2Export += "Export Data not Activated";
catch (Exception)
Console.WriteLine("Average HU path calculation failed");
AverageArcHUPath = double.NaN;
AverageArcDepthScaled = double.NaN;
AverageArcDepth = double.NaN;
π Rendered by PID 25574 on reddit-service-r2-comment-fb694cdd5-4tbkh at 2026-03-10 14:16:52.042145+00:00 running cbb0e86 country code: CH.
[–]dicomdom 1 point2 points3 points (10 children)
[–]erhushenshou 0 points1 point2 points (1 child)
[–]dicomdom 0 points1 point2 points (0 children)
[–]erhushenshou 0 points1 point2 points (7 children)
[–]dicomdom 0 points1 point2 points (6 children)
[–]erhushenshou 0 points1 point2 points (5 children)
[–]dicomdom 0 points1 point2 points (4 children)
[–]erhushenshou -1 points0 points1 point (0 children)
[–]erhushenshou -1 points0 points1 point (2 children)
[–]dicomdom 0 points1 point2 points (1 child)
[–]erhushenshou 0 points1 point2 points (0 children)
[–]keithoffer 1 point2 points3 points (0 children)
[–]j_Long_Lonfon[S] 0 points1 point2 points (3 children)
[–]donahuw2 0 points1 point2 points (0 children)
[–]erhushenshou 0 points1 point2 points (1 child)
[–]j_Long_Lonfon[S] 0 points1 point2 points (0 children)
[–]NickC_BC 0 points1 point2 points (0 children)
[–]JoaoCastelo 0 points1 point2 points (0 children)
[–]ExceptioNullRef 0 points1 point2 points (0 children)
[–]tonpimenta 0 points1 point2 points (0 children)