public static DVH CalculateSDF_DVH(StructureMeta sm, DoseMatrix dm) { var cropDM = dm.Clone(); cropDM.CropMatrixToStructure(sm, 2); // cropDM.ShowAllSlices(); var sdf = sm.CalculateSDFMatrix(cropDM); cropDM.Resample(cropDM.XRes / 3, cropDM.YRes / 3, cropDM.ZRes / 3); sdf.Resample(sdf.XRes / 3, sdf.YRes / 3, sdf.ZRes / 3); var maxDose = cropDM.MaxDose; var voxelCC = cropDM.XRes / 10 * cropDM.YRes / 10 * cropDM.ZRes / 10; var doseUnit = dm.DoseUnit == DoseUnit.ABSOLUTE ? "Gy" : "%"; var dvh = new DVH(maxDose, voxelCC, doseUnit); var vol = 0.0; for (int z = 0; z < cropDM.DimensionZ; z++) { var zPos = cropDM.ImageToPatientTx(new Core.Helpers.Vector3(0, 0, z)).Z; var contours = new SliceContourMeta[] { sdf.FindStructureContour(zPos) }; vol += contours.Sum(c => c.CalculateArea()) * cropDM.ZRes; CalculateContoursDVH(contours, cropDM, dvh); } dvh.Volume = vol; return(dvh); }
/// <summary> /// Recursive method that takes the largest contour and an array of smaller contours to /// place smaller contours which are inside the largest contour into the child list /// for area calculations /// </summary> /// <param name="largerContour">the large contour possibly containing smaller contours</param> /// <param name="smallerContours">the list of smaller contours</param> public static void OrganizeIntoChildren(SliceContourMeta largerContour, IEnumerable <SliceContourMeta> smallerContours) { if (!smallerContours.Any()) { return; } foreach (var smaller in smallerContours) { if (largerContour.CompletelyContains(smaller)) { OrganizeIntoChildren(smaller, smallerContours.Where(s => s != smaller)); largerContour.Children.Add(smaller); } } }
public static SliceContourMeta FindZeroLevelContours(this Mat mat, Func <Vector3, Vector3> imageToPatientFunc, int z) { var zPos = imageToPatientFunc(new Vector3(0, 0, z)).Z; var meta = new SliceContourMeta(); var child = new SliceContourMeta(); var level = new IsodoseLevel() { Value = 0, Color = new Scalar(255) }; Point[][] ctrs; HierarchyIndex[] hi; var thresh = mat.Threshold(level.Value, 255, ThresholdTypes.Binary); thresh = thresh.WindowAndLevel(128, 256); thresh.FindContours(out ctrs, out hi, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple); for (int i = 0; i < hi.Length; i++) { var contour = ctrs[i]; if (hi[i].Parent == -1) { contour.ToList().ForEach(c => { var pos = imageToPatientFunc(new Vector3(c.X, c.Y, 1)); pos.Z = zPos; meta.AddPoint(new Point3f((float)pos.X, (float)pos.Y, (float)pos.Z)); }); } if (hi[i].Parent != -1) { contour.ToList().ForEach(c => { var pos = imageToPatientFunc(new Vector3(c.X, c.Y, 1)); pos.Z = zPos; child.AddPoint(new Point3f((float)pos.X, (float)pos.Y, (float)pos.Z)); }); } } if (child.ContourPoints.Any()) { meta.Children.Add(child); } thresh.Dispose(); return(meta); }
/// <summary> /// Finds contours on StructureMeta model (1 inside, -1 outsize, 0 boundry) /// </summary> /// <param name="mat"></param> /// <returns></returns> public static SliceContourMeta FindStructureContour(this Matrix model, double zPos) { var contourSlice = model.GetZPlane(zPos); var meta = new SliceContourMeta(); var child = new SliceContourMeta(); Point[][] ctrs; HierarchyIndex[] hi; var thresh = contourSlice.Threshold(0, 255, ThresholdTypes.Binary).WindowAndLevel(128, 255); //Cv2.ImShow("Threshold", thresh); //Cv2.WaitKey(); thresh.FindContours(out ctrs, out hi, RetrievalModes.CComp, ContourApproximationModes.ApproxNone); for (int i = 0; i < hi.Length; i++) { var contour = ctrs[i]; if (hi[i].Parent == -1) { contour.ToList().ForEach(c => { var pos = model.ImageToPatientTx(new Vector3(c.X, c.Y, 1)); pos.Z = zPos; meta.AddPoint(new Point3f((float)pos.X, (float)pos.Y, (float)pos.Z)); }); } if (hi[i].Parent != -1) { contour.ToList().ForEach(c => { var pos = model.ImageToPatientTx(new Vector3(c.X, c.Y, 1)); pos.Z = zPos; child.AddPoint(new Point3f((float)pos.X, (float)pos.Y, (float)pos.Z)); }); } } if (child.ContourPoints.Any()) { meta.Children.Add(child); } contourSlice.Dispose(); thresh.Dispose(); return(meta); }
public static StructureMeta[] Find2DIsodoseLines(this Matrix matrix, params IsodoseLevel[] levels) { List <StructureMeta> metas = new List <StructureMeta>(); foreach (var level in levels) { var meta = new StructureMeta() { StructureId = level.Value.ToString(), StructureName = level.Value.ToString() }; meta.Color = level.Color; for (int z = 0; z < matrix.DimensionZ; z++) { using (var mat = matrix.GetZPlaneBySlice(z)) { var ctrs = mat.Find2DIsodoseLine(level); if (ctrs.Any()) { foreach (var contour in ctrs) { var allPoints = new Vec4f[contour.Length]; var points = contour.Select(c => new Vec4f(c.X, c.Y, z, 1)); using (var pointMat = new Mat <float>(new[] { contour.Length }, points.ToArray())) { var txPointMat = pointMat.Transform(matrix.PatientTransformMatrix); txPointMat.GetArray(out allPoints); } var sliceContour = new SliceContourMeta(); var points3D = allPoints.Select(a => new Point3f(a.Item0, a.Item1, a.Item2)).ToList(); points3D.ForEach(sliceContour.AddPoint); meta.SliceContours.Add(sliceContour); } } } } metas.Add(meta); } return(metas.ToArray()); }
public static StructureSetMeta ParseDICOM(string file) { var sm = new StructureSetMeta(); var dcm = DICOMObject.Read(file); var sel = dcm.GetSelector(); var metas = sel.StructureSetROISequence.Items.Select(i => { var meta = new StructureMeta(); meta.StructureId = i.GetSelector().ROIName?.Data; meta.ROINumber = i.GetSelector().ROINumber.Data; return(meta); }); foreach (var meta in metas) { try { var comatch = sel.ROIContourSequence.Items.FirstOrDefault(i => i.GetSelector().ReferencedROINumber.Data == meta.ROINumber); var romatch = sel.RTROIObservationsSequence.Items.FirstOrDefault(i => i.GetSelector().ReferencedROINumber.Data == meta.ROINumber); var colorValues = comatch.GetSelector().ROIDisplayColor.Data_; var color = new Vec3b((byte)colorValues[0], (byte)colorValues[1], (byte)colorValues[2]); var dicomType = romatch.GetSelector().RTROIInterpretedType.Data; var name = romatch.GetSelector().ROIObservationLabel.Data; meta.StructureName = name; meta.Color = new Scalar(colorValues[0], colorValues[1], colorValues[2]); var hasContours = comatch.GetSelector().ContourSequence != null; if (!hasContours) { continue; } //HAS CONTOURS - SET COLOR BYTES IN MATRIX foreach (var slice in comatch.GetSelector().ContourSequence.Items) { var contours = slice.GetSelector().ContourData.Data_; if (contours.Count % 3 != 0) { _logger.LogWarning($"Slice for structure {meta.StructureId} has {contours.Count} contour points. Not divisible by 3! Can't process."); continue; } try { var contour = new SliceContourMeta(); for (int i = 0; i < contours.Count; i += 3) { var contourPt = new OpenCvSharp.Point3f((float)contours[i + 0], (float)contours[i + 1], (float)contours[i + 2]); contour.AddPoint(contourPt); } meta.SliceContours.Add(contour); meta.DICOMType = dicomType; } catch (Exception e) { _logger.LogError(e.ToString()); } } //OrganizeContours - contours containing other contours (holes and fills) will be organized //into children. All other contours are outermost contours and not children of any other var slices = meta.SliceContours.GroupBy(s => s.Z).ToList(); foreach (var slice in slices) { var sliceContours = slice.OrderByDescending(s => s.CalculateArea()).ToList(); ContourHelper.OrganizeIntoChildren(sliceContours[0], sliceContours.Skip(1)); } sm.Structures.Add(meta.StructureId, meta); } catch (Exception e) { _logger.LogError(e, $"Could not add structure {meta.StructureId}"); } } return(sm); }