yading@11: /* yading@11: * Original copyright (c) 2002 Remi Guyomarch yading@11: * Port copyright (c) 2010 Daniel G. Taylor yading@11: * Relicensed to the LGPL with permission from Remi Guyomarch. yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: /** yading@11: * @file yading@11: * blur / sharpen filter, ported to FFmpeg from MPlayer yading@11: * libmpcodecs/unsharp.c. yading@11: * yading@11: * This code is based on: yading@11: * yading@11: * An Efficient algorithm for Gaussian blur using finite-state machines yading@11: * Frederick M. Waltz and John W. V. Miller yading@11: * yading@11: * SPIE Conf. on Machine Vision Systems for Inspection and Metrology VII yading@11: * Originally published Boston, Nov 98 yading@11: * yading@11: * http://www.engin.umd.umich.edu/~jwvm/ece581/21_GBlur.pdf yading@11: */ yading@11: yading@11: #include /* DBL_MAX */ yading@11: yading@11: #include "avfilter.h" yading@11: #include "formats.h" yading@11: #include "internal.h" yading@11: #include "video.h" yading@11: #include "libavutil/common.h" yading@11: #include "libavutil/mem.h" yading@11: #include "libavutil/opt.h" yading@11: #include "libavutil/pixdesc.h" yading@11: yading@11: #define MIN_MATRIX_SIZE 3 yading@11: #define MAX_MATRIX_SIZE 63 yading@11: yading@11: /* right-shift and round-up */ yading@11: #define SHIFTUP(x,shift) (-((-(x))>>(shift))) yading@11: yading@11: typedef struct FilterParam { yading@11: int msize_x; ///< matrix width yading@11: int msize_y; ///< matrix height yading@11: int amount; ///< effect amount yading@11: int steps_x; ///< horizontal step count yading@11: int steps_y; ///< vertical step count yading@11: int scalebits; ///< bits to shift pixel yading@11: int32_t halfscale; ///< amount to add to pixel yading@11: uint32_t *sc[MAX_MATRIX_SIZE - 1]; ///< finite state machine storage yading@11: } FilterParam; yading@11: yading@11: typedef struct { yading@11: const AVClass *class; yading@11: int lmsize_x, lmsize_y, cmsize_x, cmsize_y; yading@11: float lamount, camount; yading@11: FilterParam luma; ///< luma parameters (width, height, amount) yading@11: FilterParam chroma; ///< chroma parameters (width, height, amount) yading@11: int hsub, vsub; yading@11: } UnsharpContext; yading@11: yading@11: static void apply_unsharp( uint8_t *dst, int dst_stride, yading@11: const uint8_t *src, int src_stride, yading@11: int width, int height, FilterParam *fp) yading@11: { yading@11: uint32_t **sc = fp->sc; yading@11: uint32_t sr[MAX_MATRIX_SIZE - 1], tmp1, tmp2; yading@11: yading@11: int32_t res; yading@11: int x, y, z; yading@11: const uint8_t *src2 = NULL; //silence a warning yading@11: const int amount = fp->amount; yading@11: const int steps_x = fp->steps_x; yading@11: const int steps_y = fp->steps_y; yading@11: const int scalebits = fp->scalebits; yading@11: const int32_t halfscale = fp->halfscale; yading@11: yading@11: if (!amount) { yading@11: if (dst_stride == src_stride) yading@11: memcpy(dst, src, src_stride * height); yading@11: else yading@11: for (y = 0; y < height; y++, dst += dst_stride, src += src_stride) yading@11: memcpy(dst, src, width); yading@11: return; yading@11: } yading@11: yading@11: for (y = 0; y < 2 * steps_y; y++) yading@11: memset(sc[y], 0, sizeof(sc[y][0]) * (width + 2 * steps_x)); yading@11: yading@11: for (y = -steps_y; y < height + steps_y; y++) { yading@11: if (y < height) yading@11: src2 = src; yading@11: yading@11: memset(sr, 0, sizeof(sr[0]) * (2 * steps_x - 1)); yading@11: for (x = -steps_x; x < width + steps_x; x++) { yading@11: tmp1 = x <= 0 ? src2[0] : x >= width ? src2[width-1] : src2[x]; yading@11: for (z = 0; z < steps_x * 2; z += 2) { yading@11: tmp2 = sr[z + 0] + tmp1; sr[z + 0] = tmp1; yading@11: tmp1 = sr[z + 1] + tmp2; sr[z + 1] = tmp2; yading@11: } yading@11: for (z = 0; z < steps_y * 2; z += 2) { yading@11: tmp2 = sc[z + 0][x + steps_x] + tmp1; sc[z + 0][x + steps_x] = tmp1; yading@11: tmp1 = sc[z + 1][x + steps_x] + tmp2; sc[z + 1][x + steps_x] = tmp2; yading@11: } yading@11: if (x >= steps_x && y >= steps_y) { yading@11: const uint8_t *srx = src - steps_y * src_stride + x - steps_x; yading@11: uint8_t *dsx = dst - steps_y * dst_stride + x - steps_x; yading@11: yading@11: res = (int32_t)*srx + ((((int32_t) * srx - (int32_t)((tmp1 + halfscale) >> scalebits)) * amount) >> 16); yading@11: *dsx = av_clip_uint8(res); yading@11: } yading@11: } yading@11: if (y >= 0) { yading@11: dst += dst_stride; yading@11: src += src_stride; yading@11: } yading@11: } yading@11: } yading@11: yading@11: static void set_filter_param(FilterParam *fp, int msize_x, int msize_y, float amount) yading@11: { yading@11: fp->msize_x = msize_x; yading@11: fp->msize_y = msize_y; yading@11: fp->amount = amount * 65536.0; yading@11: yading@11: fp->steps_x = msize_x / 2; yading@11: fp->steps_y = msize_y / 2; yading@11: fp->scalebits = (fp->steps_x + fp->steps_y) * 2; yading@11: fp->halfscale = 1 << (fp->scalebits - 1); yading@11: } yading@11: yading@11: static av_cold int init(AVFilterContext *ctx) yading@11: { yading@11: UnsharpContext *unsharp = ctx->priv; yading@11: yading@11: yading@11: set_filter_param(&unsharp->luma, unsharp->lmsize_x, unsharp->lmsize_y, unsharp->lamount); yading@11: set_filter_param(&unsharp->chroma, unsharp->cmsize_x, unsharp->cmsize_y, unsharp->camount); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int query_formats(AVFilterContext *ctx) yading@11: { yading@11: static const enum AVPixelFormat pix_fmts[] = { yading@11: AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV410P, yading@11: AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, yading@11: AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_NONE yading@11: }; yading@11: yading@11: ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int init_filter_param(AVFilterContext *ctx, FilterParam *fp, const char *effect_type, int width) yading@11: { yading@11: int z; yading@11: const char *effect = fp->amount == 0 ? "none" : fp->amount < 0 ? "blur" : "sharpen"; yading@11: yading@11: if (!(fp->msize_x & fp->msize_y & 1)) { yading@11: av_log(ctx, AV_LOG_ERROR, yading@11: "Invalid even size for %s matrix size %dx%d\n", yading@11: effect_type, fp->msize_x, fp->msize_y); yading@11: return AVERROR(EINVAL); yading@11: } yading@11: yading@11: av_log(ctx, AV_LOG_VERBOSE, "effect:%s type:%s msize_x:%d msize_y:%d amount:%0.2f\n", yading@11: effect, effect_type, fp->msize_x, fp->msize_y, fp->amount / 65535.0); yading@11: yading@11: for (z = 0; z < 2 * fp->steps_y; z++) yading@11: if (!(fp->sc[z] = av_malloc(sizeof(*(fp->sc[z])) * (width + 2 * fp->steps_x)))) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int config_props(AVFilterLink *link) yading@11: { yading@11: UnsharpContext *unsharp = link->dst->priv; yading@11: const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format); yading@11: int ret; yading@11: yading@11: unsharp->hsub = desc->log2_chroma_w; yading@11: unsharp->vsub = desc->log2_chroma_h; yading@11: yading@11: ret = init_filter_param(link->dst, &unsharp->luma, "luma", link->w); yading@11: if (ret < 0) yading@11: return ret; yading@11: ret = init_filter_param(link->dst, &unsharp->chroma, "chroma", SHIFTUP(link->w, unsharp->hsub)); yading@11: if (ret < 0) yading@11: return ret; yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static void free_filter_param(FilterParam *fp) yading@11: { yading@11: int z; yading@11: yading@11: for (z = 0; z < 2 * fp->steps_y; z++) yading@11: av_free(fp->sc[z]); yading@11: } yading@11: yading@11: static av_cold void uninit(AVFilterContext *ctx) yading@11: { yading@11: UnsharpContext *unsharp = ctx->priv; yading@11: yading@11: free_filter_param(&unsharp->luma); yading@11: free_filter_param(&unsharp->chroma); yading@11: } yading@11: yading@11: static int filter_frame(AVFilterLink *link, AVFrame *in) yading@11: { yading@11: UnsharpContext *unsharp = link->dst->priv; yading@11: AVFilterLink *outlink = link->dst->outputs[0]; yading@11: AVFrame *out; yading@11: int cw = SHIFTUP(link->w, unsharp->hsub); yading@11: int ch = SHIFTUP(link->h, unsharp->vsub); yading@11: yading@11: out = ff_get_video_buffer(outlink, outlink->w, outlink->h); yading@11: if (!out) { yading@11: av_frame_free(&in); yading@11: return AVERROR(ENOMEM); yading@11: } yading@11: av_frame_copy_props(out, in); yading@11: yading@11: apply_unsharp(out->data[0], out->linesize[0], in->data[0], in->linesize[0], link->w, link->h, &unsharp->luma); yading@11: apply_unsharp(out->data[1], out->linesize[1], in->data[1], in->linesize[1], cw, ch, &unsharp->chroma); yading@11: apply_unsharp(out->data[2], out->linesize[2], in->data[2], in->linesize[2], cw, ch, &unsharp->chroma); yading@11: yading@11: av_frame_free(&in); yading@11: return ff_filter_frame(outlink, out); yading@11: } yading@11: yading@11: #define OFFSET(x) offsetof(UnsharpContext, x) yading@11: #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM yading@11: #define MIN_SIZE 3 yading@11: #define MAX_SIZE 63 yading@11: static const AVOption unsharp_options[] = { yading@11: { "luma_msize_x", "luma matrix horizontal size", OFFSET(lmsize_x), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, yading@11: { "lx", "luma matrix horizontal size", OFFSET(lmsize_x), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, yading@11: { "luma_msize_y", "luma matrix vertical size", OFFSET(lmsize_y), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, yading@11: { "ly", "luma matrix vertical size", OFFSET(lmsize_y), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, yading@11: { "luma_amount", "luma effect strength", OFFSET(lamount), AV_OPT_TYPE_FLOAT, { .dbl = 1 }, -2, 5, FLAGS }, yading@11: { "la", "luma effect strength", OFFSET(lamount), AV_OPT_TYPE_FLOAT, { .dbl = 1 }, -2, 5, FLAGS }, yading@11: { "chroma_msize_x", "chroma matrix horizontal size", OFFSET(cmsize_x), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, yading@11: { "cx", "chroma matrix horizontal size", OFFSET(cmsize_x), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, yading@11: { "chroma_msize_y", "chroma matrix vertical size", OFFSET(cmsize_y), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, yading@11: { "cy", "chroma matrix vertical size", OFFSET(cmsize_y), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, yading@11: { "chroma_amount", "chroma effect strength", OFFSET(camount), AV_OPT_TYPE_FLOAT, { .dbl = 0 }, -2, 5, FLAGS }, yading@11: { "ca", "chroma effect strength", OFFSET(camount), AV_OPT_TYPE_FLOAT, { .dbl = 0 }, -2, 5, FLAGS }, yading@11: { NULL }, yading@11: }; yading@11: yading@11: AVFILTER_DEFINE_CLASS(unsharp); yading@11: yading@11: static const AVFilterPad avfilter_vf_unsharp_inputs[] = { yading@11: { yading@11: .name = "default", yading@11: .type = AVMEDIA_TYPE_VIDEO, yading@11: .filter_frame = filter_frame, yading@11: .config_props = config_props, yading@11: }, yading@11: { NULL } yading@11: }; yading@11: yading@11: static const AVFilterPad avfilter_vf_unsharp_outputs[] = { yading@11: { yading@11: .name = "default", yading@11: .type = AVMEDIA_TYPE_VIDEO, yading@11: }, yading@11: { NULL } yading@11: }; yading@11: yading@11: AVFilter avfilter_vf_unsharp = { yading@11: .name = "unsharp", yading@11: .description = NULL_IF_CONFIG_SMALL("Sharpen or blur the input video."), yading@11: yading@11: .priv_size = sizeof(UnsharpContext), yading@11: .priv_class = &unsharp_class, yading@11: yading@11: .init = init, yading@11: .uninit = uninit, yading@11: .query_formats = query_formats, yading@11: yading@11: .inputs = avfilter_vf_unsharp_inputs, yading@11: yading@11: .outputs = avfilter_vf_unsharp_outputs, yading@11: };