2 * Copyright (c) 2012 Jeremy Tran
3 * Copyright (c) 2001 Donald A. Graft
5 * This file is part of FFmpeg.
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.
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.
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.
24 * Histogram equalization filter, based on the VirtualDub filter by
25 * Donald A. Graft <neuron2 AT home DOT com>.
26 * Implements global automatic contrast adjustment by means of
27 * histogram equalization.
30 #include "libavutil/common.h"
31 #include "libavutil/opt.h"
32 #include "libavutil/pixdesc.h"
35 #include "drawutils.h"
42 // Linear Congruential Generator, see "Numerical Recipes"
46 #define LCG(x) (((x) * LCG_A + LCG_C) % LCG_M)
47 #define LCG_SEED 739187
49 enum HisteqAntibanding
{
50 HISTEQ_ANTIBANDING_NONE
= 0,
51 HISTEQ_ANTIBANDING_WEAK
= 1,
52 HISTEQ_ANTIBANDING_STRONG
= 2,
53 HISTEQ_ANTIBANDING_NB
,
60 enum HisteqAntibanding antibanding
;
61 int in_histogram
[256]; ///< input histogram
62 int out_histogram
[256]; ///< output histogram
63 int LUT
[256]; ///< lookup table derived from histogram[]
64 uint8_t rgba_map
[4]; ///< components position
65 int bpp
; ///< bytes per pixel
68 #define OFFSET(x) offsetof(HisteqContext, x)
69 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
70 #define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, unit }
72 static const AVOption histeq_options
[] = {
73 { "strength", "set the strength", OFFSET(strength
), AV_OPT_TYPE_FLOAT
, {.dbl
=0.2}, 0, 1, FLAGS
},
74 { "intensity", "set the intensity", OFFSET(intensity
), AV_OPT_TYPE_FLOAT
, {.dbl
=0.21}, 0, 1, FLAGS
},
75 { "antibanding", "set the antibanding level", OFFSET(antibanding
), AV_OPT_TYPE_INT
, {.i64
=HISTEQ_ANTIBANDING_NONE
}, 0, HISTEQ_ANTIBANDING_NB
-1, FLAGS
, "antibanding" },
76 CONST("none", "apply no antibanding", HISTEQ_ANTIBANDING_NONE
, "antibanding"),
77 CONST("weak", "apply weak antibanding", HISTEQ_ANTIBANDING_WEAK
, "antibanding"),
78 CONST("strong", "apply strong antibanding", HISTEQ_ANTIBANDING_STRONG
, "antibanding"),
82 AVFILTER_DEFINE_CLASS(histeq
);
84 static av_cold
int init(AVFilterContext
*ctx
)
86 HisteqContext
*histeq
= ctx
->priv
;
88 av_log(ctx
, AV_LOG_VERBOSE
,
89 "strength:%0.3f intensity:%0.3f antibanding:%d\n",
90 histeq
->strength
, histeq
->intensity
, histeq
->antibanding
);
95 static int query_formats(AVFilterContext
*ctx
)
97 static const enum PixelFormat pix_fmts
[] = {
98 AV_PIX_FMT_ARGB
, AV_PIX_FMT_RGBA
, AV_PIX_FMT_ABGR
, AV_PIX_FMT_BGRA
,
99 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
,
103 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
107 static int config_input(AVFilterLink
*inlink
)
109 AVFilterContext
*ctx
= inlink
->dst
;
110 HisteqContext
*histeq
= ctx
->priv
;
111 const AVPixFmtDescriptor
*pix_desc
= av_pix_fmt_desc_get(inlink
->format
);
113 histeq
->bpp
= av_get_bits_per_pixel(pix_desc
) / 8;
114 ff_fill_rgba_map(histeq
->rgba_map
, inlink
->format
);
124 #define GET_RGB_VALUES(r, g, b, src, map) do { \
125 r = src[x + map[R]]; \
126 g = src[x + map[G]]; \
127 b = src[x + map[B]]; \
130 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*inpic
)
132 AVFilterContext
*ctx
= inlink
->dst
;
133 HisteqContext
*histeq
= ctx
->priv
;
134 AVFilterLink
*outlink
= ctx
->outputs
[0];
135 int strength
= histeq
->strength
* 1000;
136 int intensity
= histeq
->intensity
* 1000;
137 int x
, y
, i
, luthi
, lutlo
, lut
, luma
, oluma
, m
;
139 unsigned int r
, g
, b
, jran
;
142 outpic
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
144 av_frame_free(&inpic
);
145 return AVERROR(ENOMEM
);
147 av_frame_copy_props(outpic
, inpic
);
149 /* Seed random generator for antibanding. */
152 /* Calculate and store the luminance and calculate the global histogram
153 based on the luminance. */
154 memset(histeq
->in_histogram
, 0, sizeof(histeq
->in_histogram
));
155 src
= inpic
->data
[0];
156 dst
= outpic
->data
[0];
157 for (y
= 0; y
< inlink
->h
; y
++) {
158 for (x
= 0; x
< inlink
->w
* histeq
->bpp
; x
+= histeq
->bpp
) {
159 GET_RGB_VALUES(r
, g
, b
, src
, histeq
->rgba_map
);
160 luma
= (55 * r
+ 182 * g
+ 19 * b
) >> 8;
161 dst
[x
+ histeq
->rgba_map
[A
]] = luma
;
162 histeq
->in_histogram
[luma
]++;
164 src
+= inpic
->linesize
[0];
165 dst
+= outpic
->linesize
[0];
169 for (x
= 0; x
< 256; x
++)
170 av_dlog(ctx
, "in[%d]: %u\n", x
, histeq
->in_histogram
[x
]);
173 /* Calculate the lookup table. */
174 histeq
->LUT
[0] = histeq
->in_histogram
[0];
176 for (x
= 1; x
< 256; x
++)
177 histeq
->LUT
[x
] = histeq
->LUT
[x
-1] + histeq
->in_histogram
[x
];
180 for (x
= 0; x
< 256; x
++)
181 histeq
->LUT
[x
] = (histeq
->LUT
[x
] * intensity
) / (inlink
->h
* inlink
->w
);
183 /* Adjust the LUT based on the selected strength. This is an alpha
184 mix of the calculated LUT and a linear LUT with gain 1. */
185 for (x
= 0; x
< 256; x
++)
186 histeq
->LUT
[x
] = (strength
* histeq
->LUT
[x
]) / 255 +
187 ((255 - strength
) * x
) / 255;
189 /* Output the equalized frame. */
190 memset(histeq
->out_histogram
, 0, sizeof(histeq
->out_histogram
));
192 src
= inpic
->data
[0];
193 dst
= outpic
->data
[0];
194 for (y
= 0; y
< inlink
->h
; y
++) {
195 for (x
= 0; x
< inlink
->w
* histeq
->bpp
; x
+= histeq
->bpp
) {
196 luma
= dst
[x
+ histeq
->rgba_map
[A
]];
198 for (i
= 0; i
< histeq
->bpp
; ++i
)
200 histeq
->out_histogram
[0]++;
202 lut
= histeq
->LUT
[luma
];
203 if (histeq
->antibanding
!= HISTEQ_ANTIBANDING_NONE
) {
205 lutlo
= histeq
->antibanding
== HISTEQ_ANTIBANDING_WEAK
?
206 (histeq
->LUT
[luma
] + histeq
->LUT
[luma
- 1]) / 2 :
207 histeq
->LUT
[luma
- 1];
212 luthi
= (histeq
->antibanding
== HISTEQ_ANTIBANDING_WEAK
) ?
213 (histeq
->LUT
[luma
] + histeq
->LUT
[luma
+ 1]) / 2 :
214 histeq
->LUT
[luma
+ 1];
218 if (lutlo
!= luthi
) {
220 lut
= lutlo
+ ((luthi
- lutlo
+ 1) * jran
) / LCG_M
;
224 GET_RGB_VALUES(r
, g
, b
, src
, histeq
->rgba_map
);
225 if (((m
= FFMAX3(r
, g
, b
)) * lut
) / luma
> 255) {
230 r
= (r
* lut
) / luma
;
231 g
= (g
* lut
) / luma
;
232 b
= (b
* lut
) / luma
;
234 dst
[x
+ histeq
->rgba_map
[R
]] = r
;
235 dst
[x
+ histeq
->rgba_map
[G
]] = g
;
236 dst
[x
+ histeq
->rgba_map
[B
]] = b
;
237 oluma
= av_clip_uint8((55 * r
+ 182 * g
+ 19 * b
) >> 8);
238 histeq
->out_histogram
[oluma
]++;
241 src
+= inpic
->linesize
[0];
242 dst
+= outpic
->linesize
[0];
245 for (x
= 0; x
< 256; x
++)
246 av_dlog(ctx
, "out[%d]: %u\n", x
, histeq
->out_histogram
[x
]);
249 av_frame_free(&inpic
);
250 return ff_filter_frame(outlink
, outpic
);
253 static const AVFilterPad histeq_inputs
[] = {
256 .type
= AVMEDIA_TYPE_VIDEO
,
257 .config_props
= config_input
,
258 .filter_frame
= filter_frame
,
263 static const AVFilterPad histeq_outputs
[] = {
266 .type
= AVMEDIA_TYPE_VIDEO
,
271 AVFilter ff_vf_histeq
= {
273 .description
= NULL_IF_CONFIG_SMALL("Apply global color histogram equalization."),
274 .priv_size
= sizeof(HisteqContext
),
276 .query_formats
= query_formats
,
277 .inputs
= histeq_inputs
,
278 .outputs
= histeq_outputs
,
279 .priv_class
= &histeq_class
,
280 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,