  bool operator()(const OpKernelContext* context,
                  typename TTypes<float, 4>::ConstTensor grads,
                  typename TTypes<float, 2>::ConstTensor boxes,
                  typename TTypes<int32, 1>::ConstTensor box_index,
                  typename TTypes<T, 4>::Tensor grads_image,
                  const string& method_name) {
    const int batch_size = grads_image.dimension(0);
    const int image_height = grads_image.dimension(1);
    const int image_width = grads_image.dimension(2);

    const int num_boxes = grads.dimension(0);
    const int crop_height = grads.dimension(1);
    const int crop_width = grads.dimension(2);
    const int depth = grads.dimension(3);

    grads_image.setZero();

    auto CropAndResizeBackImgPerBox = [&](int64 start_box, int64 limit_box) {
      for (int b = start_box; b < limit_box; ++b) {
        const float y1 = boxes(b, 0);
        const float x1 = boxes(b, 1);
        const float y2 = boxes(b, 2);
        const float x2 = boxes(b, 3);

        const int32 b_in = box_index(b);
        if (!FastBoundsCheck(b_in, batch_size)) {
          continue;
        }

        const float height_scale =
            (crop_height > 1)
                ? (y2 - y1) * (image_height - 1) / (crop_height - 1)
                : 0;
        const float width_scale =
            (crop_width > 1) ? (x2 - x1) * (image_width - 1) / (crop_width - 1)
                             : 0;

        for (int y = 0; y < crop_height; ++y) {
          const float in_y = (crop_height > 1)
                                 ? y1 * (image_height - 1) + y * height_scale
                                 : 0.5 * (y1 + y2) * (image_height - 1);
          if (in_y < 0 || in_y > image_height - 1) {
            continue;
          }
          const int top_y_index = floorf(in_y);
          const int bottom_y_index = ceilf(in_y);
          const float y_lerp = in_y - top_y_index;

          for (int x = 0; x < crop_width; ++x) {
            const float in_x = (crop_width > 1)
                                   ? x1 * (image_width - 1) + x * width_scale
                                   : 0.5 * (x1 + x2) * (image_width - 1);
            if (in_x < 0 || in_x > image_width - 1) {
              continue;
            }

            if (method_name == "bilinear") {
              const int left_x_index = floorf(in_x);
              const int right_x_index = ceilf(in_x);
              const float x_lerp = in_x - left_x_index;

              for (int d = 0; d < depth; ++d) {
                const float dtop = (1 - y_lerp) * grads(b, y, x, d);
                grads_image(b_in, top_y_index, left_x_index, d) +=
                    static_cast<T>((1 - x_lerp) * dtop);
                grads_image(b_in, top_y_index, right_x_index, d) +=
                    static_cast<T>(x_lerp * dtop);
                const float dbottom = y_lerp * grads(b, y, x, d);
                grads_image(b_in, bottom_y_index, left_x_index, d) +=
                    static_cast<T>((1 - x_lerp) * dbottom);
                grads_image(b_in, bottom_y_index, right_x_index, d) +=
                    static_cast<T>(x_lerp * dbottom);
              }
            } else {  // method_name == "nearest"
              for (int d = 0; d < depth; ++d) {
                int closest_x_index = roundf(in_x);
                int closest_y_index = roundf(in_y);
                grads_image(b_in, closest_y_index, closest_x_index, d) +=
                    static_cast<T>(grads(b, y, x, d));
              }
            }
          }
        }
      }
    };

    // A rough estimation of the cost for each cropped box.
    // Including calculation cost in the depth loop and pixel loop.
    const double cost_per_pixel =
        (method_name == "bilinear"
             ? depth * (Eigen::TensorOpCost::AddCost<float>() * 7 +
                        Eigen::TensorOpCost::MulCost<float>() * 6 +
                        Eigen::TensorOpCost::CastCost<T, float>() * 4) +
                   Eigen::TensorOpCost::AddCost<float>() * 4
             : depth * (Eigen::TensorOpCost::AddCost<float>() +
                        Eigen::TensorOpCost::CastCost<T, float>()) +
                   Eigen::TensorOpCost::AddCost<float>() * 3);

    const double cost_per_box = crop_height * crop_width * cost_per_pixel;

    const DeviceBase::CpuWorkerThreads& worker_threads =
        *(context->device()->tensorflow_cpu_worker_threads());
    Shard(worker_threads.num_threads, worker_threads.workers, num_boxes,
          cost_per_box, CropAndResizeBackImgPerBox);

    return true;
  }
  bool operator()(const OpKernelContext* context,
                  typename TTypes<float, 4>::ConstTensor grads,
                  typename TTypes<float, 2>::ConstTensor boxes,
                  typename TTypes<int32, 1>::ConstTensor box_index,
                  typename TTypes<T, 4>::Tensor grads_image,
                  const string& method_name) {
    const int batch_size = grads_image.dimension(0);
    const int image_height = grads_image.dimension(1);
    const int image_width = grads_image.dimension(2);

    const int num_boxes = grads.dimension(0);
    const int crop_height = grads.dimension(1);
    const int crop_width = grads.dimension(2);
    const int depth = grads.dimension(3);

    grads_image.setZero();

    auto CropAndResizeBackImgPerBox = [&](int64 start_box, int64 limit_box) {
      for (int b = start_box; b < limit_box; ++b) {
        const float y1 = boxes(b, 0);
        const float x1 = boxes(b, 1);
        const float y2 = boxes(b, 2);
        const float x2 = boxes(b, 3);

        const int32 b_in = box_index(b);
        if (!FastBoundsCheck(b_in, batch_size)) {
          continue;
        }

        const float height_scale =
            (crop_height > 1)
                ? (y2 - y1) * (image_height - 1) / (crop_height - 1)
                : 0;
        const float width_scale =
            (crop_width > 1) ? (x2 - x1) * (image_width - 1) / (crop_width - 1)
                             : 0;

        for (int y = 0; y < crop_height; ++y) {
          const float in_y = (crop_height > 1)
                                 ? y1 * (image_height - 1) + y * height_scale
                                 : 0.5 * (y1 + y2) * (image_height - 1);
          if (in_y < 0 || in_y > image_height - 1) {
            continue;
          }
          const int top_y_index = floorf(in_y);
          const int bottom_y_index = ceilf(in_y);
          const float y_lerp = in_y - top_y_index;

          for (int x = 0; x < crop_width; ++x) {
            const float in_x = (crop_width > 1)
                                   ? x1 * (image_width - 1) + x * width_scale
                                   : 0.5 * (x1 + x2) * (image_width - 1);
            if (in_x < 0 || in_x > image_width - 1) {
              continue;
            }

            if (method_name == "bilinear") {
              const int left_x_index = floorf(in_x);
              const int right_x_index = ceilf(in_x);
              const float x_lerp = in_x - left_x_index;

              for (int d = 0; d < depth; ++d) {
                const float dtop = (1 - y_lerp) * grads(b, y, x, d);
                grads_image(b_in, top_y_index, left_x_index, d) +=
                    static_cast<T>((1 - x_lerp) * dtop);
                grads_image(b_in, top_y_index, right_x_index, d) +=
                    static_cast<T>(x_lerp * dtop);
                const float dbottom = y_lerp * grads(b, y, x, d);
                grads_image(b_in, bottom_y_index, left_x_index, d) +=
                    static_cast<T>((1 - x_lerp) * dbottom);
                grads_image(b_in, bottom_y_index, right_x_index, d) +=
                    static_cast<T>(x_lerp * dbottom);
              }
            } else {   
              for (int d = 0; d < depth; ++d) {
                int closest_x_index = roundf(in_x);
                int closest_y_index = roundf(in_y);
                grads_image(b_in, closest_y_index, closest_x_index, d) +=
                    static_cast<T>(grads(b, y, x, d));
              }
            }
          }
        }
      }
    };

     
     
    const double cost_per_pixel =
        (method_name == "bilinear"
             ? depth * (Eigen::TensorOpCost::AddCost<float>() * 7 +
                        Eigen::TensorOpCost::MulCost<float>() * 6 +
                        Eigen::TensorOpCost::CastCost<T, float>() * 4) +
                   Eigen::TensorOpCost::AddCost<float>() * 4
             : depth * (Eigen::TensorOpCost::AddCost<float>() +
                        Eigen::TensorOpCost::CastCost<T, float>()) +
                   Eigen::TensorOpCost::AddCost<float>() * 3);

    const double cost_per_box = crop_height * crop_width * cost_per_pixel;

    const DeviceBase::CpuWorkerThreads& worker_threads =
        *(context->device()->tensorflow_cpu_worker_threads());
    Shard(worker_threads.num_threads, worker_threads.workers, num_boxes,
          cost_per_box, CropAndResizeBackImgPerBox);

    return true;
  }