OpLevelCostEstimator::OpDimensionsFromInputs(
    const TensorShapeProto& original_image_shape, const OpInfo& op_info,
    bool* found_unknown_shapes) {
  VLOG(2) << "op features: " << op_info.DebugString();
  VLOG(2) << "Original image shape: " << original_image_shape.DebugString();
  auto image_shape =
      MaybeGetMinimumShape(original_image_shape, 4, found_unknown_shapes);
  VLOG(2) << "Image shape: " << image_shape.DebugString();

  int x_index, y_index, channel_index;
  const std::string& data_format = GetDataFormat(op_info);
  if (data_format == "NCHW") {
    channel_index = 1;
    y_index = 2;
    x_index = 3;
  } else {
    y_index = 1;
    x_index = 2;
    channel_index = 3;
  }
  int64_t batch = image_shape.dim(0).size();
  int64_t ix = image_shape.dim(x_index).size();
  int64_t iy = image_shape.dim(y_index).size();
  int64_t iz = image_shape.dim(channel_index).size();

  // Note that FusedBatchNorm doesn't have ksize attr, but GetKernelSize returns
  // {1, 1, 1, 1} in that case.
  std::vector<int64_t> ksize = GetKernelSize(op_info);
  int64_t kx = ksize[x_index];
  int64_t ky = ksize[y_index];
  // These ops don't support groupwise operation, therefore kz == iz.
  int64_t kz = iz;

  std::vector<int64_t> strides = GetStrides(op_info);
  int64_t sx = strides[x_index];
  int64_t sy = strides[y_index];
  if (sx == 0 || sy == 0) {
    return errors::InvalidArgument(
        "Stride must be > 0 for Height and Width, but got (", sy, ", ", sx,
        ")");
  }
  const auto padding = GetPadding(op_info);

  int64_t ox = GetOutputSize(ix, kx, sx, padding);
  int64_t oy = GetOutputSize(iy, ky, sy, padding);
  int64_t oz = iz;

  OpLevelCostEstimator::ConvolutionDimensions conv_dims = {
      batch, ix, iy, iz, kx, ky, kz, oz, ox, oy, sx, sy, padding};
  return conv_dims;
}
The product divides a value by zero.