Mercurial > hg > orthanc-wsi
annotate Framework/Inputs/DicomPyramid.cpp @ 318:8ad12abde290
sparse re-encoding with OpenSlide (notably for MIRAX format)
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 11 Sep 2024 16:11:16 +0200 |
parents | 0683312e21ba |
children | c42083d50ddf |
rev | line source |
---|---|
0 | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
312
0683312e21ba
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
309
diff
changeset
|
5 * Copyright (C) 2017-2023 Osimis S.A., Belgium |
0683312e21ba
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
309
diff
changeset
|
6 * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium |
309
7020852a8fa9
updated year to 2024
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
267
diff
changeset
|
7 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium |
0 | 8 * |
9 * This program is free software: you can redistribute it and/or | |
10 * modify it under the terms of the GNU Affero General Public License | |
11 * as published by the Free Software Foundation, either version 3 of | |
12 * the License, or (at your option) any later version. | |
13 * | |
14 * This program is distributed in the hope that it will be useful, but | |
15 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 * Affero General Public License for more details. | |
18 * | |
19 * You should have received a copy of the GNU Affero General Public License | |
20 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
21 **/ | |
22 | |
23 | |
16
7a88c614be04
preparing for precompiled headers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
24 #include "../PrecompiledHeadersWSI.h" |
0 | 25 #include "DicomPyramid.h" |
26 | |
27 #include "../DicomToolbox.h" | |
219
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
28 |
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
29 #include <Compatibility.h> |
192 | 30 #include <Logging.h> |
31 #include <OrthancException.h> | |
219
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
32 #include <Toolbox.h> |
0 | 33 |
34 #include <algorithm> | |
35 #include <cassert> | |
36 | |
37 namespace OrthancWSI | |
38 { | |
39 struct DicomPyramid::Comparator | |
40 { | |
41 bool operator() (DicomPyramidInstance* const& a, | |
42 DicomPyramidInstance* const& b) const | |
43 { | |
44 return a->GetTotalWidth() > b->GetTotalWidth(); | |
45 } | |
46 }; | |
47 | |
48 | |
49 void DicomPyramid::Clear() | |
50 { | |
51 for (size_t i = 0; i < levels_.size(); i++) | |
52 { | |
53 if (levels_[i] != NULL) | |
54 { | |
55 delete levels_[i]; | |
56 } | |
57 } | |
58 | |
59 for (size_t i = 0; i < instances_.size(); i++) | |
60 { | |
61 if (instances_[i] != NULL) | |
62 { | |
63 delete instances_[i]; | |
64 } | |
65 } | |
66 } | |
67 | |
68 | |
69
d529d9ce3c7e
cache for DicomPyramidInstance
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
62
diff
changeset
|
69 void DicomPyramid::RegisterInstances(const std::string& seriesId, |
d529d9ce3c7e
cache for DicomPyramidInstance
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
62
diff
changeset
|
70 bool useCache) |
0 | 71 { |
72 Json::Value series; | |
196
b0bd22077cd8
sharing code with orthanc-stone
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
192
diff
changeset
|
73 OrthancStone::IOrthancConnection::RestApiGet(series, orthanc_, "/series/" + seriesId); |
0 | 74 |
62
f45cec2c32e2
Speed-up in the Web viewer plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
61
diff
changeset
|
75 if (series.type() != Json::objectValue || |
f45cec2c32e2
Speed-up in the Web viewer plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
61
diff
changeset
|
76 !series.isMember("Instances") || |
f45cec2c32e2
Speed-up in the Web viewer plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
61
diff
changeset
|
77 series["Instances"].type() != Json::arrayValue) |
0 | 78 { |
79 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
80 } | |
81 | |
62
f45cec2c32e2
Speed-up in the Web viewer plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
61
diff
changeset
|
82 const Json::Value& instances = series["Instances"]; |
0 | 83 instances_.reserve(instances.size()); |
84 | |
85 for (Json::Value::ArrayIndex i = 0; i < instances.size(); i++) | |
86 { | |
87 if (instances[i].type() != Json::stringValue) | |
88 { | |
89 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
90 } | |
91 | |
219
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
92 std::string instanceId = instances[i].asString(); |
0 | 93 |
94 try | |
95 { | |
219
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
96 std::unique_ptr<DicomPyramidInstance> instance(new DicomPyramidInstance(orthanc_, instanceId, useCache)); |
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
97 |
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
98 std::vector<std::string> tokens; |
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
99 Orthanc::Toolbox::TokenizeString(tokens, instance->GetImageType(), '\\'); |
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
100 |
224 | 101 // Don't consider the thumbnail and overview as part of the DICOM pyramid (new in 1.0) |
219
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
102 if (tokens.size() < 2 || |
267 | 103 (tokens[1] != "THUMBNAIL" && |
104 tokens[1] != "OVERVIEW")) | |
219
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
105 { |
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
106 instances_.push_back(instance.release()); |
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
107 } |
0 | 108 } |
109 catch (Orthanc::OrthancException&) | |
110 { | |
219
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
111 LOG(ERROR) << "Skipping a DICOM instance that is not part of a whole-slide image: " << instanceId; |
0 | 112 } |
113 } | |
114 } | |
115 | |
116 | |
117 void DicomPyramid::Check(const std::string& seriesId) const | |
118 { | |
119 if (instances_.empty()) | |
120 { | |
121 LOG(ERROR) << "This series does not contain a whole-slide image: " << seriesId; | |
122 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); | |
123 } | |
124 | |
125 const DicomPyramidInstance& a = *instances_[0]; | |
126 | |
127 for (size_t i = 1; i < instances_.size(); i++) | |
128 { | |
129 const DicomPyramidInstance& b = *instances_[i]; | |
130 | |
56
83cd735c885d
speedup the loading of DICOM sources
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
16
diff
changeset
|
131 if (a.GetPixelFormat() != b.GetPixelFormat() || |
0 | 132 a.GetTotalWidth() < b.GetTotalWidth() || |
133 a.GetTotalHeight() < b.GetTotalHeight()) | |
134 { | |
135 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
136 } | |
137 | |
138 if (a.GetTotalWidth() == b.GetTotalWidth() && | |
139 a.GetTotalHeight() != b.GetTotalHeight()) | |
140 { | |
141 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
142 } | |
143 } | |
144 } | |
145 | |
146 | |
147 void DicomPyramid::CheckLevel(size_t level) const | |
148 { | |
149 if (level >= levels_.size()) | |
150 { | |
151 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
152 } | |
153 } | |
154 | |
155 | |
196
b0bd22077cd8
sharing code with orthanc-stone
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
192
diff
changeset
|
156 DicomPyramid::DicomPyramid(OrthancStone::IOrthancConnection& orthanc, |
69
d529d9ce3c7e
cache for DicomPyramidInstance
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
62
diff
changeset
|
157 const std::string& seriesId, |
d529d9ce3c7e
cache for DicomPyramidInstance
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
62
diff
changeset
|
158 bool useCache) : |
0 | 159 orthanc_(orthanc), |
160 seriesId_(seriesId) | |
161 { | |
69
d529d9ce3c7e
cache for DicomPyramidInstance
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
62
diff
changeset
|
162 RegisterInstances(seriesId, useCache); |
0 | 163 |
164 // Sort the instances of the pyramid by decreasing total widths | |
165 std::sort(instances_.begin(), instances_.end(), Comparator()); | |
166 | |
167 try | |
168 { | |
169 Check(seriesId); | |
170 } | |
171 catch (Orthanc::OrthancException&) | |
172 { | |
173 Clear(); | |
174 throw; | |
175 } | |
176 | |
177 for (size_t i = 0; i < instances_.size(); i++) | |
178 { | |
179 if (i == 0 || | |
180 instances_[i - 1]->GetTotalWidth() != instances_[i]->GetTotalWidth()) | |
181 { | |
182 levels_.push_back(new DicomPyramidLevel(*instances_[i])); | |
183 } | |
184 else | |
185 { | |
186 assert(levels_.back() != NULL); | |
187 levels_.back()->AddInstance(*instances_[i]); | |
188 } | |
189 } | |
190 } | |
191 | |
192 | |
193 unsigned int DicomPyramid::GetLevelWidth(unsigned int level) const | |
194 { | |
195 CheckLevel(level); | |
196 return levels_[level]->GetTotalWidth(); | |
197 } | |
198 | |
199 | |
200 unsigned int DicomPyramid::GetLevelHeight(unsigned int level) const | |
201 { | |
202 CheckLevel(level); | |
203 return levels_[level]->GetTotalHeight(); | |
204 } | |
205 | |
206 | |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
207 unsigned int DicomPyramid::GetTileWidth(unsigned int level) const |
0 | 208 { |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
209 CheckLevel(level); |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
210 return levels_[level]->GetTileWidth(); |
0 | 211 } |
212 | |
213 | |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
214 unsigned int DicomPyramid::GetTileHeight(unsigned int level) const |
0 | 215 { |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
216 CheckLevel(level); |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
217 return levels_[level]->GetTileHeight(); |
0 | 218 } |
219 | |
220 | |
221 bool DicomPyramid::ReadRawTile(std::string& tile, | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
222 ImageCompression& compression, |
0 | 223 unsigned int level, |
224 unsigned int tileX, | |
225 unsigned int tileY) | |
226 { | |
227 CheckLevel(level); | |
228 | |
229 Orthanc::PixelFormat format; | |
230 | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
231 if (levels_[level]->DownloadRawTile(tile, format, compression, orthanc_, tileX, tileY)) |
0 | 232 { |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
233 if (format != GetPixelFormat()) |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
234 { |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
235 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
236 } |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
237 |
0 | 238 return true; |
239 } | |
240 else | |
241 { | |
242 return false; | |
243 } | |
244 } | |
245 | |
246 | |
247 Orthanc::PixelFormat DicomPyramid::GetPixelFormat() const | |
248 { | |
249 assert(!instances_.empty() && instances_[0] != NULL); | |
250 return instances_[0]->GetPixelFormat(); | |
251 } | |
166
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
252 |
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
253 |
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
254 Orthanc::PhotometricInterpretation DicomPyramid::GetPhotometricInterpretation() const |
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
255 { |
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
256 assert(!instances_.empty() && instances_[0] != NULL); |
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
257 return instances_[0]->GetPhotometricInterpretation(); |
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
258 } |
0 | 259 } |