LCOV - code coverage report
Current view: top level - nntrainer/layers - upsample2d_layer.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 94.9 % 98 93
Test Date: 2025-12-14 20:38:17 Functions: 100.0 % 5 5

            Line data    Source code
       1              : // SPDX-License-Identifier: Apache-2.0
       2              : /**
       3              :  * Copyright (C) 2024 heka1024 <heka1024@gmail.com>
       4              :  *
       5              :  * @file   upsample2d_layer.h
       6              :  * @date   15 June 2024
       7              :  * @brief  It is a implementation of upsample layer for given size and
       8              :  * interpolation method
       9              :  * @see    https://github.com/nnstreamer/nntrainer
      10              :  * @author heka1024 <heka1024@gmail.com>
      11              :  * @bug    No known bugs except for NYI items
      12              :  */
      13              : 
      14              : #include <layer_context.h>
      15              : #include <node_exporter.h>
      16              : #include <upsample2d_layer.h>
      17              : 
      18              : namespace nntrainer {
      19              : static constexpr size_t SINGLE_INOUT_IDX = 0;
      20              : 
      21           23 : Upsample2dLayer::Upsample2dLayer() :
      22              :   Layer(),
      23              :   upsample2d_props(props::UpsampleMode(),
      24           46 :                    std::array<props::KernelSize, UPSAMPLE2D_DIM>()) {}
      25              : 
      26            9 : void Upsample2dLayer::finalize(nntrainer::InitLayerContext &context) {
      27            9 :   std::vector<nntrainer::TensorDim> dim = context.getInputDimensions();
      28              : 
      29              :   const auto &kernel_size =
      30              :     std::get<std::array<props::KernelSize, UPSAMPLE2D_DIM>>(upsample2d_props);
      31              : 
      32           18 :   for (unsigned int i = 0; i < dim.size(); ++i) {
      33            9 :     if (dim[i].getDataLen() == 0) {
      34            0 :       throw std::invalid_argument("Input dimension is not set");
      35              :     } else {
      36            9 :       dim[i].channel(dim[i].channel());
      37            9 :       dim[i].height(dim[i].height() * kernel_size[0]);
      38            9 :       dim[i].width(dim[i].width() * kernel_size[1]);
      39              :     }
      40              :   }
      41              : 
      42            9 :   context.setOutputDimensions(dim);
      43            9 : }
      44              : 
      45           25 : void Upsample2dLayer::forwarding(nntrainer::RunLayerContext &context,
      46              :                                  bool training) {
      47           25 :   nntrainer::Tensor &in = context.getInput(SINGLE_INOUT_IDX);
      48           25 :   nntrainer::Tensor &out = context.getOutput(SINGLE_INOUT_IDX);
      49              : 
      50              :   const auto &upsampling_type =
      51           25 :     std::get<props::UpsampleMode>(upsample2d_props).get();
      52              :   const auto &kernel_size =
      53              :     std::get<std::array<props::KernelSize, UPSAMPLE2D_DIM>>(upsample2d_props);
      54              : 
      55           25 :   switch (upsampling_type) {
      56              :   case props::UpsampleModeInfo::Interpolation::nearest:
      57           20 :     for (int b = 0; b < (int)out.batch(); b++) {
      58           20 :       for (int c = 0; c < (int)out.channel(); c++) {
      59           75 :         for (int h = 0; h < (int)out.height(); h++) {
      60          550 :           for (int w = 0; w < (int)out.width(); w++) {
      61          485 :             out.setValue(
      62              :               b, c, h, w,
      63          485 :               in.getValue(b, c, h / kernel_size[0], w / kernel_size[1]));
      64              :           }
      65              :         }
      66              :       }
      67              :     }
      68              :     break;
      69              :   case props::UpsampleModeInfo::Interpolation::bilinear: {
      70           15 :     float scale_h = (float)kernel_size[0];
      71           15 :     float scale_w = (float)kernel_size[1];
      72              : 
      73           30 :     for (int b = 0; b < (int)out.batch(); b++) {
      74           30 :       for (int c = 0; c < (int)out.channel(); c++) {
      75          280 :         for (int h = 0; h < (int)out.height(); h++) {
      76         8750 :           for (int w = 0; w < (int)out.width(); w++) {
      77         8485 :             float x_in = (w + 0.5f) / scale_w - 0.5f;
      78         8485 :             float y_in = (h + 0.5f) / scale_h - 0.5f;
      79              : 
      80         8485 :             if (x_in < 0) {
      81              :               x_in = 0.0f;
      82              :             }
      83         8485 :             if (y_in < 0) {
      84              :               y_in = 0.0f;
      85              :             }
      86              : 
      87         8485 :             int x0 = static_cast<int>(floor(x_in));
      88         8485 :             int y0 = static_cast<int>(floor(y_in));
      89         8485 :             int x1 = std::min(x0 + 1, (int)in.width() - 1);
      90         8485 :             int y1 = std::min(y0 + 1, (int)in.height() - 1);
      91              : 
      92         8485 :             float dx = x_in - x0;
      93         8485 :             float dy = y_in - y0;
      94              : 
      95         8485 :             float top = (1.0f - dx) * in.getValue(b, c, y1, x0) +
      96         8485 :                         dx * in.getValue(b, c, y1, x1);
      97         8485 :             float bottom = (1.0f - dx) * in.getValue(b, c, y0, x0) +
      98         8485 :                            dx * in.getValue(b, c, y0, x1);
      99         8485 :             float v = (1.0f - dy) * bottom + dy * top;
     100         8485 :             out.setValue(b, c, h, w, v);
     101              :           }
     102              :         }
     103              :       }
     104              :     }
     105              :   } break;
     106            0 :   default:
     107            0 :     throw std::runtime_error("Error: Unknown Upsample Mode Type");
     108              :   }
     109           25 : }
     110              : 
     111            5 : void Upsample2dLayer::calcDerivative(nntrainer::RunLayerContext &context) {
     112              :   const nntrainer::Tensor &derivative_ =
     113            5 :     context.getIncomingDerivative(SINGLE_INOUT_IDX);
     114              : 
     115            5 :   nntrainer::Tensor &dx = context.getOutgoingDerivative(SINGLE_INOUT_IDX);
     116              : 
     117              :   const auto &kernel_size =
     118              :     std::get<std::array<props::KernelSize, UPSAMPLE2D_DIM>>(upsample2d_props);
     119              :   const auto &upsampling_type =
     120            5 :     std::get<props::UpsampleMode>(upsample2d_props).get();
     121              : 
     122            5 :   switch (upsampling_type) {
     123              :   case props::UpsampleModeInfo::Interpolation::nearest: {
     124              :     float val = 0;
     125            4 :     for (int b = 0; b < (int)derivative_.batch(); b++) {
     126            4 :       for (int c = 0; c < (int)derivative_.channel(); c++) {
     127           15 :         for (int h = 0; h < (int)derivative_.height(); h++) {
     128          110 :           for (int w = 0; w < (int)derivative_.width(); w++) {
     129           97 :             if (h % kernel_size[0] == 0 && w % kernel_size[1] == 0) {
     130           13 :               dx.setValue(b, c, h / kernel_size[0], w / kernel_size[1], 0);
     131              :             }
     132              : 
     133           97 :             val = dx.getValue(b, c, h / kernel_size[0], w / kernel_size[1]) +
     134           97 :                   derivative_.getValue(b, c, h, w);
     135           97 :             dx.setValue(b, c, h / kernel_size[0], w / kernel_size[1], val);
     136              :           }
     137              :         }
     138              :       }
     139              :     }
     140              :   } break;
     141            3 :   case props::UpsampleModeInfo::Interpolation::bilinear: {
     142            3 :     dx.setZero();
     143              : 
     144            3 :     int input_height = dx.height();
     145            3 :     int input_width = dx.width();
     146              : 
     147            6 :     for (int b = 0; b < (int)derivative_.batch(); b++) {
     148            6 :       for (int c = 0; c < (int)derivative_.channel(); c++) {
     149           56 :         for (int h = 0; h < (int)derivative_.height(); h++) {
     150         1750 :           for (int w = 0; w < (int)derivative_.width(); w++) {
     151         1697 :             float in_h = (h + 0.5f) / kernel_size[0] - 0.5f;
     152         1697 :             float in_w = (w + 0.5f) / kernel_size[1] - 0.5f;
     153              : 
     154         1697 :             if (in_h < 0) {
     155              :               in_h = 0.0f;
     156              :             }
     157         1697 :             if (in_w < 0) {
     158              :               in_w = 0.0f;
     159              :             }
     160              : 
     161         1697 :             int y0 = static_cast<int>(floor(in_h));
     162         1697 :             int x0 = static_cast<int>(floor(in_w));
     163         1697 :             int y1 = std::min(y0 + 1, input_height - 1);
     164         1697 :             int x1 = std::min(x0 + 1, input_width - 1);
     165              : 
     166         1697 :             float dx_ = (in_w - x0); // Due to name conflict with dx
     167         1697 :             float dy_ = (in_h - y0);
     168              : 
     169         1697 :             float top_left_weight = (1.0 - dy_) * (1.0 - dx_);
     170         1697 :             float top_right_weight = (1.0 - dy_) * dx_;
     171         1697 :             float bottom_left_weight = dy_ * (1.0 - dx_);
     172         1697 :             float bottom_right_weight = dy_ * dx_;
     173              : 
     174         1697 :             float grad = derivative_.getValue(b, c, h, w);
     175              : 
     176         1697 :             dx.addValue(b, c, y0, x0, top_left_weight * grad, 1.0f);
     177         1697 :             dx.addValue(b, c, y0, x1, top_right_weight * grad, 1.0f);
     178         1697 :             dx.addValue(b, c, y1, x0, bottom_left_weight * grad, 1.0f);
     179         1697 :             dx.addValue(b, c, y1, x1, bottom_right_weight * grad, 1.0f);
     180              :           }
     181              :         }
     182              :       }
     183              :     }
     184              :   } break;
     185            0 :   default:
     186            0 :     throw std::runtime_error("Error: Unknown Upsample Mode Type");
     187              :   }
     188            5 : }
     189              : 
     190           38 : void Upsample2dLayer::setProperty(const std::vector<std::string> &values) {
     191           38 :   auto remain_props = loadProperties(values, upsample2d_props);
     192              : 
     193           37 :   if (!remain_props.empty()) {
     194              :     std::string msg = "[Upsample2dLayer] Unknown properties set with count" +
     195            2 :                       std::to_string(values.size());
     196            4 :     throw exception::not_supported(msg);
     197              :   }
     198           37 : }
     199              : } // namespace nntrainer
        

Generated by: LCOV version 2.0-1