1、使用其他方式读取DICOM RTSTRUCT文件的坐标信息。
2、按照坐标从小到大的顺序将每一个ROI在每一层上面的坐标生成一个vtkPolyData,然后将所有的vtkPolyData组成一个vtkAppendPolyData(如果不按照从小到大的顺序,会影响后面的三维重建)。
3、vtkVoxelContoursToSurfaceFilter对上面得到的vtkAppendPolyData进行三维重建。(设置vtkVoxelContoursToSurfaceFilter的坐标点必须是整数)。
4、vtkTransformPolyDataFilter和vtkTransform对三维模型缩放和平移。
5、将所有的ROI重建的三维模型通过着色,再添加到同一个vtkAppendPolyData中。
6、通过vtkPlaneCutter和vtkPlane切三维模型,就可以得到想要的平面轮廓曲线。
三维重建代码:
void GetRoiSequences(string path)
{
if (string.IsNullOrEmpty(path) || !File.Exists(path)) return;
DicomDataset dicomDataSet = DicomFile.Open(path).Dataset;
if (dicomDataSet == null || !dicomDataSet.Contains(DicomTag.StructureSetROISequence) || !dicomDataSet.Contains(DicomTag.ROIContourSequence)) return;
foreach (DicomDataset roiDataSet in dicomDataSet.GetSequence(DicomTag.StructureSetROISequence))
{
if (roiDataSet == null || !roiDataSet.Contains(DicomTag.ROINumber) || !roiDataSet.Contains(DicomTag.ROIName)) continue;
RoiSequence roiSequence = new RoiSequence();
roiSequence.RoiNumber = roiDataSet.GetValue<int>(DicomTag.ROINumber, 0);
roiSequence.RoiName = roiDataSet.GetString(DicomTag.ROIName);
roiSequences.Add(roiSequence);
}
foreach (DicomDataset roiContourDataSet in dicomDataSet.GetSequence(DicomTag.ROIContourSequence))
{
if (roiContourDataSet == null || !roiContourDataSet.Contains(DicomTag.ROIDisplayColor)
|| !roiContourDataSet.Contains(DicomTag.ReferencedROINumber) || !roiContourDataSet.Contains(DicomTag.ContourSequence))
continue;
RoiSequence? roiSequence = roiSequences.Find(p => p.RoiNumber == roiContourDataSet.GetValue<int>(DicomTag.ReferencedROINumber, 0));
if (roiSequence == null) continue;
roiSequence.RoiColor = roiContourDataSet.GetValues<int>(DicomTag.ROIDisplayColor);
roiSequence.roiContourDatas = new List<RoiContourData>();
foreach (DicomDataset contour in roiContourDataSet.GetSequence(DicomTag.ContourSequence))
{
if (contour == null || !contour.Contains(DicomTag.ContourGeometricType) || !contour.Contains(DicomTag.ContourImageSequence)
|| !contour.Contains(DicomTag.NumberOfContourPoints) || !contour.Contains(DicomTag.ContourData))
continue;
RoiContourData roiContourData = new RoiContourData();
roiContourData.ContourType = contour.GetString(DicomTag.ContourGeometricType);
roiContourData.ContourPoints = contour.GetValue<int>(DicomTag.NumberOfContourPoints, 0);
roiContourData.ContourData = contour.GetValues<double>(DicomTag.ContourData);
foreach (DicomDataset ContourImage in contour.GetSequence(DicomTag.ContourImageSequence))
{
if (ContourImage == null || !ContourImage.Contains(DicomTag.ReferencedSOPInstanceUID)) continue;
roiContourData.RefeRenceSopUId = ContourImage.GetString(DicomTag.ReferencedSOPInstanceUID);
}
roiSequence.roiContourDatas.Add(roiContourData);
}
}
double[] orderIndex = new double[2] { -(imagePositionPatient[2] + extent[5] * spacing[2]), -imagePositionPatient[2] };
foreach (RoiSequence roiSequence in roiSequences)
{
if (roiSequence == null || roiSequence.RoiColor == null || roiSequence.roiContourDatas == null) continue;
vtkAppendPolyData appendFilter = vtkAppendPolyData.New();
for (double i = orderIndex[0]; i <= orderIndex[1]; i += (float)spacing[2])
{
foreach (RoiContourData roiContour in roiSequence.roiContourDatas)
{
if (roiContour == null || roiContour.ContourData == null || roiContour.ContourData.Length == 0) continue;
if (-roiContour.ContourData[2] != i) continue;
vtkPolyData circle = vtkPolyData.New();
CreateCircle(roiContour, circle);
appendFilter.AddInputData(circle);
circle.Dispose();
}
}
if (appendFilter.GetNumberOfInputConnections(0) == 0)
{
return;
}
appendFilter.Update();
vtkPolyData contours = appendFilter.GetOutput();
roiSequence.roiBounds = contours.GetBounds();
roiSequence.roiOrigin = new double[3] { roiSequence.roiBounds[0], roiSequence.roiBounds[2], roiSequence.roiBounds[4] };
roiSequence.roiCenter = contours.GetCenter();
vtkPolyData poly = vtkPolyData.New();
vtkPoints points = vtkPoints.New();
long numPoints = contours.GetPoints().GetNumberOfPoints();
points.SetNumberOfPoints(numPoints);
for (int i = 0; i < numPoints; ++i)
{
double[] pt = new double[3];
pt = contours.GetPoints().GetPoint(i);
pt[0] = (int)((pt[0] - roiSequence.roiOrigin[0]) / spacing[0] + 0.5);
pt[1] = (int)((pt[1] - roiSequence.roiOrigin[1]) / spacing[1] + 0.5);
pt[2] = (int)((pt[2] - roiSequence.roiOrigin[2]) / spacing[2] + 0.5);
points.SetPoint(i, pt[0], pt[1], pt[2]);
}
poly.SetPolys(contours.GetPolys());
poly.SetPoints(points);
vtkVoxelContoursToSurfaceFilter contoursToSurface = vtkVoxelContoursToSurfaceFilter.New();
contoursToSurface.SetInputData(poly);
contoursToSurface.SetSpacing(spacing[0], spacing[1], spacing[2]);
contoursToSurface.Update();
roiSequence.scaleCenter = contoursToSurface.GetOutput().GetCenter();
roiSequence.scaleBounds = contoursToSurface.GetOutput().GetBounds();
vtkTransformPolyDataFilter transformFilter = vtkTransformPolyDataFilter.New();
transformFilter.SetInputData(contoursToSurface.GetOutput());
vtkTransform transform = vtkTransform.New();
transformFilter.SetTransform(transform);
transform.Translate(-roiSequence.scaleCenter[0], -roiSequence.scaleCenter[1], -roiSequence.scaleCenter[2]);
transform.Scale((roiSequence.roiBounds[1] - roiSequence.roiBounds[0]) / (roiSequence.scaleBounds[1] - roiSequence.scaleBounds[0]),
(roiSequence.roiBounds[3] - roiSequence.roiBounds[2]) / (roiSequence.scaleBounds[3] - roiSequence.scaleBounds[2]),
(roiSequence.roiBounds[5] - roiSequence.roiBounds[4]) / (roiSequence.scaleBounds[5] - roiSequence.scaleBounds[4]));
transform.Translate(roiSequence.roiCenter[0], roiSequence.roiCenter[1], roiSequence.roiCenter[2]);
transformFilter.Update();
roiSequence.roiPolyData = vtkPolyData.New();
roiSequence.roiPolyData.DeepCopy(transformFilter.GetOutput());
appendFilter.Dispose();
contours.Dispose();
poly.Dispose();
points.Dispose();
contoursToSurface.Dispose();
transformFilter.Dispose();
transform.Dispose();
}
foreach (RoiSequence roiSequence in roiSequences)
{
if (roiSequence == null || roiSequence.RoiColor == null || roiSequence.roiContourDatas == null || roiSequence.roiPolyData == null ||
roiSequence.roiBounds == null || roiSequence.roiOrigin == null || roiSequence.scaleCenter == null || roiSequence.scaleBounds == null ||
roiSequence.roiCenter == null) continue;
vtkUnsignedCharArray scalars = vtkUnsignedCharArray.New();
scalars.SetNumberOfComponents(3);
for (int i = 0; i < roiSequence.roiPolyData.GetNumberOfPoints(); i++)
{
scalars.InsertNextTuple3(roiSequence.RoiColor[0], roiSequence.RoiColor[1], roiSequence.RoiColor[2]); //set color index to 1
}
roiSequence.roiPolyData.GetPointData().SetScalars(scalars);
appendFilter.AddInputData(roiSequence.roiPolyData);
}
appendFilter.Update();
}
void CreateCircle(RoiContourData roiContour, vtkPolyData polyData)
{
if (roiContour == null || roiContour.ContourData == null || roiContour.ContourData.Length == 0) return;
vtkPoints points = vtkPoints.New();
vtkCellArray cells = vtkCellArray.New();
points.SetNumberOfPoints((long)roiContour.ContourData.Length / 3);
cells.Allocate(1, (long)roiContour.ContourData.Length / 3);
cells.InsertNextCell(roiContour.ContourData.Length / 3);
int index = 0;
for (int i = 0; i < roiContour.ContourData.Length; i += 3)
{
points.SetPoint(index, roiContour.ContourData[i], -roiContour.ContourData[i + 1], -roiContour.ContourData[i + 2]);
cells.InsertCellPoint(index);
index += 1;
}
polyData.Initialize();
polyData.SetPolys(cells);
polyData.SetPoints(points);
points.Dispose();
cells.Dispose();
}
切面代码:
dicomCenter = imageResliceAxial.GetResliceAxesOrigin();
roiImageActorAxial.Clear();
planeAxial.SetOrigin(dicomCenter[0], dicomCenter[1], dicomCenter[2]);
planeAxial.SetNormal(0, 0, 1);
cutterAxial.SetInputData(appendFilter.GetOutput());
cutterAxial.SetPlane(planeAxial);
cutterAxial.BuildTreeOff();
cutterAxial.Update();
vtkCompositeDataGeometryFilter geometryFilter = vtkCompositeDataGeometryFilter.New();
geometryFilter.SetInputData(cutterAxial.GetOutput());
vtkPolyDataMapper mapper = vtkPolyDataMapper.New();
mapper.SetInputConnection(geometryFilter.GetOutputPort());
vtkTransform transform = vtkTransform.New();
transform.Translate(-IsocenterPosition[0], -IsocenterPosition[1], 0);
// 创建一个vtkActor对象,用于表示vtkPolyDataMapper对象的输出
vtkActor actor = vtkActor.New();
actor.SetMapper(mapper);
actor.SetPosition(-dicomCenter[0], -dicomCenter[1], -dicomCenter[2]);
actor.GetProperty().LightingOff();