GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavfilter/vf_convolution.c Lines: 0 346 0.0 %
Date: 2020-08-14 10:39:37 Branches: 0 202 0.0 %

Line Branch Exec Source
1
/*
2
 * Copyright (c) 2012-2013 Oka Motofumi (chikuzen.mo at gmail dot com)
3
 * Copyright (c) 2015 Paul B Mahol
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
22
#include "libavutil/avstring.h"
23
#include "libavutil/imgutils.h"
24
#include "libavutil/intreadwrite.h"
25
#include "libavutil/opt.h"
26
#include "libavutil/pixdesc.h"
27
#include "avfilter.h"
28
#include "convolution.h"
29
#include "formats.h"
30
#include "internal.h"
31
#include "video.h"
32
33
#define OFFSET(x) offsetof(ConvolutionContext, x)
34
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
35
36
static const AVOption convolution_options[] = {
37
    { "0m", "set matrix for 1st plane", OFFSET(matrix_str[0]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS },
38
    { "1m", "set matrix for 2nd plane", OFFSET(matrix_str[1]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS },
39
    { "2m", "set matrix for 3rd plane", OFFSET(matrix_str[2]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS },
40
    { "3m", "set matrix for 4th plane", OFFSET(matrix_str[3]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS },
41
    { "0rdiv", "set rdiv for 1st plane", OFFSET(rdiv[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
42
    { "1rdiv", "set rdiv for 2nd plane", OFFSET(rdiv[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
43
    { "2rdiv", "set rdiv for 3rd plane", OFFSET(rdiv[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
44
    { "3rdiv", "set rdiv for 4th plane", OFFSET(rdiv[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
45
    { "0bias", "set bias for 1st plane", OFFSET(bias[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
46
    { "1bias", "set bias for 2nd plane", OFFSET(bias[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
47
    { "2bias", "set bias for 3rd plane", OFFSET(bias[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
48
    { "3bias", "set bias for 4th plane", OFFSET(bias[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
49
    { "0mode", "set matrix mode for 1st plane", OFFSET(mode[0]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
50
    { "1mode", "set matrix mode for 2nd plane", OFFSET(mode[1]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
51
    { "2mode", "set matrix mode for 3rd plane", OFFSET(mode[2]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
52
    { "3mode", "set matrix mode for 4th plane", OFFSET(mode[3]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
53
    { "square", "square matrix",     0, AV_OPT_TYPE_CONST, {.i64=MATRIX_SQUARE}, 0, 0, FLAGS, "mode" },
54
    { "row",    "single row matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_ROW}   , 0, 0, FLAGS, "mode" },
55
    { "column", "single column matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_COLUMN}, 0, 0, FLAGS, "mode" },
56
    { NULL }
57
};
58
59
AVFILTER_DEFINE_CLASS(convolution);
60
61
static const int same3x3[9] = {0, 0, 0,
62
                               0, 1, 0,
63
                               0, 0, 0};
64
65
static const int same5x5[25] = {0, 0, 0, 0, 0,
66
                                0, 0, 0, 0, 0,
67
                                0, 0, 1, 0, 0,
68
                                0, 0, 0, 0, 0,
69
                                0, 0, 0, 0, 0};
70
71
static const int same7x7[49] = {0, 0, 0, 0, 0, 0, 0,
72
                                0, 0, 0, 0, 0, 0, 0,
73
                                0, 0, 0, 0, 0, 0, 0,
74
                                0, 0, 0, 1, 0, 0, 0,
75
                                0, 0, 0, 0, 0, 0, 0,
76
                                0, 0, 0, 0, 0, 0, 0,
77
                                0, 0, 0, 0, 0, 0, 0};
78
79
static int query_formats(AVFilterContext *ctx)
80
{
81
    static const enum AVPixelFormat pix_fmts[] = {
82
        AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
83
        AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
84
        AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
85
        AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
86
        AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
87
        AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
88
        AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
89
        AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
90
        AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
91
        AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
92
        AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
93
        AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
94
        AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12,
95
        AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
96
        AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
97
        AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
98
        AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
99
        AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
100
        AV_PIX_FMT_NONE
101
    };
102
103
    return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
104
}
105
106
typedef struct ThreadData {
107
    AVFrame *in, *out;
108
} ThreadData;
109
110
static void filter16_prewitt(uint8_t *dstp, int width,
111
                             float scale, float delta, const int *const matrix,
112
                             const uint8_t *c[], int peak, int radius,
113
                             int dstride, int stride)
114
{
115
    uint16_t *dst = (uint16_t *)dstp;
116
    int x;
117
118
    for (x = 0; x < width; x++) {
119
        float suma = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[1][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) * -1 +
120
                     AV_RN16A(&c[6][2 * x]) *  1 + AV_RN16A(&c[7][2 * x]) *  1 + AV_RN16A(&c[8][2 * x]) *  1;
121
        float sumb = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) *  1 + AV_RN16A(&c[3][2 * x]) * -1 +
122
                     AV_RN16A(&c[5][2 * x]) *  1 + AV_RN16A(&c[6][2 * x]) * -1 + AV_RN16A(&c[8][2 * x]) *  1;
123
124
        dst[x] = av_clip(sqrtf(suma*suma + sumb*sumb) * scale + delta, 0, peak);
125
    }
126
}
127
128
static void filter16_roberts(uint8_t *dstp, int width,
129
                             float scale, float delta, const int *const matrix,
130
                             const uint8_t *c[], int peak, int radius,
131
                             int dstride, int stride)
132
{
133
    uint16_t *dst = (uint16_t *)dstp;
134
    int x;
135
136
    for (x = 0; x < width; x++) {
137
        float suma = AV_RN16A(&c[0][2 * x]) *  1 + AV_RN16A(&c[1][2 * x]) * -1;
138
        float sumb = AV_RN16A(&c[4][2 * x]) *  1 + AV_RN16A(&c[3][2 * x]) * -1;
139
140
        dst[x] = av_clip(sqrtf(suma*suma + sumb*sumb) * scale + delta, 0, peak);
141
    }
142
}
143
144
static void filter16_sobel(uint8_t *dstp, int width,
145
                           float scale, float delta, const int *const matrix,
146
                           const uint8_t *c[], int peak, int radius,
147
                           int dstride, int stride)
148
{
149
    uint16_t *dst = (uint16_t *)dstp;
150
    int x;
151
152
    for (x = 0; x < width; x++) {
153
        float suma = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[1][2 * x]) * -2 + AV_RN16A(&c[2][2 * x]) * -1 +
154
                     AV_RN16A(&c[6][2 * x]) *  1 + AV_RN16A(&c[7][2 * x]) *  2 + AV_RN16A(&c[8][2 * x]) *  1;
155
        float sumb = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) *  1 + AV_RN16A(&c[3][2 * x]) * -2 +
156
                     AV_RN16A(&c[5][2 * x]) *  2 + AV_RN16A(&c[6][2 * x]) * -1 + AV_RN16A(&c[8][2 * x]) *  1;
157
158
        dst[x] = av_clip(sqrtf(suma*suma + sumb*sumb) * scale + delta, 0, peak);
159
    }
160
}
161
162
static void filter_prewitt(uint8_t *dst, int width,
163
                           float scale, float delta, const int *const matrix,
164
                           const uint8_t *c[], int peak, int radius,
165
                           int dstride, int stride)
166
{
167
    const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2];
168
    const uint8_t *c3 = c[3], *c5 = c[5];
169
    const uint8_t *c6 = c[6], *c7 = c[7], *c8 = c[8];
170
    int x;
171
172
    for (x = 0; x < width; x++) {
173
        float suma = c0[x] * -1 + c1[x] * -1 + c2[x] * -1 +
174
                     c6[x] *  1 + c7[x] *  1 + c8[x] *  1;
175
        float sumb = c0[x] * -1 + c2[x] *  1 + c3[x] * -1 +
176
                     c5[x] *  1 + c6[x] * -1 + c8[x] *  1;
177
178
        dst[x] = av_clip_uint8(sqrtf(suma*suma + sumb*sumb) * scale + delta);
179
    }
180
}
181
182
static void filter_roberts(uint8_t *dst, int width,
183
                           float scale, float delta, const int *const matrix,
184
                           const uint8_t *c[], int peak, int radius,
185
                           int dstride, int stride)
186
{
187
    int x;
188
189
    for (x = 0; x < width; x++) {
190
        float suma = c[0][x] *  1 + c[1][x] * -1;
191
        float sumb = c[4][x] *  1 + c[3][x] * -1;
192
193
        dst[x] = av_clip_uint8(sqrtf(suma*suma + sumb*sumb) * scale + delta);
194
    }
195
}
196
197
static void filter_sobel(uint8_t *dst, int width,
198
                         float scale, float delta, const int *const matrix,
199
                         const uint8_t *c[], int peak, int radius,
200
                         int dstride, int stride)
201
{
202
    const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2];
203
    const uint8_t *c3 = c[3], *c5 = c[5];
204
    const uint8_t *c6 = c[6], *c7 = c[7], *c8 = c[8];
205
    int x;
206
207
    for (x = 0; x < width; x++) {
208
        float suma = c0[x] * -1 + c1[x] * -2 + c2[x] * -1 +
209
                     c6[x] *  1 + c7[x] *  2 + c8[x] *  1;
210
        float sumb = c0[x] * -1 + c2[x] *  1 + c3[x] * -2 +
211
                     c5[x] *  2 + c6[x] * -1 + c8[x] *  1;
212
213
        dst[x] = av_clip_uint8(sqrtf(suma*suma + sumb*sumb) * scale + delta);
214
    }
215
}
216
217
static void filter16_3x3(uint8_t *dstp, int width,
218
                         float rdiv, float bias, const int *const matrix,
219
                         const uint8_t *c[], int peak, int radius,
220
                         int dstride, int stride)
221
{
222
    uint16_t *dst = (uint16_t *)dstp;
223
    int x;
224
225
    for (x = 0; x < width; x++) {
226
        int sum = AV_RN16A(&c[0][2 * x]) * matrix[0] +
227
                  AV_RN16A(&c[1][2 * x]) * matrix[1] +
228
                  AV_RN16A(&c[2][2 * x]) * matrix[2] +
229
                  AV_RN16A(&c[3][2 * x]) * matrix[3] +
230
                  AV_RN16A(&c[4][2 * x]) * matrix[4] +
231
                  AV_RN16A(&c[5][2 * x]) * matrix[5] +
232
                  AV_RN16A(&c[6][2 * x]) * matrix[6] +
233
                  AV_RN16A(&c[7][2 * x]) * matrix[7] +
234
                  AV_RN16A(&c[8][2 * x]) * matrix[8];
235
        sum = (int)(sum * rdiv + bias + 0.5f);
236
        dst[x] = av_clip(sum, 0, peak);
237
    }
238
}
239
240
static void filter16_5x5(uint8_t *dstp, int width,
241
                         float rdiv, float bias, const int *const matrix,
242
                         const uint8_t *c[], int peak, int radius,
243
                         int dstride, int stride)
244
{
245
    uint16_t *dst = (uint16_t *)dstp;
246
    int x;
247
248
    for (x = 0; x < width; x++) {
249
        int i, sum = 0;
250
251
        for (i = 0; i < 25; i++)
252
            sum += AV_RN16A(&c[i][2 * x]) * matrix[i];
253
254
        sum = (int)(sum * rdiv + bias + 0.5f);
255
        dst[x] = av_clip(sum, 0, peak);
256
    }
257
}
258
259
static void filter16_7x7(uint8_t *dstp, int width,
260
                         float rdiv, float bias, const int *const matrix,
261
                         const uint8_t *c[], int peak, int radius,
262
                         int dstride, int stride)
263
{
264
    uint16_t *dst = (uint16_t *)dstp;
265
    int x;
266
267
    for (x = 0; x < width; x++) {
268
        int i, sum = 0;
269
270
        for (i = 0; i < 49; i++)
271
            sum += AV_RN16A(&c[i][2 * x]) * matrix[i];
272
273
        sum = (int)(sum * rdiv + bias + 0.5f);
274
        dst[x] = av_clip(sum, 0, peak);
275
    }
276
}
277
278
static void filter16_row(uint8_t *dstp, int width,
279
                         float rdiv, float bias, const int *const matrix,
280
                         const uint8_t *c[], int peak, int radius,
281
                         int dstride, int stride)
282
{
283
    uint16_t *dst = (uint16_t *)dstp;
284
    int x;
285
286
    for (x = 0; x < width; x++) {
287
        int i, sum = 0;
288
289
        for (i = 0; i < 2 * radius + 1; i++)
290
            sum += AV_RN16A(&c[i][2 * x]) * matrix[i];
291
292
        sum = (int)(sum * rdiv + bias + 0.5f);
293
        dst[x] = av_clip(sum, 0, peak);
294
    }
295
}
296
297
static void filter16_column(uint8_t *dstp, int height,
298
                            float rdiv, float bias, const int *const matrix,
299
                            const uint8_t *c[], int peak, int radius,
300
                            int dstride, int stride)
301
{
302
    uint16_t *dst = (uint16_t *)dstp;
303
    int y;
304
305
    for (y = 0; y < height; y++) {
306
        int i, sum = 0;
307
308
        for (i = 0; i < 2 * radius + 1; i++)
309
            sum += AV_RN16A(&c[i][0 + y * stride]) * matrix[i];
310
311
        sum = (int)(sum * rdiv + bias + 0.5f);
312
        dst[0] = av_clip(sum, 0, peak);
313
        dst += dstride / 2;
314
    }
315
}
316
317
static void filter_7x7(uint8_t *dst, int width,
318
                       float rdiv, float bias, const int *const matrix,
319
                       const uint8_t *c[], int peak, int radius,
320
                       int dstride, int stride)
321
{
322
    int x;
323
324
    for (x = 0; x < width; x++) {
325
        int i, sum = 0;
326
327
        for (i = 0; i < 49; i++)
328
            sum += c[i][x] * matrix[i];
329
330
        sum = (int)(sum * rdiv + bias + 0.5f);
331
        dst[x] = av_clip_uint8(sum);
332
    }
333
}
334
335
static void filter_5x5(uint8_t *dst, int width,
336
                       float rdiv, float bias, const int *const matrix,
337
                       const uint8_t *c[], int peak, int radius,
338
                       int dstride, int stride)
339
{
340
    int x;
341
342
    for (x = 0; x < width; x++) {
343
        int i, sum = 0;
344
345
        for (i = 0; i < 25; i++)
346
            sum += c[i][x] * matrix[i];
347
348
        sum = (int)(sum * rdiv + bias + 0.5f);
349
        dst[x] = av_clip_uint8(sum);
350
    }
351
}
352
353
static void filter_3x3(uint8_t *dst, int width,
354
                       float rdiv, float bias, const int *const matrix,
355
                       const uint8_t *c[], int peak, int radius,
356
                       int dstride, int stride)
357
{
358
    const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2];
359
    const uint8_t *c3 = c[3], *c4 = c[4], *c5 = c[5];
360
    const uint8_t *c6 = c[6], *c7 = c[7], *c8 = c[8];
361
    int x;
362
363
    for (x = 0; x < width; x++) {
364
        int sum = c0[x] * matrix[0] + c1[x] * matrix[1] + c2[x] * matrix[2] +
365
                  c3[x] * matrix[3] + c4[x] * matrix[4] + c5[x] * matrix[5] +
366
                  c6[x] * matrix[6] + c7[x] * matrix[7] + c8[x] * matrix[8];
367
        sum = (int)(sum * rdiv + bias + 0.5f);
368
        dst[x] = av_clip_uint8(sum);
369
    }
370
}
371
372
static void filter_row(uint8_t *dst, int width,
373
                       float rdiv, float bias, const int *const matrix,
374
                       const uint8_t *c[], int peak, int radius,
375
                       int dstride, int stride)
376
{
377
    int x;
378
379
    for (x = 0; x < width; x++) {
380
        int i, sum = 0;
381
382
        for (i = 0; i < 2 * radius + 1; i++)
383
            sum += c[i][x] * matrix[i];
384
385
        sum = (int)(sum * rdiv + bias + 0.5f);
386
        dst[x] = av_clip_uint8(sum);
387
    }
388
}
389
390
static void filter_column(uint8_t *dst, int height,
391
                          float rdiv, float bias, const int *const matrix,
392
                          const uint8_t *c[], int peak, int radius,
393
                          int dstride, int stride)
394
{
395
    int y;
396
397
    for (y = 0; y < height; y++) {
398
        int i, sum = 0;
399
400
        for (i = 0; i < 2 * radius + 1; i++)
401
            sum += c[i][0 + y * stride] * matrix[i];
402
403
        sum = (int)(sum * rdiv + bias + 0.5f);
404
        dst[0] = av_clip_uint8(sum);
405
        dst += dstride;
406
    }
407
}
408
409
static void setup_3x3(int radius, const uint8_t *c[], const uint8_t *src, int stride,
410
                      int x, int w, int y, int h, int bpc)
411
{
412
    int i;
413
414
    for (i = 0; i < 9; i++) {
415
        int xoff = FFABS(x + ((i % 3) - 1));
416
        int yoff = FFABS(y + (i / 3) - 1);
417
418
        xoff = xoff >= w ? 2 * w - 1 - xoff : xoff;
419
        yoff = yoff >= h ? 2 * h - 1 - yoff : yoff;
420
421
        c[i] = src + xoff * bpc + yoff * stride;
422
    }
423
}
424
425
static void setup_5x5(int radius, const uint8_t *c[], const uint8_t *src, int stride,
426
                      int x, int w, int y, int h, int bpc)
427
{
428
    int i;
429
430
    for (i = 0; i < 25; i++) {
431
        int xoff = FFABS(x + ((i % 5) - 2));
432
        int yoff = FFABS(y + (i / 5) - 2);
433
434
        xoff = xoff >= w ? 2 * w - 1 - xoff : xoff;
435
        yoff = yoff >= h ? 2 * h - 1 - yoff : yoff;
436
437
        c[i] = src + xoff * bpc + yoff * stride;
438
    }
439
}
440
441
static void setup_7x7(int radius, const uint8_t *c[], const uint8_t *src, int stride,
442
                      int x, int w, int y, int h, int bpc)
443
{
444
    int i;
445
446
    for (i = 0; i < 49; i++) {
447
        int xoff = FFABS(x + ((i % 7) - 3));
448
        int yoff = FFABS(y + (i / 7) - 3);
449
450
        xoff = xoff >= w ? 2 * w - 1 - xoff : xoff;
451
        yoff = yoff >= h ? 2 * h - 1 - yoff : yoff;
452
453
        c[i] = src + xoff * bpc + yoff * stride;
454
    }
455
}
456
457
static void setup_row(int radius, const uint8_t *c[], const uint8_t *src, int stride,
458
                      int x, int w, int y, int h, int bpc)
459
{
460
    int i;
461
462
    for (i = 0; i < radius * 2 + 1; i++) {
463
        int xoff = FFABS(x + i - radius);
464
465
        xoff = xoff >= w ? 2 * w - 1 - xoff : xoff;
466
467
        c[i] = src + xoff * bpc + y * stride;
468
    }
469
}
470
471
static void setup_column(int radius, const uint8_t *c[], const uint8_t *src, int stride,
472
                         int x, int w, int y, int h, int bpc)
473
{
474
    int i;
475
476
    for (i = 0; i < radius * 2 + 1; i++) {
477
        int xoff = FFABS(x + i - radius);
478
479
        xoff = xoff >= h ? 2 * h - 1 - xoff : xoff;
480
481
        c[i] = src + y * bpc + xoff * stride;
482
    }
483
}
484
485
static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
486
{
487
    ConvolutionContext *s = ctx->priv;
488
    ThreadData *td = arg;
489
    AVFrame *in = td->in;
490
    AVFrame *out = td->out;
491
    int plane;
492
493
    for (plane = 0; plane < s->nb_planes; plane++) {
494
        const int mode = s->mode[plane];
495
        const int bpc = s->bpc;
496
        const int radius = s->size[plane] / 2;
497
        const int height = s->planeheight[plane];
498
        const int width  = s->planewidth[plane];
499
        const int stride = in->linesize[plane];
500
        const int dstride = out->linesize[plane];
501
        const int sizeh = mode == MATRIX_COLUMN ? width : height;
502
        const int sizew = mode == MATRIX_COLUMN ? height : width;
503
        const int slice_start = (sizeh * jobnr) / nb_jobs;
504
        const int slice_end = (sizeh * (jobnr+1)) / nb_jobs;
505
        const float rdiv = s->rdiv[plane];
506
        const float bias = s->bias[plane];
507
        const uint8_t *src = in->data[plane];
508
        const int dst_pos = slice_start * (mode == MATRIX_COLUMN ? bpc : dstride);
509
        uint8_t *dst = out->data[plane] + dst_pos;
510
        const int *matrix = s->matrix[plane];
511
        const uint8_t *c[49];
512
        int y, x;
513
514
        if (s->copy[plane]) {
515
            if (mode == MATRIX_COLUMN)
516
                av_image_copy_plane(dst, dstride, src + slice_start * bpc, stride,
517
                                    (slice_end - slice_start) * bpc, height);
518
            else
519
                av_image_copy_plane(dst, dstride, src + slice_start * stride, stride,
520
                                    width * bpc, slice_end - slice_start);
521
            continue;
522
        }
523
524
        for (y = slice_start; y < slice_end; y++) {
525
            const int xoff = mode == MATRIX_COLUMN ? (y - slice_start) * bpc : radius * bpc;
526
            const int yoff = mode == MATRIX_COLUMN ? radius * stride : 0;
527
528
            for (x = 0; x < radius; x++) {
529
                const int xoff = mode == MATRIX_COLUMN ? (y - slice_start) * bpc : x * bpc;
530
                const int yoff = mode == MATRIX_COLUMN ? x * stride : 0;
531
532
                s->setup[plane](radius, c, src, stride, x, width, y, height, bpc);
533
                s->filter[plane](dst + yoff + xoff, 1, rdiv,
534
                                 bias, matrix, c, s->max, radius,
535
                                 dstride, stride);
536
            }
537
            s->setup[plane](radius, c, src, stride, radius, width, y, height, bpc);
538
            s->filter[plane](dst + yoff + xoff, sizew - 2 * radius,
539
                             rdiv, bias, matrix, c, s->max, radius,
540
                             dstride, stride);
541
            for (x = sizew - radius; x < sizew; x++) {
542
                const int xoff = mode == MATRIX_COLUMN ? (y - slice_start) * bpc : x * bpc;
543
                const int yoff = mode == MATRIX_COLUMN ? x * stride : 0;
544
545
                s->setup[plane](radius, c, src, stride, x, width, y, height, bpc);
546
                s->filter[plane](dst + yoff + xoff, 1, rdiv,
547
                                 bias, matrix, c, s->max, radius,
548
                                 dstride, stride);
549
            }
550
            if (mode != MATRIX_COLUMN)
551
                dst += dstride;
552
        }
553
    }
554
555
    return 0;
556
}
557
558
static int config_input(AVFilterLink *inlink)
559
{
560
    AVFilterContext *ctx = inlink->dst;
561
    ConvolutionContext *s = ctx->priv;
562
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
563
    int p;
564
565
    s->depth = desc->comp[0].depth;
566
    s->max = (1 << s->depth) - 1;
567
568
    s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
569
    s->planewidth[0] = s->planewidth[3] = inlink->w;
570
    s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
571
    s->planeheight[0] = s->planeheight[3] = inlink->h;
572
573
    s->nb_planes = av_pix_fmt_count_planes(inlink->format);
574
    s->nb_threads = ff_filter_get_nb_threads(ctx);
575
    s->bpc = (s->depth + 7) / 8;
576
577
    if (!strcmp(ctx->filter->name, "convolution")) {
578
        if (s->depth > 8) {
579
            for (p = 0; p < s->nb_planes; p++) {
580
                if (s->mode[p] == MATRIX_ROW)
581
                    s->filter[p] = filter16_row;
582
                else if (s->mode[p] == MATRIX_COLUMN)
583
                    s->filter[p] = filter16_column;
584
                else if (s->size[p] == 3)
585
                    s->filter[p] = filter16_3x3;
586
                else if (s->size[p] == 5)
587
                    s->filter[p] = filter16_5x5;
588
                else if (s->size[p] == 7)
589
                    s->filter[p] = filter16_7x7;
590
            }
591
        }
592
#if CONFIG_CONVOLUTION_FILTER && ARCH_X86_64
593
        ff_convolution_init_x86(s);
594
#endif
595
    } else if (!strcmp(ctx->filter->name, "prewitt")) {
596
        if (s->depth > 8)
597
            for (p = 0; p < s->nb_planes; p++)
598
                s->filter[p] = filter16_prewitt;
599
    } else if (!strcmp(ctx->filter->name, "roberts")) {
600
        if (s->depth > 8)
601
            for (p = 0; p < s->nb_planes; p++)
602
                s->filter[p] = filter16_roberts;
603
    } else if (!strcmp(ctx->filter->name, "sobel")) {
604
        if (s->depth > 8)
605
            for (p = 0; p < s->nb_planes; p++)
606
                s->filter[p] = filter16_sobel;
607
    }
608
609
    return 0;
610
}
611
612
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
613
{
614
    AVFilterContext *ctx = inlink->dst;
615
    ConvolutionContext *s = ctx->priv;
616
    AVFilterLink *outlink = ctx->outputs[0];
617
    AVFrame *out;
618
    ThreadData td;
619
620
    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
621
    if (!out) {
622
        av_frame_free(&in);
623
        return AVERROR(ENOMEM);
624
    }
625
    av_frame_copy_props(out, in);
626
627
    td.in = in;
628
    td.out = out;
629
    ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN3(s->planeheight[1], s->planewidth[1], s->nb_threads));
630
631
    av_frame_free(&in);
632
    return ff_filter_frame(outlink, out);
633
}
634
635
static av_cold int init(AVFilterContext *ctx)
636
{
637
    ConvolutionContext *s = ctx->priv;
638
    int i;
639
640
    if (!strcmp(ctx->filter->name, "convolution")) {
641
        for (i = 0; i < 4; i++) {
642
            int *matrix = (int *)s->matrix[i];
643
            char *p, *arg, *saveptr = NULL;
644
            float sum = 0;
645
646
            p = s->matrix_str[i];
647
            while (s->matrix_length[i] < 49) {
648
                if (!(arg = av_strtok(p, " ", &saveptr)))
649
                    break;
650
651
                p = NULL;
652
                sscanf(arg, "%d", &matrix[s->matrix_length[i]]);
653
                sum += matrix[s->matrix_length[i]];
654
                s->matrix_length[i]++;
655
            }
656
657
            if (!(s->matrix_length[i] & 1)) {
658
                av_log(ctx, AV_LOG_ERROR, "number of matrix elements must be odd\n");
659
                return AVERROR(EINVAL);
660
            }
661
            if (s->mode[i] == MATRIX_ROW) {
662
                s->filter[i] = filter_row;
663
                s->setup[i] = setup_row;
664
                s->size[i] = s->matrix_length[i];
665
            } else if (s->mode[i] == MATRIX_COLUMN) {
666
                s->filter[i] = filter_column;
667
                s->setup[i] = setup_column;
668
                s->size[i] = s->matrix_length[i];
669
            } else if (s->matrix_length[i] == 9) {
670
                s->size[i] = 3;
671
                if (!memcmp(matrix, same3x3, sizeof(same3x3)))
672
                    s->copy[i] = 1;
673
                else
674
                    s->filter[i] = filter_3x3;
675
                s->setup[i] = setup_3x3;
676
            } else if (s->matrix_length[i] == 25) {
677
                s->size[i] = 5;
678
                if (!memcmp(matrix, same5x5, sizeof(same5x5)))
679
                    s->copy[i] = 1;
680
                else
681
                    s->filter[i] = filter_5x5;
682
                s->setup[i] = setup_5x5;
683
            } else if (s->matrix_length[i] == 49) {
684
                s->size[i] = 7;
685
                if (!memcmp(matrix, same7x7, sizeof(same7x7)))
686
                    s->copy[i] = 1;
687
                else
688
                    s->filter[i] = filter_7x7;
689
                s->setup[i] = setup_7x7;
690
            } else {
691
                return AVERROR(EINVAL);
692
            }
693
694
            if (sum == 0)
695
                sum = 1;
696
            if (s->rdiv[i] == 0)
697
                s->rdiv[i] = 1. / sum;
698
699
            if (s->copy[i] && (s->rdiv[i] != 1. || s->bias[i] != 0.))
700
                s->copy[i] = 0;
701
        }
702
    } else if (!strcmp(ctx->filter->name, "prewitt")) {
703
        for (i = 0; i < 4; i++) {
704
            if ((1 << i) & s->planes)
705
                s->filter[i] = filter_prewitt;
706
            else
707
                s->copy[i] = 1;
708
            s->size[i] = 3;
709
            s->setup[i] = setup_3x3;
710
            s->rdiv[i] = s->scale;
711
            s->bias[i] = s->delta;
712
        }
713
    } else if (!strcmp(ctx->filter->name, "roberts")) {
714
        for (i = 0; i < 4; i++) {
715
            if ((1 << i) & s->planes)
716
                s->filter[i] = filter_roberts;
717
            else
718
                s->copy[i] = 1;
719
            s->size[i] = 3;
720
            s->setup[i] = setup_3x3;
721
            s->rdiv[i] = s->scale;
722
            s->bias[i] = s->delta;
723
        }
724
    } else if (!strcmp(ctx->filter->name, "sobel")) {
725
        for (i = 0; i < 4; i++) {
726
            if ((1 << i) & s->planes)
727
                s->filter[i] = filter_sobel;
728
            else
729
                s->copy[i] = 1;
730
            s->size[i] = 3;
731
            s->setup[i] = setup_3x3;
732
            s->rdiv[i] = s->scale;
733
            s->bias[i] = s->delta;
734
        }
735
    }
736
737
    return 0;
738
}
739
740
static const AVFilterPad convolution_inputs[] = {
741
    {
742
        .name         = "default",
743
        .type         = AVMEDIA_TYPE_VIDEO,
744
        .config_props = config_input,
745
        .filter_frame = filter_frame,
746
    },
747
    { NULL }
748
};
749
750
static const AVFilterPad convolution_outputs[] = {
751
    {
752
        .name = "default",
753
        .type = AVMEDIA_TYPE_VIDEO,
754
    },
755
    { NULL }
756
};
757
758
#if CONFIG_CONVOLUTION_FILTER
759
760
AVFilter ff_vf_convolution = {
761
    .name          = "convolution",
762
    .description   = NULL_IF_CONFIG_SMALL("Apply convolution filter."),
763
    .priv_size     = sizeof(ConvolutionContext),
764
    .priv_class    = &convolution_class,
765
    .init          = init,
766
    .query_formats = query_formats,
767
    .inputs        = convolution_inputs,
768
    .outputs       = convolution_outputs,
769
    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
770
};
771
772
#endif /* CONFIG_CONVOLUTION_FILTER */
773
774
#if CONFIG_PREWITT_FILTER
775
776
static const AVOption prewitt_options[] = {
777
    { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT,  {.i64=15}, 0, 15, FLAGS},
778
    { "scale",  "set scale",            OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0,  65535, FLAGS},
779
    { "delta",  "set delta",            OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
780
    { NULL }
781
};
782
783
AVFILTER_DEFINE_CLASS(prewitt);
784
785
AVFilter ff_vf_prewitt = {
786
    .name          = "prewitt",
787
    .description   = NULL_IF_CONFIG_SMALL("Apply prewitt operator."),
788
    .priv_size     = sizeof(ConvolutionContext),
789
    .priv_class    = &prewitt_class,
790
    .init          = init,
791
    .query_formats = query_formats,
792
    .inputs        = convolution_inputs,
793
    .outputs       = convolution_outputs,
794
    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
795
};
796
797
#endif /* CONFIG_PREWITT_FILTER */
798
799
#if CONFIG_SOBEL_FILTER
800
801
static const AVOption sobel_options[] = {
802
    { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT,  {.i64=15}, 0, 15, FLAGS},
803
    { "scale",  "set scale",            OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0,  65535, FLAGS},
804
    { "delta",  "set delta",            OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
805
    { NULL }
806
};
807
808
AVFILTER_DEFINE_CLASS(sobel);
809
810
AVFilter ff_vf_sobel = {
811
    .name          = "sobel",
812
    .description   = NULL_IF_CONFIG_SMALL("Apply sobel operator."),
813
    .priv_size     = sizeof(ConvolutionContext),
814
    .priv_class    = &sobel_class,
815
    .init          = init,
816
    .query_formats = query_formats,
817
    .inputs        = convolution_inputs,
818
    .outputs       = convolution_outputs,
819
    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
820
};
821
822
#endif /* CONFIG_SOBEL_FILTER */
823
824
#if CONFIG_ROBERTS_FILTER
825
826
static const AVOption roberts_options[] = {
827
    { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT,  {.i64=15}, 0, 15, FLAGS},
828
    { "scale",  "set scale",            OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0,  65535, FLAGS},
829
    { "delta",  "set delta",            OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
830
    { NULL }
831
};
832
833
AVFILTER_DEFINE_CLASS(roberts);
834
835
AVFilter ff_vf_roberts = {
836
    .name          = "roberts",
837
    .description   = NULL_IF_CONFIG_SMALL("Apply roberts cross operator."),
838
    .priv_size     = sizeof(ConvolutionContext),
839
    .priv_class    = &roberts_class,
840
    .init          = init,
841
    .query_formats = query_formats,
842
    .inputs        = convolution_inputs,
843
    .outputs       = convolution_outputs,
844
    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
845
};
846
847
#endif /* CONFIG_ROBERTS_FILTER */