avfilter: add sobel and prewitt filter
authorPaul B Mahol <onemda@gmail.com>
Sat, 10 Sep 2016 21:34:30 +0000 (23:34 +0200)
committerPaul B Mahol <onemda@gmail.com>
Mon, 12 Sep 2016 12:38:45 +0000 (14:38 +0200)
Signed-off-by: Paul B Mahol <onemda@gmail.com>
Changelog
doc/filters.texi
libavfilter/Makefile
libavfilter/allfilters.c
libavfilter/version.h
libavfilter/vf_convolution.c

index 64695c8..c5cdada 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -27,6 +27,7 @@ version <next>:
 - weave filter
 - gblur filter
 - avgblur filter
+- sobel and prewitt filter
 
 
 version 3.1:
index 2acf770..ff0ac50 100644 (file)
@@ -10860,6 +10860,23 @@ Set medium thresholding (good results, default).
 @end table
 @end table
 
+@section prewitt
+Apply prewitt operator to input video stream.
+
+The filter accepts the following option:
+
+@table @option
+@item planes
+Set which planes will be processed, unprocessed planes will be copied.
+By default value 0xf, all planes will be processed.
+
+@item scale
+Set value which will be multiplied with filtered result.
+
+@item delta
+Set value which will be added to filtered result.
+@end table
+
 @section psnr
 
 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
 @end example
 @end itemize
 
+@section sobel
+Apply sobel operator to input video stream.
+
+The filter accepts the following option:
+
+@table @option
+@item planes
+Set which planes will be processed, unprocessed planes will be copied.
+By default value 0xf, all planes will be processed.
+
+@item scale
+Set value which will be multiplied with filtered result.
+
+@item delta
+Set value which will be added to filtered result.
+@end table
+
 @anchor{spp}
 @section spp
 
index bfbeac4..5cd10fa 100644 (file)
@@ -238,6 +238,7 @@ OBJS-$(CONFIG_PHASE_FILTER)                  += vf_phase.o
 OBJS-$(CONFIG_PIXDESCTEST_FILTER)            += vf_pixdesctest.o
 OBJS-$(CONFIG_PP_FILTER)                     += vf_pp.o
 OBJS-$(CONFIG_PP7_FILTER)                    += vf_pp7.o
+OBJS-$(CONFIG_PREWITT_FILTER)                += vf_convolution.o
 OBJS-$(CONFIG_PSNR_FILTER)                   += vf_psnr.o dualinput.o framesync.o
 OBJS-$(CONFIG_PULLUP_FILTER)                 += vf_pullup.o
 OBJS-$(CONFIG_QP_FILTER)                     += vf_qp.o
@@ -270,6 +271,7 @@ OBJS-$(CONFIG_SHUFFLEFRAMES_FILTER)          += vf_shuffleframes.o
 OBJS-$(CONFIG_SHUFFLEPLANES_FILTER)          += vf_shuffleplanes.o
 OBJS-$(CONFIG_SIGNALSTATS_FILTER)            += vf_signalstats.o
 OBJS-$(CONFIG_SMARTBLUR_FILTER)              += vf_smartblur.o
+OBJS-$(CONFIG_SOBEL_FILTER)                  += vf_convolution.o
 OBJS-$(CONFIG_SPLIT_FILTER)                  += split.o
 OBJS-$(CONFIG_SPP_FILTER)                    += vf_spp.o
 OBJS-$(CONFIG_SSIM_FILTER)                   += vf_ssim.o dualinput.o framesync.o
index 9549126..47d95f5 100644 (file)
@@ -254,6 +254,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER(PIXDESCTEST,    pixdesctest,    vf);
     REGISTER_FILTER(PP,             pp,             vf);
     REGISTER_FILTER(PP7,            pp7,            vf);
+    REGISTER_FILTER(PREWITT,        prewitt,        vf);
     REGISTER_FILTER(PSNR,           psnr,           vf);
     REGISTER_FILTER(PULLUP,         pullup,         vf);
     REGISTER_FILTER(QP,             qp,             vf);
@@ -286,6 +287,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER(SHUFFLEPLANES,  shuffleplanes,  vf);
     REGISTER_FILTER(SIGNALSTATS,    signalstats,    vf);
     REGISTER_FILTER(SMARTBLUR,      smartblur,      vf);
+    REGISTER_FILTER(SOBEL,          sobel,          vf);
     REGISTER_FILTER(SPLIT,          split,          vf);
     REGISTER_FILTER(SPP,            spp,            vf);
     REGISTER_FILTER(SSIM,           ssim,           vf);
index a416561..1a7a1b5 100644 (file)
@@ -30,7 +30,7 @@
 #include "libavutil/version.h"
 
 #define LIBAVFILTER_VERSION_MAJOR   6
