avfilter: add sobel and prewitt filter
[ffmpeg.git] / libavfilter / vf_convolution.c
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/opt.h"
25 #include "libavutil/pixdesc.h"
26 #include "avfilter.h"
27 #include "formats.h"
28 #include "internal.h"
29 #include "video.h"
30
31 typedef struct ConvolutionContext {
32     const AVClass *class;
33
34     char *matrix_str[4];
35     float rdiv[4];
36     float bias[4];
37     float scale;
38     float delta;
39     int planes;
40
41     int size[4];
42     int depth;
43     int bstride;
44     uint8_t *buffer;
45     int nb_planes;
46     int planewidth[4];
47     int planeheight[4];
48     int matrix[4][25];
49     int matrix_length[4];
50     int copy[4];
51
52     void (*filter[4])(struct ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane);
53 } ConvolutionContext;
54
55 #define OFFSET(x) offsetof(ConvolutionContext, x)
56 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
57
58 static const AVOption convolution_options[] = {
59     { "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 },
60     { "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 },
61     { "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 },
62     { "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 },
63     { "0rdiv", "set rdiv for 1st plane", OFFSET(rdiv[0]), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, INT_MAX, FLAGS},
64     { "1rdiv", "set rdiv for 2nd plane", OFFSET(rdiv[1]), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, INT_MAX, FLAGS},
65     { "2rdiv", "set rdiv for 3rd plane", OFFSET(rdiv[2]), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, INT_MAX, FLAGS},
66     { "3rdiv", "set rdiv for 4th plane", OFFSET(rdiv[3]), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, INT_MAX, FLAGS},
67     { "0bias", "set bias for 1st plane", OFFSET(bias[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
68     { "1bias", "set bias for 2nd plane", OFFSET(bias[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
69     { "2bias", "set bias for 3rd plane", OFFSET(bias[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
70     { "3bias", "set bias for 4th plane", OFFSET(bias[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
71     { NULL }
72 };
73
74 AVFILTER_DEFINE_CLASS(convolution);
75
76 static const int same3x3[9] = {0, 0, 0,
77                                0, 1, 0,
78                                0, 0, 0};
79
80 static const int same5x5[25] = {0, 0, 0, 0, 0,
81                                 0, 0, 0, 0, 0,
82                                 0, 0, 1, 0, 0,
83                                 0, 0, 0, 0, 0,
84                                 0, 0, 0, 0, 0};
85
86 static int query_formats(AVFilterContext *ctx)
87 {
88     static const enum AVPixelFormat pix_fmts[] = {
89         AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
90         AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
91         AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
92         AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
93         AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
94         AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
95         AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
96         AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
97         AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
98         AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
99         AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
100         AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
101         AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
102         AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
103         AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
104         AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
105         AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY16,
106         AV_PIX_FMT_NONE
107     };
108
109     return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
110 }
111
112 static inline void line_copy8(uint8_t *line, const uint8_t *srcp, int width, int mergin)
113 {
114     int i;
115
116     memcpy(line, srcp, width);
117
118     for (i = mergin; i > 0; i--) {
119         line[-i] = line[i];
120         line[width - 1 + i] = line[width - 1 - i];
121     }
122 }
123
124 static inline void line_copy16(uint16_t *line, const uint16_t *srcp, int width, int mergin)
125 {
126     int i;
127
128     memcpy(line, srcp, width * 2);
129
130     for (i = mergin; i > 0; i--) {
131         line[-i] = line[i];
132         line[width - 1 + i] = line[width - 1 - i];
133     }
134 }
135
136 static void filter16_prewitt(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
137 {
138     const uint16_t *src = (const uint16_t *)in->data[plane];
139     uint16_t *dst = (uint16_t *)out->data[plane];
140     const int peak = (1 << s->depth) - 1;
141     const int stride = in->linesize[plane] / 2;
142     const int bstride = s->bstride;
143     const int height = s->planeheight[plane];
144     const int width  = s->planewidth[plane];
145     const float scale = s->scale;
146     const float delta = s->delta;
147     uint16_t *p0 = (uint16_t *)s->buffer + 16;
148     uint16_t *p1 = p0 + bstride;
149     uint16_t *p2 = p1 + bstride;
150     uint16_t *orig = p0, *end = p2;
151     int y, x;
152
153     line_copy16(p0, src + stride, width, 1);
154     line_copy16(p1, src, width, 1);
155
156     for (y = 0; y < height; y++) {
157         src += stride * (y < height - 1 ? 1 : -1);
158         line_copy16(p2, src, width, 1);
159
160         for (x = 0; x < width; x++) {
161             int suma = p0[x - 1] * -1 +
162                        p0[x] *     -1 +
163                        p0[x + 1] * -1 +
164                        p2[x - 1] *  1 +
165                        p2[x] *      1 +
166                        p2[x + 1] *  1;
167             int sumb = p0[x - 1] * -1 +
168                        p0[x + 1] *  1 +
169                        p1[x - 1] * -1 +
170                        p1[x + 1] *  1 +
171                        p2[x - 1] * -1 +
172                        p2[x + 1] *  1;
173
174             dst[x] = av_clip(sqrt(suma*suma + sumb*sumb) * scale + delta, 0, peak);
175         }
176
177         p0 = p1;
178         p1 = p2;
179         p2 = (p2 == end) ? orig: p2 + bstride;
180         dst += out->linesize[plane] / 2;
181     }
182 }
183
184 static void filter16_sobel(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
185 {
186     const uint16_t *src = (const uint16_t *)in->data[plane];
187     uint16_t *dst = (uint16_t *)out->data[plane];
188     const int peak = (1 << s->depth) - 1;
189     const int stride = in->linesize[plane] / 2;
190     const int bstride = s->bstride;
191     const int height = s->planeheight[plane];
192     const int width  = s->planewidth[plane];
193     const float scale = s->scale;
194     const float delta = s->delta;
195     uint16_t *p0 = (uint16_t *)s->buffer + 16;
196     uint16_t *p1 = p0 + bstride;
197     uint16_t *p2 = p1 + bstride;
198     uint16_t *orig = p0, *end = p2;
199     int y, x;
200
201     line_copy16(p0, src + stride, width, 1);
202     line_copy16(p1, src, width, 1);
203
204     for (y = 0; y < height; y++) {
205         src += stride * (y < height - 1 ? 1 : -1);
206         line_copy16(p2, src, width, 1);
207
208         for (x = 0; x < width; x++) {
209             int suma = p0[x - 1] * -1 +
210                        p0[x] *     -2 +
211                        p0[x + 1] * -1 +
212                        p2[x - 1] *  1 +
213                        p2[x] *      2 +
214                        p2[x + 1] *  1;
215             int sumb = p0[x - 1] * -1 +
216                        p0[x + 1] *  1 +
217                        p1[x - 1] * -2 +
218                        p1[x + 1] *  2 +
219                        p2[x - 1] * -1 +
220                        p2[x + 1] *  1;
221
222             dst[x] = av_clip(sqrt(suma*suma + sumb*sumb) * scale + delta, 0, peak);
223         }
224
225         p0 = p1;
226         p1 = p2;
227         p2 = (p2 == end) ? orig: p2 + bstride;
228         dst += out->linesize[plane] / 2;
229     }
230 }
231
232 static void filter_prewitt(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
233 {
234     const uint8_t *src = in->data[plane];
235     uint8_t *dst = out->data[plane];
236     const int stride = in->linesize[plane];
237     const int bstride = s->bstride;
238     const int height = s->planeheight[plane];
239     const int width  = s->planewidth[plane];
240     const float scale = s->scale;
241     const float delta = s->delta;
242     uint8_t *p0 = s->buffer + 16;
243     uint8_t *p1 = p0 + bstride;
244     uint8_t *p2 = p1 + bstride;
245     uint8_t *orig = p0, *end = p2;
246     int y, x;
247
248     line_copy8(p0, src + stride, width, 1);
249     line_copy8(p1, src, width, 1);
250
251     for (y = 0; y < height; y++) {
252         src += stride * (y < height - 1 ? 1 : -1);
253         line_copy8(p2, src, width, 1);
254
255         for (x = 0; x < width; x++) {
256             int suma = p0[x - 1] * -1 +
257                        p0[x] *     -1 +
258                        p0[x + 1] * -1 +
259                        p2[x - 1] *  1 +
260                        p2[x] *      1 +
261                        p2[x + 1] *  1;
262             int sumb = p0[x - 1] * -1 +
263                        p0[x + 1] *  1 +
264                        p1[x - 1] * -1 +
265                        p1[x + 1] *  1 +
266                        p2[x - 1] * -1 +
267                        p2[x + 1] *  1;
268
269             dst[x] = av_clip_uint8(sqrt(suma*suma + sumb*sumb) * scale + delta);
270         }
271
272         p0 = p1;
273         p1 = p2;
274         p2 = (p2 == end) ? orig: p2 + bstride;
275         dst += out->linesize[plane];
276     }
277 }
278
279 static void filter_sobel(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
280 {
281     const uint8_t *src = in->data[plane];
282     uint8_t *dst = out->data[plane];
283     const int stride = in->linesize[plane];
284     const int bstride = s->bstride;
285     const int height = s->planeheight[plane];
286     const int width  = s->planewidth[plane];
287     const float scale = s->scale;
288     const float delta = s->delta;
289     uint8_t *p0 = s->buffer + 16;
290     uint8_t *p1 = p0 + bstride;
291     uint8_t *p2 = p1 + bstride;
292     uint8_t *orig = p0, *end = p2;
293     int y, x;
294
295     line_copy8(p0, src + stride, width, 1);
296     line_copy8(p1, src, width, 1);
297
298     for (y = 0; y < height; y++) {
299         src += stride * (y < height - 1 ? 1 : -1);
300         line_copy8(p2, src, width, 1);
301
302         for (x = 0; x < width; x++) {
303             int suma = p0[x - 1] * -1 +
304                        p0[x] *     -2 +
305                        p0[x + 1] * -1 +
306                        p2[x - 1] *  1 +
307                        p2[x] *      2 +
308                        p2[x + 1] *  1;
309             int sumb = p0[x - 1] * -1 +
310                        p0[x + 1] *  1 +
311                        p1[x - 1] * -2 +
312                        p1[x + 1] *  2 +
313                        p2[x - 1] * -1 +
314                        p2[x + 1] *  1;
315
316             dst[x] = av_clip_uint8(sqrt(suma*suma + sumb*sumb) * scale + delta);
317         }
318
319         p0 = p1;
320         p1 = p2;
321         p2 = (p2 == end) ? orig: p2 + bstride;
322         dst += out->linesize[plane];
323     }
324 }
325
326 static void filter16_3x3(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
327 {
328     const uint16_t *src = (const uint16_t *)in->data[plane];
329     uint16_t *dst = (uint16_t *)out->data[plane];
330     const int peak = (1 << s->depth) - 1;
331     const int stride = in->linesize[plane] / 2;
332     const int bstride = s->bstride;
333     const int height = s->planeheight[plane];
334     const int width  = s->planewidth[plane];
335     uint16_t *p0 = (uint16_t *)s->buffer + 16;
336     uint16_t *p1 = p0 + bstride;
337     uint16_t *p2 = p1 + bstride;
338     uint16_t *orig = p0, *end = p2;
339     const int *matrix = s->matrix[plane];
340     const float rdiv = s->rdiv[plane];
341     const float bias = s->bias[plane];
342     int y, x;
343
344     line_copy16(p0, src + stride, width, 1);
345     line_copy16(p1, src, width, 1);
346
347     for (y = 0; y < height; y++) {
348         src += stride * (y < height - 1 ? 1 : -1);
349         line_copy16(p2, src, width, 1);
350
351         for (x = 0; x < width; x++) {
352             int sum = p0[x - 1] * matrix[0] +
353                       p0[x] *     matrix[1] +
354                       p0[x + 1] * matrix[2] +
355                       p1[x - 1] * matrix[3] +
356                       p1[x] *     matrix[4] +
357                       p1[x + 1] * matrix[5] +
358                       p2[x - 1] * matrix[6] +
359                       p2[x] *     matrix[7] +
360                       p2[x + 1] * matrix[8];
361             sum = (int)(sum * rdiv + bias + 0.5f);
362             dst[x] = av_clip(sum, 0, peak);
363         }
364
365         p0 = p1;
366         p1 = p2;
367         p2 = (p2 == end) ? orig: p2 + bstride;
368         dst += out->linesize[plane] / 2;
369     }
370 }
371
372 static void filter16_5x5(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
373 {
374     const uint16_t *src = (const uint16_t *)in->data[plane];
375     uint16_t *dst = (uint16_t *)out->data[plane];
376     const int peak = (1 << s->depth) - 1;
377     const int stride = in->linesize[plane] / 2;
378     const int bstride = s->bstride;
379     const int height = s->planeheight[plane];
380     const int width  = s->planewidth[plane];
381     uint16_t *p0 = (uint16_t *)s->buffer + 16;
382     uint16_t *p1 = p0 + bstride;
383     uint16_t *p2 = p1 + bstride;
384     uint16_t *p3 = p2 + bstride;
385     uint16_t *p4 = p3 + bstride;
386     uint16_t *orig = p0, *end = p4;
387     const int *matrix = s->matrix[plane];
388     float rdiv = s->rdiv[plane];
389     float bias = s->bias[plane];
390     int y, x, i;
391
392     line_copy16(p0, src + 2 * stride, width, 2);
393     line_copy16(p1, src + stride, width, 2);
394     line_copy16(p2, src, width, 2);
395     src += stride;
396     line_copy16(p3, src, width, 2);
397
398
399     for (y = 0; y < height; y++) {
400         uint16_t *array[] = {
401             p0 - 2, p0 - 1, p0, p0 + 1, p0 + 2,
402             p1 - 2, p1 - 1, p1, p1 + 1, p1 + 2,
403             p2 - 2, p2 - 1, p2, p2 + 1, p2 + 2,
404             p3 - 2, p3 - 1, p3, p3 + 1, p3 + 2,
405             p4 - 2, p4 - 1, p4, p4 + 1, p4 + 2
406         };
407
408         src += stride * (y < height - 2 ? 1 : -1);
409         line_copy16(p4, src, width, 2);
410
411         for (x = 0; x < width; x++) {
412             int sum = 0;
413
414             for (i = 0; i < 25; i++) {
415                 sum += *(array[i] + x) * matrix[i];
416             }
417             sum = (int)(sum * rdiv + bias + 0.5f);
418             dst[x] = av_clip(sum, 0, peak);
419         }
420
421         p0 = p1;
422         p1 = p2;
423         p2 = p3;
424         p3 = p4;
425         p4 = (p4 == end) ? orig: p4 + bstride;
426         dst += out->linesize[plane] / 2;
427     }
428 }
429
430 static void filter_3x3(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
431 {
432     const uint8_t *src = in->data[plane];
433     uint8_t *dst = out->data[plane];
434     const int stride = in->linesize[plane];
435     const int bstride = s->bstride;
436     const int height = s->planeheight[plane];
437     const int width  = s->planewidth[plane];
438     uint8_t *p0 = s->buffer + 16;
439     uint8_t *p1 = p0 + bstride;
440     uint8_t *p2 = p1 + bstride;
441     uint8_t *orig = p0, *end = p2;
442     const int *matrix = s->matrix[plane];
443     const float rdiv = s->rdiv[plane];
444     const float bias = s->bias[plane];
445     int y, x;
446
447     line_copy8(p0, src + stride, width, 1);
448     line_copy8(p1, src, width, 1);
449
450     for (y = 0; y < height; y++) {
451         src += stride * (y < height - 1 ? 1 : -1);
452         line_copy8(p2, src, width, 1);
453
454         for (x = 0; x < width; x++) {
455             int sum = p0[x - 1] * matrix[0] +
456                       p0[x] *     matrix[1] +
457                       p0[x + 1] * matrix[2] +
458                       p1[x - 1] * matrix[3] +
459                       p1[x] *     matrix[4] +
460                       p1[x + 1] * matrix[5] +
461                       p2[x - 1] * matrix[6] +
462                       p2[x] *     matrix[7] +
463                       p2[x + 1] * matrix[8];
464             sum = (int)(sum * rdiv + bias + 0.5f);
465             dst[x] = av_clip_uint8(sum);
466         }
467
468         p0 = p1;
469         p1 = p2;
470         p2 = (p2 == end) ? orig: p2 + bstride;
471         dst += out->linesize[plane];
472     }
473 }
474
475 static void filter_5x5(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
476 {
477     const uint8_t *src = in->data[plane];
478     uint8_t *dst = out->data[plane];
479     const int stride = in->linesize[plane];
480     const int bstride = s->bstride;
481     const int height = s->planeheight[plane];
482     const int width  = s->planewidth[plane];
483     uint8_t *p0 = s->buffer + 16;
484     uint8_t *p1 = p0 + bstride;
485     uint8_t *p2 = p1 + bstride;
486     uint8_t *p3 = p2 + bstride;
487     uint8_t *p4 = p3 + bstride;
488     uint8_t *orig = p0, *end = p4;
489     const int *matrix = s->matrix[plane];
490     float rdiv = s->rdiv[plane];
491     float bias = s->bias[plane];
492     int y, x, i;
493
494     line_copy8(p0, src + 2 * stride, width, 2);
495     line_copy8(p1, src + stride, width, 2);
496     line_copy8(p2, src, width, 2);
497     src += stride;
498     line_copy8(p3, src, width, 2);
499
500
501     for (y = 0; y < height; y++) {
502         uint8_t *array[] = {
503             p0 - 2, p0 - 1, p0, p0 + 1, p0 + 2,
504             p1 - 2, p1 - 1, p1, p1 + 1, p1 + 2,
505             p2 - 2, p2 - 1, p2, p2 + 1, p2 + 2,
506             p3 - 2, p3 - 1, p3, p3 + 1, p3 + 2,
507             p4 - 2, p4 - 1, p4, p4 + 1, p4 + 2
508         };
509
510         src += stride * (y < height - 2 ? 1 : -1);
511         line_copy8(p4, src, width, 2);
512
513         for (x = 0; x < width; x++) {
514             int sum = 0;
515
516             for (i = 0; i < 25; i++) {
517                 sum += *(array[i] + x) * matrix[i];
518             }
519             sum = (int)(sum * rdiv + bias + 0.5f);
520             dst[x] = av_clip_uint8(sum);
521         }
522
523         p0 = p1;
524         p1 = p2;
525         p2 = p3;
526         p3 = p4;
527         p4 = (p4 == end) ? orig: p4 + bstride;
528         dst += out->linesize[plane];
529     }
530 }
531
532 static int config_input(AVFilterLink *inlink)
533 {
534     AVFilterContext *ctx = inlink->dst;
535     ConvolutionContext *s = ctx->priv;
536     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
537     int ret, p;
538
539     s->depth = desc->comp[0].depth;
540     if ((ret = av_image_fill_linesizes(s->planewidth, inlink->format, inlink->w)) < 0)
541         return ret;
542
543     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
544     s->planeheight[0] = s->planeheight[3] = inlink->h;
545
546     s->nb_planes = av_pix_fmt_count_planes(inlink->format);
547
548     s->bstride = s->planewidth[0] + 32;
549     s->buffer = av_malloc_array(5 * s->bstride, (s->depth + 7) / 8);
550     if (!s->buffer)
551         return AVERROR(ENOMEM);
552
553     if (!strcmp(ctx->filter->name, "convolution")) {
554         if (s->depth > 8) {
555             for (p = 0; p < s->nb_planes; p++) {
556                 if (s->size[p] == 3)
557                     s->filter[p] = filter16_3x3;
558                 else if (s->size[p] == 5)
559                     s->filter[p] = filter16_5x5;
560             }
561         }
562     } else if (!strcmp(ctx->filter->name, "prewitt")) {
563         if (s->depth > 8)
564             for (p = 0; p < s->nb_planes; p++)
565                 s->filter[p] = filter16_prewitt;
566     } else if (!strcmp(ctx->filter->name, "sobel")) {
567         if (s->depth > 8)
568             for (p = 0; p < s->nb_planes; p++)
569                 s->filter[p] = filter16_sobel;
570     }
571
572     return 0;
573 }
574
575 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
576 {
577     ConvolutionContext *s = inlink->dst->priv;
578     AVFilterLink *outlink = inlink->dst->outputs[0];
579     AVFrame *out;
580     int plane;
581
582     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
583     if (!out) {
584         av_frame_free(&in);
585         return AVERROR(ENOMEM);
586     }
587     av_frame_copy_props(out, in);
588
589     for (plane = 0; plane < s->nb_planes; plane++) {
590         if (s->copy[plane]) {
591             av_image_copy_plane(out->data[plane], out->linesize[plane],
592                                 in->data[plane], in->linesize[plane],
593                                 s->planewidth[plane],
594                                 s->planeheight[plane]);
595             continue;
596         }
597
598         s->filter[plane](s, in, out, plane);
599     }
600
601     av_frame_free(&in);
602     return ff_filter_frame(outlink, out);
603 }
604
605 static av_cold int init(AVFilterContext *ctx)
606 {
607     ConvolutionContext *s = ctx->priv;
608     int i;
609
610     if (!strcmp(ctx->filter->name, "convolution")) {
611         for (i = 0; i < 4; i++) {
612             int *matrix = (int *)s->matrix[i];
613             char *p, *arg, *saveptr = NULL;
614
615             p = s->matrix_str[i];
616             while (s->matrix_length[i] < 25) {
617                 if (!(arg = av_strtok(p, " ", &saveptr)))
618                     break;
619
620                 p = NULL;
621                 sscanf(arg, "%d", &matrix[s->matrix_length[i]]);
622                 s->matrix_length[i]++;
623             }
624
625             if (s->matrix_length[i] == 9) {
626                 s->size[i] = 3;
627                 if (!memcmp(matrix, same3x3, sizeof(same3x3)))
628                     s->copy[i] = 1;
629                 else
630                     s->filter[i] = filter_3x3;
631             } else if (s->matrix_length[i] == 25) {
632                 s->size[i] = 5;
633                 if (!memcmp(matrix, same5x5, sizeof(same5x5)))
634                     s->copy[i] = 1;
635                 else
636                     s->filter[i] = filter_5x5;
637             } else {
638                 return AVERROR(EINVAL);
639             }
640         }
641     } else if (!strcmp(ctx->filter->name, "prewitt")) {
642         for (i = 0; i < 4; i++) {
643             if ((1 << i) & s->planes)
644                 s->filter[i] = filter_prewitt;
645             else
646                 s->copy[i] = 1;
647         }
648     } else if (!strcmp(ctx->filter->name, "sobel")) {
649         for (i = 0; i < 4; i++) {
650             if ((1 << i) & s->planes)
651                 s->filter[i] = filter_sobel;
652             else
653                 s->copy[i] = 1;
654         }
655     }
656
657     return 0;
658 }
659
660 static av_cold void uninit(AVFilterContext *ctx)
661 {
662     ConvolutionContext *s = ctx->priv;
663
664     av_freep(&s->buffer);
665 }
666
667 static const AVFilterPad convolution_inputs[] = {
668     {
669         .name         = "default",
670         .type         = AVMEDIA_TYPE_VIDEO,
671         .config_props = config_input,
672         .filter_frame = filter_frame,
673     },
674     { NULL }
675 };
676
677 static const AVFilterPad convolution_outputs[] = {
678     {
679         .name = "default",
680         .type = AVMEDIA_TYPE_VIDEO,
681     },
682     { NULL }
683 };
684
685 #if CONFIG_CONVOLUTION_FILTER
686
687 AVFilter ff_vf_convolution = {
688     .name          = "convolution",
689     .description   = NULL_IF_CONFIG_SMALL("Apply convolution filter."),
690     .priv_size     = sizeof(ConvolutionContext),
691     .priv_class    = &convolution_class,
692     .init          = init,
693     .uninit        = uninit,
694     .query_formats = query_formats,
695     .inputs        = convolution_inputs,
696     .outputs       = convolution_outputs,
697     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
698 };
699
700 #endif /* CONFIG_CONVOLUTION_FILTER */
701
702 #if CONFIG_PREWITT_FILTER
703
704 static const AVOption prewitt_options[] = {
705     { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT,  {.i64=15}, 0, 15, FLAGS},
706     { "scale",  "set scale",            OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0,  65535, FLAGS},
707     { "delta",  "set delta",            OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
708     { NULL }
709 };
710
711 AVFILTER_DEFINE_CLASS(prewitt);
712
713 AVFilter ff_vf_prewitt = {
714     .name          = "prewitt",
715     .description   = NULL_IF_CONFIG_SMALL("Apply prewitt operator."),
716     .priv_size     = sizeof(ConvolutionContext),
717     .priv_class    = &prewitt_class,
718     .init          = init,
719     .uninit        = uninit,
720     .query_formats = query_formats,
721     .inputs        = convolution_inputs,
722     .outputs       = convolution_outputs,
723     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
724 };
725
726 #endif /* CONFIG_PREWITT_FILTER */
727
728 #if CONFIG_SOBEL_FILTER
729
730 static const AVOption sobel_options[] = {
731     { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT,  {.i64=15}, 0, 15, FLAGS},
732     { "scale",  "set scale",            OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0,  65535, FLAGS},
733     { "delta",  "set delta",            OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
734     { NULL }
735 };
736
737 AVFILTER_DEFINE_CLASS(sobel);
738
739 AVFilter ff_vf_sobel = {
740     .name          = "sobel",
741     .description   = NULL_IF_CONFIG_SMALL("Apply sobel operator."),
742     .priv_size     = sizeof(ConvolutionContext),
743     .priv_class    = &sobel_class,
744     .init          = init,
745     .uninit        = uninit,
746     .query_formats = query_formats,
747     .inputs        = convolution_inputs,
748     .outputs       = convolution_outputs,
749     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
750 };
751
752 #endif /* CONFIG_SOBEL_FILTER */