Mercurial > hg > orthanc
comparison OrthancServer/OrthancFindRequestHandler.cpp @ 1898:e018037d4d0e
Support of optional tags for counting resources in C-Find
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 21 Dec 2015 19:26:38 +0100 |
parents | d7f63122c7f3 |
children | b1291df2f780 |
comparison
equal
deleted
inserted
replaced
1897:7110e2881dc0 | 1898:e018037d4d0e |
---|---|
44 #include <boost/regex.hpp> | 44 #include <boost/regex.hpp> |
45 | 45 |
46 | 46 |
47 namespace Orthanc | 47 namespace Orthanc |
48 { | 48 { |
49 static void GetChildren(std::list<std::string>& target, | |
50 ServerIndex& index, | |
51 const std::list<std::string>& source) | |
52 { | |
53 target.clear(); | |
54 | |
55 for (std::list<std::string>::const_iterator | |
56 it = source.begin(); it != source.end(); ++it) | |
57 { | |
58 std::list<std::string> tmp; | |
59 index.GetChildren(tmp, *it); | |
60 target.splice(target.end(), tmp); | |
61 } | |
62 } | |
63 | |
64 | |
65 static void StoreSetOfStrings(DicomMap& result, | |
66 const DicomTag& tag, | |
67 const std::set<std::string>& values) | |
68 { | |
69 bool isFirst = true; | |
70 | |
71 std::string s; | |
72 for (std::set<std::string>::const_iterator | |
73 it = values.begin(); it != values.end(); ++it) | |
74 { | |
75 if (isFirst) | |
76 { | |
77 isFirst = false; | |
78 } | |
79 else | |
80 { | |
81 s += "\\"; | |
82 } | |
83 | |
84 s += *it; | |
85 } | |
86 | |
87 result.SetValue(tag, s); | |
88 } | |
89 | |
90 | |
91 static void ExtractTagFromMainDicomTags(std::set<std::string>& target, | |
92 ServerIndex& index, | |
93 const DicomTag& tag, | |
94 const std::list<std::string>& resources, | |
95 ResourceType level) | |
96 { | |
97 for (std::list<std::string>::const_iterator | |
98 it = resources.begin(); it != resources.end(); ++it) | |
99 { | |
100 DicomMap tags; | |
101 if (index.GetMainDicomTags(tags, *it, level, level) && | |
102 tags.HasTag(tag)) | |
103 { | |
104 target.insert(tags.GetValue(tag).GetContent()); | |
105 } | |
106 } | |
107 } | |
108 | |
109 | |
110 static void ExtractTagFromInstances(std::set<std::string>& target, | |
111 ServerContext& context, | |
112 const DicomTag& tag, | |
113 const std::list<std::string>& instances) | |
114 { | |
115 std::string formatted = tag.Format(); | |
116 | |
117 for (std::list<std::string>::const_iterator | |
118 it = instances.begin(); it != instances.end(); ++it) | |
119 { | |
120 Json::Value dicom; | |
121 context.ReadJson(dicom, *it); | |
122 | |
123 if (dicom.isMember(formatted)) | |
124 { | |
125 const Json::Value& source = dicom[formatted]; | |
126 | |
127 if (source.type() == Json::objectValue && | |
128 source.isMember("Type") && | |
129 source.isMember("Value") && | |
130 source["Type"].asString() == "String" && | |
131 source["Value"].type() == Json::stringValue) | |
132 { | |
133 target.insert(source["Value"].asString()); | |
134 } | |
135 } | |
136 } | |
137 } | |
138 | |
139 | |
140 static void ComputePatientCounters(DicomMap& result, | |
141 ServerIndex& index, | |
142 const std::string& patient, | |
143 const DicomMap& query) | |
144 { | |
145 std::list<std::string> studies; | |
146 index.GetChildren(studies, patient); | |
147 | |
148 if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES)) | |
149 { | |
150 result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES, | |
151 boost::lexical_cast<std::string>(studies.size())); | |
152 } | |
153 | |
154 if (!query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES) && | |
155 !query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES)) | |
156 { | |
157 return; | |
158 } | |
159 | |
160 std::list<std::string> series; | |
161 GetChildren(series, index, studies); | |
162 studies.clear(); // This information is useless below | |
163 | |
164 if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES)) | |
165 { | |
166 result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES, | |
167 boost::lexical_cast<std::string>(series.size())); | |
168 } | |
169 | |
170 if (!query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES)) | |
171 { | |
172 return; | |
173 } | |
174 | |
175 std::list<std::string> instances; | |
176 GetChildren(instances, index, series); | |
177 | |
178 if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES)) | |
179 { | |
180 result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES, | |
181 boost::lexical_cast<std::string>(instances.size())); | |
182 } | |
183 } | |
184 | |
185 | |
186 static void ComputeStudyCounters(DicomMap& result, | |
187 ServerContext& context, | |
188 const std::string& study, | |
189 const DicomMap& query) | |
190 { | |
191 ServerIndex& index = context.GetIndex(); | |
192 | |
193 std::list<std::string> series; | |
194 index.GetChildren(series, study); | |
195 | |
196 if (query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES)) | |
197 { | |
198 result.SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES, | |
199 boost::lexical_cast<std::string>(series.size())); | |
200 } | |
201 | |
202 if (query.HasTag(DICOM_TAG_MODALITIES_IN_STUDY)) | |
203 { | |
204 std::set<std::string> values; | |
205 ExtractTagFromMainDicomTags(values, index, DICOM_TAG_MODALITY, series, ResourceType_Series); | |
206 StoreSetOfStrings(result, DICOM_TAG_MODALITIES_IN_STUDY, values); | |
207 } | |
208 | |
209 if (!query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES) && | |
210 !query.HasTag(DICOM_TAG_SOP_CLASSES_IN_STUDY)) | |
211 { | |
212 return; | |
213 } | |
214 | |
215 std::list<std::string> instances; | |
216 GetChildren(instances, index, series); | |
217 | |
218 if (query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES)) | |
219 { | |
220 result.SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES, | |
221 boost::lexical_cast<std::string>(instances.size())); | |
222 } | |
223 | |
224 if (query.HasTag(DICOM_TAG_SOP_CLASSES_IN_STUDY)) | |
225 { | |
226 std::set<std::string> values; | |
227 ExtractTagFromInstances(values, context, DICOM_TAG_SOP_CLASS_UID, instances); | |
228 StoreSetOfStrings(result, DICOM_TAG_SOP_CLASSES_IN_STUDY, values); | |
229 } | |
230 } | |
231 | |
232 | |
233 static void ComputeSeriesCounters(DicomMap& result, | |
234 ServerIndex& index, | |
235 const std::string& series, | |
236 const DicomMap& query) | |
237 { | |
238 std::list<std::string> instances; | |
239 index.GetChildren(instances, series); | |
240 | |
241 if (query.HasTag(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES)) | |
242 { | |
243 result.SetValue(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES, | |
244 boost::lexical_cast<std::string>(instances.size())); | |
245 } | |
246 } | |
247 | |
248 | |
249 static DicomMap* ComputeCounters(ServerContext& context, | |
250 const std::string& instanceId, | |
251 ResourceType level, | |
252 const DicomMap& query) | |
253 { | |
254 switch (level) | |
255 { | |
256 case ResourceType_Patient: | |
257 if (!query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES) && | |
258 !query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES) && | |
259 !query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES)) | |
260 { | |
261 return NULL; | |
262 } | |
263 | |
264 break; | |
265 | |
266 case ResourceType_Study: | |
267 if (!query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES) && | |
268 !query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES) && | |
269 !query.HasTag(DICOM_TAG_SOP_CLASSES_IN_STUDY) && | |
270 !query.HasTag(DICOM_TAG_MODALITIES_IN_STUDY)) | |
271 { | |
272 return NULL; | |
273 } | |
274 | |
275 break; | |
276 | |
277 case ResourceType_Series: | |
278 if (!query.HasTag(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES)) | |
279 { | |
280 return NULL; | |
281 } | |
282 | |
283 break; | |
284 | |
285 default: | |
286 return NULL; | |
287 } | |
288 | |
289 std::string parent; | |
290 if (!context.GetIndex().LookupParent(parent, instanceId, level)) | |
291 { | |
292 throw OrthancException(ErrorCode_UnknownResource); // The resource was deleted in between | |
293 } | |
294 | |
295 std::auto_ptr<DicomMap> result(new DicomMap); | |
296 | |
297 switch (level) | |
298 { | |
299 case ResourceType_Patient: | |
300 ComputePatientCounters(*result, context.GetIndex(), parent, query); | |
301 break; | |
302 | |
303 case ResourceType_Study: | |
304 ComputeStudyCounters(*result, context, parent, query); | |
305 break; | |
306 | |
307 case ResourceType_Series: | |
308 ComputeSeriesCounters(*result, context.GetIndex(), parent, query); | |
309 break; | |
310 | |
311 default: | |
312 throw OrthancException(ErrorCode_InternalError); | |
313 } | |
314 | |
315 return result.release(); | |
316 } | |
317 | |
318 | |
49 static void AddAnswer(DicomFindAnswers& answers, | 319 static void AddAnswer(DicomFindAnswers& answers, |
50 const Json::Value& resource, | 320 const Json::Value& resource, |
51 const DicomArray& query, | 321 const DicomArray& query, |
52 const std::list<DicomTag>& sequencesToReturn) | 322 const std::list<DicomTag>& sequencesToReturn, |
323 const DicomMap* counters) | |
53 { | 324 { |
54 DicomMap result; | 325 DicomMap result; |
55 | 326 |
56 for (size_t i = 0; i < query.GetSize(); i++) | 327 for (size_t i = 0; i < query.GetSize(); i++) |
57 { | 328 { |
78 result.SetValue(query.GetElement(i).GetTag(), ""); | 349 result.SetValue(query.GetElement(i).GetTag(), ""); |
79 } | 350 } |
80 } | 351 } |
81 } | 352 } |
82 | 353 |
354 if (counters != NULL) | |
355 { | |
356 DicomArray tmp(*counters); | |
357 for (size_t i = 0; i < tmp.GetSize(); i++) | |
358 { | |
359 result.SetValue(tmp.GetElement(i).GetTag(), tmp.GetElement(i).GetValue().GetContent()); | |
360 } | |
361 } | |
362 | |
83 if (result.GetSize() == 0 && | 363 if (result.GetSize() == 0 && |
84 sequencesToReturn.empty()) | 364 sequencesToReturn.empty()) |
85 { | 365 { |
86 LOG(WARNING) << "The C-FIND request does not return any DICOM tag"; | 366 LOG(WARNING) << "The C-FIND request does not return any DICOM tag"; |
87 } | 367 } |
94 ParsedDicomFile dicom(result); | 374 ParsedDicomFile dicom(result); |
95 | 375 |
96 for (std::list<DicomTag>::const_iterator tag = sequencesToReturn.begin(); | 376 for (std::list<DicomTag>::const_iterator tag = sequencesToReturn.begin(); |
97 tag != sequencesToReturn.end(); ++tag) | 377 tag != sequencesToReturn.end(); ++tag) |
98 { | 378 { |
99 std::cout << tag->Format(); | |
100 | |
101 const Json::Value& source = resource[tag->Format()]; | 379 const Json::Value& source = resource[tag->Format()]; |
102 | 380 |
103 if (source.type() == Json::objectValue && | 381 if (source.type() == Json::objectValue && |
104 source.isMember("Type") && | 382 source.isMember("Type") && |
105 source.isMember("Value") && | 383 source.isMember("Value") && |
377 complete = false; | 655 complete = false; |
378 break; | 656 break; |
379 } | 657 } |
380 else | 658 else |
381 { | 659 { |
382 AddAnswer(answers, dicom, query, sequencesToReturn); | 660 std::auto_ptr<DicomMap> counters(ComputeCounters(context_, instances[i], level, input)); |
661 AddAnswer(answers, dicom, query, sequencesToReturn, counters.get()); | |
383 } | 662 } |
384 } | 663 } |
385 } | 664 } |
386 | 665 |
387 LOG(INFO) << "Number of matching resources: " << answers.GetSize(); | 666 LOG(INFO) << "Number of matching resources: " << answers.GetSize(); |