Mercurial > hg > orthanc-stone
comparison Samples/Sdl/Loader.cpp @ 800:98a89b116b62
Added doc + fixed truncation warnings (+ inactive Orthanc IDs for BGO tests)
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Tue, 28 May 2019 14:14:10 +0200 |
parents | bc20e4c417ec |
children | f38c1fc08655 |
comparison
equal
deleted
inserted
replaced
799:a6ba676a93e2 | 800:98a89b116b62 |
---|---|
191 virtual void ApplyStyle(ISceneLayer& layer) const | 191 virtual void ApplyStyle(ISceneLayer& layer) const |
192 { | 192 { |
193 } | 193 } |
194 }; | 194 }; |
195 | 195 |
196 | 196 /** |
197 This interface is implemented by objects representing 3D volume data and | |
198 that are able to return an object that represent a slice of their data | |
199 and are able to create the corresponding visual representation. | |
200 */ | |
197 class IVolumeSlicer : public boost::noncopyable | 201 class IVolumeSlicer : public boost::noncopyable |
198 { | 202 { |
199 public: | 203 public: |
204 /** | |
205 This interface is implemented by objects representing a slice of | |
206 volume data and that are able to create a 2D layer to display a this | |
207 slice. | |
208 | |
209 The CreateSceneLayer factory method is called with an optional | |
210 configurator that possibly impacts the ISceneLayer subclass that is | |
211 created (for instance, if a LUT must be applied on the texture when | |
212 displaying it) | |
213 */ | |
200 class IExtractedSlice : public boost::noncopyable | 214 class IExtractedSlice : public boost::noncopyable |
201 { | 215 { |
202 public: | 216 public: |
203 virtual ~IExtractedSlice() | 217 virtual ~IExtractedSlice() |
204 { | 218 { |
205 } | 219 } |
206 | 220 |
221 /** | |
222 Invalid slices are created when the data is not ready yet or if the | |
223 cut is outside of the available geometry. | |
224 */ | |
207 virtual bool IsValid() = 0; | 225 virtual bool IsValid() = 0; |
208 | 226 |
209 // Must be a cheap call | 227 /** |
228 This retrieves the *revision* that gets incremented every time the | |
229 underlying object undergoes a mutable operation (that it, changes its | |
230 state). | |
231 This **must** be a cheap call. | |
232 */ | |
210 virtual uint64_t GetRevision() = 0; | 233 virtual uint64_t GetRevision() = 0; |
211 | 234 |
212 // This call can take some time | 235 // This call can take some time |
213 virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, // possibly absent | 236 virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, // possibly absent |
214 const CoordinateSystem3D& cuttingPlane) = 0; | 237 const CoordinateSystem3D& cuttingPlane) = 0; |
215 }; | 238 }; |
216 | 239 |
217 | 240 /** |
241 See IExtractedSlice.IsValid() | |
242 */ | |
218 class InvalidSlice : public IExtractedSlice | 243 class InvalidSlice : public IExtractedSlice |
219 { | 244 { |
220 public: | 245 public: |
221 virtual bool IsValid() | 246 virtual bool IsValid() |
222 { | 247 { |
238 | 263 |
239 virtual ~IVolumeSlicer() | 264 virtual ~IVolumeSlicer() |
240 { | 265 { |
241 } | 266 } |
242 | 267 |
268 /** | |
269 This method is implemented by the objects representing volumetric data | |
270 and must returns an IExtractedSlice subclass that contains all the data | |
271 needed to, later one, create its visual representation through | |
272 CreateSceneLayer. | |
273 Subclasses a.o.: ExtractedSlice, Slice, InvalidSlice | |
274 */ | |
243 virtual IExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) = 0; | 275 virtual IExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) = 0; |
244 }; | 276 }; |
245 | 277 |
246 | 278 /** |
247 | 279 This class combines a 3D image buffer, a 3D volume geometry and |
248 // This class combines a 3D image buffer, a 3D volume geometry and | 280 information about the DICOM parameters of the series. |
249 // information about the DICOM parameters of the series. | 281 (MPR means MultiPlanar Reconstruction) |
282 */ | |
250 class DicomVolumeImage : public boost::noncopyable | 283 class DicomVolumeImage : public boost::noncopyable |
251 { | 284 { |
252 public: | 285 public: |
253 ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, DicomVolumeImage); | 286 ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, DicomVolumeImage); |
254 ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentUpdatedMessage, DicomVolumeImage); | 287 ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentUpdatedMessage, DicomVolumeImage); |
339 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 372 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
340 } | 373 } |
341 } | 374 } |
342 }; | 375 }; |
343 | 376 |
344 | 377 /** |
345 | 378 Implements the IVolumeSlicer on Dicom volume data when the cutting plane |
379 that is supplied to the slicer is either axial, sagittal or coronal. | |
380 Arbitrary planes are *not* supported | |
381 */ | |
346 class DicomVolumeImageMPRSlicer : public IVolumeSlicer | 382 class DicomVolumeImageMPRSlicer : public IVolumeSlicer |
347 { | 383 { |
348 public: | 384 public: |
349 class Slice : public IExtractedSlice | 385 class Slice : public IExtractedSlice |
350 { | 386 { |
369 { | 405 { |
370 return volume_.GetRevision(); | 406 return volume_.GetRevision(); |
371 } | 407 } |
372 | 408 |
373 public: | 409 public: |
410 /** | |
411 The constructor initializes the type of projection (axial, sagittal or | |
412 coronal) and the corresponding slice index, from the cutting plane. | |
413 */ | |
374 Slice(const DicomVolumeImage& volume, | 414 Slice(const DicomVolumeImage& volume, |
375 const CoordinateSystem3D& cuttingPlane) : | 415 const CoordinateSystem3D& cuttingPlane) : |
376 volume_(volume) | 416 volume_(volume) |
377 { | 417 { |
378 valid_ = (volume_.HasDicomParameters() && | 418 valid_ = (volume_.HasDicomParameters() && |
606 { | 646 { |
607 Clear(); | 647 Clear(); |
608 } | 648 } |
609 | 649 |
610 // WARNING: The payload of "slices" must be of class "DicomInstanceParameters" | 650 // WARNING: The payload of "slices" must be of class "DicomInstanceParameters" |
651 // (called with the slices created in LoadGeometry) | |
611 void ComputeGeometry(SlicesSorter& slices) | 652 void ComputeGeometry(SlicesSorter& slices) |
612 { | 653 { |
613 Clear(); | 654 Clear(); |
614 | 655 |
615 if (!slices.Sort()) | 656 if (!slices.Sort()) |
703 return that_.seriesGeometry_.GetSliceRevision(sliceIndex); | 744 return that_.seriesGeometry_.GetSliceRevision(sliceIndex); |
704 } | 745 } |
705 else | 746 else |
706 { | 747 { |
707 // For coronal and sagittal projections, we take the global | 748 // For coronal and sagittal projections, we take the global |
708 // revision of the volume | 749 // revision of the volume because even if a single slice changes, |
750 // this means the projection will yield a different result --> | |
751 // we must increase the revision as soon as any slice changes | |
709 return that_.volume_->GetRevision(); | 752 return that_.volume_->GetRevision(); |
710 } | 753 } |
711 } | 754 } |
712 | 755 |
713 public: | 756 public: |
775 command->SetPayload(new Orthanc::SingleValueObject<unsigned int>(sliceIndex)); | 818 command->SetPayload(new Orthanc::SingleValueObject<unsigned int>(sliceIndex)); |
776 oracle_.Schedule(*this, command.release()); | 819 oracle_.Schedule(*this, command.release()); |
777 } | 820 } |
778 } | 821 } |
779 | 822 |
780 | 823 /** |
824 This is called in response to GET "/series/XXXXXXXXXXXXX/instances-tags" | |
825 */ | |
781 void LoadGeometry(const OrthancRestApiCommand::SuccessMessage& message) | 826 void LoadGeometry(const OrthancRestApiCommand::SuccessMessage& message) |
782 { | 827 { |
783 Json::Value body; | 828 Json::Value body; |
784 message.ParseJsonBody(body); | 829 message.ParseJsonBody(body); |
785 | 830 |
799 dicom.FromDicomAsJson(body[instances[i]]); | 844 dicom.FromDicomAsJson(body[instances[i]]); |
800 | 845 |
801 std::auto_ptr<DicomInstanceParameters> instance(new DicomInstanceParameters(dicom)); | 846 std::auto_ptr<DicomInstanceParameters> instance(new DicomInstanceParameters(dicom)); |
802 instance->SetOrthancInstanceIdentifier(instances[i]); | 847 instance->SetOrthancInstanceIdentifier(instances[i]); |
803 | 848 |
849 // the 3D plane corresponding to the slice | |
804 CoordinateSystem3D geometry = instance->GetGeometry(); | 850 CoordinateSystem3D geometry = instance->GetGeometry(); |
805 slices.AddSlice(geometry, instance.release()); | 851 slices.AddSlice(geometry, instance.release()); |
806 } | 852 } |
807 | 853 |
808 seriesGeometry_.ComputeGeometry(slices); | 854 seriesGeometry_.ComputeGeometry(slices); |
820 | 866 |
821 volume_->Initialize(seriesGeometry_.GetImageGeometry(), parameters.GetExpectedPixelFormat()); | 867 volume_->Initialize(seriesGeometry_.GetImageGeometry(), parameters.GetExpectedPixelFormat()); |
822 volume_->SetDicomParameters(parameters); | 868 volume_->SetDicomParameters(parameters); |
823 volume_->GetPixelData().Clear(); | 869 volume_->GetPixelData().Clear(); |
824 | 870 |
825 strategy_.reset(new BasicFetchingStrategy(sorter_->CreateSorter(slicesCount), BEST_QUALITY)); | 871 strategy_.reset(new BasicFetchingStrategy(sorter_->CreateSorter(static_cast<unsigned int>(slicesCount)), BEST_QUALITY)); |
826 | 872 |
827 assert(simultaneousDownloads_ != 0); | 873 assert(simultaneousDownloads_ != 0); |
828 for (unsigned int i = 0; i < simultaneousDownloads_; i++) | 874 for (unsigned int i = 0; i < simultaneousDownloads_; i++) |
829 { | 875 { |
830 ScheduleNextSliceDownload(); | 876 ScheduleNextSliceDownload(); |
972 } | 1018 } |
973 } | 1019 } |
974 }; | 1020 }; |
975 | 1021 |
976 | 1022 |
977 | 1023 /** |
1024 This class is supplied with Oracle commands and will schedule up to | |
1025 simultaneousDownloads_ of them at the same time, then will schedule the | |
1026 rest once slots become available. It is used, a.o., by the | |
1027 OrtancMultiframeVolumeLoader class. | |
1028 */ | |
978 class LoaderStateMachine : public IObserver | 1029 class LoaderStateMachine : public IObserver |
979 { | 1030 { |
980 protected: | 1031 protected: |
981 class State : public Orthanc::IDynamicObject | 1032 class State : public Orthanc::IDynamicObject |
982 { | 1033 { |
1301 if (transferSyntaxUid_.empty() || | 1352 if (transferSyntaxUid_.empty() || |
1302 !volume_->HasGeometry()) | 1353 !volume_->HasGeometry()) |
1303 { | 1354 { |
1304 return; | 1355 return; |
1305 } | 1356 } |
1306 | 1357 /* |
1358 1.2.840.10008.1.2 Implicit VR Endian: Default Transfer Syntax for DICOM | |
1359 1.2.840.10008.1.2.1 Explicit VR Little Endian | |
1360 1.2.840.10008.1.2.2 Explicit VR Big Endian | |
1361 | |
1362 See https://www.dicomlibrary.com/dicom/transfer-syntax/ | |
1363 */ | |
1307 if (transferSyntaxUid_ == "1.2.840.10008.1.2" || | 1364 if (transferSyntaxUid_ == "1.2.840.10008.1.2" || |
1308 transferSyntaxUid_ == "1.2.840.10008.1.2.1" || | 1365 transferSyntaxUid_ == "1.2.840.10008.1.2.1" || |
1309 transferSyntaxUid_ == "1.2.840.10008.1.2.2") | 1366 transferSyntaxUid_ == "1.2.840.10008.1.2.2") |
1310 { | 1367 { |
1311 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | 1368 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); |
1683 { | 1740 { |
1684 std::vector< std::vector<DicomStructureSet::PolygonPoint> > polygons; | 1741 std::vector< std::vector<DicomStructureSet::PolygonPoint> > polygons; |
1685 | 1742 |
1686 if (content_.ProjectStructure(polygons, i, cuttingPlane)) | 1743 if (content_.ProjectStructure(polygons, i, cuttingPlane)) |
1687 { | 1744 { |
1688 printf(">> %d\n", polygons.size()); | 1745 printf(">> %d\n", static_cast<int>(polygons.size())); |
1689 | 1746 |
1690 for (size_t j = 0; j < polygons.size(); j++) | 1747 for (size_t j = 0; j < polygons.size(); j++) |
1691 { | 1748 { |
1692 PolylineSceneLayer::Chain chain; | 1749 PolylineSceneLayer::Chain chain; |
1693 chain.resize(polygons[j].size()); | 1750 chain.resize(polygons[j].size()); |
2274 } | 2331 } |
2275 } | 2332 } |
2276 | 2333 |
2277 | 2334 |
2278 // 2017-11-17-Anonymized | 2335 // 2017-11-17-Anonymized |
2336 #if 0 | |
2337 // BGO data | |
2338 ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // CT | |
2339 doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // RT-DOSE | |
2340 //rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // RT-STRUCT | |
2341 #else | |
2279 //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT | 2342 //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT |
2280 doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE | 2343 doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE |
2281 //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT | 2344 //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6"); // RT-STRUCT |
2282 | 2345 #endif |
2283 // 2015-01-28-Multiframe | 2346 // 2015-01-28-Multiframe |
2284 //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279"); // Multiframe CT | 2347 //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279"); // Multiframe CT |
2285 | 2348 |
2286 // Delphine | 2349 // Delphine |
2287 //ctLoader->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT | 2350 //ctLoader->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT |