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, &parameters, 495 OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, &parameters,
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 }