diff --git a/Changelog b/Changelog index 1f5ca70655..a893efa4f0 100644 --- a/Changelog +++ b/Changelog @@ -21,6 +21,7 @@ version : - Gremlin Digital Video demuxer and decoder - headphone audio filter - superequalizer audio filter +- roberts video filter version 3.3: - CrystalHD decoder moved to new decode API diff --git a/doc/filters.texi b/doc/filters.texi index 53e057c774..40c89626db 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -12118,6 +12118,23 @@ trim=end=5,reverse @end example @end itemize +@section roberts +Apply roberts cross 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 rotate Rotate video by an arbitrary angle expressed in radians. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 52c44d266f..f023a0d5d6 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -268,6 +268,7 @@ OBJS-$(CONFIG_REMOVEGRAIN_FILTER) += vf_removegrain.o OBJS-$(CONFIG_REMOVELOGO_FILTER) += bbox.o lswsutils.o lavfutils.o vf_removelogo.o OBJS-$(CONFIG_REPEATFIELDS_FILTER) += vf_repeatfields.o OBJS-$(CONFIG_REVERSE_FILTER) += f_reverse.o +OBJS-$(CONFIG_ROBERTS_FILTER) += vf_convolution.o OBJS-$(CONFIG_ROTATE_FILTER) += vf_rotate.o OBJS-$(CONFIG_SAB_FILTER) += vf_sab.o OBJS-$(CONFIG_SCALE_FILTER) += vf_scale.o scale.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index bd81091000..c1c52330ef 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -279,6 +279,7 @@ static void register_all(void) REGISTER_FILTER(REMOVELOGO, removelogo, vf); REGISTER_FILTER(REPEATFIELDS, repeatfields, vf); REGISTER_FILTER(REVERSE, reverse, vf); + REGISTER_FILTER(ROBERTS, roberts, vf); REGISTER_FILTER(ROTATE, rotate, vf); REGISTER_FILTER(SAB, sab, vf); REGISTER_FILTER(SCALE, scale, vf); diff --git a/libavfilter/version.h b/libavfilter/version.h index c37a34242f..3902801b2c 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -30,7 +30,7 @@ #include "libavutil/version.h" #define LIBAVFILTER_VERSION_MAJOR 6 -#define LIBAVFILTER_VERSION_MINOR 93 +#define LIBAVFILTER_VERSION_MINOR 94 #define LIBAVFILTER_VERSION_MICRO 100 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ diff --git a/libavfilter/vf_convolution.c b/libavfilter/vf_convolution.c index 41e92497c3..530859422c 100644 --- a/libavfilter/vf_convolution.c +++ b/libavfilter/vf_convolution.c @@ -198,6 +198,55 @@ static int filter16_prewitt(AVFilterContext *ctx, void *arg, int jobnr, int nb_j return 0; } +static int filter16_roberts(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + ConvolutionContext *s = ctx->priv; + ThreadData *td = arg; + AVFrame *in = td->in; + AVFrame *out = td->out; + const int plane = td->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 int slice_start = (height * jobnr) / nb_jobs; + const int slice_end = (height * (jobnr+1)) / nb_jobs; + const uint16_t *src = (const uint16_t *)in->data[plane] + slice_start * stride; + uint16_t *dst = (uint16_t *)out->data[plane] + slice_start * (out->linesize[plane] / 2); + const float scale = s->scale; + const float delta = s->delta; + uint16_t *p0 = (uint16_t *)s->bptrs[jobnr] + 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 * (slice_start == 0 ? 1 : -1), width, 1); + line_copy16(p1, src, width, 1); + + for (y = slice_start; y < slice_end; 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 + + p1[x ] * -1; + int sumb = p0[x ] * 1 + + p1[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; + } + + return 0; +} + static int filter16_sobel(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ConvolutionContext *s = ctx->priv; @@ -311,6 +360,54 @@ static int filter_prewitt(AVFilterContext *ctx, void *arg, int jobnr, int nb_job return 0; } +static int filter_roberts(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + ConvolutionContext *s = ctx->priv; + ThreadData *td = arg; + AVFrame *in = td->in; + AVFrame *out = td->out; + const int plane = td->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 int slice_start = (height * jobnr) / nb_jobs; + const int slice_end = (height * (jobnr+1)) / nb_jobs; + const uint8_t *src = in->data[plane] + slice_start * stride; + uint8_t *dst = out->data[plane] + slice_start * out->linesize[plane]; + const float scale = s->scale; + const float delta = s->delta; + uint8_t *p0 = s->bptrs[jobnr] + 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 * (slice_start == 0 ? 1 : -1), width, 1); + line_copy8(p1, src, width, 1); + + for (y = slice_start; y < slice_end; 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 + + p1[x ] * -1; + int sumb = p0[x ] * 1 + + p1[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]; + } + + return 0; +} + static int filter_sobel(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ConvolutionContext *s = ctx->priv; @@ -651,6 +748,10 @@ static int config_input(AVFilterLink *inlink) if (s->depth > 8) for (p = 0; p < s->nb_planes; p++) s->filter[p] = filter16_prewitt; + } else if (!strcmp(ctx->filter->name, "roberts")) { + if (s->depth > 8) + for (p = 0; p < s->nb_planes; p++) + s->filter[p] = filter16_roberts; } else if (!strcmp(ctx->filter->name, "sobel")) { if (s->depth > 8) for (p = 0; p < s->nb_planes; p++) @@ -739,6 +840,13 @@ static av_cold int init(AVFilterContext *ctx) else s->copy[i] = 1; } + } else if (!strcmp(ctx->filter->name, "roberts")) { + for (i = 0; i < 4; i++) { + if ((1 << i) & s->planes) + s->filter[i] = filter_roberts; + else + s->copy[i] = 1; + } } else if (!strcmp(ctx->filter->name, "sobel")) { for (i = 0; i < 4; i++) { if ((1 << i) & s->planes) @@ -845,3 +953,29 @@ AVFilter ff_vf_sobel = { }; #endif /* CONFIG_SOBEL_FILTER */ + +#if CONFIG_ROBERTS_FILTER + +static const AVOption roberts_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(roberts); + +AVFilter ff_vf_roberts = { + .name = "roberts", + .description = NULL_IF_CONFIG_SMALL("Apply roberts cross operator."), + .priv_size = sizeof(ConvolutionContext), + .priv_class = &roberts_class, + .init = init, + .uninit = uninit, + .query_formats = query_formats, + .inputs = convolution_inputs, + .outputs = convolution_outputs, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, +}; + +#endif /* CONFIG_ROBERTS_FILTER */