Mercurial > hg > orthanc-ohif
comparison Sources/Plugin.cpp @ 8:cc4c81d08bf0
added missing tags for PET
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Sun, 18 Jun 2023 13:48:13 +0200 |
parents | eab054ee7537 |
children | e8dea04df69b |
comparison
equal
deleted
inserted
replaced
7:eab054ee7537 | 8:cc4c81d08bf0 |
---|---|
86 }; | 86 }; |
87 | 87 |
88 typedef std::map<Orthanc::DicomTag, TagInformation> TagsDictionary; | 88 typedef std::map<Orthanc::DicomTag, TagInformation> TagsDictionary; |
89 | 89 |
90 static TagsDictionary ohifStudyTags_, ohifSeriesTags_, ohifInstanceTags_, allTags_; | 90 static TagsDictionary ohifStudyTags_, ohifSeriesTags_, ohifInstanceTags_, allTags_; |
91 | |
92 | |
93 static const Orthanc::DicomTag RADIOPHARMACEUTICAL_INFORMATION_SEQUENCE(0x0054, 0x0016); | |
91 | 94 |
92 static void InitializeOhifTags() | 95 static void InitializeOhifTags() |
93 { | 96 { |
94 /** | 97 /** |
95 * Those are the tags that are found in the documentation of the | 98 * Those are the tags that are found in the documentation of the |
138 /** | 141 /** |
139 * The items below are related to PET scans. Their list can be found | 142 * The items below are related to PET scans. Their list can be found |
140 * by looking for "required metadata are missing" in | 143 * by looking for "required metadata are missing" in |
141 * "extensions/default/src/getPTImageIdInstanceMetadata.ts" | 144 * "extensions/default/src/getPTImageIdInstanceMetadata.ts" |
142 **/ | 145 **/ |
143 ohifInstanceTags_[Orthanc::DICOM_TAG_SERIES_TIME] = TagInformation(DataType_String, "SeriesTime"); | 146 ohifInstanceTags_[Orthanc::DICOM_TAG_ACQUISITION_DATE] = TagInformation(DataType_String, "AcquisitionDate"); |
144 ohifInstanceTags_[Orthanc::DicomTag(0x0010, 0x1030)] = TagInformation(DataType_Float, "PatientWeight"); | 147 ohifInstanceTags_[Orthanc::DICOM_TAG_ACQUISITION_TIME] = TagInformation(DataType_String, "AcquisitionTime"); |
145 ohifInstanceTags_[Orthanc::DicomTag(0x0028, 0x0051)] = TagInformation(DataType_ListOfStrings, "CorrectedImage"); | 148 ohifInstanceTags_[Orthanc::DICOM_TAG_SERIES_TIME] = TagInformation(DataType_String, "SeriesTime"); |
146 ohifInstanceTags_[Orthanc::DicomTag(0x0054, 0x1001)] = TagInformation(DataType_String, "Units"); | 149 ohifInstanceTags_[Orthanc::DicomTag(0x0010, 0x1020)] = TagInformation(DataType_Float, "PatientSize"); |
147 ohifInstanceTags_[Orthanc::DicomTag(0x0054, 0x1102)] = TagInformation(DataType_String, "DecayCorrection"); | 150 ohifInstanceTags_[Orthanc::DicomTag(0x0010, 0x1030)] = TagInformation(DataType_Float, "PatientWeight"); |
151 ohifInstanceTags_[Orthanc::DicomTag(0x0018, 0x1242)] = TagInformation(DataType_Integer, "ActualFrameDuration"); | |
152 ohifInstanceTags_[Orthanc::DicomTag(0x0028, 0x0051)] = TagInformation(DataType_ListOfStrings, "CorrectedImage"); | |
153 ohifInstanceTags_[Orthanc::DicomTag(0x0054, 0x1001)] = TagInformation(DataType_String, "Units"); | |
154 ohifInstanceTags_[Orthanc::DicomTag(0x0054, 0x1102)] = TagInformation(DataType_String, "DecayCorrection"); | |
155 ohifInstanceTags_[Orthanc::DicomTag(0x0054, 0x1300)] = TagInformation(DataType_Float, "FrameReferenceTime"); | |
156 ohifInstanceTags_[RADIOPHARMACEUTICAL_INFORMATION_SEQUENCE] = TagInformation(DataType_None, "RadiopharmaceuticalInformationSequence"); | |
157 | |
158 // UNTESTED | |
159 ohifInstanceTags_[Orthanc::DicomTag(0x7053, 0x1000)] = TagInformation(DataType_Float, "70531000"); // Philips SUVScaleFactor | |
160 ohifInstanceTags_[Orthanc::DicomTag(0x7053, 0x1009)] = TagInformation(DataType_Float, "70531009"); // Philips ActivityConcentrationScaleFactor | |
161 ohifInstanceTags_[Orthanc::DicomTag(0x0009, 0x100d)] = TagInformation(DataType_String, "0009100d"); // GE PrivatePostInjectionDateTime | |
148 | 162 |
149 for (TagsDictionary::const_iterator it = ohifStudyTags_.begin(); it != ohifStudyTags_.end(); ++it) | 163 for (TagsDictionary::const_iterator it = ohifStudyTags_.begin(); it != ohifStudyTags_.end(); ++it) |
150 { | 164 { |
151 assert(allTags_.find(it->first) == allTags_.end() || | 165 assert(allTags_.find(it->first) == allTags_.end() || |
152 allTags_[it->first] == it->second); | 166 allTags_[it->first] == it->second); |
234 } | 248 } |
235 } | 249 } |
236 }; | 250 }; |
237 | 251 |
238 | 252 |
253 static bool ParseTagFromOrthanc(Json::Value& target, | |
254 const Orthanc::DicomTag& tag, | |
255 const std::string& name, | |
256 DataType type, | |
257 const Json::Value& source) | |
258 { | |
259 const std::string formattedTag = tag.Format(); | |
260 | |
261 if (source.isMember(formattedTag)) | |
262 { | |
263 const Json::Value& value = source[formattedTag]; | |
264 | |
265 /** | |
266 * The cases below derive from "Toolbox::SimplifyDicomAsJson()" | |
267 * with "DicomToJsonFormat_Short", which is invoked by the REST | |
268 * API call to "/instances/.../tags?short". | |
269 **/ | |
270 | |
271 switch (value.type()) | |
272 { | |
273 case Json::nullValue: | |
274 return false; | |
275 | |
276 case Json::arrayValue: | |
277 // This should never happen, as this would correspond to a sequence | |
278 return false; | |
279 | |
280 case Json::stringValue: | |
281 { | |
282 switch (type) | |
283 { | |
284 case DataType_String: | |
285 target[name] = value; | |
286 return true; | |
287 | |
288 case DataType_Integer: | |
289 { | |
290 int32_t v; | |
291 if (Orthanc::SerializationToolbox::ParseInteger32(v, value.asString())) | |
292 { | |
293 target[name] = v; | |
294 } | |
295 return true; | |
296 } | |
297 | |
298 case DataType_Float: | |
299 { | |
300 float v; | |
301 if (Orthanc::SerializationToolbox::ParseFloat(v, value.asString())) | |
302 { | |
303 target[name] = v; | |
304 } | |
305 return true; | |
306 } | |
307 | |
308 case DataType_ListOfStrings: | |
309 { | |
310 std::vector<std::string> tokens; | |
311 Orthanc::Toolbox::TokenizeString(tokens, value.asString(), '\\'); | |
312 target[name] = Json::arrayValue; | |
313 for (size_t i = 0; i < tokens.size(); i++) | |
314 { | |
315 target[name].append(tokens[i]); | |
316 } | |
317 return true; | |
318 } | |
319 | |
320 case DataType_ListOfFloats: | |
321 { | |
322 std::vector<std::string> tokens; | |
323 Orthanc::Toolbox::TokenizeString(tokens, value.asString(), '\\'); | |
324 target[name] = Json::arrayValue; | |
325 for (size_t i = 0; i < tokens.size(); i++) | |
326 { | |
327 float v; | |
328 if (Orthanc::SerializationToolbox::ParseFloat(v, tokens[i])) | |
329 { | |
330 target[name].append(v); | |
331 } | |
332 } | |
333 return true; | |
334 } | |
335 | |
336 default: | |
337 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
338 } | |
339 } | |
340 | |
341 default: | |
342 // This should never happen | |
343 return false; | |
344 } | |
345 } | |
346 else | |
347 { | |
348 return false; | |
349 } | |
350 } | |
351 | |
352 | |
239 static bool GetOhifDicomTags(Json::Value& target, | 353 static bool GetOhifDicomTags(Json::Value& target, |
240 const std::string& instanceId) | 354 const std::string& instanceId) |
241 { | 355 { |
242 Json::Value source; | 356 Json::Value source; |
243 if (!OrthancPlugins::RestApiGet(source, "/instances/" + instanceId + "/tags?short", false)) | 357 if (!OrthancPlugins::RestApiGet(source, "/instances/" + instanceId + "/tags?short", false)) |
250 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | 364 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); |
251 } | 365 } |
252 | 366 |
253 for (TagsDictionary::const_iterator it = allTags_.begin(); it != allTags_.end(); ++it) | 367 for (TagsDictionary::const_iterator it = allTags_.begin(); it != allTags_.end(); ++it) |
254 { | 368 { |
255 const std::string tag = it->first.Format(); | 369 ParseTagFromOrthanc(target, it->first, it->first.Format(), it->second.GetType(), source); |
256 | 370 } |
257 if (source.isMember(tag)) | 371 |
258 { | 372 /** |
259 const Json::Value& value = source[tag]; | 373 * This is a sequence for PET scans that is manually injected, to be |
260 | 374 * used in function "getPTImageIdInstanceMetadata()" of |
261 /** | 375 * "extensions/default/src/getPTImageIdInstanceMetadata.ts" |
262 * The cases below derive from "Toolbox::SimplifyDicomAsJson()" | 376 **/ |
263 * with "DicomToJsonFormat_Short", which is invoked by the REST | 377 static const Orthanc::DicomTag RADIONUCLIDE_HALF_LIFE(0x0018, 0x1075); |
264 * API call to "/instances/.../tags?short". | 378 static const Orthanc::DicomTag RADIONUCLIDE_TOTAL_DOSE(0x0018, 0x1074); |
265 **/ | 379 static const Orthanc::DicomTag RADIOPHARMACEUTICAL_START_DATETIME(0x0018, 0x1078); |
266 | 380 static const Orthanc::DicomTag RADIOPHARMACEUTICAL_START_TIME(0x0018, 0x1072); |
267 switch (value.type()) | 381 |
268 { | 382 if (source.isMember(RADIOPHARMACEUTICAL_INFORMATION_SEQUENCE.Format())) |
269 case Json::nullValue: | 383 { |
270 break; | 384 const Json::Value& pharma = source[RADIOPHARMACEUTICAL_INFORMATION_SEQUENCE.Format()]; |
271 | 385 if (pharma.type() == Json::arrayValue && |
272 case Json::arrayValue: | 386 pharma.size() > 0 && |
273 // This should never happen, as this would correspond to a sequence | 387 pharma[0].type() == Json::objectValue) |
274 break; | 388 { |
275 | 389 Json::Value info; |
276 case Json::stringValue: | 390 if (ParseTagFromOrthanc(info, RADIONUCLIDE_HALF_LIFE, "RadionuclideHalfLife", DataType_Float, pharma[0]) && |
277 { | 391 ParseTagFromOrthanc(info, RADIONUCLIDE_TOTAL_DOSE, "RadionuclideTotalDose", DataType_Float, pharma[0]) && |
278 switch (it->second.GetType()) | 392 (ParseTagFromOrthanc(info, RADIOPHARMACEUTICAL_START_DATETIME, "RadiopharmaceuticalStartDateTime", DataType_String, pharma[0]) || |
279 { | 393 ParseTagFromOrthanc(info, RADIOPHARMACEUTICAL_START_TIME, "RadiopharmaceuticalStartTime", DataType_String, pharma[0]))) |
280 case DataType_String: | 394 { |
281 target[tag] = value; | 395 Json::Value sequence = Json::arrayValue; |
282 break; | 396 sequence.append(info); |
283 | 397 |
284 case DataType_Integer: | 398 target[RADIOPHARMACEUTICAL_INFORMATION_SEQUENCE.Format()] = sequence; |
285 { | |
286 int32_t v; | |
287 if (Orthanc::SerializationToolbox::ParseInteger32(v, value.asString())) | |
288 { | |
289 target[tag] = v; | |
290 } | |
291 break; | |
292 } | |
293 | |
294 case DataType_Float: | |
295 { | |
296 float v; | |
297 if (Orthanc::SerializationToolbox::ParseFloat(v, value.asString())) | |
298 { | |
299 target[tag] = v; | |
300 } | |
301 break; | |
302 } | |
303 | |
304 case DataType_ListOfStrings: | |
305 { | |
306 std::vector<std::string> tokens; | |
307 Orthanc::Toolbox::TokenizeString(tokens, value.asString(), '\\'); | |
308 target[tag] = Json::arrayValue; | |
309 for (size_t i = 0; i < tokens.size(); i++) | |
310 { | |
311 target[tag].append(tokens[i]); | |
312 } | |
313 break; | |
314 } | |
315 | |
316 case DataType_ListOfFloats: | |
317 { | |
318 std::vector<std::string> tokens; | |
319 Orthanc::Toolbox::TokenizeString(tokens, value.asString(), '\\'); | |
320 target[tag] = Json::arrayValue; | |
321 for (size_t i = 0; i < tokens.size(); i++) | |
322 { | |
323 float v; | |
324 if (Orthanc::SerializationToolbox::ParseFloat(v, tokens[i])) | |
325 { | |
326 target[tag].append(v); | |
327 } | |
328 } | |
329 break; | |
330 } | |
331 | |
332 default: | |
333 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
334 } | |
335 } | |
336 | |
337 default: | |
338 // This should never happen | |
339 break; | |
340 } | 399 } |
341 } | 400 } |
342 } | 401 } |
343 | 402 |
344 return true; | 403 return true; |