comparison OrthancFramework/Sources/Images/ImageProcessing.cpp @ 4974:fcdf399f9fc0

fix ImageProcessing::ShiftScale2() on floating-point images
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 08 Apr 2022 11:48:54 +0200
parents 5f6b13202e85
children 0ea402b4d901
comparison
equal deleted inserted replaced
4973:17c91e054636 4974:fcdf399f9fc0
412 // "ShiftScale2()" 412 // "ShiftScale2()"
413 template <typename TargetType, 413 template <typename TargetType,
414 typename SourceType, 414 typename SourceType,
415 bool UseRound, 415 bool UseRound,
416 bool Invert> 416 bool Invert>
417 static void ShiftScaleInternal(ImageAccessor& target, 417 static void ShiftScaleIntegerInternal(ImageAccessor& target,
418 const ImageAccessor& source, 418 const ImageAccessor& source,
419 float a, 419 float a,
420 float b, 420 float b)
421 const TargetType LowestValue)
422 // This function can be applied inplace (source == target) 421 // This function can be applied inplace (source == target)
423 { 422 {
423 assert(target.GetFormat() != PixelFormat_Float32);
424
424 if (source.GetWidth() != target.GetWidth() || 425 if (source.GetWidth() != target.GetWidth() ||
425 source.GetHeight() != target.GetHeight()) 426 source.GetHeight() != target.GetHeight())
426 { 427 {
427 throw OrthancException(ErrorCode_IncompatibleImageSize); 428 throw OrthancException(ErrorCode_IncompatibleImageSize);
428 } 429 }
431 source.GetFormat() != target.GetFormat()) 432 source.GetFormat() != target.GetFormat())
432 { 433 {
433 throw OrthancException(ErrorCode_IncompatibleImageFormat); 434 throw OrthancException(ErrorCode_IncompatibleImageFormat);
434 } 435 }
435 436
436 const TargetType minPixelValue = LowestValue; 437 const TargetType minPixelValue = std::numeric_limits<TargetType>::min();
437 const TargetType maxPixelValue = std::numeric_limits<TargetType>::max(); 438 const TargetType maxPixelValue = std::numeric_limits<TargetType>::max();
438 const float minFloatValue = static_cast<float>(LowestValue); 439 const float minFloatValue = static_cast<float>(minPixelValue);
439 const float maxFloatValue = static_cast<float>(maxPixelValue); 440 const float maxFloatValue = static_cast<float>(maxPixelValue);
440 441
441 const unsigned int height = target.GetHeight(); 442 const unsigned int height = target.GetHeight();
442 const unsigned int width = target.GetWidth(); 443 const unsigned int width = target.GetWidth();
443 444
474 *p = maxPixelValue - *p; 475 *p = maxPixelValue - *p;
475 } 476 }
476 } 477 }
477 } 478 }
478 } 479 }
480
481
482 template <typename SourceType>
483 static void ShiftScaleFloatInternal(ImageAccessor& target,
484 const ImageAccessor& source,
485 float a,
486 float b)
487 // This function can be applied inplace (source == target)
488 {
489 assert(target.GetFormat() == PixelFormat_Float32);
490
491 if (source.GetWidth() != target.GetWidth() ||
492 source.GetHeight() != target.GetHeight())
493 {
494 throw OrthancException(ErrorCode_IncompatibleImageSize);
495 }
496
497 if (&source == &target &&
498 source.GetFormat() != target.GetFormat())
499 {
500 throw OrthancException(ErrorCode_IncompatibleImageFormat);
501 }
502
503 const unsigned int height = target.GetHeight();
504 const unsigned int width = target.GetWidth();
505
506 for (unsigned int y = 0; y < height; y++)
507 {
508 float* p = reinterpret_cast<float*>(target.GetRow(y));
509 const SourceType* q = reinterpret_cast<const SourceType*>(source.GetConstRow(y));
510
511 for (unsigned int x = 0; x < width; x++, p++, q++)
512 {
513 *p = a * static_cast<float>(*q) + b;
514 }
515 }
516 }
517
479 518
480 template <typename PixelType> 519 template <typename PixelType>
481 static void ShiftRightInternal(ImageAccessor& image, 520 static void ShiftRightInternal(ImageAccessor& image,
482 unsigned int shift) 521 unsigned int shift)
483 { 522 {
547 bool invert) 586 bool invert)
548 { 587 {
549 assert(sizeof(SourceType) == source.GetBytesPerPixel() && 588 assert(sizeof(SourceType) == source.GetBytesPerPixel() &&
550 sizeof(TargetType) == target.GetBytesPerPixel()); 589 sizeof(TargetType) == target.GetBytesPerPixel());
551 590
552 // WARNING - "::min()" should be replaced by "::lowest()" if
553 // dealing with float or double (which is not the case so far)
554 assert(sizeof(TargetType) <= 2); // Safeguard to remember about "float/double"
555 const TargetType minTargetValue = std::numeric_limits<TargetType>::min();
556 const TargetType maxTargetValue = std::numeric_limits<TargetType>::max(); 591 const TargetType maxTargetValue = std::numeric_limits<TargetType>::max();
557 const float maxFloatValue = static_cast<float>(maxTargetValue); 592 const float maxFloatValue = static_cast<float>(maxTargetValue);
558 593
559 const float windowIntercept = windowCenter - windowWidth / 2.0f; 594 const float windowIntercept = windowCenter - windowWidth / 2.0f;
560 const float windowSlope = (maxFloatValue + 1.0f) / windowWidth; 595 const float windowSlope = (maxFloatValue + 1.0f) / windowWidth;
562 const float a = rescaleSlope * windowSlope; 597 const float a = rescaleSlope * windowSlope;
563 const float b = (rescaleIntercept - windowIntercept) * windowSlope; 598 const float b = (rescaleIntercept - windowIntercept) * windowSlope;
564 599
565 if (invert) 600 if (invert)
566 { 601 {
567 ShiftScaleInternal<TargetType, SourceType, false, true>(target, source, a, b, minTargetValue); 602 ShiftScaleIntegerInternal<TargetType, SourceType, false, true>(target, source, a, b);
568 } 603 }
569 else 604 else
570 { 605 {
571 ShiftScaleInternal<TargetType, SourceType, false, false>(target, source, a, b, minTargetValue); 606 ShiftScaleIntegerInternal<TargetType, SourceType, false, false>(target, source, a, b);
572 } 607 }
573 } 608 }
574 609
575 void ImageProcessing::ApplyWindowing_Deprecated(ImageAccessor& target, 610 void ImageProcessing::ApplyWindowing_Deprecated(ImageAccessor& target,
576 const ImageAccessor& source, 611 const ImageAccessor& source,
1433 switch (image.GetFormat()) 1468 switch (image.GetFormat())
1434 { 1469 {
1435 case PixelFormat_Grayscale8: 1470 case PixelFormat_Grayscale8:
1436 if (useRound) 1471 if (useRound)
1437 { 1472 {
1438 ShiftScaleInternal<uint8_t, uint8_t, true, false>(image, image, a, b, std::numeric_limits<uint8_t>::min()); 1473 ShiftScaleIntegerInternal<uint8_t, uint8_t, true, false>(image, image, a, b);
1439 } 1474 }
1440 else 1475 else
1441 { 1476 {
1442 ShiftScaleInternal<uint8_t, uint8_t, false, false>(image, image, a, b, std::numeric_limits<uint8_t>::min()); 1477 ShiftScaleIntegerInternal<uint8_t, uint8_t, false, false>(image, image, a, b);
1443 } 1478 }
1444 return; 1479 return;
1445 1480
1446 case PixelFormat_Grayscale16: 1481 case PixelFormat_Grayscale16:
1447 if (useRound) 1482 if (useRound)
1448 { 1483 {
1449 ShiftScaleInternal<uint16_t, uint16_t, true, false>(image, image, a, b, std::numeric_limits<uint16_t>::min()); 1484 ShiftScaleIntegerInternal<uint16_t, uint16_t, true, false>(image, image, a, b);
1450 } 1485 }
1451 else 1486 else
1452 { 1487 {
1453 ShiftScaleInternal<uint16_t, uint16_t, false, false>(image, image, a, b, std::numeric_limits<uint16_t>::min()); 1488 ShiftScaleIntegerInternal<uint16_t, uint16_t, false, false>(image, image, a, b);
1454 } 1489 }
1455 return; 1490 return;
1456 1491
1457 case PixelFormat_SignedGrayscale16: 1492 case PixelFormat_SignedGrayscale16:
1458 if (useRound) 1493 if (useRound)
1459 { 1494 {
1460 ShiftScaleInternal<int16_t, int16_t, true, false>(image, image, a, b, std::numeric_limits<int16_t>::min()); 1495 ShiftScaleIntegerInternal<int16_t, int16_t, true, false>(image, image, a, b);
1461 } 1496 }
1462 else 1497 else
1463 { 1498 {
1464 ShiftScaleInternal<int16_t, int16_t, false, false>(image, image, a, b, std::numeric_limits<int16_t>::min()); 1499 ShiftScaleIntegerInternal<int16_t, int16_t, false, false>(image, image, a, b);
1465 } 1500 }
1466 return; 1501 return;
1467 1502
1468 case PixelFormat_Float32: 1503 case PixelFormat_Float32:
1469 // "::min()" must be replaced by "::lowest()" or "-::max()" if dealing with float or double.
1470 if (useRound) 1504 if (useRound)
1471 { 1505 {
1472 ShiftScaleInternal<float, float, true, false>(image, image, a, b, -std::numeric_limits<float>::max()); 1506 ShiftScaleFloatInternal<float>(image, image, a, b);
1473 } 1507 }
1474 else 1508 else
1475 { 1509 {
1476 ShiftScaleInternal<float, float, false, false>(image, image, a, b, -std::numeric_limits<float>::max()); 1510 ShiftScaleFloatInternal<float>(image, image, a, b);
1477 } 1511 }
1478 return; 1512 return;
1479 1513
1480 default: 1514 default:
1481 throw OrthancException(ErrorCode_NotImplemented); 1515 throw OrthancException(ErrorCode_NotImplemented);
1507 switch (source.GetFormat()) 1541 switch (source.GetFormat())
1508 { 1542 {
1509 case PixelFormat_Float32: 1543 case PixelFormat_Float32:
1510 if (useRound) 1544 if (useRound)
1511 { 1545 {
1512 ShiftScaleInternal<uint8_t, float, true, false>( 1546 ShiftScaleIntegerInternal<uint8_t, float, true, false>(target, source, a, b);
1513 target, source, a, b, std::numeric_limits<uint8_t>::min());
1514 } 1547 }
1515 else 1548 else
1516 { 1549 {
1517 ShiftScaleInternal<uint8_t, float, false, false>( 1550 ShiftScaleIntegerInternal<uint8_t, float, false, false>(target, source, a, b);
1518 target, source, a, b, std::numeric_limits<uint8_t>::min());
1519 } 1551 }
1520 return; 1552 return;
1521 1553
1522 default: 1554 default:
1523 throw OrthancException(ErrorCode_NotImplemented); 1555 throw OrthancException(ErrorCode_NotImplemented);