comparison Framework/Algorithms/PyramidReader.cpp @ 0:4a7a53257c7d

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sat, 22 Oct 2016 21:48:33 +0200
parents
children 7a88c614be04
comparison
equal deleted inserted replaced
-1:000000000000 0:4a7a53257c7d
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 "PyramidReader.h"
22
23 #include "../ImageToolbox.h"
24 #include "../Orthanc/Core/Logging.h"
25 #include "../Orthanc/Core/OrthancException.h"
26
27 #include <cassert>
28
29 namespace OrthancWSI
30 {
31 class PyramidReader::SourceTile : public boost::noncopyable
32 {
33 private:
34 PyramidReader& that_;
35 unsigned int tileX_;
36 unsigned int tileY_;
37 bool hasRawTile_;
38 std::string rawTile_;
39
40 std::auto_ptr<Orthanc::ImageAccessor> decoded_;
41
42 bool IsRepaintNeeded() const
43 {
44 return (that_.parameters_.IsRepaintBackground() &&
45 ((tileX_ + 1) * that_.sourceTileWidth_ > that_.levelWidth_ ||
46 (tileY_ + 1) * that_.sourceTileHeight_ > that_.levelHeight_));
47 }
48
49 void RepaintBackground()
50 {
51 assert(decoded_.get() != NULL);
52 LOG(INFO) << "Repainting background of tile ("
53 << tileX_ << "," << tileY_ << ") at level " << that_.level_;
54
55 if ((tileY_ + 1) * that_.sourceTileHeight_ > that_.levelHeight_)
56 {
57 // Bottom overflow
58 assert(tileY_ * that_.sourceTileHeight_ < that_.levelHeight_);
59
60 unsigned int bottom = that_.levelHeight_ - tileY_ * that_.sourceTileHeight_;
61 Orthanc::ImageAccessor a = decoded_->GetRegion(0, bottom,
62 that_.sourceTileWidth_,
63 that_.sourceTileHeight_ - bottom);
64 ImageToolbox::Set(a,
65 that_.parameters_.GetBackgroundColorRed(),
66 that_.parameters_.GetBackgroundColorGreen(),
67 that_.parameters_.GetBackgroundColorBlue());
68
69 }
70
71 if ((tileX_ + 1) * that_.sourceTileWidth_ > that_.levelWidth_)
72 {
73 // Right overflow
74 assert(tileX_ * that_.sourceTileWidth_ < that_.levelWidth_);
75
76 unsigned int right = that_.levelWidth_ - tileX_ * that_.sourceTileWidth_;
77 Orthanc::ImageAccessor a = decoded_->GetRegion(right, 0,
78 that_.sourceTileWidth_ - right,
79 that_.sourceTileHeight_);
80 ImageToolbox::Set(a,
81 that_.parameters_.GetBackgroundColorRed(),
82 that_.parameters_.GetBackgroundColorGreen(),
83 that_.parameters_.GetBackgroundColorBlue());
84 }
85 }
86
87
88 public:
89 SourceTile(PyramidReader& that,
90 unsigned int tileX,
91 unsigned int tileY) :
92 that_(that),
93 tileX_(tileX),
94 tileY_(tileY)
95 {
96 if (!that_.parameters_.IsForceReencode() &&
97 !IsRepaintNeeded() &&
98 that_.source_.ReadRawTile(rawTile_, that_.level_, tileX, tileY))
99 {
100 hasRawTile_ = true;
101 }
102 else
103 {
104 hasRawTile_ = false;
105 decoded_.reset(that_.source_.DecodeTile(that_.level_, tileX, tileY));
106 if (decoded_.get() == NULL)
107 {
108 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
109 }
110
111 RepaintBackground();
112 }
113 }
114
115 bool HasRawTile() const
116 {
117 return hasRawTile_;
118 }
119
120 const std::string& GetRawTile() const
121 {
122 if (hasRawTile_)
123 {
124 return rawTile_;
125 }
126 else
127 {
128 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
129 }
130 }
131
132 const Orthanc::ImageAccessor& GetDecodedTile()
133 {
134 if (decoded_.get() == NULL)
135 {
136 if (!hasRawTile_)
137 {
138 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
139 }
140
141 decoded_.reset(ImageToolbox::DecodeTile(rawTile_, that_.source_.GetImageCompression()));
142 if (decoded_.get() == NULL)
143 {
144 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
145 }
146
147 RepaintBackground();
148 }
149
150 return *decoded_;
151 }
152 };
153
154
155 Orthanc::ImageAccessor& PyramidReader::GetOutsideTile()
156 {
157 if (outside_.get() == NULL)
158 {
159 outside_.reset(ImageToolbox::Allocate(source_.GetPixelFormat(), targetTileWidth_, targetTileHeight_));
160 ImageToolbox::Set(*outside_,
161 parameters_.GetBackgroundColorRed(),
162 parameters_.GetBackgroundColorGreen(),
163 parameters_.GetBackgroundColorBlue());
164 }
165
166 return *outside_;
167 }
168
169
170 void PyramidReader::CheckTileSize(const Orthanc::ImageAccessor& tile) const
171 {
172 if (tile.GetWidth() != sourceTileWidth_ ||
173 tile.GetHeight() != sourceTileHeight_)
174 {
175 LOG(ERROR) << "One tile in the input image has size " << tile.GetWidth() << "x" << tile.GetHeight()
176 << " instead of required " << source_.GetTileWidth() << "x" << source_.GetTileHeight();
177 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
178 }
179 }
180
181
182 void PyramidReader::CheckTileSize(const std::string& tile) const
183 {
184 if (parameters_.IsSafetyCheck())
185 {
186 std::auto_ptr<Orthanc::ImageAccessor> decoded(ImageToolbox::DecodeTile(tile, source_.GetImageCompression()));
187 CheckTileSize(*decoded);
188 }
189 }
190
191
192 PyramidReader::SourceTile& PyramidReader::AccessSourceTile(const Location& location)
193 {
194 Cache::iterator found = cache_.find(location);
195 if (found != cache_.end())
196 {
197 return *found->second;
198 }
199 else
200 {
201 SourceTile *tile = new SourceTile(*this, location.first, location.second);
202 cache_[location] = tile;
203 return *tile;
204 }
205 }
206
207
208 PyramidReader::Location PyramidReader::MapTargetToSourceLocation(unsigned int tileX,
209 unsigned int tileY)
210 {
211 return std::make_pair(tileX / (sourceTileWidth_ / targetTileWidth_),
212 tileY / (sourceTileHeight_ / targetTileHeight_));
213 }
214
215
216 PyramidReader::PyramidReader(ITiledPyramid& source,
217 unsigned int level,
218 unsigned int targetTileWidth,
219 unsigned int targetTileHeight,
220 const DicomizerParameters& parameters) :
221 source_(source),
222 level_(level),
223 levelWidth_(source.GetLevelWidth(level)),
224 levelHeight_(source.GetLevelHeight(level)),
225 sourceTileWidth_(source.GetTileWidth()),
226 sourceTileHeight_(source.GetTileHeight()),
227 targetTileWidth_(targetTileWidth),
228 targetTileHeight_(targetTileHeight),
229 parameters_(parameters)
230 {
231 if (sourceTileWidth_ % targetTileWidth_ != 0 ||
232 sourceTileHeight_ % targetTileHeight_ != 0)
233 {
234 LOG(ERROR) << "When resampling the tile size, it must be a integer divisor of the original tile size";
235 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
236 }
237 }
238
239
240 PyramidReader::~PyramidReader()
241 {
242 for (Cache::iterator it = cache_.begin(); it != cache_.end(); ++it)
243 {
244 assert(it->second != NULL);
245 delete it->second;
246 }
247 }
248
249
250 const std::string* PyramidReader::GetRawTile(unsigned int tileX,
251 unsigned int tileY)
252 {
253 if (sourceTileWidth_ != targetTileWidth_ ||
254 sourceTileHeight_ != targetTileHeight_)
255 {
256 return NULL;
257 }
258
259 SourceTile& source = AccessSourceTile(MapTargetToSourceLocation(tileX, tileY));
260 if (source.HasRawTile())
261 {
262 CheckTileSize(source.GetRawTile());
263 return &source.GetRawTile();
264 }
265 else
266 {
267 return NULL;
268 }
269 }
270
271
272 Orthanc::ImageAccessor PyramidReader::GetDecodedTile(unsigned int tileX,
273 unsigned int tileY)
274 {
275 if (tileX * targetTileWidth_ >= levelWidth_ ||
276 tileY * targetTileHeight_ >= levelHeight_)
277 {
278 // Accessing a tile out of the source image
279 return GetOutsideTile();
280 }
281
282 SourceTile& source = AccessSourceTile(MapTargetToSourceLocation(tileX, tileY));
283 const Orthanc::ImageAccessor& tile = source.GetDecodedTile();
284
285 CheckTileSize(tile);
286
287 assert(sourceTileWidth_ % targetTileWidth_ == 0 &&
288 sourceTileHeight_ % targetTileHeight_ == 0);
289
290 unsigned int xx = tileX % (sourceTileWidth_ / targetTileWidth_);
291 unsigned int yy = tileY % (sourceTileHeight_ / targetTileHeight_);
292
293 const uint8_t* bytes =
294 reinterpret_cast<const uint8_t*>(tile.GetConstRow(yy * targetTileHeight_)) +
295 GetBytesPerPixel(tile.GetFormat()) * xx * targetTileWidth_;
296
297 Orthanc::ImageAccessor region;
298 region.AssignReadOnly(tile.GetFormat(),
299 targetTileWidth_,
300 targetTileHeight_,
301 tile.GetPitch(), bytes);
302
303 return region;
304 }
305 }