From patchwork Sat Sep 10 22:04:41 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 541 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.140.134 with SMTP id o128csp939022vsd; Sat, 10 Sep 2016 15:05:05 -0700 (PDT) X-Received: by 10.28.143.209 with SMTP id r200mr3810373wmd.5.1473545105611; Sat, 10 Sep 2016 15:05:05 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id h201si2894867wma.15.2016.09.10.15.05.03; Sat, 10 Sep 2016 15:05:05 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 367CB689F83; Sun, 11 Sep 2016 01:04:50 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f68.google.com (mail-wm0-f68.google.com [74.125.82.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 79AE6689EE1 for ; Sun, 11 Sep 2016 01:04:43 +0300 (EEST) Received: by mail-wm0-f68.google.com with SMTP id 199so117439wma.1 for ; Sat, 10 Sep 2016 15:04:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id; bh=s3dDZfwfY7Aaz/wkAWgr1eNrFMtjPYa1ZA/Ep9Xq1Qc=; b=06YVD84iyWYJJxI4CF9eD+w1ptrdPSO5QSoEY/c3TBapwh/9JIkrh5nQrGTysPHbcC +o9biUYxjveQW1iK6gWi+f+SADjDSjttO+bR31iG/lVmFONKRE9sqm5pNlYxbodut/9k jKmyr1XpbZDlZI1v1eFUqg18uYoAFD3CILSsAZyCEWkDuFy/bbiGSPHEA0gfjt2I5/vK LYQPcQZKUrKH7kbJLej23td4f6vwT/yARICG1lGJ9VebTdXRklqZhzbaXVLhTxgw6KZg Rdoq+V9WyNBPvOy1NGBsbHxacqLtA+QoAIw/pvKJA5k1BgOGi47PYjkXcBg1DgCbuWRH sTGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id; bh=s3dDZfwfY7Aaz/wkAWgr1eNrFMtjPYa1ZA/Ep9Xq1Qc=; b=EKIkGVY3YtTX4J62Xexuu7zv5crxo9ObRNYhCIj1rrhKNWrtMSqN9ZU033XMk05Bnf XvD5fhfPAFeNofVMKSoTh+z+nA0+3evwr90pjYiR+y9BOcZ9w5Fk+WD1gImEa/x8G2oI laJDLmVMkGHSlm+SpNSCYGERgqiFvwoujg5sAAVf8gb/0pDDYbvG52Yxb+Zt0jE0GSw5 wMMoywsVJnFixaSZxg+z7kpohwzSoRG2GVfKLRbXrPjGcdt+5TIEPCAF4OXaQMN5K2VQ kDYnhWnXVhWpSRLRkN6x/RyB80bPbV0ZYUron4gSXcvsIvcNPCkQwy/irEzMU0e3oiff LNOw== X-Gm-Message-State: AE9vXwOeVbMSINp36WU4r8LeJqjW7G4g6xmaBYjLSJlVeIfVgOn2ZBEupQwJiRm/EiFjRQ== X-Received: by 10.194.227.203 with SMTP id sc11mr2614771wjc.73.1473545094035; Sat, 10 Sep 2016 15:04:54 -0700 (PDT) Received: from computer.gigaset.lan (141-136-205-32.dsl.iskon.hr. [141.136.205.32]) by smtp.gmail.com with ESMTPSA id yt4sm10060431wjc.48.2016.09.10.15.04.52 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 10 Sep 2016 15:04:53 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Sun, 11 Sep 2016 00:04:41 +0200 Message-Id: <1473545081-27676-1-git-send-email-onemda@gmail.com> X-Mailer: git-send-email 2.5.0 Subject: [FFmpeg-devel] [PATCH] avfilter: add sobel and prewitt filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Paul B Mahol --- doc/filters.texi | 22 +++ libavfilter/Makefile | 2 + libavfilter/allfilters.c | 2 + libavfilter/vf_convolution.c | 314 +++++++++++++++++++++++++++++++++++++++---- 4 files changed, 311 insertions(+), 29 deletions(-) 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, +};