Mercurial > hg > orthanc
comparison Core/Images/ImageProcessing.cpp @ 3502:c160eafc42a9
new functions in ImageProcessing toolbox: FlipX/Y(), Resize(), Halve()
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 26 Aug 2019 10:23:51 +0200 |
parents | d8f7c3970e25 |
children | 46cf170ba121 |
comparison
equal
deleted
inserted
replaced
3501:27b53c61aa99 | 3502:c160eafc42a9 |
---|---|
32 | 32 |
33 | 33 |
34 #include "../PrecompiledHeaders.h" | 34 #include "../PrecompiledHeaders.h" |
35 #include "ImageProcessing.h" | 35 #include "ImageProcessing.h" |
36 | 36 |
37 #include "Image.h" | |
38 #include "ImageTraits.h" | |
37 #include "PixelTraits.h" | 39 #include "PixelTraits.h" |
38 #include "../OrthancException.h" | 40 #include "../OrthancException.h" |
39 | 41 |
40 #include <boost/math/special_functions/round.hpp> | 42 #include <boost/math/special_functions/round.hpp> |
41 | 43 |
1550 } | 1552 } |
1551 default: | 1553 default: |
1552 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | 1554 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); |
1553 } | 1555 } |
1554 } | 1556 } |
1557 | |
1558 | |
1559 template <PixelFormat Format> | |
1560 static void ResizeInternal(ImageAccessor& target, | |
1561 const ImageAccessor& source) | |
1562 { | |
1563 assert(target.GetFormat() == source.GetFormat() && | |
1564 target.GetFormat() == Format); | |
1565 | |
1566 const unsigned int sourceWidth = source.GetWidth(); | |
1567 const unsigned int sourceHeight = source.GetHeight(); | |
1568 const unsigned int targetWidth = target.GetWidth(); | |
1569 const unsigned int targetHeight = target.GetHeight(); | |
1570 | |
1571 if (targetWidth == 0 || targetHeight == 0) | |
1572 { | |
1573 return; | |
1574 } | |
1575 | |
1576 if (sourceWidth == 0 || sourceHeight == 0) | |
1577 { | |
1578 // Avoids division by zero below | |
1579 ImageProcessing::Set(target, 0); | |
1580 return; | |
1581 } | |
1582 | |
1583 const float scaleX = static_cast<float>(sourceWidth) / static_cast<float>(targetWidth); | |
1584 const float scaleY = static_cast<float>(sourceHeight) / static_cast<float>(targetHeight); | |
1585 | |
1586 | |
1587 /** | |
1588 * Create two lookup tables to quickly know the (x,y) position | |
1589 * in the source image, given the (x,y) position in the target | |
1590 * image. | |
1591 **/ | |
1592 | |
1593 std::vector<unsigned int> lookupX(targetWidth); | |
1594 | |
1595 for (unsigned int x = 0; x < targetWidth; x++) | |
1596 { | |
1597 int sourceX = std::floor((static_cast<float>(x) + 0.5f) * scaleX); | |
1598 if (sourceX < 0) | |
1599 { | |
1600 sourceX = 0; // Should never happen | |
1601 } | |
1602 else if (sourceX >= static_cast<int>(sourceWidth)) | |
1603 { | |
1604 sourceX = sourceWidth - 1; | |
1605 } | |
1606 | |
1607 lookupX[x] = static_cast<unsigned int>(sourceX); | |
1608 } | |
1609 | |
1610 std::vector<unsigned int> lookupY(targetHeight); | |
1611 | |
1612 for (unsigned int y = 0; y < targetHeight; y++) | |
1613 { | |
1614 int sourceY = std::floor((static_cast<float>(y) + 0.5f) * scaleY); | |
1615 if (sourceY < 0) | |
1616 { | |
1617 sourceY = 0; // Should never happen | |
1618 } | |
1619 else if (sourceY >= static_cast<int>(sourceHeight)) | |
1620 { | |
1621 sourceY = sourceHeight - 1; | |
1622 } | |
1623 | |
1624 lookupY[y] = static_cast<unsigned int>(sourceY); | |
1625 } | |
1626 | |
1627 | |
1628 /** | |
1629 * Actual resizing | |
1630 **/ | |
1631 | |
1632 for (unsigned int targetY = 0; targetY < targetHeight; targetY++) | |
1633 { | |
1634 unsigned int sourceY = lookupY[targetY]; | |
1635 | |
1636 for (unsigned int targetX = 0; targetX < targetWidth; targetX++) | |
1637 { | |
1638 unsigned int sourceX = lookupX[targetX]; | |
1639 | |
1640 typename ImageTraits<Format>::PixelType pixel; | |
1641 ImageTraits<Format>::GetPixel(pixel, source, sourceX, sourceY); | |
1642 ImageTraits<Format>::SetPixel(target, pixel, targetX, targetY); | |
1643 } | |
1644 } | |
1645 } | |
1646 | |
1647 | |
1648 | |
1649 void ImageProcessing::Resize(ImageAccessor& target, | |
1650 const ImageAccessor& source) | |
1651 { | |
1652 if (source.GetFormat() != source.GetFormat()) | |
1653 { | |
1654 throw OrthancException(ErrorCode_IncompatibleImageFormat); | |
1655 } | |
1656 | |
1657 if (source.GetWidth() == target.GetWidth() && | |
1658 source.GetHeight() == target.GetHeight()) | |
1659 { | |
1660 Copy(target, source); | |
1661 return; | |
1662 } | |
1663 | |
1664 switch (source.GetFormat()) | |
1665 { | |
1666 case PixelFormat_Grayscale8: | |
1667 ResizeInternal<PixelFormat_Grayscale8>(target, source); | |
1668 break; | |
1669 | |
1670 case PixelFormat_RGB24: | |
1671 ResizeInternal<PixelFormat_RGB24>(target, source); | |
1672 break; | |
1673 | |
1674 default: | |
1675 throw OrthancException(ErrorCode_NotImplemented); | |
1676 } | |
1677 } | |
1678 | |
1679 | |
1680 ImageAccessor* ImageProcessing::Halve(const ImageAccessor& source, | |
1681 bool forceMinimalPitch) | |
1682 { | |
1683 std::auto_ptr<Image> target(new Image(source.GetFormat(), source.GetWidth() / 2, | |
1684 source.GetHeight() / 2, forceMinimalPitch)); | |
1685 Resize(*target, source); | |
1686 return target.release(); | |
1687 } | |
1688 | |
1689 | |
1690 template <PixelFormat Format> | |
1691 static void FlipXInternal(ImageAccessor& image) | |
1692 { | |
1693 const unsigned int height = image.GetHeight(); | |
1694 const unsigned int width = image.GetWidth(); | |
1695 | |
1696 for (unsigned int y = 0; y < height; y++) | |
1697 { | |
1698 for (unsigned int x1 = 0; x1 < width / 2; x1++) | |
1699 { | |
1700 unsigned int x2 = width - 1 - x1; | |
1701 | |
1702 typename ImageTraits<Format>::PixelType a, b; | |
1703 ImageTraits<Format>::GetPixel(a, image, x1, y); | |
1704 ImageTraits<Format>::GetPixel(b, image, x2, y); | |
1705 ImageTraits<Format>::SetPixel(image, a, x2, y); | |
1706 ImageTraits<Format>::SetPixel(image, b, x1, y); | |
1707 } | |
1708 } | |
1709 } | |
1710 | |
1711 | |
1712 void ImageProcessing::FlipX(ImageAccessor& image) | |
1713 { | |
1714 switch (image.GetFormat()) | |
1715 { | |
1716 case PixelFormat_Grayscale8: | |
1717 FlipXInternal<PixelFormat_Grayscale8>(image); | |
1718 break; | |
1719 | |
1720 case PixelFormat_RGB24: | |
1721 FlipXInternal<PixelFormat_RGB24>(image); | |
1722 break; | |
1723 | |
1724 default: | |
1725 throw OrthancException(ErrorCode_NotImplemented); | |
1726 } | |
1727 } | |
1728 | |
1729 | |
1730 template <PixelFormat Format> | |
1731 static void FlipYInternal(ImageAccessor& image) | |
1732 { | |
1733 const unsigned int height = image.GetHeight(); | |
1734 const unsigned int width = image.GetWidth(); | |
1735 | |
1736 for (unsigned int y1 = 0; y1 < height / 2; y1++) | |
1737 { | |
1738 unsigned int y2 = height - 1 - y1; | |
1739 | |
1740 for (unsigned int x = 0; x < width; x++) | |
1741 { | |
1742 typename ImageTraits<Format>::PixelType a, b; | |
1743 ImageTraits<Format>::GetPixel(a, image, x, y1); | |
1744 ImageTraits<Format>::GetPixel(b, image, x, y2); | |
1745 ImageTraits<Format>::SetPixel(image, a, x, y2); | |
1746 ImageTraits<Format>::SetPixel(image, b, x, y1); | |
1747 } | |
1748 } | |
1749 } | |
1750 | |
1751 | |
1752 void ImageProcessing::FlipY(ImageAccessor& image) | |
1753 { | |
1754 switch (image.GetFormat()) | |
1755 { | |
1756 case PixelFormat_Grayscale8: | |
1757 FlipYInternal<PixelFormat_Grayscale8>(image); | |
1758 break; | |
1759 | |
1760 case PixelFormat_RGB24: | |
1761 FlipYInternal<PixelFormat_RGB24>(image); | |
1762 break; | |
1763 | |
1764 default: | |
1765 throw OrthancException(ErrorCode_NotImplemented); | |
1766 } | |
1767 } | |
1555 } | 1768 } |