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...";