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 "OpenSlideLibrary.h"
|
|
22
|
|
23 #include "../Orthanc/Core/Logging.h"
|
|
24 #include "../Orthanc/Core/Images/Image.h"
|
|
25
|
|
26 namespace OrthancWSI
|
|
27 {
|
|
28 static std::auto_ptr<OpenSlideLibrary> globalLibrary_;
|
|
29
|
|
30
|
|
31 OpenSlideLibrary::OpenSlideLibrary(const std::string& path) :
|
|
32 library_(path)
|
|
33 {
|
|
34 close_ = (FunctionClose) library_.GetFunction("openslide_close");
|
|
35 getLevelCount_ = (FunctionGetLevelCount) library_.GetFunction("openslide_get_level_count");
|
|
36 getLevelDimensions_ = (FunctionGetLevelDimensions) library_.GetFunction("openslide_get_level_dimensions");
|
|
37 getLevelDownsample_ = (FunctionGetLevelDownsample) library_.GetFunction("openslide_get_level_downsample");
|
|
38 open_ = (FunctionOpen) library_.GetFunction("openslide_open");
|
|
39 readRegion_ = (FunctionReadRegion) library_.GetFunction("openslide_read_region");
|
|
40 }
|
|
41
|
|
42
|
|
43 OpenSlideLibrary::Image::Level::Level() :
|
|
44 width_(0),
|
|
45 height_(0),
|
|
46 downsample_(1)
|
|
47 {
|
|
48 }
|
|
49
|
|
50
|
|
51 OpenSlideLibrary::Image::Level::Level(int64_t width,
|
|
52 int64_t height,
|
|
53 double downsample) :
|
|
54 width_(static_cast<unsigned int>(width)),
|
|
55 height_(static_cast<unsigned int>(height)),
|
|
56 downsample_(downsample)
|
|
57 {
|
|
58 if (width < 0 ||
|
|
59 height < 0 ||
|
|
60 downsample <= 0)
|
|
61 {
|
|
62 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
|
|
63 }
|
|
64
|
|
65 if (static_cast<int64_t>(width_) != width ||
|
|
66 static_cast<int64_t>(height_) != height)
|
|
67 {
|
|
68 LOG(ERROR) << "The whole-slide image is too large";
|
|
69 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
|
|
70 }
|
|
71 }
|
|
72
|
|
73
|
|
74 void OpenSlideLibrary::Image::Initialize(const std::string& path)
|
|
75 {
|
|
76 handle_ = that_.open_(path.c_str());
|
|
77 if (handle_ == NULL)
|
|
78 {
|
|
79 LOG(ERROR) << "Cannot open an image with OpenSlide: " << path;
|
|
80 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
|
|
81 }
|
|
82
|
|
83 try
|
|
84 {
|
|
85 LOG(INFO) << "Opening an image with OpenSlide: " << path;
|
|
86
|
|
87 int32_t tmp = that_.getLevelCount_(handle_);
|
|
88 if (tmp <= 0)
|
|
89 {
|
|
90 LOG(ERROR) << "Image with no pyramid level";
|
|
91 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
|
|
92 }
|
|
93
|
|
94 levels_.resize(tmp);
|
|
95
|
|
96 for (int32_t level = 0; level < tmp; level++)
|
|
97 {
|
|
98 int64_t width, height;
|
|
99 that_.getLevelDimensions_(handle_, level, &width, &height);
|
|
100
|
|
101 double downsample = that_.getLevelDownsample_(handle_, level);
|
|
102
|
|
103 levels_[level] = Level(width, height, downsample);
|
|
104 }
|
|
105
|
|
106 for (size_t i = 1; i < levels_.size(); i++)
|
|
107 {
|
|
108 if (levels_[i].width_ >= levels_[i - 1].width_ ||
|
|
109 levels_[i].height_ >= levels_[i - 1].height_)
|
|
110 {
|
|
111 // This is not a pyramid with levels of decreasing sizes
|
|
112 // (level "0" must be the finest level)
|
|
113 LOG(ERROR) << "The pyramid does not have levels of strictly decreasing sizes";
|
|
114 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
|
|
115 }
|
|
116 }
|
|
117 }
|
|
118 catch (Orthanc::OrthancException&)
|
|
119 {
|
|
120 Close();
|
|
121 throw;
|
|
122 }
|
|
123 }
|
|
124
|
|
125
|
|
126 OpenSlideLibrary::Image::Image(OpenSlideLibrary& that,
|
|
127 const std::string& path) :
|
|
128 that_(that),
|
|
129 handle_(NULL)
|
|
130 {
|
|
131 Initialize(path);
|
|
132 }
|
|
133
|
|
134
|
|
135 OpenSlideLibrary::Image::Image(const std::string& path) :
|
|
136 that_(OpenSlideLibrary::GetInstance()),
|
|
137 handle_(NULL)
|
|
138 {
|
|
139 Initialize(path);
|
|
140 }
|
|
141
|
|
142
|
|
143 void OpenSlideLibrary::Image::Close()
|
|
144 {
|
|
145 if (handle_ != NULL)
|
|
146 {
|
|
147 that_.close_(handle_);
|
|
148 handle_ = NULL;
|
|
149 }
|
|
150 }
|
|
151
|
|
152
|
|
153 void OpenSlideLibrary::Image::CheckLevel(unsigned int level) const
|
|
154 {
|
|
155 if (level >= levels_.size())
|
|
156 {
|
|
157 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
158 }
|
|
159 }
|
|
160
|
|
161
|
|
162 double OpenSlideLibrary::Image::GetLevelDownsample(unsigned int level) const
|
|
163 {
|
|
164 CheckLevel(level);
|
|
165 return levels_[level].downsample_;
|
|
166 }
|
|
167
|
|
168
|
|
169 unsigned int OpenSlideLibrary::Image::GetLevelWidth(unsigned int level) const
|
|
170 {
|
|
171 CheckLevel(level);
|
|
172 return levels_[level].width_;
|
|
173 }
|
|
174
|
|
175
|
|
176 unsigned int OpenSlideLibrary::Image::GetLevelHeight(unsigned int level) const
|
|
177 {
|
|
178 CheckLevel(level);
|
|
179 return levels_[level].height_;
|
|
180 }
|
|
181
|
|
182
|
|
183 Orthanc::ImageAccessor* OpenSlideLibrary::Image::ReadRegion(unsigned int level,
|
|
184 uint64_t x,
|
|
185 uint64_t y,
|
|
186 unsigned int width,
|
|
187 unsigned int height)
|
|
188 {
|
|
189 CheckLevel(level);
|
|
190
|
|
191 // Create a new image, with minimal pitch so as to be compatible with OpenSlide API
|
|
192 std::auto_ptr<Orthanc::ImageAccessor> region(new Orthanc::Image(Orthanc::PixelFormat_RGBA32, width, height, true));
|
|
193
|
|
194 if (region->GetWidth() != 0 &&
|
|
195 region->GetHeight() != 0)
|
|
196 {
|
|
197 double zoom = levels_[level].downsample_;
|
|
198 x = static_cast<uint64_t>(zoom * static_cast<double>(x));
|
|
199 y = static_cast<uint64_t>(zoom * static_cast<double>(y));
|
|
200
|
|
201 that_.readRegion_(handle_, reinterpret_cast<uint32_t*>(region->GetBuffer()),
|
|
202 x, y, level, region->GetWidth(), region->GetHeight());
|
|
203 }
|
|
204
|
|
205 return region.release();
|
|
206 }
|
|
207
|
|
208
|
|
209 OpenSlideLibrary& OpenSlideLibrary::GetInstance()
|
|
210 {
|
|
211 if (globalLibrary_.get() == NULL)
|
|
212 {
|
|
213 LOG(ERROR) << "OpenSlide has not been initialized";
|
|
214 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
|
|
215 }
|
|
216 else
|
|
217 {
|
|
218 return *globalLibrary_;
|
|
219 }
|
|
220 }
|
|
221
|
|
222
|
|
223 void OpenSlideLibrary::Initialize(const std::string& path)
|
|
224 {
|
|
225 globalLibrary_.reset(new OpenSlideLibrary(path));
|
|
226 }
|
|
227
|
|
228
|
|
229 void OpenSlideLibrary::Finalize()
|
|
230 {
|
|
231 globalLibrary_.reset(NULL);
|
|
232 }
|
|
233 }
|