Mercurial > hg > orthanc-stone
comparison Samples/Sdl/Loader.cpp @ 776:0387485f048b
ILayerStyleConfigurator
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 24 May 2019 18:29:27 +0200 |
parents | 4ba8892870a2 |
children | 78fcb907caf6 |
comparison
equal
deleted
inserted
replaced
773:b8dfd966b5f4 | 776:0387485f048b |
---|---|
54 #include <EmbeddedResources.h> | 54 #include <EmbeddedResources.h> |
55 | 55 |
56 | 56 |
57 namespace OrthancStone | 57 namespace OrthancStone |
58 { | 58 { |
59 // Application-configurable, can be shared between 3D/2D | |
60 class ILayerStyleConfigurator | |
61 { | |
62 public: | |
63 virtual ~ILayerStyleConfigurator() | |
64 { | |
65 } | |
66 | |
67 virtual uint64_t GetRevision() const = 0; | |
68 | |
69 virtual ISceneLayer* CreateFromImage(const Orthanc::ImageAccessor& image) const = 0; | |
70 | |
71 virtual ISceneLayer* CreateFromDicomImage(const Orthanc::ImageAccessor& frame, | |
72 const DicomInstanceParameters& parameters) const = 0; | |
73 | |
74 virtual void ApplyStyle(ISceneLayer& layer) const = 0; | |
75 }; | |
76 | |
77 | |
78 | |
79 class LookupTableStyleConfigurator : public ILayerStyleConfigurator | |
80 { | |
81 private: | |
82 uint64_t revision_; | |
83 bool hasLut_; | |
84 std::string lut_; | |
85 bool hasRange_; | |
86 float minValue_; | |
87 float maxValue_; | |
88 | |
89 public: | |
90 LookupTableStyleConfigurator() : | |
91 revision_(0), | |
92 hasLut_(false), | |
93 hasRange_(false) | |
94 { | |
95 SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT); // TODO - test | |
96 } | |
97 | |
98 void SetLookupTable(Orthanc::EmbeddedResources::FileResourceId resource) | |
99 { | |
100 hasLut_ = true; | |
101 Orthanc::EmbeddedResources::GetFileResource(lut_, resource); | |
102 } | |
103 | |
104 void SetLookupTable(const std::string& lut) | |
105 { | |
106 hasLut_ = true; | |
107 lut_ = lut; | |
108 } | |
109 | |
110 void SetRange(float minValue, | |
111 float maxValue) | |
112 { | |
113 if (minValue > maxValue) | |
114 { | |
115 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
116 } | |
117 else | |
118 { | |
119 hasRange_ = true; | |
120 minValue_ = minValue; | |
121 maxValue_ = maxValue; | |
122 } | |
123 } | |
124 | |
125 virtual uint64_t GetRevision() const | |
126 { | |
127 return revision_; | |
128 } | |
129 | |
130 virtual ISceneLayer* CreateFromImage(const Orthanc::ImageAccessor& image) const | |
131 { | |
132 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
133 } | |
134 | |
135 virtual ISceneLayer* CreateFromDicomImage(const Orthanc::ImageAccessor& frame, | |
136 const DicomInstanceParameters& parameters) const | |
137 { | |
138 return parameters.CreateLookupTableTexture(frame); | |
139 } | |
140 | |
141 virtual void ApplyStyle(ISceneLayer& layer) const | |
142 { | |
143 LookupTableTextureSceneLayer& l = dynamic_cast<LookupTableTextureSceneLayer&>(layer); | |
144 | |
145 if (hasLut_) | |
146 { | |
147 l.SetLookupTable(lut_); | |
148 } | |
149 | |
150 if (hasRange_) | |
151 { | |
152 l.SetRange(minValue_, maxValue_); | |
153 } | |
154 else | |
155 { | |
156 l.FitRange(); | |
157 } | |
158 } | |
159 }; | |
160 | |
161 | |
162 class GrayscaleStyleConfigurator : public ILayerStyleConfigurator | |
163 { | |
164 private: | |
165 uint64_t revision_; | |
166 | |
167 public: | |
168 GrayscaleStyleConfigurator() : | |
169 revision_(0) | |
170 { | |
171 } | |
172 | |
173 virtual uint64_t GetRevision() const | |
174 { | |
175 return revision_; | |
176 } | |
177 | |
178 virtual ISceneLayer* CreateFromImage(const Orthanc::ImageAccessor& image) const | |
179 { | |
180 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
181 } | |
182 | |
183 virtual ISceneLayer* CreateFromDicomImage(const Orthanc::ImageAccessor& frame, | |
184 const DicomInstanceParameters& parameters) const | |
185 { | |
186 return parameters.CreateTexture(frame); | |
187 } | |
188 | |
189 virtual void ApplyStyle(ISceneLayer& layer) const | |
190 { | |
191 } | |
192 }; | |
193 | |
194 | |
59 class IVolumeSlicer : public boost::noncopyable | 195 class IVolumeSlicer : public boost::noncopyable |
60 { | 196 { |
61 public: | 197 public: |
62 class ExtractedSlice : public boost::noncopyable | 198 class ExtractedSlice : public boost::noncopyable |
63 { | 199 { |
70 | 206 |
71 // Must be a cheap call | 207 // Must be a cheap call |
72 virtual uint64_t GetRevision() = 0; | 208 virtual uint64_t GetRevision() = 0; |
73 | 209 |
74 // This call can take some time | 210 // This call can take some time |
75 virtual ISceneLayer* CreateSceneLayer(const CoordinateSystem3D& cuttingPlane) = 0; | 211 virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, // possibly absent |
212 const CoordinateSystem3D& cuttingPlane) = 0; | |
76 }; | 213 }; |
77 | 214 |
78 virtual ~IVolumeSlicer() | 215 virtual ~IVolumeSlicer() |
79 { | 216 { |
80 } | 217 } |
81 | 218 |
82 virtual ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const = 0; | 219 virtual ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const = 0; |
83 }; | 220 }; |
84 | 221 |
85 | 222 |
223 | |
224 // TODO -> Remove | |
86 class IVolumeImageSlicer : public IVolumeSlicer | 225 class IVolumeImageSlicer : public IVolumeSlicer |
87 { | 226 { |
88 public: | 227 public: |
89 virtual bool HasGeometry() const = 0; | 228 virtual bool HasGeometry() const = 0; |
90 | 229 |
103 virtual uint64_t GetRevision() | 242 virtual uint64_t GetRevision() |
104 { | 243 { |
105 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 244 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
106 } | 245 } |
107 | 246 |
108 virtual ISceneLayer* CreateSceneLayer(const CoordinateSystem3D& cuttingPlane) | 247 virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, |
248 const CoordinateSystem3D& cuttingPlane) | |
109 { | 249 { |
110 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 250 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
111 } | 251 } |
112 }; | 252 }; |
113 | 253 |
167 { | 307 { |
168 CheckValid(); | 308 CheckValid(); |
169 return GetRevisionInternal(projection_, sliceIndex_); | 309 return GetRevisionInternal(projection_, sliceIndex_); |
170 } | 310 } |
171 | 311 |
172 virtual ISceneLayer* CreateSceneLayer(const CoordinateSystem3D& cuttingPlane) | 312 virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, |
313 const CoordinateSystem3D& cuttingPlane) | |
173 { | 314 { |
174 CheckValid(); | 315 CheckValid(); |
316 | |
317 if (configurator == NULL) | |
318 { | |
319 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer, | |
320 "A style configurator is mandatory for textures"); | |
321 } | |
175 | 322 |
176 std::auto_ptr<TextureBaseSceneLayer> texture; | 323 std::auto_ptr<TextureBaseSceneLayer> texture; |
177 | 324 |
178 { | 325 { |
179 const DicomInstanceParameters& parameters = GetDicomParameters(projection_, sliceIndex_); | 326 const DicomInstanceParameters& parameters = GetDicomParameters(projection_, sliceIndex_); |
180 ImageBuffer3D::SliceReader reader(image_, projection_, sliceIndex_); | 327 ImageBuffer3D::SliceReader reader(image_, projection_, sliceIndex_); |
181 | 328 texture.reset(dynamic_cast<TextureBaseSceneLayer*> |
182 static unsigned int i = 1; | 329 (configurator->CreateFromDicomImage(reader.GetAccessor(), parameters))); |
183 | |
184 if (i % 2) | |
185 { | |
186 texture.reset(parameters.CreateTexture(reader.GetAccessor())); | |
187 } | |
188 else | |
189 { | |
190 std::string lut; | |
191 Orthanc::EmbeddedResources::GetFileResource(lut, Orthanc::EmbeddedResources::COLORMAP_HOT); | |
192 | |
193 std::auto_ptr<LookupTableTextureSceneLayer> tmp(parameters.CreateLookupTableTexture(reader.GetAccessor())); | |
194 tmp->FitRange(); | |
195 tmp->SetLookupTable(lut); | |
196 texture.reset(tmp.release()); | |
197 } | |
198 | |
199 i++; | |
200 } | 330 } |
201 | 331 |
202 const CoordinateSystem3D& system = geometry_.GetProjectionGeometry(projection_); | 332 const CoordinateSystem3D& system = geometry_.GetProjectionGeometry(projection_); |
203 | 333 |
204 double x0, y0, x1, y1; | 334 double x0, y0, x1, y1; |
1278 | 1408 |
1279 | 1409 |
1280 class VolumeSceneLayerSource : public boost::noncopyable | 1410 class VolumeSceneLayerSource : public boost::noncopyable |
1281 { | 1411 { |
1282 private: | 1412 private: |
1283 int layerDepth_; | 1413 Scene2D& scene_; |
1284 boost::shared_ptr<IVolumeSlicer> slicer_; | 1414 int layerDepth_; |
1285 bool linearInterpolation_; | 1415 boost::shared_ptr<IVolumeSlicer> slicer_; |
1286 std::auto_ptr<CoordinateSystem3D> lastPlane_; | 1416 std::auto_ptr<ILayerStyleConfigurator> configurator_; |
1287 uint64_t lastRevision_; | 1417 std::auto_ptr<CoordinateSystem3D> lastPlane_; |
1418 uint64_t lastRevision_; | |
1419 uint64_t lastConfiguratorRevision_; | |
1288 | 1420 |
1289 static bool IsSameCuttingPlane(const CoordinateSystem3D& a, | 1421 static bool IsSameCuttingPlane(const CoordinateSystem3D& a, |
1290 const CoordinateSystem3D& b) | 1422 const CoordinateSystem3D& b) |
1291 { | 1423 { |
1424 // TODO - What if the normal is reversed? | |
1292 double distance; | 1425 double distance; |
1293 return (CoordinateSystem3D::ComputeDistance(distance, a, b) && | 1426 return (CoordinateSystem3D::ComputeDistance(distance, a, b) && |
1294 LinearAlgebra::IsCloseToZero(distance)); | 1427 LinearAlgebra::IsCloseToZero(distance)); |
1295 } | 1428 } |
1296 | 1429 |
1297 public: | 1430 public: |
1298 VolumeSceneLayerSource(int layerDepth, | 1431 VolumeSceneLayerSource(Scene2D& scene, |
1432 int layerDepth, | |
1299 IVolumeSlicer* slicer) : // Takes ownership | 1433 IVolumeSlicer* slicer) : // Takes ownership |
1434 scene_(scene), | |
1300 layerDepth_(layerDepth), | 1435 layerDepth_(layerDepth), |
1301 slicer_(slicer), | 1436 slicer_(slicer) |
1302 linearInterpolation_(false) | |
1303 { | 1437 { |
1304 if (slicer == NULL) | 1438 if (slicer == NULL) |
1305 { | 1439 { |
1306 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | 1440 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); |
1307 } | 1441 } |
1310 const IVolumeSlicer& GetSlicer() const | 1444 const IVolumeSlicer& GetSlicer() const |
1311 { | 1445 { |
1312 return *slicer_; | 1446 return *slicer_; |
1313 } | 1447 } |
1314 | 1448 |
1315 void SetLinearInterpolation(bool enabled) | 1449 void RemoveConfigurator() |
1316 { | 1450 { |
1317 linearInterpolation_ = enabled; | 1451 configurator_.reset(); |
1318 } | 1452 lastPlane_.reset(); |
1319 | 1453 } |
1320 bool IsLinearInterpolation() const | 1454 |
1321 { | 1455 void SetConfigurator(ILayerStyleConfigurator* configurator) // Takes ownership |
1322 return linearInterpolation_; | 1456 { |
1323 } | 1457 if (configurator == NULL) |
1324 | 1458 { |
1325 void Update(Scene2D& scene, | 1459 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); |
1326 const CoordinateSystem3D& plane) | 1460 } |
1461 | |
1462 configurator_.reset(configurator); | |
1463 | |
1464 // Invalidate the layer | |
1465 lastPlane_.reset(NULL); | |
1466 } | |
1467 | |
1468 bool HasConfigurator() const | |
1469 { | |
1470 return configurator_.get() != NULL; | |
1471 } | |
1472 | |
1473 ILayerStyleConfigurator& GetConfigurator() const | |
1474 { | |
1475 if (configurator_.get() == NULL) | |
1476 { | |
1477 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
1478 } | |
1479 | |
1480 return *configurator_; | |
1481 } | |
1482 | |
1483 void Update(const CoordinateSystem3D& plane) | |
1327 { | 1484 { |
1328 assert(slicer_.get() != NULL); | 1485 assert(slicer_.get() != NULL); |
1329 std::auto_ptr<IVolumeSlicer::ExtractedSlice> slice(slicer_->ExtractSlice(plane)); | 1486 std::auto_ptr<IVolumeSlicer::ExtractedSlice> slice(slicer_->ExtractSlice(plane)); |
1330 | 1487 |
1331 if (slice.get() == NULL) | 1488 if (slice.get() == NULL) |
1334 } | 1491 } |
1335 | 1492 |
1336 if (!slice->IsValid()) | 1493 if (!slice->IsValid()) |
1337 { | 1494 { |
1338 // The slicer cannot handle this cutting plane: Clear the layer | 1495 // The slicer cannot handle this cutting plane: Clear the layer |
1339 scene.DeleteLayer(layerDepth_); | 1496 scene_.DeleteLayer(layerDepth_); |
1340 lastPlane_.reset(NULL); | 1497 lastPlane_.reset(NULL); |
1341 } | 1498 } |
1342 else if (lastPlane_.get() != NULL && | 1499 else if (lastPlane_.get() != NULL && |
1343 IsSameCuttingPlane(*lastPlane_, plane) && | 1500 IsSameCuttingPlane(*lastPlane_, plane) && |
1344 lastRevision_ == slice->GetRevision()) | 1501 lastRevision_ == slice->GetRevision()) |
1345 { | 1502 { |
1346 // The content of the slice has not changed: Do nothing | 1503 // The content of the slice has not changed: Don't update the |
1504 // layer content, but possibly update its style | |
1505 | |
1506 if (configurator_.get() != NULL && | |
1507 configurator_->GetRevision() != lastConfiguratorRevision_ && | |
1508 scene_.HasLayer(layerDepth_)) | |
1509 { | |
1510 configurator_->ApplyStyle(scene_.GetLayer(layerDepth_)); | |
1511 } | |
1347 } | 1512 } |
1348 else | 1513 else |
1349 { | 1514 { |
1350 // Content has changed: An update is needed | 1515 // Content has changed: An update is needed |
1351 lastPlane_.reset(new CoordinateSystem3D(plane)); | 1516 lastPlane_.reset(new CoordinateSystem3D(plane)); |
1352 lastRevision_ = slice->GetRevision(); | 1517 lastRevision_ = slice->GetRevision(); |
1353 | 1518 |
1354 std::auto_ptr<ISceneLayer> layer(slice->CreateSceneLayer(plane)); | 1519 std::auto_ptr<ISceneLayer> layer(slice->CreateSceneLayer(configurator_.get(), plane)); |
1355 if (layer.get() == NULL) | 1520 if (layer.get() == NULL) |
1356 { | 1521 { |
1357 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | 1522 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); |
1358 } | 1523 } |
1359 | 1524 |
1360 if (layer->GetType() == ISceneLayer::Type_ColorTexture || | 1525 if (configurator_.get() != NULL) |
1361 layer->GetType() == ISceneLayer::Type_FloatTexture) | 1526 { |
1362 { | 1527 lastConfiguratorRevision_ = configurator_->GetRevision(); |
1363 dynamic_cast<TextureBaseSceneLayer&>(*layer).SetLinearInterpolation(linearInterpolation_); | 1528 configurator_->ApplyStyle(*layer); |
1364 } | 1529 } |
1365 | 1530 |
1366 scene.SetLayer(layerDepth_, layer.release()); | 1531 scene_.SetLayer(layerDepth_, layer.release()); |
1367 } | 1532 } |
1368 } | 1533 } |
1369 }; | 1534 }; |
1370 | 1535 |
1371 | 1536 |
1493 plane = GetSamplePlane(*source2_); | 1658 plane = GetSamplePlane(*source2_); |
1494 } | 1659 } |
1495 | 1660 |
1496 if (source1_.get() != NULL) | 1661 if (source1_.get() != NULL) |
1497 { | 1662 { |
1498 source1_->Update(scene_, plane); | 1663 source1_->Update(plane); |
1499 } | 1664 } |
1500 | 1665 |
1501 if (source2_.get() != NULL) | 1666 if (source2_.get() != NULL) |
1502 { | 1667 { |
1503 source2_->Update(scene_, plane); | 1668 source2_->Update(plane); |
1504 } | 1669 } |
1505 | 1670 |
1506 scene_.FitContent(1024, 768); | 1671 scene_.FitContent(1024, 768); |
1507 | 1672 |
1508 { | 1673 { |
1594 (new OrthancStone::Callable | 1759 (new OrthancStone::Callable |
1595 <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle)); | 1760 <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle)); |
1596 } | 1761 } |
1597 | 1762 |
1598 void SetVolume1(int depth, | 1763 void SetVolume1(int depth, |
1599 OrthancStone::IVolumeSlicer* volume) | 1764 OrthancStone::IVolumeSlicer* volume, |
1600 { | 1765 OrthancStone::ILayerStyleConfigurator* style) |
1601 source1_.reset(new OrthancStone::VolumeSceneLayerSource(depth, volume)); | 1766 { |
1767 source1_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); | |
1768 | |
1769 if (style != NULL) | |
1770 { | |
1771 source1_->SetConfigurator(style); | |
1772 } | |
1602 } | 1773 } |
1603 | 1774 |
1604 void SetVolume2(int depth, | 1775 void SetVolume2(int depth, |
1605 OrthancStone::IVolumeSlicer* volume) | 1776 OrthancStone::IVolumeSlicer* volume, |
1606 { | 1777 OrthancStone::ILayerStyleConfigurator* style) |
1607 source2_.reset(new OrthancStone::VolumeSceneLayerSource(depth, volume)); | 1778 { |
1779 source2_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); | |
1780 | |
1781 if (style != NULL) | |
1782 { | |
1783 source2_->SetConfigurator(style); | |
1784 } | |
1608 } | 1785 } |
1609 }; | 1786 }; |
1610 | 1787 |
1611 | 1788 |
1612 void Run(OrthancStone::NativeApplicationContext& context, | 1789 void Run(OrthancStone::NativeApplicationContext& context, |
1711 // Delphine | 1888 // Delphine |
1712 //loader1->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT | 1889 //loader1->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT |
1713 //loader1->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5"); // Lung 1/10mm | 1890 //loader1->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5"); // Lung 1/10mm |
1714 | 1891 |
1715 | 1892 |
1716 toto->SetVolume2(1, new OrthancStone::OrthancMultiframeVolumeLoader::MPRSlicer(loader3)); | 1893 toto->SetVolume2(1, new OrthancStone::OrthancMultiframeVolumeLoader::MPRSlicer(loader3), |
1717 toto->SetVolume1(0, new OrthancStone::OrthancSeriesVolumeProgressiveLoader::MPRSlicer(loader1)); | 1894 new OrthancStone::LookupTableStyleConfigurator); |
1895 toto->SetVolume1(0, new OrthancStone::OrthancSeriesVolumeProgressiveLoader::MPRSlicer(loader1), | |
1896 new OrthancStone::GrayscaleStyleConfigurator); | |
1718 | 1897 |
1719 { | 1898 { |
1720 oracle.Start(); | 1899 oracle.Start(); |
1721 | 1900 |
1722 LOG(WARNING) << "...Waiting for Ctrl-C..."; | 1901 LOG(WARNING) << "...Waiting for Ctrl-C..."; |