Mercurial > hg > orthanc-stl
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); |