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