| 375 | |
| 376 | template <KernelType kernel_type> |
| 377 | TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { |
| 378 | // Retrieve tensors (All should be allocated by now) |
| 379 | const TfLiteTensor* output_shape = |
| 380 | GetInput(context, node, kOutputShapeTensor); |
| 381 | const TfLiteTensor* weights = GetInput(context, node, kWeightsTensor); |
| 382 | const TfLiteTensor* input = GetInput(context, node, kDataInputTensor); |
| 383 | TfLiteTensor* output = GetOutput(context, node, kOutputTensor); |
| 384 | OpData* data = reinterpret_cast<OpData*>(node->user_data); |
| 385 | TfLiteTensor* col2im = data->has_col2im |
| 386 | ? GetTemporary(context, node, data->col2im_index) |
| 387 | : nullptr; |
| 388 | TfLiteTensor* transposed_weights = |
| 389 | data->weights_are_transposed |
| 390 | ? GetTemporary(context, node, data->transposed_weights_index) |
| 391 | : nullptr; |
| 392 | const auto* params = |
| 393 | reinterpret_cast<TfLiteTransposeConvParams*>(node->builtin_data); |
| 394 | |
| 395 | // Resize any deferred dynamic tensors |
| 396 | if (IsDynamicTensor(output)) { |
| 397 | TF_LITE_ENSURE_OK(context, ResizeTensor(context, output_shape, output)); |
| 398 | } |
| 399 | if (data->has_col2im && IsDynamicTensor(col2im)) { |
| 400 | TF_LITE_ENSURE_OK(context, ResizeCol2ImTensor(context, output_shape, |
| 401 | weights, input, col2im)); |
| 402 | } |
| 403 | |
| 404 | // Get height and width of the output image. |
| 405 | const int width = SizeOfDimension(output, 2); |
| 406 | const int height = SizeOfDimension(output, 1); |
| 407 | const int filter_width = SizeOfDimension(weights, 2); |
| 408 | const int filter_height = SizeOfDimension(weights, 1); |
| 409 | |
| 410 | int unused_output_height, unused_output_width; |
| 411 | data->padding = ComputePaddingHeightWidth( |
| 412 | params->stride_height, params->stride_width, 1, 1, height, width, |
| 413 | filter_height, filter_width, params->padding, &unused_output_height, |
| 414 | &unused_output_width); |
| 415 | |
| 416 | // Currently support float32 and uint8. |
| 417 | switch (input->type) { |
| 418 | case kTfLiteFloat32: { |
| 419 | // Only for GenericOptimized path, we use transposed weights. |
| 420 | if (data->weights_are_transposed) { |
| 421 | if (!IsConstantTensor(weights)) { |
| 422 | ResizeAndTransposeWeights(context, weights, transposed_weights); |
| 423 | } |
| 424 | } |
| 425 | EvalFloat<kernel_type>(context, params, data, input, weights, |
| 426 | transposed_weights, col2im, output); |
| 427 | break; |
| 428 | } |
| 429 | case kTfLiteUInt8: { |
| 430 | // TODO(haoliang): support optimized implementation for quantized |
| 431 | // TransposeConv. |
| 432 | TfLiteTensor* scratch_buffer = |
| 433 | GetTemporary(context, node, data->scratch_tensor_index); |
| 434 | if (IsDynamicTensor(scratch_buffer)) { |
nothing calls this directly
no test coverage detected