diff --git a/doc/filters.texi b/doc/filters.texi
index 2acf770..19ccacf 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -10860,6 +10860,17 @@ 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.
+@end table
+
 @section psnr
 
 Obtain the average, maximum and minimum PSNR (Peak Signal to Noise
@@ -12714,6 +12725,17 @@ 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.
+@end table
+
 @anchor{spp}
 @section spp
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index bfbeac4..5cd10fa 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -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
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 9549126..47d95f5 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -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);
diff --git a/libavfilter/vf_convolution.c b/libavfilter/vf_convolution.c
index 2cfc3ba..62d6171 100644
--- a/libavfilter/vf_convolution.c
+++ b/libavfilter/vf_convolution.c
@@ -34,6 +34,7 @@ typedef struct ConvolutionContext {
     char *matrix_str[4];
     float rdiv[4];
     float bias[4];
+    int planes;
 
     int size[4];
     int depth;
@@ -130,6 +131,188 @@ 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];
+    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), 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];
+    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), 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];
+    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));
+        }
+
+        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];
+    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));
+        }
+
+        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 +521,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 +540,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 +597,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;
         }
     }
 
@@ -474,3 +684,49 @@ AVFilter ff_vf_convolution = {
     .outputs       = convolution_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
 };
+
+#define OFFSET(x) offsetof(ConvolutionContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
+
+static const AVOption prewitt_options[] = {
+    { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15}, 0, 15, 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,
+};
+
+#define OFFSET(x) offsetof(ConvolutionContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
+
+static const AVOption sobel_options[] = {
+    { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15}, 0, 15, 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,
+};
