1/*
2 * Copyright (c) 2019 Guo Yejun
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include <stdio.h>
22#include <string.h>
23#include <math.h>
24#include "libavfilter/dnn/dnn_backend_native_layer_pad.h"
25
26#define EPSON 0.00001
27
28static int test_with_mode_symmetric(void)
29{
30    // the input data and expected data are generated with below python code.
31    /*
32    x = tf.placeholder(tf.float32, shape=[1, None, None, 3])
33    y = tf.pad(x, [[0, 0], [2, 3], [3, 2], [0, 0]], 'SYMMETRIC')
34    data = np.arange(48).reshape(1, 4, 4, 3);
35
36    sess=tf.Session()
37    sess.run(tf.global_variables_initializer())
38    output = sess.run(y, feed_dict={x: data})
39
40    print(list(data.flatten()))
41    print(list(output.flatten()))
42    print(data.shape)
43    print(output.shape)
44    */
45
46    LayerPadParams params;
47    DnnOperand operands[2];
48    int32_t input_indexes[1];
49    float input[1*4*4*3] = {
50        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47
51    };
52    float expected_output[1*9*9*3] = {
53        18.0, 19.0, 20.0, 15.0, 16.0, 17.0, 12.0, 13.0, 14.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 21.0, 22.0, 23.0, 18.0, 19.0, 20.0, 6.0, 7.0, 8.0, 3.0,
54        4.0, 5.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 9.0, 10.0, 11.0, 6.0, 7.0, 8.0, 6.0, 7.0, 8.0, 3.0, 4.0, 5.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 3.0,
55        4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 9.0, 10.0, 11.0, 6.0, 7.0, 8.0, 18.0, 19.0, 20.0, 15.0, 16.0, 17.0, 12.0, 13.0, 14.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0,
56        21.0, 22.0, 23.0, 21.0, 22.0, 23.0, 18.0, 19.0, 20.0, 30.0, 31.0, 32.0, 27.0, 28.0, 29.0, 24.0, 25.0, 26.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0, 35.0, 33.0,
57        34.0, 35.0, 30.0, 31.0, 32.0, 42.0, 43.0, 44.0, 39.0, 40.0, 41.0, 36.0, 37.0, 38.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 45.0, 46.0, 47.0, 42.0, 43.0,
58        44.0, 42.0, 43.0, 44.0, 39.0, 40.0, 41.0, 36.0, 37.0, 38.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 45.0, 46.0, 47.0, 42.0, 43.0, 44.0, 30.0, 31.0, 32.0,
59        27.0, 28.0, 29.0, 24.0, 25.0, 26.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0, 35.0, 33.0, 34.0, 35.0, 30.0, 31.0, 32.0, 18.0, 19.0, 20.0, 15.0, 16.0, 17.0, 12.0,
60        13.0, 14.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 21.0, 22.0, 23.0, 18.0, 19.0, 20.0
61    };
62    float *output;
63
64    params.mode = LPMP_SYMMETRIC;
65    params.paddings[0][0] = 0;
66    params.paddings[0][1] = 0;
67    params.paddings[1][0] = 2;
68    params.paddings[1][1] = 3;
69    params.paddings[2][0] = 3;
70    params.paddings[2][1] = 2;
71    params.paddings[3][0] = 0;
72    params.paddings[3][1] = 0;
73
74    operands[0].data = input;
75    operands[0].dims[0] = 1;
76    operands[0].dims[1] = 4;
77    operands[0].dims[2] = 4;
78    operands[0].dims[3] = 3;
79    operands[1].data = NULL;
80
81    input_indexes[0] = 0;
82    ff_dnn_execute_layer_pad(operands, input_indexes, 1, &params, NULL);
83
84    output = operands[1].data;
85    for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) {
86        if (fabs(output[i] - expected_output[i]) > EPSON) {
87            printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]);
88            av_freep(&output);
89            return 1;
90        }
91    }
92
93    av_freep(&output);
94    return 0;
95
96}
97
98static int test_with_mode_reflect(void)
99{
100    // the input data and expected data are generated with below python code.
101    /*
102    x = tf.placeholder(tf.float32, shape=[3, None, None, 3])
103    y = tf.pad(x, [[1, 2], [0, 0], [0, 0], [0, 0]], 'REFLECT')
104    data = np.arange(36).reshape(3, 2, 2, 3);
105
106    sess=tf.Session()
107    sess.run(tf.global_variables_initializer())
108    output = sess.run(y, feed_dict={x: data})
109
110    print(list(data.flatten()))
111    print(list(output.flatten()))
112    print(data.shape)
113    print(output.shape)
114    */
115
116    LayerPadParams params;
117    DnnOperand operands[2];
118    int32_t input_indexes[1];
119    float input[3*2*2*3] = {
120        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
121    };
122    float expected_output[6*2*2*3] = {
123        12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0,
124        12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0,
125        35.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0
126    };
127    float *output;
128
129    params.mode = LPMP_REFLECT;
130    params.paddings[0][0] = 1;
131    params.paddings[0][1] = 2;
132    params.paddings[1][0] = 0;
133    params.paddings[1][1] = 0;
134    params.paddings[2][0] = 0;
135    params.paddings[2][1] = 0;
136    params.paddings[3][0] = 0;
137    params.paddings[3][1] = 0;
138
139    operands[0].data = input;
140    operands[0].dims[0] = 3;
141    operands[0].dims[1] = 2;
142    operands[0].dims[2] = 2;
143    operands[0].dims[3] = 3;
144    operands[1].data = NULL;
145
146    input_indexes[0] = 0;
147    ff_dnn_execute_layer_pad(operands, input_indexes, 1, &params, NULL);
148
149    output = operands[1].data;
150    for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) {
151        if (fabs(output[i] - expected_output[i]) > EPSON) {
152            printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]);
153            av_freep(&output);
154            return 1;
155        }
156    }
157
158    av_freep(&output);
159    return 0;
160
161}
162
163static int test_with_mode_constant(void)
164{
165    // the input data and expected data are generated with below python code.
166    /*
167    x = tf.placeholder(tf.float32, shape=[1, None, None, 3])
168    y = tf.pad(x, [[0, 0], [1, 0], [0, 0], [1, 2]], 'CONSTANT', constant_values=728)
169    data = np.arange(12).reshape(1, 2, 2, 3);
170
171    sess=tf.Session()
172    sess.run(tf.global_variables_initializer())
173    output = sess.run(y, feed_dict={x: data})
174
175    print(list(data.flatten()))
176    print(list(output.flatten()))
177    print(data.shape)
178    print(output.shape)
179    */
180
181    LayerPadParams params;
182    DnnOperand operands[2];
183    int32_t input_indexes[1];
184    float input[1*2*2*3] = {
185        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
186    };
187    float expected_output[1*3*2*6] = {
188        728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0,
189        728.0, 728.0, 0.0, 1.0, 2.0, 728.0, 728.0, 728.0, 3.0, 4.0, 5.0, 728.0, 728.0,
190        728.0, 6.0, 7.0, 8.0, 728.0, 728.0, 728.0, 9.0, 10.0, 11.0, 728.0, 728.0
191    };
192    float *output;
193
194    params.mode = LPMP_CONSTANT;
195    params.constant_values = 728;
196    params.paddings[0][0] = 0;
197    params.paddings[0][1] = 0;
198    params.paddings[1][0] = 1;
199    params.paddings[1][1] = 0;
200    params.paddings[2][0] = 0;
201    params.paddings[2][1] = 0;
202    params.paddings[3][0] = 1;
203    params.paddings[3][1] = 2;
204
205    operands[0].data = input;
206    operands[0].dims[0] = 1;
207    operands[0].dims[1] = 2;
208    operands[0].dims[2] = 2;
209    operands[0].dims[3] = 3;
210    operands[1].data = NULL;
211
212    input_indexes[0] = 0;
213    ff_dnn_execute_layer_pad(operands, input_indexes, 1, &params, NULL);
214
215    output = operands[1].data;
216    for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) {
217        if (fabs(output[i] - expected_output[i]) > EPSON) {
218            printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]);
219            av_freep(&output);
220            return 1;
221        }
222    }
223
224    av_freep(&output);
225    return 0;
226
227}
228
229int main(int argc, char **argv)
230{
231    if (test_with_mode_symmetric())
232        return 1;
233
234    if (test_with_mode_reflect())
235        return 1;
236
237    if (test_with_mode_constant())
238        return 1;
239}
240