35
|
1 /**
|
|
2 * SPDX-FileCopyrightText: 2023-2024 Sebastien Jodogne, UCLouvain, Belgium
|
|
3 * SPDX-License-Identifier: GPL-3.0-or-later
|
|
4 */
|
|
5
|
|
6 /**
|
|
7 * STL plugin for Orthanc
|
|
8 * Copyright (C) 2023-2024 Sebastien Jodogne, UCLouvain, Belgium
|
|
9 *
|
|
10 * This program is free software: you can redistribute it and/or
|
|
11 * modify it under the terms of the GNU General Public License as
|
|
12 * published by the Free Software Foundation, either version 3 of the
|
|
13 * License, or (at your option) any later version.
|
|
14 *
|
|
15 * This program is distributed in the hope that it will be useful, but
|
|
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
18 * General Public License for more details.
|
|
19 *
|
|
20 * You should have received a copy of the GNU General Public License
|
|
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
22 **/
|
|
23
|
|
24
|
|
25 #include "StructureSet.h"
|
|
26
|
|
27 #include "STLToolbox.h"
|
|
28
|
|
29 #include <OrthancException.h>
|
|
30
|
|
31 #include <dcmtk/dcmdata/dcdeftag.h>
|
|
32 #include <dcmtk/dcmdata/dcfilefo.h>
|
|
33
|
|
34
|
|
35 StructureSet::StructureSet(Orthanc::ParsedDicomFile& dicom) :
|
|
36 hasFrameOfReferenceUid_(false)
|
|
37 {
|
|
38 DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset();
|
|
39 patientId_ = STLToolbox::GetStringValue(dataset, DCM_PatientID);
|
|
40 studyInstanceUid_ = STLToolbox::GetStringValue(dataset, DCM_StudyInstanceUID);
|
|
41 seriesInstanceUid_ = STLToolbox::GetStringValue(dataset, DCM_SeriesInstanceUID);
|
|
42 sopInstanceUid_ = STLToolbox::GetStringValue(dataset, DCM_SOPInstanceUID);
|
|
43
|
|
44 DcmSequenceOfItems* frame = NULL;
|
|
45 if (!dataset.findAndGetSequence(DCM_ReferencedFrameOfReferenceSequence, frame).good() ||
|
|
46 frame == NULL)
|
|
47 {
|
|
48 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
|
|
49 }
|
|
50
|
|
51 if (frame->card() == 1)
|
|
52 {
|
|
53 const char* v = NULL;
|
|
54 if (frame->getItem(0)->findAndGetString(DCM_FrameOfReferenceUID, v).good() &&
|
|
55 v != NULL)
|
|
56 {
|
|
57 hasFrameOfReferenceUid_ = true;
|
|
58 frameOfReferenceUid_.assign(v);
|
|
59 }
|
|
60 }
|
|
61
|
|
62 DcmSequenceOfItems* rois = NULL;
|
|
63 if (!dataset.findAndGetSequence(DCM_ROIContourSequence, rois).good() ||
|
|
64 rois == NULL)
|
|
65 {
|
|
66 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
|
|
67 }
|
|
68
|
|
69 std::vector<DcmSequenceOfItems*> contours(rois->card());
|
|
70 size_t countPolygons = 0;
|
|
71
|
|
72 for (unsigned long i = 0; i < rois->card(); i++)
|
|
73 {
|
|
74 DcmSequenceOfItems* contour = NULL;
|
|
75 if (!rois->getItem(i)->findAndGetSequence(DCM_ContourSequence, contour).good() ||
|
|
76 contour == NULL)
|
|
77 {
|
|
78 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
|
|
79 }
|
|
80 else
|
|
81 {
|
|
82 contours[i] = contour;
|
|
83 countPolygons += contour->card();
|
|
84 }
|
|
85 }
|
|
86
|
|
87 polygons_.resize(countPolygons);
|
|
88
|
|
89 size_t pos = 0;
|
|
90 for (unsigned long i = 0; i < contours.size(); i++)
|
|
91 {
|
|
92 for (unsigned long j = 0; j < contours[i]->card(); j++, pos++)
|
|
93 {
|
|
94 polygons_[pos] = new StructurePolygon(dicom, i, j);
|
|
95 }
|
|
96 }
|
|
97
|
|
98 assert(pos == countPolygons);
|
|
99 }
|
|
100
|
|
101
|
|
102 StructureSet::~StructureSet()
|
|
103 {
|
|
104 for (size_t i = 0; i < polygons_.size(); i++)
|
|
105 {
|
|
106 assert(polygons_[i] != NULL);
|
|
107 delete polygons_[i];
|
|
108 }
|
|
109 }
|
|
110
|
|
111
|
|
112 std::string StructureSet::HashStudy() const
|
|
113 {
|
|
114 Orthanc::DicomInstanceHasher hasher(patientId_, studyInstanceUid_, seriesInstanceUid_, sopInstanceUid_);
|
|
115 return hasher.HashStudy();
|
|
116 }
|
|
117
|
|
118
|
|
119 const StructurePolygon& StructureSet::GetPolygon(size_t i) const
|
|
120 {
|
|
121 if (i >= polygons_.size())
|
|
122 {
|
|
123 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
124 }
|
|
125 else
|
|
126 {
|
|
127 assert(polygons_[i] != NULL);
|
|
128 return *polygons_[i];
|
|
129 }
|
|
130 }
|
|
131
|
|
132
|
|
133 const std::string& StructureSet::GetFrameOfReferenceUid() const
|
|
134 {
|
|
135 if (hasFrameOfReferenceUid_)
|
|
136 {
|
|
137 return frameOfReferenceUid_;
|
|
138 }
|
|
139 else
|
|
140 {
|
|
141 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
|
|
142 }
|
|
143 }
|
|
144
|
|
145
|
|
146 void StructureSet::ListStructuresNames(std::set<std::string>& target,
|
|
147 Orthanc::ParsedDicomFile& source)
|
|
148 {
|
|
149 target.clear();
|
|
150
|
|
151 DcmSequenceOfItems* sequence = NULL;
|
|
152 if (!source.GetDcmtkObject().getDataset()->findAndGetSequence(DCM_StructureSetROISequence, sequence).good() ||
|
|
153 sequence == NULL)
|
|
154 {
|
|
155 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
|
|
156 }
|
|
157
|
|
158 for (unsigned long i = 0; i < sequence->card(); i++)
|
|
159 {
|
|
160 DcmItem* item = sequence->getItem(i);
|
|
161 if (item == NULL)
|
|
162 {
|
|
163 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
|
|
164 }
|
|
165 else
|
|
166 {
|
|
167 target.insert(STLToolbox::GetStringValue(*item, DCM_ROIName));
|
|
168 }
|
|
169 }
|
|
170 }
|