Mercurial > hg > orthanc
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); |