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();