FFmpeg
Main Page
Related Pages
Modules
Namespaces
Data Structures
Files
Examples
File List
Globals
All
Data Structures
Namespaces
Files
Functions
Variables
Typedefs
Enumerations
Enumerator
Macros
Groups
Pages
libavfilter
vf_hue.c
Go to the documentation of this file.
1
/*
2
* Copyright (c) 2003 Michael Niedermayer
3
* Copyright (c) 2012 Jeremy Tran
4
*
5
* This file is part of FFmpeg.
6
*
7
* FFmpeg is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Lesser General Public
9
* License as published by the Free Software Foundation; either
10
* version 2.1 of the License, or (at your option) any later version.
11
*
12
* FFmpeg is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
16
*
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with FFmpeg; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
*/
21
22
/**
23
* @file
24
* Apply a hue/saturation filter to the input video
25
* Ported from MPlayer libmpcodecs/vf_hue.c.
26
*/
27
28
#include <float.h>
29
#include "
libavutil/eval.h
"
30
#include "
libavutil/imgutils.h
"
31
#include "
libavutil/opt.h
"
32
#include "
libavutil/pixdesc.h
"
33
34
#include "
avfilter.h
"
35
#include "
formats.h
"
36
#include "
internal.h
"
37
#include "
video.h
"
38
39
#define SAT_MIN_VAL -10
40
#define SAT_MAX_VAL 10
41
42
static
const
char
*
const
var_names
[] = {
43
"n"
,
// frame count
44
"pts"
,
// presentation timestamp expressed in AV_TIME_BASE units
45
"r"
,
// frame rate
46
"t"
,
// timestamp expressed in seconds
47
"tb"
,
// timebase
48
NULL
49
};
50
51
enum
var_name
{
52
VAR_N
,
53
VAR_PTS
,
54
VAR_R
,
55
VAR_T
,
56
VAR_TB
,
57
VAR_NB
58
};
59
60
typedef
struct
{
61
const
AVClass
*
class
;
62
float
hue_deg
;
/* hue expressed in degrees */
63
float
hue
;
/* hue expressed in radians */
64
char
*
hue_deg_expr
;
65
char
*
hue_expr
;
66
AVExpr
*
hue_deg_pexpr
;
67
AVExpr
*
hue_pexpr
;
68
float
saturation
;
69
char
*
saturation_expr
;
70
AVExpr
*
saturation_pexpr
;
71
float
brightness
;
72
char
*
brightness_expr
;
73
AVExpr
*
brightness_pexpr
;
74
int
hsub
;
75
int
vsub
;
76
int32_t
hue_sin
;
77
int32_t
hue_cos
;
78
double
var_values[
VAR_NB
];
79
uint8_t
lut_l[256];
80
uint8_t
lut_u[256][256];
81
uint8_t
lut_v[256][256];
82
}
HueContext
;
83
84
#define OFFSET(x) offsetof(HueContext, x)
85
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
86
static
const
AVOption
hue_options
[] = {
87
{
"h"
,
"set the hue angle degrees expression"
,
OFFSET
(hue_deg_expr),
AV_OPT_TYPE_STRING
,
88
{ .str = NULL }, .flags =
FLAGS
},
89
{
"s"
,
"set the saturation expression"
,
OFFSET
(saturation_expr),
AV_OPT_TYPE_STRING
,
90
{ .str =
"1"
}, .flags =
FLAGS
},
91
{
"H"
,
"set the hue angle radians expression"
,
OFFSET
(hue_expr),
AV_OPT_TYPE_STRING
,
92
{ .str = NULL }, .flags =
FLAGS
},
93
{
"b"
,
"set the brightness expression"
,
OFFSET
(brightness_expr),
AV_OPT_TYPE_STRING
,
94
{ .str =
"0"
}, .flags =
FLAGS
},
95
{ NULL }
96
};
97
98
AVFILTER_DEFINE_CLASS
(hue);
99
100
static
inline
void
compute_sin_and_cos
(
HueContext
*hue)
101
{
102
/*
103
* Scale the value to the norm of the resulting (U,V) vector, that is
104
* the saturation.
105
* This will be useful in the apply_lut function.
106
*/
107
hue->
hue_sin
=
rint
(sin(hue->
hue
) * (1 << 16) * hue->
saturation
);
108
hue->
hue_cos
=
rint
(cos(hue->
hue
) * (1 << 16) * hue->
saturation
);
109
}
110
111
static
inline
void
create_luma_lut
(
HueContext
*h)
112
{
113
const
float
b
= h->
brightness
;
114
int
i;
115
116
for
(i = 0; i < 256; i++) {
117
h->
lut_l
[i] = av_clip_uint8(i + b * 25.5);
118
}
119
}
120
121
static
inline
void
create_chrominance_lut
(
HueContext
*h,
const
int32_t
c
,
122
const
int32_t
s
)
123
{
124
int32_t
i, j,
u
,
v
, new_u, new_v;
125
126
/*
127
* If we consider U and V as the components of a 2D vector then its angle
128
* is the hue and the norm is the saturation
129
*/
130
for
(i = 0; i < 256; i++) {
131
for
(j = 0; j < 256; j++) {
132
/* Normalize the components from range [16;140] to [-112;112] */
133
u = i - 128;
134
v = j - 128;
135
/*
136
* Apply the rotation of the vector : (c * u) - (s * v)
137
* (s * u) + (c * v)
138
* De-normalize the components (without forgetting to scale 128
139
* by << 16)
140
* Finally scale back the result by >> 16
141
*/
142
new_u = ((c *
u
) - (s * v) + (1 << 15) + (128 << 16)) >> 16;
143
new_v = ((s *
u
) + (c * v) + (1 << 15) + (128 << 16)) >> 16;
144
145
/* Prevent a potential overflow */
146
h->
lut_u
[i][j] =
av_clip_uint8_c
(new_u);
147
h->
lut_v
[i][j] =
av_clip_uint8_c
(new_v);
148
}
149
}
150
}
151
152
static
int
set_expr
(
AVExpr
**pexpr_ptr,
char
**expr_ptr,
153
const
char
*expr,
const
char
*
option
,
void
*log_ctx)
154
{
155
int
ret
;
156
AVExpr
*new_pexpr;
157
char
*new_expr;
158
159
new_expr =
av_strdup
(expr);
160
if
(!new_expr)
161
return
AVERROR
(ENOMEM);
162
ret =
av_expr_parse
(&new_pexpr, expr,
var_names
,
163
NULL, NULL, NULL, NULL, 0, log_ctx);
164
if
(ret < 0) {
165
av_log
(log_ctx,
AV_LOG_ERROR
,
166
"Error when evaluating the expression '%s' for %s\n"
,
167
expr, option);
168
av_free
(new_expr);
169
return
ret
;
170
}
171
172
if
(*pexpr_ptr)
173
av_expr_free
(*pexpr_ptr);
174
*pexpr_ptr = new_pexpr;
175
av_freep
(expr_ptr);
176
*expr_ptr = new_expr;
177
178
return
0;
179
}
180
181
static
av_cold
int
init
(
AVFilterContext
*ctx)
182
{
183
HueContext
*hue = ctx->
priv
;
184
int
ret
;
185
186
if
(hue->
hue_expr
&& hue->
hue_deg_expr
) {
187
av_log
(ctx,
AV_LOG_ERROR
,
188
"H and h options are incompatible and cannot be specified "
189
"at the same time\n"
);
190
return
AVERROR
(EINVAL);
191
}
192
193
#define SET_EXPR(expr, option) \
194
if (hue->expr##_expr) do { \
195
ret = set_expr(&hue->expr##_pexpr, &hue->expr##_expr, \
196
hue->expr##_expr, option, ctx); \
197
if (ret < 0) \
198
return ret; \
199
} while (0)
200
SET_EXPR
(brightness,
"b"
);
201
SET_EXPR
(saturation,
"s"
);
202
SET_EXPR
(hue_deg,
"h"
);
203
SET_EXPR
(hue,
"H"
);
204
#undef SET_EXPR
205
206
av_log
(ctx,
AV_LOG_VERBOSE
,
207
"H_expr:%s h_deg_expr:%s s_expr:%s b_expr:%s\n"
,
208
hue->
hue_expr
, hue->
hue_deg_expr
, hue->
saturation_expr
, hue->
brightness_expr
);
209
compute_sin_and_cos
(hue);
210
211
return
0;
212
}
213
214
static
av_cold
void
uninit
(
AVFilterContext
*ctx)
215
{
216
HueContext
*hue = ctx->
priv
;
217
218
av_expr_free
(hue->
brightness_pexpr
);
219
av_expr_free
(hue->
hue_deg_pexpr
);
220
av_expr_free
(hue->
hue_pexpr
);
221
av_expr_free
(hue->
saturation_pexpr
);
222
}
223
224
static
int
query_formats
(
AVFilterContext
*ctx)
225
{
226
static
const
enum
AVPixelFormat
pix_fmts[] = {
227
AV_PIX_FMT_YUV444P
,
AV_PIX_FMT_YUV422P
,
228
AV_PIX_FMT_YUV420P
,
AV_PIX_FMT_YUV411P
,
229
AV_PIX_FMT_YUV410P
,
AV_PIX_FMT_YUV440P
,
230
AV_PIX_FMT_YUVA444P
,
AV_PIX_FMT_YUVA422P
,
231
AV_PIX_FMT_YUVA420P
,
232
AV_PIX_FMT_NONE
233
};
234
235
ff_set_common_formats
(ctx,
ff_make_format_list
(pix_fmts));
236
237
return
0;
238
}
239
240
static
int
config_props
(
AVFilterLink
*inlink)
241
{
242
HueContext
*hue = inlink->
dst
->
priv
;
243
const
AVPixFmtDescriptor
*desc =
av_pix_fmt_desc_get
(inlink->
format
);
244
245
hue->
hsub
= desc->
log2_chroma_w
;
246
hue->
vsub
= desc->
log2_chroma_h
;
247
248
hue->
var_values
[
VAR_N
] = 0;
249
hue->
var_values
[
VAR_TB
] =
av_q2d
(inlink->
time_base
);
250
hue->
var_values
[
VAR_R
] = inlink->
frame_rate
.
num
== 0 || inlink->
frame_rate
.
den
== 0 ?
251
NAN
:
av_q2d
(inlink->
frame_rate
);
252
253
return
0;
254
}
255
256
static
void
apply_luma_lut
(
HueContext
*
s
,
257
uint8_t
*ldst,
const
int
dst_linesize,
258
uint8_t
*lsrc,
const
int
src_linesize,
259
int
w,
int
h)
260
{
261
int
i;
262
263
while
(h--) {
264
for
(i = 0; i < w; i++)
265
ldst[i] = s->
lut_l
[lsrc[i]];
266
267
lsrc += src_linesize;
268
ldst += dst_linesize;
269
}
270
}
271
272
static
void
apply_lut
(
HueContext
*
s
,
273
uint8_t
*udst,
uint8_t
*vdst,
const
int
dst_linesize,
274
uint8_t
*usrc,
uint8_t
*vsrc,
const
int
src_linesize,
275
int
w,
int
h)
276
{
277
int
i;
278
279
while
(h--) {
280
for
(i = 0; i < w; i++) {
281
const
int
u
= usrc[i];
282
const
int
v
= vsrc[i];
283
284
udst[i] = s->
lut_u
[
u
][
v
];
285
vdst[i] = s->
lut_v
[
u
][
v
];
286
}
287
288
usrc += src_linesize;
289
vsrc += src_linesize;
290
udst += dst_linesize;
291
vdst += dst_linesize;
292
}
293
}
294
295
#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
296
#define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts) * av_q2d(tb))
297
298
static
int
filter_frame
(
AVFilterLink
*inlink,
AVFrame
*
inpic
)
299
{
300
HueContext
*hue = inlink->
dst
->
priv
;
301
AVFilterLink
*outlink = inlink->
dst
->
outputs
[0];
302
AVFrame
*outpic;
303
const
int32_t
old_hue_sin = hue->
hue_sin
, old_hue_cos = hue->
hue_cos
;
304
const
float
old_brightness = hue->
brightness
;
305
int
direct = 0;
306
307
if
(
av_frame_is_writable
(inpic)) {
308
direct = 1;
309
outpic =
inpic
;
310
}
else
{
311
outpic =
ff_get_video_buffer
(outlink, outlink->
w
, outlink->
h
);
312
if
(!outpic) {
313
av_frame_free
(&inpic);
314
return
AVERROR
(ENOMEM);
315
}
316
av_frame_copy_props
(outpic, inpic);
317
}
318
319
hue->
var_values
[
VAR_N
] = inlink->
frame_count
;
320
hue->
var_values
[
VAR_T
] =
TS2T
(inpic->
pts
, inlink->
time_base
);
321
hue->
var_values
[
VAR_PTS
] =
TS2D
(inpic->
pts
);
322
323
if
(hue->
saturation_expr
) {
324
hue->
saturation
=
av_expr_eval
(hue->
saturation_pexpr
, hue->
var_values
, NULL);
325
326
if
(hue->
saturation
<
SAT_MIN_VAL
|| hue->
saturation
>
SAT_MAX_VAL
) {
327
hue->
saturation
= av_clip(hue->
saturation
,
SAT_MIN_VAL
,
SAT_MAX_VAL
);
328
av_log
(inlink->
dst
,
AV_LOG_WARNING
,
329
"Saturation value not in range [%d,%d]: clipping value to %0.1f\n"
,
330
SAT_MIN_VAL
,
SAT_MAX_VAL
, hue->
saturation
);
331
}
332
}
333
334
if
(hue->
brightness_expr
) {
335
hue->
brightness
=
av_expr_eval
(hue->
brightness_pexpr
, hue->
var_values
, NULL);
336
337
if
(hue->
brightness
< -10 || hue->
brightness
> 10) {
338
hue->
brightness
= av_clipf(hue->
brightness
, -10, 10);
339
av_log
(inlink->
dst
,
AV_LOG_WARNING
,
340
"Brightness value not in range [%d,%d]: clipping value to %0.1f\n"
,
341
-10, 10, hue->
brightness
);
342
}
343
}
344
345
if
(hue->
hue_deg_expr
) {
346
hue->
hue_deg
=
av_expr_eval
(hue->
hue_deg_pexpr
, hue->
var_values
, NULL);
347
hue->
hue
= hue->
hue_deg
*
M_PI
/ 180;
348
}
else
if
(hue->
hue_expr
) {
349
hue->
hue
=
av_expr_eval
(hue->
hue_pexpr
, hue->
var_values
, NULL);
350
hue->
hue_deg
= hue->
hue
* 180 /
M_PI
;
351
}
352
353
av_log
(inlink->
dst
,
AV_LOG_DEBUG
,
354
"H:%0.1f*PI h:%0.1f s:%0.f b:%0.f t:%0.1f n:%d\n"
,
355
hue->
hue
/
M_PI
, hue->
hue_deg
, hue->
saturation
, hue->
brightness
,
356
hue->
var_values
[
VAR_T
], (
int
)hue->
var_values
[
VAR_N
]);
357
358
compute_sin_and_cos
(hue);
359
if
(old_hue_sin != hue->
hue_sin
|| old_hue_cos != hue->
hue_cos
)
360
create_chrominance_lut
(hue, hue->
hue_cos
, hue->
hue_sin
);
361
362
if
(old_brightness != hue->
brightness
&& hue->
brightness
)
363
create_luma_lut
(hue);
364
365
if
(!direct) {
366
if
(!hue->
brightness
)
367
av_image_copy_plane
(outpic->
data
[0], outpic->
linesize
[0],
368
inpic->
data
[0], inpic->
linesize
[0],
369
inlink->
w
, inlink->
h
);
370
if
(inpic->
data
[3])
371
av_image_copy_plane
(outpic->
data
[3], outpic->
linesize
[3],
372
inpic->
data
[3], inpic->
linesize
[3],
373
inlink->
w
, inlink->
h
);
374
}
375
376
apply_lut
(hue, outpic->
data
[1], outpic->
data
[2], outpic->
linesize
[1],
377
inpic->
data
[1], inpic->
data
[2], inpic->
linesize
[1],
378
FF_CEIL_RSHIFT
(inlink->
w
, hue->
hsub
),
379
FF_CEIL_RSHIFT
(inlink->
h
, hue->
vsub
));
380
if
(hue->
brightness
)
381
apply_luma_lut
(hue, outpic->
data
[0], outpic->
linesize
[0],
382
inpic->
data
[0], inpic->
linesize
[0], inlink->
w
, inlink->
h
);
383
384
if
(!direct)
385
av_frame_free
(&inpic);
386
return
ff_filter_frame
(outlink, outpic);
387
}
388
389
static
int
process_command
(
AVFilterContext
*ctx,
const
char
*cmd,
const
char
*
args
,
390
char
*
res
,
int
res_len,
int
flags
)
391
{
392
HueContext
*hue = ctx->
priv
;
393
int
ret
;
394
395
#define SET_EXPR(expr, option) \
396
do { \
397
ret = set_expr(&hue->expr##_pexpr, &hue->expr##_expr, \
398
args, option, ctx); \
399
if (ret < 0) \
400
return ret; \
401
} while (0)
402
403
if
(!strcmp(cmd,
"h"
)) {
404
SET_EXPR
(hue_deg,
"h"
);
405
av_freep
(&hue->
hue_expr
);
406
}
else
if
(!strcmp(cmd,
"H"
)) {
407
SET_EXPR
(hue,
"H"
);
408
av_freep
(&hue->
hue_deg_expr
);
409
}
else
if
(!strcmp(cmd,
"s"
)) {
410
SET_EXPR
(saturation,
"s"
);
411
}
else
if
(!strcmp(cmd,
"b"
)) {
412
SET_EXPR
(brightness,
"b"
);
413
}
else
414
return
AVERROR
(ENOSYS);
415
416
return
0;
417
}
418
419
static
const
AVFilterPad
hue_inputs
[] = {
420
{
421
.
name
=
"default"
,
422
.type =
AVMEDIA_TYPE_VIDEO
,
423
.filter_frame =
filter_frame
,
424
.config_props =
config_props
,
425
},
426
{ NULL }
427
};
428
429
static
const
AVFilterPad
hue_outputs
[] = {
430
{
431
.
name
=
"default"
,
432
.type =
AVMEDIA_TYPE_VIDEO
,
433
},
434
{ NULL }
435
};
436
437
AVFilter
avfilter_vf_hue
= {
438
.
name
=
"hue"
,
439
.description =
NULL_IF_CONFIG_SMALL
(
"Adjust the hue and saturation of the input video."
),
440
.priv_size =
sizeof
(
HueContext
),
441
.
init
=
init
,
442
.
uninit
=
uninit
,
443
.
query_formats
=
query_formats
,
444
.
process_command
=
process_command
,
445
.
inputs
= hue_inputs,
446
.
outputs
= hue_outputs,
447
.priv_class = &hue_class,
448
.
flags
=
AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,
449
};
Generated on Sat Jan 25 2014 19:52:00 for FFmpeg by
1.8.2