Mercurial > hg > orthanc-wsi
annotate Framework/Inputs/DicomPyramid.cpp @ 334:f011fc199b6a default tip
back to mainline
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 18 Oct 2024 15:44:18 +0200 |
parents | c42083d50ddf |
children |
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 { |
330
c42083d50ddf
Added support for DICOM tag "Recommended Absent Pixel CIELab" (0048,0015)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
312
diff
changeset
|
106 if (instance->HasBackgroundColor()) |
c42083d50ddf
Added support for DICOM tag "Recommended Absent Pixel CIELab" (0048,0015)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
312
diff
changeset
|
107 { |
c42083d50ddf
Added support for DICOM tag "Recommended Absent Pixel CIELab" (0048,0015)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
312
diff
changeset
|
108 backgroundRed_ = instance->GetBackgroundRed(); |
c42083d50ddf
Added support for DICOM tag "Recommended Absent Pixel CIELab" (0048,0015)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
312
diff
changeset
|
109 backgroundGreen_ = instance->GetBackgroundGreen(); |
c42083d50ddf
Added support for DICOM tag "Recommended Absent Pixel CIELab" (0048,0015)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
312
diff
changeset
|
110 backgroundBlue_ = instance->GetBackgroundBlue(); |
c42083d50ddf
Added support for DICOM tag "Recommended Absent Pixel CIELab" (0048,0015)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
312
diff
changeset
|
111 } |
c42083d50ddf
Added support for DICOM tag "Recommended Absent Pixel CIELab" (0048,0015)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
312
diff
changeset
|
112 |
219
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
113 instances_.push_back(instance.release()); |
330
c42083d50ddf
Added support for DICOM tag "Recommended Absent Pixel CIELab" (0048,0015)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
312
diff
changeset
|
114 } |
0 | 115 } |
116 catch (Orthanc::OrthancException&) | |
117 { | |
219
ef3f8c5126a4
Don't display the thumbnail/overview instances in the Web viewer
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
217
diff
changeset
|
118 LOG(ERROR) << "Skipping a DICOM instance that is not part of a whole-slide image: " << instanceId; |
0 | 119 } |
120 } | |
121 } | |
122 | |
123 | |
124 void DicomPyramid::Check(const std::string& seriesId) const | |
125 { | |
126 if (instances_.empty()) | |
127 { | |
128 LOG(ERROR) << "This series does not contain a whole-slide image: " << seriesId; | |
129 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); | |
130 } | |
131 | |
132 const DicomPyramidInstance& a = *instances_[0]; | |
133 | |
134 for (size_t i = 1; i < instances_.size(); i++) | |
135 { | |
136 const DicomPyramidInstance& b = *instances_[i]; | |
137 | |
56
83cd735c885d
speedup the loading of DICOM sources
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
16
diff
changeset
|
138 if (a.GetPixelFormat() != b.GetPixelFormat() || |
0 | 139 a.GetTotalWidth() < b.GetTotalWidth() || |
140 a.GetTotalHeight() < b.GetTotalHeight()) | |
141 { | |
142 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
143 } | |
144 | |
145 if (a.GetTotalWidth() == b.GetTotalWidth() && | |
146 a.GetTotalHeight() != b.GetTotalHeight()) | |
147 { | |
148 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
149 } | |
150 } | |
151 } | |
152 | |
153 | |
154 void DicomPyramid::CheckLevel(size_t level) const | |
155 { | |
156 if (level >= levels_.size()) | |
157 { | |
158 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
159 } | |
160 } | |
161 | |
162 | |
196
b0bd22077cd8
sharing code with orthanc-stone
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
192
diff
changeset
|
163 DicomPyramid::DicomPyramid(OrthancStone::IOrthancConnection& orthanc, |
69
d529d9ce3c7e
cache for DicomPyramidInstance
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
62
diff
changeset
|
164 const std::string& seriesId, |
d529d9ce3c7e
cache for DicomPyramidInstance
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
62
diff
changeset
|
165 bool useCache) : |
0 | 166 orthanc_(orthanc), |
330
c42083d50ddf
Added support for DICOM tag "Recommended Absent Pixel CIELab" (0048,0015)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
312
diff
changeset
|
167 seriesId_(seriesId), |
c42083d50ddf
Added support for DICOM tag "Recommended Absent Pixel CIELab" (0048,0015)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
312
diff
changeset
|
168 backgroundRed_(255), |
c42083d50ddf
Added support for DICOM tag "Recommended Absent Pixel CIELab" (0048,0015)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
312
diff
changeset
|
169 backgroundGreen_(255), |
c42083d50ddf
Added support for DICOM tag "Recommended Absent Pixel CIELab" (0048,0015)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
312
diff
changeset
|
170 backgroundBlue_(255) |
0 | 171 { |
69
d529d9ce3c7e
cache for DicomPyramidInstance
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
62
diff
changeset
|
172 RegisterInstances(seriesId, useCache); |
0 | 173 |
174 // Sort the instances of the pyramid by decreasing total widths | |
175 std::sort(instances_.begin(), instances_.end(), Comparator()); | |
176 | |
177 try | |
178 { | |
179 Check(seriesId); | |
180 } | |
181 catch (Orthanc::OrthancException&) | |
182 { | |
183 Clear(); | |
184 throw; | |
185 } | |
186 | |
187 for (size_t i = 0; i < instances_.size(); i++) | |
188 { | |
189 if (i == 0 || | |
190 instances_[i - 1]->GetTotalWidth() != instances_[i]->GetTotalWidth()) | |
191 { | |
192 levels_.push_back(new DicomPyramidLevel(*instances_[i])); | |
193 } | |
194 else | |
195 { | |
196 assert(levels_.back() != NULL); | |
197 levels_.back()->AddInstance(*instances_[i]); | |
198 } | |
199 } | |
200 } | |
201 | |
202 | |
203 unsigned int DicomPyramid::GetLevelWidth(unsigned int level) const | |
204 { | |
205 CheckLevel(level); | |
206 return levels_[level]->GetTotalWidth(); | |
207 } | |
208 | |
209 | |
210 unsigned int DicomPyramid::GetLevelHeight(unsigned int level) const | |
211 { | |
212 CheckLevel(level); | |
213 return levels_[level]->GetTotalHeight(); | |
214 } | |
215 | |
216 | |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
217 unsigned int DicomPyramid::GetTileWidth(unsigned int level) const |
0 | 218 { |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
219 CheckLevel(level); |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
220 return levels_[level]->GetTileWidth(); |
0 | 221 } |
222 | |
223 | |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
224 unsigned int DicomPyramid::GetTileHeight(unsigned int level) const |
0 | 225 { |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
226 CheckLevel(level); |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
227 return levels_[level]->GetTileHeight(); |
0 | 228 } |
229 | |
230 | |
231 bool DicomPyramid::ReadRawTile(std::string& tile, | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
232 ImageCompression& compression, |
0 | 233 unsigned int level, |
234 unsigned int tileX, | |
235 unsigned int tileY) | |
236 { | |
237 CheckLevel(level); | |
238 | |
239 Orthanc::PixelFormat format; | |
240 | |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
241 if (levels_[level]->DownloadRawTile(tile, format, compression, orthanc_, tileX, tileY)) |
0 | 242 { |
57
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
243 if (format != GetPixelFormat()) |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
244 { |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
245 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
246 } |
91fc9583b2de
big refactoring to support sparse tiling
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
247 |
0 | 248 return true; |
249 } | |
250 else | |
251 { | |
252 return false; | |
253 } | |
254 } | |
255 | |
256 | |
257 Orthanc::PixelFormat DicomPyramid::GetPixelFormat() const | |
258 { | |
259 assert(!instances_.empty() && instances_[0] != NULL); | |
260 return instances_[0]->GetPixelFormat(); | |
261 } | |
166
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
262 |
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
263 |
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
264 Orthanc::PhotometricInterpretation DicomPyramid::GetPhotometricInterpretation() const |
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
265 { |
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
266 assert(!instances_.empty() && instances_[0] != NULL); |
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
267 return instances_[0]->GetPhotometricInterpretation(); |
f0dac1e8f736
access to photometric interpretation of source pyramids
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
157
diff
changeset
|
268 } |
0 | 269 } |