Converts the given TF_Tensor to a numpy ndarray. If the returned status is OK, the caller becomes the owner of *out_array.
| 429 | // Converts the given TF_Tensor to a numpy ndarray. |
| 430 | // If the returned status is OK, the caller becomes the owner of *out_array. |
| 431 | Status TF_TensorToPyArray(Safe_TF_TensorPtr tensor, PyObject** out_ndarray) { |
| 432 | // A fetched operation will correspond to a null tensor, and a None |
| 433 | // in Python. |
| 434 | if (tensor == nullptr) { |
| 435 | Py_INCREF(Py_None); |
| 436 | *out_ndarray = Py_None; |
| 437 | return Status::OK(); |
| 438 | } |
| 439 | int64 nelems = -1; |
| 440 | gtl::InlinedVector<npy_intp, 4> dims = |
| 441 | GetPyArrayDimensionsForTensor(tensor.get(), &nelems); |
| 442 | |
| 443 | // If the type is neither string nor resource we can reuse the Tensor memory. |
| 444 | TF_Tensor* original = tensor.get(); |
| 445 | TF_Tensor* moved = TF_TensorMaybeMove(tensor.release()); |
| 446 | if (moved != nullptr) { |
| 447 | if (ArrayFromMemory(dims.size(), dims.data(), TF_TensorData(moved), |
| 448 | static_cast<DataType>(TF_TensorType(moved)), |
| 449 | [moved] { TF_DeleteTensor(moved); }, out_ndarray) |
| 450 | .ok()) { |
| 451 | return Status::OK(); |
| 452 | } |
| 453 | } |
| 454 | tensor.reset(original); |
| 455 | |
| 456 | // Copy the TF_TensorData into a newly-created ndarray and return it. |
| 457 | PyArray_Descr* descr = nullptr; |
| 458 | TF_RETURN_IF_ERROR(GetPyArrayDescrForTensor(tensor.get(), &descr)); |
| 459 | Safe_PyObjectPtr safe_out_array = |
| 460 | tensorflow::make_safe(PyArray_Empty(dims.size(), dims.data(), descr, 0)); |
| 461 | if (!safe_out_array) { |
| 462 | return errors::Internal("Could not allocate ndarray"); |
| 463 | } |
| 464 | PyArrayObject* py_array = |
| 465 | reinterpret_cast<PyArrayObject*>(safe_out_array.get()); |
| 466 | if (TF_TensorType(tensor.get()) == TF_STRING) { |
| 467 | Status s = CopyTF_TensorStringsToPyArray(tensor.get(), nelems, py_array); |
| 468 | if (!s.ok()) { |
| 469 | return s; |
| 470 | } |
| 471 | } else if (static_cast<size_t>(PyArray_NBYTES(py_array)) != |
| 472 | TF_TensorByteSize(tensor.get())) { |
| 473 | return errors::Internal("ndarray was ", PyArray_NBYTES(py_array), |
| 474 | " bytes but TF_Tensor was ", |
| 475 | TF_TensorByteSize(tensor.get()), " bytes"); |
| 476 | } else { |
| 477 | FastMemcpy(PyArray_DATA(py_array), TF_TensorData(tensor.get()), |
| 478 | PyArray_NBYTES(py_array)); |
| 479 | } |
| 480 | |
| 481 | *out_ndarray = safe_out_array.release(); |
| 482 | return Status::OK(); |
| 483 | } |
| 484 | |
| 485 | Status PyArrayToTF_Tensor(PyObject* ndarray, Safe_TF_TensorPtr* out_tensor) { |
| 486 | DCHECK(out_tensor != nullptr); |
no test coverage detected