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
|
|
5 *
|
|
6 * This program is free software: you can redistribute it and/or
|
|
7 * modify it under the terms of the GNU Affero General Public License
|
|
8 * as published by the Free Software Foundation, either version 3 of
|
|
9 * the License, or (at your option) any later version.
|
|
10 *
|
|
11 * This program is distributed in the hope that it will be useful, but
|
|
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
14 * Affero General Public License for more details.
|
|
15 *
|
|
16 * You should have received a copy of the GNU Affero General Public License
|
|
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
18 **/
|
|
19
|
|
20
|
|
21 #include "DicomPyramid.h"
|
|
22
|
|
23 #include "../DicomToolbox.h"
|
|
24 #include "../Orthanc/Core/Logging.h"
|
|
25 #include "../Orthanc/Core/OrthancException.h"
|
|
26
|
|
27 #include <algorithm>
|
|
28 #include <cassert>
|
|
29
|
|
30 namespace OrthancWSI
|
|
31 {
|
|
32 struct DicomPyramid::Comparator
|
|
33 {
|
|
34 bool operator() (DicomPyramidInstance* const& a,
|
|
35 DicomPyramidInstance* const& b) const
|
|
36 {
|
|
37 return a->GetTotalWidth() > b->GetTotalWidth();
|
|
38 }
|
|
39 };
|
|
40
|
|
41
|
|
42 void DicomPyramid::Clear()
|
|
43 {
|
|
44 for (size_t i = 0; i < levels_.size(); i++)
|
|
45 {
|
|
46 if (levels_[i] != NULL)
|
|
47 {
|
|
48 delete levels_[i];
|
|
49 }
|
|
50 }
|
|
51
|
|
52 for (size_t i = 0; i < instances_.size(); i++)
|
|
53 {
|
|
54 if (instances_[i] != NULL)
|
|
55 {
|
|
56 delete instances_[i];
|
|
57 }
|
|
58 }
|
|
59 }
|
|
60
|
|
61
|
|
62 void DicomPyramid::RegisterInstances(const std::string& seriesId)
|
|
63 {
|
|
64 Json::Value series;
|
|
65 IOrthancConnection::RestApiGet(series, orthanc_, "/series/" + seriesId);
|
|
66
|
|
67 if (series.type() != Json::objectValue)
|
|
68 {
|
|
69 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
|
|
70 }
|
|
71
|
|
72 const Json::Value& instances = DicomToolbox::GetSequenceTag(series, "Instances");
|
|
73 instances_.reserve(instances.size());
|
|
74
|
|
75 for (Json::Value::ArrayIndex i = 0; i < instances.size(); i++)
|
|
76 {
|
|
77 if (instances[i].type() != Json::stringValue)
|
|
78 {
|
|
79 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
|
|
80 }
|
|
81
|
|
82 std::string instance = instances[i].asString();
|
|
83
|
|
84 try
|
|
85 {
|
|
86 instances_.push_back(new DicomPyramidInstance(orthanc_, instance));
|
|
87 }
|
|
88 catch (Orthanc::OrthancException&)
|
|
89 {
|
|
90 LOG(ERROR) << "Skipping a DICOM instance that is not part of a whole-slide image: " << instance;
|
|
91 }
|
|
92 }
|
|
93 }
|
|
94
|
|
95
|
|
96 void DicomPyramid::Check(const std::string& seriesId) const
|
|
97 {
|
|
98 if (instances_.empty())
|
|
99 {
|
|
100 LOG(ERROR) << "This series does not contain a whole-slide image: " << seriesId;
|
|
101 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
|
|
102 }
|
|
103
|
|
104 const DicomPyramidInstance& a = *instances_[0];
|
|
105
|
|
106 for (size_t i = 1; i < instances_.size(); i++)
|
|
107 {
|
|
108 const DicomPyramidInstance& b = *instances_[i];
|
|
109
|
|
110 if (a.GetImageCompression() != b.GetImageCompression() ||
|
|
111 a.GetPixelFormat() != b.GetPixelFormat() ||
|
|
112 a.GetTileWidth() != b.GetTileWidth() ||
|
|
113 a.GetTileHeight() != b.GetTileHeight() ||
|
|
114 a.GetTotalWidth() < b.GetTotalWidth() ||
|
|
115 a.GetTotalHeight() < b.GetTotalHeight())
|
|
116 {
|
|
117 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
|
|
118 }
|
|
119
|
|
120 if (a.GetTotalWidth() == b.GetTotalWidth() &&
|
|
121 a.GetTotalHeight() != b.GetTotalHeight())
|
|
122 {
|
|
123 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
|
|
124 }
|
|
125 }
|
|
126 }
|
|
127
|
|
128
|
|
129 void DicomPyramid::CheckLevel(size_t level) const
|
|
130 {
|
|
131 if (level >= levels_.size())
|
|
132 {
|
|
133 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
134 }
|
|
135 }
|
|
136
|
|
137
|
|
138 DicomPyramid::DicomPyramid(IOrthancConnection& orthanc,
|
|
139 const std::string& seriesId) :
|
|
140 orthanc_(orthanc),
|
|
141 seriesId_(seriesId)
|
|
142 {
|
|
143 RegisterInstances(seriesId);
|
|
144
|
|
145 // Sort the instances of the pyramid by decreasing total widths
|
|
146 std::sort(instances_.begin(), instances_.end(), Comparator());
|
|
147
|
|
148 try
|
|
149 {
|
|
150 Check(seriesId);
|
|
151 }
|
|
152 catch (Orthanc::OrthancException&)
|
|
153 {
|
|
154 Clear();
|
|
155 throw;
|
|
156 }
|
|
157
|
|
158 for (size_t i = 0; i < instances_.size(); i++)
|
|
159 {
|
|
160 if (i == 0 ||
|
|
161 instances_[i - 1]->GetTotalWidth() != instances_[i]->GetTotalWidth())
|
|
162 {
|
|
163 levels_.push_back(new DicomPyramidLevel(*instances_[i]));
|
|
164 }
|
|
165 else
|
|
166 {
|
|
167 assert(levels_.back() != NULL);
|
|
168 levels_.back()->AddInstance(*instances_[i]);
|
|
169 }
|
|
170 }
|
|
171 }
|
|
172
|
|
173
|
|
174 unsigned int DicomPyramid::GetLevelWidth(unsigned int level) const
|
|
175 {
|
|
176 CheckLevel(level);
|
|
177 return levels_[level]->GetTotalWidth();
|
|
178 }
|
|
179
|
|
180
|
|
181 unsigned int DicomPyramid::GetLevelHeight(unsigned int level) const
|
|
182 {
|
|
183 CheckLevel(level);
|
|
184 return levels_[level]->GetTotalHeight();
|
|
185 }
|
|
186
|
|
187
|
|
188 unsigned int DicomPyramid::GetTileWidth() const
|
|
189 {
|
|
190 assert(!levels_.empty() && levels_[0] != NULL);
|
|
191 return levels_[0]->GetTileWidth();
|
|
192 }
|
|
193
|
|
194
|
|
195 unsigned int DicomPyramid::GetTileHeight() const
|
|
196 {
|
|
197 assert(!levels_.empty() && levels_[0] != NULL);
|
|
198 return levels_[0]->GetTileHeight();
|
|
199 }
|
|
200
|
|
201
|
|
202 bool DicomPyramid::ReadRawTile(std::string& tile,
|
|
203 unsigned int level,
|
|
204 unsigned int tileX,
|
|
205 unsigned int tileY)
|
|
206 {
|
|
207 CheckLevel(level);
|
|
208
|
|
209 ImageCompression compression;
|
|
210 Orthanc::PixelFormat format;
|
|
211
|
|
212 if (levels_[level]->DownloadRawTile(compression, format, tile, orthanc_, tileX, tileY))
|
|
213 {
|
|
214 assert(compression == GetImageCompression() &&
|
|
215 format == GetPixelFormat());
|
|
216 return true;
|
|
217 }
|
|
218 else
|
|
219 {
|
|
220 return false;
|
|
221 }
|
|
222 }
|
|
223
|
|
224
|
|
225 ImageCompression DicomPyramid::GetImageCompression() const
|
|
226 {
|
|
227 assert(!instances_.empty() && instances_[0] != NULL);
|
|
228 return instances_[0]->GetImageCompression();
|
|
229 }
|
|
230
|
|
231
|
|
232 Orthanc::PixelFormat DicomPyramid::GetPixelFormat() const
|
|
233 {
|
|
234 assert(!instances_.empty() && instances_[0] != NULL);
|
|
235 return instances_[0]->GetPixelFormat();
|
|
236 }
|
|
237 }
|