-#define LIBAVFILTER_VERSION_MINOR  61
+#define LIBAVFILTER_VERSION_MINOR  62
 #define LIBAVFILTER_VERSION_MICRO 100
 
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
index 2cfc3ba..7b51267 100644 (file)
@@ -34,6 +34,9 @@ typedef struct ConvolutionContext {
     char *matrix_str[4];
     float rdiv[4];
     float bias[4];
+    float scale;
+    float delta;
+    int planes;
 
     int size[4];
     int depth;
@@ -130,6 +133,196 @@ static inline void line_copy16(uint16_t *line, const uint16_t *srcp, int width,
     }
 }
 
+static void filter16_prewitt(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
+{
+    const uint16_t *src = (const uint16_t *)in->data[plane];
+    uint16_t *dst = (uint16_t *)out->data[plane];
+    const int peak = (1 << s->depth) - 1;
+    const int stride = in->linesize[plane] / 2;
+    const int bstride = s->bstride;
+    const int height = s->planeheight[plane];
+    const int width  = s->planewidth[plane];
+    const float scale = s->scale;
+    const float delta = s->delta;
+    uint16_t *p0 = (uint16_t *)s->buffer + 16;
+    uint16_t *p1 = p0 + bstride;
+    uint16_t *p2 = p1 + bstride;
+    uint16_t *orig = p0, *end = p2;
+    int y, x;
+
+    line_copy16(p0, src + stride, width, 1);
+    line_copy16(p1, src, width, 1);
+
+    for (y = 0; y < height; y++) {
+        src += stride * (y < height - 1 ? 1 : -1);
+        line_copy16(p2, src, width, 1);
+
+        for (x = 0; x < width; x++) {
+            int suma = p0[x - 1] * -1 +
+                       p0[x] *     -1 +
+                       p0[x + 1] * -1 +
+                       p2[x - 1] *  1 +
+                       p2[x] *      1 +
+                       p2[x + 1] *  1;
+            int sumb = p0[x - 1] * -1 +
+                       p0[x + 1] *  1 +
+                       p1[x - 1] * -1 +
+                       p1[x + 1] *  1 +
+                       p2[x - 1] * -1 +
+                       p2[x + 1] *  1;
+
+            dst[x] = av_clip(sqrt(suma*suma + sumb*sumb) * scale + delta, 0, peak);
+        }
+
+        p0 = p1;
+        p1 = p2;
+        p2 = (p2 == end) ? orig: p2 + bstride;
+        dst += out->linesize[plane] / 2;
+    }
+}
+
+static void filter16_sobel(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
+{
+    const uint16_t *src = (const uint16_t *)in->data[plane];
+    uint16_t *dst = (uint16_t *)out->data[plane];
+    const int peak = (1 << s->depth) - 1;
+    const int stride = in->linesize[plane] / 2;
+    const int bstride = s->bstride;
+    const int height = s->planeheight[plane];
+    const int width  = s->planewidth[plane];
+    const float scale = s->scale;
+    const float delta = s->delta;
+    uint16_t *p0 = (uint16_t *)s->buffer + 16;
+    uint16_t *p1 = p0 + bstride;
+    uint16_t *p2 = p1 + bstride;
+    uint16_t *orig = p0, *end = p2;
+    int y, x;
+
+    line_copy16(p0, src + stride, width, 1);
+    line_copy16(p1, src, width, 1);
+
+    for (y = 0; y < height; y++) {
+        src += stride * (y < height - 1 ? 1 : -1);
+        line_copy16(p2, src, width, 1);
+
+        for (x = 0; x < width; x++) {
+            int suma = p0[x - 1] * -1 +
+                       p0[x] *     -2 +
+                       p0[x + 1] * -1 +
+                       p2[x - 1] *  1 +
+                       p2[x] *      2 +
+                       p2[x + 1] *  1;
+            int sumb = p0[x - 1] * -1 +
+                       p0[x + 1] *  1 +
+                       p1[x - 1] * -2 +
+                       p1[x + 1] *  2 +
+                       p2[x - 1] * -1 +
+                       p2[x + 1] *  1;
+
+            dst[x] = av_clip(sqrt(suma*suma + sumb*sumb) * scale + delta, 0, peak);
+        }
+
+        p0 = p1;
+        p1 = p2;
+        p2 = (p2 == end) ? orig: p2 + bstride;
+        dst += out->linesize[plane] / 2;
+    }
+}
+
+static void filter_prewitt(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
+{
+    const uint8_t *src = in->data[plane];
+    uint8_t *dst = out->data[plane];
+    const int stride = in->linesize[plane];
+    const int bstride = s->bstride;
+    const int height = s->planeheight[plane];
+    const int width  = s->planewidth[plane];
+    const float scale = s->scale;
+    const float delta = s->delta;
+    uint8_t *p0 = s->buffer + 16;
+    uint8_t *p1 = p0 + bstride;
+    uint8_t *p2 = p1 + bstride;
+    uint8_t *orig = p0, *end = p2;
+    int y, x;
+
+    line_copy8(p0, src + stride, width, 1);
+    line_copy8(p1, src, width, 1);
+
+    for (y = 0; y < height; y++) {
+        src += stride * (y < height - 1 ? 1 : -1);
+        line_copy8(p2, src, width, 1);
+
+        for (x = 0; x < width; x++) {
+            int suma = p0[x - 1] * -1 +
+                       p0[x] *     -1 +
+                       p0[x + 1] * -1 +
+                       p2[x - 1] *  1 +
+                       p2[x] *      1 +
+                       p2[x + 1] *  1;
+            int sumb = p0[x - 1] * -1 +
+                       p0[x + 1] *  1 +
+                       p1[x - 1] * -1 +
+                       p1[x + 1] *  1 +
+                       p2[x - 1] * -1 +
+                       p2[x + 1] *  1;
+
+            dst[x] = av_clip_uint8(sqrt(suma*suma + sumb*sumb) * scale + delta);
+        }
+
+        p0 = p1;
+        p1 = p2;
+        p2 = (p2 == end) ? orig: p2 + bstride;
+        dst += out->linesize[plane];
+    }
+}
+
+static void filter_sobel(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
+{
+    const uint8_t *src = in->data[plane];
+    uint8_t *dst = out->data[plane];
+    const int stride = in->linesize[plane];
+    const int bstride = s->bstride;
+    const int height = s->planeheight[plane];
+    const int width  = s->planewidth[plane];
+    const float scale = s->scale;
+    const float delta = s->delta;
+    uint8_t *p0 = s->buffer + 16;
+    uint8_t *p1 = p0 + bstride;
+    uint8_t *p2 = p1 + bstride;
+    uint8_t *orig = p0, *end = p2;
+    int y, x;
+
+    line_copy8(p0, src + stride, width, 1);
+    line_copy8(p1, src, width, 1);
+
+    for (y = 0; y < height; y++) {
+        src += stride * (y < height - 1 ? 1 : -1);
+        line_copy8(p2, src, width, 1);
+
+        for (x = 0; x < width; x++) {
+            int suma = p0[x - 1] * -1 +
+                       p0[x] *     -2 +
+                       p0[x + 1] * -1 +
+                       p2[x - 1] *  1 +
+                       p2[x] *      2 +
+                       p2[x + 1] *  1;
+            int sumb = p0[x - 1] * -1 +
+                       p0[x + 1] *  1 +
+                       p1[x - 1] * -2 +
+                       p1[x + 1] *  2 +
+                       p2[x - 1] * -1 +
+                       p2[x + 1] *  1;
+
+            dst[x] = av_clip_uint8(sqrt(suma*suma + sumb*sumb) * scale + delta);
+        }
+
+        p0 = p1;
+        p1 = p2;
+        p2 = (p2 == end) ? orig: p2 + bstride;
+        dst += out->linesize[plane];
+    }
+}
+
 static void filter16_3x3(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
 {
     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
 
 static int config_input(AVFilterLink *inlink)
 {
-    ConvolutionContext *s = inlink->dst->priv;
+    AVFilterContext *ctx = inlink->dst;
+    ConvolutionContext *s = ctx->priv;
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
     int ret, p;
 
@@ -356,13 +550,23 @@ static int config_input(AVFilterLink *inlink)
     if (!s->buffer)
         return AVERROR(ENOMEM);
 
-    if (s->depth > 8) {
-        for (p = 0; p < s->nb_planes; p++) {
-            if (s->size[p] == 3)
-                s->filter[p] = filter16_3x3;
-            else if (s->size[p] == 5)
-                s->filter[p] = filter16_5x5;
+    if (!strcmp(ctx->filter->name, "convolution")) {
+        if (s->depth > 8) {
+            for (p = 0; p < s->nb_planes; p++) {
+                if (s->size[p] == 3)
+                    s->filter[p] = filter16_3x3;
+                else if (s->size[p] == 5)
+                    s->filter[p] = filter16_5x5;
+            }
         }
+    } else if (!strcmp(ctx->filter->name, "prewitt")) {
+        if (s->depth > 8)
+            for (p = 0; p < s->nb_planes; p++)
+                s->filter[p] = filter16_prewitt;
+    } else if (!strcmp(ctx->filter->name, "sobel")) {
+        if (s->depth > 8)
+            for (p = 0; p < s->nb_planes; p++)
+                s->filter[p] = filter16_sobel;
     }
 
     return 0;
@@ -403,34 +607,50 @@ static av_cold int init(AVFilterContext *ctx)
     ConvolutionContext *s = ctx->priv;
     int i;
 
-    for (i = 0; i < 4; i++) {
-        int *matrix = (int *)s->matrix[i];
-        char *p, *arg, *saveptr = NULL;
+    if (!strcmp(ctx->filter->name, "convolution")) {
+        for (i = 0; i < 4; i++) {
+            int *matrix = (int *)s->matrix[i];
+            char *p, *arg, *saveptr = NULL;
 
-        p = s->matrix_str[i];
-        while (s->matrix_length[i] < 25) {
-            if (!(arg = av_strtok(p, " ", &saveptr)))
-                break;
+            p = s->matrix_str[i];
+            while (s->matrix_length[i] < 25) {
+                if (!(arg = av_strtok(p, " ", &saveptr)))
+                    break;
 
-            p = NULL;
-            sscanf(arg, "%d", &matrix[s->matrix_length[i]]);
-            s->matrix_length[i]++;
-        }
+                p = NULL;
+                sscanf(arg, "%d", &matrix[s->matrix_length[i]]);
+                s->matrix_length[i]++;
+            }
 
-        if (s->matrix_length[i] == 9) {
-            s->size[i] = 3;
-            if (!memcmp(matrix, same3x3, sizeof(same3x3)))
-                s->copy[i] = 1;
+            if (s->matrix_length[i] == 9) {
+                s->size[i] = 3;
+                if (!memcmp(matrix, same3x3, sizeof(same3x3)))
+                    s->copy[i] = 1;
+                else
+                    s->filter[i] = filter_3x3;
+            } else if (s->matrix_length[i] == 25) {
+                s->size[i] = 5;
+                if (!memcmp(matrix, same5x5, sizeof(same5x5)))
+                    s->copy[i] = 1;
+                else
+                    s->filter[i] = filter_5x5;
+            } else {
+                return AVERROR(EINVAL);
+            }
+        }
+    } else if (!strcmp(ctx->filter->name, "prewitt")) {
+        for (i = 0; i < 4; i++) {
+            if ((1 << i) & s->planes)
+                s->filter[i] = filter_prewitt;
             else
-                s->filter[i] = filter_3x3;
-        } else if (s->matrix_length[i] == 25) {
-            s->size[i] = 5;
-            if (!memcmp(matrix, same5x5, sizeof(same5x5)))
                 s->copy[i] = 1;
+        }
+    } else if (!strcmp(ctx->filter->name, "sobel")) {
+        for (i = 0; i < 4; i++) {
+            if ((1 << i) & s->planes)
+                s->filter[i] = filter_sobel;
             else
-                s->filter[i] = filter_5x5;
-        } else {
-            return AVERROR(EINVAL);
+                s->copy[i] = 1;
         }
     }
 
@@ -462,6 +682,8 @@ static const AVFilterPad convolution_outputs[] = {
     { NULL }
 };
 
+#if CONFIG_CONVOLUTION_FILTER
+
 AVFilter ff_vf_convolution = {
     .name          = "convolution",
     .description   = NULL_IF_CONFIG_SMALL("Apply convolution filter."),
@@ -474,3 +696,57 @@ AVFilter ff_vf_convolution = {
     .outputs       = convolution_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
 };
+
+#endif /* CONFIG_CONVOLUTION_FILTER */
+
+#if CONFIG_PREWITT_FILTER
+
+static const AVOption prewitt_options[] = {
+    { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT,  {.i64=15}, 0, 15, FLAGS},
+    { "scale",  "set scale",            OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0,  65535, FLAGS},
+    { "delta",  "set delta",            OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(prewitt);
+
+AVFilter ff_vf_prewitt = {
+    .name          = "prewitt",
+    .description   = NULL_IF_CONFIG_SMALL("Apply prewitt operator."),
+    .priv_size     = sizeof(ConvolutionContext),
+    .priv_class    = &prewitt_class,
+    .init          = init,
+    .uninit        = uninit,
+    .query_formats = query_formats,
+    .inputs        = convolution_inputs,
+    .outputs       = convolution_outputs,
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+};
+
+#endif /* CONFIG_PREWITT_FILTER */
+
+#if CONFIG_SOBEL_FILTER
+
+static const AVOption sobel_options[] = {
+    { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT,  {.i64=15}, 0, 15, FLAGS},
+    { "scale",  "set scale",            OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0,  65535, FLAGS},
+    { "delta",  "set delta",            OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(sobel);
+
+AVFilter ff_vf_sobel = {
+    .name          = "sobel",
+    .description   = NULL_IF_CONFIG_SMALL("Apply sobel operator."),
+    .priv_size     = sizeof(ConvolutionContext),
+    .priv_class    = &sobel_class,
+    .init          = init,
+    .uninit        = uninit,
+    .query_formats = query_formats,
+    .inputs        = convolution_inputs,
+    .outputs       = convolution_outputs,
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+};
+
+#endif /* CONFIG_SOBEL_FILTER */