Program Listing for File InterpolationVarShapeWrap.hpp

Return to documentation for file (nvcv_types/include/nvcv/cuda/InterpolationVarShapeWrap.hpp)

/*
 * SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef NVCV_CUDA_INTERPOLATION_VAR_SHAPE_WRAP_HPP
#define NVCV_CUDA_INTERPOLATION_VAR_SHAPE_WRAP_HPP

#include "BorderVarShapeWrap.hpp" // for BorderVarShapeWrap, etc.
#include "InterpolationWrap.hpp"  // for GetIndexForInterpolation, etc.

#include <nvcv/ImageBatchData.hpp> // for ImageBatchVarShapeDataStridedCuda, etc.

namespace nvcv::cuda {

namespace detail {

template<typename T, NVCVBorderType B, NVCVInterpolationType I>
class InterpolationVarShapeWrapImpl
{
public:
    using BorderWrapper     = BorderVarShapeWrap<T, B>;
    using ImageBatchWrapper = typename BorderWrapper::ImageBatchWrapper;
    using ValueType         = typename BorderWrapper::ValueType;

    static constexpr int                   kNumDimensions     = BorderWrapper::kNumDimensions;
    static constexpr NVCVInterpolationType kInterpolationType = I;

    static constexpr auto kActiveDimensions    = BorderWrapper::kActiveDimensions;
    static constexpr int  kNumActiveDimensions = BorderWrapper::kNumActiveDimensions;

    InterpolationVarShapeWrapImpl() = default;

    explicit __host__ InterpolationVarShapeWrapImpl(const ImageBatchVarShapeDataStridedCuda &images,
                                                    ValueType                                borderValue = {})
        : m_borderWrap(images, borderValue)
    {
    }

    template<typename... Args>
    explicit __host__ __device__ InterpolationVarShapeWrapImpl(ImageBatchWrapper imageBatchWrap, ValueType borderValue,
                                                               Args... tensorShape)
        : m_borderWrap(imageBatchWrap, borderValue, tensorShape...)
    {
    }

    explicit __host__ __device__ InterpolationVarShapeWrapImpl(BorderWrapper borderWrap)
        : m_borderWrap(borderWrap)
    {
    }

    inline const __host__ __device__ BorderWrapper &borderWrap() const
    {
        return m_borderWrap;
    }

    inline __host__ __device__ float scaleX() const
    {
        return float{};
    }

    inline __host__ __device__ float scaleY() const
    {
        return float{};
    }

    inline __host__ __device__ bool isIntegerArea() const
    {
        return bool{};
    }

protected:
    template<typename DimType,
             class = Require<
                 std::is_same_v<BaseType<DimType>, float> && (NumElements<DimType> == 3 || NumElements<DimType> == 4)>>
    inline const __host__ __device__ ValueType &doGetValue(DimType c) const
    {
        return m_borderWrap[StaticCast<int>(c)];
    }

    inline const __host__ __device__ ValueType &doGetValue(float3 c, int x, int y) const
    {
        return m_borderWrap[int3{x, y, static_cast<int>(c.z)}];
    }

    inline const __host__ __device__ ValueType &doGetValue(float4 c, int x, int y) const
    {
        return m_borderWrap[int4{x, y, static_cast<int>(c.z), static_cast<int>(c.w)}];
    }

    const BorderWrapper m_borderWrap = {};
};

} // namespace detail

template<typename T, NVCVBorderType B, NVCVInterpolationType I>
class InterpolationVarShapeWrap : public detail::InterpolationVarShapeWrapImpl<T, B, I>
{
};

template<typename T, NVCVBorderType B>
class InterpolationVarShapeWrap<T, B, NVCV_INTERP_NEAREST>
    : public detail::InterpolationVarShapeWrapImpl<T, B, NVCV_INTERP_NEAREST>
{
    using Base = detail::InterpolationVarShapeWrapImpl<T, B, NVCV_INTERP_NEAREST>;

public:
    using typename Base::BorderWrapper;
    using typename Base::ImageBatchWrapper;
    using typename Base::ValueType;

    using Base::kInterpolationType;
    using Base::kNumDimensions;

    InterpolationVarShapeWrap() = default;

    explicit __host__ InterpolationVarShapeWrap(const ImageBatchVarShapeDataStridedCuda &images,
                                                ValueType borderValue = {}, float scaleX = {}, float scaleY = {})
        : Base(images, borderValue)
    {
    }

    template<typename... Args>
    explicit __host__ __device__ InterpolationVarShapeWrap(ImageBatchWrapper imageBatchWrap, ValueType borderValue,
                                                           float scaleX, float scaleY, Args... tensorShape)
        : Base(imageBatchWrap, borderValue, tensorShape...)
    {
    }

    explicit __host__ __device__ InterpolationVarShapeWrap(BorderWrapper borderWrap, float scaleX = {},
                                                           float scaleY = {})
        : Base(borderWrap)
    {
    }

    // Get the border wrap wrapped by this interpolation wrap.
    using Base::borderWrap;

    // Get the scale X of this interpolation wrap, none is stored so an empty value is returned.
    using Base::scaleX;

    // Get the scale Y of this interpolation wrap, none is stored so an empty value is returned.
    using Base::scaleY;

    // True if this interpolation wrap is integer Area, none is stored so an empty value is returned.
    using Base::isIntegerArea;

    template<typename DimType,
             class = Require<
                 std::is_same_v<BaseType<DimType>, float> && (NumElements<DimType> == 3 || NumElements<DimType> == 4)>>
    inline __host__ __device__ ValueType operator[](DimType c) const
    {
        c.x = GetIndexForInterpolation<kInterpolationType>(c.x + .5f);
        c.y = GetIndexForInterpolation<kInterpolationType>(c.y + .5f);

        return doGetValue(c);
    }
};

template<typename T, NVCVBorderType B>
class InterpolationVarShapeWrap<T, B, NVCV_INTERP_LINEAR>
    : public detail::InterpolationVarShapeWrapImpl<T, B, NVCV_INTERP_LINEAR>
{
    using Base = detail::InterpolationVarShapeWrapImpl<T, B, NVCV_INTERP_LINEAR>;

public:
    using typename Base::BorderWrapper;
    using typename Base::ImageBatchWrapper;
    using typename Base::ValueType;

    using Base::kInterpolationType;
    using Base::kNumDimensions;

    InterpolationVarShapeWrap() = default;

    explicit __host__ InterpolationVarShapeWrap(const ImageBatchVarShapeDataStridedCuda &images,
                                                ValueType borderValue = {}, float scaleX = {}, float scaleY = {})
        : Base(images, borderValue)
    {
    }

    template<typename... Args>
    explicit __host__ __device__ InterpolationVarShapeWrap(ImageBatchWrapper imageBatchWrap, ValueType borderValue,
                                                           float scaleX, float scaleY, Args... tensorShape)
        : Base(imageBatchWrap, borderValue, tensorShape...)
    {
    }

    explicit __host__ __device__ InterpolationVarShapeWrap(BorderWrapper borderWrap, float scaleX = {},
                                                           float scaleY = {})
        : Base(borderWrap)
    {
    }

    // Get the border wrap wrapped by this interpolation wrap.
    using Base::borderWrap;

    // Get the scale X of this interpolation wrap, none is stored so an empty value is returned.
    using Base::scaleX;

    // Get the scale Y of this interpolation wrap, none is stored so an empty value is returned.
    using Base::scaleY;

    // True if this interpolation wrap is integer Area, none is stored so an empty value is returned.
    using Base::isIntegerArea;

    template<typename DimType,
             class = Require<
                 std::is_same_v<BaseType<DimType>, float> && (NumElements<DimType> == 3 || NumElements<DimType> == 4)>>
    inline __host__ __device__ ValueType operator[](DimType c) const
    {
        const int x1 = GetIndexForInterpolation<kInterpolationType>(c.x);
        const int x2 = x1 + 1;
        const int y1 = GetIndexForInterpolation<kInterpolationType>(c.y);
        const int y2 = y1 + 1;

        auto out = SetAll<ConvertBaseTypeTo<float, std::remove_cv_t<ValueType>>>(0);

        out += Base::doGetValue(c, x1, y1) * (x2 - c.x) * (y2 - c.y);
        out += Base::doGetValue(c, x2, y1) * (c.x - x1) * (y2 - c.y);
        out += Base::doGetValue(c, x1, y2) * (x2 - c.x) * (c.y - y1);
        out += Base::doGetValue(c, x2, y2) * (c.x - x1) * (c.y - y1);

        return SaturateCast<ValueType>(out);
    }
};

template<typename T, NVCVBorderType B>
class InterpolationVarShapeWrap<T, B, NVCV_INTERP_CUBIC>
    : public detail::InterpolationVarShapeWrapImpl<T, B, NVCV_INTERP_CUBIC>
{
    using Base = detail::InterpolationVarShapeWrapImpl<T, B, NVCV_INTERP_CUBIC>;

public:
    using typename Base::BorderWrapper;
    using typename Base::ImageBatchWrapper;
    using typename Base::ValueType;

    using Base::kInterpolationType;
    using Base::kNumDimensions;

    InterpolationVarShapeWrap() = default;

    explicit __host__ InterpolationVarShapeWrap(const ImageBatchVarShapeDataStridedCuda &images,
                                                ValueType borderValue = {}, float scaleX = {}, float scaleY = {})
        : Base(images, borderValue)
    {
    }

    template<typename... Args>
    explicit __host__ __device__ InterpolationVarShapeWrap(ImageBatchWrapper imageBatchWrap, ValueType borderValue,
                                                           float scaleX, float scaleY, Args... tensorShape)
        : Base(imageBatchWrap, borderValue, tensorShape...)
    {
    }

    explicit __host__ __device__ InterpolationVarShapeWrap(BorderWrapper borderWrap, float scaleX = {},
                                                           float scaleY = {})
        : Base(borderWrap)
    {
    }

    // Get the border wrap wrapped by this interpolation wrap.
    using Base::borderWrap;

    // Get the scale X of this interpolation wrap, none is stored so an empty value is returned.
    using Base::scaleX;

    // Get the scale Y of this interpolation wrap, none is stored so an empty value is returned.
    using Base::scaleY;

    // True if this interpolation wrap is integer Area, none is stored so an empty value is returned.
    using Base::isIntegerArea;

    template<typename DimType,
             class = Require<
                 std::is_same_v<BaseType<DimType>, float> && (NumElements<DimType> == 3 || NumElements<DimType> == 4)>>
    inline __host__ __device__ ValueType operator[](DimType c) const
    {
        const int xmin = GetIndexForInterpolation<kInterpolationType, 1>(c.x - 2.f);
        const int xmax = GetIndexForInterpolation<kInterpolationType, 2>(c.x + 2.f);
        const int ymin = GetIndexForInterpolation<kInterpolationType, 1>(c.y - 2.f);
        const int ymax = GetIndexForInterpolation<kInterpolationType, 2>(c.y + 2.f);

        using FT = ConvertBaseTypeTo<float, std::remove_cv_t<ValueType>>;
        auto sum = SetAll<FT>(0);

        float w, wsum = 0.f;

        for (int cy = ymin; cy <= ymax; cy++)
        {
            for (int cx = xmin; cx <= xmax; cx++)
            {
                w = GetCubicCoeff(c.x - cx) * GetCubicCoeff(c.y - cy);
                sum += w * Base::doGetValue(c, cx, cy);
                wsum += w;
            }
        }

        sum = (wsum == 0.f) ? SetAll<FT>(0) : sum / wsum;

        return SaturateCast<ValueType>(sum);
    }
};

template<typename T, NVCVBorderType B>
class InterpolationVarShapeWrap<T, B, NVCV_INTERP_AREA>
    : public detail::InterpolationVarShapeWrapImpl<T, B, NVCV_INTERP_AREA>
{
    using Base = detail::InterpolationVarShapeWrapImpl<T, B, NVCV_INTERP_AREA>;

public:
    using typename Base::BorderWrapper;
    using typename Base::ImageBatchWrapper;
    using typename Base::ValueType;

    using Base::kInterpolationType;
    using Base::kNumDimensions;

    InterpolationVarShapeWrap() = default;

    explicit __host__ InterpolationVarShapeWrap(const ImageBatchVarShapeDataStridedCuda &images,
                                                ValueType borderValue = {}, float scaleX = {}, float scaleY = {})
        : Base(images, borderValue)
        , m_scaleX(scaleX)
        , m_scaleY(scaleY)
        , m_isIntegerArea(isIntegerArea(scaleX, scaleY))
    {
    }

    template<typename... Args>
    explicit __host__ __device__ InterpolationVarShapeWrap(ImageBatchWrapper imageBatchWrap, ValueType borderValue,
                                                           float scaleX, float scaleY, Args... tensorShape)
        : Base(imageBatchWrap, borderValue, tensorShape...)
        , m_scaleX(scaleX)
        , m_scaleY(scaleY)
        , m_isIntegerArea(isIntegerArea(scaleX, scaleY))
    {
    }

    explicit __host__ __device__ InterpolationVarShapeWrap(BorderWrapper borderWrap, float scaleX = {},
                                                           float scaleY = {})
        : Base(borderWrap)
        , m_scaleX(scaleX)
        , m_scaleY(scaleY)
        , m_isIntegerArea(isIntegerArea(scaleX, scaleY))
    {
    }

    // Get the border wrap wrapped by this interpolation wrap.
    using Base::borderWrap;

    // Get the Area scale X of this interpolation wrap.
    inline __host__ __device__ float scaleX() const
    {
        return m_scaleX;
    }

    // Get the Area scale Y of this interpolation wrap.
    inline __host__ __device__ float scaleY() const
    {
        return m_scaleY;
    }

    // True if this interpolation wrap is integer Area.
    inline __host__ __device__ bool isIntegerArea() const
    {
        return m_isIntegerArea;
    }

    template<typename DimType,
             class = Require<
                 std::is_same_v<BaseType<DimType>, float> && (NumElements<DimType> == 3 || NumElements<DimType> == 4)>>
    inline __host__ __device__ ValueType operator[](DimType c) const
    {
        const float fsx1 = c.x * m_scaleX;
        const float fsy1 = c.y * m_scaleY;
        const float fsx2 = fsx1 + m_scaleX;
        const float fsy2 = fsy1 + m_scaleY;
        const int   xmin = GetIndexForInterpolation<kInterpolationType, 1>(fsx1);
        const int   xmax = GetIndexForInterpolation<kInterpolationType, 2>(fsx2);
        const int   ymin = GetIndexForInterpolation<kInterpolationType, 1>(fsy1);
        const int   ymax = GetIndexForInterpolation<kInterpolationType, 2>(fsy2);

        auto out = SetAll<ConvertBaseTypeTo<float, std::remove_cv_t<ValueType>>>(0);

        if (m_isIntegerArea)
        {
            const float scale = 1.f / (m_scaleX * m_scaleY);

            for (int cy = ymin; cy < ymax; ++cy)
            {
                for (int cx = xmin; cx < xmax; ++cx)
                {
                    out += Base::doGetValue(c, cx, cy) * scale;
                }
            }
        }
        else
        {
            int w, h;

            if constexpr (NumElements<DimType> == 3)
                w = Base::m_borderWrap.imageBatchWrap().width(static_cast<int>(c.z));
            else
                w = Base::m_borderWrap.imageBatchWrap().width(static_cast<int>(c.w), static_cast<int>(c.z));

            if constexpr (NumElements<DimType> == 3)
                h = Base::m_borderWrap.imageBatchWrap().height(static_cast<int>(c.z));
            else
                h = Base::m_borderWrap.imageBatchWrap().height(static_cast<int>(c.w), static_cast<int>(c.z));

            const float scale = 1.f / (min(m_scaleX, w - fsx1) * min(m_scaleY, h - fsy1));

            for (int cy = ymin; cy < ymax; ++cy)
            {
                for (int cx = xmin; cx < xmax; ++cx)
                {
                    out += Base::doGetValue(c, cx, cy) * scale;
                }

                if (xmin > fsx1)
                {
                    out += Base::doGetValue(c, (xmin - 1), cy) * ((xmin - fsx1) * scale);
                }

                if (xmax < fsx2)
                {
                    out += Base::doGetValue(c, xmax, cy) * ((fsx2 - xmax) * scale);
                }
            }

            if (ymin > fsy1)
            {
                for (int cx = xmin; cx < xmax; ++cx)
                {
                    out += Base::doGetValue(c, cx, (ymin - 1)) * ((ymin - fsy1) * scale);
                }

                if (xmin > fsx1)
                {
                    out += Base::doGetValue(c, (xmin - 1), (ymin - 1)) * ((ymin - fsy1) * (xmin - fsx1) * scale);
                }

                if (xmax < fsx2)
                {
                    out += Base::doGetValue(c, xmax, (ymin - 1)) * ((ymin - fsy1) * (fsx2 - xmax) * scale);
                }
            }

            if (ymax < fsy2)
            {
                for (int cx = xmin; cx < xmax; ++cx)
                {
                    out += Base::doGetValue(c, cx, ymax) * ((fsy2 - ymax) * scale);
                }

                if (xmax < fsx2)
                {
                    out += Base::doGetValue(c, xmax, ymax) * ((fsy2 - ymax) * (fsx2 - xmax) * scale);
                }

                if (xmin > fsx1)
                {
                    out += Base::doGetValue(c, (xmin - 1), ymax) * ((fsy2 - ymax) * (xmin - fsx1) * scale);
                }
            }
        }

        return SaturateCast<ValueType>(out);
    }

private:
    inline __host__ __device__ bool isIntegerArea(float scaleX, float scaleY) const
    {
        return cuda::round<RoundMode::UP, int>(scaleX) == scaleX && cuda::round<RoundMode::UP, int>(scaleY) == scaleY;
    }

    const float m_scaleX        = {};
    const float m_scaleY        = {};
    const bool  m_isIntegerArea = {};
};

} // namespace nvcv::cuda

#endif // NVCV_CUDA_INTERPOLATION_VAR_SHAPE_WRAP_HPP