Internal change.
PiperOrigin-RevId: 501136760
This commit is contained in:
		
							parent
							
								
									e869e57cb4
								
							
						
					
					
						commit
						54268594dd
					
				
							
								
								
									
										240
									
								
								mediapipe/framework/formats/tensor/cpu_buffer_converters.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								mediapipe/framework/formats/tensor/cpu_buffer_converters.cc
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,240 @@
 | 
			
		|||
#include <algorithm>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "mediapipe/framework/formats/tensor/backend.h"
 | 
			
		||||
#include "mediapipe/framework/formats/tensor/tensor2.h"
 | 
			
		||||
#include "mediapipe/framework/formats/tensor/views/buffer.h"
 | 
			
		||||
#include "mediapipe/framework/formats/tensor/views/cpu_buffer.h"
 | 
			
		||||
#include "third_party/FP16/include/fp16.h"
 | 
			
		||||
 | 
			
		||||
namespace mediapipe {
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
template <class SourceType, class DestinationType>
 | 
			
		||||
auto ConverterCheckFunction() {
 | 
			
		||||
  return
 | 
			
		||||
      [](const Tensor2& tensor, uint64_t source_descriptor_type_id,
 | 
			
		||||
         const Tensor2::ViewDescriptor& source_base_descriptor,
 | 
			
		||||
         uint64_t destination_descriptor_type_id,
 | 
			
		||||
         const Tensor2::ViewDescriptor& destination_base_descriptor) -> bool {
 | 
			
		||||
        if (source_descriptor_type_id != TensorCpuView::kId ||
 | 
			
		||||
            destination_descriptor_type_id != TensorCpuView::kId)
 | 
			
		||||
          return false;
 | 
			
		||||
        auto source_descriptor =
 | 
			
		||||
            static_cast<const TensorCpuViewDescriptor&>(source_base_descriptor);
 | 
			
		||||
        auto destination_descriptor =
 | 
			
		||||
            static_cast<const TensorCpuViewDescriptor&>(
 | 
			
		||||
                destination_base_descriptor);
 | 
			
		||||
        return source_descriptor.buffer.format ==
 | 
			
		||||
                   TensorTypeToFormat<SourceType>::value &&
 | 
			
		||||
               destination_descriptor.buffer.format ==
 | 
			
		||||
                   TensorTypeToFormat<DestinationType>::value;
 | 
			
		||||
      };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <class SourceType, class DestinationType>
 | 
			
		||||
auto ConvertFunction() {
 | 
			
		||||
  return [](const Tensor2& tensor, const Tensor2::View& source_base_view,
 | 
			
		||||
            const Tensor2::View& destination_base_view) -> bool {
 | 
			
		||||
    auto source = source_base_view.DownCast<TensorCpuView>();
 | 
			
		||||
    auto destination = destination_base_view.DownCast<TensorCpuView>();
 | 
			
		||||
    if (source->descriptor().buffer.format ==
 | 
			
		||||
        destination->descriptor().buffer.format) {
 | 
			
		||||
      std::memcpy(
 | 
			
		||||
          destination->data<void>(), source->data<void>(),
 | 
			
		||||
          TensorBufferSize(destination->descriptor().buffer, tensor.shape()));
 | 
			
		||||
    } else {
 | 
			
		||||
      auto source_pointer = source->data<SourceType>();
 | 
			
		||||
      auto destination_pointer = destination->data<DestinationType>();
 | 
			
		||||
      for (int i = 0; i < tensor.shape().NumElements(); i++) {
 | 
			
		||||
        *destination_pointer++ =
 | 
			
		||||
            GpuLikeTypeCast<DestinationType>(*source_pointer++);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define REGISTER_CONVERTER(SourceType, DestinationType)       \
 | 
			
		||||
  TENSOR_REGISTER_CONVERTER(                                  \
 | 
			
		||||
      {ConverterCheckFunction<SourceType, DestinationType>(), \
 | 
			
		||||
       ConvertFunction<SourceType, DestinationType>()});
 | 
			
		||||
 | 
			
		||||
REGISTER_CONVERTER(float, Float16);
 | 
			
		||||
REGISTER_CONVERTER(float, int8_t);
 | 
			
		||||
REGISTER_CONVERTER(float, uint8_t);
 | 
			
		||||
REGISTER_CONVERTER(float, int16_t);
 | 
			
		||||
REGISTER_CONVERTER(float, uint16_t);
 | 
			
		||||
REGISTER_CONVERTER(float, int32_t);
 | 
			
		||||
REGISTER_CONVERTER(float, uint32_t);
 | 
			
		||||
 | 
			
		||||
REGISTER_CONVERTER(Float16, float);
 | 
			
		||||
REGISTER_CONVERTER(Float16, int8_t);
 | 
			
		||||
REGISTER_CONVERTER(Float16, uint8_t);
 | 
			
		||||
REGISTER_CONVERTER(Float16, int16_t);
 | 
			
		||||
REGISTER_CONVERTER(Float16, uint16_t);
 | 
			
		||||
REGISTER_CONVERTER(Float16, int32_t);
 | 
			
		||||
REGISTER_CONVERTER(Float16, uint32_t);
 | 
			
		||||
 | 
			
		||||
REGISTER_CONVERTER(int8_t, float);
 | 
			
		||||
REGISTER_CONVERTER(int8_t, Float16);
 | 
			
		||||
REGISTER_CONVERTER(int8_t, uint8_t);
 | 
			
		||||
REGISTER_CONVERTER(int8_t, int16_t);
 | 
			
		||||
REGISTER_CONVERTER(int8_t, uint16_t);
 | 
			
		||||
REGISTER_CONVERTER(int8_t, int32_t);
 | 
			
		||||
REGISTER_CONVERTER(int8_t, uint32_t);
 | 
			
		||||
 | 
			
		||||
REGISTER_CONVERTER(uint8_t, float);
 | 
			
		||||
REGISTER_CONVERTER(uint8_t, Float16);
 | 
			
		||||
REGISTER_CONVERTER(uint8_t, int8_t);
 | 
			
		||||
REGISTER_CONVERTER(uint8_t, int16_t);
 | 
			
		||||
REGISTER_CONVERTER(uint8_t, uint16_t);
 | 
			
		||||
REGISTER_CONVERTER(uint8_t, int32_t);
 | 
			
		||||
REGISTER_CONVERTER(uint8_t, uint32_t);
 | 
			
		||||
 | 
			
		||||
REGISTER_CONVERTER(int16_t, float);
 | 
			
		||||
REGISTER_CONVERTER(int16_t, Float16);
 | 
			
		||||
REGISTER_CONVERTER(int16_t, int8_t);
 | 
			
		||||
REGISTER_CONVERTER(int16_t, uint8_t);
 | 
			
		||||
REGISTER_CONVERTER(int16_t, uint16_t);
 | 
			
		||||
REGISTER_CONVERTER(int16_t, uint32_t);
 | 
			
		||||
REGISTER_CONVERTER(int16_t, uint32_t);
 | 
			
		||||
 | 
			
		||||
REGISTER_CONVERTER(uint16_t, float);
 | 
			
		||||
REGISTER_CONVERTER(uint16_t, Float16);
 | 
			
		||||
REGISTER_CONVERTER(uint16_t, int8_t);
 | 
			
		||||
REGISTER_CONVERTER(uint16_t, uint8_t);
 | 
			
		||||
REGISTER_CONVERTER(uint16_t, int16_t);
 | 
			
		||||
REGISTER_CONVERTER(uint16_t, int32_t);
 | 
			
		||||
REGISTER_CONVERTER(uint16_t, uint32_t);
 | 
			
		||||
 | 
			
		||||
REGISTER_CONVERTER(int32_t, float);
 | 
			
		||||
REGISTER_CONVERTER(int32_t, Float16);
 | 
			
		||||
REGISTER_CONVERTER(int32_t, int8_t);
 | 
			
		||||
REGISTER_CONVERTER(int32_t, uint8_t);
 | 
			
		||||
REGISTER_CONVERTER(int32_t, int16_t);
 | 
			
		||||
REGISTER_CONVERTER(int32_t, uint16_t);
 | 
			
		||||
REGISTER_CONVERTER(int32_t, uint32_t);
 | 
			
		||||
 | 
			
		||||
REGISTER_CONVERTER(uint32_t, float);
 | 
			
		||||
REGISTER_CONVERTER(uint32_t, Float16);
 | 
			
		||||
REGISTER_CONVERTER(uint32_t, int8_t);
 | 
			
		||||
REGISTER_CONVERTER(uint32_t, uint8_t);
 | 
			
		||||
REGISTER_CONVERTER(uint32_t, int16_t);
 | 
			
		||||
REGISTER_CONVERTER(uint32_t, uint16_t);
 | 
			
		||||
REGISTER_CONVERTER(uint32_t, int32_t);
 | 
			
		||||
 | 
			
		||||
template <class DestinationType>
 | 
			
		||||
auto DequantizationCheckFunction() {
 | 
			
		||||
  return
 | 
			
		||||
      [](const Tensor2& tensor, uint64_t source_descriptor_type_id,
 | 
			
		||||
         const Tensor2::ViewDescriptor& source_base_descriptor,
 | 
			
		||||
         uint64_t destination_descriptor_type_id,
 | 
			
		||||
         const Tensor2::ViewDescriptor& destination_base_descriptor) -> bool {
 | 
			
		||||
        if (source_descriptor_type_id != TensorCpuView::kId ||
 | 
			
		||||
            destination_descriptor_type_id != TensorCpuView::kId)
 | 
			
		||||
          return false;
 | 
			
		||||
        auto source_descriptor =
 | 
			
		||||
            static_cast<const TensorCpuViewDescriptor&>(source_base_descriptor);
 | 
			
		||||
        auto destination_descriptor =
 | 
			
		||||
            static_cast<const TensorCpuViewDescriptor&>(
 | 
			
		||||
                destination_base_descriptor);
 | 
			
		||||
        return source_descriptor.buffer.format ==
 | 
			
		||||
                   TensorBufferDescriptor::Format::kQuantizedInt8 &&
 | 
			
		||||
               destination_descriptor.buffer.format ==
 | 
			
		||||
                   TensorTypeToFormat<DestinationType>::value;
 | 
			
		||||
      };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <class DestinationType>
 | 
			
		||||
auto DequantizationConvertFunction() {
 | 
			
		||||
  return [](const Tensor2& tensor, const Tensor2::View& source_base_view,
 | 
			
		||||
            const Tensor2::View& destination_base_view) -> bool {
 | 
			
		||||
    auto source = source_base_view.DownCast<TensorCpuView>();
 | 
			
		||||
    auto destination = destination_base_view.DownCast<TensorCpuView>();
 | 
			
		||||
    auto source_pointer = source->data<int8_t>();
 | 
			
		||||
    auto destination_pointer = destination->data<DestinationType>();
 | 
			
		||||
    int zero_point =
 | 
			
		||||
        source->descriptor().buffer.quantization_parameters.zero_point;
 | 
			
		||||
    float scale = source->descriptor().buffer.quantization_parameters.scale;
 | 
			
		||||
    for (int i = 0; i < tensor.shape().NumElements(); i++) {
 | 
			
		||||
      *destination_pointer++ = static_cast<DestinationType>(
 | 
			
		||||
          (*source_pointer++ - zero_point) * scale);
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define REGISTER_DEQUANTIZATION_CONVERTER(DestinationType) \
 | 
			
		||||
  TENSOR_REGISTER_CONVERTER(                               \
 | 
			
		||||
      {DequantizationCheckFunction<DestinationType>(),     \
 | 
			
		||||
       DequantizationConvertFunction<DestinationType>()});
 | 
			
		||||
 | 
			
		||||
REGISTER_DEQUANTIZATION_CONVERTER(float);
 | 
			
		||||
REGISTER_DEQUANTIZATION_CONVERTER(Float16);
 | 
			
		||||
REGISTER_DEQUANTIZATION_CONVERTER(int8_t);
 | 
			
		||||
REGISTER_DEQUANTIZATION_CONVERTER(uint8_t);
 | 
			
		||||
REGISTER_DEQUANTIZATION_CONVERTER(int16_t);
 | 
			
		||||
REGISTER_DEQUANTIZATION_CONVERTER(uint16_t);
 | 
			
		||||
REGISTER_DEQUANTIZATION_CONVERTER(int32_t);
 | 
			
		||||
REGISTER_DEQUANTIZATION_CONVERTER(uint32_t);
 | 
			
		||||
 | 
			
		||||
template <class SourceType>
 | 
			
		||||
auto QuantizationCheckFunction() {
 | 
			
		||||
  return
 | 
			
		||||
      [](const Tensor2& tensor, uint64_t source_descriptor_type_id,
 | 
			
		||||
         const Tensor2::ViewDescriptor& source_base_descriptor,
 | 
			
		||||
         uint64_t destination_descriptor_type_id,
 | 
			
		||||
         const Tensor2::ViewDescriptor& destination_base_descriptor) -> bool {
 | 
			
		||||
        if (source_descriptor_type_id != TensorCpuView::kId ||
 | 
			
		||||
            destination_descriptor_type_id != TensorCpuView::kId)
 | 
			
		||||
          return false;
 | 
			
		||||
        auto source_descriptor =
 | 
			
		||||
            static_cast<const TensorCpuViewDescriptor&>(source_base_descriptor);
 | 
			
		||||
        auto destination_descriptor =
 | 
			
		||||
            static_cast<const TensorCpuViewDescriptor&>(
 | 
			
		||||
                destination_base_descriptor);
 | 
			
		||||
        bool same = source_descriptor.buffer.format ==
 | 
			
		||||
                        TensorTypeToFormat<SourceType>::value &&
 | 
			
		||||
                    destination_descriptor.buffer.format ==
 | 
			
		||||
                        TensorBufferDescriptor::Format::kQuantizedInt8;
 | 
			
		||||
        return same;
 | 
			
		||||
      };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <class SourceType>
 | 
			
		||||
auto QuantizationConvertFunction() {
 | 
			
		||||
  return [](const Tensor2& tensor, const Tensor2::View& source_base_view,
 | 
			
		||||
            const Tensor2::View& destination_base_view) -> bool {
 | 
			
		||||
    auto source = source_base_view.DownCast<TensorCpuView>();
 | 
			
		||||
    auto destination = destination_base_view.DownCast<TensorCpuView>();
 | 
			
		||||
    auto source_pointer = source->data<SourceType>();
 | 
			
		||||
    auto destination_pointer = destination->data<int8_t>();
 | 
			
		||||
    int zero_point =
 | 
			
		||||
        destination->descriptor().buffer.quantization_parameters.zero_point;
 | 
			
		||||
    float scale =
 | 
			
		||||
        destination->descriptor().buffer.quantization_parameters.scale;
 | 
			
		||||
    for (int i = 0; i < tensor.shape().NumElements(); i++) {
 | 
			
		||||
      *destination_pointer++ =
 | 
			
		||||
          static_cast<int8_t>(*source_pointer++ / scale + zero_point);
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define REGISTER_QUANTIZATION_CONVERTER(SourceType)                   \
 | 
			
		||||
  TENSOR_REGISTER_CONVERTER({QuantizationCheckFunction<SourceType>(), \
 | 
			
		||||
                             QuantizationConvertFunction<SourceType>()});
 | 
			
		||||
 | 
			
		||||
REGISTER_QUANTIZATION_CONVERTER(float);
 | 
			
		||||
REGISTER_QUANTIZATION_CONVERTER(Float16);
 | 
			
		||||
REGISTER_QUANTIZATION_CONVERTER(int8_t);
 | 
			
		||||
REGISTER_QUANTIZATION_CONVERTER(uint8_t);
 | 
			
		||||
REGISTER_QUANTIZATION_CONVERTER(int16_t);
 | 
			
		||||
REGISTER_QUANTIZATION_CONVERTER(uint16_t);
 | 
			
		||||
REGISTER_QUANTIZATION_CONVERTER(int32_t);
 | 
			
		||||
REGISTER_QUANTIZATION_CONVERTER(uint32_t);
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
}  // namespace mediapipe
 | 
			
		||||
							
								
								
									
										282
									
								
								mediapipe/framework/formats/tensor/cpu_buffer_converters_test.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								mediapipe/framework/formats/tensor/cpu_buffer_converters_test.cc
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,282 @@
 | 
			
		|||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
#include "mediapipe/framework/formats/tensor/tensor2.h"
 | 
			
		||||
#include "mediapipe/framework/formats/tensor/views/buffer.h"
 | 
			
		||||
#include "mediapipe/framework/formats/tensor/views/cpu_buffer.h"
 | 
			
		||||
#include "testing/base/public/gmock.h"
 | 
			
		||||
#include "testing/base/public/gunit.h"
 | 
			
		||||
 | 
			
		||||
MATCHER_P(NearWithPrecision, precision, "") {
 | 
			
		||||
  return std::abs(std::get<0>(arg) - std::get<1>(arg)) < precision;
 | 
			
		||||
}
 | 
			
		||||
MATCHER_P(IntegerEqual, precision, "") {
 | 
			
		||||
  return std::get<0>(arg) == std::get<1>(arg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace mediapipe {
 | 
			
		||||
 | 
			
		||||
TEST(TensorCpuViewTest, TestWrite32ThenRead16) {
 | 
			
		||||
  Tensor2 tensor{Tensor2::Shape({1})};
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kWriteOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format =
 | 
			
		||||
                               TensorBufferDescriptor::Format::kFloat32}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    *view->data<float>() = 1234.0f;
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kReadOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format =
 | 
			
		||||
                               TensorBufferDescriptor::Format::kFloat16}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    EXPECT_EQ(*view->data<Float16>(), 1234.0f);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(TensorCpuViewTest, TestWrite16ThenRead32) {
 | 
			
		||||
  Tensor2 tensor{Tensor2::Shape({1})};
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kWriteOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format =
 | 
			
		||||
                               TensorBufferDescriptor::Format::kFloat16}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    *view->data<Float16>() = 1234.0f;
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kReadOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format =
 | 
			
		||||
                               TensorBufferDescriptor::Format::kFloat32}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    EXPECT_EQ(*view->data<float>(), 1234.0f);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(TensorCpuViewTest, TestWriteFloat32ThenReadInt8) {
 | 
			
		||||
  Tensor2 tensor{Tensor2::Shape({1})};
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kWriteOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format =
 | 
			
		||||
                               TensorBufferDescriptor::Format::kFloat32}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    *view->data<float>() = 0.121569f;
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kReadOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format = TensorBufferDescriptor::Format::kUInt8}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    EXPECT_EQ(
 | 
			
		||||
        *view->data<uint8_t>(),
 | 
			
		||||
        static_cast<uint8_t>(0.121569f * std::numeric_limits<uint8_t>::max()));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(TensorCpuViewTest, TestWriteInt8ThenReadFloat32) {
 | 
			
		||||
  Tensor2 tensor{Tensor2::Shape({1})};
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kWriteOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format = TensorBufferDescriptor::Format::kUInt8}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    *view->data<uint8_t>() =
 | 
			
		||||
        static_cast<uint8_t>(0.123f * std::numeric_limits<uint8_t>::max());
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kReadOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format =
 | 
			
		||||
                               TensorBufferDescriptor::Format::kFloat32}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    EXPECT_NEAR(*view->data<float>(), 0.123f,
 | 
			
		||||
                1.0f / std::numeric_limits<uint8_t>::max());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(TensorCpuViewTest, TestWriteUInt8ThenReadUInt16) {
 | 
			
		||||
  Tensor2 tensor{Tensor2::Shape({1})};
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kWriteOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format = TensorBufferDescriptor::Format::kUInt8}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    *view->data<uint8_t>() = 123;
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kReadOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format =
 | 
			
		||||
                               TensorBufferDescriptor::Format::kUInt16}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    EXPECT_EQ(*view->data<uint16_t>(), uint16_t{123} << 8);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(TensorCpuViewTest, TestWriteUInt16ThenReadUInt8) {
 | 
			
		||||
  Tensor2 tensor{Tensor2::Shape({1})};
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kWriteOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format =
 | 
			
		||||
                               TensorBufferDescriptor::Format::kUInt16}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    *view->data<uint16_t>() = uint16_t{123} << 8;
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kReadOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format = TensorBufferDescriptor::Format::kUInt8}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    EXPECT_EQ(*view->data<uint8_t>(), 123);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(TensorCpuViewTest, TestWriteNegativeInt8ThenReadUInt8) {
 | 
			
		||||
  Tensor2 tensor{Tensor2::Shape({1})};
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kWriteOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format = TensorBufferDescriptor::Format::kInt8}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    *view->data<int8_t>() = -123;
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kReadOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format = TensorBufferDescriptor::Format::kUInt8}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    EXPECT_EQ(*view->data<uint8_t>(), 0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(TensorCpuViewTest, TestWritePositiveInt8ThenReadUInt8) {
 | 
			
		||||
  Tensor2 tensor{Tensor2::Shape({1})};
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kWriteOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format = TensorBufferDescriptor::Format::kInt8}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    *view->data<int8_t>() = 123;
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kReadOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format = TensorBufferDescriptor::Format::kUInt8}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    EXPECT_EQ(*view->data<uint8_t>(), 123 * 2);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(TensorCpuViewTest, TestDequantization) {
 | 
			
		||||
  constexpr int num_elements = 20;
 | 
			
		||||
  // Gives quantization values in range [-100, 90].
 | 
			
		||||
  constexpr int zero_point = -100;
 | 
			
		||||
  constexpr float scale = 2.0f;
 | 
			
		||||
  Tensor2 tensor{Tensor2::Shape({num_elements})};
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kWriteOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {
 | 
			
		||||
                    .format = TensorBufferDescriptor::Format::kQuantizedInt8,
 | 
			
		||||
                    .quantization_parameters = {.scale = scale,
 | 
			
		||||
                                                .zero_point = zero_point}}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    auto data = view->data<int8_t>();
 | 
			
		||||
    for (int i = 0; i < num_elements; ++i) {
 | 
			
		||||
      // Add some bias (+1) to make round-up take place.
 | 
			
		||||
      data[i] = (i * 20 + 1) / scale + zero_point;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kReadOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format =
 | 
			
		||||
                               TensorBufferDescriptor::Format::kFloat32}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    std::vector<float> reference(num_elements);
 | 
			
		||||
    for (int i = 0; i < num_elements; ++i) {
 | 
			
		||||
      reference[i] = i * 20.0f + 1.0f;
 | 
			
		||||
    }
 | 
			
		||||
    EXPECT_THAT(absl::Span<float>(view->data<float>(), num_elements),
 | 
			
		||||
                testing::Pointwise(NearWithPrecision(1.001), reference));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(TensorCpuViewTest, TestQuantization) {
 | 
			
		||||
  constexpr int num_elements = 20;
 | 
			
		||||
  // Gives quantization values in range [-100, 90].
 | 
			
		||||
  constexpr int zero_point = -100;
 | 
			
		||||
  constexpr float scale = 2.0f;
 | 
			
		||||
  Tensor2 tensor{Tensor2::Shape({num_elements})};
 | 
			
		||||
  {
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view,
 | 
			
		||||
        tensor.GetView<Tensor2::View::Access::kWriteOnly>(
 | 
			
		||||
            TensorCpuViewDescriptor{
 | 
			
		||||
                .buffer = {.format =
 | 
			
		||||
                               TensorBufferDescriptor::Format::kFloat32}}));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    auto data = view->data<float>();
 | 
			
		||||
    for (int i = 0; i < num_elements; ++i) {
 | 
			
		||||
      // Add some bias (+1) to make round-up take place.
 | 
			
		||||
      data[i] = i * 20 + 1;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    TensorCpuViewDescriptor d{
 | 
			
		||||
        .buffer = {.format = TensorBufferDescriptor::Format::kQuantizedInt8,
 | 
			
		||||
                   .quantization_parameters = {.scale = scale,
 | 
			
		||||
                                               .zero_point = zero_point}}};
 | 
			
		||||
    MP_ASSERT_OK_AND_ASSIGN(
 | 
			
		||||
        auto view, tensor.GetView<Tensor2::View::Access::kReadOnly>(d));
 | 
			
		||||
    ASSERT_NE(view->data<void>(), nullptr);
 | 
			
		||||
    std::vector<int8_t> reference(num_elements);
 | 
			
		||||
    for (int i = 0; i < num_elements; ++i) {
 | 
			
		||||
      reference[i] = (i * 20 + 1) / scale + zero_point;
 | 
			
		||||
    }
 | 
			
		||||
    EXPECT_THAT(absl::Span<int8_t>(view->data<int8_t>(), num_elements),
 | 
			
		||||
                testing::Pointwise(IntegerEqual(0), reference));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace mediapipe
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user