| 118 | |
| 119 | template <KernelType kernel_type> |
| 120 | TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { |
| 121 | PadContext op_context(context, node); |
| 122 | |
| 123 | if (op_context.constant_values != nullptr) { |
| 124 | // Ensure that constant_values is a scalar. |
| 125 | TF_LITE_ENSURE_EQ(context, NumElements(op_context.constant_values), 1); |
| 126 | } |
| 127 | |
| 128 | // Resize the output tensor if the output tensor is dynamic. |
| 129 | if (IsDynamicTensor(op_context.output)) { |
| 130 | TF_LITE_ENSURE_OK(context, ResizeOutputTensor(context, &op_context)); |
| 131 | } |
| 132 | |
| 133 | // TODO(nupurgarg): Change kernel implementation to take in int* instead of |
| 134 | // vector<int> to remove malloc from Eval(). |
| 135 | // Create before and after padding arrays that are accepted by the kernel. |
| 136 | std::vector<int> before_padding; |
| 137 | std::vector<int> after_padding; |
| 138 | const int32* paddings_data = GetTensorData<int32>(op_context.paddings); |
| 139 | |
| 140 | // TODO(nupurgarg): Change kernel implementation to use padding arrays in |
| 141 | // forward order (depth, width, height, batch). |
| 142 | // Build paddings in order of int[] = {batch, height, width, depth} to match |
| 143 | // kernel implementation of Pad in reference_ops.h and optimized_ops.h. |
| 144 | for (int idx = op_context.dims - 1; idx >= 0; --idx) { |
| 145 | before_padding.push_back(paddings_data[idx * 2]); |
| 146 | after_padding.push_back(paddings_data[idx * 2 + 1]); |
| 147 | } |
| 148 | |
| 149 | #define TF_LITE_PAD(type, op_name, scalar, pad_value) \ |
| 150 | TF_LITE_ENSURE(context, before_padding.size() <= 4); \ |
| 151 | TF_LITE_ENSURE(context, after_padding.size() <= 4); \ |
| 152 | tflite::PadParams op_params; \ |
| 153 | op_params.left_padding_count = before_padding.size(); \ |
| 154 | op_params.right_padding_count = after_padding.size(); \ |
| 155 | for (int i = 0; i < op_context.dims; ++i) { \ |
| 156 | op_params.left_padding[i] = before_padding[op_context.dims - 1 - i]; \ |
| 157 | op_params.right_padding[i] = after_padding[op_context.dims - 1 - i]; \ |
| 158 | } \ |
| 159 | const scalar pad_value_copy = pad_value; \ |
| 160 | \ |
| 161 | type::op_name(op_params, GetTensorShape(op_context.input), \ |
| 162 | GetTensorData<scalar>(op_context.input), &pad_value_copy, \ |
| 163 | GetTensorShape(op_context.output), \ |
| 164 | GetTensorData<scalar>(op_context.output)) |
| 165 | switch (op_context.input->type) { |
| 166 | case kTfLiteFloat32: { |
| 167 | float pad_value = op_context.constant_values == nullptr |
| 168 | ? 0.f |
| 169 | : *GetTensorData<float>(op_context.constant_values); |
| 170 | if (kernel_type == kReference) { |
| 171 | if (op_context.resizing_category == ResizingCategory::kImageStyle) { |
| 172 | TF_LITE_PAD(reference_ops, PadImageStyle, float, pad_value); |
| 173 | } else { |
| 174 | TF_LITE_PAD(reference_ops, Pad, float, pad_value); |
| 175 | } |
| 176 | } else if (kernel_type == kGenericOptimized) { |
| 177 | if (op_context.resizing_category == ResizingCategory::kImageStyle) { |
nothing calls this directly
no test coverage detected