Mercurial > hg > orthanc
comparison Core/Images/ImageProcessing.cpp @ 3600:4066998150ef
/instances/{id}/preview route now takes the windowing into account
author | Alain Mazy <alain@mazy.be> |
---|---|
date | Thu, 09 Jan 2020 18:54:40 +0100 |
parents | 113a9643e8bb |
children | 94f4a18a79cc |
comparison
equal
deleted
inserted
replaced
3599:e01900f913e7 | 3600:4066998150ef |
---|---|
487 { | 487 { |
488 memcpy(target.GetRow(y), source.GetConstRow(y), lineSize); | 488 memcpy(target.GetRow(y), source.GetConstRow(y), lineSize); |
489 } | 489 } |
490 } | 490 } |
491 | 491 |
492 template <typename TargetType, typename SourceType> | |
493 static void ApplyWindowingInternal(ImageAccessor& target, | |
494 const ImageAccessor& source, | |
495 float windowCenter, | |
496 float windowWidth, | |
497 float rescaleSlope, | |
498 float rescaleIntercept, | |
499 bool invert) | |
500 { | |
501 // WARNING - "::min()" should be replaced by "::lowest()" if | |
502 // dealing with float or double (which is not the case so far) | |
503 assert(sizeof(TargetType) <= 2); // Safeguard to remember about "float/double" | |
504 const TargetType minTargetValue = std::numeric_limits<TargetType>::min(); | |
505 const TargetType maxTargetValue = std::numeric_limits<TargetType>::max(); | |
506 const float minFloatValue = static_cast<float>(minTargetValue); | |
507 const float maxFloatValue = static_cast<float>(maxTargetValue); | |
508 | |
509 const float windowIntercept = windowCenter - windowWidth / 2.0f; | |
510 const float windowSlope = (maxFloatValue + 1.0f) / windowWidth; | |
511 | |
512 const unsigned int width = source.GetWidth(); | |
513 const unsigned int height = source.GetHeight(); | |
514 | |
515 for (unsigned int y = 0; y < height; y++) | |
516 { | |
517 TargetType* t = reinterpret_cast<TargetType*>(target.GetRow(y)); | |
518 const SourceType* s = reinterpret_cast<const SourceType*>(source.GetConstRow(y)); | |
519 | |
520 for (unsigned int x = 0; x < width; x++, t++, s++) | |
521 { | |
522 float rescaledValue = *s * rescaleSlope + rescaleIntercept; | |
523 float v = (rescaledValue - windowIntercept) * windowSlope; | |
524 if (v <= minFloatValue) | |
525 { | |
526 v = minFloatValue; | |
527 } | |
528 else if (v >= maxFloatValue) | |
529 { | |
530 v = maxFloatValue; | |
531 } | |
532 | |
533 TargetType vv = static_cast<TargetType>(v); | |
534 | |
535 if (invert) | |
536 { | |
537 vv = maxTargetValue - vv; | |
538 } | |
539 | |
540 *t = static_cast<TargetType>(vv); | |
541 } | |
542 } | |
543 } | |
492 | 544 |
493 void ImageProcessing::ApplyWindowing(ImageAccessor& target, | 545 void ImageProcessing::ApplyWindowing(ImageAccessor& target, |
494 const ImageAccessor& source, | 546 const ImageAccessor& source, |
495 float windowCenter, | 547 float windowCenter, |
496 float windowWidth, | 548 float windowWidth, |
497 Orthanc::PhotometricInterpretation sourcePhotometricInterpretation) | 549 float rescaleSlope, |
498 { | 550 float rescaleIntercept, |
499 if (source.GetFormat() != Orthanc::PixelFormat_Float32) | 551 bool invert) |
500 { | 552 { |
501 throw OrthancException(ErrorCode_NotImplemented); | |
502 } | |
503 | |
504 if (sourcePhotometricInterpretation != Orthanc::PhotometricInterpretation_Monochrome1 | |
505 && sourcePhotometricInterpretation != Orthanc::PhotometricInterpretation_Monochrome2) | |
506 { | |
507 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
508 } | |
509 | |
510 if (target.GetWidth() != source.GetWidth() || | 553 if (target.GetWidth() != source.GetWidth() || |
511 target.GetHeight() != source.GetHeight()) | 554 target.GetHeight() != source.GetHeight()) |
512 { | 555 { |
513 throw OrthancException(ErrorCode_IncompatibleImageSize); | 556 throw OrthancException(ErrorCode_IncompatibleImageSize); |
514 } | 557 } |
515 | 558 |
516 unsigned int targetBytesPerPixels = target.GetBytesPerPixel(); | 559 switch (source.GetFormat()) |
517 unsigned int targetChannelsPerPixels = 0; | 560 { |
518 switch (target.GetFormat()) | 561 case Orthanc::PixelFormat_Float32: |
519 { | 562 { |
520 case Orthanc::PixelFormat_Grayscale8: | 563 switch (target.GetFormat()) |
521 targetChannelsPerPixels = 1; | 564 { |
522 break; | 565 case Orthanc::PixelFormat_Grayscale8: |
523 case Orthanc::PixelFormat_RGBA32: | 566 ApplyWindowingInternal<uint8_t, float>(target, source, windowCenter, windowWidth, rescaleSlope, rescaleIntercept, invert); |
524 case Orthanc::PixelFormat_BGRA32: | 567 break; |
525 case Orthanc::PixelFormat_RGB24: | 568 case Orthanc::PixelFormat_Grayscale16: |
526 targetChannelsPerPixels = 3; | 569 ApplyWindowingInternal<uint16_t, float>(target, source, windowCenter, windowWidth, rescaleSlope, rescaleIntercept, invert); |
527 break; | 570 break; |
528 default: | 571 default: |
529 throw OrthancException(ErrorCode_NotImplemented); | 572 throw OrthancException(ErrorCode_NotImplemented); |
530 } | 573 } |
531 | 574 };break; |
532 const float a = windowCenter - windowWidth / 2.0f; | 575 case Orthanc::PixelFormat_Grayscale8: |
533 const float slope = 256.0f / windowWidth; | 576 { |
534 bool isInverted = sourcePhotometricInterpretation == Orthanc::PhotometricInterpretation_Monochrome1; | 577 switch (target.GetFormat()) |
535 | 578 { |
536 const unsigned int width = source.GetWidth(); | 579 case Orthanc::PixelFormat_Grayscale8: |
537 const unsigned int height = source.GetHeight(); | 580 ApplyWindowingInternal<uint8_t, uint8_t>(target, source, windowCenter, windowWidth, rescaleSlope, rescaleIntercept, invert); |
538 | 581 break; |
539 assert(sizeof(float) == 4); | 582 case Orthanc::PixelFormat_Grayscale16: |
540 | 583 ApplyWindowingInternal<uint16_t, uint8_t>(target, source, windowCenter, windowWidth, rescaleSlope, rescaleIntercept, invert); |
541 for (unsigned int y = 0; y < height; y++) | 584 break; |
542 { | 585 default: |
543 const float* p = reinterpret_cast<const float*>(source.GetConstRow(y)); | 586 throw OrthancException(ErrorCode_NotImplemented); |
544 uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y)); | 587 } |
545 | 588 };break; |
546 for (unsigned int x = 0; x < width; x++) | 589 case Orthanc::PixelFormat_Grayscale16: |
547 { | 590 { |
548 float v = (*p - a) * slope; | 591 switch (target.GetFormat()) |
549 if (v <= 0) | 592 { |
550 { | 593 case Orthanc::PixelFormat_Grayscale8: |
551 v = 0; | 594 ApplyWindowingInternal<uint8_t, uint16_t>(target, source, windowCenter, windowWidth, rescaleSlope, rescaleIntercept, invert); |
552 } | 595 break; |
553 else if (v >= 255) | 596 case Orthanc::PixelFormat_Grayscale16: |
554 { | 597 ApplyWindowingInternal<uint16_t, uint16_t>(target, source, windowCenter, windowWidth, rescaleSlope, rescaleIntercept, invert); |
555 v = 255; | 598 break; |
556 } | 599 default: |
557 | 600 throw OrthancException(ErrorCode_NotImplemented); |
558 uint8_t vv = static_cast<uint8_t>(v); | 601 } |
559 | 602 };break; |
560 if (isInverted) | 603 default: |
561 { | 604 throw OrthancException(ErrorCode_NotImplemented); |
562 vv = 255 - vv; | 605 } |
563 } | |
564 | |
565 for (unsigned int c = 0; c < targetChannelsPerPixels; c++) | |
566 { | |
567 q[c] = vv; | |
568 } | |
569 | |
570 p++; | |
571 q += targetBytesPerPixels; | |
572 } | |
573 } | |
574 | |
575 } | 606 } |
576 | 607 |
577 | 608 |
578 void ImageProcessing::Convert(ImageAccessor& target, | 609 void ImageProcessing::Convert(ImageAccessor& target, |
579 const ImageAccessor& source) | 610 const ImageAccessor& source) |