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