FFmpeg
Loading...
Searching...
No Matches
vf_signalstats.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2010 Mark Heath mjpeg0 @ silicontrip dot org
3 * Copyright (c) 2014 Clément Bœsch
4 * Copyright (c) 2014 Dave Rice
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
24#include "libavutil/mem.h"
25#include "libavutil/opt.h"
26#include "libavutil/pixdesc.h"
27#include "filters.h"
28
36
37typedef struct SignalstatsContext {
38 const AVClass *class;
39 int chromah; // height of chroma plane
40 int chromaw; // width of chroma plane
41 int hsub; // horizontal subsampling
42 int vsub; // vertical subsampling
43 int depth; // pixel depth
44 int fs; // pixel count per frame
45 int cfs; // pixel count per frame of chroma planes
46 int outfilter; // FilterMode
49 uint8_t rgba_color[4];
50 int yuv_color[3];
53
54 int maxsize; // history stats array size
56
60
61typedef struct ThreadData {
62 const AVFrame *in;
63 AVFrame *out;
65
70
71#define OFFSET(x) offsetof(SignalstatsContext, x)
72#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
73
74static const AVOption signalstats_options[] = {
75 {"stat", "set statistics filters", OFFSET(filters), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, INT_MAX, FLAGS, .unit = "filters"},
76 {"tout", "analyze pixels for temporal outliers", 0, AV_OPT_TYPE_CONST, {.i64=1<<FILTER_TOUT}, 0, 0, FLAGS, .unit = "filters"},
77 {"vrep", "analyze video lines for vertical line repetition", 0, AV_OPT_TYPE_CONST, {.i64=1<<FILTER_VREP}, 0, 0, FLAGS, .unit = "filters"},
78 {"brng", "analyze for pixels outside of broadcast range", 0, AV_OPT_TYPE_CONST, {.i64=1<<FILTER_BRNG}, 0, 0, FLAGS, .unit = "filters"},
79 {"out", "set video filter", OFFSET(outfilter), AV_OPT_TYPE_INT, {.i64=FILTER_NONE}, -1, FILT_NUMB-1, FLAGS, .unit = "out"},
80 {"tout", "highlight pixels that depict temporal outliers", 0, AV_OPT_TYPE_CONST, {.i64=FILTER_TOUT}, 0, 0, FLAGS, .unit = "out"},
81 {"vrep", "highlight video lines that depict vertical line repetition", 0, AV_OPT_TYPE_CONST, {.i64=FILTER_VREP}, 0, 0, FLAGS, .unit = "out"},
82 {"brng", "highlight pixels that are outside of broadcast range", 0, AV_OPT_TYPE_CONST, {.i64=FILTER_BRNG}, 0, 0, FLAGS, .unit = "out"},
83 {"c", "set highlight color", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str="yellow"}, .flags=FLAGS},
84 {"color", "set highlight color", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str="yellow"}, .flags=FLAGS},
85 {NULL}
86};
87
89
91{
92 uint8_t r, g, b;
93 SignalstatsContext *s = ctx->priv;
94
95 if (s->outfilter != FILTER_NONE)
96 s->filters |= 1 << s->outfilter;
97
98 r = s->rgba_color[0];
99 g = s->rgba_color[1];
100 b = s->rgba_color[2];
101 s->yuv_color[0] = (( 66*r + 129*g + 25*b + (1<<7)) >> 8) + 16;
102 s->yuv_color[1] = ((-38*r + -74*g + 112*b + (1<<7)) >> 8) + 128;
103 s->yuv_color[2] = ((112*r + -94*g + -18*b + (1<<7)) >> 8) + 128;
104 return 0;
105}
106
108{
109 SignalstatsContext *s = ctx->priv;
110 av_frame_free(&s->frame_prev);
111 av_frame_free(&s->frame_sat);
112 av_frame_free(&s->frame_hue);
113 av_freep(&s->jobs_rets);
114 av_freep(&s->histy);
115 av_freep(&s->histu);
116 av_freep(&s->histv);
117 av_freep(&s->histsat);
118}
119
120// TODO: add more
135
136static AVFrame *alloc_frame(enum AVPixelFormat pixfmt, int w, int h)
137{
139 if (!frame)
140 return NULL;
141
142 frame->format = pixfmt;
143 frame->width = w;
144 frame->height = h;
145
146 if (av_frame_get_buffer(frame, 0) < 0) {
148 return NULL;
149 }
150
151 return frame;
152}
153
154static int config_output(AVFilterLink *outlink)
155{
156 AVFilterContext *ctx = outlink->src;
157 SignalstatsContext *s = ctx->priv;
158 AVFilterLink *inlink = outlink->src->inputs[0];
160 s->hsub = desc->log2_chroma_w;
161 s->vsub = desc->log2_chroma_h;
162 s->depth = desc->comp[0].depth;
163 s->maxsize = 1 << s->depth;
164 s->histy = av_malloc_array(s->maxsize, sizeof(*s->histy));
165 s->histu = av_malloc_array(s->maxsize, sizeof(*s->histu));
166 s->histv = av_malloc_array(s->maxsize, sizeof(*s->histv));
167 s->histsat = av_malloc_array(s->maxsize, sizeof(*s->histsat));
168
169 if (!s->histy || !s->histu || !s->histv || !s->histsat)
170 return AVERROR(ENOMEM);
171
172 outlink->w = inlink->w;
173 outlink->h = inlink->h;
174
175 s->chromaw = AV_CEIL_RSHIFT(inlink->w, s->hsub);
176 s->chromah = AV_CEIL_RSHIFT(inlink->h, s->vsub);
177
178 s->fs = inlink->w * inlink->h;
179 s->cfs = s->chromaw * s->chromah;
180
181 s->nb_jobs = FFMAX(1, FFMIN(inlink->h, ff_filter_get_nb_threads(ctx)));
182 s->jobs_rets = av_malloc_array(s->nb_jobs, sizeof(*s->jobs_rets));
183 if (!s->jobs_rets)
184 return AVERROR(ENOMEM);
185
186 s->frame_sat = alloc_frame(s->depth > 8 ? AV_PIX_FMT_GRAY16 : AV_PIX_FMT_GRAY8, inlink->w, inlink->h);
187 s->frame_hue = alloc_frame(AV_PIX_FMT_GRAY16, inlink->w, inlink->h);
188 if (!s->frame_sat || !s->frame_hue)
189 return AVERROR(ENOMEM);
190
191 return 0;
192}
193
194static void burn_frame8(const SignalstatsContext *s, AVFrame *f, int x, int y)
195{
196 const int chromax = x >> s->hsub;
197 const int chromay = y >> s->vsub;
198 f->data[0][y * f->linesize[0] + x] = s->yuv_color[0];
199 f->data[1][chromay * f->linesize[1] + chromax] = s->yuv_color[1];
200 f->data[2][chromay * f->linesize[2] + chromax] = s->yuv_color[2];
201}
202
203static void burn_frame16(const SignalstatsContext *s, AVFrame *f, int x, int y)
204{
205 const int chromax = x >> s->hsub;
206 const int chromay = y >> s->vsub;
207 const int mult = 1 << (s->depth - 8);
208 AV_WN16(f->data[0] + y * f->linesize[0] + x * 2, s->yuv_color[0] * mult);
209 AV_WN16(f->data[1] + chromay * f->linesize[1] + chromax * 2, s->yuv_color[1] * mult);
210 AV_WN16(f->data[2] + chromay * f->linesize[2] + chromax * 2, s->yuv_color[2] * mult);
211}
212
213static int filter8_brng(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
214{
215 ThreadData *td = arg;
216 const SignalstatsContext *s = ctx->priv;
217 const AVFrame *in = td->in;
218 AVFrame *out = td->out;
219 const int w = in->width;
220 const int h = in->height;
221 const int slice_start = ff_slice_pos(h, jobnr, nb_jobs);
222 const int slice_end = ff_slice_pos(h, jobnr + 1, nb_jobs);
223 int x, y, score = 0;
224
225 for (y = slice_start; y < slice_end; y++) {
226 const int yc = y >> s->vsub;
227 const uint8_t *pluma = &in->data[0][y * in->linesize[0]];
228 const uint8_t *pchromau = &in->data[1][yc * in->linesize[1]];
229 const uint8_t *pchromav = &in->data[2][yc * in->linesize[2]];
230
231 for (x = 0; x < w; x++) {
232 const int xc = x >> s->hsub;
233 const int luma = pluma[x];
234 const int chromau = pchromau[xc];
235 const int chromav = pchromav[xc];
236 const int filt = luma < 16 || luma > 235 ||
237 chromau < 16 || chromau > 240 ||
238 chromav < 16 || chromav > 240;
239 score += filt;
240 if (out && filt)
241 burn_frame8(s, out, x, y);
242 }
243 }
244 return score;
245}
246
247static int filter16_brng(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
248{
249 ThreadData *td = arg;
250 const SignalstatsContext *s = ctx->priv;
251 const AVFrame *in = td->in;
252 AVFrame *out = td->out;
253 const int mult = 1 << (s->depth - 8);
254 const int w = in->width;
255 const int h = in->height;
256 const int slice_start = ff_slice_pos(h, jobnr, nb_jobs);
257 const int slice_end = ff_slice_pos(h, jobnr + 1, nb_jobs);
258 int x, y, score = 0;
259
260 for (y = slice_start; y < slice_end; y++) {
261 const int yc = y >> s->vsub;
262 const uint16_t *pluma = (uint16_t *)&in->data[0][y * in->linesize[0]];
263 const uint16_t *pchromau = (uint16_t *)&in->data[1][yc * in->linesize[1]];
264 const uint16_t *pchromav = (uint16_t *)&in->data[2][yc * in->linesize[2]];
265
266 for (x = 0; x < w; x++) {
267 const int xc = x >> s->hsub;
268 const int luma = pluma[x];
269 const int chromau = pchromau[xc];
270 const int chromav = pchromav[xc];
271 const int filt = luma < 16 * mult || luma > 235 * mult ||
272 chromau < 16 * mult || chromau > 240 * mult ||
273 chromav < 16 * mult || chromav > 240 * mult;
274 score += filt;
275 if (out && filt)
276 burn_frame16(s, out, x, y);
277 }
278 }
279 return score;
280}
281
282static int filter_tout_outlier(uint8_t x, uint8_t y, uint8_t z)
283{
284 return ((abs(x - y) + abs (z - y)) / 2) - abs(z - x) > 4; // make 4 configurable?
285}
286
287static int filter8_tout(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
288{
289 ThreadData *td = arg;
290 const SignalstatsContext *s = ctx->priv;
291 const AVFrame *in = td->in;
292 AVFrame *out = td->out;
293 const int w = in->width;
294 const int h = in->height;
295 const int slice_start = ff_slice_pos(h, jobnr, nb_jobs);
296 const int slice_end = ff_slice_pos(h, jobnr + 1, nb_jobs);
297 const uint8_t *p = in->data[0];
298 int lw = in->linesize[0];
299 int x, y, score = 0, filt;
300
301 for (y = slice_start; y < slice_end; y++) {
302
303 if (y - 1 < 0 || y + 1 >= h)
304 continue;
305
306 // detect two pixels above and below (to eliminate interlace artefacts)
307 // should check that video format is in fact interlaced.
308
309#define FILTER(i, j) \
310 filter_tout_outlier(p[(y-j) * lw + x + i], \
311 p[ y * lw + x + i], \
312 p[(y+j) * lw + x + i])
313
314#define FILTER3(j) (FILTER(-1, j) && FILTER(0, j) && FILTER(1, j))
315
316 if (y - 2 >= 0 && y + 2 < h) {
317 for (x = 1; x < w - 1; x++) {
318 filt = FILTER3(2) && FILTER3(1);
319 score += filt;
320 if (filt && out)
321 burn_frame8(s, out, x, y);
322 }
323 } else {
324 for (x = 1; x < w - 1; x++) {
325 filt = FILTER3(1);
326 score += filt;
327 if (filt && out)
328 burn_frame8(s, out, x, y);
329 }
330 }
331 }
332 return score;
333}
334
335static int filter16_tout(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
336{
337 ThreadData *td = arg;
338 const SignalstatsContext *s = ctx->priv;
339 const AVFrame *in = td->in;
340 AVFrame *out = td->out;
341 const int w = in->width;
342 const int h = in->height;
343 const int slice_start = ff_slice_pos(h, jobnr, nb_jobs);
344 const int slice_end = ff_slice_pos(h, jobnr + 1, nb_jobs);
345 const uint16_t *p = (uint16_t *)in->data[0];
346 int lw = in->linesize[0] / 2;
347 int x, y, score = 0, filt;
348
349 for (y = slice_start; y < slice_end; y++) {
350
351 if (y - 1 < 0 || y + 1 >= h)
352 continue;
353
354 // detect two pixels above and below (to eliminate interlace artefacts)
355 // should check that video format is in fact interlaced.
356
357 if (y - 2 >= 0 && y + 2 < h) {
358 for (x = 1; x < w - 1; x++) {
359 filt = FILTER3(2) && FILTER3(1);
360 score += filt;
361 if (filt && out)
362 burn_frame16(s, out, x, y);
363 }
364 } else {
365 for (x = 1; x < w - 1; x++) {
366 filt = FILTER3(1);
367 score += filt;
368 if (filt && out)
369 burn_frame16(s, out, x, y);
370 }
371 }
372 }
373 return score;
374}
375
376#define VREP_START 4
377
378static int filter8_vrep(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
379{
380 ThreadData *td = arg;
381 const SignalstatsContext *s = ctx->priv;
382 const AVFrame *in = td->in;
383 AVFrame *out = td->out;
384 const int w = in->width;
385 const int h = in->height;
386 const int slice_start = ff_slice_pos(h, jobnr, nb_jobs);
387 const int slice_end = ff_slice_pos(h, jobnr + 1, nb_jobs);
388 const uint8_t *p = in->data[0];
389 const int lw = in->linesize[0];
390 int x, y, score = 0;
391
392 for (y = slice_start; y < slice_end; y++) {
393 const int y2lw = (y - VREP_START) * lw;
394 const int ylw = y * lw;
395 int filt, totdiff = 0;
396
397 if (y < VREP_START)
398 continue;
399
400 for (x = 0; x < w; x++)
401 totdiff += abs(p[y2lw + x] - p[ylw + x]);
402 filt = totdiff < w;
403
404 score += filt;
405 if (filt && out)
406 for (x = 0; x < w; x++)
407 burn_frame8(s, out, x, y);
408 }
409 return score * w;
410}
411
412static int filter16_vrep(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
413{
414 ThreadData *td = arg;
415 const SignalstatsContext *s = ctx->priv;
416 const AVFrame *in = td->in;
417 AVFrame *out = td->out;
418 const int w = in->width;
419 const int h = in->height;
420 const int slice_start = ff_slice_pos(h, jobnr, nb_jobs);
421 const int slice_end = ff_slice_pos(h, jobnr + 1, nb_jobs);
422 const uint16_t *p = (uint16_t *)in->data[0];
423 const int lw = in->linesize[0] / 2;
424 int x, y, score = 0;
425
426 for (y = slice_start; y < slice_end; y++) {
427 const int y2lw = (y - VREP_START) * lw;
428 const int ylw = y * lw;
429 int64_t totdiff = 0;
430 int filt;
431
432 if (y < VREP_START)
433 continue;
434
435 for (x = 0; x < w; x++)
436 totdiff += abs(p[y2lw + x] - p[ylw + x]);
437 filt = totdiff < w;
438
439 score += filt;
440 if (filt && out)
441 for (x = 0; x < w; x++)
442 burn_frame16(s, out, x, y);
443 }
444 return score * w;
445}
446
447static const struct {
448 const char *name;
449 int (*process8)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
450 int (*process16)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
451} filters_def[] = {
452 {"TOUT", filter8_tout, filter16_tout},
453 {"VREP", filter8_vrep, filter16_vrep},
454 {"BRNG", filter8_brng, filter16_brng},
455 {NULL}
457
458static int compute_sat_hue_metrics8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
459{
460 int i, j;
462 const SignalstatsContext *s = ctx->priv;
463 const AVFrame *src = td->src;
464 AVFrame *dst_sat = td->dst_sat;
465 AVFrame *dst_hue = td->dst_hue;
466
467 const int slice_start = ff_slice_pos(s->chromah, jobnr, nb_jobs);
468 const int slice_end = ff_slice_pos(s->chromah, jobnr + 1, nb_jobs);
469
470 const int lsz_u = src->linesize[1];
471 const int lsz_v = src->linesize[2];
472 const uint8_t *p_u = src->data[1] + slice_start * lsz_u;
473 const uint8_t *p_v = src->data[2] + slice_start * lsz_v;
474
475 const int lsz_sat = dst_sat->linesize[0];
476 const int lsz_hue = dst_hue->linesize[0];
477 uint8_t *p_sat = dst_sat->data[0] + slice_start * lsz_sat;
478 uint8_t *p_hue = dst_hue->data[0] + slice_start * lsz_hue;
479
480 for (j = slice_start; j < slice_end; j++) {
481 for (i = 0; i < s->chromaw; i++) {
482 const int yuvu = p_u[i];
483 const int yuvv = p_v[i];
484 p_sat[i] = hypotf(yuvu - 128, yuvv - 128); // int or round?
485 ((int16_t*)p_hue)[i] = fmodf(floorf((180.f / M_PI) * atan2f(yuvu-128, yuvv-128) + 180.f), 360.f);
486 }
487 p_u += lsz_u;
488 p_v += lsz_v;
489 p_sat += lsz_sat;
490 p_hue += lsz_hue;
491 }
492
493 return 0;
494}
495
496static int compute_sat_hue_metrics16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
497{
498 int i, j;
500 const SignalstatsContext *s = ctx->priv;
501 const AVFrame *src = td->src;
502 AVFrame *dst_sat = td->dst_sat;
503 AVFrame *dst_hue = td->dst_hue;
504 const int mid = 1 << (s->depth - 1);
505
506 const int slice_start = ff_slice_pos(s->chromah, jobnr, nb_jobs);
507 const int slice_end = ff_slice_pos(s->chromah, jobnr + 1, nb_jobs);
508
509 const int lsz_u = src->linesize[1] / 2;
510 const int lsz_v = src->linesize[2] / 2;
511 const uint16_t *p_u = (uint16_t*)src->data[1] + slice_start * lsz_u;
512 const uint16_t *p_v = (uint16_t*)src->data[2] + slice_start * lsz_v;
513
514 const int lsz_sat = dst_sat->linesize[0] / 2;
515 const int lsz_hue = dst_hue->linesize[0] / 2;
516 uint16_t *p_sat = (uint16_t*)dst_sat->data[0] + slice_start * lsz_sat;
517 uint16_t *p_hue = (uint16_t*)dst_hue->data[0] + slice_start * lsz_hue;
518
519 for (j = slice_start; j < slice_end; j++) {
520 for (i = 0; i < s->chromaw; i++) {
521 const int yuvu = p_u[i];
522 const int yuvv = p_v[i];
523 p_sat[i] = hypotf(yuvu - mid, yuvv - mid); // int or round?
524 ((int16_t*)p_hue)[i] = fmodf(floorf((180.f / M_PI) * atan2f(yuvu-mid, yuvv-mid) + 180.f), 360.f);
525 }
526 p_u += lsz_u;
527 p_v += lsz_v;
528 p_sat += lsz_sat;
529 p_hue += lsz_hue;
530 }
531
532 return 0;
533}
534
535static unsigned compute_bit_depth(uint16_t mask)
536{
537 return av_popcount(mask);
538}
539
540static int filter_frame(AVFilterLink *link, AVFrame *in)
541{
542 AVFilterContext *ctx = link->dst;
543 SignalstatsContext *s = ctx->priv;
544 AVFilterLink *outlink = ctx->outputs[0];
545 AVFrame *out = in;
546 int w = 0, cw = 0, // in
547 pw = 0, cpw = 0; // prev
548 int fil;
549 char metabuf[128];
550 unsigned int *histy = s->histy,
551 *histu = s->histu,
552 *histv = s->histv,
553 histhue[360] = {0},
554 *histsat = s->histsat;
555 int miny = -1, minu = -1, minv = -1;
556 int maxy = -1, maxu = -1, maxv = -1;
557 int lowy = -1, lowu = -1, lowv = -1;
558 int highy = -1, highu = -1, highv = -1;
559 int minsat = -1, maxsat = -1, lowsat = -1, highsat = -1;
560 int lowp, highp, clowp, chighp;
561 int accy, accu, accv;
562 int accsat, acchue = 0;
563 int medhue, maxhue;
564 int64_t toty = 0, totu = 0, totv = 0, totsat=0;
565 int64_t tothue = 0;
566 int64_t dify = 0, difu = 0, difv = 0;
567 uint16_t masky = 0, masku = 0, maskv = 0;
568
569 int filtot[FILT_NUMB] = {0};
570 AVFrame *prev;
571 int ret;
572 AVFrame *sat = s->frame_sat;
573 AVFrame *hue = s->frame_hue;
574 const int hbd = s->depth > 8;
575 ThreadDataHueSatMetrics td_huesat = {
576 .src = in,
577 .dst_sat = sat,
578 .dst_hue = hue,
579 };
580
581 if (!s->frame_prev)
582 s->frame_prev = av_frame_clone(in);
583
584 prev = s->frame_prev;
585
586 if (s->outfilter != FILTER_NONE) {
587 out = av_frame_clone(in);
588 if (!out) {
589 av_frame_free(&in);
590 return AVERROR(ENOMEM);
591 }
593 if (ret < 0) {
595 av_frame_free(&in);
596 return ret;
597 }
598 }
599
601 : compute_sat_hue_metrics8, &td_huesat,
603
604 memset(s->histy, 0, s->maxsize * sizeof(*s->histy));
605 memset(s->histu, 0, s->maxsize * sizeof(*s->histu));
606 memset(s->histv, 0, s->maxsize * sizeof(*s->histv));
607 memset(s->histsat, 0, s->maxsize * sizeof(*s->histsat));
608
609 if (hbd) {
610 const uint16_t *p_sat = (uint16_t *)sat->data[0];
611 const uint16_t *p_hue = (uint16_t *)hue->data[0];
612 const int lsz_sat = sat->linesize[0] / 2;
613 const int lsz_hue = hue->linesize[0] / 2;
614 // Calculate luma histogram and difference with previous frame or field.
615 for (int j = 0; j < link->h; j++) {
616 for (int i = 0; i < link->w; i++) {
617 const int yuv = AV_RN16(in->data[0] + w + i * 2);
618
619 masky |= yuv;
620 histy[yuv]++;
621 dify += abs(yuv - (int)AV_RN16(prev->data[0] + pw + i * 2));
622 }
623 w += in->linesize[0];
624 pw += prev->linesize[0];
625 }
626
627 // Calculate chroma histogram and difference with previous frame or field.
628 for (int j = 0; j < s->chromah; j++) {
629 for (int i = 0; i < s->chromaw; i++) {
630 const int yuvu = AV_RN16(in->data[1] + cw + i * 2);
631 const int yuvv = AV_RN16(in->data[2] + cw + i * 2);
632
633 masku |= yuvu;
634 maskv |= yuvv;
635 histu[yuvu]++;
636 difu += abs(yuvu - (int)AV_RN16(prev->data[1] + cpw + i * 2));
637 histv[yuvv]++;
638 difv += abs(yuvv - (int)AV_RN16(prev->data[2] + cpw + i * 2));
639
640 histsat[p_sat[i]]++;
641 histhue[((int16_t*)p_hue)[i]]++;
642 }
643 cw += in->linesize[1];
644 cpw += prev->linesize[1];
645 p_sat += lsz_sat;
646 p_hue += lsz_hue;
647 }
648 } else {
649 const uint8_t *p_sat = sat->data[0];
650 const uint8_t *p_hue = hue->data[0];
651 const int lsz_sat = sat->linesize[0];
652 const int lsz_hue = hue->linesize[0];
653 // Calculate luma histogram and difference with previous frame or field.
654 for (int j = 0; j < link->h; j++) {
655 for (int i = 0; i < link->w; i++) {
656 const int yuv = in->data[0][w + i];
657
658 masky |= yuv;
659 histy[yuv]++;
660 dify += abs(yuv - prev->data[0][pw + i]);
661 }
662 w += in->linesize[0];
663 pw += prev->linesize[0];
664 }
665
666 // Calculate chroma histogram and difference with previous frame or field.
667 for (int j = 0; j < s->chromah; j++) {
668 for (int i = 0; i < s->chromaw; i++) {
669 const int yuvu = in->data[1][cw+i];
670 const int yuvv = in->data[2][cw+i];
671
672 masku |= yuvu;
673 maskv |= yuvv;
674 histu[yuvu]++;
675 difu += abs(yuvu - prev->data[1][cpw+i]);
676 histv[yuvv]++;
677 difv += abs(yuvv - prev->data[2][cpw+i]);
678
679 histsat[p_sat[i]]++;
680 histhue[((int16_t*)p_hue)[i]]++;
681 }
682 cw += in->linesize[1];
683 cpw += prev->linesize[1];
684 p_sat += lsz_sat;
685 p_hue += lsz_hue;
686 }
687 }
688
689 for (fil = 0; fil < FILT_NUMB; fil ++) {
690 if (s->filters & 1<<fil) {
691 ThreadData td = {
692 .in = in,
693 .out = out != in && s->outfilter == fil ? out : NULL,
694 };
695 memset(s->jobs_rets, 0, s->nb_jobs * sizeof(*s->jobs_rets));
697 &td, s->jobs_rets, s->nb_jobs);
698 for (int i = 0; i < s->nb_jobs; i++)
699 filtot[fil] += s->jobs_rets[i];
700 }
701 }
702
703 // find low / high based on histogram percentile
704 // these only need to be calculated once.
705
706 lowp = lrint(s->fs * 10 / 100.);
707 highp = lrint(s->fs * 90 / 100.);
708 clowp = lrint(s->cfs * 10 / 100.);
709 chighp = lrint(s->cfs * 90 / 100.);
710
711 accy = accu = accv = accsat = 0;
712 for (fil = 0; fil < s->maxsize; fil++) {
713 if (miny < 0 && histy[fil]) miny = fil;
714 if (minu < 0 && histu[fil]) minu = fil;
715 if (minv < 0 && histv[fil]) minv = fil;
716 if (minsat < 0 && histsat[fil]) minsat = fil;
717
718 if (histy[fil]) maxy = fil;
719 if (histu[fil]) maxu = fil;
720 if (histv[fil]) maxv = fil;
721 if (histsat[fil]) maxsat = fil;
722
723 toty += (uint64_t)histy[fil] * fil;
724 totu += (uint64_t)histu[fil] * fil;
725 totv += (uint64_t)histv[fil] * fil;
726 totsat += (uint64_t)histsat[fil] * fil;
727
728 accy += histy[fil];
729 accu += histu[fil];
730 accv += histv[fil];
731 accsat += histsat[fil];
732
733 if (lowy == -1 && accy >= lowp) lowy = fil;
734 if (lowu == -1 && accu >= clowp) lowu = fil;
735 if (lowv == -1 && accv >= clowp) lowv = fil;
736 if (lowsat == -1 && accsat >= clowp) lowsat = fil;
737
738 if (highy == -1 && accy >= highp) highy = fil;
739 if (highu == -1 && accu >= chighp) highu = fil;
740 if (highv == -1 && accv >= chighp) highv = fil;
741 if (highsat == -1 && accsat >= chighp) highsat = fil;
742 }
743
744 maxhue = histhue[0];
745 medhue = -1;
746 for (fil = 0; fil < 360; fil++) {
747 tothue += (uint64_t)histhue[fil] * fil;
748 acchue += histhue[fil];
749
750 if (medhue == -1 && acchue > s->cfs / 2)
751 medhue = fil;
752 if (histhue[fil] > maxhue) {
753 maxhue = histhue[fil];
754 }
755 }
756
757 av_frame_free(&s->frame_prev);
758 s->frame_prev = av_frame_clone(in);
759
760#define SET_META(key, fmt, val) do { \
761 snprintf(metabuf, sizeof(metabuf), fmt, val); \
762 av_dict_set(&out->metadata, "lavfi.signalstats." key, metabuf, 0); \
763} while (0)
764
765 av_dict_set_int(&out->metadata, "lavfi.signalstats.YMIN", miny, 0);
766 av_dict_set_int(&out->metadata, "lavfi.signalstats.YLOW", lowy, 0);
767 SET_META("YAVG", "%g", 1.0 * toty / s->fs);
768 av_dict_set_int(&out->metadata, "lavfi.signalstats.YHIGH", highy, 0);
769 av_dict_set_int(&out->metadata, "lavfi.signalstats.YMAX", maxy, 0);
770
771 av_dict_set_int(&out->metadata, "lavfi.signalstats.UMIN", minu, 0);
772 av_dict_set_int(&out->metadata, "lavfi.signalstats.ULOW", lowu, 0);
773 SET_META("UAVG", "%g", 1.0 * totu / s->cfs);
774 av_dict_set_int(&out->metadata, "lavfi.signalstats.UHIGH", highu, 0);
775 av_dict_set_int(&out->metadata, "lavfi.signalstats.UMAX", maxu, 0);
776
777 av_dict_set_int(&out->metadata, "lavfi.signalstats.VMIN", minv, 0);
778 av_dict_set_int(&out->metadata, "lavfi.signalstats.VLOW", lowv, 0);
779 SET_META("VAVG", "%g", 1.0 * totv / s->cfs);
780 av_dict_set_int(&out->metadata, "lavfi.signalstats.VHIGH", highv, 0);
781 av_dict_set_int(&out->metadata, "lavfi.signalstats.VMAX", maxv, 0);
782
783 av_dict_set_int(&out->metadata, "lavfi.signalstats.SATMIN", minsat, 0);
784 av_dict_set_int(&out->metadata, "lavfi.signalstats.SATLOW", lowsat, 0);
785 SET_META("SATAVG", "%g", 1.0 * totsat / s->cfs);
786 av_dict_set_int(&out->metadata, "lavfi.signalstats.SATHIGH", highsat, 0);
787 av_dict_set_int(&out->metadata, "lavfi.signalstats.SATMAX", maxsat, 0);
788
789 av_dict_set_int(&out->metadata, "lavfi.signalstats.HUEMED", medhue, 0);
790 SET_META("HUEAVG", "%g", 1.0 * tothue / s->cfs);
791
792 SET_META("YDIF", "%g", 1.0 * dify / s->fs);
793 SET_META("UDIF", "%g", 1.0 * difu / s->cfs);
794 SET_META("VDIF", "%g", 1.0 * difv / s->cfs);
795
796 av_dict_set_int(&out->metadata, "lavfi.signalstats.YBITDEPTH", compute_bit_depth(masky), 0);
797 av_dict_set_int(&out->metadata, "lavfi.signalstats.UBITDEPTH", compute_bit_depth(masku), 0);
798 av_dict_set_int(&out->metadata, "lavfi.signalstats.VBITDEPTH", compute_bit_depth(maskv), 0);
799
800 for (fil = 0; fil < FILT_NUMB; fil ++) {
801 if (s->filters & 1<<fil) {
802 char metaname[128];
803 snprintf(metabuf, sizeof(metabuf), "%g", 1.0 * filtot[fil] / s->fs);
804 snprintf(metaname, sizeof(metaname), "lavfi.signalstats.%s", filters_def[fil].name);
805 av_dict_set(&out->metadata, metaname, metabuf, 0);
806 }
807 }
808
809 if (in != out)
810 av_frame_free(&in);
811 return ff_filter_frame(outlink, out);
812}
813
815 {
816 .name = "default",
817 .type = AVMEDIA_TYPE_VIDEO,
818 .filter_frame = filter_frame,
819 },
820};
821
823 {
824 .name = "default",
825 .config_props = config_output,
826 .type = AVMEDIA_TYPE_VIDEO,
827 },
828};
829
831 .p.name = "signalstats",
832 .p.description = "Generate statistics from video analysis.",
833 .p.priv_class = &signalstats_class,
835 .init = init,
836 .uninit = uninit,
837 .priv_size = sizeof(SignalstatsContext),
841};
#define filters(fmt, type, inverse, clp, inverset, clip, one, clip_fn, packed)
static const int8_t filt[NUMTAPS *2]
Definition af_earwax.c:40
const FFFilter ff_vf_signalstats
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
int ff_filter_execute(AVFilterContext *ctx, avfilter_action_func *func, void *arg, int *ret, int nb_jobs)
Definition avfilter.c:1696
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition avfilter.c:846
int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe)
Make sure a frame is writable.
Definition avfilter.c:1567
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define f(width, name)
Definition cbs_vp8.c:236
#define s(width, name)
Definition cbs_vp9.c:198
#define FLAGS
Definition cmdutils.c:598
#define av_popcount
Definition common.h:154
#define AV_CEIL_RSHIFT(a, b)
Definition common.h:60
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
#define abs(x)
static __device__ float floorf(float a)
static AVFrame * frame
static int filter_frame(DBEDecodeContext *s, AVFrame *frame)
Definition dolby_e.c:1067
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition opt.h:298
@ AV_OPT_TYPE_FLAGS
Underlying C type is unsigned int.
Definition opt.h:254
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_COLOR
Underlying C type is uint8_t[4].
Definition opt.h:322
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition avfilter.h:166
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
Definition dict.c:86
int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, int flags)
Convenience wrapper for av_dict_set() that converts the value to a string and stores it.
Definition dict.c:177
#define AVERROR(e)
Definition error.h:45
int av_frame_get_buffer(AVFrame *frame, int align)
Allocate new buffer(s) for audio or video data.
Definition frame.c:206
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition frame.c:52
AVFrame * av_frame_clone(const AVFrame *src)
Create a new frame that references the same data as src.
Definition frame.c:483
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
#define r
Definition input.c:42
#define b
Definition input.c:43
#define AV_WN16(p, v)
#define AV_RN16(p)
enum AVPixelFormat pixfmt
Definition kmsgrab.c:367
static av_cold void uninit(AVBitStreamFilterContext *ctx)
static int config_output(AVBitStreamFilterLink *outlink)
static int16_t mult(Float11 *f1, Float11 *f2)
Definition g726.c:60
const char * arg
Definition jacosubdec.c:65
#define FILTER_INPUTS(array)
Definition filters.h:264
#define FILTER_OUTPUTS(array)
Definition filters.h:265
static int ff_slice_pos(int total, int jobnr, int nb_jobs)
Compute the boundary index for a slice when work of size total is split into nb_jobs slices.
Definition filters.h:763
#define FILTER_PIXFMTS_ARRAY(array)
Definition filters.h:244
#define AVFILTER_DEFINE_CLASS(fname)
Definition filters.h:478
#define av_cold
Definition attributes.h:117
static enum AVPixelFormat pix_fmts[]
Definition libkvazaar.c:296
#define atan2f(y, x)
Definition libm.h:47
const char * desc
Definition libsvtav1.c:83
uint8_t w
Definition llvidencdsp.c:39
static const uint16_t mask[17]
Definition lzw.c:38
#define FFMIN(a, b)
Definition macros.h:49
#define FFMAX(a, b)
Definition macros.h:47
#define M_PI
Definition mathematics.h:67
Memory handling functions.
static int slice_end(AVCodecContext *avctx, AVFrame *pict, int *got_output)
Handle slice ends.
Definition mpeg12dec.c:1697
AVOptions.
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3460
#define AV_PIX_FMT_YUV420P16
Definition pixfmt.h:556
#define AV_PIX_FMT_YUV444P12
Definition pixfmt.h:552
#define AV_PIX_FMT_YUV444P9
Definition pixfmt.h:544
#define AV_PIX_FMT_YUV420P10
Definition pixfmt.h:545
#define AV_PIX_FMT_YUV440P12
Definition pixfmt.h:551
#define AV_PIX_FMT_YUV422P9
Definition pixfmt.h:543
#define AV_PIX_FMT_YUV420P12
Definition pixfmt.h:549
#define AV_PIX_FMT_YUV422P12
Definition pixfmt.h:550
#define AV_PIX_FMT_YUV422P10
Definition pixfmt.h:546
#define AV_PIX_FMT_YUV420P9
Definition pixfmt.h:542
#define AV_PIX_FMT_YUV420P14
Definition pixfmt.h:553
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_NONE
Definition pixfmt.h:72
@ AV_PIX_FMT_YUV420P
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition pixfmt.h:73
@ AV_PIX_FMT_YUV440P
planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
Definition pixfmt.h:106
@ AV_PIX_FMT_YUV422P
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition pixfmt.h:77
@ AV_PIX_FMT_GRAY8
Y , 8bpp.
Definition pixfmt.h:81
@ AV_PIX_FMT_YUVJ440P
planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range
Definition pixfmt.h:107
@ AV_PIX_FMT_YUV411P
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
Definition pixfmt.h:80
@ AV_PIX_FMT_YUV444P
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition pixfmt.h:78
@ AV_PIX_FMT_YUVJ411P
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) full scale (JPEG), deprecated in favor ...
Definition pixfmt.h:283
@ AV_PIX_FMT_YUVJ422P
planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting col...
Definition pixfmt.h:86
@ AV_PIX_FMT_YUVJ444P
planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting col...
Definition pixfmt.h:87
@ AV_PIX_FMT_YUVJ420P
planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting col...
Definition pixfmt.h:85
#define AV_PIX_FMT_YUV422P14
Definition pixfmt.h:554
#define AV_PIX_FMT_YUV422P16
Definition pixfmt.h:557
#define AV_PIX_FMT_YUV440P10
Definition pixfmt.h:547
#define AV_PIX_FMT_GRAY16
Definition pixfmt.h:528
#define AV_PIX_FMT_YUV444P14
Definition pixfmt.h:555
#define AV_PIX_FMT_YUV444P16
Definition pixfmt.h:558
#define AV_PIX_FMT_YUV444P10
Definition pixfmt.h:548
const char * name
Definition qsvenc.c:142
#define FILTER_NONE
Definition ralf.c:39
#define snprintf
Definition snprintf.h:34
Describe the class of an AVClass context structure.
Definition log.h:76
An instance of a filter.
Definition avfilter.h:273
AVFilterLink ** inputs
array of pointers to input links
Definition avfilter.h:281
A filter pad used for either input or output.
Definition filters.h:40
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition frame.h:493
int width
Definition frame.h:544
int height
Definition frame.h:544
int linesize[AV_NUM_DATA_POINTERS]
For video, a positive or negative value, which is typically indicating the size in bytes of each pict...
Definition frame.h:517
AVOption.
Definition opt.h:428
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition pixdesc.h:69
Used for passing data between threads.
Definition dsddec.c:71
AVFrame * out
#define lrint
Definition tablegen.h:53
#define av_malloc_array(a, b)
#define av_freep(p)
#define src
Definition vp8dsp.c:248
static FILE * out
Definition movenc.c:55
static AVFormatContext * ctx
Definition movenc.c:49
const char * g
Definition vf_curves.c:128
#define SET_META(key, fmt, val)
static const AVFilterPad signalstats_outputs[]
static AVFrame * alloc_frame(enum AVPixelFormat pixfmt, int w, int h)
static int filter8_tout(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
static void burn_frame8(const SignalstatsContext *s, AVFrame *f, int x, int y)
@ FILT_NUMB
@ FILTER_VREP
@ FILTER_BRNG
@ FILTER_TOUT
int(* process16)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
static const AVFilterPad signalstats_inputs[]
static int compute_sat_hue_metrics8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
static int filter_frame(AVFilterLink *link, AVFrame *in)
static int compute_sat_hue_metrics16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
static int filter8_vrep(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
static unsigned compute_bit_depth(uint16_t mask)
static int filter16_vrep(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
static int filter16_tout(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
static int filter8_brng(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
static const struct @017302175103271015024365147256334144304120275223 filters_def[]
#define VREP_START
static av_cold void uninit(AVFilterContext *ctx)
int(* process8)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
#define OFFSET(x)
static int config_output(AVFilterLink *outlink)
static void burn_frame16(const SignalstatsContext *s, AVFrame *f, int x, int y)
static const AVOption signalstats_options[]
#define FILTER3(j)
static int filter_tout_outlier(uint8_t x, uint8_t y, uint8_t z)
static int filter16_brng(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
FilterMode
Definition vp9.h:64
static int slice_start(SliceContext *sc, VVCContext *s, VVCFrameContext *fc, const CodedBitstreamUnit *unit, const int is_first_slice)
Definition dec.c:844