Skip to content

Commit 3c31ee9

Browse files
committed
Add support for bicubic interpolation in Upsampling2D layer, issue #401
1 parent 527d065 commit 3c31ee9

File tree

4 files changed

+64
-1
lines changed

4 files changed

+64
-1
lines changed

include/fdeep/layers/upsampling_2d_layer.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ class upsampling_2d_layer : public layer
4242
return {resize2d_bilinear(
4343
input, shape2(scale_factor_.height_ * input.shape().height_, scale_factor_.width_ * input.shape().width_))};
4444
}
45+
else if (interpolation_ == "bicubic")
46+
{
47+
return {resize2d_bicubic(
48+
input, shape2(scale_factor_.height_ * input.shape().height_, scale_factor_.width_ * input.shape().width_))};
49+
}
4550
else
4651
{
4752
raise_error("Invalid interpolation method: " + interpolation_);

include/fdeep/tensor.hpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,62 @@ inline tensor resize2d_bilinear(const tensor& in_vol, const shape2& target_size)
10601060
return out_vol;
10611061
}
10621062

1063+
//just a clamping function, since I don't know if project has one
1064+
inline std::size_t clamp(const std::size_t x, const std::size_t min, const std::size_t max) {
1065+
return fplus::min(fplus::max(x, min), max);
1066+
}
1067+
//cubic spline interpolation
1068+
inline float_type cerp(const float_type* p, const float_type t) {
1069+
return p[1] + 0.5f * t * (p[2] - p[0] + t * (2.0f * p[0] - 5.0f * p[1] + 4.0f * p[2] - p[3] + t * (3.0f * (p[1] - p[2]) + p[3] - p[0])));
1070+
}
1071+
//bicubic interpolation
1072+
inline float_type bicerp(const float_type* p, float_type x, float_type y) {
1073+
float_type tmp[4];
1074+
tmp[0] = cerp(p, x);
1075+
tmp[1] = cerp(p + 4, x);
1076+
tmp[2] = cerp(p + 8, x);
1077+
tmp[3] = cerp(p + 12, x);
1078+
return cerp(tmp, y);
1079+
}
1080+
inline float_type interpolate_2d_value_bicubicly(const tensor& t, float_type y, float_type x, std::size_t z) {
1081+
//Clamping to max size is done in the gathering step
1082+
y = fplus::max(0, y);
1083+
x = fplus::max(0, x);
1084+
//Corner coordinates as integers
1085+
std::size_t y0 = static_cast<std::size_t>(y);
1086+
std::size_t x0 = static_cast<std::size_t>(x);
1087+
//Relative position for interpolation
1088+
float_type vals[16];
1089+
//Gather values in area of 4x4 pixels, might want to switch the loop ordering, since idk what is the storage order
1090+
for(std::size_t i = 0; i < 4; i++) {
1091+
std::size_t pos_y = clamp(y0 + i - 1, 0, t.height() - 1);
1092+
for(std::size_t j = 0; j < 4; j++) {
1093+
std::size_t pos_x = clamp(x0 + j - 1, 0, t.width() - 1);
1094+
vals[i * 4 + j] = t.get_ignore_rank(tensor_pos(pos_y, pos_x, z));
1095+
}
1096+
}
1097+
float_type x_t = x - static_cast<float_type>(x0);
1098+
float_type y_t = y - static_cast<float_type>(y0);
1099+
return static_cast<float_type>(bicerp(vals, x_t, y_t));
1100+
}
1101+
1102+
inline tensor resize2d_bicubic(const tensor& in_vol, const shape2& target_size) {
1103+
tensor out_vol(tensor_shape(target_size.height_, target_size.width_, in_vol.shape().depth_), 0);
1104+
const float_type scale_y = static_cast<float_type>(target_size.height_) / static_cast<float_type>(in_vol.shape().height_);
1105+
const float_type scale_x = static_cast<float_type>(target_size.width_) / static_cast<float_type>(in_vol.shape().width_);
1106+
for(std::size_t y = 0; y < out_vol.shape().height_; ++y) {
1107+
const auto y_in = (static_cast<float_type>(y) + 0.5f) / scale_y - 0.5f;
1108+
for(std::size_t x = 0; x < out_vol.shape().width_; ++x) {
1109+
const auto x_in = (static_cast<float_type>(x) + 0.5f) / scale_x - 0.5f;
1110+
for(std::size_t z = 0; z < in_vol.shape().depth_; ++z) {
1111+
float_type val = interpolate_2d_value_bicubicly(in_vol, y_in, x_in, z);
1112+
out_vol.set_ignore_rank(tensor_pos(y, x, z), val);
1113+
}
1114+
}
1115+
}
1116+
return out_vol;
1117+
}
1118+
10631119
inline float_type interpolate_2d_value_area(const tensor& t,
10641120
float_type top, float_type bottom, float_type left, float_type right,
10651121
std::size_t z)

keras_export/convert_model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ def show_normalization_layer(layer):
510510

511511
def show_upsampling2d_layer(layer):
512512
"""Serialize UpSampling2D layer to dict"""
513-
assert layer.interpolation in ["nearest", "bilinear"]
513+
assert layer.interpolation in ["nearest", "bilinear", "bicubic"]
514514

515515

516516
def show_resizing_layer(layer):

keras_export/generate_test_models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ def get_test_model_exhaustive():
219219
outputs.append(UpSampling2D(size=(5, 3), interpolation='nearest')(inputs[4]))
220220
outputs.append(UpSampling2D(size=(1, 2), interpolation='bilinear')(inputs[4]))
221221
outputs.append(UpSampling2D(size=(5, 3), interpolation='bilinear')(inputs[4]))
222+
outputs.append(UpSampling2D(size=(1, 2), interpolation='bicubic')(inputs[4]))
223+
outputs.append(UpSampling2D(size=(5, 3), interpolation='bicubic')(inputs[4]))
222224

223225
outputs.append(Resizing(4, 5)(inputs[4]))
224226
outputs.append(Resizing(5, 6)(inputs[4]))

0 commit comments

Comments
 (0)