Browse code

avfilter: add sobel and prewitt filter

Signed-off-by: Paul B Mahol <onemda@gmail.com>

Paul B Mahol authored on 2016/09/11 06:34:30
Showing 6 changed files
... ...
@@ -27,6 +27,7 @@ version <next>:
27 27
 - weave filter
28 28
 - gblur filter
29 29
 - avgblur filter
30
+- sobel and prewitt filter
30 31
 
31 32
 
32 33
 version 3.1:
... ...
@@ -10860,6 +10860,23 @@ Set medium thresholding (good results, default).
10860 10860
 @end table
10861 10861
 @end table
10862 10862
 
10863
+@section prewitt
10864
+Apply prewitt operator to input video stream.
10865
+
10866
+The filter accepts the following option:
10867
+
10868
+@table @option
10869
+@item planes
10870
+Set which planes will be processed, unprocessed planes will be copied.
10871
+By default value 0xf, all planes will be processed.
10872
+
10873
+@item scale
10874
+Set value which will be multiplied with filtered result.
10875
+
10876
+@item delta
10877
+Set value which will be added to filtered result.
10878
+@end table
10879
+
10863 10880
 @section psnr
10864 10881
 
10865 10882
 Obtain the average, maximum and minimum PSNR (Peak Signal to Noise
... ...
@@ -12714,6 +12731,23 @@ asendcmd='5.0 astreamselect map 1',astreamselect=inputs=2:map=0
12714 12714
 @end example
12715 12715
 @end itemize
12716 12716
 
12717
+@section sobel
12718
+Apply sobel operator to input video stream.
12719
+
12720
+The filter accepts the following option:
12721
+
12722
+@table @option
12723
+@item planes
12724
+Set which planes will be processed, unprocessed planes will be copied.
12725
+By default value 0xf, all planes will be processed.
12726
+
12727
+@item scale
12728
+Set value which will be multiplied with filtered result.
12729
+
12730
+@item delta
12731
+Set value which will be added to filtered result.
12732
+@end table
12733
+
12717 12734
 @anchor{spp}
12718 12735
 @section spp
12719 12736
 
... ...
@@ -238,6 +238,7 @@ OBJS-$(CONFIG_PHASE_FILTER)                  += vf_phase.o
238 238
 OBJS-$(CONFIG_PIXDESCTEST_FILTER)            += vf_pixdesctest.o
239 239
 OBJS-$(CONFIG_PP_FILTER)                     += vf_pp.o
240 240
 OBJS-$(CONFIG_PP7_FILTER)                    += vf_pp7.o
241
+OBJS-$(CONFIG_PREWITT_FILTER)                += vf_convolution.o
241 242
 OBJS-$(CONFIG_PSNR_FILTER)                   += vf_psnr.o dualinput.o framesync.o
242 243
 OBJS-$(CONFIG_PULLUP_FILTER)                 += vf_pullup.o
243 244
 OBJS-$(CONFIG_QP_FILTER)                     += vf_qp.o
... ...
@@ -270,6 +271,7 @@ OBJS-$(CONFIG_SHUFFLEFRAMES_FILTER)          += vf_shuffleframes.o
270 270
 OBJS-$(CONFIG_SHUFFLEPLANES_FILTER)          += vf_shuffleplanes.o
271 271
 OBJS-$(CONFIG_SIGNALSTATS_FILTER)            += vf_signalstats.o
272 272
 OBJS-$(CONFIG_SMARTBLUR_FILTER)              += vf_smartblur.o
273
+OBJS-$(CONFIG_SOBEL_FILTER)                  += vf_convolution.o
273 274
 OBJS-$(CONFIG_SPLIT_FILTER)                  += split.o
274 275
 OBJS-$(CONFIG_SPP_FILTER)                    += vf_spp.o
275 276
 OBJS-$(CONFIG_SSIM_FILTER)                   += vf_ssim.o dualinput.o framesync.o
... ...
@@ -254,6 +254,7 @@ void avfilter_register_all(void)
254 254
     REGISTER_FILTER(PIXDESCTEST,    pixdesctest,    vf);
255 255
     REGISTER_FILTER(PP,             pp,             vf);
256 256
     REGISTER_FILTER(PP7,            pp7,            vf);
257
+    REGISTER_FILTER(PREWITT,        prewitt,        vf);
257 258
     REGISTER_FILTER(PSNR,           psnr,           vf);
258 259
     REGISTER_FILTER(PULLUP,         pullup,         vf);
259 260
     REGISTER_FILTER(QP,             qp,             vf);
... ...
@@ -286,6 +287,7 @@ void avfilter_register_all(void)
286 286
     REGISTER_FILTER(SHUFFLEPLANES,  shuffleplanes,  vf);
287 287
     REGISTER_FILTER(SIGNALSTATS,    signalstats,    vf);
288 288
     REGISTER_FILTER(SMARTBLUR,      smartblur,      vf);
289
+    REGISTER_FILTER(SOBEL,          sobel,          vf);
289 290
     REGISTER_FILTER(SPLIT,          split,          vf);
290 291
     REGISTER_FILTER(SPP,            spp,            vf);
291 292
     REGISTER_FILTER(SSIM,           ssim,           vf);
... ...
@@ -30,7 +30,7 @@
30 30
 #include "libavutil/version.h"
31 31
 
32 32
 #define LIBAVFILTER_VERSION_MAJOR   6
33
-#define LIBAVFILTER_VERSION_MINOR  61
33
+#define LIBAVFILTER_VERSION_MINOR  62
34 34
 #define LIBAVFILTER_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
... ...
@@ -34,6 +34,9 @@ typedef struct ConvolutionContext {
34 34
     char *matrix_str[4];
35 35
     float rdiv[4];
36 36
     float bias[4];
37
+    float scale;
38
+    float delta;
39
+    int planes;
37 40
 
38 41
     int size[4];
39 42
     int depth;
... ...
@@ -130,6 +133,196 @@ static inline void line_copy16(uint16_t *line, const uint16_t *srcp, int width,
130 130
     }
131 131
 }
132 132
 
133
+static void filter16_prewitt(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
134
+{
135
+    const uint16_t *src = (const uint16_t *)in->data[plane];
136
+    uint16_t *dst = (uint16_t *)out->data[plane];
137
+    const int peak = (1 << s->depth) - 1;
138
+    const int stride = in->linesize[plane] / 2;
139
+    const int bstride = s->bstride;
140
+    const int height = s->planeheight[plane];
141
+    const int width  = s->planewidth[plane];
142
+    const float scale = s->scale;
143
+    const float delta = s->delta;
144
+    uint16_t *p0 = (uint16_t *)s->buffer + 16;
145
+    uint16_t *p1 = p0 + bstride;
146
+    uint16_t *p2 = p1 + bstride;
147
+    uint16_t *orig = p0, *end = p2;
148
+    int y, x;
149
+
150
+    line_copy16(p0, src + stride, width, 1);
151
+    line_copy16(p1, src, width, 1);
152
+
153
+    for (y = 0; y < height; y++) {
154
+        src += stride * (y < height - 1 ? 1 : -1);
155
+        line_copy16(p2, src, width, 1);
156
+
157
+        for (x = 0; x < width; x++) {
158
+            int suma = p0[x - 1] * -1 +
159
+                       p0[x] *     -1 +
160
+                       p0[x + 1] * -1 +
161
+                       p2[x - 1] *  1 +
162
+                       p2[x] *      1 +
163
+                       p2[x + 1] *  1;
164
+            int sumb = p0[x - 1] * -1 +
165
+                       p0[x + 1] *  1 +
166
+                       p1[x - 1] * -1 +
167
+                       p1[x + 1] *  1 +
168
+                       p2[x - 1] * -1 +
169
+                       p2[x + 1] *  1;
170
+
171
+            dst[x] = av_clip(sqrt(suma*suma + sumb*sumb) * scale + delta, 0, peak);
172
+        }
173
+
174
+        p0 = p1;
175
+        p1 = p2;
176
+        p2 = (p2 == end) ? orig: p2 + bstride;
177
+        dst += out->linesize[plane] / 2;
178
+    }
179
+}
180
+
181
+static void filter16_sobel(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
182
+{
183
+    const uint16_t *src = (const uint16_t *)in->data[plane];
184
+    uint16_t *dst = (uint16_t *)out->data[plane];
185
+    const int peak = (1 << s->depth) - 1;
186
+    const int stride = in->linesize[plane] / 2;
187
+    const int bstride = s->bstride;
188
+    const int height = s->planeheight[plane];
189
+    const int width  = s->planewidth[plane];
190
+    const float scale = s->scale;
191
+    const float delta = s->delta;
192
+    uint16_t *p0 = (uint16_t *)s->buffer + 16;
193
+    uint16_t *p1 = p0 + bstride;
194
+    uint16_t *p2 = p1 + bstride;
195
+    uint16_t *orig = p0, *end = p2;
196
+    int y, x;
197
+
198
+    line_copy16(p0, src + stride, width, 1);
199
+    line_copy16(p1, src, width, 1);
200
+
201
+    for (y = 0; y < height; y++) {
202
+        src += stride * (y < height - 1 ? 1 : -1);
203
+        line_copy16(p2, src, width, 1);
204
+
205
+        for (x = 0; x < width; x++) {
206
+            int suma = p0[x - 1] * -1 +
207
+                       p0[x] *     -2 +
208
+                       p0[x + 1] * -1 +
209
+                       p2[x - 1] *  1 +
210
+                       p2[x] *      2 +
211
+                       p2[x + 1] *  1;
212
+            int sumb = p0[x - 1] * -1 +
213
+                       p0[x + 1] *  1 +
214
+                       p1[x - 1] * -2 +
215
+                       p1[x + 1] *  2 +
216
+                       p2[x - 1] * -1 +
217
+                       p2[x + 1] *  1;
218
+
219
+            dst[x] = av_clip(sqrt(suma*suma + sumb*sumb) * scale + delta, 0, peak);
220
+        }
221
+
222
+        p0 = p1;
223
+        p1 = p2;
224
+        p2 = (p2 == end) ? orig: p2 + bstride;
225
+        dst += out->linesize[plane] / 2;
226
+    }
227
+}
228
+
229
+static void filter_prewitt(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
230
+{
231
+    const uint8_t *src = in->data[plane];
232
+    uint8_t *dst = out->data[plane];
233
+    const int stride = in->linesize[plane];
234
+    const int bstride = s->bstride;
235
+    const int height = s->planeheight[plane];
236
+    const int width  = s->planewidth[plane];
237
+    const float scale = s->scale;
238
+    const float delta = s->delta;
239
+    uint8_t *p0 = s->buffer + 16;
240
+    uint8_t *p1 = p0 + bstride;
241
+    uint8_t *p2 = p1 + bstride;
242
+    uint8_t *orig = p0, *end = p2;
243
+    int y, x;
244
+
245
+    line_copy8(p0, src + stride, width, 1);
246
+    line_copy8(p1, src, width, 1);
247
+
248
+    for (y = 0; y < height; y++) {
249
+        src += stride * (y < height - 1 ? 1 : -1);
250
+        line_copy8(p2, src, width, 1);
251
+
252
+        for (x = 0; x < width; x++) {
253
+            int suma = p0[x - 1] * -1 +
254
+                       p0[x] *     -1 +
255
+                       p0[x + 1] * -1 +
256
+                       p2[x - 1] *  1 +
257
+                       p2[x] *      1 +
258
+                       p2[x + 1] *  1;
259
+            int sumb = p0[x - 1] * -1 +
260
+                       p0[x + 1] *  1 +
261
+                       p1[x - 1] * -1 +
262
+                       p1[x + 1] *  1 +
263
+                       p2[x - 1] * -1 +
264
+                       p2[x + 1] *  1;
265
+
266
+            dst[x] = av_clip_uint8(sqrt(suma*suma + sumb*sumb) * scale + delta);
267
+        }
268
+
269
+        p0 = p1;
270
+        p1 = p2;
271
+        p2 = (p2 == end) ? orig: p2 + bstride;
272
+        dst += out->linesize[plane];
273
+    }
274
+}
275
+
276
+static void filter_sobel(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
277
+{
278
+    const uint8_t *src = in->data[plane];
279
+    uint8_t *dst = out->data[plane];
280
+    const int stride = in->linesize[plane];
281
+    const int bstride = s->bstride;
282
+    const int height = s->planeheight[plane];
283
+    const int width  = s->planewidth[plane];
284
+    const float scale = s->scale;
285
+    const float delta = s->delta;
286
+    uint8_t *p0 = s->buffer + 16;
287
+    uint8_t *p1 = p0 + bstride;
288
+    uint8_t *p2 = p1 + bstride;
289
+    uint8_t *orig = p0, *end = p2;
290
+    int y, x;
291
+
292
+    line_copy8(p0, src + stride, width, 1);
293
+    line_copy8(p1, src, width, 1);
294
+
295
+    for (y = 0; y < height; y++) {
296
+        src += stride * (y < height - 1 ? 1 : -1);
297
+        line_copy8(p2, src, width, 1);
298
+
299
+        for (x = 0; x < width; x++) {
300
+            int suma = p0[x - 1] * -1 +
301
+                       p0[x] *     -2 +
302
+                       p0[x + 1] * -1 +
303
+                       p2[x - 1] *  1 +
304
+                       p2[x] *      2 +
305
+                       p2[x + 1] *  1;
306
+            int sumb = p0[x - 1] * -1 +
307
+                       p0[x + 1] *  1 +
308
+                       p1[x - 1] * -2 +
309
+                       p1[x + 1] *  2 +
310
+                       p2[x - 1] * -1 +
311
+                       p2[x + 1] *  1;
312
+
313
+            dst[x] = av_clip_uint8(sqrt(suma*suma + sumb*sumb) * scale + delta);
314
+        }
315
+
316
+        p0 = p1;
317
+        p1 = p2;
318
+        p2 = (p2 == end) ? orig: p2 + bstride;
319
+        dst += out->linesize[plane];
320
+    }
321
+}
322
+
133 323
 static void filter16_3x3(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
134 324
 {
135 325
     const uint16_t *src = (const uint16_t *)in->data[plane];
... ...
@@ -338,7 +531,8 @@ static void filter_5x5(ConvolutionContext *s, AVFrame *in, AVFrame *out, int pla
338 338
 
339 339
 static int config_input(AVFilterLink *inlink)
340 340
 {
341
-    ConvolutionContext *s = inlink->dst->priv;
341
+    AVFilterContext *ctx = inlink->dst;
342
+    ConvolutionContext *s = ctx->priv;
342 343
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
343 344
     int ret, p;
344 345
 
... ...
@@ -356,13 +550,23 @@ static int config_input(AVFilterLink *inlink)
356 356
     if (!s->buffer)
357 357
         return AVERROR(ENOMEM);
358 358
 
359
-    if (s->depth > 8) {
360
-        for (p = 0; p < s->nb_planes; p++) {
361
-            if (s->size[p] == 3)
362
-                s->filter[p] = filter16_3x3;
363
-            else if (s->size[p] == 5)
364
-                s->filter[p] = filter16_5x5;
359
+    if (!strcmp(ctx->filter->name, "convolution")) {
360
+        if (s->depth > 8) {
361
+            for (p = 0; p < s->nb_planes; p++) {
362
+                if (s->size[p] == 3)
363
+                    s->filter[p] = filter16_3x3;
364
+                else if (s->size[p] == 5)
365
+                    s->filter[p] = filter16_5x5;
366
+            }
365 367
         }
368
+    } else if (!strcmp(ctx->filter->name, "prewitt")) {
369
+        if (s->depth > 8)
370
+            for (p = 0; p < s->nb_planes; p++)
371
+                s->filter[p] = filter16_prewitt;
372
+    } else if (!strcmp(ctx->filter->name, "sobel")) {
373
+        if (s->depth > 8)
374
+            for (p = 0; p < s->nb_planes; p++)
375
+                s->filter[p] = filter16_sobel;
366 376
     }
367 377
 
368 378
     return 0;
... ...
@@ -403,34 +607,50 @@ static av_cold int init(AVFilterContext *ctx)
403 403
     ConvolutionContext *s = ctx->priv;
404 404
     int i;
405 405
 
406
-    for (i = 0; i < 4; i++) {
407
-        int *matrix = (int *)s->matrix[i];
408
-        char *p, *arg, *saveptr = NULL;
406
+    if (!strcmp(ctx->filter->name, "convolution")) {
407
+        for (i = 0; i < 4; i++) {
408
+            int *matrix = (int *)s->matrix[i];
409
+            char *p, *arg, *saveptr = NULL;
409 410
 
410
-        p = s->matrix_str[i];
411
-        while (s->matrix_length[i] < 25) {
412
-            if (!(arg = av_strtok(p, " ", &saveptr)))
413
-                break;
411
+            p = s->matrix_str[i];
412
+            while (s->matrix_length[i] < 25) {
413
+                if (!(arg = av_strtok(p, " ", &saveptr)))
414
+                    break;
414 415
 
415
-            p = NULL;
416
-            sscanf(arg, "%d", &matrix[s->matrix_length[i]]);
417
-            s->matrix_length[i]++;
418
-        }
416
+                p = NULL;
417
+                sscanf(arg, "%d", &matrix[s->matrix_length[i]]);
418
+                s->matrix_length[i]++;
419
+            }
419 420
 
420
-        if (s->matrix_length[i] == 9) {
421
-            s->size[i] = 3;
422
-            if (!memcmp(matrix, same3x3, sizeof(same3x3)))
423
-                s->copy[i] = 1;
421
+            if (s->matrix_length[i] == 9) {
422
+                s->size[i] = 3;
423
+                if (!memcmp(matrix, same3x3, sizeof(same3x3)))
424
+                    s->copy[i] = 1;
425
+                else
426
+                    s->filter[i] = filter_3x3;
427
+            } else if (s->matrix_length[i] == 25) {
428
+                s->size[i] = 5;
429
+                if (!memcmp(matrix, same5x5, sizeof(same5x5)))
430
+                    s->copy[i] = 1;
431
+                else
432
+                    s->filter[i] = filter_5x5;
433
+            } else {
434
+                return AVERROR(EINVAL);
435
+            }
436
+        }
437
+    } else if (!strcmp(ctx->filter->name, "prewitt")) {
438
+        for (i = 0; i < 4; i++) {
439
+            if ((1 << i) & s->planes)
440
+                s->filter[i] = filter_prewitt;
424 441
             else
425
-                s->filter[i] = filter_3x3;
426
-        } else if (s->matrix_length[i] == 25) {
427
-            s->size[i] = 5;
428
-            if (!memcmp(matrix, same5x5, sizeof(same5x5)))
429 442
                 s->copy[i] = 1;
443
+        }
444
+    } else if (!strcmp(ctx->filter->name, "sobel")) {
445
+        for (i = 0; i < 4; i++) {
446
+            if ((1 << i) & s->planes)
447
+                s->filter[i] = filter_sobel;
430 448
             else
431
-                s->filter[i] = filter_5x5;
432
-        } else {
433
-            return AVERROR(EINVAL);
449
+                s->copy[i] = 1;
434 450
         }
435 451
     }
436 452
 
... ...
@@ -462,6 +682,8 @@ static const AVFilterPad convolution_outputs[] = {
462 462
     { NULL }
463 463
 };
464 464
 
465
+#if CONFIG_CONVOLUTION_FILTER
466
+
465 467
 AVFilter ff_vf_convolution = {
466 468
     .name          = "convolution",
467 469
     .description   = NULL_IF_CONFIG_SMALL("Apply convolution filter."),
... ...
@@ -474,3 +696,57 @@ AVFilter ff_vf_convolution = {
474 474
     .outputs       = convolution_outputs,
475 475
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
476 476
 };
477
+
478
+#endif /* CONFIG_CONVOLUTION_FILTER */
479
+
480
+#if CONFIG_PREWITT_FILTER
481
+
482
+static const AVOption prewitt_options[] = {
483
+    { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT,  {.i64=15}, 0, 15, FLAGS},
484
+    { "scale",  "set scale",            OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0,  65535, FLAGS},
485
+    { "delta",  "set delta",            OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
486
+    { NULL }
487
+};
488
+
489
+AVFILTER_DEFINE_CLASS(prewitt);
490
+
491
+AVFilter ff_vf_prewitt = {
492
+    .name          = "prewitt",
493
+    .description   = NULL_IF_CONFIG_SMALL("Apply prewitt operator."),
494
+    .priv_size     = sizeof(ConvolutionContext),
495
+    .priv_class    = &prewitt_class,
496
+    .init          = init,
497
+    .uninit        = uninit,
498
+    .query_formats = query_formats,
499
+    .inputs        = convolution_inputs,
500
+    .outputs       = convolution_outputs,
501
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
502
+};
503
+
504
+#endif /* CONFIG_PREWITT_FILTER */
505
+
506
+#if CONFIG_SOBEL_FILTER
507
+
508
+static const AVOption sobel_options[] = {
509
+    { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT,  {.i64=15}, 0, 15, FLAGS},
510
+    { "scale",  "set scale",            OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0,  65535, FLAGS},
511
+    { "delta",  "set delta",            OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
512
+    { NULL }
513
+};
514
+
515
+AVFILTER_DEFINE_CLASS(sobel);
516
+
517
+AVFilter ff_vf_sobel = {
518
+    .name          = "sobel",
519
+    .description   = NULL_IF_CONFIG_SMALL("Apply sobel operator."),
520
+    .priv_size     = sizeof(ConvolutionContext),
521
+    .priv_class    = &sobel_class,
522
+    .init          = init,
523
+    .uninit        = uninit,
524
+    .query_formats = query_formats,
525
+    .inputs        = convolution_inputs,
526
+    .outputs       = convolution_outputs,
527
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
528
+};
529
+
530
+#endif /* CONFIG_SOBEL_FILTER */