2 * Original copyright (c) 2002 Remi Guyomarch <rguyom@pobox.com>
3 * Port copyright (c) 2010 Daniel G. Taylor <dan@programmer-art.org>
4 * Relicensed to the LGPL with permission from Remi Guyomarch.
6 * This file is part of FFmpeg.
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 * blur / sharpen filter, ported to FFmpeg from MPlayer
26 * libmpcodecs/unsharp.c.
28 * This code is based on:
30 * An Efficient algorithm for Gaussian blur using finite-state machines
31 * Frederick M. Waltz and John W. V. Miller
33 * SPIE Conf. on Machine Vision Systems for Inspection and Metrology VII
34 * Originally published Boston, Nov 98
36 * http://www.engin.umd.umich.edu/~jwvm/ece581/21_GBlur.pdf
43 #include "libavutil/common.h"
44 #include "libavutil/imgutils.h"
45 #include "libavutil/mem.h"
46 #include "libavutil/opt.h"
47 #include "libavutil/pixdesc.h"
49 #include "unsharp_opencl.h"
51 static void apply_unsharp( uint8_t *dst
, int dst_stride
,
52 const uint8_t *src
, int src_stride
,
53 int width
, int height
, UnsharpFilterParam
*fp
)
55 uint32_t **sc
= fp
->sc
;
56 uint32_t sr
[MAX_MATRIX_SIZE
- 1], tmp1
, tmp2
;
60 const uint8_t *src2
= NULL
; //silence a warning
61 const int amount
= fp
->amount
;
62 const int steps_x
= fp
->steps_x
;
63 const int steps_y
= fp
->steps_y
;
64 const int scalebits
= fp
->scalebits
;
65 const int32_t halfscale
= fp
->halfscale
;
68 av_image_copy_plane(dst
, dst_stride
, src
, src_stride
, width
, height
);
72 for (y
= 0; y
< 2 * steps_y
; y
++)
73 memset(sc
[y
], 0, sizeof(sc
[y
][0]) * (width
+ 2 * steps_x
));
75 for (y
= -steps_y
; y
< height
+ steps_y
; y
++) {
79 memset(sr
, 0, sizeof(sr
[0]) * (2 * steps_x
- 1));
80 for (x
= -steps_x
; x
< width
+ steps_x
; x
++) {
81 tmp1
= x
<= 0 ? src2
[0] : x
>= width
? src2
[width
-1] : src2
[x
];
82 for (z
= 0; z
< steps_x
* 2; z
+= 2) {
83 tmp2
= sr
[z
+ 0] + tmp1
; sr
[z
+ 0] = tmp1
;
84 tmp1
= sr
[z
+ 1] + tmp2
; sr
[z
+ 1] = tmp2
;
86 for (z
= 0; z
< steps_y
* 2; z
+= 2) {
87 tmp2
= sc
[z
+ 0][x
+ steps_x
] + tmp1
; sc
[z
+ 0][x
+ steps_x
] = tmp1
;
88 tmp1
= sc
[z
+ 1][x
+ steps_x
] + tmp2
; sc
[z
+ 1][x
+ steps_x
] = tmp2
;
90 if (x
>= steps_x
&& y
>= steps_y
) {
91 const uint8_t *srx
= src
- steps_y
* src_stride
+ x
- steps_x
;
92 uint8_t *dsx
= dst
- steps_y
* dst_stride
+ x
- steps_x
;
94 res
= (int32_t)*srx
+ ((((int32_t) * srx
- (int32_t)((tmp1
+ halfscale
) >> scalebits
)) * amount
) >> 16);
95 *dsx
= av_clip_uint8(res
);
105 static int apply_unsharp_c(AVFilterContext
*ctx
, AVFrame
*in
, AVFrame
*out
)
107 AVFilterLink
*inlink
= ctx
->inputs
[0];
108 UnsharpContext
*unsharp
= ctx
->priv
;
109 int i
, plane_w
[3], plane_h
[3];
110 UnsharpFilterParam
*fp
[3];
111 plane_w
[0] = inlink
->w
;
112 plane_w
[1] = plane_w
[2] = FF_CEIL_RSHIFT(inlink
->w
, unsharp
->hsub
);
113 plane_h
[0] = inlink
->h
;
114 plane_h
[1] = plane_h
[2] = FF_CEIL_RSHIFT(inlink
->h
, unsharp
->vsub
);
115 fp
[0] = &unsharp
->luma
;
116 fp
[1] = fp
[2] = &unsharp
->chroma
;
117 for (i
= 0; i
< 3; i
++) {
118 apply_unsharp(out
->data
[i
], out
->linesize
[i
], in
->data
[i
], in
->linesize
[i
], plane_w
[i
], plane_h
[i
], fp
[i
]);
123 static void set_filter_param(UnsharpFilterParam
*fp
, int msize_x
, int msize_y
, float amount
)
125 fp
->msize_x
= msize_x
;
126 fp
->msize_y
= msize_y
;
127 fp
->amount
= amount
* 65536.0;
129 fp
->steps_x
= msize_x
/ 2;
130 fp
->steps_y
= msize_y
/ 2;
131 fp
->scalebits
= (fp
->steps_x
+ fp
->steps_y
) * 2;
132 fp
->halfscale
= 1 << (fp
->scalebits
- 1);
135 static av_cold
int init(AVFilterContext
*ctx
)
138 UnsharpContext
*unsharp
= ctx
->priv
;
141 set_filter_param(&unsharp
->luma
, unsharp
->lmsize_x
, unsharp
->lmsize_y
, unsharp
->lamount
);
142 set_filter_param(&unsharp
->chroma
, unsharp
->cmsize_x
, unsharp
->cmsize_y
, unsharp
->camount
);
144 unsharp
->apply_unsharp
= apply_unsharp_c
;
145 if (!CONFIG_OPENCL
&& unsharp
->opencl
) {
146 av_log(ctx
, AV_LOG_ERROR
, "OpenCL support was not enabled in this build, cannot be selected\n");
147 return AVERROR(EINVAL
);
149 if (CONFIG_OPENCL
&& unsharp
->opencl
) {
150 unsharp
->apply_unsharp
= ff_opencl_apply_unsharp
;
151 ret
= ff_opencl_unsharp_init(ctx
);
158 static int query_formats(AVFilterContext
*ctx
)
160 static const enum AVPixelFormat pix_fmts
[] = {
161 AV_PIX_FMT_YUV420P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV410P
,
162 AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV440P
, AV_PIX_FMT_YUVJ420P
, AV_PIX_FMT_YUVJ422P
,
163 AV_PIX_FMT_YUVJ444P
, AV_PIX_FMT_YUVJ440P
, AV_PIX_FMT_NONE
166 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
171 static int init_filter_param(AVFilterContext
*ctx
, UnsharpFilterParam
*fp
, const char *effect_type
, int width
)
174 const char *effect
= fp
->amount
== 0 ? "none" : fp
->amount
< 0 ? "blur" : "sharpen";
176 if (!(fp
->msize_x
& fp
->msize_y
& 1)) {
177 av_log(ctx
, AV_LOG_ERROR
,
178 "Invalid even size for %s matrix size %dx%d\n",
179 effect_type
, fp
->msize_x
, fp
->msize_y
);
180 return AVERROR(EINVAL
);
183 av_log(ctx
, AV_LOG_VERBOSE
, "effect:%s type:%s msize_x:%d msize_y:%d amount:%0.2f\n",
184 effect
, effect_type
, fp
->msize_x
, fp
->msize_y
, fp
->amount
/ 65535.0);
186 for (z
= 0; z
< 2 * fp
->steps_y
; z
++)
187 if (!(fp
->sc
[z
] = av_malloc_array(width
+ 2 * fp
->steps_x
,
188 sizeof(*(fp
->sc
[z
])))))
189 return AVERROR(ENOMEM
);
194 static int config_props(AVFilterLink
*link
)
196 UnsharpContext
*unsharp
= link
->dst
->priv
;
197 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(link
->format
);
200 unsharp
->hsub
= desc
->log2_chroma_w
;
201 unsharp
->vsub
= desc
->log2_chroma_h
;
203 ret
= init_filter_param(link
->dst
, &unsharp
->luma
, "luma", link
->w
);
206 ret
= init_filter_param(link
->dst
, &unsharp
->chroma
, "chroma", FF_CEIL_RSHIFT(link
->w
, unsharp
->hsub
));
213 static void free_filter_param(UnsharpFilterParam
*fp
)
217 for (z
= 0; z
< 2 * fp
->steps_y
; z
++)
221 static av_cold
void uninit(AVFilterContext
*ctx
)
223 UnsharpContext
*unsharp
= ctx
->priv
;
225 if (CONFIG_OPENCL
&& unsharp
->opencl
) {
226 ff_opencl_unsharp_uninit(ctx
);
229 free_filter_param(&unsharp
->luma
);
230 free_filter_param(&unsharp
->chroma
);
233 static int filter_frame(AVFilterLink
*link
, AVFrame
*in
)
235 UnsharpContext
*unsharp
= link
->dst
->priv
;
236 AVFilterLink
*outlink
= link
->dst
->outputs
[0];
240 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
243 return AVERROR(ENOMEM
);
245 av_frame_copy_props(out
, in
);
246 if (CONFIG_OPENCL
&& unsharp
->opencl
) {
247 ret
= ff_opencl_unsharp_process_inout_buf(link
->dst
, in
, out
);
252 ret
= unsharp
->apply_unsharp(link
->dst
, in
, out
);
258 return ff_filter_frame(outlink
, out
);
261 #define OFFSET(x) offsetof(UnsharpContext, x)
262 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
265 static const AVOption unsharp_options
[] = {
266 { "luma_msize_x", "set luma matrix horizontal size", OFFSET(lmsize_x
), AV_OPT_TYPE_INT
, { .i64
= 5 }, MIN_SIZE
, MAX_SIZE
, FLAGS
},
267 { "lx", "set luma matrix horizontal size", OFFSET(lmsize_x
), AV_OPT_TYPE_INT
, { .i64
= 5 }, MIN_SIZE
, MAX_SIZE
, FLAGS
},
268 { "luma_msize_y", "set luma matrix vertical size", OFFSET(lmsize_y
), AV_OPT_TYPE_INT
, { .i64
= 5 }, MIN_SIZE
, MAX_SIZE
, FLAGS
},
269 { "ly", "set luma matrix vertical size", OFFSET(lmsize_y
), AV_OPT_TYPE_INT
, { .i64
= 5 }, MIN_SIZE
, MAX_SIZE
, FLAGS
},
270 { "luma_amount", "set luma effect strength", OFFSET(lamount
), AV_OPT_TYPE_FLOAT
, { .dbl
= 1 }, -2, 5, FLAGS
},
271 { "la", "set luma effect strength", OFFSET(lamount
), AV_OPT_TYPE_FLOAT
, { .dbl
= 1 }, -2, 5, FLAGS
},
272 { "chroma_msize_x", "set chroma matrix horizontal size", OFFSET(cmsize_x
), AV_OPT_TYPE_INT
, { .i64
= 5 }, MIN_SIZE
, MAX_SIZE
, FLAGS
},
273 { "cx", "set chroma matrix horizontal size", OFFSET(cmsize_x
), AV_OPT_TYPE_INT
, { .i64
= 5 }, MIN_SIZE
, MAX_SIZE
, FLAGS
},
274 { "chroma_msize_y", "set chroma matrix vertical size", OFFSET(cmsize_y
), AV_OPT_TYPE_INT
, { .i64
= 5 }, MIN_SIZE
, MAX_SIZE
, FLAGS
},
275 { "cy", "set chroma matrix vertical size", OFFSET(cmsize_y
), AV_OPT_TYPE_INT
, { .i64
= 5 }, MIN_SIZE
, MAX_SIZE
, FLAGS
},
276 { "chroma_amount", "set chroma effect strength", OFFSET(camount
), AV_OPT_TYPE_FLOAT
, { .dbl
= 0 }, -2, 5, FLAGS
},
277 { "ca", "set chroma effect strength", OFFSET(camount
), AV_OPT_TYPE_FLOAT
, { .dbl
= 0 }, -2, 5, FLAGS
},
278 { "opencl", "use OpenCL filtering capabilities", OFFSET(opencl
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, 1, FLAGS
},
282 AVFILTER_DEFINE_CLASS(unsharp
);
284 static const AVFilterPad avfilter_vf_unsharp_inputs
[] = {
287 .type
= AVMEDIA_TYPE_VIDEO
,
288 .filter_frame
= filter_frame
,
289 .config_props
= config_props
,
294 static const AVFilterPad avfilter_vf_unsharp_outputs
[] = {
297 .type
= AVMEDIA_TYPE_VIDEO
,
302 AVFilter ff_vf_unsharp
= {
304 .description
= NULL_IF_CONFIG_SMALL("Sharpen or blur the input video."),
305 .priv_size
= sizeof(UnsharpContext
),
306 .priv_class
= &unsharp_class
,
309 .query_formats
= query_formats
,
310 .inputs
= avfilter_vf_unsharp_inputs
,
311 .outputs
= avfilter_vf_unsharp_outputs
,
312 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,