Mercurial > hg > orthanc
comparison OrthancServer/Internals/DicomImageDecoder.cpp @ 847:03ea55da7429 jpeg
fully functional JPEG-LS conversion
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 05 Jun 2014 18:14:16 +0200 |
parents | 715ab7674993 |
children | 9ee2e7a5efaf |
comparison
equal
deleted
inserted
replaced
846:715ab7674993 | 847:03ea55da7429 |
---|---|
76 =========================================================================*/ | 76 =========================================================================*/ |
77 | 77 |
78 | 78 |
79 | 79 |
80 #include "../../Core/OrthancException.h" | 80 #include "../../Core/OrthancException.h" |
81 #include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h" | |
81 #include "../ToDcmtkBridge.h" | 82 #include "../ToDcmtkBridge.h" |
82 | 83 #include "../FromDcmtkBridge.h" |
84 | |
85 #include <glog/logging.h> | |
86 | |
87 #include <boost/lexical_cast.hpp> | |
88 | |
89 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 | |
83 #include <dcmtk/dcmjpls/djcodecd.h> | 90 #include <dcmtk/dcmjpls/djcodecd.h> |
84 #include <dcmtk/dcmjpls/djcparam.h> | 91 #include <dcmtk/dcmjpls/djcparam.h> |
85 #include <dcmtk/dcmjpeg/djrplol.h> | 92 #include <dcmtk/dcmjpeg/djrplol.h> |
86 #include <boost/lexical_cast.hpp> | |
87 | |
88 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 | |
89 #endif | 93 #endif |
90 | 94 |
91 | 95 |
92 namespace Orthanc | 96 namespace Orthanc |
93 { | 97 { |
272 return (dataset.getOriginalXfer() == EXS_JPEGLSLossless || | 276 return (dataset.getOriginalXfer() == EXS_JPEGLSLossless || |
273 dataset.getOriginalXfer() == EXS_JPEGLSLossy); | 277 dataset.getOriginalXfer() == EXS_JPEGLSLossy); |
274 } | 278 } |
275 | 279 |
276 | 280 |
281 bool DicomImageDecoder::IsUncompressedImage(const DcmDataset& dataset) | |
282 { | |
283 return (dataset.getOriginalXfer() == EXS_Unknown || | |
284 dataset.getOriginalXfer() == EXS_LittleEndianImplicit || | |
285 dataset.getOriginalXfer() == EXS_BigEndianImplicit || | |
286 dataset.getOriginalXfer() == EXS_LittleEndianExplicit || | |
287 dataset.getOriginalXfer() == EXS_BigEndianExplicit); | |
288 } | |
289 | |
290 | |
291 template <typename PixelType> | |
292 static void CopyPixels(ImageAccessor& target, | |
293 const DicomIntegerPixelAccessor& source) | |
294 { | |
295 const PixelType minValue = std::numeric_limits<PixelType>::min(); | |
296 const PixelType maxValue = std::numeric_limits<PixelType>::max(); | |
297 | |
298 for (unsigned int y = 0; y < source.GetHeight(); y++) | |
299 { | |
300 PixelType* pixel = reinterpret_cast<PixelType*>(target.GetRow(y)); | |
301 for (unsigned int x = 0; x < source.GetWidth(); x++) | |
302 { | |
303 for (unsigned int c = 0; c < source.GetChannelCount(); c++, pixel++) | |
304 { | |
305 int32_t v = source.GetValue(x, y, c); | |
306 if (v < static_cast<int32_t>(minValue)) | |
307 { | |
308 *pixel = minValue; | |
309 } | |
310 else if (v > static_cast<int32_t>(maxValue)) | |
311 { | |
312 *pixel = maxValue; | |
313 } | |
314 else | |
315 { | |
316 *pixel = static_cast<PixelType>(v); | |
317 } | |
318 } | |
319 } | |
320 } | |
321 } | |
322 | |
323 | |
324 void DicomImageDecoder::DecodeUncompressedImage(ImageBuffer& target, | |
325 DcmDataset& dataset, | |
326 unsigned int frame) | |
327 { | |
328 if (!IsUncompressedImage(dataset)) | |
329 { | |
330 throw OrthancException(ErrorCode_BadParameterType); | |
331 } | |
332 | |
333 DecodeUncompressedImageInternal(target, dataset, frame); | |
334 } | |
335 | |
336 | |
337 void DicomImageDecoder::DecodeUncompressedImageInternal(ImageBuffer& target, | |
338 DcmDataset& dataset, | |
339 unsigned int frame) | |
340 { | |
341 // See also: http://support.dcmtk.org/wiki/dcmtk/howto/accessing-compressed-data | |
342 | |
343 std::auto_ptr<DicomIntegerPixelAccessor> source; | |
344 | |
345 DicomMap m; | |
346 FromDcmtkBridge::Convert(m, dataset); | |
347 | |
348 | |
349 /** | |
350 * Create an accessor to the raw values of the DICOM image. | |
351 **/ | |
352 | |
353 std::string privateContent; | |
354 | |
355 DcmElement* e; | |
356 if (dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_DATA), e).good() && | |
357 e != NULL) | |
358 { | |
359 Uint8* pixData = NULL; | |
360 if (e->getUint8Array(pixData) == EC_Normal) | |
361 { | |
362 source.reset(new DicomIntegerPixelAccessor(m, pixData, e->getLength())); | |
363 } | |
364 } | |
365 else if (DicomImageDecoder::DecodePsmctRle1(privateContent, dataset)) | |
366 { | |
367 LOG(INFO) << "The PMSCT_RLE1 decoding has succeeded"; | |
368 Uint8* pixData = NULL; | |
369 if (privateContent.size() > 0) | |
370 { | |
371 pixData = reinterpret_cast<Uint8*>(&privateContent[0]); | |
372 } | |
373 | |
374 source.reset(new DicomIntegerPixelAccessor(m, pixData, privateContent.size())); | |
375 } | |
376 | |
377 if (source.get() == NULL) | |
378 { | |
379 throw OrthancException(ErrorCode_BadFileFormat); | |
380 } | |
381 | |
382 source->SetCurrentFrame(frame); | |
383 | |
384 | |
385 /** | |
386 * Resize the target image, with some sanity checks. | |
387 **/ | |
388 | |
389 SetupImageBuffer(target, dataset); | |
390 | |
391 if (target.GetWidth() != target.GetWidth() || | |
392 target.GetHeight() != target.GetHeight()) | |
393 { | |
394 throw OrthancException(ErrorCode_InternalError); | |
395 } | |
396 | |
397 bool ok; | |
398 switch (target.GetFormat()) | |
399 { | |
400 case PixelFormat_RGB24: | |
401 ok = source->GetChannelCount() == 3; | |
402 break; | |
403 | |
404 case PixelFormat_RGBA32: | |
405 ok = source->GetChannelCount() == 4; | |
406 break; | |
407 | |
408 case PixelFormat_Grayscale8: | |
409 case PixelFormat_Grayscale16: | |
410 case PixelFormat_SignedGrayscale16: | |
411 ok = source->GetChannelCount() == 1; | |
412 break; | |
413 | |
414 default: | |
415 ok = false; // (*) | |
416 break; | |
417 } | |
418 | |
419 if (!ok) | |
420 { | |
421 throw OrthancException(ErrorCode_InternalError); | |
422 } | |
423 | |
424 | |
425 /** | |
426 * Loop over the DICOM buffer, storing its value into the target | |
427 * image. | |
428 **/ | |
429 | |
430 ImageAccessor accessor(target.GetAccessor()); | |
431 | |
432 switch (target.GetFormat()) | |
433 { | |
434 case PixelFormat_RGB24: | |
435 case PixelFormat_RGBA32: | |
436 case PixelFormat_Grayscale8: | |
437 CopyPixels<uint8_t>(accessor, *source); | |
438 break; | |
439 | |
440 case PixelFormat_Grayscale16: | |
441 CopyPixels<uint16_t>(accessor, *source); | |
442 break; | |
443 | |
444 case PixelFormat_SignedGrayscale16: | |
445 CopyPixels<int16_t>(accessor, *source); | |
446 break; | |
447 | |
448 default: | |
449 throw OrthancException(ErrorCode_InternalError); | |
450 } | |
451 } | |
452 | |
453 | |
277 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 | 454 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 |
278 void DicomImageDecoder::DecodeJpegLossless(ImageBuffer& target, | 455 void DicomImageDecoder::DecodeJpegLossless(ImageBuffer& target, |
279 DcmDataset& dataset) | 456 DcmDataset& dataset, |
457 unsigned int frame) | |
280 { | 458 { |
281 if (!IsJpegLossless(dataset)) | 459 if (!IsJpegLossless(dataset)) |
282 { | 460 { |
283 throw OrthancException(ErrorCode_BadParameterType); | 461 throw OrthancException(ErrorCode_BadParameterType); |
284 } | 462 } |
313 | 491 |
314 Uint32 startFragment = 0; // Default | 492 Uint32 startFragment = 0; // Default |
315 OFString decompressedColorModel; // Out | 493 OFString decompressedColorModel; // Out |
316 DJ_RPLossless representationParameter; | 494 DJ_RPLossless representationParameter; |
317 OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, ¶meters, | 495 OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, ¶meters, |
318 &dataset, 0, startFragment, accessor.GetBuffer(), | 496 &dataset, frame, startFragment, accessor.GetBuffer(), |
319 accessor.GetSize(), decompressedColorModel); | 497 accessor.GetSize(), decompressedColorModel); |
320 | 498 |
321 if (!c.good()) | 499 if (!c.good()) |
322 { | 500 { |
323 throw OrthancException(ErrorCode_InternalError); | 501 throw OrthancException(ErrorCode_InternalError); |
324 } | 502 } |
325 } | 503 } |
326 #endif | 504 #endif |
327 | 505 |
328 | 506 |
507 | |
508 bool DicomImageDecoder::Decode(ImageBuffer& target, | |
509 DcmDataset& dataset, | |
510 unsigned int frame) | |
511 { | |
512 if (IsUncompressedImage(dataset)) | |
513 { | |
514 DecodeUncompressedImage(target, dataset, frame); | |
515 return true; | |
516 } | |
517 | |
518 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 | |
519 if (IsJpegLossless(dataset)) | |
520 { | |
521 LOG(INFO) << "Decoding a JPEG-LS image"; | |
522 DecodeJpegLossless(target, dataset, frame); | |
523 return true; | |
524 } | |
525 #endif | |
526 | |
527 /** | |
528 * This DICOM image format is not natively supported by | |
529 * Orthanc. As a last resort, try and decode it through | |
530 * DCMTK. This will result in higher memory consumption. This is | |
531 * actually the second example of the following page: | |
532 * http://support.dcmtk.org/docs/mod_dcmjpeg.html#Examples | |
533 **/ | |
534 | |
535 { | |
536 LOG(INFO) << "Using DCMTK to decode a compressed image"; | |
537 | |
538 std::auto_ptr<DcmDataset> converted(dynamic_cast<DcmDataset*>(dataset.clone())); | |
539 converted->chooseRepresentation(EXS_LittleEndianExplicit, NULL); | |
540 | |
541 if (converted->canWriteXfer(EXS_LittleEndianExplicit)) | |
542 { | |
543 DecodeUncompressedImageInternal(target, *converted, frame); | |
544 return true; | |
545 } | |
546 } | |
547 | |
548 return false; | |
549 } | |
329 } | 550 } |