comparison UnitTestsSources/TestStructureSet.cpp @ 1445:04d0c25819c3 loader-injection-feature

Added integration test for optimized CT / RTSTRUCT loading
author Benjamin Golinvaux <bgo@osimis.io>
date Tue, 26 May 2020 17:12:34 +0200
parents 4e7751a4b603
children ab00f36718ed
comparison
equal deleted inserted replaced
1444:687457d4018f 1445:04d0c25819c3
32 #include "Framework/Toolbox/DicomStructureSet2.h" 32 #include "Framework/Toolbox/DicomStructureSet2.h"
33 #include "Framework/Toolbox/DisjointDataSet.h" 33 #include "Framework/Toolbox/DisjointDataSet.h"
34 34
35 #include "Framework/Loaders/GenericLoadersContext.h" 35 #include "Framework/Loaders/GenericLoadersContext.h"
36 #include "Framework/Loaders/DicomStructureSetLoader.h" 36 #include "Framework/Loaders/DicomStructureSetLoader.h"
37 #include "Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
37 38
38 #include "boost/date_time/posix_time/posix_time.hpp" 39 #include "boost/date_time/posix_time/posix_time.hpp"
39 40
40 #include <Core/SystemToolbox.h> 41 #include <Core/SystemToolbox.h>
41 42
5451 }; 5452 };
5452 #endif 5453 #endif
5453 5454
5454 } 5455 }
5455 5456
5456
5457 TEST(StructureSet, DISABLED_StructureSetLoader_injection_feature_2020_05_10) 5457 TEST(StructureSet, DISABLED_StructureSetLoader_injection_feature_2020_05_10)
5458 { 5458 {
5459 namespace pt = boost::posix_time; 5459 namespace pt = boost::posix_time;
5460 5460
5461 std::unique_ptr<OrthancStone::ILoadersContext> loadersContext(new OrthancStone::GenericLoadersContext(1,4,1)); 5461 std::unique_ptr<OrthancStone::ILoadersContext> loadersContext(new OrthancStone::GenericLoadersContext(1,4,1));
5488 } 5488 }
5489 } 5489 }
5490 } 5490 }
5491 } 5491 }
5492 5492
5493 5493 class SliceProcessor :
5494 public OrthancStone::OrthancSeriesVolumeProgressiveLoader::ISlicePostProcessor,
5495 public OrthancStone::DicomStructureSetLoader::IInstanceLookupHandler
5496 {
5497 public:
5498 SliceProcessor(OrthancStone::DicomStructureSetLoader& structLoader) : structLoader_(structLoader)
5499 {
5500 }
5501
5502 virtual void ProcessCTDicomSlice(const Orthanc::DicomMap& instance) ORTHANC_OVERRIDE
5503 {
5504 std::string sopInstanceUid;
5505 if (!instance.LookupStringValue(sopInstanceUid, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false))
5506 {
5507 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Missing SOPInstanceUID in a DICOM instance");
5508 }
5509 slicesDicom_[sopInstanceUid] = boost::shared_ptr<DicomMap>(instance.Clone());
5510 }
5511
5512 virtual void RetrieveReferencedSlices(const std::set<std::string>& nonEmptyInstances) ORTHANC_OVERRIDE
5513 {
5514 for (std::set<std::string>::const_iterator it = nonEmptyInstances.begin();
5515 it != nonEmptyInstances.end();
5516 ++it)
5517 {
5518 const std::string nonEmptyInstance = *it;
5519 if (slicesDicom_.find(nonEmptyInstance) == slicesDicom_.end())
5520 {
5521 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Referenced SOPInstanceUID not found in CT");
5522 }
5523 boost::shared_ptr<Orthanc::DicomMap> instance = slicesDicom_[nonEmptyInstance];
5524 structLoader_.AddReferencedSlice(*instance);
5525 }
5526 }
5527
5528 OrthancStone::DicomStructureSetLoader& structLoader_;
5529 std::map<std::string, boost::shared_ptr<Orthanc::DicomMap> > slicesDicom_;
5530 };
5531
5532 void LoadCtSeriesBlocking(boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader, std::string seriesId)
5533 {
5534 namespace pt = boost::posix_time;
5535
5536 // Load the CT
5537 ctLoader->LoadSeries(seriesId);
5538
5539 // Wait for CT to be loaded
5540 pt::ptime initialTime = pt::second_clock::local_time();
5541 {
5542 bool bContinue(true);
5543 while (bContinue)
5544 {
5545 bContinue = !ctLoader->IsVolumeImageReadyInHighQuality();
5546 boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
5547
5548 {
5549 pt::ptime nowTime = pt::second_clock::local_time();
5550 pt::time_duration diff = nowTime - initialTime;
5551 double seconds = static_cast<double>(diff.total_milliseconds()) * 0.001;
5552 std::cout << seconds << " seconds elapsed...\n";
5553 if (seconds > 30)
5554 {
5555 const char* msg = "More than 30 seconds elapsed when waiting for CT... Aborting test :(\n";
5556 GTEST_FATAL_FAILURE_(msg);
5557 bContinue = false;
5558 }
5559 }
5560 }
5561 }
5562 }
5563
5564
5565 void LoadRtStructBlocking(boost::shared_ptr<OrthancStone::DicomStructureSetLoader> structLoader, std::string instanceId)
5566 {
5567 namespace pt = boost::posix_time;
5568
5569 // Load RTSTRUCT
5570 structLoader->LoadInstanceFullVisibility(instanceId);
5571
5572 pt::ptime initialTime = pt::second_clock::local_time();
5573
5574 // Wait for the loading process to complete
5575 {
5576 bool bContinue(true);
5577 while (bContinue)
5578 {
5579 bContinue = !structLoader->AreStructuresReady();
5580 boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
5581
5582 {
5583 pt::ptime nowTime = pt::second_clock::local_time();
5584 pt::time_duration diff = nowTime - initialTime;
5585 double seconds = static_cast<double>(diff.total_milliseconds()) * 0.001;
5586 std::cout << seconds << " seconds elapsed...\n";
5587 if (seconds > 30)
5588 {
5589 const char* msg = "More than 30 seconds elapsed when waiting for RTSTRUCT... Aborting test :(\n";
5590 GTEST_FATAL_FAILURE_(msg);
5591 bContinue = false;
5592 }
5593 }
5594 }
5595 }
5596 }
5597
5598 TEST(StructureSet, DISABLED_Integration_Compound_CT_Struct_Loading)
5599 {
5600 const double TOLERANCE = 0.0000001;
5601
5602 // create loaders context
5603 std::unique_ptr<OrthancStone::ILoadersContext> loadersContext(new OrthancStone::GenericLoadersContext(1,4,1));
5604 Initialize("http://localhost:8042/", *loadersContext);
5605
5606 const char* ctSeriesId = "a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa";
5607 const char* rtStructInstanceId = "54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9";
5608
5609 // we'll compare normal loading and optimized loading with SliceProcessor to store the dicom
5610
5611 boost::shared_ptr<OrthancStone::DicomStructureSetLoader> normalStructLoader;
5612 boost::shared_ptr<OrthancStone::DicomStructureSetLoader> optimizedStructLoader;
5613
5614 {
5615 // Create the CT volume
5616 boost::shared_ptr<OrthancStone::DicomVolumeImage> volume = boost::make_shared<OrthancStone::DicomVolumeImage>();
5617
5618 // Create CT loader
5619 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader =
5620 OrthancStone::OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext, volume);
5621
5622 // Create struct loader
5623 normalStructLoader = OrthancStone::DicomStructureSetLoader::Create(*loadersContext);
5624
5625 // Load the CT
5626 LoadCtSeriesBlocking(ctLoader, ctSeriesId);
5627
5628 const OrthancStone::VolumeImageGeometry& imageGeometry = ctLoader->GetImageGeometry();
5629 unsigned int width = imageGeometry.GetWidth();
5630 EXPECT_EQ(512u, width);
5631 unsigned int height = imageGeometry.GetHeight();
5632 EXPECT_EQ(512u, height);
5633 unsigned int depth = imageGeometry.GetDepth();
5634 EXPECT_EQ(109u, depth);
5635
5636 // Load the RTStruct
5637 LoadRtStructBlocking(normalStructLoader, rtStructInstanceId);
5638 }
5639
5640 {
5641 // Create the CT volume
5642 boost::shared_ptr<OrthancStone::DicomVolumeImage> volume = boost::make_shared<OrthancStone::DicomVolumeImage>();
5643
5644 // Create CT loader
5645 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader =
5646 OrthancStone::OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext, volume);
5647
5648 // Create struct loader
5649 optimizedStructLoader = OrthancStone::DicomStructureSetLoader::Create(*loadersContext);
5650
5651 // create the slice processor / instance lookup
5652 boost::shared_ptr<SliceProcessor> sliceProcessor = boost::make_shared<SliceProcessor>(*optimizedStructLoader);
5653
5654 // Inject it into CT loader
5655 ctLoader->SetDicomSlicePostProcessor(sliceProcessor);
5656
5657 // Inject it into RTSTRUCT loader
5658 optimizedStructLoader->SetInstanceLookupHandler(sliceProcessor);
5659
5660 // Load the CT
5661 LoadCtSeriesBlocking(ctLoader, ctSeriesId);
5662
5663 // now, the slices are collected. let's do some checks
5664 EXPECT_EQ(109u, sliceProcessor->slicesDicom_.size());
5665
5666 // Load the RTStruct
5667 LoadRtStructBlocking(optimizedStructLoader, rtStructInstanceId);
5668 }
5669
5670 // DO NOT DELETE THOSE!
5671 OrthancStone::DicomStructureSet* normalContent = normalStructLoader->GetContent();
5672 OrthancStone::DicomStructureSet* optimizedContent = optimizedStructLoader->GetContent();
5673
5674 EXPECT_EQ(normalContent->GetStructuresCount(), optimizedContent->GetStructuresCount());
5675
5676 for (size_t i = 0; i < normalContent->GetStructuresCount(); ++i)
5677 {
5678 Vector structureCenter1 = normalContent->GetStructureCenter(i);
5679 const std::string& structureName1 = normalContent->GetStructureName(i);
5680 const std::string& structureInterpretation1 = normalContent->GetStructureInterpretation(i);
5681 Color structureColor1 = normalContent->GetStructureColor(i);
5682
5683 Vector structureCenter2 = optimizedContent->GetStructureCenter(i);
5684 const std::string& structureName2 = optimizedContent->GetStructureName(i);
5685 const std::string& structureInterpretation2 = optimizedContent->GetStructureInterpretation(i);
5686 Color structureColor2 = optimizedContent->GetStructureColor(i);
5687
5688 EXPECT_NEAR(structureCenter1[0], structureCenter2[0], TOLERANCE);
5689 EXPECT_NEAR(structureCenter1[1], structureCenter2[1], TOLERANCE);
5690 EXPECT_NEAR(structureCenter1[2], structureCenter2[2], TOLERANCE);
5691
5692 EXPECT_EQ(structureName1, structureName2);
5693 EXPECT_EQ(structureInterpretation1, structureInterpretation2);
5694 EXPECT_EQ(structureColor1.GetRed(), structureColor2.GetRed());
5695 EXPECT_EQ(structureColor1.GetGreen(), structureColor2.GetGreen());
5696 EXPECT_EQ(structureColor1.GetBlue(), structureColor2.GetBlue());
5697 }
5698 }