comparison OrthancStone/UnitTestsSources/Graveyard/TestStructureSet_BGO.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
children
comparison
equal deleted inserted replaced
1540:e20a2381200d 1541:ae17c8c8838f
1 /*
2 these tests are single-threaded... no worries for old buggy compilers
3 (I'm talking to YOU, cl.exe v100! And to your ancestors!)
4 */
5 static std::string& GetTestJson()
6 {
7 static const char* resultRaw = NULL;
8 static std::string result;
9 if (resultRaw == NULL)
10 {
11 std::stringstream sst;
12
13 sst << k_rtStruct_json00
14 << k_rtStruct_json01
15 << k_rtStruct_json02
16 << k_rtStruct_json03
17 << k_rtStruct_json04
18 << k_rtStruct_json05
19 << k_rtStruct_json06
20 << k_rtStruct_json07
21 << k_rtStruct_json08;
22
23 std::string wholeBody = sst.str();
24 result.swap(wholeBody);
25 resultRaw = result.c_str();
26 }
27 return result;
28 }
29
30
31 namespace
32 {
33 void Initialize(const char* orthancApiUrl, OrthancStone::ILoadersContext& loadersContext)
34 {
35 Orthanc::WebServiceParameters p;
36
37 OrthancStone::GenericLoadersContext& typedLoadersContext =
38 dynamic_cast<OrthancStone::GenericLoadersContext&>(loadersContext);
39 // Default is http://localhost:8042
40 // Here's how you may change it
41 p.SetUrl(orthancApiUrl);
42 p.SetCredentials("orthanc", "orthanc");
43 typedLoadersContext.SetOrthancParameters(p);
44
45 typedLoadersContext.StartOracle();
46 }
47
48 void Exitialize(OrthancStone::ILoadersContext& loadersContext)
49 {
50 OrthancStone::GenericLoadersContext& typedLoadersContext =
51 dynamic_cast<OrthancStone::GenericLoadersContext&>(loadersContext);
52
53 typedLoadersContext.StopOracle();
54 }
55
56
57 #if 0
58 class TestObserver : public ObserverBase<TestObserver>
59 {
60 public:
61 TestObserver() {};
62
63 virtual void Handle
64
65 };
66 #endif
67
68 }
69
70 TEST(StructureSet, DISABLED_StructureSetLoader_injection_feature_2020_05_10)
71 {
72 namespace pt = boost::posix_time;
73
74 std::unique_ptr<OrthancStone::ILoadersContext> loadersContext(new OrthancStone::GenericLoadersContext(1,4,1));
75 Initialize("http://localhost:8042/", *loadersContext);
76
77 boost::shared_ptr<DicomStructureSetLoader> loader = DicomStructureSetLoader::Create(*loadersContext);
78
79 // replace with Orthanc ID of an uploaded RTSTRUCT instance!
80 loader->LoadInstanceFullVisibility("72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661");
81
82 bool bContinue(true);
83
84 pt::ptime initialTime = pt::second_clock::local_time();
85
86 while (bContinue)
87 {
88 bContinue = !loader->AreStructuresReady();
89 boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
90
91 {
92 pt::ptime nowTime = pt::second_clock::local_time();
93 pt::time_duration diff = nowTime - initialTime;
94 double seconds = static_cast<double>(diff.total_milliseconds()) * 0.001;
95 std::cout << seconds << " seconds elapsed...\n";
96 if (seconds > 30)
97 {
98 std::cout << "More than 30 seconds elapsed... Aborting test :(\n";
99 //GTEST_FATAL_FAILURE_("More than 30 seconds elapsed... Aborting test :(");
100 //bContinue = false;
101 }
102 }
103 }
104 }
105
106 class SliceProcessor :
107 public OrthancStone::OrthancSeriesVolumeProgressiveLoader::ISlicePostProcessor,
108 public OrthancStone::DicomStructureSetLoader::IInstanceLookupHandler
109 {
110 public:
111 SliceProcessor(OrthancStone::DicomStructureSetLoader& structLoader) : structLoader_(structLoader)
112 {
113 }
114
115 virtual void ProcessCTDicomSlice(const Orthanc::DicomMap& instance) ORTHANC_OVERRIDE
116 {
117 std::string sopInstanceUid;
118 if (!instance.LookupStringValue(sopInstanceUid, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false))
119 {
120 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Missing SOPInstanceUID in a DICOM instance");
121 }
122 slicesDicom_[sopInstanceUid] = boost::shared_ptr<DicomMap>(instance.Clone());
123 }
124
125 virtual void RetrieveReferencedSlices(const std::set<std::string>& nonEmptyInstances) ORTHANC_OVERRIDE
126 {
127 for (std::set<std::string>::const_iterator it = nonEmptyInstances.begin();
128 it != nonEmptyInstances.end();
129 ++it)
130 {
131 const std::string nonEmptyInstance = *it;
132 if (slicesDicom_.find(nonEmptyInstance) == slicesDicom_.end())
133 {
134 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Referenced SOPInstanceUID not found in CT");
135 }
136 boost::shared_ptr<Orthanc::DicomMap> instance = slicesDicom_[nonEmptyInstance];
137 structLoader_.AddReferencedSlice(*instance);
138 }
139 }
140
141 OrthancStone::DicomStructureSetLoader& structLoader_;
142 std::map<std::string, boost::shared_ptr<Orthanc::DicomMap> > slicesDicom_;
143 };
144
145 void LoadCtSeriesBlocking(boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader, std::string seriesId)
146 {
147 namespace pt = boost::posix_time;
148
149 // Load the CT
150 ctLoader->LoadSeries(seriesId);
151
152 // Wait for CT to be loaded
153 pt::ptime initialTime = pt::second_clock::local_time();
154 {
155 bool bContinue(true);
156 while (bContinue)
157 {
158 bContinue = !ctLoader->IsVolumeImageReadyInHighQuality();
159 boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
160
161 {
162 pt::ptime nowTime = pt::second_clock::local_time();
163 pt::time_duration diff = nowTime - initialTime;
164 double seconds = static_cast<double>(diff.total_milliseconds()) * 0.001;
165 std::cout << seconds << " seconds elapsed...\n";
166 if (seconds > 30)
167 {
168 const char* msg = "More than 30 seconds elapsed when waiting for CT... Aborting test :(\n";
169 GTEST_FATAL_FAILURE_(msg);
170 bContinue = false;
171 }
172 }
173 }
174 }
175 }
176
177
178 /**
179 Will fill planes
180 */
181 void GetCTPlanes(std::vector<OrthancStone::CoordinateSystem3D>& planes,
182 OrthancStone::VolumeProjection projection,
183 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader)
184 {
185 planes.clear(); // inefficient : we don't care
186
187 const VolumeImageGeometry& geometry = ctLoader->GetImageGeometry();
188 const unsigned int depth = geometry.GetProjectionDepth(projection);
189
190 planes.resize(depth);
191
192 for (unsigned int z = 0; z < depth; z++)
193 {
194 planes[z] = geometry.GetProjectionSlice(projection, z);
195 }
196 }
197
198 void LoadRtStructBlocking(boost::shared_ptr<OrthancStone::DicomStructureSetLoader> structLoader, std::string instanceId)
199 {
200 namespace pt = boost::posix_time;
201
202 // Load RTSTRUCT
203 structLoader->LoadInstanceFullVisibility(instanceId);
204
205 pt::ptime initialTime = pt::second_clock::local_time();
206
207 // Wait for the loading process to complete
208 {
209 bool bContinue(true);
210 while (bContinue)
211 {
212 bContinue = !structLoader->AreStructuresReady();
213 boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
214
215 {
216 pt::ptime nowTime = pt::second_clock::local_time();
217 pt::time_duration diff = nowTime - initialTime;
218 double seconds = static_cast<double>(diff.total_milliseconds()) * 0.001;
219 std::cout << seconds << " seconds elapsed...\n";
220 if (seconds > 30)
221 {
222 const char* msg = "More than 30 seconds elapsed when waiting for RTSTRUCT... Aborting test :(\n";
223 GTEST_FATAL_FAILURE_(msg);
224 bContinue = false;
225 }
226 }
227 }
228 }
229 }
230
231 TEST(StructureSet, DISABLED_Integration_Compound_CT_Struct_Loading)
232 {
233 const double TOLERANCE = 0.0000001;
234
235 // create loaders context
236 std::unique_ptr<OrthancStone::ILoadersContext> loadersContext(new OrthancStone::GenericLoadersContext(1,4,1));
237 Initialize("http://localhost:8042/", *loadersContext);
238
239 const char* ctSeriesId = "a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa";
240 const char* rtStructInstanceId = "54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9";
241
242 // we'll compare normal loading and optimized loading with SliceProcessor to store the dicom
243
244 boost::shared_ptr<OrthancStone::DicomStructureSetLoader> normalStructLoader;
245 boost::shared_ptr<OrthancStone::DicomStructureSetLoader> optimizedStructLoader;
246
247 {
248 // Create the CT volume
249 boost::shared_ptr<OrthancStone::DicomVolumeImage> volume = boost::make_shared<OrthancStone::DicomVolumeImage>();
250
251 // Create CT loader
252 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader =
253 OrthancStone::OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext, volume);
254
255 // Create struct loader
256 normalStructLoader = OrthancStone::DicomStructureSetLoader::Create(*loadersContext);
257
258 // Load the CT
259 LoadCtSeriesBlocking(ctLoader, ctSeriesId);
260
261 const OrthancStone::VolumeImageGeometry& imageGeometry = ctLoader->GetImageGeometry();
262 unsigned int width = imageGeometry.GetWidth();
263 EXPECT_EQ(512u, width);
264 unsigned int height = imageGeometry.GetHeight();
265 EXPECT_EQ(512u, height);
266 unsigned int depth = imageGeometry.GetDepth();
267 EXPECT_EQ(109u, depth);
268
269 // Load the RTStruct
270 LoadRtStructBlocking(normalStructLoader, rtStructInstanceId);
271 }
272
273 std::vector<OrthancStone::CoordinateSystem3D> axialPlanes;
274 std::vector<OrthancStone::CoordinateSystem3D> coronalPlanes;
275 std::vector<OrthancStone::CoordinateSystem3D> sagittalPlanes;
276
277 {
278 // Create the CT volume
279 boost::shared_ptr<OrthancStone::DicomVolumeImage> volume = boost::make_shared<OrthancStone::DicomVolumeImage>();
280
281 // Create CT loader
282 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader =
283 OrthancStone::OrthancSeriesVolumeProgressiveLoader::Create(*loadersContext, volume);
284
285 // Create struct loader
286 optimizedStructLoader = OrthancStone::DicomStructureSetLoader::Create(*loadersContext);
287
288 // create the slice processor / instance lookup
289 boost::shared_ptr<SliceProcessor> sliceProcessor(new SliceProcessor(*optimizedStructLoader));
290
291 // Inject it into CT loader
292 ctLoader->SetDicomSlicePostProcessor(sliceProcessor);
293
294 // Inject it into RTSTRUCT loader
295 optimizedStructLoader->SetInstanceLookupHandler(sliceProcessor);
296
297 // Load the CT
298 LoadCtSeriesBlocking(ctLoader, ctSeriesId);
299
300 // now, the slices are collected. let's do some checks
301 EXPECT_EQ(109u, sliceProcessor->slicesDicom_.size());
302
303 // Load the RTStruct
304 LoadRtStructBlocking(optimizedStructLoader, rtStructInstanceId);
305
306 GetCTPlanes(axialPlanes, VolumeProjection_Axial, ctLoader);
307 GetCTPlanes(coronalPlanes, VolumeProjection_Coronal, ctLoader);
308 GetCTPlanes(sagittalPlanes, VolumeProjection_Sagittal, ctLoader);
309 }
310
311 // DO NOT DELETE THOSE!
312 OrthancStone::DicomStructureSet* normalContent = normalStructLoader->GetContent();
313 OrthancStone::DicomStructureSet* optimizedContent = optimizedStructLoader->GetContent();
314
315 EXPECT_EQ(normalContent->GetStructuresCount(), optimizedContent->GetStructuresCount());
316
317 /*void GetCTPlanes(std::vector<OrthancStone::CoordinateSystem3D>& planes,
318 OrthancStone::VolumeProjection projection,
319 boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader)*/
320
321
322 std::vector<OrthancStone::CoordinateSystem3D> allPlanes;
323
324 // let's gather all the possible cutting planes in a single struct
325 for (size_t i = 0; i < axialPlanes.size(); ++i)
326 allPlanes.push_back(axialPlanes[i]);
327
328 for (size_t i = 0; i < coronalPlanes.size(); ++i)
329 allPlanes.push_back(coronalPlanes[i]);
330
331 for (size_t i = 0; i < sagittalPlanes.size(); ++i)
332 allPlanes.push_back(sagittalPlanes[i]);
333
334 for (size_t i = 0; i < normalContent->GetStructuresCount(); ++i)
335 {
336 std::cout << "Testing structure (" << i << "/" << normalContent->GetStructuresCount() << ")\n";
337 Vector structureCenter1 = normalContent->GetStructureCenter(i);
338 const std::string& structureName1 = normalContent->GetStructureName(i);
339 const std::string& structureInterpretation1 = normalContent->GetStructureInterpretation(i);
340 Color structureColor1 = normalContent->GetStructureColor(i);
341
342 Vector structureCenter2 = optimizedContent->GetStructureCenter(i);
343 const std::string& structureName2 = optimizedContent->GetStructureName(i);
344 const std::string& structureInterpretation2 = optimizedContent->GetStructureInterpretation(i);
345 Color structureColor2 = optimizedContent->GetStructureColor(i);
346
347 EXPECT_NEAR(structureCenter1[0], structureCenter2[0], TOLERANCE);
348 EXPECT_NEAR(structureCenter1[1], structureCenter2[1], TOLERANCE);
349 EXPECT_NEAR(structureCenter1[2], structureCenter2[2], TOLERANCE);
350
351 EXPECT_EQ(structureName1, structureName2);
352 EXPECT_EQ(structureInterpretation1, structureInterpretation2);
353 EXPECT_EQ(structureColor1.GetRed(), structureColor2.GetRed());
354 EXPECT_EQ(structureColor1.GetGreen(), structureColor2.GetGreen());
355 EXPECT_EQ(structureColor1.GetBlue(), structureColor2.GetBlue());
356
357 // "random" walk through the planes. Processing them all takes too long (~ 1 min)
358 for (size_t j = 0; j < allPlanes.size(); j += 37)
359 {
360 const OrthancStone::CoordinateSystem3D& plane = allPlanes[j];
361
362 std::vector< std::pair<Point2D, Point2D> > segments1;
363 std::vector< std::pair<Point2D, Point2D> > segments2;
364
365 bool ok1 = normalContent->ProjectStructure(segments1, i, plane);
366 bool ok2 = optimizedContent->ProjectStructure(segments2, i, plane);
367
368 // checks here
369 EXPECT_EQ(ok1, ok2);
370 EXPECT_EQ(segments1.size(), segments2.size());
371
372 for (size_t k = 0; k < segments1.size(); ++k)
373 {
374 EXPECT_NEAR(segments1[k].first.x, segments2[k].first.x, TOLERANCE);
375 EXPECT_NEAR(segments1[k].first.y, segments2[k].first.y, TOLERANCE);
376 EXPECT_NEAR(segments1[k].second.x, segments2[k].second.x, TOLERANCE);
377 EXPECT_NEAR(segments1[k].second.y, segments2[k].second.y, TOLERANCE);
378 }
379 }
380 }
381
382 Exitialize(*loadersContext);
383 }