comparison Sources/Plugin.cpp @ 3:0fb06c6a6c87

added route "/instances/{id}/stl"
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 18 Jul 2023 10:01:31 +0200
parents 2bdb9acb7dcf
children 5ee4448a8ff8
comparison
equal deleted inserted replaced
2:2bdb9acb7dcf 3:0fb06c6a6c87
117 117
118 void ServeFile(OrthancPluginRestOutput* output, 118 void ServeFile(OrthancPluginRestOutput* output,
119 const char* url, 119 const char* url,
120 const OrthancPluginHttpRequest* request) 120 const OrthancPluginHttpRequest* request)
121 { 121 {
122 if (request->method != OrthancPluginHttpMethod_Get)
123 {
124 OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "GET");
125 return;
126 }
127
122 std::string file = request->groups[0]; 128 std::string file = request->groups[0];
123 129
124 if (file == "viewer.html") 130 if (file == "viewer.html")
125 { 131 {
126 std::string s; 132 std::string s;
232 y2_ = std::max(y2_, y); 238 y2_ = std::max(y2_, y);
233 } 239 }
234 } 240 }
235 }; 241 };
236 242
237 static void GetStringValue(std::string& value, 243 static std::string GetStringValue(DcmItem& item,
238 DcmItem& item, 244 const DcmTagKey& key)
239 const DcmTagKey& key)
240 { 245 {
241 const char* s = NULL; 246 const char* s = NULL;
242 if (!item.findAndGetString(key, s).good() || 247 if (!item.findAndGetString(key, s).good() ||
243 s == NULL) 248 s == NULL)
244 { 249 {
245 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); 250 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
246 } 251 }
247 else 252 else
248 { 253 {
249 value.assign(s); 254 return Orthanc::Toolbox::StripSpaces(s);
250 } 255 }
251 } 256 }
252 257
253 258
254 static void ListStructuresNames(std::set<std::string>& target, 259 static void ListStructuresNames(std::set<std::string>& target,
270 { 275 {
271 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); 276 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
272 } 277 }
273 else 278 else
274 { 279 {
275 std::string value; 280 target.insert(GetStringValue(*item, DCM_ROIName));
276 GetStringValue(value, *item, DCM_ROIName);
277 target.insert(value);
278 } 281 }
279 } 282 }
280 } 283 }
281 284
282 285
413 referenced->card() != 1) 416 referenced->card() != 1)
414 { 417 {
415 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); 418 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
416 } 419 }
417 420
418 GetStringValue(roiName_, *structure, DCM_ROIName); 421 roiName_ = GetStringValue(*structure, DCM_ROIName);
419 GetStringValue(referencedSopInstanceUid_, *referenced->getItem(0), DCM_ReferencedSOPInstanceUID); 422 referencedSopInstanceUid_ = GetStringValue(*referenced->getItem(0), DCM_ReferencedSOPInstanceUID);
420 423
421 std::string s; 424 if (GetStringValue(*contour, DCM_ContourGeometricType) != "CLOSED_PLANAR")
422 GetStringValue(s, *contour, DCM_ContourGeometricType);
423 if (s != "CLOSED_PLANAR")
424 { 425 {
425 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); 426 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
426 } 427 }
427 428
428 { 429 {
429 std::string color;
430 GetStringValue(color, *roi, DCM_ROIDisplayColor);
431
432 std::vector<std::string> tokens; 430 std::vector<std::string> tokens;
433 Orthanc::Toolbox::TokenizeString(tokens, color, '\\'); 431 Orthanc::Toolbox::TokenizeString(tokens, GetStringValue(*roi, DCM_ROIDisplayColor), '\\');
434 432
435 uint32_t r, g, b; 433 uint32_t r, g, b;
436 if (tokens.size() != 3 || 434 if (tokens.size() != 3 ||
437 !Orthanc::SerializationToolbox::ParseFirstUnsignedInteger32(r, tokens[0]) || 435 !Orthanc::SerializationToolbox::ParseFirstUnsignedInteger32(r, tokens[0]) ||
438 !Orthanc::SerializationToolbox::ParseFirstUnsignedInteger32(g, tokens[1]) || 436 !Orthanc::SerializationToolbox::ParseFirstUnsignedInteger32(g, tokens[1]) ||
448 green_ = g; 446 green_ = g;
449 blue_ = b; 447 blue_ = b;
450 } 448 }
451 449
452 { 450 {
453 GetStringValue(s, *contour, DCM_ContourData);
454
455 std::vector<std::string> tokens; 451 std::vector<std::string> tokens;
456 Orthanc::Toolbox::TokenizeString(tokens, s, '\\'); 452 Orthanc::Toolbox::TokenizeString(tokens, GetStringValue(*contour, DCM_ContourData), '\\');
457 453
458 GetStringValue(s, *contour, DCM_NumberOfContourPoints); 454 const std::string s = GetStringValue(*contour, DCM_NumberOfContourPoints);
459 455
460 uint32_t countPoints; 456 uint32_t countPoints;
461 if (!Orthanc::SerializationToolbox::ParseUnsignedInteger32(countPoints, s) || 457 if (!Orthanc::SerializationToolbox::ParseUnsignedInteger32(countPoints, s) ||
462 tokens.size() != 3 * countPoints) 458 tokens.size() != 3 * countPoints)
463 { 459 {
692 slicesSpacing_(0), 688 slicesSpacing_(0),
693 minProjectionAlongNormal_(0), 689 minProjectionAlongNormal_(0),
694 maxProjectionAlongNormal_(0) 690 maxProjectionAlongNormal_(0)
695 { 691 {
696 DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset(); 692 DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset();
697 GetStringValue(patientId_, dataset, DCM_PatientID); 693 patientId_ = GetStringValue(dataset, DCM_PatientID);
698 GetStringValue(studyInstanceUid_, dataset, DCM_StudyInstanceUID); 694 studyInstanceUid_ = GetStringValue(dataset, DCM_StudyInstanceUID);
699 GetStringValue(seriesInstanceUid_, dataset, DCM_SeriesInstanceUID); 695 seriesInstanceUid_ = GetStringValue(dataset, DCM_SeriesInstanceUID);
700 GetStringValue(sopInstanceUid_, dataset, DCM_SOPInstanceUID); 696 sopInstanceUid_ = GetStringValue(dataset, DCM_SOPInstanceUID);
701 697
702 DcmSequenceOfItems* rois = NULL; 698 DcmSequenceOfItems* rois = NULL;
703 if (!dataset.findAndGetSequence(DCM_ROIContourSequence, rois).good() || 699 if (!dataset.findAndGetSequence(DCM_ROIContourSequence, rois).good() ||
704 rois == NULL) 700 rois == NULL)
705 { 701 {
1109 1105
1110 return true; 1106 return true;
1111 } 1107 }
1112 1108
1113 1109
1110 static Orthanc::ParsedDicomFile* LoadInstance(const std::string& instanceId)
1111 {
1112 std::string dicom;
1113
1114 if (!OrthancPlugins::RestApiGetString(dicom, "/instances/" + instanceId + "/file", false))
1115 {
1116 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
1117 }
1118 else
1119 {
1120 return new Orthanc::ParsedDicomFile(dicom);
1121 }
1122 }
1123
1124
1114 void ListStructures(OrthancPluginRestOutput* output, 1125 void ListStructures(OrthancPluginRestOutput* output,
1115 const char* url, 1126 const char* url,
1116 const OrthancPluginHttpRequest* request) 1127 const OrthancPluginHttpRequest* request)
1117 { 1128 {
1118 const std::string instanceId(request->groups[0]);
1119
1120 if (request->method != OrthancPluginHttpMethod_Get) 1129 if (request->method != OrthancPluginHttpMethod_Get)
1121 { 1130 {
1122 OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "GET"); 1131 OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "GET");
1123 return; 1132 return;
1124 } 1133 }
1125 1134
1126 std::string dicom; 1135 const std::string instanceId(request->groups[0]);
1127 if (!OrthancPlugins::RestApiGetString(dicom, "/instances/" + instanceId + "/file", false)) 1136
1128 { 1137 std::unique_ptr<Orthanc::ParsedDicomFile> dicom(LoadInstance(instanceId));
1129 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); 1138
1130 } 1139 std::set<std::string> names;
1131 else 1140 ListStructuresNames(names, *dicom);
1132 { 1141
1133 Orthanc::ParsedDicomFile parsed(dicom); 1142 Json::Value answer = Json::arrayValue;
1134 1143
1135 std::set<std::string> names; 1144 for (std::set<std::string>::const_iterator it = names.begin(); it != names.end(); ++it)
1136 ListStructuresNames(names, parsed); 1145 {
1137 1146 answer.append(*it);
1138 Json::Value answer = Json::arrayValue; 1147 }
1139 1148
1140 for (std::set<std::string>::const_iterator it = names.begin(); it != names.end(); ++it) 1149 std::string s = answer.toStyledString();
1141 { 1150 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::MIME_JSON);
1142 answer.append(*it);
1143 }
1144
1145 std::string s = answer.toStyledString();
1146 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::MIME_JSON);
1147 }
1148 } 1151 }
1149 1152
1150 1153
1151 void Encode(OrthancPluginRestOutput* output, 1154 void Encode(OrthancPluginRestOutput* output,
1152 const char* url, 1155 const char* url,
1179 256 /* default value */); 1182 256 /* default value */);
1180 1183
1181 std::set<std::string> roiNames; 1184 std::set<std::string> roiNames;
1182 Orthanc::SerializationToolbox::ReadSetOfStrings(roiNames, body, KEY_ROI_NAMES); 1185 Orthanc::SerializationToolbox::ReadSetOfStrings(roiNames, body, KEY_ROI_NAMES);
1183 1186
1184 std::string dicom; 1187 std::unique_ptr<Orthanc::ParsedDicomFile> dicom(LoadInstance(instanceId));
1185 if (!OrthancPlugins::RestApiGetString(dicom, "/instances/" + instanceId + "/file", false)) 1188
1186 { 1189 StructureSet structureSet(*dicom);
1187 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); 1190
1191 std::string stl;
1192 if (!EncodeStructureSetMesh(stl, structureSet, roiNames, resolution, smooth))
1193 {
1194 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot encode STL");
1188 } 1195 }
1189 else 1196 else
1190 { 1197 {
1191 Orthanc::ParsedDicomFile parsed(dicom); 1198 std::string content;
1192 StructureSet structureSet(parsed); 1199 Orthanc::Toolbox::EncodeDataUriScheme(content, "model/stl", stl);
1193 1200
1194 std::string stl; 1201 Json::Value create;
1195 if (!EncodeStructureSetMesh(stl, structureSet, roiNames, resolution, smooth)) 1202 create["Content"] = content;
1196 { 1203 create["Parent"] = structureSet.HashStudy();
1197 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot encode STL"); 1204
1205 if (body.isMember(KEY_TAGS))
1206 {
1207 create[KEY_TAGS] = body[KEY_TAGS];
1198 } 1208 }
1199 else 1209 else
1200 { 1210 {
1201 std::string content; 1211 std::string description;
1202 Orthanc::Toolbox::EncodeDataUriScheme(content, "model/stl", stl); 1212
1203 1213 if (dicom->GetTagValue(description, Orthanc::DICOM_TAG_SERIES_DESCRIPTION))
1204 Json::Value create; 1214 {
1205 create["Content"] = content; 1215 description += ": ";
1206 create["Parent"] = structureSet.HashStudy();
1207
1208 if (body.isMember(KEY_TAGS))
1209 {
1210 create[KEY_TAGS] = body[KEY_TAGS];
1211 } 1216 }
1212 else 1217 else
1213 { 1218 {
1214 std::string description; 1219 description.clear();
1215 1220 }
1216 if (parsed.GetTagValue(description, Orthanc::DICOM_TAG_SERIES_DESCRIPTION)) 1221
1222 bool first = true;
1223 for (std::set<std::string>::const_iterator it = roiNames.begin(); it != roiNames.end(); ++it)
1224 {
1225 if (first)
1217 { 1226 {
1218 description += ": "; 1227 first = false;
1219 } 1228 }
1220 else 1229 else
1221 { 1230 {
1222 description.clear(); 1231 description += ", ";
1223 } 1232 }
1224 1233
1225 bool first = true; 1234 description += *it;
1226 for (std::set<std::string>::const_iterator it = roiNames.begin(); it != roiNames.end(); ++it) 1235 }
1227 { 1236
1228 if (first) 1237 create[KEY_TAGS] = Json::objectValue;
1229 { 1238 create[KEY_TAGS]["SeriesDescription"] = description;
1230 first = false; 1239 }
1231 } 1240
1232 else 1241 Json::Value answer;
1233 { 1242 if (OrthancPlugins::RestApiPost(answer, "/tools/create-dicom", create.toStyledString(), false))
1234 description += ", "; 1243 {
1235 } 1244 std::string s = answer.toStyledString();
1236 1245 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::MIME_JSON);
1237 description += *it; 1246 }
1238 } 1247 else
1239 1248 {
1240 create[KEY_TAGS] = Json::objectValue; 1249 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "Cannot create DICOM from STL");
1241 create[KEY_TAGS]["SeriesDescription"] = description; 1250 }
1242 } 1251 }
1243 1252 }
1244 Json::Value answer; 1253
1245 if (OrthancPlugins::RestApiPost(answer, "/tools/create-dicom", create.toStyledString(), false)) 1254
1246 { 1255 void ExtractStl(OrthancPluginRestOutput* output,
1247 std::string s = answer.toStyledString(); 1256 const char* url,
1248 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::MIME_JSON); 1257 const OrthancPluginHttpRequest* request)
1249 } 1258 {
1250 else 1259 if (request->method != OrthancPluginHttpMethod_Get)
1251 { 1260 {
1252 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "Cannot create DICOM from STL"); 1261 OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "GET");
1253 } 1262 return;
1254 } 1263 }
1264
1265 const std::string instanceId(request->groups[0]);
1266
1267 std::unique_ptr<Orthanc::ParsedDicomFile> dicom(LoadInstance(instanceId));
1268 DcmDataset& dataset = *dicom->GetDcmtkObject().getDataset();
1269
1270 std::string stl;
1271 if (GetStringValue(dataset, DCM_MIMETypeOfEncapsulatedDocument) != Orthanc::MIME_STL ||
1272 GetStringValue(dataset, DCM_SOPClassUID) != "1.2.840.10008.5.1.4.1.1.104.3" ||
1273 !dicom->GetTagValue(stl, Orthanc::DICOM_TAG_ENCAPSULATED_DOCUMENT))
1274 {
1275 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "DICOM instance not encapsulating a STL model: " + instanceId);
1276 }
1277 else
1278 {
1279 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output,
1280 stl.empty() ? NULL : stl.c_str(), stl.size(), Orthanc::MIME_STL);
1255 } 1281 }
1256 } 1282 }
1257 1283
1258 1284
1259 extern "C" 1285 extern "C"
1292 } 1318 }
1293 1319
1294 OrthancPluginSetDescription(context, "STL plugin for Orthanc."); 1320 OrthancPluginSetDescription(context, "STL plugin for Orthanc.");
1295 1321
1296 OrthancPlugins::RegisterRestCallback<ServeFile>("/stl/app/(.*)", true); 1322 OrthancPlugins::RegisterRestCallback<ServeFile>("/stl/app/(.*)", true);
1323 OrthancPlugins::RegisterRestCallback<ExtractStl>("/instances/([0-9a-f-]+)/stl", true);
1297 OrthancPlugins::RegisterRestCallback<ListStructures>("/stl/rt-struct/([0-9a-f-]+)", true); 1324 OrthancPlugins::RegisterRestCallback<ListStructures>("/stl/rt-struct/([0-9a-f-]+)", true);
1298 1325
1299 if (hasCreateDicomStl_) 1326 if (hasCreateDicomStl_)
1300 { 1327 {
1301 OrthancPlugins::RegisterRestCallback<Encode>("/stl/encode", true); 1328 OrthancPlugins::RegisterRestCallback<Encode>("/stl/encode", true);