| @@ -51,7 +51,9 @@ uint8_t DataType::AsCVType() const { | |||||
| } | } | ||||
| if (res == kCVInvalidType) { | if (res == kCVInvalidType) { | ||||
| MS_LOG(ERROR) << "Cannot convert to OpenCV type. Return invalid type!"; | |||||
| std::string err_msg = "Cannot convert [" + std::string(kTypeInfo[type_].name_) + "] to OpenCV type."; | |||||
| err_msg += " Currently unsupported data type: [uint32, int64, uint64, string]"; | |||||
| MS_LOG(ERROR) << err_msg; | |||||
| } | } | ||||
| return res; | return res; | ||||
| @@ -269,10 +269,11 @@ Status Tensor::CreateFromFile(const std::string &path, std::shared_ptr<Tensor> * | |||||
| CHECK_FAIL_RETURN_UNEXPECTED(!fs.fail(), "Fail to open file: " + path); | CHECK_FAIL_RETURN_UNEXPECTED(!fs.fail(), "Fail to open file: " + path); | ||||
| int64_t num_bytes = fs.seekg(0, std::ios::end).tellg(); | int64_t num_bytes = fs.seekg(0, std::ios::end).tellg(); | ||||
| CHECK_FAIL_RETURN_UNEXPECTED(num_bytes <= kDeMaxDim, "Invalid file to allocate tensor memory, check path: " + path); | CHECK_FAIL_RETURN_UNEXPECTED(num_bytes <= kDeMaxDim, "Invalid file to allocate tensor memory, check path: " + path); | ||||
| CHECK_FAIL_RETURN_UNEXPECTED(fs.seekg(0, std::ios::beg).good(), "Fail to find size of file"); | |||||
| CHECK_FAIL_RETURN_UNEXPECTED(fs.seekg(0, std::ios::beg).good(), "Fail to find size of file, check path: " + path); | |||||
| RETURN_IF_NOT_OK(Tensor::CreateEmpty(TensorShape{num_bytes}, DataType(DataType::DE_UINT8), out)); | RETURN_IF_NOT_OK(Tensor::CreateEmpty(TensorShape{num_bytes}, DataType(DataType::DE_UINT8), out)); | ||||
| int64_t written_bytes = fs.read(reinterpret_cast<char *>((*out)->GetMutableBuffer()), num_bytes).gcount(); | int64_t written_bytes = fs.read(reinterpret_cast<char *>((*out)->GetMutableBuffer()), num_bytes).gcount(); | ||||
| CHECK_FAIL_RETURN_UNEXPECTED(written_bytes == num_bytes && fs.good(), "Error in writing to tensor"); | |||||
| CHECK_FAIL_RETURN_UNEXPECTED(written_bytes == num_bytes && fs.good(), | |||||
| "Error in writing to tensor, check path: " + path); | |||||
| fs.close(); | fs.close(); | ||||
| return Status::OK(); | return Status::OK(); | ||||
| } | } | ||||
| @@ -873,6 +874,42 @@ Status Tensor::CopyLastDimAt(const std::shared_ptr<Tensor> &src, const std::vect | |||||
| return Status::OK(); | return Status::OK(); | ||||
| } | } | ||||
| Status Tensor::GetSliceOption(const SliceOption &slice_option, const int32_t &slice_index, | |||||
| SliceOption *slice_option_ptr) { | |||||
| if (slice_option.indices_.empty() && !slice_option.slice_.valid()) { | |||||
| RETURN_STATUS_UNEXPECTED("Both indices and slices can not be empty."); | |||||
| } | |||||
| if (!slice_option.indices_.empty() && slice_option.slice_.valid()) { | |||||
| RETURN_STATUS_UNEXPECTED("Both indices and slices can not be given."); | |||||
| } | |||||
| // if slice object was provided, indices should be empty. Generate indices from the slice object. | |||||
| if (slice_option.indices_.empty()) { | |||||
| // check if slice is valid | |||||
| mindspore::dataset::Slice slice_copy = slice_option.slice_; | |||||
| slice_copy.start_ = HandleNeg(slice_option.slice_.start_, shape_[slice_index]); | |||||
| slice_copy.stop_ = HandleNeg(slice_option.slice_.stop_, shape_[slice_index]); | |||||
| slice_copy.start_ = slice_copy.start_ < 0 ? 0 : slice_copy.start_; | |||||
| slice_copy.stop_ = slice_copy.stop_ < 0 ? 0 : slice_copy.stop_; | |||||
| dsize_t max_idx = shape_[slice_index]; | |||||
| slice_copy.start_ = slice_copy.start_ > max_idx ? max_idx : slice_copy.start_; | |||||
| slice_copy.stop_ = slice_copy.stop_ > max_idx ? max_idx : slice_copy.stop_; | |||||
| *slice_option_ptr = SliceOption(slice_copy); | |||||
| } else { | |||||
| // indices validation | |||||
| std::vector<dsize_t> indices_copy; | |||||
| for (int j = 0; j < slice_option.indices_.size(); j++) { | |||||
| dsize_t index = HandleNeg(slice_option.indices_[j], shape_[slice_index]); | |||||
| CHECK_FAIL_RETURN_UNEXPECTED(index < shape_[slice_index] && index >= 0, | |||||
| "Index " + std::to_string(index) + " is out of bounds."); | |||||
| indices_copy.emplace_back(index); | |||||
| } | |||||
| *slice_option_ptr = SliceOption(indices_copy); | |||||
| } | |||||
| return Status::OK(); | |||||
| } | |||||
| Status Tensor::Slice(std::shared_ptr<Tensor> *out, const std::vector<SliceOption> slice_options_) { | Status Tensor::Slice(std::shared_ptr<Tensor> *out, const std::vector<SliceOption> slice_options_) { | ||||
| std::vector<SliceOption> converted_slice_objects; | std::vector<SliceOption> converted_slice_objects; | ||||
| @@ -885,37 +922,9 @@ Status Tensor::Slice(std::shared_ptr<Tensor> *out, const std::vector<SliceOption | |||||
| continue; | continue; | ||||
| } | } | ||||
| if (slice_option.indices_.empty() && !slice_option.slice_.valid()) { | |||||
| RETURN_STATUS_UNEXPECTED("Both indices and slices can not be empty."); | |||||
| } | |||||
| if (!slice_option.indices_.empty() && slice_option.slice_.valid()) { | |||||
| RETURN_STATUS_UNEXPECTED("Both indices and slices can not be given."); | |||||
| } | |||||
| // if slice object was provided, indices should be empty. Generate indices from the slice object. | |||||
| if (slice_option.indices_.empty()) { | |||||
| // check if slice is valid | |||||
| mindspore::dataset::Slice slice_copy = slice_option.slice_; | |||||
| slice_copy.start_ = HandleNeg(slice_option.slice_.start_, shape_[i]); | |||||
| slice_copy.stop_ = HandleNeg(slice_option.slice_.stop_, shape_[i]); | |||||
| slice_copy.start_ = slice_copy.start_ < 0 ? 0 : slice_copy.start_; | |||||
| slice_copy.stop_ = slice_copy.stop_ < 0 ? 0 : slice_copy.stop_; | |||||
| dsize_t max_idx = shape_[i]; | |||||
| slice_copy.start_ = slice_copy.start_ > max_idx ? max_idx : slice_copy.start_; | |||||
| slice_copy.stop_ = slice_copy.stop_ > max_idx ? max_idx : slice_copy.stop_; | |||||
| converted_slice_objects.emplace_back(SliceOption(slice_copy)); | |||||
| } else { | |||||
| // indices validation | |||||
| std::vector<dsize_t> indices_copy; | |||||
| for (int j = 0; j < slice_option.indices_.size(); j++) { | |||||
| dsize_t index = HandleNeg(slice_option.indices_[j], shape_[i]); | |||||
| CHECK_FAIL_RETURN_UNEXPECTED(index < shape_[i] && index >= 0, | |||||
| "Index " + std::to_string(index) + " is out of bounds."); | |||||
| indices_copy.emplace_back(index); | |||||
| } | |||||
| converted_slice_objects.emplace_back(SliceOption(indices_copy)); | |||||
| } | |||||
| SliceOption slice_option_item(false); | |||||
| RETURN_IF_NOT_OK(GetSliceOption(slice_option, i, &slice_option_item)); | |||||
| converted_slice_objects.emplace_back(slice_option_item); | |||||
| } | } | ||||
| // if a string with partial slices, pass in the rest | // if a string with partial slices, pass in the rest | ||||
| @@ -405,6 +405,13 @@ class Tensor { | |||||
| /// \return Status error code | /// \return Status error code | ||||
| Status Slice(TensorPtr *out, const std::vector<mindspore::dataset::SliceOption> slice_options); | Status Slice(TensorPtr *out, const std::vector<mindspore::dataset::SliceOption> slice_options); | ||||
| /// Get slice_option according to shape and index. | |||||
| /// \param[in] slice_option input SliceOption object | |||||
| /// \param[in] slice_index index of SliceOption object | |||||
| /// \param[out] output slice_option with shape info | |||||
| /// \return Status error code | |||||
| Status GetSliceOption(const SliceOption &slice_option, const int32_t &slice_index, SliceOption *slice_option_ptr); | |||||
| #ifdef ENABLE_PYTHON | #ifdef ENABLE_PYTHON | ||||
| /// Constructs numpy array from input tensor | /// Constructs numpy array from input tensor | ||||
| /// \param[in] data this data is the location of python data | /// \param[in] data this data is the location of python data | ||||
| @@ -41,6 +41,7 @@ | |||||
| #include "include/api/status.h" | #include "include/api/status.h" | ||||
| #include "minddata/dataset/core/constants.h" | #include "minddata/dataset/core/constants.h" | ||||
| #include "minddata/dataset/core/data_type.h" | #include "minddata/dataset/core/data_type.h" | ||||
| #include "minddata/dataset/core/tensor_helpers.h" | |||||
| #include "minddata/dataset/core/tensor_shape.h" | #include "minddata/dataset/core/tensor_shape.h" | ||||
| #include "minddata/dataset/core/de_tensor.h" | #include "minddata/dataset/core/de_tensor.h" | ||||
| #ifndef ENABLE_ANDROID | #ifndef ENABLE_ANDROID | ||||
| @@ -171,6 +172,18 @@ class Tensor { | |||||
| return CreateFromVector(items, TensorShape({static_cast<dsize_t>(items.size())}), out); | return CreateFromVector(items, TensorShape({static_cast<dsize_t>(items.size())}), out); | ||||
| } | } | ||||
| /// Create a 1D boolean Tensor from a given list of boolean values. | |||||
| /// \param[in] items elements of the tensor | |||||
| /// \param[in] shape shape of the output tensor | |||||
| /// \param[out] out output argument to hold the created Tensor | |||||
| /// \return Status Code | |||||
| static Status CreateFromVector(const std::vector<bool> &items, const TensorShape &shape, TensorPtr *out) { | |||||
| std::vector<uint8_t> temp(items.begin(), items.end()); | |||||
| RETURN_IF_NOT_OK(CreateFromVector(temp, shape, out)); | |||||
| (*out)->type_ = DataType(DataType::DE_BOOL); | |||||
| return Status::OK(); | |||||
| } | |||||
| /// Create a numeric scalar Tensor from the given value. | /// Create a numeric scalar Tensor from the given value. | ||||
| /// \tparam T type of value | /// \tparam T type of value | ||||
| /// \param[in] item value | /// \param[in] item value | ||||
| @@ -282,7 +295,7 @@ class Tensor { | |||||
| const TensorShape &shape() const { return shape_; } | const TensorShape &shape() const { return shape_; } | ||||
| /// Check if tensor has data | /// Check if tensor has data | ||||
| /// \return bool - true if tensor is empty | |||||
| /// \return bool - true if tensor is not empty | |||||
| bool HasData() const { return data_ != nullptr; } | bool HasData() const { return data_ != nullptr; } | ||||
| /// Reshape the tensor. The given shape should have the same number of elements in the Tensor | /// Reshape the tensor. The given shape should have the same number of elements in the Tensor | ||||
| @@ -367,20 +380,37 @@ class Tensor { | |||||
| } | } | ||||
| /// Handle negative indices. | /// Handle negative indices. | ||||
| /// \param[out] out modified index | |||||
| /// \param[in] index | |||||
| /// \param[in] length axis length used to modify index | |||||
| /// \return dsize_t modified index | |||||
| static inline dsize_t HandleNeg(dsize_t index, dsize_t length) { return (index < 0) ? (index + length) : index; } | static inline dsize_t HandleNeg(dsize_t index, dsize_t length) { return (index < 0) ? (index + length) : index; } | ||||
| /// Slice tensor bases on the given indices. Copy the sliced data into out tensor. Only rank1 tensors are supported. | |||||
| /// Handle negative indices for a vector of indices. | |||||
| /// \param[out] out modified vector of indices | |||||
| /// \param[in] index_vector vector of indices | |||||
| /// \return std::vector<dsize_t> modified vector of indices | |||||
| static inline std::vector<dsize_t> HandleNegIndices(std::vector<dsize_t> index_vector, std::vector<dsize_t> length) { | |||||
| std::vector<dsize_t> indices(index_vector.size(), 0); | |||||
| for (int i = 0; i < index_vector.size(); i++) { | |||||
| indices[i] = HandleNeg(index_vector[i], length[i]); | |||||
| } | |||||
| return indices; | |||||
| } | |||||
| /// Slice tensor bases on the given indices. Copy the sliced data into out tensor. | |||||
| /// Based on the type of tensor, SliceNumeric or SliceString will be called | /// Based on the type of tensor, SliceNumeric or SliceString will be called | ||||
| /// \param[out] out Tensor | /// \param[out] out Tensor | ||||
| /// \param[in] indices vector of indices | |||||
| /// \param[in] slice_options vector of SliceOption objects | |||||
| /// \return Status error code | /// \return Status error code | ||||
| Status Slice(TensorPtr *out, const std::vector<dsize_t> &indices); | |||||
| /// Slice numeric tensors. | |||||
| Status SliceNumeric(TensorPtr *out, const std::vector<dsize_t> &indices); | |||||
| Status Slice(TensorPtr *out, const std::vector<mindspore::dataset::SliceOption> slice_options); | |||||
| /// Slice string tensors | |||||
| Status SliceString(TensorPtr *out, const std::vector<dsize_t> &indices); | |||||
| /// Get slice_option according to shape and index. | |||||
| /// \param[in] slice_option input SliceOption object | |||||
| /// \param[in] slice_index index of SliceOption object | |||||
| /// \param[out] output slice_option with shape info | |||||
| /// \return Status error code | |||||
| Status GetSliceOption(const SliceOption &slice_option, const int32_t &slice_index, SliceOption *slice_option_ptr); | |||||
| #ifdef ENABLE_PYTHON | #ifdef ENABLE_PYTHON | ||||
| /// Constructs numpy array from input tensor | /// Constructs numpy array from input tensor | ||||
| @@ -659,6 +689,12 @@ class Tensor { | |||||
| private: | private: | ||||
| friend class DETensor; | friend class DETensor; | ||||
| /// Slice numeric tensors. | |||||
| Status SliceNumeric(TensorPtr *out, const std::vector<std::vector<dsize_t>> &indices, const TensorShape &shape); | |||||
| /// Slice string tensors | |||||
| Status SliceString(TensorPtr *out, const std::vector<std::vector<dsize_t>> &indices, const TensorShape &shape); | |||||
| /// Copy raw data of a array based on shape and strides to the destination pointer | /// Copy raw data of a array based on shape and strides to the destination pointer | ||||
| /// \param dst [out] Pointer to the destination array where the content is to be copied | /// \param dst [out] Pointer to the destination array where the content is to be copied | ||||
| /// \param[in] src Pointer to the source of strided array to be copied | /// \param[in] src Pointer to the source of strided array to be copied | ||||
| @@ -56,6 +56,10 @@ def normalize(img, mean, std, pad_channel=False, dtype="float32"): | |||||
| Returns: | Returns: | ||||
| img (numpy.ndarray), Normalized image. | img (numpy.ndarray), Normalized image. | ||||
| """ | """ | ||||
| if np.issubdtype(img.dtype, np.integer): | |||||
| raise NotImplementedError("Unsupported image datatype: [{}], pls execute [ToTensor] before [Normalize]." | |||||
| .format(img.dtype)) | |||||
| if not is_numpy(img): | if not is_numpy(img): | ||||
| raise TypeError("img should be NumPy image. Got {}.".format(type(img))) | raise TypeError("img should be NumPy image. Got {}.".format(type(img))) | ||||
| @@ -74,6 +78,7 @@ def normalize(img, mean, std, pad_channel=False, dtype="float32"): | |||||
| mean = np.array(mean, dtype=img.dtype) | mean = np.array(mean, dtype=img.dtype) | ||||
| std = np.array(std, dtype=img.dtype) | std = np.array(std, dtype=img.dtype) | ||||
| image = (img - mean[:, None, None]) / std[:, None, None] | image = (img - mean[:, None, None]) / std[:, None, None] | ||||
| if pad_channel: | if pad_channel: | ||||
| zeros = np.zeros([1, image.shape[1], image.shape[2]], dtype=np.float32) | zeros = np.zeros([1, image.shape[1], image.shape[2]], dtype=np.float32) | ||||