|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <cstdint> |
|
#define EIGEN_USE_THREADS |
|
|
|
#define _USE_MATH_DEFINES |
|
|
|
#include <algorithm> |
|
#include <iterator> |
|
#include <set> |
|
#include <unordered_map> |
|
#include <unordered_set> |
|
#include <vector> |
|
|
|
#include "tensorflow/core/framework/op_kernel.h" |
|
#include "tensorflow/core/framework/register_types.h" |
|
#include "tensorflow/core/framework/tensor.h" |
|
#include "tensorflow/core/framework/tensor_shape.h" |
|
#include "tensorflow/core/framework/types.h" |
|
#include "tensorflow/core/lib/core/errors.h" |
|
#include "tensorflow/core/lib/core/status.h" |
|
#include "tensorflow/core/platform/logging.h" |
|
#include "merge_semantic_and_instance_maps_op_kernel.h" |
|
|
|
namespace tensorflow_models { |
|
namespace deeplab { |
|
namespace deeplab2 { |
|
|
|
namespace { |
|
|
|
using tensorflow::Tensor; |
|
using tensorflow::TensorShape; |
|
using tensorflow::TTypes; |
|
using tensorflow::errors::InvalidArgument; |
|
|
|
} |
|
|
|
namespace functor { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <> |
|
void MergeSemanticAndInstanceMaps<Eigen::ThreadPoolDevice>::operator()( |
|
const Eigen::ThreadPoolDevice& d, |
|
typename TTypes<int32_t, 3>::ConstTensor semantic_maps, |
|
typename TTypes<int32_t, 3>::ConstTensor instance_maps, |
|
const std::unordered_set<int32_t>& thing_ids_set, int label_divisor, |
|
int stuff_area_limit, int void_label, |
|
typename TTypes<int32_t, 3>::Tensor parsing_maps) { |
|
const int num_batches = semantic_maps.dimension(0); |
|
const int height = semantic_maps.dimension(1); |
|
const int width = semantic_maps.dimension(2); |
|
|
|
for (int b = 0; b < num_batches; ++b) { |
|
|
|
|
|
std::vector<bool> is_thing(height * width, true); |
|
|
|
|
|
|
|
|
|
|
|
|
|
using InstanceIdType = int32_t; |
|
using SemanticLabelType = int32_t; |
|
using CountsType = int32_t; |
|
std::unordered_map<InstanceIdType, |
|
std::unordered_map<SemanticLabelType, CountsType>> |
|
instance_id_to_semantic_histogram; |
|
|
|
std::unordered_map<SemanticLabelType, CountsType> stuff_label_to_area; |
|
for (int h = 0; h < height; ++h) { |
|
for (int w = 0; w < width; ++w) { |
|
const int semantic_val = semantic_maps(b, h, w); |
|
if (thing_ids_set.find(semantic_val) == thing_ids_set.end()) { |
|
|
|
is_thing[w + width * h] = false; |
|
++stuff_label_to_area[semantic_val]; |
|
continue; |
|
} |
|
const int instance_val = instance_maps(b, h, w); |
|
++instance_id_to_semantic_histogram[instance_val][semantic_val]; |
|
} |
|
} |
|
|
|
std::unordered_map<SemanticLabelType, CountsType> |
|
semantic_label_to_instance_counts; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::unordered_map<InstanceIdType, |
|
std::pair<SemanticLabelType, InstanceIdType>> |
|
instance_id_to_new_semantic_label_and_instance_id; |
|
for (const auto& instance_to_histogram : |
|
instance_id_to_semantic_histogram) { |
|
const int instance_val = instance_to_histogram.first; |
|
const std::unordered_map<SemanticLabelType, CountsType> |
|
semantic_histogram = instance_to_histogram.second; |
|
int semantic_label = -1; |
|
int max_count = 0; |
|
|
|
for (const auto& semantic_to_count : semantic_histogram) { |
|
|
|
if (semantic_to_count.second > max_count || |
|
(semantic_to_count.second == max_count && |
|
semantic_to_count.first < semantic_label)) { |
|
max_count = semantic_to_count.second; |
|
semantic_label = semantic_to_count.first; |
|
} |
|
} |
|
++semantic_label_to_instance_counts[semantic_label]; |
|
|
|
|
|
instance_id_to_new_semantic_label_and_instance_id[instance_val] = { |
|
semantic_label, semantic_label_to_instance_counts[semantic_label]}; |
|
} |
|
|
|
|
|
std::vector<SemanticLabelType> semantic_map(height * width); |
|
|
|
std::vector<InstanceIdType> instance_map(height * width); |
|
for (int h = 0; h < height; ++h) { |
|
for (int w = 0; w < width; ++w) { |
|
const int pixel = w + width * h; |
|
if (is_thing[pixel]) { |
|
const int instance_val = instance_maps(b, h, w); |
|
|
|
|
|
std::tie(semantic_map[pixel], instance_map[pixel]) = |
|
instance_id_to_new_semantic_label_and_instance_id[instance_val]; |
|
} else { |
|
|
|
|
|
|
|
|
|
const int semantic_val = semantic_maps(b, h, w); |
|
if (stuff_area_limit > 0 && |
|
stuff_label_to_area[semantic_val] <= stuff_area_limit) { |
|
semantic_map[pixel] = void_label; |
|
} else { |
|
semantic_map[pixel] = semantic_val; |
|
} |
|
|
|
|
|
instance_map[pixel] = 0; |
|
} |
|
} |
|
} |
|
|
|
for (int h = 0; h < height; ++h) { |
|
for (int w = 0; w < width; ++w) { |
|
const int pixel = w + width * h; |
|
parsing_maps(b, h, w) = |
|
semantic_map[pixel] * label_divisor + instance_map[pixel]; |
|
} |
|
} |
|
} |
|
} |
|
|
|
template <> |
|
std::unordered_set<int32_t> Convert1DInt32TensorToSet( |
|
const Eigen::ThreadPoolDevice& d, const Tensor& tensor) { |
|
std::unordered_set<int32_t> target_set; |
|
const int n_vals = tensor.dim_size(0); |
|
typename TTypes<int32_t, 1>::ConstTensor tensor_data = |
|
tensor.tensor<int32_t, 1>(); |
|
for (int i = 0; i < n_vals; i++) { |
|
target_set.insert(tensor_data(i)); |
|
} |
|
|
|
return target_set; |
|
} |
|
|
|
} |
|
|
|
template <typename Device> |
|
class MergeSemanticAndInstanceMapsOp : public tensorflow::OpKernel { |
|
public: |
|
explicit MergeSemanticAndInstanceMapsOp( |
|
tensorflow::OpKernelConstruction* context) |
|
: OpKernel(context) { |
|
OP_REQUIRES_OK(context, context->GetAttr("label_divisor", &label_divisor_)); |
|
OP_REQUIRES(context, label_divisor_ > 0, |
|
InvalidArgument("Label divisor must be positive.")); |
|
OP_REQUIRES_OK(context, |
|
context->GetAttr("stuff_area_limit", &stuff_area_limit_)); |
|
OP_REQUIRES(context, stuff_area_limit_ >= 0, |
|
InvalidArgument("Stuff area limit must be non-negative.")); |
|
OP_REQUIRES_OK(context, context->GetAttr("void_label", &void_label_)); |
|
OP_REQUIRES(context, void_label_ >= 0, |
|
InvalidArgument("Void label must be non-negative.")); |
|
} |
|
|
|
void Compute(tensorflow::OpKernelContext* context) override { |
|
|
|
const Tensor& semantic_maps = context->input(0); |
|
const Tensor& instance_maps = context->input(1); |
|
const Tensor& thing_ids_tensor = context->input(2); |
|
|
|
|
|
std::unordered_set<int32_t> thing_ids_set = |
|
functor::Convert1DInt32TensorToSet(context->eigen_device<Device>(), |
|
thing_ids_tensor); |
|
|
|
|
|
const int batch = semantic_maps.dim_size(0); |
|
const int height = semantic_maps.dim_size(1); |
|
const int width = semantic_maps.dim_size(2); |
|
|
|
|
|
OP_REQUIRES(context, |
|
instance_maps.dim_size(0) == batch && |
|
instance_maps.dim_size(1) == height && |
|
instance_maps.dim_size(2) == width, |
|
InvalidArgument( |
|
"Expect semantic and instance maps have the same shape.", |
|
instance_maps.shape().DebugString())); |
|
|
|
Tensor* parsing_maps = nullptr; |
|
OP_REQUIRES_OK(context, |
|
context->allocate_output( |
|
0, TensorShape({batch, height, width}), &parsing_maps)); |
|
|
|
functor::MergeSemanticAndInstanceMaps<Device>()( |
|
context->eigen_device<Device>(), semantic_maps.tensor<int32_t, 3>(), |
|
instance_maps.tensor<int32_t, 3>(), thing_ids_set, label_divisor_, |
|
stuff_area_limit_, void_label_, parsing_maps->tensor<int32_t, 3>()); |
|
} |
|
|
|
private: |
|
|
|
|
|
int label_divisor_; |
|
|
|
|
|
|
|
int stuff_area_limit_; |
|
|
|
|
|
int void_label_; |
|
}; |
|
|
|
REGISTER_KERNEL_BUILDER( |
|
Name("MergeSemanticAndInstanceMaps").Device(tensorflow::DEVICE_CPU), |
|
MergeSemanticAndInstanceMapsOp<Eigen::ThreadPoolDevice>); |
|
|
|
#ifdef GOOGLE_CUDA |
|
REGISTER_KERNEL_BUILDER( |
|
Name("MergeSemanticAndInstanceMaps").Device(tensorflow::DEVICE_GPU), |
|
MergeSemanticAndInstanceMapsOp<Eigen::GpuDevice>) |
|
#endif |
|
|
|
} |
|
} |
|
} |
|
|