Mercurial > hg > orthanc-stone
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 } |