2 * Copyright (c) 2020 Paul B Mahol
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "libavutil/imgutils.h"
22 #include "libavutil/mem.h"
23 #include "libavutil/opt.h"
24 #include "libavutil/pixdesc.h"
29 typedef struct DBlurContext
{
36 float b0
, b1
, q
, c
, R3
;
45 #define OFFSET(x) offsetof(DBlurContext, x)
46 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
48 static const AVOption dblur_options
[] = {
49 { "angle", "set angle", OFFSET(angle
), AV_OPT_TYPE_FLOAT
, {.dbl
=45}, 0.0, 360, FLAGS
},
50 { "radius", "set radius", OFFSET(radius
), AV_OPT_TYPE_FLOAT
, {.dbl
=5}, 0, 8192, FLAGS
},
51 { "planes", "set planes to filter", OFFSET(planes
), AV_OPT_TYPE_INT
, {.i64
=0xF}, 0, 0xF, FLAGS
},
55 AVFILTER_DEFINE_CLASS(dblur
);
57 #define f(n, m) (dst[(n) * width + (m)])
59 static int filter_horizontally(AVFilterContext
*ctx
, int width
, int height
)
61 DBlurContext
*s
= ctx
->priv
;
62 const float b0
= s
->b0
;
63 const float b1
= s
->b1
;
66 float *dst
= s
->buffer
;
70 for (int y
= 1; y
< height
; y
++) {
71 g
= q
* f(y
, 0) + c
* f(y
, 0);
72 for (int x
= 0; x
< width
; x
++) {
73 f(y
, x
) = b0
* f(y
, x
) + b1
* f(y
- 1, x
) + g
;
74 g
= q
* f(y
, x
) + c
* f(y
- 1, x
);
78 for (int y
= height
- 2; y
>= 0; y
--) {
79 g
= q
* f(y
, width
- 1) + c
* f(y
, width
- 1);
80 for (int x
= width
- 1; x
>= 0; x
--) {
81 f(y
, x
) = b0
* f(y
, x
) + b1
* f(y
+ 1, x
) + g
;
82 g
= q
* f(y
, x
) + c
* f(y
+ 1, x
);
86 for (int y
= 1; y
< height
; y
++) {
87 g
= q
* f(y
, width
- 1) + c
* f(y
, width
- 1);
88 for (int x
= width
- 1; x
>= 0; x
--) {
89 f(y
, x
) = b0
* f(y
, x
) + b1
* f(y
- 1, x
) + g
;
90 g
= q
* f(y
, x
) + c
* f(y
- 1, x
);
94 for (int y
= height
- 2; y
>= 0; y
--) {
95 g
= q
* f(y
, 0) + c
* f(y
, 0);
96 for (int x
= 0; x
< width
; x
++) {
97 f(y
, x
) = b0
* f(y
, x
) + b1
* f(y
+ 1, x
) + g
;
98 g
= q
* f(y
, x
) + c
* f(y
+ 1, x
);
106 static void diriir2d(AVFilterContext
*ctx
, int plane
)
108 DBlurContext
*s
= ctx
->priv
;
109 const int width
= s
->planewidth
[plane
];
110 const int height
= s
->planeheight
[plane
];
112 filter_horizontally(ctx
, width
, height
);
115 static const enum AVPixelFormat pix_fmts
[] = {
116 AV_PIX_FMT_YUVA444P
, AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV440P
,
117 AV_PIX_FMT_YUVJ444P
, AV_PIX_FMT_YUVJ440P
,
118 AV_PIX_FMT_YUVA422P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUVA420P
, AV_PIX_FMT_YUV420P
,
119 AV_PIX_FMT_YUVJ422P
, AV_PIX_FMT_YUVJ420P
,
120 AV_PIX_FMT_YUVJ411P
, AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
,
121 AV_PIX_FMT_YUV420P9
, AV_PIX_FMT_YUV422P9
, AV_PIX_FMT_YUV444P9
,
122 AV_PIX_FMT_YUV420P10
, AV_PIX_FMT_YUV422P10
, AV_PIX_FMT_YUV444P10
,
123 AV_PIX_FMT_YUV420P12
, AV_PIX_FMT_YUV422P12
, AV_PIX_FMT_YUV444P12
, AV_PIX_FMT_YUV440P12
,
124 AV_PIX_FMT_YUV420P14
, AV_PIX_FMT_YUV422P14
, AV_PIX_FMT_YUV444P14
,
125 AV_PIX_FMT_YUV420P16
, AV_PIX_FMT_YUV422P16
, AV_PIX_FMT_YUV444P16
,
126 AV_PIX_FMT_YUVA420P9
, AV_PIX_FMT_YUVA422P9
, AV_PIX_FMT_YUVA444P9
,
127 AV_PIX_FMT_YUVA420P10
, AV_PIX_FMT_YUVA422P10
, AV_PIX_FMT_YUVA444P10
,
128 AV_PIX_FMT_YUVA422P12
, AV_PIX_FMT_YUVA444P12
,
129 AV_PIX_FMT_YUVA420P16
, AV_PIX_FMT_YUVA422P16
, AV_PIX_FMT_YUVA444P16
,
130 AV_PIX_FMT_GBRP
, AV_PIX_FMT_GBRP9
, AV_PIX_FMT_GBRP10
,
131 AV_PIX_FMT_GBRP12
, AV_PIX_FMT_GBRP14
, AV_PIX_FMT_GBRP16
,
132 AV_PIX_FMT_GBRAP
, AV_PIX_FMT_GBRAP10
, AV_PIX_FMT_GBRAP12
, AV_PIX_FMT_GBRAP16
,
133 AV_PIX_FMT_GRAY8
, AV_PIX_FMT_GRAY9
, AV_PIX_FMT_GRAY10
, AV_PIX_FMT_GRAY12
, AV_PIX_FMT_GRAY14
, AV_PIX_FMT_GRAY16
,
134 AV_PIX_FMT_GRAYF32
, AV_PIX_FMT_GBRPF32
, AV_PIX_FMT_GBRAPF32
,
138 static av_cold
void uninit(AVFilterContext
*ctx
)
140 DBlurContext
*s
= ctx
->priv
;
142 av_freep(&s
->buffer
);
145 static int config_input(AVFilterLink
*inlink
)
147 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
148 DBlurContext
*s
= inlink
->dst
->priv
;
152 s
->depth
= desc
->comp
[0].depth
;
153 s
->planewidth
[1] = s
->planewidth
[2] = AV_CEIL_RSHIFT(inlink
->w
, desc
->log2_chroma_w
);
154 s
->planewidth
[0] = s
->planewidth
[3] = inlink
->w
;
155 s
->planeheight
[1] = s
->planeheight
[2] = AV_CEIL_RSHIFT(inlink
->h
, desc
->log2_chroma_h
);
156 s
->planeheight
[0] = s
->planeheight
[3] = inlink
->h
;
158 s
->nb_planes
= av_pix_fmt_count_planes(inlink
->format
);
160 s
->buffer
= av_malloc_array(FFALIGN(inlink
->w
, 16), FFALIGN(inlink
->h
, 16) * sizeof(*s
->buffer
));
162 return AVERROR(ENOMEM
);
167 static void set_params(DBlurContext
*s
, float angle
, float r
)
169 float mu
, nu
, R1
, R2
, w1
, w2
;
170 float a0
, a1
, a2
, a3
;
172 angle
= angle
* M_PI
/ 180.f
;
176 R1
= (mu
* r
) * (mu
* r
);
177 R2
= (nu
* r
) * (nu
* r
);
178 s
->R3
= mu
* nu
* r
* r
;
179 w1
= sqrtf(0.25f
+ R1
);
180 w2
= sqrtf(0.25f
+ R2
);
181 a0
= (w1
+ 0.5f
) * (w2
+ 0.5f
) - fabsf(s
->R3
);
191 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
193 AVFilterContext
*ctx
= inlink
->dst
;
194 DBlurContext
*s
= ctx
->priv
;
195 AVFilterLink
*outlink
= ctx
->outputs
[0];
199 set_params(s
, s
->angle
, s
->radius
);
201 if (av_frame_is_writable(in
)) {
204 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
207 return AVERROR(ENOMEM
);
209 av_frame_copy_props(out
, in
);
212 for (plane
= 0; plane
< s
->nb_planes
; plane
++) {
213 const int height
= s
->planeheight
[plane
];
214 const int width
= s
->planewidth
[plane
];
215 float *bptr
= s
->buffer
;
216 const uint8_t *src
= in
->data
[plane
];
217 const uint16_t *src16
= (const uint16_t *)in
->data
[plane
];
218 const float *src32
= (const float *)in
->data
[plane
];
219 uint8_t *dst
= out
->data
[plane
];
220 uint16_t *dst16
= (uint16_t *)out
->data
[plane
];
221 float *dst32
= (float *)out
->data
[plane
];
224 if (!(s
->planes
& (1 << plane
))) {
226 av_image_copy_plane(out
->data
[plane
], out
->linesize
[plane
],
227 in
->data
[plane
], in
->linesize
[plane
],
228 width
* ((s
->depth
+ 7) / 8), height
);
233 for (y
= 0; y
< height
; y
++) {
234 for (x
= 0; x
< width
; x
++) {
238 src
+= in
->linesize
[plane
];
240 } else if (s
->depth
<= 16) {
241 for (y
= 0; y
< height
; y
++) {
242 for (x
= 0; x
< width
; x
++) {
246 src16
+= in
->linesize
[plane
] / 2;
249 for (y
= 0; y
< height
; y
++) {
250 for (x
= 0; x
< width
; x
++) {
251 memcpy(bptr
, src32
, width
* sizeof(float));
254 src32
+= in
->linesize
[plane
] / 4;
258 diriir2d(ctx
, plane
);
262 for (y
= 0; y
< height
; y
++) {
263 for (x
= 0; x
< width
; x
++) {
264 dst
[x
] = av_clip_uint8(lrintf(bptr
[x
]));
267 dst
+= out
->linesize
[plane
];
269 } else if (s
->depth
<= 16) {
270 for (y
= 0; y
< height
; y
++) {
271 for (x
= 0; x
< width
; x
++) {
272 dst16
[x
] = av_clip_uintp2_c(lrintf(bptr
[x
]), s
->depth
);
275 dst16
+= out
->linesize
[plane
] / 2;
278 for (y
= 0; y
< height
; y
++) {
279 for (x
= 0; x
< width
; x
++) {
280 memcpy(dst32
, bptr
, width
* sizeof(float));
283 dst32
+= out
->linesize
[plane
] / 4;
290 return ff_filter_frame(outlink
, out
);
293 static const AVFilterPad dblur_inputs
[] = {
296 .type
= AVMEDIA_TYPE_VIDEO
,
297 .config_props
= config_input
,
298 .filter_frame
= filter_frame
,
302 const FFFilter ff_vf_dblur
= {
304 .p
.description
= NULL_IF_CONFIG_SMALL("Apply Directional Blur filter."),
305 .p
.priv_class
= &dblur_class
,
306 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,
307 .priv_size
= sizeof(DBlurContext
),
309 FILTER_INPUTS(dblur_inputs
),
310 FILTER_OUTPUTS(ff_video_default_filterpad
),
311 FILTER_PIXFMTS_ARRAY(pix_fmts
),
312 .process_command
= ff_filter_process_command
,