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();