15
#include "gridsample.h"
19
GridSample::GridSample()
21
one_blob_only = false;
22
support_inplace = false;
25
int GridSample::load_param(const ParamDict& pd)
27
sample_type = pd.get(0, 1);
28
padding_mode = pd.get(1, 1);
29
align_corner = pd.get(2, 0);
30
permute_fusion = pd.get(3, 0);
32
if (sample_type < 1 || sample_type > 3)
34
NCNN_LOGE("unsupported sample type %d", sample_type);
38
if (padding_mode < 1 || padding_mode > 3)
40
NCNN_LOGE("unsupported padding mode %d", padding_mode);
56
static float grid_sample_unormalize(int w, float coordx, int align_corner)
58
return align_corner ? (coordx + 1) / 2.f * (w - 1) : ((coordx + 1) * w - 1) / 2.f;
61
static float border_coord(float x, float border)
63
return std::min(border, std::max(x, 0.0f));
66
static float reflect_coord(float x, int high)
69
x = high - fabs(x - high);
73
static float compute_coord(float sx, int w, int padding_mode, int align_corner)
75
if (padding_mode == 2)
77
sx = border_coord(sx, w - 1);
79
else if (padding_mode == 3)
83
sx = reflect_coord(sx, w - 1);
87
sx = reflect_coord(sx + 0.5, w) - 0.5;
88
sx = border_coord(sx, w - 1);
95
static bool in_bounds(const Mat& image, int x, int y)
97
return x >= 0 && y >= 0 && x < image.w && y < image.h;
100
static bool in_bounds(const Mat& image, int x, int y, int z)
102
return x >= 0 && y >= 0 && z >= 0 && x < image.w && y < image.h && z < image.c;
105
static float get_value_bounded(const Mat& image, int x, int y)
107
return in_bounds(image, x, y) ? image.row(y)[x] : 0.f;
110
static float get_value_bounded(const Mat& image, int x, int y, int z)
112
return in_bounds(image, x, y, z) ? image.depth(z).row(y)[x] : 0.f;
115
static float get_value_bounded(const Mat& image, int x, int y, int padding_mode, int align_corner)
117
x = compute_coord(x, image.w, padding_mode, align_corner);
118
y = compute_coord(y, image.h, padding_mode, align_corner);
120
return get_value_bounded(image, x, y);
123
static inline void interpolate_cubic(float fx, float* coeffs)
125
const float A = -0.75f;
132
coeffs[0] = A * fx0 * fx0 * fx0 - 5 * A * fx0 * fx0 + 8 * A * fx0 - 4 * A;
133
coeffs[1] = (A + 2) * fx1 * fx1 * fx1 - (A + 3) * fx1 * fx1 + 1;
134
coeffs[2] = (A + 2) * fx2 * fx2 * fx2 - (A + 3) * fx2 * fx2 + 1;
135
coeffs[3] = 1.f - coeffs[0] - coeffs[1] - coeffs[2];
138
int GridSample::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_blobs, const Option& opt) const
140
const Mat& bottom_blob = bottom_blobs[0];
141
const Mat& grid = bottom_blobs[1];
142
Mat& top_blob = top_blobs[0];
144
int w = bottom_blob.w;
145
int h = bottom_blob.h;
146
int d = bottom_blob.d;
147
int channels = bottom_blob.c;
148
int dims = bottom_blob.dims;
149
size_t elemsize = bottom_blob.elemsize;
153
int outw = permute_fusion == 0 ? grid.h : grid.w;
154
int outh = permute_fusion == 0 ? grid.c : grid.h;
156
top_blob.create(outw, outh, channels, elemsize, opt.blob_allocator);
159
offset_blob.create(outw, outh, grid.c, elemsize, opt.workspace_allocator);
161
if (top_blob.empty() || offset_blob.empty())
165
if (permute_fusion == 0)
167
float* offsetptr_x = offset_blob.channel(0);
168
float* offsetptr_y = offset_blob.channel(1);
170
for (int y = 0; y < outh; y++)
172
const float* gridptr = grid.channel(y);
173
for (int x = 0; x < outw; x++)
175
float sample_x = gridptr[0];
176
float sample_y = gridptr[1];
178
sample_x = grid_sample_unormalize(w, sample_x, align_corner);
179
sample_y = grid_sample_unormalize(h, sample_y, align_corner);
181
*offsetptr_x = sample_x;
182
*offsetptr_y = sample_y;
192
const float* gridptr_x = grid.channel(0);
193
const float* gridptr_y = grid.channel(1);
194
float* offsetptr_x = offset_blob.channel(0);
195
float* offsetptr_y = offset_blob.channel(1);
197
for (int y = 0; y < outh; y++)
199
for (int x = 0; x < outw; x++)
201
float sample_x = *gridptr_x;
202
float sample_y = *gridptr_y;
204
sample_x = grid_sample_unormalize(w, sample_x, align_corner);
205
sample_y = grid_sample_unormalize(h, sample_y, align_corner);
207
*offsetptr_x = sample_x;
208
*offsetptr_y = sample_y;
218
if (sample_type == Interpolation_BILINEAR)
220
#pragma omp parallel for num_threads(opt.num_threads)
221
for (int q = 0; q < channels; q++)
223
const Mat image = bottom_blob.channel(q);
224
float* outptr = top_blob.channel(q);
225
const float* offsetptr_x = offset_blob.channel(0);
226
const float* offsetptr_y = offset_blob.channel(1);
228
for (int y = 0; y < outh; y++)
230
for (int x = 0; x < outw; x++)
232
float sample_x = *offsetptr_x;
233
float sample_y = *offsetptr_y;
238
sample_x = compute_coord(sample_x, w, padding_mode, align_corner);
239
sample_y = compute_coord(sample_y, h, padding_mode, align_corner);
240
int x0 = floor(sample_x);
241
int y0 = floor(sample_y);
245
float v00 = get_value_bounded(image, x0, y0);
246
float v01 = get_value_bounded(image, x1, y0);
247
float v10 = get_value_bounded(image, x0, y1);
248
float v11 = get_value_bounded(image, x1, y1);
250
float alpha = sample_x - x0;
251
float beta = sample_y - y0;
253
float v0 = v00 * (1 - alpha) + v01 * alpha;
254
float v1 = v10 * (1 - alpha) + v11 * alpha;
256
v = v0 * (1 - beta) + v1 * beta;
268
else if (sample_type == Interpolation_NEAREST)
270
#pragma omp parallel for num_threads(opt.num_threads)
271
for (int q = 0; q < channels; q++)
273
const Mat image = bottom_blob.channel(q);
274
float* outptr = top_blob.channel(q);
275
const float* offsetptr_x = offset_blob.channel(0);
276
const float* offsetptr_y = offset_blob.channel(1);
278
for (int y = 0; y < outh; y++)
280
for (int x = 0; x < outw; x++)
282
float sample_x = *offsetptr_x;
283
float sample_y = *offsetptr_y;
284
sample_x = compute_coord(sample_x, w, padding_mode, align_corner);
285
sample_y = compute_coord(sample_y, h, padding_mode, align_corner);
287
int x0 = static_cast<int>(floor(sample_x + 0.5f));
288
int y0 = static_cast<int>(floor(sample_y + 0.5f));
290
float v = get_value_bounded(image, x0, y0);
301
else if (sample_type == Interpolation_BICUBIC)
303
#pragma omp parallel for num_threads(opt.num_threads)
304
for (int q = 0; q < channels; q++)
306
const Mat image = bottom_blob.channel(q);
307
float* outptr = top_blob.channel(q);
308
const float* offsetptr_x = offset_blob.channel(0);
309
const float* offsetptr_y = offset_blob.channel(1);
311
for (int y = 0; y < outh; y++)
313
for (int x = 0; x < outw; x++)
315
float sample_x = *offsetptr_x;
316
float sample_y = *offsetptr_y;
321
int x1 = (int)floorf(sample_x);
322
int y1 = (int)floorf(sample_y);
330
float v00 = get_value_bounded(image, x0, y0, padding_mode, align_corner);
331
float v01 = get_value_bounded(image, x1, y0, padding_mode, align_corner);
332
float v02 = get_value_bounded(image, x2, y0, padding_mode, align_corner);
333
float v03 = get_value_bounded(image, x3, y0, padding_mode, align_corner);
334
float v10 = get_value_bounded(image, x0, y1, padding_mode, align_corner);
335
float v11 = get_value_bounded(image, x1, y1, padding_mode, align_corner);
336
float v12 = get_value_bounded(image, x2, y1, padding_mode, align_corner);
337
float v13 = get_value_bounded(image, x3, y1, padding_mode, align_corner);
338
float v20 = get_value_bounded(image, x0, y2, padding_mode, align_corner);
339
float v21 = get_value_bounded(image, x1, y2, padding_mode, align_corner);
340
float v22 = get_value_bounded(image, x2, y2, padding_mode, align_corner);
341
float v23 = get_value_bounded(image, x3, y2, padding_mode, align_corner);
342
float v30 = get_value_bounded(image, x0, y3, padding_mode, align_corner);
343
float v31 = get_value_bounded(image, x1, y3, padding_mode, align_corner);
344
float v32 = get_value_bounded(image, x2, y3, padding_mode, align_corner);
345
float v33 = get_value_bounded(image, x3, y3, padding_mode, align_corner);
349
interpolate_cubic(sample_x - x1, x_coeffs);
350
interpolate_cubic(sample_y - y1, y_coeffs);
352
float v0 = v00 * x_coeffs[0] + v01 * x_coeffs[1] + v02 * x_coeffs[2] + v03 * x_coeffs[3];
353
float v1 = v10 * x_coeffs[0] + v11 * x_coeffs[1] + v12 * x_coeffs[2] + v13 * x_coeffs[3];
354
float v2 = v20 * x_coeffs[0] + v21 * x_coeffs[1] + v22 * x_coeffs[2] + v23 * x_coeffs[3];
355
float v3 = v30 * x_coeffs[0] + v31 * x_coeffs[1] + v32 * x_coeffs[2] + v33 * x_coeffs[3];
357
v = v0 * y_coeffs[0] + v1 * y_coeffs[1] + v2 * y_coeffs[2] + v3 * y_coeffs[3];
373
int outw = permute_fusion == 0 ? grid.h : grid.w;
374
int outh = permute_fusion == 0 ? grid.d : grid.h;
375
int outd = permute_fusion == 0 ? grid.c : grid.d;
377
top_blob.create(outw, outh, outd, channels, elemsize, opt.blob_allocator);
380
offset_blob.create(outw, outh, outd, grid.c, elemsize, opt.workspace_allocator);
382
if (top_blob.empty() || offset_blob.empty())
386
if (permute_fusion == 0)
388
float* offsetptr_x = offset_blob.channel(0);
389
float* offsetptr_y = offset_blob.channel(1);
390
float* offsetptr_z = offset_blob.channel(2);
392
for (int z = 0; z < outd; z++)
394
const float* gridptr = grid.channel(z);
395
for (int y = 0; y < outh; y++)
397
for (int x = 0; x < outw; x++)
399
float sample_x = gridptr[0];
400
float sample_y = gridptr[1];
401
float sample_z = gridptr[2];
403
sample_x = grid_sample_unormalize(w, sample_x, align_corner);
404
sample_x = compute_coord(sample_x, w, padding_mode, align_corner);
406
sample_y = grid_sample_unormalize(h, sample_y, align_corner);
407
sample_y = compute_coord(sample_y, h, padding_mode, align_corner);
409
sample_z = grid_sample_unormalize(d, sample_z, align_corner);
410
sample_z = compute_coord(sample_z, d, padding_mode, align_corner);
412
*offsetptr_x = sample_x;
413
*offsetptr_y = sample_y;
414
*offsetptr_z = sample_z;
426
const float* gridptr_x = grid.channel(0);
427
const float* gridptr_y = grid.channel(1);
428
const float* gridptr_z = grid.channel(2);
429
float* offsetptr_x = offset_blob.channel(0);
430
float* offsetptr_y = offset_blob.channel(1);
431
float* offsetptr_z = offset_blob.channel(2);
433
for (int z = 0; z < outd; z++)
435
for (int y = 0; y < outh; y++)
437
for (int x = 0; x < outw; x++)
439
float sample_x = *gridptr_x;
440
float sample_y = *gridptr_y;
441
float sample_z = *gridptr_z;
443
sample_x = grid_sample_unormalize(w, sample_x, align_corner);
444
sample_x = compute_coord(sample_x, w, padding_mode, align_corner);
446
sample_y = grid_sample_unormalize(h, sample_y, align_corner);
447
sample_y = compute_coord(sample_y, h, padding_mode, align_corner);
449
sample_z = grid_sample_unormalize(d, sample_z, align_corner);
450
sample_z = compute_coord(sample_z, d, padding_mode, align_corner);
452
*offsetptr_x = sample_x;
453
*offsetptr_y = sample_y;
454
*offsetptr_z = sample_z;
467
if (sample_type == Interpolation_BILINEAR)
469
#pragma omp parallel for num_threads(opt.num_threads)
470
for (int q = 0; q < channels; q++)
472
const Mat image = bottom_blob.channel(q);
473
float* outptr = top_blob.channel(q);
474
const float* offsetptr_x = offset_blob.channel(0);
475
const float* offsetptr_y = offset_blob.channel(1);
476
const float* offsetptr_z = offset_blob.channel(2);
478
for (int z = 0; z < outd; z++)
480
for (int y = 0; y < outh; y++)
482
for (int x = 0; x < outw; x++)
484
float sample_x = *offsetptr_x;
485
float sample_y = *offsetptr_y;
486
float sample_z = *offsetptr_z;
491
int x0 = (int)floor(sample_x);
492
int y0 = (int)floor(sample_y);
493
int z0 = (int)floor(sample_z);
498
float v000 = get_value_bounded(image, x0, y0, z0);
499
float v001 = get_value_bounded(image, x1, y0, z0);
500
float v010 = get_value_bounded(image, x0, y1, z0);
501
float v011 = get_value_bounded(image, x1, y1, z0);
502
float v100 = get_value_bounded(image, x0, y0, z1);
503
float v101 = get_value_bounded(image, x1, y0, z1);
504
float v110 = get_value_bounded(image, x0, y1, z1);
505
float v111 = get_value_bounded(image, x1, y1, z1);
507
float alpha = sample_x - x0;
508
float beta = sample_y - y0;
509
float gamma = sample_z - z0;
511
float v00 = v000 * (1 - alpha) + v001 * alpha;
512
float v01 = v010 * (1 - alpha) + v011 * alpha;
513
float v10 = v100 * (1 - alpha) + v101 * alpha;
514
float v11 = v110 * (1 - alpha) + v111 * alpha;
516
float v0 = v00 * (1 - beta) + v01 * beta;
517
float v1 = v10 * (1 - beta) + v11 * beta;
519
v = v0 * (1 - gamma) + v1 * gamma;
533
else if (sample_type == Interpolation_NEAREST)
535
#pragma omp parallel for num_threads(opt.num_threads)
536
for (int q = 0; q < channels; q++)
538
const Mat image = bottom_blob.channel(q);
539
float* outptr = top_blob.channel(q);
540
const float* offsetptr_x = offset_blob.channel(0);
541
const float* offsetptr_y = offset_blob.channel(1);
542
const float* offsetptr_z = offset_blob.channel(2);
544
for (int z = 0; z < outd; z++)
546
for (int y = 0; y < outh; y++)
548
for (int x = 0; x < outw; x++)
550
float sample_x = *offsetptr_x;
551
float sample_y = *offsetptr_y;
552
float sample_z = *offsetptr_z;
554
int x0 = static_cast<int>(floor(sample_x + 0.5f));
555
int y0 = static_cast<int>(floor(sample_y + 0.5f));
556
int z0 = static_cast<int>(floor(sample_z + 0.5f));
558
float v = get_value_bounded(image, x0, y0, z0);
571
else if (sample_type == 3)
573
NCNN_LOGE("unsupported bicubic when dims == 4");