yading@10
|
1 /*
|
yading@10
|
2 * Copyright (c) 2012 Jeremy Tran
|
yading@10
|
3 * Copyright (c) 2001 Donald A. Graft
|
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 * Histogram equalization filter, based on the VirtualDub filter by
|
yading@10
|
25 * Donald A. Graft <neuron2 AT home DOT com>.
|
yading@10
|
26 * Implements global automatic contrast adjustment by means of
|
yading@10
|
27 * histogram equalization.
|
yading@10
|
28 */
|
yading@10
|
29
|
yading@10
|
30 #include "libavutil/common.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 "drawutils.h"
|
yading@10
|
36 #include "formats.h"
|
yading@10
|
37 #include "internal.h"
|
yading@10
|
38 #include "video.h"
|
yading@10
|
39
|
yading@10
|
40 // #define DEBUG
|
yading@10
|
41
|
yading@10
|
42 // Linear Congruential Generator, see "Numerical Recipes"
|
yading@10
|
43 #define LCG_A 4096
|
yading@10
|
44 #define LCG_C 150889
|
yading@10
|
45 #define LCG_M 714025
|
yading@10
|
46 #define LCG(x) (((x) * LCG_A + LCG_C) % LCG_M)
|
yading@10
|
47 #define LCG_SEED 739187
|
yading@10
|
48
|
yading@10
|
49 enum HisteqAntibanding {
|
yading@10
|
50 HISTEQ_ANTIBANDING_NONE = 0,
|
yading@10
|
51 HISTEQ_ANTIBANDING_WEAK = 1,
|
yading@10
|
52 HISTEQ_ANTIBANDING_STRONG = 2,
|
yading@10
|
53 HISTEQ_ANTIBANDING_NB,
|
yading@10
|
54 };
|
yading@10
|
55
|
yading@10
|
56 typedef struct {
|
yading@10
|
57 const AVClass *class;
|
yading@10
|
58 float strength;
|
yading@10
|
59 float intensity;
|
yading@10
|
60 enum HisteqAntibanding antibanding;
|
yading@10
|
61 char* antibanding_str;
|
yading@10
|
62 int in_histogram [256]; ///< input histogram
|
yading@10
|
63 int out_histogram[256]; ///< output histogram
|
yading@10
|
64 int LUT[256]; ///< lookup table derived from histogram[]
|
yading@10
|
65 uint8_t rgba_map[4]; ///< components position
|
yading@10
|
66 int bpp; ///< bytes per pixel
|
yading@10
|
67 } HisteqContext;
|
yading@10
|
68
|
yading@10
|
69 #define OFFSET(x) offsetof(HisteqContext, x)
|
yading@10
|
70 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
|
yading@10
|
71 #define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, unit }
|
yading@10
|
72
|
yading@10
|
73 static const AVOption histeq_options[] = {
|
yading@10
|
74 { "strength", "set the strength", OFFSET(strength), AV_OPT_TYPE_FLOAT, {.dbl=0.2}, 0, 1, FLAGS },
|
yading@10
|
75 { "intensity", "set the intensity", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0.21}, 0, 1, FLAGS },
|
yading@10
|
76 { "antibanding", "set the antibanding level", OFFSET(antibanding), AV_OPT_TYPE_INT, {.i64=HISTEQ_ANTIBANDING_NONE}, 0, HISTEQ_ANTIBANDING_NB-1, FLAGS, "antibanding" },
|
yading@10
|
77 CONST("none", "apply no antibanding", HISTEQ_ANTIBANDING_NONE, "antibanding"),
|
yading@10
|
78 CONST("weak", "apply weak antibanding", HISTEQ_ANTIBANDING_WEAK, "antibanding"),
|
yading@10
|
79 CONST("strong", "apply strong antibanding", HISTEQ_ANTIBANDING_STRONG, "antibanding"),
|
yading@10
|
80 { NULL }
|
yading@10
|
81 };
|
yading@10
|
82
|
yading@10
|
83 AVFILTER_DEFINE_CLASS(histeq);
|
yading@10
|
84
|
yading@10
|
85 static av_cold int init(AVFilterContext *ctx)
|
yading@10
|
86 {
|
yading@10
|
87 HisteqContext *histeq = ctx->priv;
|
yading@10
|
88
|
yading@10
|
89 av_log(ctx, AV_LOG_VERBOSE,
|
yading@10
|
90 "strength:%0.3f intensity:%0.3f antibanding:%d\n",
|
yading@10
|
91 histeq->strength, histeq->intensity, histeq->antibanding);
|
yading@10
|
92
|
yading@10
|
93 return 0;
|
yading@10
|
94 }
|
yading@10
|
95
|
yading@10
|
96 static int query_formats(AVFilterContext *ctx)
|
yading@10
|
97 {
|
yading@10
|
98 static const enum PixelFormat pix_fmts[] = {
|
yading@10
|
99 AV_PIX_FMT_ARGB, AV_PIX_FMT_RGBA, AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA,
|
yading@10
|
100 AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
|
yading@10
|
101 AV_PIX_FMT_NONE
|
yading@10
|
102 };
|
yading@10
|
103
|
yading@10
|
104 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
|
yading@10
|
105 return 0;
|
yading@10
|
106 }
|
yading@10
|
107
|
yading@10
|
108 static int config_input(AVFilterLink *inlink)
|
yading@10
|
109 {
|
yading@10
|
110 AVFilterContext *ctx = inlink->dst;
|
yading@10
|
111 HisteqContext *histeq = ctx->priv;
|
yading@10
|
112 const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
|
yading@10
|
113
|
yading@10
|
114 histeq->bpp = av_get_bits_per_pixel(pix_desc) / 8;
|
yading@10
|
115 ff_fill_rgba_map(histeq->rgba_map, inlink->format);
|
yading@10
|
116
|
yading@10
|
117 return 0;
|
yading@10
|
118 }
|
yading@10
|
119
|
yading@10
|
120 #define R 0
|
yading@10
|
121 #define G 1
|
yading@10
|
122 #define B 2
|
yading@10
|
123 #define A 3
|
yading@10
|
124
|
yading@10
|
125 #define GET_RGB_VALUES(r, g, b, src, map) do { \
|
yading@10
|
126 r = src[x + map[R]]; \
|
yading@10
|
127 g = src[x + map[G]]; \
|
yading@10
|
128 b = src[x + map[B]]; \
|
yading@10
|
129 } while (0)
|
yading@10
|
130
|
yading@10
|
131 static int filter_frame(AVFilterLink *inlink, AVFrame *inpic)
|
yading@10
|
132 {
|
yading@10
|
133 AVFilterContext *ctx = inlink->dst;
|
yading@10
|
134 HisteqContext *histeq = ctx->priv;
|
yading@10
|
135 AVFilterLink *outlink = ctx->outputs[0];
|
yading@10
|
136 int strength = histeq->strength * 1000;
|
yading@10
|
137 int intensity = histeq->intensity * 1000;
|
yading@10
|
138 int x, y, i, luthi, lutlo, lut, luma, oluma, m;
|
yading@10
|
139 AVFrame *outpic;
|
yading@10
|
140 unsigned int r, g, b, jran;
|
yading@10
|
141 uint8_t *src, *dst;
|
yading@10
|
142
|
yading@10
|
143 outpic = ff_get_video_buffer(outlink, outlink->w, outlink->h);
|
yading@10
|
144 if (!outpic) {
|
yading@10
|
145 av_frame_free(&inpic);
|
yading@10
|
146 return AVERROR(ENOMEM);
|
yading@10
|
147 }
|
yading@10
|
148 av_frame_copy_props(outpic, inpic);
|
yading@10
|
149
|
yading@10
|
150 /* Seed random generator for antibanding. */
|
yading@10
|
151 jran = LCG_SEED;
|
yading@10
|
152
|
yading@10
|
153 /* Calculate and store the luminance and calculate the global histogram
|
yading@10
|
154 based on the luminance. */
|
yading@10
|
155 memset(histeq->in_histogram, 0, sizeof(histeq->in_histogram));
|
yading@10
|
156 src = inpic->data[0];
|
yading@10
|
157 dst = outpic->data[0];
|
yading@10
|
158 for (y = 0; y < inlink->h; y++) {
|
yading@10
|
159 for (x = 0; x < inlink->w * histeq->bpp; x += histeq->bpp) {
|
yading@10
|
160 GET_RGB_VALUES(r, g, b, src, histeq->rgba_map);
|
yading@10
|
161 luma = (55 * r + 182 * g + 19 * b) >> 8;
|
yading@10
|
162 dst[x + histeq->rgba_map[A]] = luma;
|
yading@10
|
163 histeq->in_histogram[luma]++;
|
yading@10
|
164 }
|
yading@10
|
165 src += inpic->linesize[0];
|
yading@10
|
166 dst += outpic->linesize[0];
|
yading@10
|
167 }
|
yading@10
|
168
|
yading@10
|
169 #ifdef DEBUG
|
yading@10
|
170 for (x = 0; x < 256; x++)
|
yading@10
|
171 av_dlog(ctx, "in[%d]: %u\n", x, histeq->in_histogram[x]);
|
yading@10
|
172 #endif
|
yading@10
|
173
|
yading@10
|
174 /* Calculate the lookup table. */
|
yading@10
|
175 histeq->LUT[0] = histeq->in_histogram[0];
|
yading@10
|
176 /* Accumulate */
|
yading@10
|
177 for (x = 1; x < 256; x++)
|
yading@10
|
178 histeq->LUT[x] = histeq->LUT[x-1] + histeq->in_histogram[x];
|
yading@10
|
179
|
yading@10
|
180 /* Normalize */
|
yading@10
|
181 for (x = 0; x < 256; x++)
|
yading@10
|
182 histeq->LUT[x] = (histeq->LUT[x] * intensity) / (inlink->h * inlink->w);
|
yading@10
|
183
|
yading@10
|
184 /* Adjust the LUT based on the selected strength. This is an alpha
|
yading@10
|
185 mix of the calculated LUT and a linear LUT with gain 1. */
|
yading@10
|
186 for (x = 0; x < 256; x++)
|
yading@10
|
187 histeq->LUT[x] = (strength * histeq->LUT[x]) / 255 +
|
yading@10
|
188 ((255 - strength) * x) / 255;
|
yading@10
|
189
|
yading@10
|
190 /* Output the equalized frame. */
|
yading@10
|
191 memset(histeq->out_histogram, 0, sizeof(histeq->out_histogram));
|
yading@10
|
192
|
yading@10
|
193 src = inpic->data[0];
|
yading@10
|
194 dst = outpic->data[0];
|
yading@10
|
195 for (y = 0; y < inlink->h; y++) {
|
yading@10
|
196 for (x = 0; x < inlink->w * histeq->bpp; x += histeq->bpp) {
|
yading@10
|
197 luma = dst[x + histeq->rgba_map[A]];
|
yading@10
|
198 if (luma == 0) {
|
yading@10
|
199 for (i = 0; i < histeq->bpp; ++i)
|
yading@10
|
200 dst[x + i] = 0;
|
yading@10
|
201 histeq->out_histogram[0]++;
|
yading@10
|
202 } else {
|
yading@10
|
203 lut = histeq->LUT[luma];
|
yading@10
|
204 if (histeq->antibanding != HISTEQ_ANTIBANDING_NONE) {
|
yading@10
|
205 if (luma > 0) {
|
yading@10
|
206 lutlo = histeq->antibanding == HISTEQ_ANTIBANDING_WEAK ?
|
yading@10
|
207 (histeq->LUT[luma] + histeq->LUT[luma - 1]) / 2 :
|
yading@10
|
208 histeq->LUT[luma - 1];
|
yading@10
|
209 } else
|
yading@10
|
210 lutlo = lut;
|
yading@10
|
211
|
yading@10
|
212 if (luma < 255) {
|
yading@10
|
213 luthi = (histeq->antibanding == HISTEQ_ANTIBANDING_WEAK) ?
|
yading@10
|
214 (histeq->LUT[luma] + histeq->LUT[luma + 1]) / 2 :
|
yading@10
|
215 histeq->LUT[luma + 1];
|
yading@10
|
216 } else
|
yading@10
|
217 luthi = lut;
|
yading@10
|
218
|
yading@10
|
219 if (lutlo != luthi) {
|
yading@10
|
220 jran = LCG(jran);
|
yading@10
|
221 lut = lutlo + ((luthi - lutlo + 1) * jran) / LCG_M;
|
yading@10
|
222 }
|
yading@10
|
223 }
|
yading@10
|
224
|
yading@10
|
225 GET_RGB_VALUES(r, g, b, src, histeq->rgba_map);
|
yading@10
|
226 if (((m = FFMAX3(r, g, b)) * lut) / luma > 255) {
|
yading@10
|
227 r = (r * 255) / m;
|
yading@10
|
228 g = (g * 255) / m;
|
yading@10
|
229 b = (b * 255) / m;
|
yading@10
|
230 } else {
|
yading@10
|
231 r = (r * lut) / luma;
|
yading@10
|
232 g = (g * lut) / luma;
|
yading@10
|
233 b = (b * lut) / luma;
|
yading@10
|
234 }
|
yading@10
|
235 dst[x + histeq->rgba_map[R]] = r;
|
yading@10
|
236 dst[x + histeq->rgba_map[G]] = g;
|
yading@10
|
237 dst[x + histeq->rgba_map[B]] = b;
|
yading@10
|
238 oluma = av_clip_uint8((55 * r + 182 * g + 19 * b) >> 8);
|
yading@10
|
239 histeq->out_histogram[oluma]++;
|
yading@10
|
240 }
|
yading@10
|
241 }
|
yading@10
|
242 src += inpic->linesize[0];
|
yading@10
|
243 dst += outpic->linesize[0];
|
yading@10
|
244 }
|
yading@10
|
245 #ifdef DEBUG
|
yading@10
|
246 for (x = 0; x < 256; x++)
|
yading@10
|
247 av_dlog(ctx, "out[%d]: %u\n", x, histeq->out_histogram[x]);
|
yading@10
|
248 #endif
|
yading@10
|
249
|
yading@10
|
250 av_frame_free(&inpic);
|
yading@10
|
251 return ff_filter_frame(outlink, outpic);
|
yading@10
|
252 }
|
yading@10
|
253
|
yading@10
|
254 static const AVFilterPad histeq_inputs[] = {
|
yading@10
|
255 {
|
yading@10
|
256 .name = "default",
|
yading@10
|
257 .type = AVMEDIA_TYPE_VIDEO,
|
yading@10
|
258 .config_props = config_input,
|
yading@10
|
259 .filter_frame = filter_frame,
|
yading@10
|
260 },
|
yading@10
|
261 { NULL }
|
yading@10
|
262 };
|
yading@10
|
263
|
yading@10
|
264 static const AVFilterPad histeq_outputs[] = {
|
yading@10
|
265 {
|
yading@10
|
266 .name = "default",
|
yading@10
|
267 .type = AVMEDIA_TYPE_VIDEO,
|
yading@10
|
268 },
|
yading@10
|
269 { NULL }
|
yading@10
|
270 };
|
yading@10
|
271
|
yading@10
|
272 AVFilter avfilter_vf_histeq = {
|
yading@10
|
273 .name = "histeq",
|
yading@10
|
274 .description = NULL_IF_CONFIG_SMALL("Apply global color histogram equalization."),
|
yading@10
|
275 .priv_size = sizeof(HisteqContext),
|
yading@10
|
276 .init = init,
|
yading@10
|
277 .query_formats = query_formats,
|
yading@10
|
278
|
yading@10
|
279 .inputs = histeq_inputs,
|
yading@10
|
280 .outputs = histeq_outputs,
|
yading@10
|
281 .priv_class = &histeq_class,
|
yading@10
|
282 };
|