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)