@@ -141,66 +141,7 @@ void ti_cpu_upsample_generic_aa(
141
141
// Helper structs to use with ti_upsample_generic_Nd_kernel_impl
142
142
template <typename index_t , typename scalar_t >
143
143
struct HelperInterpBase {
144
- static inline void init_indices_weights (
145
- std::vector<Tensor>& output,
146
- int64_t output_size,
147
- int64_t ndims,
148
- int64_t reshape_dim,
149
- int interp_size) {
150
- auto new_shape = std::vector<int64_t >(ndims, 1 );
151
- new_shape[reshape_dim] = output_size;
152
-
153
- for (int j = 0 ; j < interp_size; j++) {
154
- output.emplace_back (
155
- empty (new_shape, CPU (c10::CppTypeToScalarType<index_t >())));
156
- output.emplace_back (
157
- empty (new_shape, CPU (c10::CppTypeToScalarType<scalar_t >())));
158
- }
159
- }
160
- };
161
-
162
- template <typename index_t , typename scalar_t >
163
- struct HelperInterpLinear : public HelperInterpBase <index_t , scalar_t > {
164
- static const int interp_size = 2 ;
165
-
166
- static inline std::vector<Tensor> compute_indices_weights (
167
- int64_t input_size,
168
- int64_t output_size,
169
- int64_t stride,
170
- int64_t ndims,
171
- int64_t reshape_dim,
172
- bool align_corners,
173
- const c10::optional<double > opt_scale,
174
- bool antialias,
175
- int & out_interp_size) {
176
- scalar_t scale = area_pixel_compute_scale<scalar_t >(
177
- input_size, output_size, align_corners, opt_scale);
178
- TORCH_INTERNAL_ASSERT (antialias);
179
-
180
- return _compute_indices_weights_aa (
181
- input_size,
182
- output_size,
183
- stride,
184
- ndims,
185
- reshape_dim,
186
- align_corners,
187
- scale,
188
- out_interp_size);
189
- }
190
-
191
- // taken from
192
- // https://github.com/python-pillow/Pillow/blob/6812205f18ca4ef54372e87e1a13ce4a859434df/
193
- // src/libImaging/Resample.c#L20-L29
194
- static inline scalar_t _filter (scalar_t x) {
195
- if (x < 0.0 ) {
196
- x = -x;
197
- }
198
- if (x < 1.0 ) {
199
- return 1.0 - x;
200
- }
201
- return 0.0 ;
202
- }
203
-
144
+ template <typename filter_fn_t >
204
145
static inline std::vector<Tensor> _compute_indices_weights_aa (
205
146
int64_t input_size,
206
147
int64_t output_size,
@@ -209,14 +150,15 @@ struct HelperInterpLinear : public HelperInterpBase<index_t, scalar_t> {
209
150
int64_t reshape_dim,
210
151
bool align_corners,
211
152
scalar_t scale,
212
- int & out_interp_size) {
213
- int interp_size = HelperInterpLinear<index_t , scalar_t >::interp_size;
153
+ int & in_out_interp_size,
154
+ filter_fn_t filter_fn) {
155
+ int interp_size = in_out_interp_size;
214
156
scalar_t support =
215
- (scale >= 1.0 ) ? (interp_size / 2 ) * scale : interp_size / 2 * 1.0 ;
157
+ (scale >= 1.0 ) ? (interp_size * 0.5 ) * scale : interp_size * 0.5 ;
216
158
interp_size = (int )ceilf (support) * 2 + 1 ;
217
159
218
160
// return interp_size
219
- out_interp_size = interp_size;
161
+ in_out_interp_size = interp_size;
220
162
221
163
std::vector<Tensor> output;
222
164
auto new_shape = std::vector<int64_t >(ndims, 1 );
@@ -269,7 +211,7 @@ struct HelperInterpLinear : public HelperInterpBase<index_t, scalar_t> {
269
211
270
212
total_w = 0.0 ;
271
213
for (j = 0 ; j < xmax; j++) {
272
- scalar_t w = _filter ((j + xmin - center + 0.5 ) * invscale);
214
+ scalar_t w = filter_fn ((j + xmin - center + 0.5 ) * invscale);
273
215
wt_ptr[i * interp_size + j] = w;
274
216
total_w += w;
275
217
}
@@ -287,6 +229,102 @@ struct HelperInterpLinear : public HelperInterpBase<index_t, scalar_t> {
287
229
}
288
230
};
289
231
232
+ template <typename index_t , typename scalar_t >
233
+ struct HelperInterpLinear : public HelperInterpBase <index_t , scalar_t > {
234
+ static const int interp_size = 2 ;
235
+
236
+ // taken from
237
+ // https://github.com/python-pillow/Pillow/blob/6812205f18ca4ef54372e87e1a13ce4a859434df/
238
+ // src/libImaging/Resample.c#L20-L29
239
+ static inline scalar_t _filter (scalar_t x) {
240
+ if (x < 0.0 ) {
241
+ x = -x;
242
+ }
243
+ if (x < 1.0 ) {
244
+ return 1.0 - x;
245
+ }
246
+ return 0.0 ;
247
+ }
248
+
249
+ static inline std::vector<Tensor> compute_indices_weights (
250
+ int64_t input_size,
251
+ int64_t output_size,
252
+ int64_t stride,
253
+ int64_t ndims,
254
+ int64_t reshape_dim,
255
+ bool align_corners,
256
+ const c10::optional<double > opt_scale,
257
+ bool antialias,
258
+ int & out_interp_size) {
259
+ TORCH_INTERNAL_ASSERT (antialias);
260
+ scalar_t scale = area_pixel_compute_scale<scalar_t >(
261
+ input_size, output_size, align_corners, opt_scale);
262
+
263
+ out_interp_size = HelperInterpLinear<index_t , scalar_t >::interp_size;
264
+ return HelperInterpLinear<index_t , scalar_t >::_compute_indices_weights_aa (
265
+ input_size,
266
+ output_size,
267
+ stride,
268
+ ndims,
269
+ reshape_dim,
270
+ align_corners,
271
+ scale,
272
+ out_interp_size,
273
+ _filter);
274
+ }
275
+ };
276
+
277
+ template <typename index_t , typename scalar_t >
278
+ struct HelperInterpCubic : public HelperInterpBase <index_t , scalar_t > {
279
+ static const int interp_size = 4 ;
280
+
281
+ static inline std::vector<Tensor> compute_indices_weights (
282
+ int64_t input_size,
283
+ int64_t output_size,
284
+ int64_t stride,
285
+ int64_t ndims,
286
+ int64_t reshape_dim,
287
+ bool align_corners,
288
+ const c10::optional<double > opt_scale,
289
+ bool antialias,
290
+ int & out_interp_size) {
291
+ TORCH_INTERNAL_ASSERT (antialias);
292
+ scalar_t scale = area_pixel_compute_scale<scalar_t >(
293
+ input_size, output_size, align_corners, opt_scale);
294
+
295
+ out_interp_size = HelperInterpCubic<index_t , scalar_t >::interp_size;
296
+ return HelperInterpCubic<index_t , scalar_t >::_compute_indices_weights_aa (
297
+ input_size,
298
+ output_size,
299
+ stride,
300
+ ndims,
301
+ reshape_dim,
302
+ align_corners,
303
+ scale,
304
+ out_interp_size,
305
+ _filter);
306
+ }
307
+
308
+ // taken from
309
+ // https://github.com/python-pillow/Pillow/blob/6812205f18ca4ef54372e87e1a13ce4a859434df/
310
+ // src/libImaging/Resample.c#L46-L62
311
+ static inline scalar_t _filter (scalar_t x) {
312
+ // https://en.wikipedia.org/wiki/Bicubic_interpolation#Bicubic_convolution_algorithm
313
+ #define a -0.5
314
+ if (x < 0.0 ) {
315
+ x = -x;
316
+ }
317
+ if (x < 1.0 ) {
318
+ return ((a + 2.0 ) * x - (a + 3.0 )) * x * x + 1 ;
319
+ }
320
+ if (x < 2.0 ) {
321
+ return (((x - 5 ) * x + 8 ) * x - 4 ) * a;
322
+ }
323
+ return 0.0 ;
324
+ #undef a
325
+ }
326
+ };
327
+
290
328
template <
291
329
typename index_t ,
292
330
int out_ndims,
@@ -396,16 +434,15 @@ void ti_separable_upsample_generic_Nd_kernel_impl(
396
434
index_t ,
397
435
out_ndims,
398
436
scale_t ,
399
- HelperInterpLinear >(
437
+ F >(
400
438
temp_output, temp_input, interp_dim, align_corners, scales, antialias);
401
439
temp_input = temp_output;
402
440
}
403
441
_ti_separable_upsample_generic_Nd_kernel_impl_single_dim<
404
442
index_t ,
405
443
out_ndims,
406
444
scale_t ,
407
- HelperInterpLinear>(
408
- output, temp_input, 2 , align_corners, scales, antialias);
445
+ F>(output, temp_input, 2 , align_corners, scales, antialias);
409
446
}
410
447
411
448
void _ti_upsample_bilinear2d_kernel_impl (
@@ -423,6 +460,21 @@ void _ti_upsample_bilinear2d_kernel_impl(
423
460
output, input, align_corners, {scales_h, scales_w}, antialias);
424
461
}
425
462
463
+ void _ti_upsample_bicubic2d_kernel_impl (
464
+ Tensor& output,
465
+ const Tensor& input,
466
+ bool align_corners,
467
+ c10::optional<double > scales_h,
468
+ c10::optional<double > scales_w,
469
+ bool antialias) {
470
+ ti_separable_upsample_generic_Nd_kernel_impl<
471
+ int64_t ,
472
+ 2 ,
473
+ scale_t ,
474
+ HelperInterpCubic>(
475
+ output, input, align_corners, {scales_h, scales_w}, antialias);
476
+ }
477
+
426
478
} // namespace internal_upsample
427
479
} // namespace native
428
480
} // namespace at
@@ -463,6 +515,37 @@ at::Tensor interpolate_linear_aa_forward_kernel(
463
515
return output;
464
516
}
465
517
518
+ at::Tensor interpolate_bicubic_aa_forward_kernel (
519
+ const at::Tensor& input,
520
+ at::IntArrayRef output_size,
521
+ bool align_corners) {
522
+ TORCH_CHECK (input.device ().is_cpu (), " input must be a CPU tensor" );
523
+
524
+ c10::optional<c10::ArrayRef<double >> scale_factors = {};
525
+
526
+ // Copied from UpSampleBilinear2d.cpp
527
+ auto output = at::empty ({0 }, input.options ());
528
+ auto osize = at::native::upsample::compute_output_size (
529
+ input.sizes (), output_size, scale_factors);
530
+ auto scale_h = at::native::upsample::get_scale_value (scale_factors, 0 );
531
+ auto scale_w = at::native::upsample::get_scale_value (scale_factors, 1 );
532
+ auto full_output_size =
533
+ at::native::upsample_2d_common_check (input.sizes (), osize);
534
+
535
+ // Allow for empty batch size but not other dimensions
536
+ TORCH_CHECK (
537
+ input.numel () != 0 ||
538
+ c10::multiply_integers (
539
+ input.sizes ().begin () + 1 , input.sizes ().end ()),
540
+ " Non-empty 4D data tensor expected but got a tensor with sizes " ,
541
+ input.sizes ());
542
+
543
+ output.resize_ (full_output_size, input.suggest_memory_format ());
544
+ at::native::internal_upsample::_ti_upsample_bicubic2d_kernel_impl (
545
+ output, input, align_corners, scale_h, scale_w, /* antialias=*/ true );
546
+ return output;
547
+ }
548
+
466
549
// TODO: Implement backward function
467
550
// at::Tensor interpolate_linear_aa_backward_kernel(
468
551
// const at::Tensor& grad) {
@@ -475,6 +558,10 @@ TORCH_LIBRARY_IMPL(torchvision, CPU, m) {
475
558
m.impl (
476
559
TORCH_SELECTIVE_NAME (" torchvision::_interpolate_linear_aa" ),
477
560
TORCH_FN (interpolate_linear_aa_forward_kernel));
561
+ m.impl (
562
+ TORCH_SELECTIVE_NAME (" torchvision::_interpolate_bicubic_aa" ),
563
+ TORCH_FN (interpolate_bicubic_aa_forward_kernel));
564
+
478
565
// TODO: Implement backward function
479
566
// m.impl(
480
567
// TORCH_SELECTIVE_NAME("torchvision::_interpolate_linear_aa_backward"),
0 commit comments