[TF FE] Support AdjustHue operation (#24769)
### Details: - Adapt from #24511 . Converting code repeated. Instead import from there? - Converting logic a little different from https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/kernels/image/adjust_hue_op.cc . Maybe less efficient? ### Tickets: - #24033 Sorry, commit history is wrong, should have created branch from latest upstream. --------- Co-authored-by: Roman Kazantsev <roman.kazantsev@intel.com>
This commit is contained in:
parent
3ffac369f1
commit
5ea0e96fc2
|
|
@ -21,8 +21,8 @@ A "supported operation" is one that TensorFlow Frontend can convert to the OpenV
|
|||
| AddV2 | YES | |
|
||||
| AdjustContrast | NO | |
|
||||
| AdjustContrastv2 | YES | |
|
||||
| AdjustHue | NO | |
|
||||
| AdjustSaturation | YES | |
|
||||
| AdjustHue | YES | |
|
||||
| AdjustSaturation | YES | |
|
||||
| All | YES | |
|
||||
| AllCandidateSampler | NO | |
|
||||
| AllToAll | NO | |
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ const std::map<std::string, CreatorFunction> get_supported_ops() {
|
|||
// Separate translators:
|
||||
{"AddN", CreatorFunction(translate_add_n_op)},
|
||||
{"AdjustContrastv2", CreatorFunction(translate_adjust_contrast_op)},
|
||||
{"AdjustHue", CreatorFunction(translate_adjust_hue_op)},
|
||||
{"AdjustSaturation", CreatorFunction(translate_adjust_saturation_op)},
|
||||
{"Angle", CreatorFunction(translate_angle_op)},
|
||||
{"ArgMax", CreatorFunction(translate_arg_max_op)},
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ OP_CONVERTER(translate_addv2_op);
|
|||
OP_CONVERTER(translate_add_n_op);
|
||||
OP_CONVERTER(translate_approximate_equal_op);
|
||||
OP_CONVERTER(translate_adjust_contrast_op);
|
||||
OP_CONVERTER(translate_adjust_hue_op);
|
||||
OP_CONVERTER(translate_adjust_saturation_op);
|
||||
OP_CONVERTER(translate_angle_op);
|
||||
OP_CONVERTER(translate_arg_max_op);
|
||||
|
|
|
|||
|
|
@ -155,6 +155,13 @@ ov::Output<ov::Node> get_data_slice(const ov::Output<ov::Node>& data,
|
|||
|
||||
ov::Output<ov::Node> compute_broadcast_args(const ov::Output<ov::Node>& shape1, const ov::Output<ov::Node>& shape2);
|
||||
|
||||
std::shared_ptr<std::tuple<std::shared_ptr<ov::Node>, std::shared_ptr<ov::Node>, std::shared_ptr<ov::Node>>> rgb_to_hsv(
|
||||
const std::shared_ptr<ov::Node>& images);
|
||||
|
||||
std::shared_ptr<ov::Node> hsv_to_rgb(const std::shared_ptr<ov::Node>& h,
|
||||
const std::shared_ptr<ov::Node>& s,
|
||||
const std::shared_ptr<ov::Node>& v);
|
||||
|
||||
} // namespace tensorflow
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (C) 2018-2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "common_op_table.hpp"
|
||||
#include "openvino/op/add.hpp"
|
||||
#include "openvino/op/convert_like.hpp"
|
||||
#include "openvino/op/floor.hpp"
|
||||
#include "openvino/op/subtract.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ov;
|
||||
using namespace ov::op;
|
||||
|
||||
namespace ov {
|
||||
namespace frontend {
|
||||
namespace tensorflow {
|
||||
namespace op {
|
||||
|
||||
OutputVector translate_adjust_hue_op(const NodeContext& node) {
|
||||
default_op_checks(node, 2, {"AdjustHue"});
|
||||
auto images = node.get_input(0);
|
||||
auto delta = node.get_input(1);
|
||||
auto node_name = node.get_name();
|
||||
|
||||
auto hsv_components = rgb_to_hsv(images.get_node_shared_ptr());
|
||||
auto hh = get<0>(*hsv_components);
|
||||
auto ss = get<1>(*hsv_components);
|
||||
auto vv = get<2>(*hsv_components);
|
||||
|
||||
delta = make_shared<v1::ConvertLike>(delta, images);
|
||||
|
||||
auto hh_adjust_ = make_shared<v1::Add>(hh, delta);
|
||||
auto hh_adjust_floor = make_shared<v0::Floor>(hh_adjust_);
|
||||
auto hh_adjust = make_shared<v1::Subtract>(hh_adjust_, hh_adjust_floor);
|
||||
|
||||
auto new_images = hsv_to_rgb(hh_adjust, ss, vv);
|
||||
|
||||
auto new_images_adjust_hue = new_images->output(0);
|
||||
|
||||
set_node_name(node_name, new_images_adjust_hue.get_node_shared_ptr());
|
||||
return {new_images_adjust_hue};
|
||||
}
|
||||
|
||||
} // namespace op
|
||||
} // namespace tensorflow
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
||||
|
|
@ -3,33 +3,10 @@
|
|||
//
|
||||
|
||||
#include "common_op_table.hpp"
|
||||
#include "openvino/op/abs.hpp"
|
||||
#include "openvino/op/add.hpp"
|
||||
#include "openvino/op/broadcast.hpp"
|
||||
#include "openvino/op/clamp.hpp"
|
||||
#include "openvino/op/concat.hpp"
|
||||
#include "openvino/op/constant.hpp"
|
||||
#include "openvino/op/convert.hpp"
|
||||
#include "openvino/op/convert_like.hpp"
|
||||
#include "openvino/op/divide.hpp"
|
||||
#include "openvino/op/equal.hpp"
|
||||
#include "openvino/op/floor.hpp"
|
||||
#include "openvino/op/floor_mod.hpp"
|
||||
#include "openvino/op/gather.hpp"
|
||||
#include "openvino/op/greater.hpp"
|
||||
#include "openvino/op/less.hpp"
|
||||
#include "openvino/op/maximum.hpp"
|
||||
#include "openvino/op/minimum.hpp"
|
||||
#include "openvino/op/multiply.hpp"
|
||||
#include "openvino/op/reduce_max.hpp"
|
||||
#include "openvino/op/reduce_mean.hpp"
|
||||
#include "openvino/op/reduce_min.hpp"
|
||||
#include "openvino/op/select.hpp"
|
||||
#include "openvino/op/shape_of.hpp"
|
||||
#include "openvino/op/split.hpp"
|
||||
#include "openvino/op/squeeze.hpp"
|
||||
#include "openvino/op/subtract.hpp"
|
||||
#include "openvino/op/unsqueeze.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ov;
|
||||
|
|
@ -40,150 +17,14 @@ namespace frontend {
|
|||
namespace tensorflow {
|
||||
namespace op {
|
||||
|
||||
shared_ptr<tuple<shared_ptr<Node>, shared_ptr<Node>, shared_ptr<Node>>> convert_rgb_to_hsv(
|
||||
const shared_ptr<Node>& images,
|
||||
element::Type type) {
|
||||
// image format conversion based on
|
||||
// https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/kernels/image/adjust_saturation_op.cc
|
||||
|
||||
auto const_zero_f_ = make_shared<v0::Constant>(type, Shape{}, 0.0f);
|
||||
auto const_one_f_ = make_shared<v0::Constant>(type, Shape{}, 1.0f);
|
||||
auto const_six_f_ = make_shared<v0::Constant>(type, Shape{}, 6.0f);
|
||||
|
||||
// Find max and min across channel axis. Max = Value (V)
|
||||
auto const_minus_one_i_1 = make_shared<v0::Constant>(element::i32, Shape{1}, -1);
|
||||
auto max_rgb = make_shared<v1::ReduceMax>(images, const_minus_one_i_1, true);
|
||||
auto min_rgb = make_shared<v1::ReduceMin>(images, const_minus_one_i_1, true);
|
||||
|
||||
auto range = make_shared<v1::Subtract>(max_rgb, min_rgb);
|
||||
auto vv = max_rgb;
|
||||
|
||||
// compute Saturation (S)
|
||||
auto ss_ = make_shared<v1::Divide>(range, vv);
|
||||
auto ss = make_shared<v1::Select>(make_shared<v1::Greater>(vv, const_zero_f_), ss_, const_zero_f_);
|
||||
|
||||
// compute normalization factor (for Hue calculation)
|
||||
auto norm = make_shared<v1::Divide>(const_one_f_, make_shared<v1::Multiply>(const_six_f_, range));
|
||||
|
||||
// Split the image tensor into R, G, B channels
|
||||
auto const_minus_one_i = make_shared<v0::Constant>(element::i32, Shape{}, -1);
|
||||
auto channels = make_shared<v1::Split>(images, const_minus_one_i, 3);
|
||||
|
||||
auto r = channels->output(0);
|
||||
auto g = channels->output(1);
|
||||
auto b = channels->output(2);
|
||||
|
||||
// compute Hue (H)
|
||||
// determine which component is the max (V) to compute Hue (H)
|
||||
auto r_eq_v = make_shared<v1::Equal>(r, vv);
|
||||
auto g_eq_v = make_shared<v1::Equal>(g, vv);
|
||||
|
||||
// r == vv: hh = norm * (g - b)
|
||||
auto hue_case_r = make_shared<v1::Multiply>(norm, make_shared<v1::Subtract>(g, b));
|
||||
|
||||
// g == vv: hh = norm * (b - r) + 2.0 / 6.0
|
||||
auto const_2_by_6 = make_shared<v0::Constant>(type, Shape{}, 2.0f / 6.0f);
|
||||
auto hue_case_g =
|
||||
make_shared<v1::Add>(make_shared<v1::Multiply>(norm, make_shared<v1::Subtract>(b, r)), const_2_by_6);
|
||||
|
||||
// b == vv: hh = norm * (r - g) + 4.0 / 6.0
|
||||
auto const_4_by_6 = make_shared<v0::Constant>(type, Shape{}, 4.0f / 6.0f);
|
||||
auto hue_case_b =
|
||||
make_shared<v1::Add>(make_shared<v1::Multiply>(norm, make_shared<v1::Subtract>(r, g)), const_4_by_6);
|
||||
|
||||
// select hue based on the maximum component
|
||||
// check if `r` is the max, otherwise check if `g` is the max, if not use `b`'s hue
|
||||
auto hh = make_shared<v1::Select>(r_eq_v,
|
||||
hue_case_r, // Use hue_case_r if r is max
|
||||
make_shared<v1::Select>(g_eq_v,
|
||||
hue_case_g, // Use hue_case_g if g is max
|
||||
hue_case_b // Use hue_case_b otherwise (b is max)
|
||||
));
|
||||
|
||||
// range = 0.0: hh = 0
|
||||
auto hh_zero_range = make_shared<v1::Select>(make_shared<v1::Equal>(range, const_zero_f_), const_zero_f_, hh);
|
||||
|
||||
// hh < 0.0: hh = hh + 1
|
||||
auto hh_final = make_shared<v1::Select>(make_shared<v1::Less>(hh, const_zero_f_),
|
||||
make_shared<v1::Add>(hh_zero_range, const_one_f_),
|
||||
hh_zero_range);
|
||||
|
||||
return make_shared<tuple<shared_ptr<Node>, shared_ptr<Node>, shared_ptr<Node>>>(hh_final, ss, vv);
|
||||
}
|
||||
|
||||
shared_ptr<Node> hsv_to_rgb(const shared_ptr<Node>& h,
|
||||
const shared_ptr<Node>& s,
|
||||
const shared_ptr<Node>& v,
|
||||
element::Type type) {
|
||||
// image format conversion based on
|
||||
// https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/kernels/image/adjust_saturation_op.cc
|
||||
auto const_six_f_ = make_shared<v0::Constant>(type, Shape{}, 6.0f);
|
||||
auto const_two_f_ = make_shared<v0::Constant>(type, Shape{}, 2.0f);
|
||||
auto const_one_f_ = make_shared<v0::Constant>(type, Shape{}, 1.0f);
|
||||
auto const_zero_f_ = make_shared<v0::Constant>(type, Shape{}, 0.0f);
|
||||
|
||||
auto const_minus_one_i_ = make_shared<v0::Constant>(element::i32, Shape{}, -1);
|
||||
auto const_minus_two_i_ = make_shared<v0::Constant>(element::i32, Shape{}, -2);
|
||||
|
||||
// c = s * v;
|
||||
auto c = make_shared<v1::Multiply>(s, v);
|
||||
// m = v - c;
|
||||
auto m = make_shared<v1::Subtract>(v, c);
|
||||
// dh = h * 6;
|
||||
auto dh = make_shared<v1::Multiply>(h, const_six_f_);
|
||||
|
||||
// fmodu rounded to within [0, 2)
|
||||
auto fmodu = make_shared<v1::FloorMod>(dh, const_two_f_);
|
||||
|
||||
// x = c * (1 - std::abs(fmodu - 1));
|
||||
auto x = make_shared<v1::Multiply>(
|
||||
c,
|
||||
make_shared<v1::Subtract>(const_one_f_, make_shared<v0::Abs>(make_shared<v1::Subtract>(fmodu, const_one_f_))));
|
||||
|
||||
// h_category: [batch_dims..., H, W, 1]
|
||||
auto h_category = make_shared<v0::Convert>(make_shared<v0::Floor>(dh), element::i32);
|
||||
|
||||
auto zeros = make_shared<v3::Broadcast>(const_zero_f_, make_shared<v3::ShapeOf>(x));
|
||||
|
||||
auto rr_options = NodeVector{c, x, zeros, zeros, x, c};
|
||||
auto gg_options = NodeVector{x, c, c, x, zeros, zeros};
|
||||
auto bb_options = NodeVector{zeros, zeros, x, c, c, x};
|
||||
|
||||
// rr_concat: [batch_dims..., H, W, 6]
|
||||
auto rr_concat = make_shared<v0::Concat>(rr_options, -1);
|
||||
auto gg_concat = make_shared<v0::Concat>(gg_options, -1);
|
||||
auto bb_concat = make_shared<v0::Concat>(bb_options, -1);
|
||||
|
||||
// rr_unsqueeze: [batch_dims..., H, W, 6, 1]
|
||||
auto rr_unsqueeze = make_shared<v0::Unsqueeze>(rr_concat, const_minus_one_i_);
|
||||
auto gg_unsqueeze = make_shared<v0::Unsqueeze>(gg_concat, const_minus_one_i_);
|
||||
auto bb_unsqueeze = make_shared<v0::Unsqueeze>(bb_concat, const_minus_one_i_);
|
||||
|
||||
// rgb_options: [batch_dims..., H, W, 6, 3]
|
||||
auto rgb_options = make_shared<v0::Concat>(NodeVector{rr_unsqueeze, gg_unsqueeze, bb_unsqueeze}, -1);
|
||||
|
||||
// use a gather operation to select the correct channel values based on h_category
|
||||
// rgb: [batch_dims..., H, W, 3]
|
||||
// int batch_dim = rgb_options->get_shape().size() - 2;
|
||||
int batch_dim = -1;
|
||||
auto rgb_gather = make_shared<v8::Gather>(rgb_options, h_category, const_minus_two_i_, batch_dim);
|
||||
auto rgb = make_shared<v0::Squeeze>(rgb_gather, const_minus_two_i_);
|
||||
|
||||
auto rgb_adjust = make_shared<v1::Add>(rgb, m);
|
||||
|
||||
// return concatenated RGB
|
||||
return rgb_adjust;
|
||||
}
|
||||
|
||||
OutputVector translate_adjust_saturation_op(const NodeContext& node) {
|
||||
default_op_checks(node, 2, {"AdjustSaturation"});
|
||||
auto images = node.get_input(0);
|
||||
auto scale = node.get_input(1);
|
||||
auto node_name = node.get_name();
|
||||
|
||||
auto type = images.get_element_type();
|
||||
auto hsv_components = rgb_to_hsv(images.get_node_shared_ptr());
|
||||
|
||||
auto hsv_components = convert_rgb_to_hsv(images.get_node_shared_ptr(), type);
|
||||
auto hh = get<0>(*hsv_components);
|
||||
auto ss = get<1>(*hsv_components);
|
||||
auto vv = get<2>(*hsv_components);
|
||||
|
|
@ -192,12 +33,12 @@ OutputVector translate_adjust_saturation_op(const NodeContext& node) {
|
|||
|
||||
auto ss_adjust = make_shared<v0::Clamp>(make_shared<v1::Multiply>(ss, scale), 0.0f, 1.0f);
|
||||
|
||||
auto new_images = hsv_to_rgb(hh, ss_adjust, vv, type);
|
||||
auto new_images = hsv_to_rgb(hh, ss_adjust, vv);
|
||||
|
||||
auto adjust_saturation = new_images->output(0);
|
||||
auto new_images_adjust_saturation = new_images->output(0);
|
||||
|
||||
set_node_name(node_name, adjust_saturation.get_node_shared_ptr());
|
||||
return {adjust_saturation};
|
||||
set_node_name(node_name, new_images_adjust_saturation.get_node_shared_ptr());
|
||||
return {new_images_adjust_saturation};
|
||||
}
|
||||
|
||||
} // namespace op
|
||||
|
|
|
|||
|
|
@ -8,20 +8,36 @@
|
|||
|
||||
#include "common_op_table.hpp"
|
||||
#include "helper_ops/complex_type_mark.hpp"
|
||||
#include "openvino/op/abs.hpp"
|
||||
#include "openvino/op/add.hpp"
|
||||
#include "openvino/op/broadcast.hpp"
|
||||
#include "openvino/op/concat.hpp"
|
||||
#include "openvino/op/constant.hpp"
|
||||
#include "openvino/op/convert.hpp"
|
||||
#include "openvino/op/convolution.hpp"
|
||||
#include "openvino/op/divide.hpp"
|
||||
#include "openvino/op/equal.hpp"
|
||||
#include "openvino/op/floor.hpp"
|
||||
#include "openvino/op/floor_mod.hpp"
|
||||
#include "openvino/op/gather.hpp"
|
||||
#include "openvino/op/greater.hpp"
|
||||
#include "openvino/op/group_conv.hpp"
|
||||
#include "openvino/op/less.hpp"
|
||||
#include "openvino/op/maximum.hpp"
|
||||
#include "openvino/op/multiply.hpp"
|
||||
#include "openvino/op/pad.hpp"
|
||||
#include "openvino/op/parameter.hpp"
|
||||
#include "openvino/op/reduce_max.hpp"
|
||||
#include "openvino/op/reduce_min.hpp"
|
||||
#include "openvino/op/reshape.hpp"
|
||||
#include "openvino/op/select.hpp"
|
||||
#include "openvino/op/shape_of.hpp"
|
||||
#include "openvino/op/slice.hpp"
|
||||
#include "openvino/op/split.hpp"
|
||||
#include "openvino/op/squeeze.hpp"
|
||||
#include "openvino/op/subtract.hpp"
|
||||
#include "openvino/op/transpose.hpp"
|
||||
#include "openvino/op/unsqueeze.hpp"
|
||||
|
||||
using namespace ov;
|
||||
using namespace ov::op;
|
||||
|
|
@ -414,6 +430,136 @@ Output<Node> compute_broadcast_args(const Output<Node>& shape1, const Output<Nod
|
|||
auto broadcasted_shape = make_shared<v1::Maximum>(padded_s0, padded_s1);
|
||||
return broadcasted_shape->output(0);
|
||||
}
|
||||
|
||||
shared_ptr<tuple<shared_ptr<Node>, shared_ptr<Node>, shared_ptr<Node>>> rgb_to_hsv(const shared_ptr<Node>& images) {
|
||||
// image format conversion based on
|
||||
// https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/kernels/image/adjust_saturation_op.cc
|
||||
auto const_zero_f_ = create_same_type_const_scalar<float>(images, 0.0f);
|
||||
auto const_one_f_ = create_same_type_const_scalar<float>(images, 1.0f);
|
||||
auto const_six_f_ = create_same_type_const_scalar<float>(images, 6.0f);
|
||||
|
||||
// find max and min across channel axis. Max = Value (V)
|
||||
auto const_minus_one_i_1 = make_shared<v0::Constant>(element::i32, Shape{1}, -1);
|
||||
auto max_rgb = make_shared<v1::ReduceMax>(images, const_minus_one_i_1, true);
|
||||
auto min_rgb = make_shared<v1::ReduceMin>(images, const_minus_one_i_1, true);
|
||||
|
||||
auto range = make_shared<v1::Subtract>(max_rgb, min_rgb);
|
||||
auto vv = max_rgb;
|
||||
|
||||
// compute Saturation (S)
|
||||
auto ss_ = make_shared<v1::Divide>(range, vv);
|
||||
auto ss = make_shared<v1::Select>(make_shared<v1::Greater>(vv, const_zero_f_), ss_, const_zero_f_);
|
||||
|
||||
// compute normalization factor (for Hue calculation)
|
||||
auto norm = make_shared<v1::Divide>(const_one_f_, make_shared<v1::Multiply>(const_six_f_, range));
|
||||
|
||||
// split the image tensor into R, G, B channels
|
||||
auto const_minus_one_i = make_shared<v0::Constant>(element::i32, Shape{}, -1);
|
||||
auto channels = make_shared<v1::Split>(images, const_minus_one_i, 3);
|
||||
|
||||
auto r = channels->output(0);
|
||||
auto g = channels->output(1);
|
||||
auto b = channels->output(2);
|
||||
|
||||
// compute Hue (H)
|
||||
// determine which component is the max (V) to compute Hue (H)
|
||||
auto r_eq_v = make_shared<v1::Equal>(r, vv);
|
||||
auto g_eq_v = make_shared<v1::Equal>(g, vv);
|
||||
|
||||
// r == vv: hh = norm * (g - b)
|
||||
auto hue_case_r = make_shared<v1::Multiply>(norm, make_shared<v1::Subtract>(g, b));
|
||||
|
||||
// g == vv: hh = norm * (b - r) + 2.0 / 6.0
|
||||
auto const_2_by_6 = create_same_type_const_scalar<float>(images, 2.0f / 6.0f);
|
||||
auto hue_case_g =
|
||||
make_shared<v1::Add>(make_shared<v1::Multiply>(norm, make_shared<v1::Subtract>(b, r)), const_2_by_6);
|
||||
|
||||
// b == vv: hh = norm * (r - g) + 4.0 / 6.0
|
||||
auto const_4_by_6 = create_same_type_const_scalar<float>(images, 4.0f / 6.0f);
|
||||
auto hue_case_b =
|
||||
make_shared<v1::Add>(make_shared<v1::Multiply>(norm, make_shared<v1::Subtract>(r, g)), const_4_by_6);
|
||||
|
||||
// select hue based on the maximum component
|
||||
// check if `r` is the max, otherwise check if `g` is the max, if not use `b`'s hue
|
||||
auto hh = make_shared<v1::Select>(r_eq_v,
|
||||
hue_case_r, // Use hue_case_r if r is max
|
||||
make_shared<v1::Select>(g_eq_v,
|
||||
hue_case_g, // Use hue_case_g if g is max
|
||||
hue_case_b // Use hue_case_b otherwise (b is max)
|
||||
));
|
||||
|
||||
// range = 0.0: hh = 0
|
||||
auto hh_zero_range = make_shared<v1::Select>(make_shared<v1::Equal>(range, const_zero_f_), const_zero_f_, hh);
|
||||
|
||||
// hh < 0.0: hh = hh + 1
|
||||
auto hh_final = make_shared<v1::Select>(make_shared<v1::Less>(hh, const_zero_f_),
|
||||
make_shared<v1::Add>(hh_zero_range, const_one_f_),
|
||||
hh_zero_range);
|
||||
|
||||
return make_shared<tuple<shared_ptr<Node>, shared_ptr<Node>, shared_ptr<Node>>>(hh_final, ss, vv);
|
||||
}
|
||||
|
||||
shared_ptr<Node> hsv_to_rgb(const shared_ptr<Node>& h, const shared_ptr<Node>& s, const shared_ptr<Node>& v) {
|
||||
// image format conversion based on
|
||||
// https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/kernels/image/adjust_saturation_op.cc
|
||||
auto const_six_f_ = create_same_type_const_scalar<float>(h, 6.0f);
|
||||
auto const_two_f_ = create_same_type_const_scalar<float>(h, 2.0f);
|
||||
auto const_one_f_ = create_same_type_const_scalar<float>(h, 1.0f);
|
||||
auto const_zero_f_ = create_same_type_const_scalar<float>(h, 0.0f);
|
||||
|
||||
auto const_minus_one_i_ = make_shared<v0::Constant>(element::i32, Shape{}, -1);
|
||||
auto const_minus_two_i_ = make_shared<v0::Constant>(element::i32, Shape{}, -2);
|
||||
|
||||
// c = s * v;
|
||||
auto c = make_shared<v1::Multiply>(s, v);
|
||||
// m = v - c;
|
||||
auto m = make_shared<v1::Subtract>(v, c);
|
||||
// dh = h * 6;
|
||||
auto dh = make_shared<v1::Multiply>(h, const_six_f_);
|
||||
|
||||
// fmodu rounded to within [0, 2)
|
||||
auto fmodu = make_shared<v1::FloorMod>(dh, const_two_f_);
|
||||
|
||||
// x = c * (1 - std::abs(fmodu - 1));
|
||||
auto x = make_shared<v1::Multiply>(
|
||||
c,
|
||||
make_shared<v1::Subtract>(const_one_f_, make_shared<v0::Abs>(make_shared<v1::Subtract>(fmodu, const_one_f_))));
|
||||
|
||||
// h_category: [batch_dims..., H, W, 1]
|
||||
auto h_category = make_shared<v0::Convert>(make_shared<v0::Floor>(dh), element::i32);
|
||||
|
||||
auto zeros = make_shared<v3::Broadcast>(const_zero_f_, make_shared<v3::ShapeOf>(x));
|
||||
|
||||
auto rr_options = NodeVector{c, x, zeros, zeros, x, c};
|
||||
auto gg_options = NodeVector{x, c, c, x, zeros, zeros};
|
||||
auto bb_options = NodeVector{zeros, zeros, x, c, c, x};
|
||||
|
||||
// rr_concat: [batch_dims..., H, W, 6]
|
||||
auto rr_concat = make_shared<v0::Concat>(rr_options, -1);
|
||||
auto gg_concat = make_shared<v0::Concat>(gg_options, -1);
|
||||
auto bb_concat = make_shared<v0::Concat>(bb_options, -1);
|
||||
|
||||
// rr_unsqueeze: [batch_dims..., H, W, 6, 1]
|
||||
auto rr_unsqueeze = make_shared<v0::Unsqueeze>(rr_concat, const_minus_one_i_);
|
||||
auto gg_unsqueeze = make_shared<v0::Unsqueeze>(gg_concat, const_minus_one_i_);
|
||||
auto bb_unsqueeze = make_shared<v0::Unsqueeze>(bb_concat, const_minus_one_i_);
|
||||
|
||||
// rgb_options: [batch_dims..., H, W, 6, 3]
|
||||
auto rgb_options = make_shared<v0::Concat>(NodeVector{rr_unsqueeze, gg_unsqueeze, bb_unsqueeze}, -1);
|
||||
|
||||
// use a gather operation to select the correct channel values based on h_category
|
||||
// rgb: [batch_dims..., H, W, 3]
|
||||
// int batch_dim = rgb_options->get_shape().size() - 2;
|
||||
int batch_dim = -1;
|
||||
auto rgb_gather = make_shared<v8::Gather>(rgb_options, h_category, const_minus_two_i_, batch_dim);
|
||||
auto rgb = make_shared<v0::Squeeze>(rgb_gather, const_minus_two_i_);
|
||||
|
||||
auto rgb_adjust = make_shared<v1::Add>(rgb, m);
|
||||
|
||||
// return concatenated RGB
|
||||
return rgb_adjust;
|
||||
}
|
||||
|
||||
} // namespace tensorflow
|
||||
} // namespace frontend
|
||||
} // namespace ov
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
# Copyright (C) 2018-2024 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import platform
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
import tensorflow as tf
|
||||
from common.tf_layer_test_class import CommonTFLayerTest
|
||||
|
||||
class TestAdjustHue(CommonTFLayerTest):
|
||||
def _prepare_input(self, inputs_info):
|
||||
assert 'images:0' in inputs_info
|
||||
if self.special_case == "Black Image":
|
||||
images_shape = inputs_info['images:0']
|
||||
inputs_data = {}
|
||||
inputs_data['images:0'] = np.zeros(images_shape).astype(self.input_type)
|
||||
elif self.special_case == "Grayscale Image":
|
||||
images_shape = inputs_info['images:0']
|
||||
inputs_data = {}
|
||||
inputs_data['images:0'] = np.ones(images_shape).astype(self.input_type) * np.random.rand()
|
||||
else:
|
||||
images_shape = inputs_info['images:0']
|
||||
inputs_data = {}
|
||||
inputs_data['images:0'] = np.random.rand(*images_shape).astype(self.input_type)
|
||||
# delta: [-1,1]
|
||||
inputs_data['delta:0'] = 2*np.random.rand() - 1
|
||||
|
||||
return inputs_data
|
||||
|
||||
def create_adjust_hue_net(self, input_shape, input_type, special_case=False):
|
||||
self.special_case = special_case
|
||||
self.input_type = input_type
|
||||
tf.compat.v1.reset_default_graph()
|
||||
# Create the graph and model
|
||||
with tf.compat.v1.Session() as sess:
|
||||
images = tf.compat.v1.placeholder(input_type, input_shape, 'images')
|
||||
delta = tf.compat.v1.placeholder(input_type, [], 'delta')
|
||||
tf.raw_ops.AdjustHue(images=images, delta=delta)
|
||||
tf.compat.v1.global_variables_initializer()
|
||||
tf_net = sess.graph_def
|
||||
|
||||
return tf_net, None
|
||||
|
||||
# Each input is a tensor of at least 3 dimensions.
|
||||
# The last dimension is interpreted as channels, and must be three.
|
||||
test_data_basic = [
|
||||
# tf op does not support np.float16
|
||||
dict(input_shape=[7, 7, 3], input_type=np.float32, special_case="Black Image"),
|
||||
dict(input_shape=[7, 7, 3], input_type=np.float32, special_case="Grayscale Image"),
|
||||
dict(input_shape=[5, 5, 3], input_type=np.float32),
|
||||
dict(input_shape=[5, 23, 27, 3], input_type=np.float32),
|
||||
dict(input_shape=[3, 4, 13, 15, 3], input_type=np.float32),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize("params", test_data_basic)
|
||||
@pytest.mark.precommit
|
||||
@pytest.mark.nightly
|
||||
@pytest.mark.xfail(condition=platform.system() in ('Darwin', 'Linux') and platform.machine() in ['arm', 'armv7l',
|
||||
'aarch64',
|
||||
'arm64', 'ARM64'],
|
||||
reason='Ticket - 126314, 132699')
|
||||
def test_adjust_hue_basic(self, params, ie_device, precision, ir_version, temp_dir,
|
||||
use_legacy_frontend):
|
||||
if ie_device == 'GPU':
|
||||
pytest.skip("Accuracy mismatch on GPU")
|
||||
self._test(*self.create_adjust_hue_net(**params),
|
||||
ie_device, precision, ir_version, temp_dir=temp_dir,
|
||||
use_legacy_frontend=use_legacy_frontend)
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
# Copyright (C) 2018-2024 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import platform
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
import tensorflow as tf
|
||||
|
|
@ -43,20 +45,25 @@ class TestAdjustSaturation(CommonTFLayerTest):
|
|||
# Each input is a tensor of at least 3 dimensions.
|
||||
# The last dimension is interpreted as channels, and must be three.
|
||||
test_data_basic = [
|
||||
# tf op does not support np.float16
|
||||
dict(input_shape=[7, 7, 3], input_type=np.float32, special_case="Black Image"),
|
||||
dict(input_shape=[7, 7, 3], input_type=np.float32, special_case="Grayscale Image"),
|
||||
dict(input_shape=[5, 5, 3], input_type=np.float32),
|
||||
dict(input_shape=[2, 3, 4, 3], input_type=np.float32),
|
||||
dict(input_shape=[1, 2, 3, 3, 3], input_type=np.float32),
|
||||
dict(input_shape=[5, 23, 27, 3], input_type=np.float32),
|
||||
dict(input_shape=[3, 4, 13, 15, 3], input_type=np.float32),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize("params", test_data_basic)
|
||||
@pytest.mark.precommit
|
||||
@pytest.mark.nightly
|
||||
@pytest.mark.xfail(condition=platform.system() in ('Darwin', 'Linux') and platform.machine() in ['arm', 'armv7l',
|
||||
'aarch64',
|
||||
'arm64', 'ARM64'],
|
||||
reason='Ticket - 126314, 132699')
|
||||
def test_adjust_saturation_basic(self, params, ie_device, precision, ir_version, temp_dir,
|
||||
use_legacy_frontend):
|
||||
if ie_device == 'GPU':
|
||||
pytest.skip("Accuracy mismatch on GPU")
|
||||
self._test(*self.create_adjust_saturation_net(**params),
|
||||
ie_device, precision, ir_version, temp_dir=temp_dir,
|
||||
use_legacy_frontend=use_legacy_frontend)
|
||||
use_legacy_frontend=use_legacy_frontend)
|
||||
|
|
|
|||
Loading…
Reference in New Issue