Browse code

avfilter: add pixscope filter

Signed-off-by: Paul B Mahol <onemda@gmail.com>

Paul B Mahol authored on 2017/04/23 05:02:49
Showing 5 changed files
... ...
@@ -10925,6 +10925,29 @@ format=monow, pixdesctest
10925 10925
 
10926 10926
 can be used to test the monowhite pixel format descriptor definition.
10927 10927
 
10928
+@section pixscope
10929
+
10930
+Display sample values of color channels. Mainly useful for checking color and levels.
10931
+
10932
+The filters accept the following options:
10933
+
10934
+@table @option
10935
+@item x
10936
+Set scope X position, offset on X axis.
10937
+
10938
+@item y
10939
+Set scope Y position, offset on Y axis.
10940
+
10941
+@item w
10942
+Set scope width.
10943
+
10944
+@item h
10945
+Set scope height.
10946
+
10947
+@item o
10948
+Set window opacity. This window also holds statistics about pixel area.
10949
+@end table
10950
+
10928 10951
 @section pp
10929 10952
 
10930 10953
 Enable the specified chain of postprocessing subfilters using libpostproc. This
... ...
@@ -245,6 +245,7 @@ OBJS-$(CONFIG_PERMS_FILTER)                  += f_perms.o
245 245
 OBJS-$(CONFIG_PERSPECTIVE_FILTER)            += vf_perspective.o
246 246
 OBJS-$(CONFIG_PHASE_FILTER)                  += vf_phase.o
247 247
 OBJS-$(CONFIG_PIXDESCTEST_FILTER)            += vf_pixdesctest.o
248
+OBJS-$(CONFIG_PIXSCOPE_FILTER)               += vf_datascope.o
248 249
 OBJS-$(CONFIG_PP_FILTER)                     += vf_pp.o
249 250
 OBJS-$(CONFIG_PP7_FILTER)                    += vf_pp7.o
250 251
 OBJS-$(CONFIG_PREMULTIPLY_FILTER)            += vf_premultiply.o framesync.o
... ...
@@ -255,6 +255,7 @@ static void register_all(void)
255 255
     REGISTER_FILTER(PERSPECTIVE,    perspective,    vf);
256 256
     REGISTER_FILTER(PHASE,          phase,          vf);
257 257
     REGISTER_FILTER(PIXDESCTEST,    pixdesctest,    vf);
258
+    REGISTER_FILTER(PIXSCOPE,       pixscope,       vf);
258 259
     REGISTER_FILTER(PP,             pp,             vf);
259 260
     REGISTER_FILTER(PP7,            pp7,            vf);
260 261
     REGISTER_FILTER(PREMULTIPLY,    premultiply,    vf);
... ...
@@ -30,7 +30,7 @@
30 30
 #include "libavutil/version.h"
31 31
 
32 32
 #define LIBAVFILTER_VERSION_MAJOR   6
33
-#define LIBAVFILTER_VERSION_MINOR  87
33
+#define LIBAVFILTER_VERSION_MINOR  88
34 34
 #define LIBAVFILTER_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
... ...
@@ -76,7 +76,7 @@ static int query_formats(AVFilterContext *ctx)
76 76
     return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0));
77 77
 }
78 78
 
79
-static void draw_text(DatascopeContext *s, AVFrame *frame, FFDrawColor *color,
79
+static void draw_text(FFDrawContext *draw, AVFrame *frame, FFDrawColor *color,
80 80
                       int x0, int y0, const uint8_t *text, int vertical)
81 81
 {
82 82
     int x = x0;
... ...
@@ -87,7 +87,7 @@ static void draw_text(DatascopeContext *s, AVFrame *frame, FFDrawColor *color,
87 87
             y0 += 8;
88 88
             continue;
89 89
         }
90
-        ff_blend_mask(&s->draw, color, frame->data, frame->linesize,
90
+        ff_blend_mask(draw, color, frame->data, frame->linesize,
91 91
                       frame->width, frame->height,
92 92
                       avpriv_cga_font + *text * 8, 1, 8, 8, 0, 0, x, y0);
93 93
         if (vertical) {
... ...
@@ -201,7 +201,7 @@ static int filter_color2(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs
201 201
                 char text[256];
202 202
 
203 203
                 snprintf(text, sizeof(text), format[C>>2], value[p]);
204
-                draw_text(s, out, &reverse, xoff + x * C * 10 + 2, yoff + y * P * 12 + p * 10 + 2, text, 0);
204
+                draw_text(&s->draw, out, &reverse, xoff + x * C * 10 + 2, yoff + y * P * 12 + p * 10 + 2, text, 0);
205 205
             }
206 206
         }
207 207
     }
... ...
@@ -239,7 +239,7 @@ static int filter_color(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
239 239
                 char text[256];
240 240
 
241 241
                 snprintf(text, sizeof(text), format[C>>2], value[p]);
242
-                draw_text(s, out, &color, xoff + x * C * 10 + 2, yoff + y * P * 12 + p * 10 + 2, text, 0);
242
+                draw_text(&s->draw, out, &color, xoff + x * C * 10 + 2, yoff + y * P * 12 + p * 10 + 2, text, 0);
243 243
             }
244 244
         }
245 245
     }
