Mercurial > hg > orthanc-stl
comparison Sources/Plugin.cpp @ 4:5ee4448a8ff8
generated STL pass dciodvfy
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 18 Jul 2023 11:30:06 +0200 |
parents | 0fb06c6a6c87 |
children | c02d12eb34d4 |
comparison
equal
deleted
inserted
replaced
3:0fb06c6a6c87 | 4:5ee4448a8ff8 |
---|---|
22 **/ | 22 **/ |
23 | 23 |
24 | 24 |
25 #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" | 25 #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" |
26 | 26 |
27 #include <EmbeddedResources.h> | |
28 | |
27 #include <ChunkedBuffer.h> | 29 #include <ChunkedBuffer.h> |
30 #include <DicomParsing/FromDcmtkBridge.h> | |
28 #include <DicomParsing/ParsedDicomFile.h> | 31 #include <DicomParsing/ParsedDicomFile.h> |
29 #include <Images/ImageProcessing.h> | 32 #include <Images/ImageProcessing.h> |
30 #include <Logging.h> | 33 #include <Logging.h> |
31 #include <OrthancFramework.h> | 34 #include <OrthancFramework.h> |
32 #include <SerializationToolbox.h> | 35 #include <SerializationToolbox.h> |
33 #include <SystemToolbox.h> | 36 #include <SystemToolbox.h> |
34 | 37 |
35 #include <EmbeddedResources.h> | 38 #include <vtkImageConstantPad.h> |
36 | |
37 #include <vtkImageData.h> | 39 #include <vtkImageData.h> |
38 #include <vtkImageResize.h> | 40 #include <vtkImageResize.h> |
39 #include <vtkMarchingCubes.h> | 41 #include <vtkMarchingCubes.h> |
40 #include <vtkNew.h> | 42 #include <vtkNew.h> |
41 #include <vtkPolyData.h> | 43 #include <vtkPolyData.h> |
44 #include <vtkPolyDataNormals.h> | |
45 #include <vtkSmoothPolyDataFilter.h> | |
42 #include <vtkTriangle.h> | 46 #include <vtkTriangle.h> |
43 #include <vtkSmoothPolyDataFilter.h> | |
44 #include <vtkPolyDataNormals.h> | |
45 #include <vtkImageConstantPad.h> | |
46 | 47 |
47 #include <boost/thread/shared_mutex.hpp> | 48 #include <boost/thread/shared_mutex.hpp> |
48 | 49 |
49 // Forward declaration | 50 // Forward declaration |
50 void ReadStaticAsset(std::string& target, | 51 void ReadStaticAsset(std::string& target, |
590 double maxProjectionAlongNormal_; | 591 double maxProjectionAlongNormal_; |
591 std::string patientId_; | 592 std::string patientId_; |
592 std::string studyInstanceUid_; | 593 std::string studyInstanceUid_; |
593 std::string seriesInstanceUid_; | 594 std::string seriesInstanceUid_; |
594 std::string sopInstanceUid_; | 595 std::string sopInstanceUid_; |
596 bool hasFrameOfReferenceUid_; | |
597 std::string frameOfReferenceUid_; | |
595 | 598 |
596 void ComputeGeometry() | 599 void ComputeGeometry() |
597 { | 600 { |
598 std::list<double> positionsList; | 601 std::list<double> positionsList; |
599 hasGeometry_ = false; | 602 hasGeometry_ = false; |
685 public: | 688 public: |
686 explicit StructureSet(Orthanc::ParsedDicomFile& dicom) : | 689 explicit StructureSet(Orthanc::ParsedDicomFile& dicom) : |
687 hasGeometry_(false), | 690 hasGeometry_(false), |
688 slicesSpacing_(0), | 691 slicesSpacing_(0), |
689 minProjectionAlongNormal_(0), | 692 minProjectionAlongNormal_(0), |
690 maxProjectionAlongNormal_(0) | 693 maxProjectionAlongNormal_(0), |
694 hasFrameOfReferenceUid_(false) | |
691 { | 695 { |
692 DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset(); | 696 DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset(); |
693 patientId_ = GetStringValue(dataset, DCM_PatientID); | 697 patientId_ = GetStringValue(dataset, DCM_PatientID); |
694 studyInstanceUid_ = GetStringValue(dataset, DCM_StudyInstanceUID); | 698 studyInstanceUid_ = GetStringValue(dataset, DCM_StudyInstanceUID); |
695 seriesInstanceUid_ = GetStringValue(dataset, DCM_SeriesInstanceUID); | 699 seriesInstanceUid_ = GetStringValue(dataset, DCM_SeriesInstanceUID); |
696 sopInstanceUid_ = GetStringValue(dataset, DCM_SOPInstanceUID); | 700 sopInstanceUid_ = GetStringValue(dataset, DCM_SOPInstanceUID); |
701 | |
702 DcmSequenceOfItems* frame = NULL; | |
703 if (!dataset.findAndGetSequence(DCM_ReferencedFrameOfReferenceSequence, frame).good() || | |
704 frame == NULL) | |
705 { | |
706 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
707 } | |
708 | |
709 if (frame->card() == 1) | |
710 { | |
711 const char* v = NULL; | |
712 if (frame->getItem(0)->findAndGetString(DCM_FrameOfReferenceUID, v).good() && | |
713 v != NULL) | |
714 { | |
715 hasFrameOfReferenceUid_ = true; | |
716 frameOfReferenceUid_.assign(v); | |
717 } | |
718 } | |
697 | 719 |
698 DcmSequenceOfItems* rois = NULL; | 720 DcmSequenceOfItems* rois = NULL; |
699 if (!dataset.findAndGetSequence(DCM_ROIContourSequence, rois).good() || | 721 if (!dataset.findAndGetSequence(DCM_ROIContourSequence, rois).good() || |
700 rois == NULL) | 722 rois == NULL) |
701 { | 723 { |
886 } | 908 } |
887 } | 909 } |
888 } | 910 } |
889 | 911 |
890 return false; | 912 return false; |
913 } | |
914 | |
915 bool HasFrameOfReferenceUid() const | |
916 { | |
917 return hasFrameOfReferenceUid_; | |
918 } | |
919 | |
920 const std::string& GetFrameOfReferenceUid() const | |
921 { | |
922 if (hasFrameOfReferenceUid_) | |
923 { | |
924 return frameOfReferenceUid_; | |
925 } | |
926 else | |
927 { | |
928 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
929 } | |
891 } | 930 } |
892 }; | 931 }; |
893 | 932 |
894 | 933 |
895 | 934 |
1149 std::string s = answer.toStyledString(); | 1188 std::string s = answer.toStyledString(); |
1150 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::MIME_JSON); | 1189 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::MIME_JSON); |
1151 } | 1190 } |
1152 | 1191 |
1153 | 1192 |
1193 static void AddDefaultTagValue(Json::Value& target, | |
1194 const Orthanc::DicomTag& tag, | |
1195 const std::string& value) | |
1196 { | |
1197 if (!target.isMember(tag.Format())) | |
1198 { | |
1199 target[tag.Format()] = value; | |
1200 } | |
1201 } | |
1202 | |
1203 | |
1204 static void AddDefaultTagValue(Json::Value& target, | |
1205 const DcmTagKey& tag, | |
1206 const std::string& value) | |
1207 { | |
1208 AddDefaultTagValue(target, Orthanc::DicomTag(tag.getGroup(), tag.getElement()), value); | |
1209 } | |
1210 | |
1211 | |
1154 void Encode(OrthancPluginRestOutput* output, | 1212 void Encode(OrthancPluginRestOutput* output, |
1155 const char* url, | 1213 const char* url, |
1156 const OrthancPluginHttpRequest* request) | 1214 const OrthancPluginHttpRequest* request) |
1157 { | 1215 { |
1158 static const char* const KEY_INSTANCE = "Instance"; | 1216 static const char* const KEY_INSTANCE = "Instance"; |
1196 else | 1254 else |
1197 { | 1255 { |
1198 std::string content; | 1256 std::string content; |
1199 Orthanc::Toolbox::EncodeDataUriScheme(content, "model/stl", stl); | 1257 Orthanc::Toolbox::EncodeDataUriScheme(content, "model/stl", stl); |
1200 | 1258 |
1201 Json::Value create; | 1259 Json::Value normalized = Json::objectValue; |
1202 create["Content"] = content; | |
1203 create["Parent"] = structureSet.HashStudy(); | |
1204 | 1260 |
1205 if (body.isMember(KEY_TAGS)) | 1261 if (body.isMember(KEY_TAGS)) |
1206 { | 1262 { |
1207 create[KEY_TAGS] = body[KEY_TAGS]; | 1263 const Json::Value& tags = body[KEY_TAGS]; |
1208 } | 1264 if (tags.type() != Json::objectValue) |
1209 else | 1265 { |
1266 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "Tags must be provided as a JSON object"); | |
1267 } | |
1268 | |
1269 std::vector<std::string> keys = tags.getMemberNames(); | |
1270 for (size_t i = 0; i < keys.size(); i++) | |
1271 { | |
1272 const Orthanc::DicomTag tag = Orthanc::FromDcmtkBridge::ParseTag(keys[i]); | |
1273 normalized[tag.Format()] = tags[keys[i]]; | |
1274 } | |
1275 } | |
1276 | |
1277 if (!normalized.isMember(Orthanc::DICOM_TAG_SERIES_DESCRIPTION.Format())) | |
1210 { | 1278 { |
1211 std::string description; | 1279 std::string description; |
1212 | 1280 |
1213 if (dicom->GetTagValue(description, Orthanc::DICOM_TAG_SERIES_DESCRIPTION)) | 1281 if (dicom->GetTagValue(description, Orthanc::DICOM_TAG_SERIES_DESCRIPTION)) |
1214 { | 1282 { |
1232 } | 1300 } |
1233 | 1301 |
1234 description += *it; | 1302 description += *it; |
1235 } | 1303 } |
1236 | 1304 |
1237 create[KEY_TAGS] = Json::objectValue; | 1305 normalized[Orthanc::DICOM_TAG_SERIES_DESCRIPTION.Format()] = description; |
1238 create[KEY_TAGS]["SeriesDescription"] = description; | 1306 } |
1239 } | 1307 |
1308 AddDefaultTagValue(normalized, Orthanc::DICOM_TAG_SERIES_NUMBER, "1"); | |
1309 | |
1310 std::string s; | |
1311 if (structureSet.HasFrameOfReferenceUid()) | |
1312 { | |
1313 s = structureSet.GetFrameOfReferenceUid(); | |
1314 } | |
1315 else | |
1316 { | |
1317 s = Orthanc::FromDcmtkBridge::GenerateUniqueIdentifier(Orthanc::ResourceType_Instance); | |
1318 } | |
1319 | |
1320 AddDefaultTagValue(normalized, Orthanc::DICOM_TAG_FRAME_OF_REFERENCE_UID, s); | |
1321 AddDefaultTagValue(normalized, Orthanc::DICOM_TAG_INSTANCE_NUMBER, "1"); | |
1322 | |
1323 const std::string title = "STL model generated from DICOM RT-STRUCT"; | |
1324 | |
1325 AddDefaultTagValue(normalized, DCM_BurnedInAnnotation, "NO"); | |
1326 AddDefaultTagValue(normalized, DCM_DeviceSerialNumber, ORTHANC_STL_VERSION); | |
1327 AddDefaultTagValue(normalized, DCM_DocumentTitle, title); | |
1328 AddDefaultTagValue(normalized, DCM_Manufacturer, "Orthanc STL plugin"); | |
1329 AddDefaultTagValue(normalized, DCM_ManufacturerModelName, "Orthanc STL plugin"); | |
1330 AddDefaultTagValue(normalized, DCM_PositionReferenceIndicator, ""); | |
1331 AddDefaultTagValue(normalized, DCM_SoftwareVersions, ORTHANC_STL_VERSION); | |
1332 AddDefaultTagValue(normalized, DCM_ConceptNameCodeSequence, ""); | |
1333 | |
1334 std::string date, time; | |
1335 Orthanc::SystemToolbox::GetNowDicom(date, time, true /* use UTC time (not local time) */); | |
1336 AddDefaultTagValue(normalized, DCM_AcquisitionDateTime, date + time); | |
1337 | |
1338 const Orthanc::DicomTag MEASUREMENT_UNITS_CODE_SEQUENCE(DCM_MeasurementUnitsCodeSequence.getGroup(), | |
1339 DCM_MeasurementUnitsCodeSequence.getElement()); | |
1340 | |
1341 if (!normalized.isMember(MEASUREMENT_UNITS_CODE_SEQUENCE.Format())) | |
1342 { | |
1343 Json::Value item; | |
1344 item["CodeValue"] = "mm"; | |
1345 item["CodingSchemeDesignator"] = "UCUM"; | |
1346 item["CodeMeaning"] = title; | |
1347 | |
1348 normalized[MEASUREMENT_UNITS_CODE_SEQUENCE.Format()].append(item); | |
1349 } | |
1350 | |
1351 Json::Value create; | |
1352 create["Content"] = content; | |
1353 create["Parent"] = structureSet.HashStudy(); | |
1354 create["Tags"] = normalized; | |
1240 | 1355 |
1241 Json::Value answer; | 1356 Json::Value answer; |
1242 if (OrthancPlugins::RestApiPost(answer, "/tools/create-dicom", create.toStyledString(), false)) | 1357 if (OrthancPlugins::RestApiPost(answer, "/tools/create-dicom", create.toStyledString(), false)) |
1243 { | 1358 { |
1244 std::string s = answer.toStyledString(); | 1359 std::string s = answer.toStyledString(); |