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;