Mercurial > hg > orthanc
comparison OrthancServer/OrthancFindRequestHandler.cpp @ 615:ec0b7a51d7bd find-move-scp
speed up find
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 24 Oct 2013 11:42:46 +0200 |
parents | fdd5f7f9c4d7 |
children | 5ab377df6d8b |
comparison
equal
deleted
inserted
replaced
614:f27923072afd | 615:ec0b7a51d7bd |
---|---|
37 #include "../Core/DicomFormat/DicomArray.h" | 37 #include "../Core/DicomFormat/DicomArray.h" |
38 #include "ServerToolbox.h" | 38 #include "ServerToolbox.h" |
39 | 39 |
40 namespace Orthanc | 40 namespace Orthanc |
41 { | 41 { |
42 static bool IsWildcard(const std::string& constraint) | |
43 { | |
44 return (constraint.find('-') != std::string::npos || | |
45 constraint.find('*') != std::string::npos || | |
46 constraint.find('\\') != std::string::npos || | |
47 constraint.find('?') != std::string::npos); | |
48 } | |
49 | |
42 static std::string ToLowerCase(const std::string& s) | 50 static std::string ToLowerCase(const std::string& s) |
43 { | 51 { |
44 std::string result = s; | 52 std::string result = s; |
45 Toolbox::ToLowerCase(result); | 53 Toolbox::ToLowerCase(result); |
46 return result; | 54 return result; |
207 | 215 |
208 answers.Add(result); | 216 answers.Add(result); |
209 } | 217 } |
210 | 218 |
211 | 219 |
212 static bool ApplyModalitiesInStudyFilter(Json::Value& filteredStudies, | 220 static bool ApplyModalitiesInStudyFilter(std::list<std::string>& filteredStudies, |
213 const Json::Value& studies, | 221 const std::list<std::string>& studies, |
214 const DicomMap& input, | 222 const DicomMap& input, |
215 ServerIndex& index) | 223 ServerIndex& index) |
216 { | 224 { |
217 filteredStudies = Json::arrayValue; | 225 filteredStudies.clear(); |
218 | 226 |
219 const DicomValue& v = input.GetValue(DICOM_TAG_MODALITIES_IN_STUDY); | 227 const DicomValue& v = input.GetValue(DICOM_TAG_MODALITIES_IN_STUDY); |
220 if (v.IsNull()) | 228 if (v.IsNull()) |
221 { | 229 { |
222 return false; | 230 return false; |
231 { | 239 { |
232 modalities.insert(tmp[i]); | 240 modalities.insert(tmp[i]); |
233 } | 241 } |
234 | 242 |
235 // Loop over the studies | 243 // Loop over the studies |
236 for (Json::Value::ArrayIndex i = 0; i < studies.size(); i++) | 244 for (std::list<std::string>::const_iterator |
245 it = studies.begin(); it != studies.end(); it++) | |
237 { | 246 { |
238 try | 247 try |
239 { | 248 { |
240 // We are considering a single study. Check whether one of | 249 // We are considering a single study. Check whether one of |
241 // its child series matches one of the modalities. | 250 // its child series matches one of the modalities. |
242 Json::Value study; | 251 Json::Value study; |
243 if (index.LookupResource(study, studies[i].asString(), ResourceType_Study)) | 252 if (index.LookupResource(study, *it, ResourceType_Study)) |
244 { | 253 { |
245 // Loop over the series of the considered study. | 254 // Loop over the series of the considered study. |
246 for (Json::Value::ArrayIndex j = 0; j < study["Series"].size(); j++) // (*) | 255 for (Json::Value::ArrayIndex j = 0; j < study["Series"].size(); j++) // (*) |
247 { | 256 { |
248 Json::Value series; | 257 Json::Value series; |
255 if (modalities.find(modality) != modalities.end()) | 264 if (modalities.find(modality) != modalities.end()) |
256 { | 265 { |
257 // This series of the considered study matches one | 266 // This series of the considered study matches one |
258 // of the required modalities. Take the study into | 267 // of the required modalities. Take the study into |
259 // consideration for future filtering. | 268 // consideration for future filtering. |
260 filteredStudies.append(studies[i]); | 269 filteredStudies.push_back(*it); |
261 | 270 |
262 // We have finished considering this study. Break the study loop at (*). | 271 // We have finished considering this study. Break the study loop at (*). |
263 break; | 272 break; |
264 } | 273 } |
265 } | 274 } |
275 | 284 |
276 return true; | 285 return true; |
277 } | 286 } |
278 | 287 |
279 | 288 |
289 static bool LookupCandidateResourcesInternal(/* out */ std::list<std::string>& resources, | |
290 /* in */ ServerIndex& index, | |
291 /* in */ ResourceType level, | |
292 /* in */ const DicomMap& query, | |
293 /* in */ DicomTag tag) | |
294 { | |
295 if (query.HasTag(tag)) | |
296 { | |
297 const DicomValue& value = query.GetValue(tag); | |
298 if (!value.IsNull()) | |
299 { | |
300 std::string str = query.GetValue(tag).AsString(); | |
301 if (!IsWildcard(str)) | |
302 { | |
303 printf(">> [%s]\n", str.c_str()); | |
304 index.LookupTagValue(resources, tag, str/*, level*/); | |
305 return true; | |
306 } | |
307 } | |
308 } | |
309 | |
310 return false; | |
311 } | |
312 | |
313 | |
314 static void LookupCandidateResources(/* out */ std::list<std::string>& resources, | |
315 /* in */ ServerIndex& index, | |
316 /* in */ ResourceType level, | |
317 /* in */ const DicomMap& query) | |
318 { | |
319 // TODO : Speed up using full querying against the MainDicomTags. | |
320 | |
321 resources.clear(); | |
322 | |
323 bool done = false; | |
324 | |
325 switch (level) | |
326 { | |
327 case ResourceType_Patient: | |
328 done = LookupCandidateResourcesInternal(resources, index, level, query, DICOM_TAG_PATIENT_ID); | |
329 break; | |
330 | |
331 case ResourceType_Study: | |
332 done = LookupCandidateResourcesInternal(resources, index, level, query, DICOM_TAG_STUDY_INSTANCE_UID); | |
333 break; | |
334 | |
335 case ResourceType_Series: | |
336 done = LookupCandidateResourcesInternal(resources, index, level, query, DICOM_TAG_SERIES_INSTANCE_UID); | |
337 break; | |
338 | |
339 case ResourceType_Instance: | |
340 done = LookupCandidateResourcesInternal(resources, index, level, query, DICOM_TAG_SOP_INSTANCE_UID); | |
341 break; | |
342 | |
343 default: | |
344 break; | |
345 } | |
346 | |
347 if (!done) | |
348 { | |
349 Json::Value allResources; | |
350 index.GetAllUuids(allResources, level); | |
351 assert(allResources.type() == Json::arrayValue); | |
352 | |
353 for (Json::Value::ArrayIndex i = 0; i < allResources.size(); i++) | |
354 { | |
355 resources.push_back(allResources[i].asString()); | |
356 } | |
357 } | |
358 } | |
359 | |
360 | |
280 void OrthancFindRequestHandler::Handle(const DicomMap& input, | 361 void OrthancFindRequestHandler::Handle(const DicomMap& input, |
281 DicomFindAnswers& answers) | 362 DicomFindAnswers& answers) |
282 { | 363 { |
283 LOG(WARNING) << "Find-SCU request received"; | 364 LOG(WARNING) << "Find-SCU request received"; |
284 | 365 |
301 throw OrthancException(ErrorCode_NotImplemented); | 382 throw OrthancException(ErrorCode_NotImplemented); |
302 } | 383 } |
303 | 384 |
304 | 385 |
305 /** | 386 /** |
306 * Retrieve all the resources for this query level. | 387 * Retrieve the candidate resources for this query level. Whenever |
388 * possible, we avoid returning ALL the resources for this query | |
389 * level, as it would imply reading the JSON file on the harddisk | |
390 * for each of them. | |
307 **/ | 391 **/ |
308 | 392 |
309 Json::Value resources; | 393 std::list<std::string> resources; |
310 context_.GetIndex().GetAllUuids(resources, level); | 394 LookupCandidateResources(resources, context_.GetIndex(), level, input); |
311 assert(resources.type() == Json::arrayValue); | |
312 | |
313 // TODO : Speed up using MainDicomTags (to avoid looping over ALL | |
314 // the resources and reading the JSON file for each of them) | |
315 | |
316 | 395 |
317 | 396 |
318 /** | 397 /** |
319 * Apply filtering on modalities for studies, if asked (this is an | 398 * Apply filtering on modalities for studies, if asked (this is an |
320 * extension to standard DICOM) | 399 * extension to standard DICOM) |
322 **/ | 401 **/ |
323 | 402 |
324 if (level == ResourceType_Study && | 403 if (level == ResourceType_Study && |
325 input.HasTag(DICOM_TAG_MODALITIES_IN_STUDY)) | 404 input.HasTag(DICOM_TAG_MODALITIES_IN_STUDY)) |
326 { | 405 { |
327 Json::Value filtered; | 406 std::list<std::string> filtered; |
328 if (ApplyModalitiesInStudyFilter(filtered, resources, input, context_.GetIndex())) | 407 if (ApplyModalitiesInStudyFilter(filtered, resources, input, context_.GetIndex())) |
329 { | 408 { |
330 resources = filtered; | 409 resources = filtered; |
331 } | 410 } |
332 } | 411 } |
335 /** | 414 /** |
336 * Loop over all the resources for this query level. | 415 * Loop over all the resources for this query level. |
337 **/ | 416 **/ |
338 | 417 |
339 DicomArray query(input); | 418 DicomArray query(input); |
340 for (Json::Value::ArrayIndex i = 0; i < resources.size(); i++) | 419 for (std::list<std::string>::const_iterator |
420 resource = resources.begin(); resource != resources.end(); resource++) | |
341 { | 421 { |
342 try | 422 try |
343 { | 423 { |
344 std::string instance; | 424 std::string instance; |
345 if (LookupOneInstance(instance, context_.GetIndex(), resources[i].asString(), level)) | 425 if (LookupOneInstance(instance, context_.GetIndex(), *resource, level)) |
346 { | 426 { |
347 Json::Value resource; | 427 Json::Value info; |
348 context_.ReadJson(resource, instance); | 428 context_.ReadJson(info, instance); |
349 | 429 |
350 if (Matches(resource, query)) | 430 if (Matches(info, query)) |
351 { | 431 { |
352 AddAnswer(answers, resource, query); | 432 AddAnswer(answers, info, query); |
353 } | 433 } |
354 } | 434 } |
355 } | 435 } |
356 catch (OrthancException&) | 436 catch (OrthancException&) |
357 { | 437 { |