Mercurial > hg > orthanc-stone
comparison Framework/Loaders/DicomStructureSetLoader.cpp @ 815:df442f1ba0c6
reorganization
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 28 May 2019 21:59:20 +0200 |
parents | |
children | 28f99af358fa |
comparison
equal
deleted
inserted
replaced
814:aead999345e0 | 815:df442f1ba0c6 |
---|---|
1 /** | |
2 * Stone of Orthanc | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017-2019 Osimis S.A., Belgium | |
6 * | |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU Affero General Public License | |
9 * as published by the Free Software Foundation, either version 3 of | |
10 * the License, or (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Affero General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Affero General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 **/ | |
20 | |
21 | |
22 #include "DicomStructureSetLoader.h" | |
23 | |
24 #include "../Scene2D/PolylineSceneLayer.h" | |
25 #include "../Toolbox/GeometryToolbox.h" | |
26 | |
27 namespace OrthancStone | |
28 { | |
29 class DicomStructureSetLoader::AddReferencedInstance : public LoaderStateMachine::State | |
30 { | |
31 private: | |
32 std::string instanceId_; | |
33 | |
34 public: | |
35 AddReferencedInstance(DicomStructureSetLoader& that, | |
36 const std::string& instanceId) : | |
37 State(that), | |
38 instanceId_(instanceId) | |
39 { | |
40 } | |
41 | |
42 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) | |
43 { | |
44 Json::Value tags; | |
45 message.ParseJsonBody(tags); | |
46 | |
47 Orthanc::DicomMap dicom; | |
48 dicom.FromDicomAsJson(tags); | |
49 | |
50 DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); | |
51 loader.content_->AddReferencedSlice(dicom); | |
52 | |
53 loader.countProcessedInstances_ ++; | |
54 assert(loader.countProcessedInstances_ <= loader.countReferencedInstances_); | |
55 | |
56 if (loader.countProcessedInstances_ == loader.countReferencedInstances_) | |
57 { | |
58 // All the referenced instances have been loaded, finalize the RT-STRUCT | |
59 loader.content_->CheckReferencedSlices(); | |
60 loader.revision_++; | |
61 } | |
62 } | |
63 }; | |
64 | |
65 | |
66 // State that converts a "SOP Instance UID" to an Orthanc identifier | |
67 class DicomStructureSetLoader::LookupInstance : public LoaderStateMachine::State | |
68 { | |
69 private: | |
70 std::string sopInstanceUid_; | |
71 | |
72 public: | |
73 LookupInstance(DicomStructureSetLoader& that, | |
74 const std::string& sopInstanceUid) : | |
75 State(that), | |
76 sopInstanceUid_(sopInstanceUid) | |
77 { | |
78 } | |
79 | |
80 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) | |
81 { | |
82 DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); | |
83 | |
84 Json::Value lookup; | |
85 message.ParseJsonBody(lookup); | |
86 | |
87 if (lookup.type() != Json::arrayValue || | |
88 lookup.size() != 1 || | |
89 !lookup[0].isMember("Type") || | |
90 !lookup[0].isMember("Path") || | |
91 lookup[0]["Type"].type() != Json::stringValue || | |
92 lookup[0]["ID"].type() != Json::stringValue || | |
93 lookup[0]["Type"].asString() != "Instance") | |
94 { | |
95 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); | |
96 } | |
97 | |
98 const std::string instanceId = lookup[0]["ID"].asString(); | |
99 | |
100 { | |
101 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | |
102 command->SetHttpHeader("Accept-Encoding", "gzip"); | |
103 command->SetUri("/instances/" + instanceId + "/tags"); | |
104 command->SetPayload(new AddReferencedInstance(loader, instanceId)); | |
105 Schedule(command.release()); | |
106 } | |
107 } | |
108 }; | |
109 | |
110 | |
111 class DicomStructureSetLoader::LoadStructure : public LoaderStateMachine::State | |
112 { | |
113 public: | |
114 LoadStructure(DicomStructureSetLoader& that) : | |
115 State(that) | |
116 { | |
117 } | |
118 | |
119 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) | |
120 { | |
121 DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); | |
122 | |
123 { | |
124 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer()); | |
125 loader.content_.reset(new DicomStructureSet(dicom)); | |
126 } | |
127 | |
128 std::set<std::string> instances; | |
129 loader.content_->GetReferencedInstances(instances); | |
130 | |
131 loader.countReferencedInstances_ = instances.size(); | |
132 | |
133 for (std::set<std::string>::const_iterator | |
134 it = instances.begin(); it != instances.end(); ++it) | |
135 { | |
136 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | |
137 command->SetUri("/tools/lookup"); | |
138 command->SetMethod(Orthanc::HttpMethod_Post); | |
139 command->SetBody(*it); | |
140 command->SetPayload(new LookupInstance(loader, *it)); | |
141 Schedule(command.release()); | |
142 } | |
143 } | |
144 }; | |
145 | |
146 | |
147 class DicomStructureSetLoader::Slice : public IExtractedSlice | |
148 { | |
149 private: | |
150 const DicomStructureSet& content_; | |
151 uint64_t revision_; | |
152 bool isValid_; | |
153 | |
154 public: | |
155 Slice(const DicomStructureSet& content, | |
156 uint64_t revision, | |
157 const CoordinateSystem3D& cuttingPlane) : | |
158 content_(content), | |
159 revision_(revision) | |
160 { | |
161 bool opposite; | |
162 | |
163 const Vector normal = content.GetNormal(); | |
164 isValid_ = ( | |
165 GeometryToolbox::IsParallelOrOpposite(opposite, normal, cuttingPlane.GetNormal()) || | |
166 GeometryToolbox::IsParallelOrOpposite(opposite, normal, cuttingPlane.GetAxisX()) || | |
167 GeometryToolbox::IsParallelOrOpposite(opposite, normal, cuttingPlane.GetAxisY())); | |
168 } | |
169 | |
170 virtual bool IsValid() | |
171 { | |
172 return isValid_; | |
173 } | |
174 | |
175 virtual uint64_t GetRevision() | |
176 { | |
177 return revision_; | |
178 } | |
179 | |
180 virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, | |
181 const CoordinateSystem3D& cuttingPlane) | |
182 { | |
183 assert(isValid_); | |
184 | |
185 std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); | |
186 layer->SetThickness(2); | |
187 | |
188 for (size_t i = 0; i < content_.GetStructuresCount(); i++) | |
189 { | |
190 const Color& color = content_.GetStructureColor(i); | |
191 | |
192 std::vector< std::vector<DicomStructureSet::PolygonPoint> > polygons; | |
193 | |
194 if (content_.ProjectStructure(polygons, i, cuttingPlane)) | |
195 { | |
196 for (size_t j = 0; j < polygons.size(); j++) | |
197 { | |
198 PolylineSceneLayer::Chain chain; | |
199 chain.resize(polygons[j].size()); | |
200 | |
201 for (size_t k = 0; k < polygons[j].size(); k++) | |
202 { | |
203 chain[k] = ScenePoint2D(polygons[j][k].first, polygons[j][k].second); | |
204 } | |
205 | |
206 layer->AddChain(chain, true /* closed */, color); | |
207 } | |
208 } | |
209 } | |
210 | |
211 return layer.release(); | |
212 } | |
213 }; | |
214 | |
215 | |
216 DicomStructureSetLoader::DicomStructureSetLoader(IOracle& oracle, | |
217 IObservable& oracleObservable) : | |
218 LoaderStateMachine(oracle, oracleObservable), | |
219 revision_(0), | |
220 countProcessedInstances_(0), | |
221 countReferencedInstances_(0) | |
222 { | |
223 } | |
224 | |
225 | |
226 void DicomStructureSetLoader::LoadInstance(const std::string& instanceId) | |
227 { | |
228 Start(); | |
229 | |
230 instanceId_ = instanceId; | |
231 | |
232 { | |
233 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | |
234 command->SetHttpHeader("Accept-Encoding", "gzip"); | |
235 command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3006-0050"); | |
236 command->SetPayload(new LoadStructure(*this)); | |
237 Schedule(command.release()); | |
238 } | |
239 } | |
240 | |
241 | |
242 IVolumeSlicer::IExtractedSlice* DicomStructureSetLoader::ExtractSlice(const CoordinateSystem3D& cuttingPlane) | |
243 { | |
244 if (content_.get() == NULL) | |
245 { | |
246 // Geometry is not available yet | |
247 return new IVolumeSlicer::InvalidSlice; | |
248 } | |
249 else | |
250 { | |
251 return new Slice(*content_, revision_, cuttingPlane); | |
252 } | |
253 } | |
254 } |