comparison Samples/Sdl/Loader.cpp @ 766:d6cd7c5ca6ea

fix
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 23 May 2019 16:34:03 +0200
parents f6438fdc447e
children dce5b067d040
comparison
equal deleted inserted replaced
765:f6438fdc447e 766:d6cd7c5ca6ea
65 65
66 // Must be a cheap call 66 // Must be a cheap call
67 virtual uint64_t GetRevision() = 0; 67 virtual uint64_t GetRevision() = 0;
68 68
69 // This call can take some time 69 // This call can take some time
70 virtual ISceneLayer* CreateSceneLayer() = 0; 70 virtual ISceneLayer* CreateSceneLayer(const CoordinateSystem3D& cuttingPlane) = 0;
71 }; 71 };
72 72
73 virtual ~IVolumeSlicer() 73 virtual ~IVolumeSlicer()
74 { 74 {
75 } 75 }
76 76
77 virtual ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const = 0; 77 virtual ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const = 0;
78 }; 78 };
79 79
80 80
81 class IVolumeImageSlicer : public IVolumeSlicer
82 {
83 public:
84 virtual bool HasGeometry() const = 0;
85
86 virtual const VolumeImageGeometry& GetGeometry() const = 0;
87 };
88
89
81 class InvalidExtractedSlice : public IVolumeSlicer::ExtractedSlice 90 class InvalidExtractedSlice : public IVolumeSlicer::ExtractedSlice
82 { 91 {
83 public: 92 public:
84 virtual bool IsValid() 93 virtual bool IsValid()
85 { 94 {
89 virtual uint64_t GetRevision() 98 virtual uint64_t GetRevision()
90 { 99 {
91 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 100 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
92 } 101 }
93 102
94 virtual ISceneLayer* CreateSceneLayer() 103 virtual ISceneLayer* CreateSceneLayer(const CoordinateSystem3D& cuttingPlane)
95 { 104 {
96 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 105 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
97 } 106 }
98 }; 107 };
99 108
153 { 162 {
154 CheckValid(); 163 CheckValid();
155 return GetRevisionInternal(projection_, sliceIndex_); 164 return GetRevisionInternal(projection_, sliceIndex_);
156 } 165 }
157 166
158 virtual ISceneLayer* CreateSceneLayer() 167 virtual ISceneLayer* CreateSceneLayer(const CoordinateSystem3D& cuttingPlane)
159 { 168 {
160 CheckValid(); 169 CheckValid();
161 170
162 std::auto_ptr<TextureBaseSceneLayer> texture; 171 std::auto_ptr<TextureBaseSceneLayer> texture;
163 172
166 ImageBuffer3D::SliceReader reader(image_, projection_, sliceIndex_); 175 ImageBuffer3D::SliceReader reader(image_, projection_, sliceIndex_);
167 texture.reset(parameters.CreateTexture(reader.GetAccessor())); 176 texture.reset(parameters.CreateTexture(reader.GetAccessor()));
168 } 177 }
169 178
170 const CoordinateSystem3D& system = geometry_.GetProjectionGeometry(projection_); 179 const CoordinateSystem3D& system = geometry_.GetProjectionGeometry(projection_);
171 180
172 double x0, y0, x1, y1; 181 double x0, y0, x1, y1;
173 system.ProjectPoint(x0, y0, system.GetOrigin()); 182 cuttingPlane.ProjectPoint(x0, y0, system.GetOrigin());
174 system.ProjectPoint(x1, y1, system.GetOrigin() + system.GetAxisX()); 183 cuttingPlane.ProjectPoint(x1, y1, system.GetOrigin() + system.GetAxisX());
175 texture->SetOrigin(x0, y0); 184 texture->SetOrigin(x0, y0);
176 185
177 double dx = x1 - x0; 186 double dx = x1 - x0;
178 double dy = y1 - y0; 187 double dy = y1 - y0;
179 if (!LinearAlgebra::IsCloseToZero(dx) || 188 if (!LinearAlgebra::IsCloseToZero(dx) ||
182 texture->SetAngle(atan2(dy, dx)); 191 texture->SetAngle(atan2(dy, dx));
183 } 192 }
184 193
185 Vector tmp = geometry_.GetVoxelDimensions(projection_); 194 Vector tmp = geometry_.GetVoxelDimensions(projection_);
186 texture->SetPixelSpacing(tmp[0], tmp[1]); 195 texture->SetPixelSpacing(tmp[0], tmp[1]);
196
197 printf("%.1f %.1f %.1f => %.1f %.1f => %.1f %.1f\n",
198 system.GetOrigin() [0],
199 system.GetOrigin() [1],
200 system.GetOrigin() [2],
201 x0, y0,
202 x0 + texture->GetTexture().GetWidth() * tmp[0],
203 y0 + texture->GetTexture().GetHeight() * tmp[1]);
187 204
188 return texture.release(); 205 return texture.release();
189 } 206 }
190 }; 207 };
191 208
331 } 348 }
332 } 349 }
333 350
334 351
335 public: 352 public:
336 DicomSeriesVolumeImage() 353 DicomSeriesVolumeImage() :
354 revision_(0)
337 { 355 {
338 } 356 }
339 357
340 ~DicomSeriesVolumeImage() 358 ~DicomSeriesVolumeImage()
341 { 359 {
635 (new DicomSeriesVolumeImage::ExtractedOrthogonalSlice(volume_, cuttingPlane)); 653 (new DicomSeriesVolumeImage::ExtractedOrthogonalSlice(volume_, cuttingPlane));
636 654
637 assert(slice.get() != NULL && 655 assert(slice.get() != NULL &&
638 strategy_.get() != NULL); 656 strategy_.get() != NULL);
639 657
640 if (slice->GetProjection() == VolumeProjection_Axial) 658 if (slice->IsValid() &&
659 slice->GetProjection() == VolumeProjection_Axial)
641 { 660 {
642 strategy_->SetCurrent(slice->GetSliceIndex()); 661 strategy_->SetCurrent(slice->GetSliceIndex());
643 } 662 }
644 663
645 return slice.release(); 664 return slice.release();
650 } 669 }
651 } 670 }
652 671
653 672
654 public: 673 public:
655 class MPRSlicer : public IVolumeSlicer 674 class MPRSlicer : public IVolumeImageSlicer
656 { 675 {
657 private: 676 private:
658 boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> that_; 677 boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> that_;
659 678
660 public: 679 public:
661 MPRSlicer(const boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader>& that) : 680 MPRSlicer(const boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader>& that) :
662 that_(that) 681 that_(that)
663 { 682 {
664 } 683 }
665 684
666 virtual IVolumeSlicer::ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const 685 virtual ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const
667 { 686 {
668 return that_->ExtractOrthogonalSlice(cuttingPlane); 687 return that_->ExtractOrthogonalSlice(cuttingPlane);
669 } 688 }
670 689
671 const DicomSeriesVolumeImage& GetVolume() const 690 virtual bool HasGeometry() const
672 { 691 {
673 return that_->GetVolume(); 692 return that_->GetVolume().HasGeometry();
693 }
694
695 virtual const VolumeImageGeometry& GetGeometry() const
696 {
697 return that_->GetVolume().GetGeometry();
674 } 698 }
675 }; 699 };
676 700
677 OrthancSeriesVolumeProgressiveLoader(IOracle& oracle, 701 OrthancSeriesVolumeProgressiveLoader(IOracle& oracle,
678 IObservable& oracleObservable) : 702 IObservable& oracleObservable) :
980 dicom_->GetPixelSpacingY(), 1004 dicom_->GetPixelSpacingY(),
981 spacingZ); 1005 spacingZ);
982 1006
983 image_.reset(new ImageBuffer3D(format, width, height, depth, 1007 image_.reset(new ImageBuffer3D(format, width, height, depth,
984 false /* don't compute range */)); 1008 false /* don't compute range */));
1009 image_->Clear();
985 1010
986 ScheduleFrameDownloads(); 1011 ScheduleFrameDownloads();
987 } 1012 }
988 1013
989 1014
1056 } 1081 }
1057 1082
1058 revision_ ++; 1083 revision_ ++;
1059 } 1084 }
1060 1085
1086
1087 private:
1088 class ExtractedOrthogonalSlice : public DicomVolumeImageOrthogonalSlice
1089 {
1090 private:
1091 const OrthancMultiframeVolumeLoader& that_;
1092
1093 protected:
1094 virtual uint64_t GetRevisionInternal(VolumeProjection projection,
1095 unsigned int sliceIndex) const
1096 {
1097 return that_.revision_;
1098 }
1099
1100 virtual const DicomInstanceParameters& GetDicomParameters(VolumeProjection projection,
1101 unsigned int sliceIndex) const
1102 {
1103 return that_.GetDicomParameters();
1104 }
1105
1106 public:
1107 ExtractedOrthogonalSlice(const OrthancMultiframeVolumeLoader& that,
1108 const CoordinateSystem3D& plane) :
1109 DicomVolumeImageOrthogonalSlice(that.GetImage(), that.GetGeometry(), plane),
1110 that_(that)
1111 {
1112 }
1113 };
1114
1061 1115
1062 public: 1116 public:
1117 class MPRSlicer : public IVolumeImageSlicer
1118 {
1119 private:
1120 boost::shared_ptr<OrthancMultiframeVolumeLoader> that_;
1121
1122 public:
1123 MPRSlicer(const boost::shared_ptr<OrthancMultiframeVolumeLoader>& that) :
1124 that_(that)
1125 {
1126 }
1127
1128 virtual ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const
1129 {
1130 if (that_->HasGeometry())
1131 {
1132 return new ExtractedOrthogonalSlice(*that_, cuttingPlane);
1133 }
1134 else
1135 {
1136 return new InvalidExtractedSlice;
1137 }
1138 }
1139
1140 virtual bool HasGeometry() const
1141 {
1142 return that_->HasGeometry();
1143 }
1144
1145 virtual const VolumeImageGeometry& GetGeometry() const
1146 {
1147 return that_->GetGeometry();
1148 }
1149 };
1150
1151
1063 OrthancMultiframeVolumeLoader(IOracle& oracle, 1152 OrthancMultiframeVolumeLoader(IOracle& oracle,
1064 IObservable& oracleObservable) : 1153 IObservable& oracleObservable) :
1065 IObserver(oracleObservable.GetBroker()), 1154 IObserver(oracleObservable.GetBroker()),
1066 oracle_(oracle), 1155 oracle_(oracle),
1067 active_(false) 1156 active_(false),
1157 revision_(0)
1068 { 1158 {
1069 oracleObservable.RegisterObserverCallback( 1159 oracleObservable.RegisterObserverCallback(
1070 new Callable<OrthancMultiframeVolumeLoader, OrthancRestApiCommand::SuccessMessage> 1160 new Callable<OrthancMultiframeVolumeLoader, OrthancRestApiCommand::SuccessMessage>
1071 (*this, &OrthancMultiframeVolumeLoader::Handle)); 1161 (*this, &OrthancMultiframeVolumeLoader::Handle));
1072 } 1162 }
1075 bool HasGeometry() const 1165 bool HasGeometry() const
1076 { 1166 {
1077 return (dicom_.get() != NULL && 1167 return (dicom_.get() != NULL &&
1078 geometry_.get() != NULL && 1168 geometry_.get() != NULL &&
1079 image_.get() != NULL); 1169 image_.get() != NULL);
1170 }
1171
1172
1173 const ImageBuffer3D& GetImage() const
1174 {
1175 if (!HasGeometry())
1176 {
1177 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1178 }
1179 else
1180 {
1181 return *image_;
1182 }
1183 }
1184
1185
1186 const VolumeImageGeometry& GetGeometry() const
1187 {
1188 if (!HasGeometry())
1189 {
1190 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1191 }
1192 else
1193 {
1194 return *geometry_;
1195 }
1196 }
1197
1198
1199 const DicomInstanceParameters& GetDicomParameters() const
1200 {
1201 if (!HasGeometry())
1202 {
1203 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1204 }
1205 else
1206 {
1207 return *dicom_;
1208 }
1080 } 1209 }
1081 1210
1082 1211
1083 void LoadInstance(const std::string& instanceId) 1212 void LoadInstance(const std::string& instanceId)
1084 { 1213 {
1128 LinearAlgebra::IsCloseToZero(distance)); 1257 LinearAlgebra::IsCloseToZero(distance));
1129 } 1258 }
1130 1259
1131 public: 1260 public:
1132 VolumeSceneLayerSource(int layerDepth, 1261 VolumeSceneLayerSource(int layerDepth,
1133 IVolumeSlicer* slicer) : // Takes ownership 1262 IVolumeSlicer* slicer) : // Takes ownership
1134 layerDepth_(layerDepth), 1263 layerDepth_(layerDepth),
1135 slicer_(slicer), 1264 slicer_(slicer),
1136 linearInterpolation_(false) 1265 linearInterpolation_(false)
1137 { 1266 {
1138 if (slicer == NULL) 1267 if (slicer == NULL)
1183 { 1312 {
1184 // Content has changed: An update is needed 1313 // Content has changed: An update is needed
1185 lastPlane_.reset(new CoordinateSystem3D(plane)); 1314 lastPlane_.reset(new CoordinateSystem3D(plane));
1186 lastRevision_ = slice->GetRevision(); 1315 lastRevision_ = slice->GetRevision();
1187 1316
1188 std::auto_ptr<ISceneLayer> layer(slice->CreateSceneLayer()); 1317 std::auto_ptr<ISceneLayer> layer(slice->CreateSceneLayer(plane));
1189 if (layer.get() == NULL) 1318 if (layer.get() == NULL)
1190 { 1319 {
1191 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 1320 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
1192 } 1321 }
1193 1322
1281 class Toto : public OrthancStone::IObserver 1410 class Toto : public OrthancStone::IObserver
1282 { 1411 {
1283 private: 1412 private:
1284 OrthancStone::IOracle& oracle_; 1413 OrthancStone::IOracle& oracle_;
1285 OrthancStone::Scene2D scene_; 1414 OrthancStone::Scene2D scene_;
1286 std::auto_ptr<OrthancStone::VolumeSceneLayerSource> source_; 1415 std::auto_ptr<OrthancStone::VolumeSceneLayerSource> source1_, source2_;
1416
1417
1418 OrthancStone::CoordinateSystem3D GetSamplePlane
1419 (const OrthancStone::VolumeSceneLayerSource& source) const
1420 {
1421 const OrthancStone::IVolumeImageSlicer& slicer =
1422 dynamic_cast<const OrthancStone::IVolumeImageSlicer&>(source.GetSlicer());
1423
1424 OrthancStone::CoordinateSystem3D plane;
1425
1426 if (slicer.HasGeometry())
1427 {
1428 //plane = slicer.GetGeometry().GetSagittalGeometry();
1429 //plane = slicer.GetGeometry().GetAxialGeometry();
1430 plane = slicer.GetGeometry().GetCoronalGeometry();
1431 plane.SetOrigin(slicer.GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f));
1432 }
1433
1434 return plane;
1435 }
1436
1287 1437
1288 void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message) 1438 void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message)
1289 { 1439 {
1290 if (message.GetOrigin().HasPayload()) 1440 if (message.GetOrigin().HasPayload())
1291 { 1441 {
1293 } 1443 }
1294 else 1444 else
1295 { 1445 {
1296 printf("TIMEOUT\n"); 1446 printf("TIMEOUT\n");
1297 1447
1298 if (source_.get() != NULL) 1448 OrthancStone::CoordinateSystem3D plane;
1299 { 1449
1300 OrthancStone::CoordinateSystem3D plane; 1450 if (source1_.get() != NULL)
1301 1451 {
1302 const OrthancStone::OrthancSeriesVolumeProgressiveLoader::MPRSlicer& loader = 1452 plane = GetSamplePlane(*source1_);
1303 dynamic_cast<const OrthancStone::OrthancSeriesVolumeProgressiveLoader::MPRSlicer&>(source_->GetSlicer()); 1453 }
1454 else if (source2_.get() != NULL)
1455 {
1456 plane = GetSamplePlane(*source2_);
1457 }
1458
1459 if (source1_.get() != NULL)
1460 {
1461 source1_->Update(scene_, plane);
1462 }
1463
1464 if (source2_.get() != NULL)
1465 {
1466 source2_->Update(scene_, plane);
1467 }
1468
1469 scene_.FitContent(1024, 768);
1470
1471 {
1472 OrthancStone::CairoCompositor compositor(scene_, 1024, 768);
1473 compositor.Refresh();
1304 1474
1305 if (loader.GetVolume().HasGeometry()) 1475 Orthanc::ImageAccessor accessor;
1306 { 1476 compositor.GetCanvas().GetReadOnlyAccessor(accessor);
1307 plane = loader.GetVolume().GetGeometry().GetSagittalGeometry(); 1477
1308 plane.SetOrigin(loader.GetVolume().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); 1478 Orthanc::Image tmp(Orthanc::PixelFormat_RGB24, accessor.GetWidth(), accessor.GetHeight(), false);
1309 } 1479 Orthanc::ImageProcessing::Convert(tmp, accessor);
1310 1480
1311 source_->Update(scene_, plane); 1481 static unsigned int count = 0;
1312 scene_.FitContent(1024, 768); 1482 char buf[64];
1313 1483 sprintf(buf, "scene-%06d.png", count++);
1314 { 1484
1315 OrthancStone::CairoCompositor compositor(scene_, 1024, 768); 1485 Orthanc::PngWriter writer;
1316 compositor.Refresh(); 1486 writer.WriteToFile(buf, tmp);
1317
1318 Orthanc::ImageAccessor accessor;
1319 compositor.GetCanvas().GetReadOnlyAccessor(accessor);
1320
1321 Orthanc::Image tmp(Orthanc::PixelFormat_RGB24, accessor.GetWidth(), accessor.GetHeight(), false);
1322 Orthanc::ImageProcessing::Convert(tmp, accessor);
1323
1324 static unsigned int count = 0;
1325 char buf[64];
1326 sprintf(buf, "scene-%06d.png", count++);
1327
1328 Orthanc::PngWriter writer;
1329 writer.WriteToFile(buf, tmp);
1330 }
1331 } 1487 }
1332 1488
1333 /** 1489 /**
1334 * The sleep() leads to a crash if the oracle is still running, 1490 * The sleep() leads to a crash if the oracle is still running,
1335 * while this object is destroyed. Always stop the oracle before 1491 * while this object is destroyed. Always stop the oracle before
1400 oracleObservable.RegisterObserverCallback 1556 oracleObservable.RegisterObserverCallback
1401 (new OrthancStone::Callable 1557 (new OrthancStone::Callable
1402 <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle)); 1558 <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle));
1403 } 1559 }
1404 1560
1405 void SetVolume(int depth, 1561 void SetVolume1(int depth,
1406 OrthancStone::IVolumeSlicer* volume) 1562 OrthancStone::IVolumeSlicer* volume)
1407 { 1563 {
1408 source_.reset(new OrthancStone::VolumeSceneLayerSource(0, volume)); 1564 source1_.reset(new OrthancStone::VolumeSceneLayerSource(0, volume));
1565 }
1566
1567 void SetVolume2(int depth,
1568 OrthancStone::IVolumeSlicer* volume)
1569 {
1570 source2_.reset(new OrthancStone::VolumeSceneLayerSource(0, volume));
1409 } 1571 }
1410 }; 1572 };
1411 1573
1412 1574
1413 void Run(OrthancStone::NativeApplicationContext& context, 1575 void Run(OrthancStone::NativeApplicationContext& context,
1501 oracle.Schedule(*toto, command.release()); 1663 oracle.Schedule(*toto, command.release());
1502 } 1664 }
1503 } 1665 }
1504 1666
1505 // 2017-11-17-Anonymized 1667 // 2017-11-17-Anonymized
1506 //loader1->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT 1668 loader1->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT
1507 loader3->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE 1669 loader3->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE
1508 1670
1509 // 2015-01-28-Multiframe 1671 // 2015-01-28-Multiframe
1510 //loader3->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279"); // Multiframe CT 1672 //loader3->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279"); // Multiframe CT
1511 1673
1512 // Delphine 1674 // Delphine
1513 //loader1->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT 1675 //loader1->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT
1514 //loader1->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5"); // Lung 1/10mm 1676 //loader1->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5"); // Lung 1/10mm
1515 1677
1516 1678
1517 toto->SetVolume(0, new OrthancStone::OrthancSeriesVolumeProgressiveLoader::MPRSlicer(loader1)); 1679 toto->SetVolume2(1, new OrthancStone::OrthancMultiframeVolumeLoader::MPRSlicer(loader3));
1680 toto->SetVolume1(0, new OrthancStone::OrthancSeriesVolumeProgressiveLoader::MPRSlicer(loader1));
1518 1681
1519 { 1682 {
1520 oracle.Start(); 1683 oracle.Start();
1521 1684
1522 LOG(WARNING) << "...Waiting for Ctrl-C..."; 1685 LOG(WARNING) << "...Waiting for Ctrl-C...";