Mercurial > hg > orthanc-stone
annotate Framework/Loaders/DicomStructureSetLoader.cpp @ 914:4d1f57773b5b
Added image inversion support in GrayscaleStyleConfigurator + OpenGLFloatTextureProgram
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Fri, 19 Jul 2019 10:54:03 +0200 |
parents | 2b245953b44b |
children | 401808e7ff2e |
rev | line source |
---|---|
815 | 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 { | |
841
266e2b0b9abc
better error reporting in DicomStructureSetLoader + fixed POST request logic
Benjamin Golinvaux <bgo@osimis.io>
parents:
828
diff
changeset
|
95 std::stringstream msg; |
266e2b0b9abc
better error reporting in DicomStructureSetLoader + fixed POST request logic
Benjamin Golinvaux <bgo@osimis.io>
parents:
828
diff
changeset
|
96 msg << "Unknown resource! message.GetAnswer() = " << message.GetAnswer() << " message.GetAnswerHeaders() = "; |
842
2b245953b44b
removed some c++11 for older compilers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
841
diff
changeset
|
97 for (OrthancRestApiCommand::HttpHeaders::const_iterator it = message.GetAnswerHeaders().begin(); |
2b245953b44b
removed some c++11 for older compilers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
841
diff
changeset
|
98 it != message.GetAnswerHeaders().end(); ++it) |
841
266e2b0b9abc
better error reporting in DicomStructureSetLoader + fixed POST request logic
Benjamin Golinvaux <bgo@osimis.io>
parents:
828
diff
changeset
|
99 { |
842
2b245953b44b
removed some c++11 for older compilers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
841
diff
changeset
|
100 msg << "\nkey: \"" << it->first << "\" value: \"" << it->second << "\"\n"; |
841
266e2b0b9abc
better error reporting in DicomStructureSetLoader + fixed POST request logic
Benjamin Golinvaux <bgo@osimis.io>
parents:
828
diff
changeset
|
101 } |
842
2b245953b44b
removed some c++11 for older compilers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
841
diff
changeset
|
102 const std::string msgStr = msg.str(); |
841
266e2b0b9abc
better error reporting in DicomStructureSetLoader + fixed POST request logic
Benjamin Golinvaux <bgo@osimis.io>
parents:
828
diff
changeset
|
103 LOG(ERROR) << msgStr; |
815 | 104 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); |
105 } | |
106 | |
107 const std::string instanceId = lookup[0]["ID"].asString(); | |
108 | |
109 { | |
110 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | |
111 command->SetHttpHeader("Accept-Encoding", "gzip"); | |
112 command->SetUri("/instances/" + instanceId + "/tags"); | |
113 command->SetPayload(new AddReferencedInstance(loader, instanceId)); | |
114 Schedule(command.release()); | |
115 } | |
116 } | |
117 }; | |
118 | |
119 | |
120 class DicomStructureSetLoader::LoadStructure : public LoaderStateMachine::State | |
121 { | |
122 public: | |
123 LoadStructure(DicomStructureSetLoader& that) : | |
124 State(that) | |
125 { | |
126 } | |
127 | |
128 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) | |
129 { | |
130 DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); | |
131 | |
132 { | |
133 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer()); | |
134 loader.content_.reset(new DicomStructureSet(dicom)); | |
135 } | |
136 | |
137 std::set<std::string> instances; | |
138 loader.content_->GetReferencedInstances(instances); | |
139 | |
828 | 140 loader.countReferencedInstances_ = static_cast<unsigned int>(instances.size()); |
815 | 141 |
142 for (std::set<std::string>::const_iterator | |
143 it = instances.begin(); it != instances.end(); ++it) | |
144 { | |
145 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | |
146 command->SetUri("/tools/lookup"); | |
147 command->SetMethod(Orthanc::HttpMethod_Post); | |
148 command->SetBody(*it); | |
149 command->SetPayload(new LookupInstance(loader, *it)); | |
841
266e2b0b9abc
better error reporting in DicomStructureSetLoader + fixed POST request logic
Benjamin Golinvaux <bgo@osimis.io>
parents:
828
diff
changeset
|
150 //LOG(TRACE) << "About to schedule a /tools/lookup POST request. URI = " << command->GetUri() << " Body size = " << (*it).size() << " Body = " << (*it) << "\n"; |
815 | 151 Schedule(command.release()); |
152 } | |
153 } | |
154 }; | |
155 | |
156 | |
157 class DicomStructureSetLoader::Slice : public IExtractedSlice | |
158 { | |
159 private: | |
160 const DicomStructureSet& content_; | |
161 uint64_t revision_; | |
162 bool isValid_; | |
163 | |
164 public: | |
165 Slice(const DicomStructureSet& content, | |
166 uint64_t revision, | |
167 const CoordinateSystem3D& cuttingPlane) : | |
168 content_(content), | |
169 revision_(revision) | |
170 { | |
171 bool opposite; | |
172 | |
173 const Vector normal = content.GetNormal(); | |
174 isValid_ = ( | |
175 GeometryToolbox::IsParallelOrOpposite(opposite, normal, cuttingPlane.GetNormal()) || | |
176 GeometryToolbox::IsParallelOrOpposite(opposite, normal, cuttingPlane.GetAxisX()) || | |
177 GeometryToolbox::IsParallelOrOpposite(opposite, normal, cuttingPlane.GetAxisY())); | |
178 } | |
179 | |
180 virtual bool IsValid() | |
181 { | |
182 return isValid_; | |
183 } | |
184 | |
185 virtual uint64_t GetRevision() | |
186 { | |
187 return revision_; | |
188 } | |
189 | |
190 virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, | |
191 const CoordinateSystem3D& cuttingPlane) | |
192 { | |
193 assert(isValid_); | |
194 | |
195 std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); | |
196 layer->SetThickness(2); | |
197 | |
198 for (size_t i = 0; i < content_.GetStructuresCount(); i++) | |
199 { | |
200 const Color& color = content_.GetStructureColor(i); | |
201 | |
202 std::vector< std::vector<DicomStructureSet::PolygonPoint> > polygons; | |
203 | |
204 if (content_.ProjectStructure(polygons, i, cuttingPlane)) | |
205 { | |
206 for (size_t j = 0; j < polygons.size(); j++) | |
207 { | |
208 PolylineSceneLayer::Chain chain; | |
209 chain.resize(polygons[j].size()); | |
210 | |
211 for (size_t k = 0; k < polygons[j].size(); k++) | |
212 { | |
213 chain[k] = ScenePoint2D(polygons[j][k].first, polygons[j][k].second); | |
214 } | |
215 | |
216 layer->AddChain(chain, true /* closed */, color); | |
217 } | |
218 } | |
219 } | |
220 | |
221 return layer.release(); | |
222 } | |
223 }; | |
224 | |
225 | |
226 DicomStructureSetLoader::DicomStructureSetLoader(IOracle& oracle, | |
227 IObservable& oracleObservable) : | |
228 LoaderStateMachine(oracle, oracleObservable), | |
229 revision_(0), | |
230 countProcessedInstances_(0), | |
231 countReferencedInstances_(0) | |
232 { | |
233 } | |
234 | |
235 | |
236 void DicomStructureSetLoader::LoadInstance(const std::string& instanceId) | |
237 { | |
238 Start(); | |
239 | |
240 instanceId_ = instanceId; | |
241 | |
242 { | |
243 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | |
244 command->SetHttpHeader("Accept-Encoding", "gzip"); | |
245 command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3006-0050"); | |
246 command->SetPayload(new LoadStructure(*this)); | |
247 Schedule(command.release()); | |
248 } | |
249 } | |
250 | |
251 | |
252 IVolumeSlicer::IExtractedSlice* DicomStructureSetLoader::ExtractSlice(const CoordinateSystem3D& cuttingPlane) | |
253 { | |
254 if (content_.get() == NULL) | |
255 { | |
256 // Geometry is not available yet | |
257 return new IVolumeSlicer::InvalidSlice; | |
258 } | |
259 else | |
260 { | |
261 return new Slice(*content_, revision_, cuttingPlane); | |
262 } | |
263 } | |
264 } |