2 * Copyright (c) 2012-2013 Oka Motofumi (chikuzen.mo at gmail dot com)
3 * Copyright (c) 2015 Paul B Mahol
5 * This file is part of FFmpeg.
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.
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.
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
22 #include "libavutil/avstring.h"
23 #include "libavutil/imgutils.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/pixdesc.h"
31 typedef struct ConvolutionContext {
55 int (*filter[4])(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
58 #define OFFSET(x) offsetof(ConvolutionContext, x)
59 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
61 static const AVOption convolution_options[] = {
62 { "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 },
63 { "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 },
64 { "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 },
65 { "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 },
66 { "0rdiv", "set rdiv for 1st plane", OFFSET(rdiv[0]), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, INT_MAX, FLAGS},
67 { "1rdiv", "set rdiv for 2nd plane", OFFSET(rdiv[1]), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, INT_MAX, FLAGS},
68 { "2rdiv", "set rdiv for 3rd plane", OFFSET(rdiv[2]), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, INT_MAX, FLAGS},
69 { "3rdiv", "set rdiv for 4th plane", OFFSET(rdiv[3]), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, INT_MAX, FLAGS},
70 { "0bias", "set bias for 1st plane", OFFSET(bias[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
71 { "1bias", "set bias for 2nd plane", OFFSET(bias[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
72 { "2bias", "set bias for 3rd plane", OFFSET(bias[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
73 { "3bias", "set bias for 4th plane", OFFSET(bias[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
77 AVFILTER_DEFINE_CLASS(convolution);
79 static const int same3x3[9] = {0, 0, 0,
83 static const int same5x5[25] = {0, 0, 0, 0, 0,
89 static const int same7x7[49] = {0, 0, 0, 0, 0, 0, 0,
97 static int query_formats(AVFilterContext *ctx)
99 static const enum AVPixelFormat pix_fmts[] = {
100 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
101 AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
102 AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
103 AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
104 AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
105 AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
106 AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
107 AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
108 AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
109 AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
110 AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
111 AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
112 AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
113 AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
114 AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
115 AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
116 AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY16,
120 return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
123 static inline void line_copy8(uint8_t *line, const uint8_t *srcp, int width, int mergin)
127 memcpy(line, srcp, width);
129 for (i = mergin; i > 0; i--) {
131 line[width - 1 + i] = line[width - 1 - i];
135 static inline void line_copy16(uint16_t *line, const uint16_t *srcp, int width, int mergin)
139 memcpy(line, srcp, width * 2);
141 for (i = mergin; i > 0; i--) {
143 line[width - 1 + i] = line[width - 1 - i];
147 typedef struct ThreadData {
152 static int filter16_prewitt(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
154 ConvolutionContext *s = ctx->priv;
155 ThreadData *td = arg;
156 AVFrame *in = td->in;
157 AVFrame *out = td->out;
158 const int plane = td->plane;
159 const int peak = (1 << s->depth) - 1;
160 const int stride = in->linesize[plane] / 2;
161 const int bstride = s->bstride;
162 const int height = s->planeheight[plane];
163 const int width = s->planewidth[plane];
164 const int slice_start = (height * jobnr) / nb_jobs;
165 const int slice_end = (height * (jobnr+1)) / nb_jobs;
166 const uint16_t *src = (const uint16_t *)in->data[plane] + slice_start * stride;
167 uint16_t *dst = (uint16_t *)out->data[plane] + slice_start * (out->linesize[plane] / 2);
168 const float scale = s->scale;
169 const float delta = s->delta;
170 uint16_t *p0 = (uint16_t *)s->bptrs[jobnr] + 16;
171 uint16_t *p1 = p0 + bstride;
172 uint16_t *p2 = p1 + bstride;
173 uint16_t *orig = p0, *end = p2;
176 line_copy16(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
177 line_copy16(p1, src, width, 1);
179 for (y = slice_start; y < slice_end; y++) {
180 src += stride * (y < height - 1 ? 1 : -1);
181 line_copy16(p2, src, width, 1);
183 for (x = 0; x < width; x++) {
184 int suma = p0[x - 1] * -1 +
190 int sumb = p0[x - 1] * -1 +
197 dst[x] = av_clip(sqrt(suma*suma + sumb*sumb) * scale + delta, 0, peak);
202 p2 = (p2 == end) ? orig: p2 + bstride;
203 dst += out->linesize[plane] / 2;
209 static int filter16_roberts(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
211 ConvolutionContext *s = ctx->priv;
212 ThreadData *td = arg;
213 AVFrame *in = td->in;
214 AVFrame *out = td->out;
215 const int plane = td->plane;
216 const int peak = (1 << s->depth) - 1;
217 const int stride = in->linesize[plane] / 2;
218 const int bstride = s->bstride;
219 const int height = s->planeheight[plane];
220 const int width = s->planewidth[plane];
221 const int slice_start = (height * jobnr) / nb_jobs;
222 const int slice_end = (height * (jobnr+1)) / nb_jobs;
223 const uint16_t *src = (const uint16_t *)in->data[plane] + slice_start * stride;
224 uint16_t *dst = (uint16_t *)out->data[plane] + slice_start * (out->linesize[plane] / 2);
225 const float scale = s->scale;
226 const float delta = s->delta;
227 uint16_t *p0 = (uint16_t *)s->bptrs[jobnr] + 16;
228 uint16_t *p1 = p0 + bstride;
229 uint16_t *p2 = p1 + bstride;
230 uint16_t *orig = p0, *end = p2;
233 line_copy16(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
234 line_copy16(p1, src, width, 1);
236 for (y = slice_start; y < slice_end; y++) {
237 src += stride * (y < height - 1 ? 1 : -1);
238 line_copy16(p2, src, width, 1);
240 for (x = 0; x < width; x++) {
241 int suma = p0[x - 1] * 1 +
243 int sumb = p0[x ] * 1 +
246 dst[x] = av_clip(sqrt(suma*suma + sumb*sumb) * scale + delta, 0, peak);
251 p2 = (p2 == end) ? orig: p2 + bstride;
252 dst += out->linesize[plane] / 2;
258 static int filter16_sobel(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
260 ConvolutionContext *s = ctx->priv;
261 ThreadData *td = arg;
262 AVFrame *in = td->in;
263 AVFrame *out = td->out;
264 const int plane = td->plane;
265 const int peak = (1 << s->depth) - 1;
266 const int stride = in->linesize[plane] / 2;
267 const int bstride = s->bstride;
268 const int height = s->planeheight[plane];
269 const int width = s->planewidth[plane];
270 const int slice_start = (height * jobnr) / nb_jobs;
271 const int slice_end = (height * (jobnr+1)) / nb_jobs;
272 const uint16_t *src = (const uint16_t *)in->data[plane] + slice_start * stride;
273 uint16_t *dst = (uint16_t *)out->data[plane] + slice_start * (out->linesize[plane] / 2);
274 const float scale = s->scale;
275 const float delta = s->delta;
276 uint16_t *p0 = (uint16_t *)s->bptrs[jobnr] + 16;
277 uint16_t *p1 = p0 + bstride;
278 uint16_t *p2 = p1 + bstride;
279 uint16_t *orig = p0, *end = p2;
282 line_copy16(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
283 line_copy16(p1, src, width, 1);
285 for (y = slice_start; y < slice_end; y++) {
286 src += stride * (y < height - 1 ? 1 : -1);
287 line_copy16(p2, src, width, 1);
289 for (x = 0; x < width; x++) {
290 int suma = p0[x - 1] * -1 +
296 int sumb = p0[x - 1] * -1 +
303 dst[x] = av_clip(sqrt(suma*suma + sumb*sumb) * scale + delta, 0, peak);
308 p2 = (p2 == end) ? orig: p2 + bstride;
309 dst += out->linesize[plane] / 2;
315 static int filter_prewitt(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
317 ConvolutionContext *s = ctx->priv;
318 ThreadData *td = arg;
319 AVFrame *in = td->in;
320 AVFrame *out = td->out;
321 const int plane = td->plane;
322 const int stride = in->linesize[plane];
323 const int bstride = s->bstride;
324 const int height = s->planeheight[plane];
325 const int width = s->planewidth[plane];
326 const int slice_start = (height * jobnr) / nb_jobs;
327 const int slice_end = (height * (jobnr+1)) / nb_jobs;
328 const uint8_t *src = in->data[plane] + slice_start * stride;
329 uint8_t *dst = out->data[plane] + slice_start * out->linesize[plane];
330 const float scale = s->scale;
331 const float delta = s->delta;
332 uint8_t *p0 = s->bptrs[jobnr] + 16;
333 uint8_t *p1 = p0 + bstride;
334 uint8_t *p2 = p1 + bstride;
335 uint8_t *orig = p0, *end = p2;
338 line_copy8(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
339 line_copy8(p1, src, width, 1);
341 for (y = slice_start; y < slice_end; y++) {
342 src += stride * (y < height - 1 ? 1 : -1);
343 line_copy8(p2, src, width, 1);
345 for (x = 0; x < width; x++) {
346 int suma = p0[x - 1] * -1 +
352 int sumb = p0[x - 1] * -1 +
359 dst[x] = av_clip_uint8(sqrt(suma*suma + sumb*sumb) * scale + delta);
364 p2 = (p2 == end) ? orig: p2 + bstride;
365 dst += out->linesize[plane];
371 static int filter_roberts(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
373 ConvolutionContext *s = ctx->priv;
374 ThreadData *td = arg;
375 AVFrame *in = td->in;
376 AVFrame *out = td->out;
377 const int plane = td->plane;
378 const int stride = in->linesize[plane];
379 const int bstride = s->bstride;
380 const int height = s->planeheight[plane];
381 const int width = s->planewidth[plane];
382 const int slice_start = (height * jobnr) / nb_jobs;
383 const int slice_end = (height * (jobnr+1)) / nb_jobs;
384 const uint8_t *src = in->data[plane] + slice_start * stride;
385 uint8_t *dst = out->data[plane] + slice_start * out->linesize[plane];
386 const float scale = s->scale;
387 const float delta = s->delta;
388 uint8_t *p0 = s->bptrs[jobnr] + 16;
389 uint8_t *p1 = p0 + bstride;
390 uint8_t *p2 = p1 + bstride;
391 uint8_t *orig = p0, *end = p2;
394 line_copy8(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
395 line_copy8(p1, src, width, 1);
397 for (y = slice_start; y < slice_end; y++) {
398 src += stride * (y < height - 1 ? 1 : -1);
399 line_copy8(p2, src, width, 1);
401 for (x = 0; x < width; x++) {
402 int suma = p0[x - 1] * 1 +
404 int sumb = p0[x ] * 1 +
407 dst[x] = av_clip_uint8(sqrt(suma*suma + sumb*sumb) * scale + delta);
412 p2 = (p2 == end) ? orig: p2 + bstride;
413 dst += out->linesize[plane];
419 static int filter_sobel(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
421 ConvolutionContext *s = ctx->priv;
422 ThreadData *td = arg;
423 AVFrame *in = td->in;
424 AVFrame *out = td->out;
425 const int plane = td->plane;
426 const int stride = in->linesize[plane];
427 const int bstride = s->bstride;
428 const int height = s->planeheight[plane];
429 const int width = s->planewidth[plane];
430 const int slice_start = (height * jobnr) / nb_jobs;
431 const int slice_end = (height * (jobnr+1)) / nb_jobs;
432 const uint8_t *src = in->data[plane] + slice_start * stride;
433 uint8_t *dst = out->data[plane] + slice_start * out->linesize[plane];
434 const float scale = s->scale;
435 const float delta = s->delta;
436 uint8_t *p0 = s->bptrs[jobnr] + 16;
437 uint8_t *p1 = p0 + bstride;
438 uint8_t *p2 = p1 + bstride;
439 uint8_t *orig = p0, *end = p2;
442 line_copy8(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
443 line_copy8(p1, src, width, 1);
445 for (y = slice_start; y < slice_end; y++) {
446 src += stride * (y < height - 1 ? 1 : -1);
447 line_copy8(p2, src, width, 1);
449 for (x = 0; x < width; x++) {
450 int suma = p0[x - 1] * -1 +
456 int sumb = p0[x - 1] * -1 +
463 dst[x] = av_clip_uint8(sqrt(suma*suma + sumb*sumb) * scale + delta);
468 p2 = (p2 == end) ? orig: p2 + bstride;
469 dst += out->linesize[plane];
475 static int filter16_3x3(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
477 ConvolutionContext *s = ctx->priv;
478 ThreadData *td = arg;
479 AVFrame *in = td->in;
480 AVFrame *out = td->out;
481 const int plane = td->plane;
482 const int peak = (1 << s->depth) - 1;
483 const int stride = in->linesize[plane] / 2;
484 const int bstride = s->bstride;
485 const int height = s->planeheight[plane];
486 const int width = s->planewidth[plane];
487 const int slice_start = (height * jobnr) / nb_jobs;
488 const int slice_end = (height * (jobnr+1)) / nb_jobs;
489 const uint16_t *src = (const uint16_t *)in->data[plane] + slice_start * stride;
490 uint16_t *dst = (uint16_t *)out->data[plane] + slice_start * (out->linesize[plane] / 2);
491 uint16_t *p0 = (uint16_t *)s->bptrs[jobnr] + 16;
492 uint16_t *p1 = p0 + bstride;
493 uint16_t *p2 = p1 + bstride;
494 uint16_t *orig = p0, *end = p2;
495 const int *matrix = s->matrix[plane];
496 const float rdiv = s->rdiv[plane];
497 const float bias = s->bias[plane];
500 line_copy16(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
501 line_copy16(p1, src, width, 1);
503 for (y = slice_start; y < slice_end; y++) {
504 src += stride * (y < height - 1 ? 1 : -1);
505 line_copy16(p2, src, width, 1);
507 for (x = 0; x < width; x++) {
508 int sum = p0[x - 1] * matrix[0] +
510 p0[x + 1] * matrix[2] +
511 p1[x - 1] * matrix[3] +
513 p1[x + 1] * matrix[5] +
514 p2[x - 1] * matrix[6] +
516 p2[x + 1] * matrix[8];
517 sum = (int)(sum * rdiv + bias + 0.5f);
518 dst[x] = av_clip(sum, 0, peak);
523 p2 = (p2 == end) ? orig: p2 + bstride;
524 dst += out->linesize[plane] / 2;
530 static int filter16_5x5(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
532 ConvolutionContext *s = ctx->priv;
533 ThreadData *td = arg;
534 AVFrame *in = td->in;
535 AVFrame *out = td->out;
536 const int plane = td->plane;
537 const int peak = (1 << s->depth) - 1;
538 const int stride = in->linesize[plane] / 2;
539 const int bstride = s->bstride;
540 const int height = s->planeheight[plane];
541 const int width = s->planewidth[plane];
542 const int slice_start = (height * jobnr) / nb_jobs;
543 const int slice_end = (height * (jobnr+1)) / nb_jobs;
544 const uint16_t *src = (const uint16_t *)in->data[plane] + slice_start * stride;
545 uint16_t *dst = (uint16_t *)out->data[plane] + slice_start * (out->linesize[plane] / 2);
546 uint16_t *p0 = (uint16_t *)s->bptrs[jobnr] + 16;
547 uint16_t *p1 = p0 + bstride;
548 uint16_t *p2 = p1 + bstride;
549 uint16_t *p3 = p2 + bstride;
550 uint16_t *p4 = p3 + bstride;
551 uint16_t *orig = p0, *end = p4;
552 const int *matrix = s->matrix[plane];
553 float rdiv = s->rdiv[plane];
554 float bias = s->bias[plane];
557 line_copy16(p0, src + 2 * stride * (slice_start < 2 ? 1 : -1), width, 2);
558 line_copy16(p1, src + stride * (slice_start == 0 ? 1 : -1), width, 2);
559 line_copy16(p2, src, width, 2);
561 line_copy16(p3, src, width, 2);
563 for (y = slice_start; y < slice_end; y++) {
564 uint16_t *array[] = {
565 p0 - 2, p0 - 1, p0, p0 + 1, p0 + 2,
566 p1 - 2, p1 - 1, p1, p1 + 1, p1 + 2,
567 p2 - 2, p2 - 1, p2, p2 + 1, p2 + 2,
568 p3 - 2, p3 - 1, p3, p3 + 1, p3 + 2,
569 p4 - 2, p4 - 1, p4, p4 + 1, p4 + 2
572 src += stride * (y < height - 2 ? 1 : -1);
573 line_copy16(p4, src, width, 2);
575 for (x = 0; x < width; x++) {
578 for (i = 0; i < 25; i++) {
579 sum += *(array[i] + x) * matrix[i];
581 sum = (int)(sum * rdiv + bias + 0.5f);
582 dst[x] = av_clip(sum, 0, peak);
589 p4 = (p4 == end) ? orig: p4 + bstride;
590 dst += out->linesize[plane] / 2;
596 static int filter16_7x7(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
598 ConvolutionContext *s = ctx->priv;
599 ThreadData *td = arg;
600 AVFrame *in = td->in;
601 AVFrame *out = td->out;
602 const int plane = td->plane;
603 const int peak = (1 << s->depth) - 1;
604 const int stride = in->linesize[plane] / 2;
605 const int bstride = s->bstride;
606 const int height = s->planeheight[plane];
607 const int width = s->planewidth[plane];
608 const int slice_start = (height * jobnr) / nb_jobs;
609 const int slice_end = (height * (jobnr+1)) / nb_jobs;
610 const uint16_t *src = (const uint16_t *)in->data[plane] + slice_start * stride;
611 uint16_t *dst = (uint16_t *)out->data[plane] + slice_start * (out->linesize[plane] / 2);
612 uint16_t *p0 = (uint16_t *)s->bptrs[jobnr] + 32;
613 uint16_t *p1 = p0 + bstride;
614 uint16_t *p2 = p1 + bstride;
615 uint16_t *p3 = p2 + bstride;
616 uint16_t *p4 = p3 + bstride;
617 uint16_t *p5 = p4 + bstride;
618 uint16_t *p6 = p5 + bstride;
619 uint16_t *orig = p0, *end = p6;
620 const int *matrix = s->matrix[plane];
621 float rdiv = s->rdiv[plane];
622 float bias = s->bias[plane];
625 line_copy16(p0, src + 3 * stride * (slice_start < 3 ? 1 : -1), width, 3);
626 line_copy16(p1, src + 2 * stride * (slice_start < 2 ? 1 : -1), width, 3);
627 line_copy16(p2, src + stride * (slice_start == 0 ? 1 : -1), width, 3);
628 line_copy16(p3, src, width, 3);
630 line_copy16(p4, src, width, 3);
632 line_copy16(p5, src, width, 3);
634 for (y = slice_start; y < slice_end; y++) {
635 uint16_t *array[] = {
636 p0 - 3, p0 - 2, p0 - 1, p0, p0 + 1, p0 + 2, p0 + 3,
637 p1 - 3, p1 - 2, p1 - 1, p1, p1 + 1, p1 + 2, p1 + 3,
638 p2 - 3, p2 - 2, p2 - 1, p2, p2 + 1, p2 + 2, p2 + 3,
639 p3 - 3, p3 - 2, p3 - 1, p3, p3 + 1, p3 + 2, p3 + 3,
640 p4 - 3, p4 - 2, p4 - 1, p4, p4 + 1, p4 + 2, p4 + 3,
641 p5 - 3, p5 - 2, p5 - 1, p5, p5 + 1, p5 + 2, p5 + 3,
642 p6 - 3, p6 - 2, p6 - 1, p6, p6 + 1, p6 + 2, p6 + 3,
645 src += stride * (y < height - 3 ? 1 : -1);
646 line_copy16(p6, src, width, 3);
648 for (x = 0; x < width; x++) {
651 for (i = 0; i < 25; i++) {
652 sum += *(array[i] + x) * matrix[i];
654 sum = (int)(sum * rdiv + bias + 0.5f);
655 dst[x] = av_clip(sum, 0, peak);
664 p6 = (p6 == end) ? orig: p6 + bstride;
665 dst += out->linesize[plane] / 2;
671 static int filter_3x3(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
673 ConvolutionContext *s = ctx->priv;
674 ThreadData *td = arg;
675 AVFrame *in = td->in;
676 AVFrame *out = td->out;
677 const int plane = td->plane;
678 const int stride = in->linesize[plane];
679 const int bstride = s->bstride;
680 const int height = s->planeheight[plane];
681 const int width = s->planewidth[plane];
682 const int slice_start = (height * jobnr) / nb_jobs;
683 const int slice_end = (height * (jobnr+1)) / nb_jobs;
684 const uint8_t *src = in->data[plane] + slice_start * stride;
685 uint8_t *dst = out->data[plane] + slice_start * out->linesize[plane];
686 uint8_t *p0 = s->bptrs[jobnr] + 16;
687 uint8_t *p1 = p0 + bstride;
688 uint8_t *p2 = p1 + bstride;
689 uint8_t *orig = p0, *end = p2;
690 const int *matrix = s->matrix[plane];
691 const float rdiv = s->rdiv[plane];
692 const float bias = s->bias[plane];
695 line_copy8(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
696 line_copy8(p1, src, width, 1);
698 for (y = slice_start; y < slice_end; y++) {
699 src += stride * (y < height - 1 ? 1 : -1);
700 line_copy8(p2, src, width, 1);
702 for (x = 0; x < width; x++) {
703 int sum = p0[x - 1] * matrix[0] +
705 p0[x + 1] * matrix[2] +
706 p1[x - 1] * matrix[3] +
708 p1[x + 1] * matrix[5] +
709 p2[x - 1] * matrix[6] +
711 p2[x + 1] * matrix[8];
712 sum = (int)(sum * rdiv + bias + 0.5f);
713 dst[x] = av_clip_uint8(sum);
718 p2 = (p2 == end) ? orig: p2 + bstride;
719 dst += out->linesize[plane];
725 static int filter_5x5(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
727 ConvolutionContext *s = ctx->priv;
728 ThreadData *td = arg;
729 AVFrame *in = td->in;
730 AVFrame *out = td->out;
731 const int plane = td->plane;
732 const int stride = in->linesize[plane];
733 const int bstride = s->bstride;
734 const int height = s->planeheight[plane];
735 const int width = s->planewidth[plane];
736 const int slice_start = (height * jobnr) / nb_jobs;
737 const int slice_end = (height * (jobnr+1)) / nb_jobs;
738 const uint8_t *src = in->data[plane] + slice_start * stride;
739 uint8_t *dst = out->data[plane] + slice_start * out->linesize[plane];
740 uint8_t *p0 = s->bptrs[jobnr] + 16;
741 uint8_t *p1 = p0 + bstride;
742 uint8_t *p2 = p1 + bstride;
743 uint8_t *p3 = p2 + bstride;
744 uint8_t *p4 = p3 + bstride;
745 uint8_t *orig = p0, *end = p4;
746 const int *matrix = s->matrix[plane];
747 float rdiv = s->rdiv[plane];
748 float bias = s->bias[plane];
751 line_copy8(p0, src + 2 * stride * (slice_start < 2 ? 1 : -1), width, 2);
752 line_copy8(p1, src + stride * (slice_start == 0 ? 1 : -1), width, 2);
753 line_copy8(p2, src, width, 2);
755 line_copy8(p3, src, width, 2);
758 for (y = slice_start; y < slice_end; y++) {
760 p0 - 2, p0 - 1, p0, p0 + 1, p0 + 2,
761 p1 - 2, p1 - 1, p1, p1 + 1, p1 + 2,
762 p2 - 2, p2 - 1, p2, p2 + 1, p2 + 2,
763 p3 - 2, p3 - 1, p3, p3 + 1, p3 + 2,
764 p4 - 2, p4 - 1, p4, p4 + 1, p4 + 2
767 src += stride * (y < height - 2 ? 1 : -1);
768 line_copy8(p4, src, width, 2);
770 for (x = 0; x < width; x++) {
773 for (i = 0; i < 25; i++) {
774 sum += *(array[i] + x) * matrix[i];
776 sum = (int)(sum * rdiv + bias + 0.5f);
777 dst[x] = av_clip_uint8(sum);
784 p4 = (p4 == end) ? orig: p4 + bstride;
785 dst += out->linesize[plane];
791 static int filter_7x7(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
793 ConvolutionContext *s = ctx->priv;
794 ThreadData *td = arg;
795 AVFrame *in = td->in;
796 AVFrame *out = td->out;
797 const int plane = td->plane;
798 const int stride = in->linesize[plane];
799 const int bstride = s->bstride;
800 const int height = s->planeheight[plane];
801 const int width = s->planewidth[plane];
802 const int slice_start = (height * jobnr) / nb_jobs;
803 const int slice_end = (height * (jobnr+1)) / nb_jobs;
804 const uint8_t *src = in->data[plane] + slice_start * stride;
805 uint8_t *dst = out->data[plane] + slice_start * out->linesize[plane];
806 uint8_t *p0 = s->bptrs[jobnr] + 32;
807 uint8_t *p1 = p0 + bstride;
808 uint8_t *p2 = p1 + bstride;
809 uint8_t *p3 = p2 + bstride;
810 uint8_t *p4 = p3 + bstride;
811 uint8_t *p5 = p4 + bstride;
812 uint8_t *p6 = p5 + bstride;
813 uint8_t *orig = p0, *end = p6;
814 const int *matrix = s->matrix[plane];
815 float rdiv = s->rdiv[plane];
816 float bias = s->bias[plane];
819 line_copy8(p0, src + 3 * stride * (slice_start < 3 ? 1 : -1), width, 3);
820 line_copy8(p1, src + 2 * stride * (slice_start < 2 ? 1 : -1), width, 3);
821 line_copy8(p2, src + stride * (slice_start == 0 ? 1 : -1), width, 3);
822 line_copy8(p3, src, width, 3);
824 line_copy8(p4, src, width, 3);
826 line_copy8(p5, src, width, 3);
828 for (y = slice_start; y < slice_end; y++) {
830 p0 - 3, p0 - 2, p0 - 1, p0, p0 + 1, p0 + 2, p0 + 3,
831 p1 - 3, p1 - 2, p1 - 1, p1, p1 + 1, p1 + 2, p1 + 3,
832 p2 - 3, p2 - 2, p2 - 1, p2, p2 + 1, p2 + 2, p2 + 3,
833 p3 - 3, p3 - 2, p3 - 1, p3, p3 + 1, p3 + 2, p3 + 3,
834 p4 - 3, p4 - 2, p4 - 1, p4, p4 + 1, p4 + 2, p4 + 3,
835 p5 - 3, p5 - 2, p5 - 1, p5, p5 + 1, p5 + 2, p5 + 3,
836 p6 - 3, p6 - 2, p6 - 1, p6, p6 + 1, p6 + 2, p6 + 3,
839 src += stride * (y < height - 3 ? 1 : -1);
840 line_copy8(p6, src, width, 3);
842 for (x = 0; x < width; x++) {
845 for (i = 0; i < 49; i++) {
846 sum += *(array[i] + x) * matrix[i];
848 sum = (int)(sum * rdiv + bias + 0.5f);
849 dst[x] = av_clip_uint8(sum);
858 p6 = (p6 == end) ? orig: p6 + bstride;
859 dst += out->linesize[plane];
865 static int config_input(AVFilterLink *inlink)
867 AVFilterContext *ctx = inlink->dst;
868 ConvolutionContext *s = ctx->priv;
869 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
872 s->depth = desc->comp[0].depth;
874 s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
875 s->planewidth[0] = s->planewidth[3] = inlink->w;
876 s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
877 s->planeheight[0] = s->planeheight[3] = inlink->h;
879 s->nb_planes = av_pix_fmt_count_planes(inlink->format);
880 s->nb_threads = ff_filter_get_nb_threads(ctx);
881 s->bptrs = av_calloc(s->nb_threads, sizeof(*s->bptrs));
883 return AVERROR(ENOMEM);
885 s->bstride = s->planewidth[0] + 64;
886 s->bpc = (s->depth + 7) / 8;
887 s->buffer = av_malloc_array(7 * s->bstride * s->nb_threads, s->bpc);
889 return AVERROR(ENOMEM);
891 for (p = 0; p < s->nb_threads; p++) {
892 s->bptrs[p] = s->buffer + 7 * s->bstride * s->bpc * p;
895 if (!strcmp(ctx->filter->name, "convolution")) {
897 for (p = 0; p < s->nb_planes; p++) {
899 s->filter[p] = filter16_3x3;
900 else if (s->size[p] == 5)
901 s->filter[p] = filter16_5x5;
902 else if (s->size[p] == 7)
903 s->filter[p] = filter16_7x7;
906 } else if (!strcmp(ctx->filter->name, "prewitt")) {
908 for (p = 0; p < s->nb_planes; p++)
909 s->filter[p] = filter16_prewitt;
910 } else if (!strcmp(ctx->filter->name, "roberts")) {
912 for (p = 0; p < s->nb_planes; p++)
913 s->filter[p] = filter16_roberts;
914 } else if (!strcmp(ctx->filter->name, "sobel")) {
916 for (p = 0; p < s->nb_planes; p++)
917 s->filter[p] = filter16_sobel;
923 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
925 AVFilterContext *ctx = inlink->dst;
926 ConvolutionContext *s = ctx->priv;
927 AVFilterLink *outlink = ctx->outputs[0];
931 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
934 return AVERROR(ENOMEM);
936 av_frame_copy_props(out, in);
938 for (plane = 0; plane < s->nb_planes; plane++) {
941 if (s->copy[plane]) {
942 av_image_copy_plane(out->data[plane], out->linesize[plane],
943 in->data[plane], in->linesize[plane],
944 s->planewidth[plane] * s->bpc,
945 s->planeheight[plane]);
952 ctx->internal->execute(ctx, s->filter[plane], &td, NULL, FFMIN(s->planeheight[plane], s->nb_threads));
956 return ff_filter_frame(outlink, out);
959 static av_cold int init(AVFilterContext *ctx)
961 ConvolutionContext *s = ctx->priv;
964 if (!strcmp(ctx->filter->name, "convolution")) {
965 for (i = 0; i < 4; i++) {
966 int *matrix = (int *)s->matrix[i];
967 char *p, *arg, *saveptr = NULL;
969 p = s->matrix_str[i];
970 while (s->matrix_length[i] < 49) {
971 if (!(arg = av_strtok(p, " ", &saveptr)))
975 sscanf(arg, "%d", &matrix[s->matrix_length[i]]);
976 s->matrix_length[i]++;
979 if (s->matrix_length[i] == 9) {
981 if (!memcmp(matrix, same3x3, sizeof(same3x3)))
984 s->filter[i] = filter_3x3;
985 } else if (s->matrix_length[i] == 25) {
987 if (!memcmp(matrix, same5x5, sizeof(same5x5)))
990 s->filter[i] = filter_5x5;
991 } else if (s->matrix_length[i] == 49) {
993 if (!memcmp(matrix, same7x7, sizeof(same7x7)))
996 s->filter[i] = filter_7x7;
998 return AVERROR(EINVAL);
1001 if (s->copy[i] && (s->rdiv[i] != 1. || s->bias[i] != 0.))
1004 } else if (!strcmp(ctx->filter->name, "prewitt")) {
1005 for (i = 0; i < 4; i++) {
1006 if ((1 << i) & s->planes)
1007 s->filter[i] = filter_prewitt;
1011 } else if (!strcmp(ctx->filter->name, "roberts")) {
1012 for (i = 0; i < 4; i++) {
1013 if ((1 << i) & s->planes)
1014 s->filter[i] = filter_roberts;
1018 } else if (!strcmp(ctx->filter->name, "sobel")) {
1019 for (i = 0; i < 4; i++) {
1020 if ((1 << i) & s->planes)
1021 s->filter[i] = filter_sobel;
1030 static av_cold void uninit(AVFilterContext *ctx)
1032 ConvolutionContext *s = ctx->priv;
1034 av_freep(&s->bptrs);
1035 av_freep(&s->buffer);
1038 static const AVFilterPad convolution_inputs[] = {
1041 .type = AVMEDIA_TYPE_VIDEO,
1042 .config_props = config_input,
1043 .filter_frame = filter_frame,
1048 static const AVFilterPad convolution_outputs[] = {
1051 .type = AVMEDIA_TYPE_VIDEO,
1056 #if CONFIG_CONVOLUTION_FILTER
1058 AVFilter ff_vf_convolution = {
1059 .name = "convolution",
1060 .description = NULL_IF_CONFIG_SMALL("Apply convolution filter."),
1061 .priv_size = sizeof(ConvolutionContext),
1062 .priv_class = &convolution_class,
1065 .query_formats = query_formats,
1066 .inputs = convolution_inputs,
1067 .outputs = convolution_outputs,
1068 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
1071 #endif /* CONFIG_CONVOLUTION_FILTER */
1073 #if CONFIG_PREWITT_FILTER
1075 static const AVOption prewitt_options[] = {
1076 { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15}, 0, 15, FLAGS},
1077 { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, 65535, FLAGS},
1078 { "delta", "set delta", OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
1082 AVFILTER_DEFINE_CLASS(prewitt);
1084 AVFilter ff_vf_prewitt = {
1086 .description = NULL_IF_CONFIG_SMALL("Apply prewitt operator."),
1087 .priv_size = sizeof(ConvolutionContext),
1088 .priv_class = &prewitt_class,
1091 .query_formats = query_formats,
1092 .inputs = convolution_inputs,
1093 .outputs = convolution_outputs,
1094 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
1097 #endif /* CONFIG_PREWITT_FILTER */
1099 #if CONFIG_SOBEL_FILTER
1101 static const AVOption sobel_options[] = {
1102 { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15}, 0, 15, FLAGS},
1103 { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, 65535, FLAGS},
1104 { "delta", "set delta", OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
1108 AVFILTER_DEFINE_CLASS(sobel);
1110 AVFilter ff_vf_sobel = {
1112 .description = NULL_IF_CONFIG_SMALL("Apply sobel operator."),
1113 .priv_size = sizeof(ConvolutionContext),
1114 .priv_class = &sobel_class,
1117 .query_formats = query_formats,
1118 .inputs = convolution_inputs,
1119 .outputs = convolution_outputs,
1120 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
1123 #endif /* CONFIG_SOBEL_FILTER */
1125 #if CONFIG_ROBERTS_FILTER
1127 static const AVOption roberts_options[] = {
1128 { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15}, 0, 15, FLAGS},
1129 { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, 65535, FLAGS},
1130 { "delta", "set delta", OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
1134 AVFILTER_DEFINE_CLASS(roberts);
1136 AVFilter ff_vf_roberts = {
1138 .description = NULL_IF_CONFIG_SMALL("Apply roberts cross operator."),
1139 .priv_size = sizeof(ConvolutionContext),
1140 .priv_class = &roberts_class,
1143 .query_formats = query_formats,
1144 .inputs = convolution_inputs,
1145 .outputs = convolution_outputs,
1146 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
1149 #endif /* CONFIG_ROBERTS_FILTER */