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 "DicomizerParameters.h"
|
|
22
|
|
23 #include "Messaging/FolderTarget.h"
|
|
24 #include "Messaging/OrthancTarget.h"
|
|
25
|
|
26 #include "Orthanc/Core/OrthancException.h"
|
|
27
|
|
28 #include <boost/thread.hpp>
|
|
29 #include <boost/lexical_cast.hpp>
|
|
30
|
|
31 namespace OrthancWSI
|
|
32 {
|
|
33 static unsigned int ChooseNumberOfThreads()
|
|
34 {
|
|
35 unsigned int nthreads = boost::thread::hardware_concurrency();
|
|
36
|
|
37 if (nthreads % 2 == 0)
|
|
38 {
|
|
39 nthreads = nthreads / 2;
|
|
40 }
|
|
41 else
|
|
42 {
|
|
43 nthreads = nthreads / 2 + 1;
|
|
44 }
|
|
45
|
|
46 if (nthreads == 0)
|
|
47 {
|
|
48 nthreads = 1;
|
|
49 }
|
|
50
|
|
51 return nthreads;
|
|
52 }
|
|
53
|
|
54
|
|
55 DicomizerParameters::DicomizerParameters()
|
|
56 {
|
|
57 safetyCheck_ = false;
|
|
58 repaintBackground_ = false;
|
|
59 backgroundColor_[0] = 255;
|
|
60 backgroundColor_[1] = 255;
|
|
61 backgroundColor_[2] = 255;
|
|
62 targetCompression_ = ImageCompression_Jpeg;
|
|
63 hasTargetTileSize_ = false;
|
|
64 threadsCount_ = ChooseNumberOfThreads();
|
|
65 maxDicomFileSize_ = 10 * 1024 * 1024; // 10MB
|
|
66 reconstructPyramid_ = false;
|
|
67 pyramidLevelsCount_ = 0;
|
|
68 pyramidLowerLevelsCount_ = 0;
|
|
69 smooth_ = false;
|
|
70 jpegQuality_ = 90;
|
|
71 forceReencode_ = false;
|
|
72 opticalPath_ = OpticalPath_Brightfield;
|
|
73 }
|
|
74
|
|
75
|
|
76 void DicomizerParameters::SetBackgroundColor(uint8_t red,
|
|
77 uint8_t green,
|
|
78 uint8_t blue)
|
|
79 {
|
|
80 repaintBackground_ = true;
|
|
81 backgroundColor_[0] = red;
|
|
82 backgroundColor_[1] = green;
|
|
83 backgroundColor_[2] = blue;
|
|
84 }
|
|
85
|
|
86
|
|
87 void DicomizerParameters::SetTargetTileSize(unsigned int width,
|
|
88 unsigned int height)
|
|
89 {
|
|
90 hasTargetTileSize_ = true;
|
|
91 targetTileWidth_ = width;
|
|
92 targetTileHeight_ = height;
|
|
93 }
|
|
94
|
|
95
|
|
96 unsigned int DicomizerParameters::GetTargetTileWidth(unsigned int defaultWidth) const
|
|
97 {
|
|
98 if (hasTargetTileSize_ &&
|
|
99 targetTileWidth_ != 0)
|
|
100 {
|
|
101 return targetTileWidth_;
|
|
102 }
|
|
103 else
|
|
104 {
|
|
105 return defaultWidth;
|
|
106 }
|
|
107 }
|
|
108
|
|
109
|
|
110 unsigned int DicomizerParameters::GetTargetTileHeight(unsigned int defaultHeight) const
|
|
111 {
|
|
112 if (hasTargetTileSize_ &&
|
|
113 targetTileHeight_ != 0)
|
|
114 {
|
|
115 return targetTileHeight_;
|
|
116 }
|
|
117 else
|
|
118 {
|
|
119 return defaultHeight;
|
|
120 }
|
|
121 }
|
|
122
|
|
123
|
|
124 void DicomizerParameters::SetThreadsCount(unsigned int threads)
|
|
125 {
|
|
126 if (threads == 0)
|
|
127 {
|
|
128 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
129 }
|
|
130
|
|
131 threadsCount_ = threads;
|
|
132 }
|
|
133
|
|
134
|
|
135 void DicomizerParameters::SetDicomMaxFileSize(unsigned int size)
|
|
136 {
|
|
137 if (size <= 1024)
|
|
138 {
|
|
139 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
140 }
|
|
141
|
|
142 maxDicomFileSize_ = size;
|
|
143 }
|
|
144
|
|
145
|
|
146 void DicomizerParameters::SetPyramidLevelsCount(unsigned int count)
|
|
147 {
|
|
148 if (count <= 0)
|
|
149 {
|
|
150 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
151 }
|
|
152
|
|
153 pyramidLevelsCount_ = count;
|
|
154 }
|
|
155
|
|
156
|
|
157 unsigned int DicomizerParameters::GetPyramidLevelsCount(const IPyramidWriter& target,
|
|
158 const ITiledPyramid& source) const
|
|
159 {
|
|
160 if (!reconstructPyramid_)
|
|
161 {
|
|
162 // Only makes sense if reconstructing the pyramid
|
|
163 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
|
|
164 }
|
|
165
|
|
166 if (pyramidLevelsCount_ != 0)
|
|
167 {
|
|
168 return pyramidLevelsCount_;
|
|
169 }
|
|
170
|
|
171 // By default, the number of levels for the pyramid is taken so that
|
|
172 // the upper level is reduced either to 1 column of tiles, or to 1
|
|
173 // row of tiles.
|
|
174 unsigned int totalWidth = source.GetLevelWidth(0);
|
|
175 unsigned int totalHeight = source.GetLevelHeight(0);
|
|
176
|
|
177 unsigned int countLevels = 1;
|
|
178 for (;;)
|
|
179 {
|
|
180 unsigned int zoom = 1 << (countLevels - 1);
|
|
181
|
|
182 if (CeilingDivision(totalWidth, zoom) <= target.GetTileWidth() ||
|
|
183 CeilingDivision(totalHeight, zoom) <= target.GetTileHeight())
|
|
184 {
|
|
185 break;
|
|
186 }
|
|
187
|
|
188 countLevels += 1;
|
|
189 }
|
|
190
|
|
191 return countLevels;
|
|
192 }
|
|
193
|
|
194
|
|
195 void DicomizerParameters::SetPyramidLowerLevelsCount(unsigned int count)
|
|
196 {
|
|
197 if (count <= 0)
|
|
198 {
|
|
199 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
200 }
|
|
201
|
|
202 pyramidLowerLevelsCount_ = count;
|
|
203 }
|
|
204
|
|
205
|
|
206 unsigned int DicomizerParameters::GetPyramidLowerLevelsCount(const IPyramidWriter& target,
|
|
207 const ITiledPyramid& source) const
|
|
208 {
|
|
209 if (!reconstructPyramid_)
|
|
210 {
|
|
211 // Only makes sense if reconstructing the pyramid
|
|
212 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
|
|
213 }
|
|
214
|
|
215 if (pyramidLowerLevelsCount_ != 0)
|
|
216 {
|
|
217 return pyramidLowerLevelsCount_;
|
|
218 }
|
|
219
|
|
220 unsigned int fullNumberOfTiles = (
|
|
221 CeilingDivision(source.GetLevelWidth(0), source.GetTileWidth()) *
|
|
222 CeilingDivision(source.GetLevelHeight(0), source.GetTileHeight()));
|
|
223
|
|
224 // By default, the number of lower levels in the pyramid is chosen
|
|
225 // as a compromise between the number of tasks (there should not be
|
|
226 // too few tasks, otherwise multithreading would not be efficient)
|
|
227 // and memory consumption (maximum 64MB of RAM due to the decoding
|
|
228 // of the tiles of the source image per thread: cf. PyramidReader).
|
|
229 unsigned int result = 1;
|
|
230 for (;;)
|
|
231 {
|
|
232 unsigned int zoom = 1 << (result - 1);
|
|
233 unsigned int numberOfTiles = CeilingDivision(fullNumberOfTiles, zoom * zoom);
|
|
234
|
|
235 if (result + 1 > target.GetLevelCount() ||
|
|
236 numberOfTiles < 4 * GetThreadsCount() ||
|
|
237 zoom * target.GetTileWidth() > 4096 ||
|
|
238 zoom * target.GetTileHeight() > 4096)
|
|
239 {
|
|
240 break;
|
|
241 }
|
|
242
|
|
243 result += 1;
|
|
244 }
|
|
245
|
|
246 return result - 1;
|
|
247 }
|
|
248
|
|
249
|
|
250 void DicomizerParameters::SetJpegQuality(int quality)
|
|
251 {
|
|
252 if (quality <= 0 ||
|
|
253 quality > 100)
|
|
254 {
|
|
255 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
256 }
|
|
257
|
|
258 jpegQuality_ = quality;
|
|
259 }
|
|
260
|
|
261
|
|
262 IFileTarget* DicomizerParameters::CreateTarget() const
|
|
263 {
|
|
264 if (folder_.empty() ||
|
|
265 folderPattern_.empty())
|
|
266 {
|
|
267 return new OrthancTarget(orthanc_);
|
|
268 }
|
|
269 else
|
|
270 {
|
|
271 return new FolderTarget(folder_ + "/" + folderPattern_);
|
|
272 }
|
|
273 }
|
|
274 }
|