... ...
@@ -276,7 +276,7 @@ static int filter_mono(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
276 276
                 char text[256];
277 277
 
278 278
                 snprintf(text, sizeof(text), format[C>>2], value[p]);
279
-                draw_text(s, out, &s->white, xoff + x * C * 10 + 2, yoff + y * P * 12 + p * 10 + 2, text, 0);
279
+                draw_text(&s->draw, out, &s->white, xoff + x * C * 10 + 2, yoff + y * P * 12 + p * 10 + 2, text, 0);
280 280
             }
281 281
         }
282 282
     }
... ...
@@ -328,7 +328,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
328 328
             ff_fill_rectangle(&s->draw, &s->gray, out->data, out->linesize,
329 329
                               0, xmaxlen + y * P * 12 + (P + 1) * P - 2, ymaxlen, 10);
330 330
 
331
-            draw_text(s, out, &s->yellow, 2, xmaxlen + y * P * 12 + (P + 1) * P, text, 0);
331
+            draw_text(&s->draw, out, &s->yellow, 2, xmaxlen + y * P * 12 + (P + 1) * P, text, 0);
332 332
         }
333 333
 
334 334
         for (x = 0; x < X; x++) {
... ...
@@ -337,7 +337,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
337 337
             ff_fill_rectangle(&s->draw, &s->gray, out->data, out->linesize,
338 338
                               ymaxlen + x * C * 10 + 2 * C - 2, 0, 10, xmaxlen);
339 339
 
340
-            draw_text(s, out, &s->yellow, ymaxlen + x * C * 10 + 2 * C, 2, text, 1);
340
+            draw_text(&s->draw, out, &s->yellow, ymaxlen + x * C * 10 + 2 * C, 2, text, 1);
341 341
         }
342 342
     }
343 343
 
... ...
@@ -419,3 +419,225 @@ AVFilter ff_vf_datascope = {
419 419
     .outputs       = outputs,
420 420
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
421 421
 };
