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 
test_with_mode_symmetric(void)28 static 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     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 
test_with_mode_reflect(void)98 static 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     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 
test_with_mode_constant(void)163 static 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     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 
main(int argc,char ** argv)229 int 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