comparison Sources/Plugin.cpp @ 8:d1267c6c33e1

added route /stl/encode-nifti
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 18 Jul 2023 15:58:21 +0200
parents e3e59de705f6
children dd0cd39e6259
comparison
equal deleted inserted replaced
7:e3e59de705f6 8:d1267c6c33e1
1013 1013
1014 buffer.Flatten(target); 1014 buffer.Flatten(target);
1015 } 1015 }
1016 1016
1017 1017
1018 bool EncodeStructureSetMesh(std::string& stl, 1018 bool EncodeVolume(std::string& stl,
1019 vtkImageData* volume, 1019 vtkImageData* volume,
1020 unsigned int resolution, 1020 unsigned int resolution,
1021 bool smooth) 1021 bool smooth)
1022 { 1022 {
1023 if (volume == NULL) 1023 if (volume == NULL)
1024 { 1024 {
1025 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 1025 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
1026 } 1026 }
1163 (structureSet.GetMaxProjectionAlongNormal() - structureSet.GetMinProjectionAlongNormal()) / static_cast<double>(depth)); 1163 (structureSet.GetMaxProjectionAlongNormal() - structureSet.GetMinProjectionAlongNormal()) / static_cast<double>(depth));
1164 1164
1165 // TODO 1165 // TODO
1166 // volume->SetOrigin() 1166 // volume->SetOrigin()
1167 1167
1168 return EncodeStructureSetMesh(stl, volume.Get(), resolution, smooth); 1168 return EncodeVolume(stl, volume.Get(), resolution, smooth);
1169 } 1169 }
1170 1170
1171 1171
1172 static Orthanc::ParsedDicomFile* LoadInstance(const std::string& instanceId) 1172 static Orthanc::ParsedDicomFile* LoadInstance(const std::string& instanceId)
1173 { 1173 {
1348 StructureSet structureSet(*dicom); 1348 StructureSet structureSet(*dicom);
1349 1349
1350 std::string stl; 1350 std::string stl;
1351 if (!EncodeStructureSetMesh(stl, structureSet, roiNames, resolution, smooth)) 1351 if (!EncodeStructureSetMesh(stl, structureSet, roiNames, resolution, smooth))
1352 { 1352 {
1353 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot encode STL"); 1353 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot encode STL from RT-STRUCT");
1354 } 1354 }
1355 else 1355 else
1356 { 1356 {
1357 std::string seriesDescription; 1357 std::string seriesDescription;
1358 1358
1497 { 1497 {
1498 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, 1498 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
1499 "Only 3D NIfTI volumes are allowed"); 1499 "Only 3D NIfTI volumes are allowed");
1500 } 1500 }
1501 1501
1502 if (header.GetInfo().datatype != DT_UNSIGNED_CHAR) 1502 size_t itemSize;
1503 { 1503 int vtkType;
1504 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); 1504
1505 switch (header.GetInfo().datatype)
1506 {
1507 case DT_UNSIGNED_CHAR:
1508 itemSize = 1;
1509 vtkType = VTK_UNSIGNED_CHAR;
1510 break;
1511
1512 case DT_FLOAT:
1513 itemSize = sizeof(float);
1514 vtkType = VTK_FLOAT;
1515 break;
1516
1517 case DT_DOUBLE:
1518 itemSize = sizeof(double);
1519 vtkType = VTK_DOUBLE;
1520 break;
1521
1522 default:
1523 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
1505 } 1524 }
1506 1525
1507 assert(static_cast<int>(header.GetInfo().nvox) == header.GetInfo().nx * header.GetInfo().ny * header.GetInfo().nz); 1526 assert(static_cast<int>(header.GetInfo().nvox) == header.GetInfo().nx * header.GetInfo().ny * header.GetInfo().nz);
1508 1527
1509 const size_t pixelDataOffset = sizeof(nifti_1_header) + 4 /* extension */; 1528 const size_t pixelDataOffset = sizeof(nifti_1_header) + 4 /* extension */;
1510 1529
1511 if (nifti.size() != pixelDataOffset + header.GetInfo().nvox) 1530 if (nifti.size() != pixelDataOffset + header.GetInfo().nvox * itemSize)
1512 { 1531 {
1513 throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile); 1532 throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile);
1514 } 1533 }
1515 1534
1516 volume->SetDimensions(header.GetInfo().nx, header.GetInfo().ny, header.GetInfo().nz); 1535 volume->SetDimensions(header.GetInfo().nx, header.GetInfo().ny, header.GetInfo().nz);
1517 volume->AllocateScalars(VTK_UNSIGNED_CHAR, 1); 1536 volume->AllocateScalars(vtkType, 1);
1518 volume->SetSpacing(header.GetInfo().dx, header.GetInfo().dy, header.GetInfo().dz); 1537 volume->SetSpacing(header.GetInfo().dx, header.GetInfo().dy, header.GetInfo().dz);
1519 memcpy(volume->GetScalarPointer(), &nifti[pixelDataOffset], header.GetInfo().nvox * sizeof(unsigned char)); 1538 memcpy(volume->GetScalarPointer(), &nifti[pixelDataOffset], header.GetInfo().nvox * itemSize);
1520 } 1539 }
1540
1541
1542 void EncodeNifti(OrthancPluginRestOutput* output,
1543 const char* url,
1544 const OrthancPluginHttpRequest* request)
1545 {
1546 static const char* const KEY_NIFTI = "Nifti";
1547 static const char* const KEY_RESOLUTION = "Resolution";
1548 static const char* const KEY_PARENT_STUDY = "ParentStudy";
1549 static const char* const KEY_SMOOTH = "Smooth";
1550
1551 if (request->method != OrthancPluginHttpMethod_Post)
1552 {
1553 OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "POST");
1554 return;
1555 }
1556
1557 Json::Value body;
1558 if (!Orthanc::Toolbox::ReadJson(body, request->body, request->bodySize))
1559 {
1560 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest);
1561 }
1562
1563 std::string mime, nifti;
1564 if (!Orthanc::Toolbox::DecodeDataUriScheme(mime, nifti, Orthanc::SerializationToolbox::ReadString(body, KEY_NIFTI)))
1565 {
1566 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "Missing the \"Nifti\" argument containing the NIfTI file");
1567 }
1568
1569 const std::string parentStudy = Orthanc::SerializationToolbox::ReadString(body, KEY_PARENT_STUDY);
1570 const bool smooth = (body.isMember(KEY_SMOOTH) ?
1571 Orthanc::SerializationToolbox::ReadBoolean(body, KEY_SMOOTH) :
1572 true /* smooth by default */);
1573 const unsigned int resolution = (body.isMember(KEY_RESOLUTION) ?
1574 Orthanc::SerializationToolbox::ReadUnsignedInteger(body, KEY_RESOLUTION) :
1575 256 /* default value */);
1576
1577 vtkNew<vtkImageData> volume;
1578 LoadNifti(volume.Get(), nifti);
1579
1580 std::string stl;
1581 if (!EncodeVolume(stl, volume.Get(), resolution, smooth))
1582 {
1583 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot encode STL from NIfTI");
1584 }
1585 else
1586 {
1587 const std::string title = "STL model generated from NIfTI";
1588
1589 const std::string frameOfReferenceUid = Orthanc::FromDcmtkBridge::GenerateUniqueIdentifier(Orthanc::ResourceType_Instance);
1590
1591 Json::Value answer;
1592 CallCreateDicom(answer, stl, body, parentStudy, title, frameOfReferenceUid, title);
1593
1594 std::string s = answer.toStyledString();
1595 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::MIME_JSON);
1596 }
1597 }
1598
1521 1599
1522 extern "C" 1600 extern "C"
1523 { 1601 {
1524 ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context) 1602 ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)
1525 { 1603 {
1560 OrthancPlugins::RegisterRestCallback<ExtractStl>("/instances/([0-9a-f-]+)/stl", true); 1638 OrthancPlugins::RegisterRestCallback<ExtractStl>("/instances/([0-9a-f-]+)/stl", true);
1561 OrthancPlugins::RegisterRestCallback<ListStructures>("/stl/rt-struct/([0-9a-f-]+)", true); 1639 OrthancPlugins::RegisterRestCallback<ListStructures>("/stl/rt-struct/([0-9a-f-]+)", true);
1562 1640
1563 if (hasCreateDicomStl_) 1641 if (hasCreateDicomStl_)
1564 { 1642 {
1565 OrthancPlugins::RegisterRestCallback<EncodeStructureSet>("/stl/encode", true); 1643 OrthancPlugins::RegisterRestCallback<EncodeStructureSet>("/stl/encode-rtstruct", true);
1644 OrthancPlugins::RegisterRestCallback<EncodeNifti>("/stl/encode-nifti", true);
1566 } 1645 }
1567 1646
1568 // Extend the default Orthanc Explorer with custom JavaScript for STL 1647 // Extend the default Orthanc Explorer with custom JavaScript for STL
1569 std::string explorer; 1648 std::string explorer;
1570 1649