comparison Sources/Plugin.cpp @ 6:c02d12eb34d4

added LoadNifti()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 18 Jul 2023 14:45:20 +0200
parents 5ee4448a8ff8
children e3e59de705f6
comparison
equal deleted inserted replaced
5:1cc024bb662a 6:c02d12eb34d4
24 24
25 #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" 25 #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
26 26
27 #include <EmbeddedResources.h> 27 #include <EmbeddedResources.h>
28 28
29 #include <Compression/GzipCompressor.h>
29 #include <ChunkedBuffer.h> 30 #include <ChunkedBuffer.h>
30 #include <DicomParsing/FromDcmtkBridge.h> 31 #include <DicomParsing/FromDcmtkBridge.h>
31 #include <DicomParsing/ParsedDicomFile.h> 32 #include <DicomParsing/ParsedDicomFile.h>
32 #include <Images/ImageProcessing.h> 33 #include <Images/ImageProcessing.h>
33 #include <Logging.h> 34 #include <Logging.h>
44 #include <vtkPolyDataNormals.h> 45 #include <vtkPolyDataNormals.h>
45 #include <vtkSmoothPolyDataFilter.h> 46 #include <vtkSmoothPolyDataFilter.h>
46 #include <vtkTriangle.h> 47 #include <vtkTriangle.h>
47 48
48 #include <boost/thread/shared_mutex.hpp> 49 #include <boost/thread/shared_mutex.hpp>
50
51 #include <nifti1_io.h>
52
49 53
50 // Forward declaration 54 // Forward declaration
51 void ReadStaticAsset(std::string& target, 55 void ReadStaticAsset(std::string& target,
52 const std::string& path); 56 const std::string& path);
53 57
1109 padding->SetOutputNumberOfScalarComponents(1); 1113 padding->SetOutputNumberOfScalarComponents(1);
1110 padding->SetOutputWholeExtent(-1, resolution, -1, resolution, -1, resolution); 1114 padding->SetOutputWholeExtent(-1, resolution, -1, resolution, -1, resolution);
1111 padding->SetInputData(resize->GetOutput()); 1115 padding->SetInputData(resize->GetOutput());
1112 padding->Update(); 1116 padding->Update();
1113 1117
1118 double range[2];
1119 padding->GetOutput()->GetScalarRange(range);
1120
1121 const double isoValue = (range[0] + range[1]) / 2.0;
1122
1114 vtkNew<vtkMarchingCubes> surface; 1123 vtkNew<vtkMarchingCubes> surface;
1115 surface->SetInputData(padding->GetOutput()); 1124 surface->SetInputData(padding->GetOutput());
1116 surface->ComputeNormalsOn(); 1125 surface->ComputeNormalsOn();
1117 surface->SetValue(0, 128 /*isoValue*/); 1126 surface->SetValue(0, isoValue);
1118 surface->Update(); 1127 surface->Update();
1119 1128
1120 if (smooth) 1129 if (smooth)
1121 { 1130 {
1122 vtkNew<vtkSmoothPolyDataFilter> smoothFilter; 1131 vtkNew<vtkSmoothPolyDataFilter> smoothFilter;
1395 stl.empty() ? NULL : stl.c_str(), stl.size(), Orthanc::MIME_STL); 1404 stl.empty() ? NULL : stl.c_str(), stl.size(), Orthanc::MIME_STL);
1396 } 1405 }
1397 } 1406 }
1398 1407
1399 1408
1409
1410 class NiftiHeader : public boost::noncopyable
1411 {
1412 private:
1413 nifti_image* image_;
1414
1415 public:
1416 NiftiHeader(const std::string& nifti)
1417 {
1418 nifti_1_header header;
1419 if (nifti.size() < sizeof(header))
1420 {
1421 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
1422 }
1423
1424 memcpy(&header, nifti.c_str(), sizeof(header));
1425 if (!nifti_hdr_looks_good(&header))
1426 {
1427 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
1428 }
1429
1430 image_ = nifti_convert_nhdr2nim(header, "dummy_filename");
1431 if (image_ == NULL)
1432 {
1433 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
1434 }
1435 }
1436
1437 ~NiftiHeader()
1438 {
1439 nifti_image_free(image_);
1440 }
1441
1442 const nifti_image& GetInfo() const
1443 {
1444 assert(image_ != NULL);
1445 return *image_;
1446 }
1447 };
1448
1449
1450 static void LoadNifti(vtkImageData* volume,
1451 std::string& nifti)
1452 {
1453 if (volume == NULL)
1454 {
1455 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
1456 }
1457
1458 const uint8_t* p = reinterpret_cast<const uint8_t*>(nifti.c_str());
1459
1460 if (nifti.size() >= 2 &&
1461 p[0] == 0x1f &&
1462 p[1] == 0x8b)
1463 {
1464 Orthanc::GzipCompressor compressor;
1465 std::string uncompressed;
1466 Orthanc::IBufferCompressor::Uncompress(uncompressed, compressor, nifti);
1467 nifti.swap(uncompressed);
1468 }
1469
1470 NiftiHeader header(nifti);
1471
1472 if (header.GetInfo().ndim != 3)
1473 {
1474 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
1475 "Only 3D NIfTI volumes are allowed");
1476 }
1477
1478 if (header.GetInfo().datatype != DT_UNSIGNED_CHAR)
1479 {
1480 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
1481 }
1482
1483 assert(static_cast<int>(header.GetInfo().nvox) == header.GetInfo().nx * header.GetInfo().ny * header.GetInfo().nz);
1484
1485 const size_t pixelDataOffset = sizeof(nifti_1_header) + 4 /* extension */;
1486
1487 if (nifti.size() != pixelDataOffset + header.GetInfo().nvox)
1488 {
1489 throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile);
1490 }
1491
1492 volume->SetDimensions(header.GetInfo().nx, header.GetInfo().ny, header.GetInfo().nz);
1493 volume->AllocateScalars(VTK_UNSIGNED_CHAR, 1);
1494 volume->SetSpacing(header.GetInfo().dx, header.GetInfo().dy, header.GetInfo().dz);
1495 memcpy(volume->GetScalarPointer(), &nifti[pixelDataOffset], header.GetInfo().nvox * sizeof(unsigned char));
1496 }
1497
1400 extern "C" 1498 extern "C"
1401 { 1499 {
1402 ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context) 1500 ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)
1403 { 1501 {
1404 OrthancPlugins::SetGlobalContext(context); 1502 OrthancPlugins::SetGlobalContext(context);