Mercurial > hg > orthanc-webviewer
comparison Plugin/DecodedImageAdapter.cpp @ 96:a6ba21a083e5 refactoring
major refactoring
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 27 Nov 2015 18:26:55 +0100 |
parents | 111689a2c177 |
children | ef1b27ba7dfc |
comparison
equal
deleted
inserted
replaced
95:dbe7c97b6b4a | 96:a6ba21a083e5 |
---|---|
18 **/ | 18 **/ |
19 | 19 |
20 | 20 |
21 #include "DecodedImageAdapter.h" | 21 #include "DecodedImageAdapter.h" |
22 | 22 |
23 #include "../Orthanc/Core/Images/ImageBuffer.h" | |
24 #include "../Orthanc/Core/Images/ImageProcessing.h" | |
25 #include "../Orthanc/Core/OrthancException.h" | |
26 #include "../Orthanc/Plugins/Samples/GdcmDecoder/OrthancImageWrapper.h" | |
27 #include "../Orthanc/Resources/ThirdParty/base64/base64.h" | |
28 #include "ParsedDicomImage.h" | |
23 #include "ViewerToolbox.h" | 29 #include "ViewerToolbox.h" |
24 #include "ParsedDicomImage.h" | |
25 | 30 |
26 #include <boost/lexical_cast.hpp> | 31 #include <boost/lexical_cast.hpp> |
27 #include <boost/algorithm/string/predicate.hpp> | 32 #include <boost/algorithm/string/predicate.hpp> |
28 #include <json/writer.h> | 33 #include <json/writer.h> |
29 | 34 |
82 if (!ParseUri(type, level, instanceId, uri)) | 87 if (!ParseUri(type, level, instanceId, uri)) |
83 { | 88 { |
84 return false; | 89 return false; |
85 } | 90 } |
86 | 91 |
92 | |
93 bool ok = false; | |
94 | |
95 #if 1 | |
96 Json::Value tags; | |
97 std::string dicom; | |
98 if (!GetStringFromOrthanc(dicom, context_, "/instances/" + instanceId + "/file") || | |
99 !GetJsonFromOrthanc(tags, context_, "/instances/" + instanceId + "/tags")) | |
100 { | |
101 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); | |
102 } | |
103 | |
104 std::auto_ptr<OrthancImageWrapper> image(decoderCache_.Decode(context_, dicom.c_str(), dicom.size(), 0 /* TODO Frame */)); | |
105 | |
106 Json::Value json; | |
107 if (GetCornerstoneMetadata(json, tags, *image)) | |
108 { | |
109 if (type == CompressionType_Deflate) | |
110 { | |
111 ok = EncodeUsingDeflate(json, *image, 9); | |
112 } | |
113 else if (type == CompressionType_Jpeg) | |
114 { | |
115 ok = EncodeUsingJpeg(json, *image, level); | |
116 } | |
117 } | |
118 | |
119 #else | |
120 | |
87 ParsedDicomImage image(context_, instanceId); | 121 ParsedDicomImage image(context_, instanceId); |
88 | 122 |
89 Json::Value json; | 123 Json::Value json; |
90 bool ok = false; | |
91 | 124 |
92 if (type == CompressionType_Deflate) | 125 if (type == CompressionType_Deflate) |
93 { | 126 { |
94 ok = image.EncodeUsingDeflate(json, 9); | 127 ok = image.EncodeUsingDeflate(json, 9); |
95 } | 128 } |
96 else if (type == CompressionType_Jpeg) | 129 else if (type == CompressionType_Jpeg) |
97 { | 130 { |
98 ok = image.EncodeUsingJpeg(json, level); | 131 ok = image.EncodeUsingJpeg(json, level); |
99 } | 132 } |
133 #endif | |
100 | 134 |
101 if (ok) | 135 if (ok) |
102 { | 136 { |
103 Json::FastWriter writer; | 137 Json::FastWriter writer; |
104 content = writer.write(json); | 138 content = writer.write(json); |
110 sprintf(msg, "Unable to decode the following instance: %s", uri.c_str()); | 144 sprintf(msg, "Unable to decode the following instance: %s", uri.c_str()); |
111 OrthancPluginLogWarning(context_, msg); | 145 OrthancPluginLogWarning(context_, msg); |
112 return false; | 146 return false; |
113 } | 147 } |
114 } | 148 } |
149 | |
150 | |
151 static bool GetTagValue(std::string& value, | |
152 const Json::Value& tags, | |
153 const std::string& tag) | |
154 { | |
155 if (tags.type() == Json::objectValue && | |
156 tags.isMember(tag) && | |
157 tags[tag].type() == Json::objectValue && | |
158 tags[tag].isMember("Type") && | |
159 tags[tag].isMember("Value") && | |
160 tags[tag]["Type"].type() == Json::stringValue && | |
161 tags[tag]["Value"].type() == Json::stringValue && | |
162 tags[tag]["Type"].asString() == "String") | |
163 { | |
164 value = tags[tag]["Value"].asString(); | |
165 return true; | |
166 } | |
167 else | |
168 { | |
169 return false; | |
170 } | |
171 } | |
172 | |
173 | |
174 | |
175 bool DecodedImageAdapter::GetCornerstoneMetadata(Json::Value& result, | |
176 const Json::Value& tags, | |
177 OrthancImageWrapper& image) | |
178 { | |
179 using namespace Orthanc; | |
180 | |
181 float windowCenter, windowWidth; | |
182 | |
183 Orthanc::ImageAccessor accessor; | |
184 accessor.AssignReadOnly(OrthancPlugins::Convert(image.GetFormat()), image.GetWidth(), | |
185 image.GetHeight(), image.GetPitch(), image.GetBuffer()); | |
186 | |
187 switch (accessor.GetFormat()) | |
188 { | |
189 case PixelFormat_Grayscale8: | |
190 case PixelFormat_Grayscale16: | |
191 case PixelFormat_SignedGrayscale16: | |
192 { | |
193 int64_t a, b; | |
194 Orthanc::ImageProcessing::GetMinMaxValue(a, b, accessor); | |
195 result["minPixelValue"] = (a < 0 ? static_cast<int32_t>(a) : 0); | |
196 result["maxPixelValue"] = (b > 0 ? static_cast<int32_t>(b) : 1); | |
197 result["color"] = false; | |
198 | |
199 windowCenter = static_cast<float>(a + b) / 2.0f; | |
200 | |
201 if (a == b) | |
202 { | |
203 windowWidth = 256.0f; // Arbitrary value | |
204 } | |
205 else | |
206 { | |
207 windowWidth = static_cast<float>(b - a) / 2.0f; | |
208 } | |
209 | |
210 break; | |
211 } | |
212 | |
213 case PixelFormat_RGB24: | |
214 result["minPixelValue"] = 0; | |
215 result["maxPixelValue"] = 255; | |
216 result["color"] = true; | |
217 windowCenter = 127.5f; | |
218 windowWidth = 256.0f; | |
219 break; | |
220 | |
221 default: | |
222 return false; | |
223 } | |
224 | |
225 result["slope"] = image.GetSlope(); | |
226 result["intercept"] = image.GetIntercept(); | |
227 result["rows"] = image.GetHeight(); | |
228 result["columns"] = image.GetWidth(); | |
229 result["height"] = image.GetHeight(); | |
230 result["width"] = image.GetWidth(); | |
231 result["columnPixelSpacing"] = image.GetColumnPixelSpacing(); | |
232 result["rowPixelSpacing"] = image.GetRowPixelSpacing(); | |
233 | |
234 result["windowCenter"] = windowCenter * image.GetSlope() + image.GetIntercept(); | |
235 result["windowWidth"] = windowWidth * image.GetSlope(); | |
236 | |
237 try | |
238 { | |
239 std::string width, center; | |
240 if (GetTagValue(center, tags, "0028,1050" /*DICOM_TAG_WINDOW_CENTER*/) && | |
241 GetTagValue(width, tags, "0028,1051" /*DICOM_TAG_WINDOW_WIDTH*/)) | |
242 { | |
243 float a = boost::lexical_cast<float>(width); | |
244 float b = boost::lexical_cast<float>(center); | |
245 result["windowWidth"] = a; | |
246 result["windowCenter"] = b; | |
247 } | |
248 } | |
249 catch (boost::bad_lexical_cast&) | |
250 { | |
251 } | |
252 | |
253 return true; | |
254 } | |
255 | |
256 | |
257 | |
258 bool DecodedImageAdapter::EncodeUsingDeflate(Json::Value& result, | |
259 OrthancImageWrapper& image, | |
260 uint8_t compressionLevel /* between 0 and 9 */) | |
261 { | |
262 Orthanc::ImageAccessor accessor; | |
263 accessor.AssignReadOnly(OrthancPlugins::Convert(image.GetFormat()), image.GetWidth(), | |
264 image.GetHeight(), image.GetPitch(), image.GetBuffer()); | |
265 | |
266 Orthanc::ImageBuffer buffer; | |
267 buffer.SetMinimalPitchForced(true); | |
268 | |
269 Orthanc::ImageAccessor converted; | |
270 | |
271 switch (accessor.GetFormat()) | |
272 { | |
273 case Orthanc::PixelFormat_RGB24: | |
274 converted = accessor; | |
275 break; | |
276 | |
277 case Orthanc::PixelFormat_Grayscale8: | |
278 case Orthanc::PixelFormat_Grayscale16: | |
279 buffer.SetFormat(Orthanc::PixelFormat_SignedGrayscale16); | |
280 buffer.SetWidth(accessor.GetWidth()); | |
281 buffer.SetHeight(accessor.GetHeight()); | |
282 converted = buffer.GetAccessor(); | |
283 Orthanc::ImageProcessing::Convert(converted, accessor); | |
284 break; | |
285 | |
286 case Orthanc::PixelFormat_SignedGrayscale16: | |
287 converted = accessor; | |
288 break; | |
289 | |
290 default: | |
291 // Unsupported pixel format | |
292 return false; | |
293 } | |
294 | |
295 // Sanity check: The pitch must be minimal | |
296 assert(converted.GetSize() == converted.GetWidth() * converted.GetHeight() * | |
297 GetBytesPerPixel(converted.GetFormat())); | |
298 result["Orthanc"]["Compression"] = "Deflate"; | |
299 result["sizeInBytes"] = converted.GetSize(); | |
300 | |
301 std::string z; | |
302 CompressUsingDeflate(z, image.GetContext(), converted.GetConstBuffer(), converted.GetSize()); | |
303 | |
304 result["Orthanc"]["PixelData"] = base64_encode(z); | |
305 | |
306 return true; | |
307 } | |
308 | |
309 | |
310 | |
311 template <typename TargetType, typename SourceType> | |
312 static void ChangeDynamics(Orthanc::ImageAccessor& target, | |
313 const Orthanc::ImageAccessor& source, | |
314 SourceType source1, TargetType target1, | |
315 SourceType source2, TargetType target2) | |
316 { | |
317 if (source.GetWidth() != target.GetWidth() || | |
318 source.GetHeight() != target.GetHeight()) | |
319 { | |
320 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize); | |
321 } | |
322 | |
323 float scale = static_cast<float>(target2 - target1) / static_cast<float>(source2 - source1); | |
324 float offset = static_cast<float>(target1) - scale * static_cast<float>(source1); | |
325 | |
326 const float minValue = static_cast<float>(std::numeric_limits<TargetType>::min()); | |
327 const float maxValue = static_cast<float>(std::numeric_limits<TargetType>::max()); | |
328 | |
329 for (unsigned int y = 0; y < source.GetHeight(); y++) | |
330 { | |
331 const SourceType* p = reinterpret_cast<const SourceType*>(source.GetConstRow(y)); | |
332 TargetType* q = reinterpret_cast<TargetType*>(target.GetRow(y)); | |
333 | |
334 for (unsigned int x = 0; x < source.GetWidth(); x++, p++, q++) | |
335 { | |
336 float v = (scale * static_cast<float>(*p)) + offset; | |
337 | |
338 if (v > maxValue) | |
339 { | |
340 *q = std::numeric_limits<TargetType>::max(); | |
341 } | |
342 else if (v < minValue) | |
343 { | |
344 *q = std::numeric_limits<TargetType>::min(); | |
345 } | |
346 else | |
347 { | |
348 *q = static_cast<TargetType>(boost::math::iround(v)); | |
349 } | |
350 } | |
351 } | |
352 } | |
353 | |
354 | |
355 bool DecodedImageAdapter::EncodeUsingJpeg(Json::Value& result, | |
356 OrthancImageWrapper& image, | |
357 uint8_t quality /* between 0 and 100 */) | |
358 { | |
359 Orthanc::ImageAccessor accessor; | |
360 accessor.AssignReadOnly(OrthancPlugins::Convert(image.GetFormat()), image.GetWidth(), | |
361 image.GetHeight(), image.GetPitch(), image.GetBuffer()); | |
362 | |
363 Orthanc::ImageBuffer buffer; | |
364 buffer.SetMinimalPitchForced(true); | |
365 | |
366 Orthanc::ImageAccessor converted; | |
367 | |
368 if (accessor.GetFormat() == Orthanc::PixelFormat_Grayscale8 || | |
369 accessor.GetFormat() == Orthanc::PixelFormat_RGB24) | |
370 { | |
371 result["Orthanc"]["Stretched"] = false; | |
372 converted = accessor; | |
373 } | |
374 else if (accessor.GetFormat() == Orthanc::PixelFormat_Grayscale16 || | |
375 accessor.GetFormat() == Orthanc::PixelFormat_SignedGrayscale16) | |
376 { | |
377 result["Orthanc"]["Stretched"] = true; | |
378 buffer.SetFormat(Orthanc::PixelFormat_Grayscale8); | |
379 buffer.SetWidth(accessor.GetWidth()); | |
380 buffer.SetHeight(accessor.GetHeight()); | |
381 converted = buffer.GetAccessor(); | |
382 | |
383 int64_t a, b; | |
384 Orthanc::ImageProcessing::GetMinMaxValue(a, b, accessor); | |
385 result["Orthanc"]["StretchLow"] = static_cast<int32_t>(a); | |
386 result["Orthanc"]["StretchHigh"] = static_cast<int32_t>(b); | |
387 | |
388 if (accessor.GetFormat() == Orthanc::PixelFormat_Grayscale16) | |
389 { | |
390 ChangeDynamics<uint8_t, uint16_t>(converted, accessor, a, 0, b, 255); | |
391 } | |
392 else | |
393 { | |
394 ChangeDynamics<uint8_t, int16_t>(converted, accessor, a, 0, b, 255); | |
395 } | |
396 } | |
397 else | |
398 { | |
399 return false; | |
400 } | |
401 | |
402 result["Orthanc"]["Compression"] = "Jpeg"; | |
403 result["sizeInBytes"] = converted.GetSize(); | |
404 | |
405 std::string jpeg; | |
406 WriteJpegToMemory(jpeg, image.GetContext(), converted, quality); | |
407 | |
408 result["Orthanc"]["PixelData"] = base64_encode(jpeg); | |
409 return true; | |
410 } | |
115 } | 411 } |