comparison Framework/Toolbox/MessagingToolbox.cpp @ 35:6465fbd23bce

move
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 19 Dec 2016 11:37:35 +0100
parents Framework/Messaging/MessagingToolbox.cpp@a865c7992a87
children 7207a407bcd8
comparison
equal deleted inserted replaced
34:a865c7992a87 35:6465fbd23bce
1 /**
2 * Stone of Orthanc
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 General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * In addition, as a special exception, the copyright holders of this
12 * program give permission to link the code of its release with the
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it
14 * that use the same license as the "OpenSSL" library), and distribute
15 * the linked executables. You must obey the GNU General Public License
16 * in all respects for all of the code used other than "OpenSSL". If you
17 * modify file(s) with this exception, you may extend this exception to
18 * your version of the file(s), but you are not obligated to do so. If
19 * you do not wish to do so, delete this exception statement from your
20 * version. If you delete this exception statement from all source files
21 * in the program, then also delete it here.
22 *
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 * General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 **/
31
32
33 #include "MessagingToolbox.h"
34
35 #include "../../Resources/Orthanc/Core/Images/Image.h"
36 #include "../../Resources/Orthanc/Core/Images/ImageProcessing.h"
37 #include "../../Resources/Orthanc/Core/Images/JpegReader.h"
38 #include "../../Resources/Orthanc/Core/Images/PngReader.h"
39 #include "../../Resources/Orthanc/Core/OrthancException.h"
40 #include "../../Resources/Orthanc/Core/Toolbox.h"
41 #include "../../Resources/Orthanc/Core/Logging.h"
42
43 #include <boost/lexical_cast.hpp>
44 #include <json/reader.h>
45
46 #if defined(__native_client__)
47 # include <boost/math/special_functions/round.hpp>
48 #else
49 # include <boost/date_time/posix_time/posix_time.hpp>
50 # include <boost/date_time/microsec_time_clock.hpp>
51 #endif
52
53 namespace OrthancStone
54 {
55 namespace MessagingToolbox
56 {
57 #if defined(__native_client__)
58 static pp::Core* core_ = NULL;
59
60 void Timestamp::Initialize(pp::Core* core)
61 {
62 if (core == NULL)
63 {
64 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
65 }
66
67 core_ = core;
68 }
69
70 Timestamp::Timestamp()
71 {
72 if (core_ == NULL)
73 {
74 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
75 }
76
77 time_ = core_->GetTimeTicks();
78 }
79
80 int Timestamp::GetMillisecondsSince(const Timestamp& other)
81 {
82 double difference = time_ - other.time_;
83 return static_cast<int>(boost::math::iround(difference * 1000.0));
84 }
85 #else
86 Timestamp::Timestamp()
87 {
88 time_ = boost::posix_time::microsec_clock::local_time();
89 }
90
91 int Timestamp::GetMillisecondsSince(const Timestamp& other)
92 {
93 boost::posix_time::time_duration difference = time_ - other.time_;
94 return static_cast<int>(difference.total_milliseconds());
95 }
96 #endif
97
98 static bool ParseVersion(std::string& version,
99 unsigned int& major,
100 unsigned int& minor,
101 unsigned int& patch,
102 const Json::Value& info)
103 {
104 if (info.type() != Json::objectValue ||
105 !info.isMember("Version") ||
106 info["Version"].type() != Json::stringValue)
107 {
108 return false;
109 }
110
111 version = info["Version"].asString();
112 if (version == "mainline")
113 {
114 // Some arbitrary high values Orthanc versions will never reach ;)
115 major = 999;
116 minor = 999;
117 patch = 999;
118 return true;
119 }
120
121 std::vector<std::string> tokens;
122 Orthanc::Toolbox::TokenizeString(tokens, version, '.');
123
124 if (tokens.size() != 2 &&
125 tokens.size() != 3)
126 {
127 return false;
128 }
129
130 int a, b, c;
131 try
132 {
133 a = boost::lexical_cast<int>(tokens[0]);
134 b = boost::lexical_cast<int>(tokens[1]);
135
136 if (tokens.size() == 3)
137 {
138 c = boost::lexical_cast<int>(tokens[2]);
139 }
140 else
141 {
142 c = 0;
143 }
144 }
145 catch (boost::bad_lexical_cast&)
146 {
147 return false;
148 }
149
150 if (a < 0 ||
151 b < 0 ||
152 c < 0)
153 {
154 return false;
155 }
156 else
157 {
158 major = static_cast<unsigned int>(a);
159 minor = static_cast<unsigned int>(b);
160 patch = static_cast<unsigned int>(c);
161 return true;
162 }
163 }
164
165
166 static void ParseJson(Json::Value& target,
167 const std::string& source)
168 {
169 Json::Reader reader;
170 if (!reader.parse(source, target))
171 {
172 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
173 }
174 }
175
176
177 void RestApiGet(Json::Value& target,
178 OrthancPlugins::IOrthancConnection& orthanc,
179 const std::string& uri)
180 {
181 std::string tmp;
182 orthanc.RestApiGet(tmp, uri);
183 ParseJson(target, tmp);
184 }
185
186
187 void RestApiPost(Json::Value& target,
188 OrthancPlugins::IOrthancConnection& orthanc,
189 const std::string& uri,
190 const std::string& body)
191 {
192 std::string tmp;
193 orthanc.RestApiPost(tmp, uri, body);
194 ParseJson(target, tmp);
195 }
196
197
198 bool HasWebViewerInstalled(OrthancPlugins::IOrthancConnection& orthanc)
199 {
200 try
201 {
202 Json::Value json;
203 RestApiGet(json, orthanc, "/plugins/web-viewer");
204 return json.type() == Json::objectValue;
205 }
206 catch (Orthanc::OrthancException&)
207 {
208 return false;
209 }
210 }
211
212
213 bool CheckOrthancVersion(OrthancPlugins::IOrthancConnection& orthanc)
214 {
215 Json::Value json;
216 std::string version;
217 unsigned int major, minor, patch;
218
219 try
220 {
221 RestApiGet(json, orthanc, "/system");
222 }
223 catch (Orthanc::OrthancException&)
224 {
225 LOG(ERROR) << "Cannot connect to your Orthanc server";
226 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
227 }
228
229 if (!ParseVersion(version, major, minor, patch, json))
230 {
231 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
232 }
233
234 LOG(WARNING) << "Version of the Orthanc core (must be above 1.1.0): " << version;
235
236 // Stone is only compatible with Orthanc >= 1.1.0, otherwise deadlocks might occur
237 if (major < 1 ||
238 (major == 1 && minor < 1))
239 {
240 return false;
241 }
242
243 try
244 {
245 RestApiGet(json, orthanc, "/plugins/web-viewer");
246 }
247 catch (Orthanc::OrthancException&)
248 {
249 // The Web viewer is not installed, this is OK
250 LOG(WARNING) << "The Web viewer plugin is not installed, progressive download is disabled";
251 return true;
252 }
253
254 if (!ParseVersion(version, major, minor, patch, json))
255 {
256 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
257 }
258
259 LOG(WARNING) << "Version of the Web viewer plugin (must be above 2.2): " << version;
260
261 return (major >= 3 ||
262 (major == 2 && minor >= 2));
263 }
264
265
266 Orthanc::ImageAccessor* DecodeFrame(OrthancPlugins::IOrthancConnection& orthanc,
267 const std::string& instance,
268 unsigned int frame,
269 Orthanc::PixelFormat targetFormat)
270 {
271 std::string uri = ("instances/" + instance + "/frames/" +
272 boost::lexical_cast<std::string>(frame));
273
274 std::string compressed;
275
276 switch (targetFormat)
277 {
278 case Orthanc::PixelFormat_RGB24:
279 orthanc.RestApiGet(compressed, uri + "/preview");
280 break;
281
282 case Orthanc::PixelFormat_Grayscale16:
283 orthanc.RestApiGet(compressed, uri + "/image-uint16");
284 break;
285
286 case Orthanc::PixelFormat_SignedGrayscale16:
287 orthanc.RestApiGet(compressed, uri + "/image-int16");
288 break;
289
290 default:
291 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
292 }
293
294 std::auto_ptr<Orthanc::PngReader> result(new Orthanc::PngReader);
295 result->ReadFromMemory(compressed);
296
297 if (targetFormat == Orthanc::PixelFormat_SignedGrayscale16)
298 {
299 if (result->GetFormat() == Orthanc::PixelFormat_Grayscale16)
300 {
301 result->SetFormat(Orthanc::PixelFormat_SignedGrayscale16);
302 }
303 else
304 {
305 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
306 }
307 }
308
309 return result.release();
310 }
311
312
313 Orthanc::ImageAccessor* DecodeJpegFrame(OrthancPlugins::IOrthancConnection& orthanc,
314 const std::string& instance,
315 unsigned int frame,
316 unsigned int quality,
317 Orthanc::PixelFormat targetFormat)
318 {
319 if (quality <= 0 ||
320 quality > 100)
321 {
322 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
323 }
324
325 // This requires the official Web viewer plugin to be installed!
326 std::string uri = ("web-viewer/instances/jpeg" +
327 boost::lexical_cast<std::string>(quality) +
328 "-" + instance + "_" +
329 boost::lexical_cast<std::string>(frame));
330
331 Json::Value encoded;
332 RestApiGet(encoded, orthanc, uri);
333
334 if (encoded.type() != Json::objectValue ||
335 !encoded.isMember("Orthanc") ||
336 encoded["Orthanc"].type() != Json::objectValue)
337 {
338 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
339 }
340
341 Json::Value& info = encoded["Orthanc"];
342 if (!info.isMember("PixelData") ||
343 !info.isMember("Stretched") ||
344 !info.isMember("Compression") ||
345 info["Compression"].type() != Json::stringValue ||
346 info["PixelData"].type() != Json::stringValue ||
347 info["Stretched"].type() != Json::booleanValue ||
348 info["Compression"].asString() != "Jpeg")
349 {
350 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
351 }
352
353 bool isSigned = false;
354 bool isStretched = info["Stretched"].asBool();
355
356 if (info.isMember("IsSigned"))
357 {
358 if (info["IsSigned"].type() != Json::booleanValue)
359 {
360 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
361 }
362 else
363 {
364 isSigned = info["IsSigned"].asBool();
365 }
366 }
367
368 std::string jpeg;
369 Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString());
370
371 std::auto_ptr<Orthanc::JpegReader> reader(new Orthanc::JpegReader);
372 reader->ReadFromMemory(jpeg);
373
374 if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image
375 {
376 if (targetFormat != Orthanc::PixelFormat_RGB24)
377 {
378 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
379 }
380
381 if (isSigned || isStretched)
382 {
383 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
384 }
385 else
386 {
387 return reader.release();
388 }
389 }
390
391 if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8)
392 {
393 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
394 }
395
396 if (!isStretched)
397 {
398 if (targetFormat != reader->GetFormat())
399 {
400 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
401 }
402
403 return reader.release();
404 }
405
406 int32_t stretchLow = 0;
407 int32_t stretchHigh = 0;
408
409 if (!info.isMember("StretchLow") ||
410 !info.isMember("StretchHigh") ||
411 info["StretchLow"].type() != Json::intValue ||
412 info["StretchHigh"].type() != Json::intValue)
413 {
414 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
415 }
416
417 stretchLow = info["StretchLow"].asInt();
418 stretchHigh = info["StretchHigh"].asInt();
419
420 if (stretchLow < -32768 ||
421 stretchHigh > 65535 ||
422 (stretchLow < 0 && stretchHigh > 32767))
423 {
424 // This range cannot be represented with a uint16_t or an int16_t
425 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
426 }
427
428 // Decode a grayscale JPEG 8bpp image coming from the Web viewer
429 std::auto_ptr<Orthanc::ImageAccessor> image
430 (new Orthanc::Image(targetFormat, reader->GetWidth(), reader->GetHeight(), false));
431
432 float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f;
433 float offset = static_cast<float>(stretchLow) / scaling;
434
435 Orthanc::ImageProcessing::Convert(*image, *reader);
436 Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling);
437
438 #if 0
439 /*info.removeMember("PixelData");
440 std::cout << info.toStyledString();*/
441
442 int64_t a, b;
443 Orthanc::ImageProcessing::GetMinMaxValue(a, b, *image);
444 std::cout << stretchLow << "->" << stretchHigh << " = " << a << "->" << b << std::endl;
445 #endif
446
447 return image.release();
448 }
449 }
450 }