Mercurial > hg > orthanc-stone
comparison OrthancStone/UnitTestsSources/TestStructureSet.cpp @ 1541:ae17c8c8838f
standalone compilation of unit tests
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 11 Aug 2020 13:47:24 +0200 |
parents | 244ad1e4e76a |
children |
comparison
equal
deleted
inserted
replaced
1540:e20a2381200d | 1541:ae17c8c8838f |
---|---|
4266 #ifdef _MSC_VER | 4266 #ifdef _MSC_VER |
4267 #pragma endregion | 4267 #pragma endregion |
4268 #endif | 4268 #endif |
4269 // _MSC_VER | 4269 // _MSC_VER |
4270 | 4270 |
4271 /* | |
4272 these tests are single-threaded... no worries for old buggy compilers | |
4273 (I'm talking to YOU, cl.exe v100! And to your ancestors!) | |
4274 */ | |
4275 static std::string& GetTestJson() | |
4276 { | |
4277 static const char* resultRaw = NULL; | |
4278 static std::string result; | |
4279 if (resultRaw == NULL) | |
4280 { | |
4281 std::stringstream sst; | |
4282 | |
4283 sst << k_rtStruct_json00 | |
4284 << k_rtStruct_json01 | |
4285 << k_rtStruct_json02 | |
4286 << k_rtStruct_json03 | |
4287 << k_rtStruct_json04 | |
4288 << k_rtStruct_json05 | |
4289 << k_rtStruct_json06 | |
4290 << k_rtStruct_json07 | |
4291 << k_rtStruct_json08; | |
4292 | |
4293 std::string wholeBody = sst.str(); | |
4294 result.swap(wholeBody); | |
4295 resultRaw = result.c_str(); | |
4296 } | |
4297 return result; | |
4298 } | |
4299 | 4271 |
4300 #define STONE_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) | 4272 #define STONE_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) |
4301 | 4273 |
4302 #ifdef BGO_ENABLE_DICOMSTRUCTURESETLOADER2 | 4274 #ifdef BGO_ENABLE_DICOMSTRUCTURESETLOADER2 |
4303 | 4275 |
5412 const std::vector<DicomStructure2>& structures = structureSet.structures_; | 5384 const std::vector<DicomStructure2>& structures = structureSet.structures_; |
5413 } | 5385 } |
5414 | 5386 |
5415 #endif | 5387 #endif |
5416 // BGO_ENABLE_DICOMSTRUCTURESETLOADER2 | 5388 // BGO_ENABLE_DICOMSTRUCTURESETLOADER2 |
5417 | |
5418 namespace | |
5419 { | |
5420 void Initialize(const char* orthancApiUrl, OrthancStone::ILoadersContext& loadersContext) | |
5421 { | |
5422 Orthanc::WebServiceParameters p; | |
5423 | |
5424 OrthancStone::GenericLoadersContext& typedLoadersContext = | |
5425 dynamic_cast<OrthancStone::GenericLoadersContext&>(loadersContext); | |
5426 // Default is http://localhost:8042 | |
5427 // Here's how you may change it | |
5428 p.SetUrl(orthancApiUrl); | |
5429 p.SetCredentials("orthanc", "orthanc"); | |
5430 typedLoadersContext.SetOrthancParameters(p); | |
5431 | |
5432 typedLoadersContext.StartOracle(); | |
5433 } | |
5434 | |
5435 void Exitialize(OrthancStone::ILoadersContext& loadersContext) | |
5436 { | |
5437 OrthancStone::GenericLoadersContext& typedLoadersContext = | |
5438 dynamic_cast<OrthancStone::GenericLoadersContext&>(loadersContext); | |
5439 | |
5440 typedLoadersContext.StopOracle(); | |
5441 } | |
5442 | |
5443 | |
5444 #if 0 | |
5445 class TestObserver : public ObserverBase<TestObserver> | |
5446 { | |
5447 public: | |
5448 TestObserver() {}; | |
5449 | |
5450 virtual void Handle | |
5451 | |
5452 }; | |
5453 #endif | |
5454 | |
5455 } | |
5456 | |
5457 TEST(StructureSet, DISABLED_StructureSetLoader_injection_feature_2020_05_10) | |
5458 { | |
5459 namespace pt = boost::posix_time; | |
5460 | |
5461 std::unique_ptr<OrthancStone::ILoadersContext> loadersContext(new OrthancStone::GenericLoadersContext(1,4,1)); | |
5462 Initialize("http://localhost:8042/", *loadersContext); | |
5463 | |
5464 boost::shared_ptr<DicomStructureSetLoader> loader = DicomStructureSetLoader::Create(*loadersContext); | |
5465 | |
5466 // replace with Orthanc ID of an uploaded RTSTRUCT instance! | |
5467 loader->LoadInstanceFullVisibility("72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661"); | |
5468 | |
5469 bool bContinue(true); | |
5470 | |
5471 pt::ptime initialTime = pt::second_clock::local_time(); | |
5472 | |
5473 while (bContinue) | |
5474 { | |
5475 bContinue = !loader->AreStructuresReady(); | |
5476 boost::this_thread::sleep_for(boost::chrono::milliseconds(1000)); | |
5477 | |
5478 { | |
5479 pt::ptime nowTime = pt::second_clock::local_time(); | |
5480 pt::time_duration diff = nowTime - initialTime; | |
5481 double seconds = static_cast<double>(diff.total_milliseconds()) * 0.001; | |
5482 std::cout << seconds << " seconds elapsed...\n"; | |
5483 if (seconds > 30) | |
5484 { | |
5485 std::cout << "More than 30 seconds elapsed... Aborting test :(\n"; | |
5486 //GTEST_FATAL_FAILURE_("More than 30 seconds elapsed... Aborting test :("); | |
5487 //bContinue = false; | |
5488 } | |
5489 } | |
5490 } | |
5491 } | |
5492 | |
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 /** | |
5566 Will fill planes | |
5567 */ | |
5568 void GetCTPlanes(std::vector<OrthancStone::CoordinateSystem3D>& planes, | |
5569 OrthancStone::VolumeProjection projection, | |
5570 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader) | |
5571 { | |
5572 planes.clear(); // inefficient : we don't care | |
5573 | |
5574 const VolumeImageGeometry& geometry = ctLoader->GetImageGeometry(); | |
5575 const unsigned int depth = geometry.GetProjectionDepth(projection); | |
5576 | |
5577 planes.resize(depth); | |
5578 | |
5579 for (unsigned int z = 0; z < depth; z++) | |
5580 { | |
5581 planes[z] = geometry.GetProjectionSlice(projection, z); | |
5582 } | |
5583 } | |
5584 | |
5585 void LoadRtStructBlocking(boost::shared_ptr<OrthancStone::DicomStructureSetLoader> structLoader, std::string instanceId) | |
5586 { | |
5587 namespace pt = boost::posix_time; | |
5588 | |
5589 // Load RTSTRUCT | |
5590 structLoader->LoadInstanceFullVisibility(instanceId); | |
5591 | |
5592 pt::ptime initialTime = pt::second_clock::local_time(); | |
5593 | |
5594 // Wait for the loading process to complete | |
5595 { | |
5596 bool bContinue(true); | |
5597 while (bContinue) | |
5598 { | |
5599 bContinue = !structLoader->AreStructuresReady(); | |
5600 boost::this_thread::sleep_for(boost::chrono::milliseconds(1000)); | |
5601 | |
5602 { | |
5603 pt::ptime nowTime = pt::second_clock::local_time(); | |
5604 pt::time_duration diff = nowTime - initialTime; | |
5605 double seconds = static_cast<double>(diff.total_milliseconds()) * 0.001; | |
5606 std::cout << seconds << " seconds elapsed...\n"; | |
5607 if (seconds > 30) | |
5608 { | |
5609 const char* msg = "More than 30 seconds elapsed when waiting for RTSTRUCT... Aborting test :(\n"; | |
5610 GTEST_FATAL_FAILURE_(msg); | |
5611 bContinue = false; | |
5612 } | |
5613 } | |
5614 } | |
5615 } | |
5616 } | |
5617 | |
5618 TEST(StructureSet, DISABLED_Integration_Compound_CT_Struct_Loading) | |
5619 { | |
5620 const double TOLERANCE = 0.0000001; | |
5621 | |
5622 // create loaders context | |
5623 std::unique_ptr<OrthancStone::ILoadersContext> loadersContext(new OrthancStone::GenericLoadersContext(1,4,1)); | |
5624 Initialize("http://localhost:8042/", *loadersContext); | |
5625 | |
5626 const char* ctSeriesId = "a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"; | |
5627 const char* rtStructInstanceId = "54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"; | |
5628 | |
5629 // we'll compare normal loading and optimized loading with SliceProcessor to store the dicom | |
5630 | |
5631 boost::shared_ptr<OrthancStone::DicomStructureSetLoader> normalStructLoader; | |
5632 boost::shared_ptr<OrthancStone::DicomStructureSetLoader> optimizedStructLoader; | |
5633 | |
5634 { | |
5635 // Create the CT volume | |
5636 boost::shared_ptr<OrthancStone::DicomVolumeImage> volume = boost::make_shared<OrthancStone::DicomVolumeImage>(); | |
5637 | |
5638 // Create CT loader | |
5639 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader = | |
5640 OrthancStone::OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext, volume); | |
5641 | |
5642 // Create struct loader | |
5643 normalStructLoader = OrthancStone::DicomStructureSetLoader::Create(*loadersContext); | |
5644 | |
5645 // Load the CT | |
5646 LoadCtSeriesBlocking(ctLoader, ctSeriesId); | |
5647 | |
5648 const OrthancStone::VolumeImageGeometry& imageGeometry = ctLoader->GetImageGeometry(); | |
5649 unsigned int width = imageGeometry.GetWidth(); | |
5650 EXPECT_EQ(512u, width); | |
5651 unsigned int height = imageGeometry.GetHeight(); | |
5652 EXPECT_EQ(512u, height); | |
5653 unsigned int depth = imageGeometry.GetDepth(); | |
5654 EXPECT_EQ(109u, depth); | |
5655 | |
5656 // Load the RTStruct | |
5657 LoadRtStructBlocking(normalStructLoader, rtStructInstanceId); | |
5658 } | |
5659 | |
5660 std::vector<OrthancStone::CoordinateSystem3D> axialPlanes; | |
5661 std::vector<OrthancStone::CoordinateSystem3D> coronalPlanes; | |
5662 std::vector<OrthancStone::CoordinateSystem3D> sagittalPlanes; | |
5663 | |
5664 { | |
5665 // Create the CT volume | |
5666 boost::shared_ptr<OrthancStone::DicomVolumeImage> volume = boost::make_shared<OrthancStone::DicomVolumeImage>(); | |
5667 | |
5668 // Create CT loader | |
5669 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader = | |
5670 OrthancStone::OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext, volume); | |
5671 | |
5672 // Create struct loader | |
5673 optimizedStructLoader = OrthancStone::DicomStructureSetLoader::Create(*loadersContext); | |
5674 | |
5675 // create the slice processor / instance lookup | |
5676 boost::shared_ptr<SliceProcessor> sliceProcessor(new SliceProcessor(*optimizedStructLoader)); | |
5677 | |
5678 // Inject it into CT loader | |
5679 ctLoader->SetDicomSlicePostProcessor(sliceProcessor); | |
5680 | |
5681 // Inject it into RTSTRUCT loader | |
5682 optimizedStructLoader->SetInstanceLookupHandler(sliceProcessor); | |
5683 | |
5684 // Load the CT | |
5685 LoadCtSeriesBlocking(ctLoader, ctSeriesId); | |
5686 | |
5687 // now, the slices are collected. let's do some checks | |
5688 EXPECT_EQ(109u, sliceProcessor->slicesDicom_.size()); | |
5689 | |
5690 // Load the RTStruct | |
5691 LoadRtStructBlocking(optimizedStructLoader, rtStructInstanceId); | |
5692 | |
5693 GetCTPlanes(axialPlanes, VolumeProjection_Axial, ctLoader); | |
5694 GetCTPlanes(coronalPlanes, VolumeProjection_Coronal, ctLoader); | |
5695 GetCTPlanes(sagittalPlanes, VolumeProjection_Sagittal, ctLoader); | |
5696 } | |
5697 | |
5698 // DO NOT DELETE THOSE! | |
5699 OrthancStone::DicomStructureSet* normalContent = normalStructLoader->GetContent(); | |
5700 OrthancStone::DicomStructureSet* optimizedContent = optimizedStructLoader->GetContent(); | |
5701 | |
5702 EXPECT_EQ(normalContent->GetStructuresCount(), optimizedContent->GetStructuresCount()); | |
5703 | |
5704 /*void GetCTPlanes(std::vector<OrthancStone::CoordinateSystem3D>& planes, | |
5705 OrthancStone::VolumeProjection projection, | |
5706 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader)*/ | |
5707 | |
5708 | |
5709 std::vector<OrthancStone::CoordinateSystem3D> allPlanes; | |
5710 | |
5711 // let's gather all the possible cutting planes in a single struct | |
5712 for (size_t i = 0; i < axialPlanes.size(); ++i) | |
5713 allPlanes.push_back(axialPlanes[i]); | |
5714 | |
5715 for (size_t i = 0; i < coronalPlanes.size(); ++i) | |
5716 allPlanes.push_back(coronalPlanes[i]); | |
5717 | |
5718 for (size_t i = 0; i < sagittalPlanes.size(); ++i) | |
5719 allPlanes.push_back(sagittalPlanes[i]); | |
5720 | |
5721 for (size_t i = 0; i < normalContent->GetStructuresCount(); ++i) | |
5722 { | |
5723 std::cout << "Testing structure (" << i << "/" << normalContent->GetStructuresCount() << ")\n"; | |
5724 Vector structureCenter1 = normalContent->GetStructureCenter(i); | |
5725 const std::string& structureName1 = normalContent->GetStructureName(i); | |
5726 const std::string& structureInterpretation1 = normalContent->GetStructureInterpretation(i); | |
5727 Color structureColor1 = normalContent->GetStructureColor(i); | |
5728 | |
5729 Vector structureCenter2 = optimizedContent->GetStructureCenter(i); | |
5730 const std::string& structureName2 = optimizedContent->GetStructureName(i); | |
5731 const std::string& structureInterpretation2 = optimizedContent->GetStructureInterpretation(i); | |
5732 Color structureColor2 = optimizedContent->GetStructureColor(i); | |
5733 | |
5734 EXPECT_NEAR(structureCenter1[0], structureCenter2[0], TOLERANCE); | |
5735 EXPECT_NEAR(structureCenter1[1], structureCenter2[1], TOLERANCE); | |
5736 EXPECT_NEAR(structureCenter1[2], structureCenter2[2], TOLERANCE); | |
5737 | |
5738 EXPECT_EQ(structureName1, structureName2); | |
5739 EXPECT_EQ(structureInterpretation1, structureInterpretation2); | |
5740 EXPECT_EQ(structureColor1.GetRed(), structureColor2.GetRed()); | |
5741 EXPECT_EQ(structureColor1.GetGreen(), structureColor2.GetGreen()); | |
5742 EXPECT_EQ(structureColor1.GetBlue(), structureColor2.GetBlue()); | |
5743 | |
5744 // "random" walk through the planes. Processing them all takes too long (~ 1 min) | |
5745 for (size_t j = 0; j < allPlanes.size(); j += 37) | |
5746 { | |
5747 const OrthancStone::CoordinateSystem3D& plane = allPlanes[j]; | |
5748 | |
5749 std::vector< std::pair<Point2D, Point2D> > segments1; | |
5750 std::vector< std::pair<Point2D, Point2D> > segments2; | |
5751 | |
5752 bool ok1 = normalContent->ProjectStructure(segments1, i, plane); | |
5753 bool ok2 = optimizedContent->ProjectStructure(segments2, i, plane); | |
5754 | |
5755 // checks here | |
5756 EXPECT_EQ(ok1, ok2); | |
5757 EXPECT_EQ(segments1.size(), segments2.size()); | |
5758 | |
5759 for (size_t k = 0; k < segments1.size(); ++k) | |
5760 { | |
5761 EXPECT_NEAR(segments1[k].first.x, segments2[k].first.x, TOLERANCE); | |
5762 EXPECT_NEAR(segments1[k].first.y, segments2[k].first.y, TOLERANCE); | |
5763 EXPECT_NEAR(segments1[k].second.x, segments2[k].second.x, TOLERANCE); | |
5764 EXPECT_NEAR(segments1[k].second.y, segments2[k].second.y, TOLERANCE); | |
5765 } | |
5766 } | |
5767 } | |
5768 | |
5769 Exitialize(*loadersContext); | |
5770 } |