422
+
423
+typedef struct PixscopeContext {
424
+    const AVClass *class;
425
+
426
+    float xpos, ypos;
427
+    int w, h;
428
+    float o;
429
+
430
+    int x, y;
431
+    int ww, wh;
432
+
433
+    int nb_planes;
434
+    int nb_comps;
435
+    int is_rgb;
436
+    uint8_t rgba_map[4];
437
+    FFDrawContext draw;
438
+    FFDrawColor   dark;
439
+    FFDrawColor   black;
440
+    FFDrawColor   white;
441
+    FFDrawColor   green;
442
+    FFDrawColor   blue;
443
+    FFDrawColor   red;
444
+    FFDrawColor  *colors[4];
445
+
446
+    void (*pick_color)(FFDrawContext *draw, FFDrawColor *color, AVFrame *in, int x, int y, int *value);
447
+} PixscopeContext;
448
+
449
+#define POFFSET(x) offsetof(PixscopeContext, x)
450
+
451
+static const AVOption pixscope_options[] = {
452
+    { "x", "set scope x offset", POFFSET(xpos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0,  1, FLAGS },
453
+    { "y", "set scope y offset", POFFSET(ypos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0,  1, FLAGS },
454
+    { "w", "set scope width",    POFFSET(w),    AV_OPT_TYPE_INT,   {.i64=7},   1, 80, FLAGS },
455
+    { "h", "set scope height",   POFFSET(h),    AV_OPT_TYPE_INT,   {.i64=7},   1, 80, FLAGS },
456
+    { "o", "set window opacity", POFFSET(o),    AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0,  1, FLAGS },
457
+    { NULL }
458
+};
459
+
460
+AVFILTER_DEFINE_CLASS(pixscope);
461
+
462
+static int pixscope_config_input(AVFilterLink *inlink)
463
+{
464
+    PixscopeContext *s = inlink->dst->priv;
465
+
466
+    s->nb_planes = av_pix_fmt_count_planes(inlink->format);
467
+    ff_draw_init(&s->draw, inlink->format, 0);
468
+    ff_draw_color(&s->draw, &s->dark,  (uint8_t[]){ 0, 0, 0, s->o * 255} );
469
+    ff_draw_color(&s->draw, &s->black, (uint8_t[]){ 0, 0, 0, 255} );
470
+    ff_draw_color(&s->draw, &s->white, (uint8_t[]){ 255, 255, 255, 255} );
471
+    ff_draw_color(&s->draw, &s->green, (uint8_t[]){   0, 255,   0, 255} );
472
+    ff_draw_color(&s->draw, &s->blue,  (uint8_t[]){   0,   0, 255, 255} );
473
+    ff_draw_color(&s->draw, &s->red,   (uint8_t[]){ 255,   0,   0, 255} );
474
+    s->nb_comps = s->draw.desc->nb_components;
475
+    s->is_rgb   = s->draw.desc->flags & AV_PIX_FMT_FLAG_RGB;
476
+
477
+    if (s->is_rgb) {
478
+        s->colors[0] = &s->red;
479
+        s->colors[1] = &s->green;
480
+        s->colors[2] = &s->blue;
481
+        s->colors[3] = &s->white;
482
+        ff_fill_rgba_map(s->rgba_map, inlink->format);
483
+    } else {
484
+        s->colors[0] = &s->white;
485
+        s->colors[1] = &s->blue;
486
+        s->colors[2] = &s->red;
487
+        s->colors[3] = &s->white;
488
+        s->rgba_map[0] = 0;
489
+        s->rgba_map[1] = 1;
490
+        s->rgba_map[2] = 2;
491
+        s->rgba_map[3] = 3;
492
+    }
493
+
494
+    if (s->draw.desc->comp[0].depth <= 8) {
495
+        s->pick_color = pick_color8;
496
+    } else {
497
+        s->pick_color = pick_color16;
498
+    }
499
+
500
+    if (inlink->w < 640 || inlink->h < 480) {
501
+        av_log(inlink->dst, AV_LOG_ERROR, "min supported resolution is 640x480\n");
502
+        return AVERROR(EINVAL);
503
+    }
504
+
505
+    s->ww = 300;
506
+    s->wh = 300 * 1.6180;
507
+    s->x = s->xpos * (inlink->w - 1);
508
+    s->y = s->ypos * (inlink->h - 1);
509
+    if (s->x + s->w >= inlink->w || s->y + s->h >= inlink->h) {
510
+        av_log(inlink->dst, AV_LOG_WARNING, "scope position is out of range, clipping\n");
511
+        s->x = FFMIN(s->x, inlink->w - s->w);
512
+        s->y = FFMIN(s->y, inlink->h - s->h);
513
+    }
514
+
515
+    return 0;
516
+}
517
+
518
+static int pixscope_filter_frame(AVFilterLink *inlink, AVFrame *in)
519
+{
520
+    AVFilterContext *ctx  = inlink->dst;
521
+    PixscopeContext *s = ctx->priv;
522
+    AVFilterLink *outlink = ctx->outputs[0];
523
+    int max[4] = { 0 }, min[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX };
524
+    float average[4] = { 0 };
525
+    double rms[4] = { 0 };
526
+    const char rgba[4] = { 'R', 'G', 'B', 'A' };
527
+    const char yuva[4] = { 'Y', 'U', 'V', 'A' };
528
+    int x, y, X, Y, i, w, h;
529
+    char text[128];
530
+
531
+    w = s->ww / s->w;
532
+    h = s->ww / s->h;
533
+
534
+    if (s->x <= s->ww && s->y <= s->wh) {
535
+        X = in->width - s->ww;
536
+        Y = in->height - s->wh;
537
+    } else {
538
+        X = 0;
539
+        Y = 0;
540
+    }
541
+
542
+    ff_blend_rectangle(&s->draw, &s->dark, in->data, in->linesize,
543
+                       in->width, in->height,
544
+                       X,
545
+                       Y,
546
+                       s->ww,
547
+                       s->wh);
548
+
549
+    for (y = 0; y < s->h; y++) {
550
+        for (x = 0; x < s->w; x++) {
551
+            FFDrawColor color = { { 0 } };
552
+            int value[4] = { 0 };
553
+
554
+            s->pick_color(&s->draw, &color, in, x + s->x, y + s->y, value);
555
+            ff_fill_rectangle(&s->draw, &color, in->data, in->linesize,
556
+                              x * w + (s->ww - 4 - (s->w * w)) / 2 + X, y * h + 2 + Y, w, h);
557
+            for (i = 0; i < 4; i++) {
558
+                rms[i]     += (double)value[i] * (double)value[i];
559
+                average[i] += value[i];
560
+                min[i]      = FFMIN(min[i], value[i]);
561
+                max[i]      = FFMAX(max[i], value[i]);
562
+            }
563
+        }
564
+    }
565
+
566
+    ff_blend_rectangle(&s->draw, &s->black, in->data, in->linesize,
567
+                       in->width, in->height,
568
+                       s->x - 2, s->y - 2, s->w + 4, 1);
569
+
570
+    ff_blend_rectangle(&s->draw, &s->white, in->data, in->linesize,
571
+                       in->width, in->height,
572
+                       s->x - 1, s->y - 1, s->w + 2, 1);
573
+
574
+    ff_blend_rectangle(&s->draw, &s->white, in->data, in->linesize,
575
+                       in->width, in->height,
576
+                       s->x - 1, s->y - 1, 1, s->h + 2);
577
+
578
+    ff_blend_rectangle(&s->draw, &s->black, in->data, in->linesize,
579
+                       in->width, in->height,
580
+                       s->x - 2, s->y - 2, 1, s->h + 4);
581
+
582
+    ff_blend_rectangle(&s->draw, &s->white, in->data, in->linesize,
583
+                       in->width, in->height,
584
+                       s->x - 1, s->y + 1 + s->h, s->w + 3, 1);
585
+
586
+    ff_blend_rectangle(&s->draw, &s->black, in->data, in->linesize,
587
+                       in->width, in->height,
588
+                       s->x - 2, s->y + 2 + s->h, s->w + 4, 1);
589
+
590
+    ff_blend_rectangle(&s->draw, &s->white, in->data, in->linesize,
591
+                       in->width, in->height,
592
+                       s->x + 1 + s->w, s->y - 1, 1, s->h + 2);
593
+
594
+    ff_blend_rectangle(&s->draw, &s->black, in->data, in->linesize,
595
+                       in->width, in->height,
596
+                       s->x + 2 + s->w, s->y - 2, 1, s->h + 5);
597
+
598
+    for (i = 0; i < 4; i++) {
599
+        rms[i] /= s->w * s->h;
600
+        rms[i]  = sqrt(rms[i]);
601
+        average[i] /= s->w * s->h;
602
+    }
603
+
604
+    snprintf(text, sizeof(text), "CH   AVG    MIN    MAX    RMS\n");
605
+    draw_text(&s->draw, in, &s->white,        X + 28, Y + s->ww + 20,           text, 0);
606
+    for (i = 0; i < s->nb_comps; i++) {
607
+        int c = s->rgba_map[i];
608
+
609
+        snprintf(text, sizeof(text), "%c  %07.1f %05d %05d %07.1f\n", s->is_rgb ? rgba[i] : yuva[i], average[c], min[c], max[c], rms[c]);
610
+        draw_text(&s->draw, in, s->colors[i], X + 28, Y + s->ww + 20 * (i + 2), text, 0);
611
+    }
612
+
613
+    return ff_filter_frame(outlink, in);
614
+}
615
+
616
+static const AVFilterPad pixscope_inputs[] = {
617
+    {
618
+        .name           = "default",
619
+        .type           = AVMEDIA_TYPE_VIDEO,
620
+        .filter_frame   = pixscope_filter_frame,
621
+        .config_props   = pixscope_config_input,
622
+        .needs_writable = 1,
623
+    },
624
+    { NULL }
625
+};
626
+
627
+static const AVFilterPad pixscope_outputs[] = {
628
+    {
629
+        .name         = "default",
630
+        .type         = AVMEDIA_TYPE_VIDEO,
631
+    },
632
+    { NULL }
633
+};
634
+
635
+AVFilter ff_vf_pixscope = {
636
+    .name          = "pixscope",
637
+    .description   = NULL_IF_CONFIG_SMALL("Pixel data analysis."),
638
+    .priv_size     = sizeof(PixscopeContext),
639
+    .priv_class    = &pixscope_class,
640
+    .query_formats = query_formats,
641
+    .inputs        = pixscope_inputs,
642
+    .outputs       = pixscope_outputs,
643
+};