|
|
|
/*
|
|
|
|
* Copyright (c) 2020
|
|
|
|
*
|
|
|
|
* This file is part of FFmpeg.
|
|
|
|
*
|
|
|
|
* FFmpeg is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* FFmpeg is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with FFmpeg; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include "libavfilter/dnn/dnn_backend_native_layer_mathbinary.h"
|
|
|
|
#include "libavutil/avassert.h"
|
|
|
|
|
dnn-layer-mathbinary-test: Fix tests for cases with extra intermediate precision
This fixes tests on 32 bit x86 mingw with clang, which uses x87
fpu by default.
In this setup, while the get_expected function is declared to
return float, the compiler is (especially given the optimization
flags set) free to keep the intermediate values (in this case,
the return value from the inlined function) in higher precision.
This results in the situation where 7.28 (which actually, as
a float, ends up as 7.2800002098), multiplied by 100, is
728.000000 when really forced into a 32 bit float, but 728.000021
when kept with higher intermediate precision.
For the multiplication case, a more suitable epsilon would e.g.
be 2*FLT_EPSILON*fabs(expected_output), but just increase the
current hardcoded threshold for now.
Signed-off-by: Martin Storsjö <martin@martin.st>
5 years ago
|
|
|
#define EPSON 0.00005
|
|
|
|
|
|
|
|
static float get_expected(float f1, float f2, DNNMathBinaryOperation op)
|
|
|
|
{
|
|
|
|
switch (op)
|
|
|
|
{
|
|
|
|
case DMBO_SUB:
|
|
|
|
return f1 - f2;
|
|
|
|
case DMBO_ADD:
|
|
|
|
return f1 + f2;
|
|
|
|
case DMBO_MUL:
|
|
|
|
return f1 * f2;
|
|
|
|
case DMBO_REALDIV:
|
|
|
|
return f1 / f2;
|
|
|
|
case DMBO_MINIMUM:
|
|
|
|
return (f1 < f2) ? f1 : f2;
|
|
|
|
case DMBO_FLOORMOD:
|
|
|
|
return (float)((int)(f1) % (int)(f2));
|
|
|
|
default:
|
|
|
|
av_assert0(!"not supported yet");
|
|
|
|
return 0.f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int test_broadcast_input0(DNNMathBinaryOperation op)
|
|
|
|
{
|
|
|
|
DnnLayerMathBinaryParams params;
|
|
|
|
DnnOperand operands[2];
|
|
|
|
int32_t input_indexes[1];
|
|
|
|
float input[1*1*2*3] = {
|
|
|
|
-3, 2.5, 2, -2.1, 7.8, 100
|
|
|
|
};
|
|
|
|
float *output;
|
|
|
|
|
|
|
|
params.bin_op = op;
|
|
|
|
params.input0_broadcast = 1;
|
|
|
|
params.input1_broadcast = 0;
|
|
|
|
params.v = 7.28;
|
|
|
|
|
|
|
|
operands[0].data = input;
|
|
|
|
operands[0].dims[0] = 1;
|
|
|
|
operands[0].dims[1] = 1;
|
|
|
|
operands[0].dims[2] = 2;
|
|
|
|
operands[0].dims[3] = 3;
|
|
|
|
operands[1].data = NULL;
|
|
|
|
|
|
|
|
input_indexes[0] = 0;
|
|
|
|
ff_dnn_execute_layer_math_binary(operands, input_indexes, 1, ¶ms, NULL);
|
|
|
|
|
|
|
|
output = operands[1].data;
|
|
|
|
for (int i = 0; i < sizeof(input) / sizeof(float); i++) {
|
|
|
|
float expected_output = get_expected(params.v, input[i], op);
|
|
|
|
if (fabs(output[i] - expected_output) > EPSON) {
|
|
|
|
printf("op %d, at index %d, output: %f, expected_output: %f (%s:%d)\n",
|
|
|
|
op, i, output[i], expected_output, __FILE__, __LINE__);
|
|
|
|
av_freep(&output);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
av_freep(&output);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int test_broadcast_input1(DNNMathBinaryOperation op)
|
|
|
|
{
|
|
|
|
DnnLayerMathBinaryParams params;
|
|
|
|
DnnOperand operands[2];
|
|
|
|
int32_t input_indexes[1];
|
|
|
|
float input[1*1*2*3] = {
|
|
|
|
-3, 2.5, 2, -2.1, 7.8, 100
|
|
|
|
};
|
|
|
|
float *output;
|
|
|
|
|
|
|
|
params.bin_op = op;
|
|
|
|
params.input0_broadcast = 0;
|
|
|
|
params.input1_broadcast = 1;
|
|
|
|
params.v = 7.28;
|
|
|
|
|
|
|
|
operands[0].data = input;
|
|
|
|
operands[0].dims[0] = 1;
|
|
|
|
operands[0].dims[1] = 1;
|
|
|
|
operands[0].dims[2] = 2;
|
|
|
|
operands[0].dims[3] = 3;
|
|
|
|
operands[1].data = NULL;
|
|
|
|
|
|
|
|
input_indexes[0] = 0;
|
|
|
|
ff_dnn_execute_layer_math_binary(operands, input_indexes, 1, ¶ms, NULL);
|
|
|
|
|
|
|
|
output = operands[1].data;
|
|
|
|
for (int i = 0; i < sizeof(input) / sizeof(float); i++) {
|
|
|
|
float expected_output = get_expected(input[i], params.v, op);
|
|
|
|
if (fabs(output[i] - expected_output) > EPSON) {
|
|
|
|
printf("op %d, at index %d, output: %f, expected_output: %f (%s:%d)\n",
|
|
|
|
op, i, output[i], expected_output, __FILE__, __LINE__);
|
|
|
|
av_freep(&output);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
av_freep(&output);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int test_no_broadcast(DNNMathBinaryOperation op)
|
|
|
|
{
|
|
|
|
DnnLayerMathBinaryParams params;
|
|
|
|
DnnOperand operands[3];
|
|
|
|
int32_t input_indexes[2];
|
|
|
|
float input0[1*1*2*3] = {
|
|
|
|
-3, 2.5, 2, -2.1, 7.8, 100
|
|
|
|
};
|
|
|
|
float input1[1*1*2*3] = {
|
|
|
|
-1, 2, 3, -21, 8, 10.0
|
|
|
|
};
|
|
|
|
float *output;
|
|
|
|
|
|
|
|
params.bin_op = op;
|
|
|
|
params.input0_broadcast = 0;
|
|
|
|
params.input1_broadcast = 0;
|
|
|
|
|
|
|
|
operands[0].data = input0;
|
|
|
|
operands[0].dims[0] = 1;
|
|
|
|
operands[0].dims[1] = 1;
|
|
|
|
operands[0].dims[2] = 2;
|
|
|
|
operands[0].dims[3] = 3;
|
|
|
|
operands[1].data = input1;
|
|
|
|
operands[1].dims[0] = 1;
|
|
|
|
operands[1].dims[1] = 1;
|
|
|
|
operands[1].dims[2] = 2;
|
|
|
|
operands[1].dims[3] = 3;
|
|
|
|
operands[2].data = NULL;
|
|
|
|
|
|
|
|
input_indexes[0] = 0;
|
|
|
|
input_indexes[1] = 1;
|
|
|
|
ff_dnn_execute_layer_math_binary(operands, input_indexes, 2, ¶ms, NULL);
|
|
|
|
|
|
|
|
output = operands[2].data;
|
|
|
|
for (int i = 0; i < sizeof(input0) / sizeof(float); i++) {
|
|
|
|
float expected_output = get_expected(input0[i], input1[i], op);
|
|
|
|
if (fabs(output[i] - expected_output) > EPSON) {
|
|
|
|
printf("op %d, at index %d, output: %f, expected_output: %f (%s:%d)\n",
|
|
|
|
op, i, output[i], expected_output, __FILE__, __LINE__);
|
|
|
|
av_freep(&output);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
av_freep(&output);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int test(DNNMathBinaryOperation op)
|
|
|
|
{
|
|
|
|
if (test_broadcast_input0(op))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (test_broadcast_input1(op))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (test_no_broadcast(op))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
if (test(DMBO_SUB))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (test(DMBO_ADD))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (test(DMBO_MUL))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (test(DMBO_REALDIV))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (test(DMBO_MINIMUM))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (test(DMBO_FLOORMOD))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|