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 modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (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
15
* GNU General Public License for more details.
16
*
17
* You should have received a copy of the GNU General Public License along
18
* with FFmpeg; if not, write to the Free Software Foundation, Inc.,
19
* 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
int
hsub
;
72
int
vsub
;
73
int32_t
hue_sin
;
74
int32_t
hue_cos
;
75
double
var_values[
VAR_NB
];
76
}
HueContext
;
77
78
#define OFFSET(x) offsetof(HueContext, x)
79
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
80
static
const
AVOption
hue_options
[] = {
81
{
"h"
,
"set the hue angle degrees expression"
,
OFFSET
(hue_deg_expr),
AV_OPT_TYPE_STRING
,
82
{ .str = NULL }, .flags =
FLAGS
},
83
{
"s"
,
"set the saturation expression"
,
OFFSET
(saturation_expr),
AV_OPT_TYPE_STRING
,
84
{ .str =
"1"
}, .flags =
FLAGS
},
85
{
"H"
,
"set the hue angle radians expression"
,
OFFSET
(hue_expr),
AV_OPT_TYPE_STRING
,
86
{ .str = NULL }, .flags =
FLAGS
},
87
{ NULL }
88
};
89
90
AVFILTER_DEFINE_CLASS
(hue);
91
92
static
inline
void
compute_sin_and_cos
(
HueContext
*hue)
93
{
94
/*
95
* Scale the value to the norm of the resulting (U,V) vector, that is
96
* the saturation.
97
* This will be useful in the process_chrominance function.
98
*/
99
hue->
hue_sin
=
rint
(sin(hue->
hue
) * (1 << 16) * hue->
saturation
);
100
hue->
hue_cos
=
rint
(cos(hue->
hue
) * (1 << 16) * hue->
saturation
);
101
}
102
103
static
int
set_expr
(
AVExpr
**pexpr_ptr,
char
**expr_ptr,
104
const
char
*expr,
const
char
*
option
,
void
*log_ctx)
105
{
106
int
ret
;
107
AVExpr
*new_pexpr;
108
char
*new_expr;
109
110
new_expr =
av_strdup
(expr);
111
if
(!new_expr)
112
return
AVERROR
(ENOMEM);
113
ret =
av_expr_parse
(&new_pexpr, expr,
var_names
,
114
NULL, NULL, NULL, NULL, 0, log_ctx);
115
if
(ret < 0) {
116
av_log
(log_ctx,
AV_LOG_ERROR
,
117
"Error when evaluating the expression '%s' for %s\n"
,
118
expr, option);
119
av_free
(new_expr);
120
return
ret
;
121
}
122
123
if
(*pexpr_ptr)
124
av_expr_free
(*pexpr_ptr);
125
*pexpr_ptr = new_pexpr;
126
av_freep
(expr_ptr);
127
*expr_ptr = new_expr;
128
129
return
0;
130
}
131
132
static
av_cold
int
init
(
AVFilterContext
*ctx)
133
{
134
HueContext
*hue = ctx->
priv
;
135
int
ret
;
136
137
if
(hue->
hue_expr
&& hue->
hue_deg_expr
) {
138
av_log
(ctx,
AV_LOG_ERROR
,
139
"H and h options are incompatible and cannot be specified "
140
"at the same time\n"
);
141
return
AVERROR
(EINVAL);
142
}
143
144
#define SET_EXPR(expr, option) \
145
if (hue->expr##_expr) do { \
146
ret = set_expr(&hue->expr##_pexpr, &hue->expr##_expr, \
147
hue->expr##_expr, option, ctx); \
148
if (ret < 0) \
149
return ret; \
150
} while (0)
151
SET_EXPR
(saturation,
"s"
);
152
SET_EXPR
(hue_deg,
"h"
);
153
SET_EXPR
(hue,
"H"
);
154
#undef SET_EXPR
155
156
av_log
(ctx,
AV_LOG_VERBOSE
,
157
"H_expr:%s h_deg_expr:%s s_expr:%s\n"
,
158
hue->
hue_expr
, hue->
hue_deg_expr
, hue->
saturation_expr
);
159
compute_sin_and_cos
(hue);
160
161
return
0;
162
}
163
164
static
av_cold
void
uninit
(
AVFilterContext
*ctx)
165
{
166
HueContext
*hue = ctx->
priv
;
167
168
av_expr_free
(hue->
hue_deg_pexpr
);
169
av_expr_free
(hue->
hue_pexpr
);
170
av_expr_free
(hue->
saturation_pexpr
);
171
}
172
173
static
int
query_formats
(
AVFilterContext
*ctx)
174
{
175
static
const
enum
AVPixelFormat
pix_fmts[] = {
176
AV_PIX_FMT_YUV444P
,
AV_PIX_FMT_YUV422P
,
177
AV_PIX_FMT_YUV420P
,
AV_PIX_FMT_YUV411P
,
178
AV_PIX_FMT_YUV410P
,
AV_PIX_FMT_YUV440P
,
179
AV_PIX_FMT_YUVA444P
,
AV_PIX_FMT_YUVA422P
,
180
AV_PIX_FMT_YUVA420P
,
181
AV_PIX_FMT_NONE
182
};
183
184
ff_set_common_formats
(ctx,
ff_make_format_list
(pix_fmts));
185
186
return
0;
187
}
188
189
static
int
config_props
(
AVFilterLink
*inlink)
190
{
191
HueContext
*hue = inlink->
dst
->
priv
;
192
const
AVPixFmtDescriptor
*desc =
av_pix_fmt_desc_get
(inlink->
format
);
193
194
hue->
hsub
= desc->
log2_chroma_w
;
195
hue->
vsub
= desc->
log2_chroma_h
;
196
197
hue->
var_values
[
VAR_N
] = 0;
198
hue->
var_values
[
VAR_TB
] =
av_q2d
(inlink->
time_base
);
199
hue->
var_values
[
VAR_R
] = inlink->
frame_rate
.
num
== 0 || inlink->
frame_rate
.
den
== 0 ?
200
NAN
:
av_q2d
(inlink->
frame_rate
);
201
202
return
0;
203
}
204
205
static
void
process_chrominance
(
uint8_t
*udst,
uint8_t
*vdst,
const
int
dst_linesize,
206
uint8_t
*usrc,
uint8_t
*vsrc,
const
int
src_linesize,
207
int
w,
int
h,
208
const
int32_t
c
,
const
int32_t
s
)
209
{
210
int32_t
u
,
v
, new_u, new_v;
211
int
i;
212
213
/*
214
* If we consider U and V as the components of a 2D vector then its angle
215
* is the hue and the norm is the saturation
216
*/
217
while
(h--) {
218
for
(i = 0; i < w; i++) {
219
/* Normalize the components from range [16;140] to [-112;112] */
220
u = usrc[i] - 128;
221
v = vsrc[i] - 128;
222
/*
223
* Apply the rotation of the vector : (c * u) - (s * v)
224
* (s * u) + (c * v)
225
* De-normalize the components (without forgetting to scale 128
226
* by << 16)
227
* Finally scale back the result by >> 16
228
*/
229
new_u = ((c *
u
) - (s * v) + (1 << 15) + (128 << 16)) >> 16;
230
new_v = ((s *
u
) + (c * v) + (1 << 15) + (128 << 16)) >> 16;
231
232
/* Prevent a potential overflow */
233
udst[i] =
av_clip_uint8_c
(new_u);
234
vdst[i] =
av_clip_uint8_c
(new_v);
235
}
236
237
usrc += src_linesize;
238
vsrc += src_linesize;
239
udst += dst_linesize;
240
vdst += dst_linesize;
241
}
242
}
243
244
#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
245
#define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts) * av_q2d(tb))
246
247
static
int
filter_frame
(
AVFilterLink
*inlink,
AVFrame
*
inpic
)
248
{
249
HueContext
*hue = inlink->
dst
->
priv
;
250
AVFilterLink
*outlink = inlink->
dst
->
outputs
[0];
251
AVFrame
*outpic;
252
int
direct = 0;
253
254
if
(
av_frame_is_writable
(inpic)) {
255
direct = 1;
256
outpic =
inpic
;
257
}
else
{
258
outpic =
ff_get_video_buffer
(outlink, outlink->
w
, outlink->
h
);
259
if
(!outpic) {
260
av_frame_free
(&inpic);
261
return
AVERROR
(ENOMEM);
262
}
263
av_frame_copy_props
(outpic, inpic);
264
}
265
266
hue->
var_values
[
VAR_N
] = inlink->
frame_count
;
267
hue->
var_values
[
VAR_T
] =
TS2T
(inpic->
pts
, inlink->
time_base
);
268
hue->
var_values
[
VAR_PTS
] =
TS2D
(inpic->
pts
);
269
270
if
(hue->
saturation_expr
) {
271
hue->
saturation
=
av_expr_eval
(hue->
saturation_pexpr
, hue->
var_values
, NULL);
272
273
if
(hue->
saturation
<
SAT_MIN_VAL
|| hue->
saturation
>
SAT_MAX_VAL
) {
274
hue->
saturation
= av_clip(hue->
saturation
,
SAT_MIN_VAL
,
SAT_MAX_VAL
);
275
av_log
(inlink->
dst
,
AV_LOG_WARNING
,
276
"Saturation value not in range [%d,%d]: clipping value to %0.1f\n"
,
277
SAT_MIN_VAL
,
SAT_MAX_VAL
, hue->
saturation
);
278
}
279
}
280
281
if
(hue->
hue_deg_expr
) {
282
hue->
hue_deg
=
av_expr_eval
(hue->
hue_deg_pexpr
, hue->
var_values
, NULL);
283
hue->
hue
= hue->
hue_deg
*
M_PI
/ 180;
284
}
else
if
(hue->
hue_expr
) {
285
hue->
hue
=
av_expr_eval
(hue->
hue_pexpr
, hue->
var_values
, NULL);
286
hue->
hue_deg
= hue->
hue
* 180 /
M_PI
;
287
}
288
289
av_log
(inlink->
dst
,
AV_LOG_DEBUG
,
290
"H:%0.1f*PI h:%0.1f s:%0.f t:%0.1f n:%d\n"
,
291
hue->
hue
/
M_PI
, hue->
hue_deg
, hue->
saturation
,
292
hue->
var_values
[
VAR_T
], (
int
)hue->
var_values
[
VAR_N
]);
293
294
compute_sin_and_cos
(hue);
295
296
if
(!direct) {
297
av_image_copy_plane
(outpic->
data
[0], outpic->
linesize
[0],
298
inpic->
data
[0], inpic->
linesize
[0],
299
inlink->
w
, inlink->
h
);
300
if
(inpic->
data
[3])
301
av_image_copy_plane
(outpic->
data
[3], outpic->
linesize
[3],
302
inpic->
data
[3], inpic->
linesize
[3],
303
inlink->
w
, inlink->
h
);
304
}
305
306
process_chrominance
(outpic->
data
[1], outpic->
data
[2], outpic->
linesize
[1],
307
inpic->
data
[1], inpic->
data
[2], inpic->
linesize
[1],
308
FF_CEIL_RSHIFT
(inlink->
w
, hue->
hsub
),
309
FF_CEIL_RSHIFT
(inlink->
h
, hue->
vsub
),
310
hue->
hue_cos
, hue->
hue_sin
);
311
312
if
(!direct)
313
av_frame_free
(&inpic);
314
return
ff_filter_frame
(outlink, outpic);
315
}
316
317
static
int
process_command
(
AVFilterContext
*ctx,
const
char
*cmd,
const
char
*
args
,
318
char
*
res
,
int
res_len,
int
flags
)
319
{
320
HueContext
*hue = ctx->
priv
;
321
int
ret
;
322
323
#define SET_EXPR(expr, option) \
324
do { \
325
ret = set_expr(&hue->expr##_pexpr, &hue->expr##_expr, \
326
args, option, ctx); \
327
if (ret < 0) \
328
return ret; \
329
} while (0)
330
331
if
(!strcmp(cmd,
"h"
)) {
332
SET_EXPR
(hue_deg,
"h"
);
333
av_freep
(&hue->
hue_expr
);
334
}
else
if
(!strcmp(cmd,
"H"
)) {
335
SET_EXPR
(hue,
"H"
);
336
av_freep
(&hue->
hue_deg_expr
);
337
}
else
if
(!strcmp(cmd,
"s"
)) {
338
SET_EXPR
(saturation,
"s"
);
339
}
else
340
return
AVERROR
(ENOSYS);
341
342
return
0;
343
}
344
345
static
const
AVFilterPad
hue_inputs
[] = {
346
{
347
.
name
=
"default"
,
348
.type =
AVMEDIA_TYPE_VIDEO
,
349
.filter_frame =
filter_frame
,
350
.config_props =
config_props
,
351
},
352
{ NULL }
353
};
354
355
static
const
AVFilterPad
hue_outputs
[] = {
356
{
357
.
name
=
"default"
,
358
.type =
AVMEDIA_TYPE_VIDEO
,
359
},
360
{ NULL }
361
};
362
363
AVFilter
avfilter_vf_hue
= {
364
.
name
=
"hue"
,
365
.description =
NULL_IF_CONFIG_SMALL
(
"Adjust the hue and saturation of the input video."
),
366
367
.priv_size =
sizeof
(
HueContext
),
368
369
.
init
=
init
,
370
.
uninit
=
uninit
,
371
.
query_formats
=
query_formats
,
372
.
process_command
=
process_command
,
373
.
inputs
= hue_inputs,
374
.
outputs
= hue_outputs,
375
.priv_class = &hue_class,
376
.
flags
=
AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,
377
};
Generated on Wed Jul 10 2013 23:48:10 for FFmpeg by
1.8.2