Hi, patch attached.
There is bug in format negotiation, xyz12 format is preferred over others when using yuv inputs. The hacky xyz12 swscale output sucks.
From d2712eec65842b3423cb4eff012d718d8c5c714f Mon Sep 17 00:00:00 2001 From: Paul B Mahol <one...@gmail.com> Date: Mon, 1 Feb 2016 21:05:16 +0100 Subject: [PATCH] avfilter: add chromascope filter Signed-off-by: Paul B Mahol <one...@gmail.com> --- libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/vf_chromascope.c | 1079 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1081 insertions(+) create mode 100644 libavfilter/vf_chromascope.c diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 10c2e0b..d6660cd 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -121,6 +121,7 @@ OBJS-$(CONFIG_BLEND_FILTER) += vf_blend.o dualinput.o framesync OBJS-$(CONFIG_BOXBLUR_FILTER) += vf_boxblur.o OBJS-$(CONFIG_BWDIF_FILTER) += vf_bwdif.o OBJS-$(CONFIG_CHROMAKEY_FILTER) += vf_chromakey.o +OBJS-$(CONFIG_CHROMASCOPE_FILTER) += vf_chromascope.o OBJS-$(CONFIG_CODECVIEW_FILTER) += vf_codecview.o OBJS-$(CONFIG_COLORBALANCE_FILTER) += vf_colorbalance.o OBJS-$(CONFIG_COLORCHANNELMIXER_FILTER) += vf_colorchannelmixer.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index ed52649..65b024c 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -142,6 +142,7 @@ void avfilter_register_all(void) REGISTER_FILTER(BOXBLUR, boxblur, vf); REGISTER_FILTER(BWDIF, bwdif, vf); REGISTER_FILTER(CHROMAKEY, chromakey, vf); + REGISTER_FILTER(CHROMASCOPE, chromascope, vf); REGISTER_FILTER(CODECVIEW, codecview, vf); REGISTER_FILTER(COLORBALANCE, colorbalance, vf); REGISTER_FILTER(COLORCHANNELMIXER, colorchannelmixer, vf); diff --git a/libavfilter/vf_chromascope.c b/libavfilter/vf_chromascope.c new file mode 100644 index 0000000..63594b3 --- /dev/null +++ b/libavfilter/vf_chromascope.c @@ -0,0 +1,1079 @@ +/* + * Copyright (c) 2000 John Walker + * Copyright (c) 2016 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avassert.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +enum ColorsSystems { + NTSCsystem, + EBUsystem, + SMPTEsystem, + HDTVsystem, + APPLEsystem, + CIE1931system, + Rec709system, + Rec2020system, + NB_CS +}; + +typedef struct ChromascopeContext { + const AVClass *class; + int color_system; + int size; + int show_white; + int showBlack; + int full_chart; + int correct_gamma; + int luv; + float intensity; + int background; + + double m[3][3]; + AVFrame *f; +} ChromascopeContext; + +#define OFFSET(x) offsetof(ChromascopeContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM + +static const AVOption chromascope_options[] = { + { "system", "set chromascope color system", OFFSET(color_system), AV_OPT_TYPE_INT, {.i64=Rec709system}, 0, NB_CS-1, FLAGS, "system" }, + { "ntsc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=NTSCsystem}, 0, 0, FLAGS, "system" }, + { "ebu", NULL, 0, AV_OPT_TYPE_CONST, {.i64=EBUsystem}, 0, 0, FLAGS, "system" }, + { "smpte", NULL, 0, AV_OPT_TYPE_CONST, {.i64=SMPTEsystem}, 0, 0, FLAGS, "system" }, + { "hdtv", NULL, 0, AV_OPT_TYPE_CONST, {.i64=HDTVsystem}, 0, 0, FLAGS, "system" }, + { "apple", NULL, 0, AV_OPT_TYPE_CONST, {.i64=APPLEsystem}, 0, 0, FLAGS, "system" }, + { "cie1931", NULL, 0, AV_OPT_TYPE_CONST, {.i64=CIE1931system}, 0, 0, FLAGS, "system" }, + { "rec709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=Rec709system}, 0, 0, FLAGS, "system" }, + { "rec2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=Rec2020system}, 0, 0, FLAGS, "system" }, + { "size", "set chromascope size", OFFSET(size), AV_OPT_TYPE_INT, {.i64=512}, 256, 8192, FLAGS }, + { "s", "set chromascope size", OFFSET(size), AV_OPT_TYPE_INT, {.i64=512}, 256, 8192, FLAGS }, + { "intensity", "set chromascope intensity", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0.001}, 0, 1, FLAGS }, + { "i", "set chromascope intensity", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0.001}, 0, 1, FLAGS }, + { "fullchart", NULL, OFFSET(full_chart), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, + { "correctgamma", NULL, OFFSET(correct_gamma), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, + { "showwhite", NULL, OFFSET(show_white), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, + { "luv", "CIE 1976 Lu'v'", OFFSET(luv), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(chromascope); + +static const enum AVPixelFormat in_pix_fmts[] = { + AV_PIX_FMT_RGBA, + AV_PIX_FMT_XYZ12, + AV_PIX_FMT_NONE +}; + +static const enum AVPixelFormat out_pix_fmts[] = { + AV_PIX_FMT_RGBA64, + AV_PIX_FMT_NONE +}; + +static int query_formats(AVFilterContext *ctx) +{ + int ret; + + if ((ret = ff_formats_ref(ff_make_format_list(in_pix_fmts), &ctx->inputs[0]->out_formats)) < 0) + return ret; + + if ((ret = ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->in_formats)) < 0) + return ret; + + return 0; +} + +static int config_output(AVFilterLink *outlink) +{ + ChromascopeContext *s = outlink->src->priv; + + outlink->h = outlink->w = s->size; + outlink->sample_aspect_ratio = (AVRational){1,1}; + return 0; +} + +/* A color system is defined by the CIE x and y coordinates of its + three primary illuminants and the x and y coordinates of the white + point. */ + +struct ColorSystem { + const char *name; /* Color system name */ + double xRed, yRed, /* Red primary illuminant */ + xGreen, yGreen, /* Green primary illuminant */ + xBlue, yBlue, /* Blue primary illuminant */ + xWhite, yWhite, /* White point */ + gamma; /* gamma of nonlinear correction */ +}; + +/* The following table gives the CIE color matching functions + \bar{x}(\lambda), \bar{y}(\lambda), and \bar{z}(\lambda), for + wavelengths \lambda at 5 nanometre increments from 380 nm through + 780 nm. This table is used in conjunction with Planck's law for + the energy spectrum of a black body at a given temperature to plot + the black body curve on the CIE chart. */ + +static float const cie_color_match[][3] = { + { 0.0014, 0.0000, 0.0065 }, /* 380 nm */ + { 0.0022, 0.0001, 0.0105 }, + { 0.0042, 0.0001, 0.0201 }, + { 0.0076, 0.0002, 0.0362 }, + { 0.0143, 0.0004, 0.0679 }, + { 0.0232, 0.0006, 0.1102 }, + { 0.0435, 0.0012, 0.2074 }, + { 0.0776, 0.0022, 0.3713 }, + { 0.1344, 0.0040, 0.6456 }, + { 0.2148, 0.0073, 1.0391 }, + { 0.2839, 0.0116, 1.3856 }, + { 0.3285, 0.0168, 1.6230 }, + { 0.3483, 0.0230, 1.7471 }, + { 0.3481, 0.0298, 1.7826 }, + { 0.3362, 0.0380, 1.7721 }, + { 0.3187, 0.0480, 1.7441 }, + { 0.2908, 0.0600, 1.6692 }, + { 0.2511, 0.0739, 1.5281 }, + { 0.1954, 0.0910, 1.2876 }, + { 0.1421, 0.1126, 1.0419 }, + { 0.0956, 0.1390, 0.8130 }, + { 0.0580, 0.1693, 0.6162 }, + { 0.0320, 0.2080, 0.4652 }, + { 0.0147, 0.2586, 0.3533 }, + { 0.0049, 0.3230, 0.2720 }, + { 0.0024, 0.4073, 0.2123 }, + { 0.0093, 0.5030, 0.1582 }, + { 0.0291, 0.6082, 0.1117 }, + { 0.0633, 0.7100, 0.0782 }, + { 0.1096, 0.7932, 0.0573 }, + { 0.1655, 0.8620, 0.0422 }, + { 0.2257, 0.9149, 0.0298 }, + { 0.2904, 0.9540, 0.0203 }, + { 0.3597, 0.9803, 0.0134 }, + { 0.4334, 0.9950, 0.0087 }, + { 0.5121, 1.0000, 0.0057 }, + { 0.5945, 0.9950, 0.0039 }, + { 0.6784, 0.9786, 0.0027 }, + { 0.7621, 0.9520, 0.0021 }, + { 0.8425, 0.9154, 0.0018 }, + { 0.9163, 0.8700, 0.0017 }, + { 0.9786, 0.8163, 0.0014 }, + { 1.0263, 0.7570, 0.0011 }, + { 1.0567, 0.6949, 0.0010 }, + { 1.0622, 0.6310, 0.0008 }, + { 1.0456, 0.5668, 0.0006 }, + { 1.0026, 0.5030, 0.0003 }, + { 0.9384, 0.4412, 0.0002 }, + { 0.8544, 0.3810, 0.0002 }, + { 0.7514, 0.3210, 0.0001 }, + { 0.6424, 0.2650, 0.0000 }, + { 0.5419, 0.2170, 0.0000 }, + { 0.4479, 0.1750, 0.0000 }, + { 0.3608, 0.1382, 0.0000 }, + { 0.2835, 0.1070, 0.0000 }, + { 0.2187, 0.0816, 0.0000 }, + { 0.1649, 0.0610, 0.0000 }, + { 0.1212, 0.0446, 0.0000 }, + { 0.0874, 0.0320, 0.0000 }, + { 0.0636, 0.0232, 0.0000 }, + { 0.0468, 0.0170, 0.0000 }, + { 0.0329, 0.0119, 0.0000 }, + { 0.0227, 0.0082, 0.0000 }, + { 0.0158, 0.0057, 0.0000 }, + { 0.0114, 0.0041, 0.0000 }, + { 0.0081, 0.0029, 0.0000 }, + { 0.0058, 0.0021, 0.0000 }, + { 0.0041, 0.0015, 0.0000 }, + { 0.0029, 0.0010, 0.0000 }, + { 0.0020, 0.0007, 0.0000 }, + { 0.0014, 0.0005, 0.0000 }, + { 0.0010, 0.0004, 0.0000 }, + { 0.0007, 0.0002, 0.0000 }, + { 0.0005, 0.0002, 0.0000 }, + { 0.0003, 0.0001, 0.0000 }, + { 0.0002, 0.0001, 0.0000 }, + { 0.0002, 0.0001, 0.0000 }, + { 0.0001, 0.0000, 0.0000 }, + { 0.0001, 0.0000, 0.0000 }, + { 0.0001, 0.0000, 0.0000 }, + { 0.0000, 0.0000, 0.0000 } /* 780 nm */ +}; + +/* The following table gives the spectral chromaticity co-ordinates + x(\lambda) and y(\lambda) for wavelengths in 5 nanometre increments + from 380 nm through 780 nm. These co-ordinates represent the + position in the CIE x-y space of pure spectral colors of the given + wavelength, and thus define the outline of the CIE "tongue" + diagram. */ + +static float spectral_chromaticity[81][3] = { + { 0.1741, 0.0050 }, /* 380 nm */ + { 0.1740, 0.0050 }, + { 0.1738, 0.0049 }, + { 0.1736, 0.0049 }, + { 0.1733, 0.0048 }, + { 0.1730, 0.0048 }, + { 0.1726, 0.0048 }, + { 0.1721, 0.0048 }, + { 0.1714, 0.0051 }, + { 0.1703, 0.0058 }, + { 0.1689, 0.0069 }, + { 0.1669, 0.0086 }, + { 0.1644, 0.0109 }, + { 0.1611, 0.0138 }, + { 0.1566, 0.0177 }, + { 0.1510, 0.0227 }, + { 0.1440, 0.0297 }, + { 0.1355, 0.0399 }, + { 0.1241, 0.0578 }, + { 0.1096, 0.0868 }, + { 0.0913, 0.1327 }, + { 0.0687, 0.2007 }, + { 0.0454, 0.2950 }, + { 0.0235, 0.4127 }, + { 0.0082, 0.5384 }, + { 0.0039, 0.6548 }, + { 0.0139, 0.7502 }, + { 0.0389, 0.8120 }, + { 0.0743, 0.8338 }, + { 0.1142, 0.8262 }, + { 0.1547, 0.8059 }, + { 0.1929, 0.7816 }, + { 0.2296, 0.7543 }, + { 0.2658, 0.7243 }, + { 0.3016, 0.6923 }, + { 0.3373, 0.6589 }, + { 0.3731, 0.6245 }, + { 0.4087, 0.5896 }, + { 0.4441, 0.5547 }, + { 0.4788, 0.5202 }, + { 0.5125, 0.4866 }, + { 0.5448, 0.4544 }, + { 0.5752, 0.4242 }, + { 0.6029, 0.3965 }, + { 0.6270, 0.3725 }, + { 0.6482, 0.3514 }, + { 0.6658, 0.3340 }, + { 0.6801, 0.3197 }, + { 0.6915, 0.3083 }, + { 0.7006, 0.2993 }, + { 0.7079, 0.2920 }, + { 0.7140, 0.2859 }, + { 0.7190, 0.2809 }, + { 0.7230, 0.2770 }, + { 0.7260, 0.2740 }, + { 0.7283, 0.2717 }, + { 0.7300, 0.2700 }, + { 0.7311, 0.2689 }, + { 0.7320, 0.2680 }, + { 0.7327, 0.2673 }, + { 0.7334, 0.2666 }, + { 0.7340, 0.2660 }, + { 0.7344, 0.2656 }, + { 0.7346, 0.2654 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 } /* 780 nm */ +}; + + +/* Standard white point chromaticities. */ + +#define C 0.31006, 0.31616 +#define E 1.0/3.0, 1.0/3.0 +#define D65 0.31271, 0.32902 + +/* Gamma of nonlinear correction. + See Charles Poynton's ColorFAQ Item 45 and GammaFAQ Item 6 at + http://www.inforamp.net/~poynton/ColorFAQ.html + http://www.inforamp.net/~poynton/GammaFAQ.html +*/ + +#define GAMMA_REC709 0. /* Rec. 709 */ + +static const struct ColorSystem color_systems[] = { + [NTSCsystem] = { // also BT 470M + "NTSC", + 0.67, 0.33, 0.21, 0.71, 0.14, 0.08, + C, GAMMA_REC709 + }, + [EBUsystem] = { + "EBU (PAL/SECAM)", // also BT 601_625 + 0.64, 0.33, 0.29, 0.60, 0.15, 0.06, + D65, GAMMA_REC709 + }, + [SMPTEsystem] = { // also BT 601_525 + "SMPTE", + 0.630, 0.340, 0.310, 0.595, 0.155, 0.070, + D65, GAMMA_REC709 + }, + [HDTVsystem] = { + "HDTV", + 0.670, 0.330, 0.210, 0.710, 0.150, 0.060, + D65, GAMMA_REC709 + }, + [APPLEsystem] = { + "Apple", + 0.625, 0.340, 0.280, 0.595, 0.115, 0.070, + D65, GAMMA_REC709 + }, + [CIE1931system] = { + "CIE 1931", + 0.7347, 0.2653, 0.2738, 0.7174, 0.1666, 0.0089, + E, GAMMA_REC709 + }, + [Rec709system] = { + "CIE REC 709", + 0.64, 0.33, 0.30, 0.60, 0.15, 0.06, + D65, GAMMA_REC709 + }, + [Rec2020system] = { + "CIE REC 2020", + 0.708, 0.292, 0.170, 0.797, 0.131, 0.046, + D65, GAMMA_REC709 + }, +}; + +static struct ColorSystem CustomSystem = { + "Custom", + 0.64, 0.33, 0.30, 0.60, 0.15, 0.06, + D65, GAMMA_REC709 +}; + +static void +upvp_to_xy(double const up, + double const vp, + double * const xc, + double * const yc) +{ +/*---------------------------------------------------------------------------- + Given 1976 coordinates u', v', determine 1931 chromaticities x, y +-----------------------------------------------------------------------------*/ + *xc = 9*up / (6*up - 16*vp + 12); + *yc = 4*vp / (6*up - 16*vp + 12); +} + +static void +xy_to_upvp(double xc, + double yc, + double * const up, + double * const vp) +{ +/*---------------------------------------------------------------------------- + Given 1931 chromaticities x, y, determine 1976 coordinates u', v' +-----------------------------------------------------------------------------*/ + *up = 4*xc / (- 2*xc + 12*yc + 3); + *vp = 9*yc / (- 2*xc + 12*yc + 3); +} + +static void +xyz_to_rgb(const struct ColorSystem * const cs, + double xc, + double yc, + double zc, + double * const r, + double * const g, + double * const b) +{ +/*---------------------------------------------------------------------------- + Given an additive tricolor system CS, defined by the CIE x and y + chromaticities of its three primaries (z is derived trivially as + 1-(x+y)), and a desired chromaticity (XC, YC, ZC) in CIE space, + determine the contribution of each primary in a linear combination + which sums to the desired chromaticity. If the requested + chromaticity falls outside the Maxwell triangle (color gamut) + formed by the three primaries, one of the r, g, or b weights will + be negative. + + Caller can use constrain_rgb() to desaturate an outside-gamut + color to the closest representation within the available + gamut. +-----------------------------------------------------------------------------*/ + double xr, yr, zr, xg, yg, zg, xb, yb, zb; + double xw, yw, zw; + double rx, ry, rz, gx, gy, gz, bx, by, bz; + double rw, gw, bw; + + xr = cs->xRed; yr = cs->yRed; zr = 1 - (xr + yr); + xg = cs->xGreen; yg = cs->yGreen; zg = 1 - (xg + yg); + xb = cs->xBlue; yb = cs->yBlue; zb = 1 - (xb + yb); + + xw = cs->xWhite; yw = cs->yWhite; zw = 1 - (xw + yw); + + /* xyz -> rgb matrix, before scaling to white. */ + rx = yg*zb - yb*zg; ry = xb*zg - xg*zb; rz = xg*yb - xb*yg; + gx = yb*zr - yr*zb; gy = xr*zb - xb*zr; gz = xb*yr - xr*yb; + bx = yr*zg - yg*zr; by = xg*zr - xr*zg; bz = xr*yg - xg*yr; + + /* White scaling factors. + Dividing by yw scales the white luminance to unity, as conventional. */ + rw = (rx*xw + ry*yw + rz*zw) / yw; + gw = (gx*xw + gy*yw + gz*zw) / yw; + bw = (bx*xw + by*yw + bz*zw) / yw; + + /* xyz -> rgb matrix, correctly scaled to white. */ + rx = rx / rw; ry = ry / rw; rz = rz / rw; + gx = gx / gw; gy = gy / gw; gz = gz / gw; + bx = bx / bw; by = by / bw; bz = bz / bw; + + /* rgb of the desired point */ + *r = rx*xc + ry*yc + rz*zc; + *g = gx*xc + gy*yc + gz*zc; + *b = bx*xc + by*yc + bz*zc; +} + +static void invert_matrix3x3(double m[3][3]) +{ + int i, j; + double m00 = m[0][0], m01 = m[0][1], m02 = m[0][2], + m10 = m[1][0], m11 = m[1][1], m12 = m[1][2], + m20 = m[2][0], m21 = m[2][1], m22 = m[2][2]; + + // calculate the adjoint + m[0][0] = (m11 * m22 - m21 * m12); + m[0][1] = -(m01 * m22 - m21 * m02); + m[0][2] = (m01 * m12 - m11 * m02); + m[1][0] = -(m10 * m22 - m20 * m12); + m[1][1] = (m00 * m22 - m20 * m02); + m[1][2] = -(m00 * m12 - m10 * m02); + m[2][0] = (m10 * m21 - m20 * m11); + m[2][1] = -(m00 * m21 - m20 * m01); + m[2][2] = (m00 * m11 - m10 * m01); + + // calculate the determinant (as inverse == 1/det * adjoint, + // adjoint * m == identity * det, so this calculates the det) + double det = m00 * m[0][0] + m10 * m[0][1] + m20 * m[0][2]; + det = 1.0 / det; + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) + m[i][j] *= det; + } +} + +static void get_rgb2xyz_matrix(struct ColorSystem system, double m[3][3]) +{ + double S[3], X[4], Z[4]; + int i; + + // Convert from CIE xyY to XYZ. Note that Y=1 holds true for all primaries + X[0] = system.xRed / system.yRed; + X[1] = system.xGreen / system.yGreen; + X[2] = system.xBlue / system.yBlue; + X[3] = system.xWhite / system.yWhite; + + Z[0] = (1 - system.xRed - system.yRed) / system.yRed; + Z[1] = (1 - system.xGreen - system.yGreen) / system.yGreen; + Z[2] = (1 - system.xBlue - system.yBlue) / system.yBlue; + Z[3] = (1 - system.xWhite - system.yWhite) / system.yWhite; + + // S = XYZ^-1 * W + for (i = 0; i < 3; i++) { + m[0][i] = X[i]; + m[1][i] = 1; + m[2][i] = Z[i]; + } + + invert_matrix3x3(m); + + for (i = 0; i < 3; i++) + S[i] = m[i][0] * X[3] + m[i][1] * 1 + m[i][2] * Z[3]; + + // M = [Sc * XYZc] + for (i = 0; i < 3; i++) { + m[0][i] = S[i] * X[i]; + m[1][i] = S[i] * 1; + m[2][i] = S[i] * Z[i]; + } +} + +static void +rgb_to_xyz(double rc, + double gc, + double bc, + double * const x, + double * const y, + double * const z, + const double m[3][3]) +{ + double sum; + + *x = m[0][0] * rc + m[0][1] * gc + m[0][2] * bc; + *y = m[1][0] * rc + m[1][1] * gc + m[1][2] * bc; + *z = m[2][0] * rc + m[2][1] * gc + m[2][2] * bc; + + sum = *x + *y + *z; + + *x = *x / sum; + *y = *y / sum; +} + +static int +constrain_rgb(double * const r, + double * const g, + double * const b) +{ +/*---------------------------------------------------------------------------- + If the requested RGB shade contains a negative weight for one of + the primaries, it lies outside the color gamut accessible from + the given triple of primaries. Desaturate it by adding white, + equal quantities of R, G, and B, enough to make RGB all positive. +-----------------------------------------------------------------------------*/ + double w; + + /* Amount of white needed is w = - min(0, *r, *g, *b) */ + w = (0 < *r) ? 0 : *r; + w = (w < *g) ? w : *g; + w = (w < *b) ? w : *b; + w = - w; + + /* Add just enough white to make r, g, b all positive. */ + if (w > 0) { + *r += w; *g += w; *b += w; + + return 1; /* Color modified to fit RGB gamut */ + } + + return 0; /* Color within RGB gamut */ +} + +static void +gamma_correct(const struct ColorSystem * const cs, + double * const c) +{ +/*---------------------------------------------------------------------------- + Transform linear RGB values to nonlinear RGB values. + + Rec. 709 is ITU-R Recommendation BT. 709 (1990) + ``Basic Parameter Values for the HDTV Standard for the Studio and for + International Programme Exchange'', formerly CCIR Rec. 709. + + For details see + http://www.inforamp.net/~poynton/ColorFAQ.html + http://www.inforamp.net/~poynton/GammaFAQ.html +-----------------------------------------------------------------------------*/ + double gamma; + double cc; + + gamma = cs->gamma; + + if (gamma == 0.) { + /* Rec. 709 gamma correction. */ + cc = 0.018; + if (*c < cc) { + *c *= (1.099 * pow(cc, 0.45) - 0.099) / cc; + } else { + *c = 1.099 * pow(*c, 0.45) - 0.099; + } + } else { + /* Nonlinear color = (Linear color)^(1/gamma) */ + *c = pow(*c, 1./gamma); + } +} + + + +static void +gamma_correct_rgb(const struct ColorSystem * const cs, + double * const r, + double * const g, + double * const b) +{ + gamma_correct(cs, r); + gamma_correct(cs, g); + gamma_correct(cs, b); +} + +/* Sz(X) is the displacement in pixels of a displacement of X normalized + distance units. (A normalized distance unit is 1/512 of the smaller + dimension of the canvas) +*/ +#define Sz(x) (((x) * (int)FFMIN(w, h)) / 512) + +static void +computeMonochromeColorLocation( + double waveLength, + int w, + int h, + int upvp, + int * xP, + int * yP) +{ + const int ix = (waveLength - 380) / 5; + const double px = spectral_chromaticity[ix][0]; + const double py = spectral_chromaticity[ix][1]; + + if (upvp) { + double up, vp; + xy_to_upvp(px, py, &up, &vp); + *xP = up * (w - 1); + *yP = (h - 1) - vp * (h - 1); + } else { + *xP = px * (w - 1); + *yP = (h - 1) - py * (h - 1); + } +} + +static void +find_tongue(uint16_t* pixels, + int const w, + int const linesize, + int const row, + int * const presentP, + int * const leftEdgeP, + int * const rightEdgeP) +{ + int i; + + for (i = 0; i < w && pixels[row * linesize + i * 4 + 0] == 0; i++) + ; + + if (i >= w) { + *presentP = 0; + } else { + int j; + int const leftEdge = i; + + *presentP = 1; + + for (j = w - 1; j >= leftEdge && pixels[row * linesize + j * 4 + 0] == 0; j--) + ; + + *rightEdgeP = j; + *leftEdgeP = leftEdge; + } +} + +static void draw_line(uint16_t *const pixels, int linesize, + int x0, int y0, int x1, int y1, + int w, int h, + const uint16_t *const rgbcolor) +{ + int dx = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = (dx > dy ? dx : -dy) / 2, e2; + + for (;;) { + pixels[y0 * linesize + x0 * 4 + 0] = rgbcolor[0]; + pixels[y0 * linesize + x0 * 4 + 1] = rgbcolor[1]; + pixels[y0 * linesize + x0 * 4 + 2] = rgbcolor[2]; + pixels[y0 * linesize + x0 * 4 + 3] = rgbcolor[3]; + + if (x0 == x1 && y0 == y1) + break; + + e2 = err; + + if (e2 >-dx) { + err -= dy; + x0 += sx; + } + + if (e2 < dy) { + err += dx; + y0 += sy; + } + } +} + +static void draw_rline(uint16_t *const pixels, int linesize, + int x0, int y0, int x1, int y1, + int w, int h) +{ + int dx = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = (dx > dy ? dx : -dy) / 2, e2; + + for (;;) { + pixels[y0 * linesize + x0 * 4 + 0] = 65535 - pixels[y0 * linesize + x0 * 4 + 0]; + pixels[y0 * linesize + x0 * 4 + 1] = 65535 - pixels[y0 * linesize + x0 * 4 + 1]; + pixels[y0 * linesize + x0 * 4 + 2] = 65535 - pixels[y0 * linesize + x0 * 4 + 2]; + pixels[y0 * linesize + x0 * 4 + 3] = 65535; + + if (x0 == x1 && y0 == y1) + break; + + e2 = err; + + if (e2 >-dx) { + err -= dy; + x0 += sx; + } + + if (e2 < dy) { + err += dx; + y0 += sy; + } + } +} + +static void +tongue_outline(uint16_t* const pixels, + int const linesize, + int const w, + int const h, + uint16_t const maxval, + int const upvp, + int const xBias, + int const yBias) +{ + const uint16_t rgbcolor[4] = { 65535, 65535, 65535, 65535 }; + int wavelength; + int lx, ly; + int fx, fy; + + for (wavelength = 380; wavelength <= 700; wavelength += 5) { + int icx, icy; + + computeMonochromeColorLocation(wavelength, w, h, upvp, + &icx, &icy); + + if (wavelength > 380) + draw_line(pixels, linesize, lx, ly, icx, icy, w, h, rgbcolor); + else { + fx = icx; + fy = icy; + } + lx = icx; + ly = icy; + } + draw_line(pixels, linesize, lx, ly, fx, fy, w, h, rgbcolor); +} + +static void +fill_in_tongue(uint16_t* const pixels, + int const linesize, + int const w, + int const h, + uint16_t const maxval, + const struct ColorSystem * const cs, + int const upvp, + int const xBias, + int const yBias, + int const highlight_gamut, + int const correct_gamma) +{ + int y; + + /* Scan the image line by line and fill the tongue outline + with the RGB values determined by the color system for the x-y + co-ordinates within the tongue. + */ + + for (y = 0; y < h; ++y) { + int present; /* There is some tongue on this line */ + int leftEdge; /* x position of leftmost pixel in tongue on this line */ + int rightEdge; /* same, but rightmost */ + + find_tongue(pixels, w, linesize, y, &present, &leftEdge, &rightEdge); + + if (present) { + int x; + + for (x = leftEdge; x <= rightEdge; ++x) { + double cx, cy, cz, jr, jg, jb, jmax; + int r, g, b, mx; + + if (upvp) { + double up, vp; + up = ((double) x) / (w - 1); + vp = 1.0 - ((double) y) / (h - 1); + upvp_to_xy(up, vp, &cx, &cy); + cz = 1.0 - (cx + cy); + } else { + cx = ((double) x) / (w - 1); + cy = 1.0 - ((double) y) / (h - 1); + cz = 1.0 - (cx + cy); + } + + xyz_to_rgb(cs, cx, cy, cz, &jr, &jg, &jb); + + mx = maxval; + + /* Check whether the requested color is within the + gamut achievable with the given color system. If + not, draw it in a reduced intensity, interpolated + by desaturation to the closest within-gamut color. */ + + if (constrain_rgb(&jr, &jg, &jb)) + mx = highlight_gamut ? maxval : ((maxval + 1) * 3) / 4; + + /* Scale to max(rgb) = 1. */ + jmax = FFMAX(jr, FFMAX(jg, jb)); + if (jmax > 0) { + jr = jr / jmax; + jg = jg / jmax; + jb = jb / jmax; + } + /* gamma correct from linear rgb to nonlinear rgb. */ + if (correct_gamma) + gamma_correct_rgb(cs, &jr, &jg, &jb); + r = mx * jr; + g = mx * jg; + b = mx * jb; + pixels[y * linesize + x * 4 + 0] = r; + pixels[y * linesize + x * 4 + 1] = g; + pixels[y * linesize + x * 4 + 2] = b; + pixels[y * linesize + x * 4 + 3] = 65535; + } + } + } +} + +static void +plot_white_point(uint16_t* pixels, + int const linesize, + int const w, + int const h, + int const maxval, + int const color_system, + int const upvp) +{ + const struct ColorSystem *cs = &color_systems[color_system]; + int wx, wy; + + if (upvp) { + double wup, wvp; + xy_to_upvp(cs->xWhite, cs->yWhite, &wup, &wvp); + wx = wup; + wy = wvp; + wx = (w - 1) * wup; + wy = (h - 1) - ((int) ((h - 1) * wvp)); + } else { + wx = (w - 1) * cs->xWhite; + wy = (h - 1) - ((int) ((h - 1) * cs->yWhite)); + } + + draw_rline(pixels, linesize, + wx + Sz(3), wy, wx + Sz(10), wy, + w, h); + draw_rline(pixels, linesize, + wx - Sz(3), wy, wx - Sz(10), wy, + w, h); + draw_rline(pixels, linesize, + wx, wy + Sz(3), wx, wy + Sz(10), + w, h); + draw_rline(pixels, linesize, + wx, wy - Sz(3), wx, wy - Sz(10), + w, h); +} + +static int draw_background(AVFilterContext *ctx) +{ + ChromascopeContext *s = ctx->priv; + const struct ColorSystem *cs = &color_systems[s->color_system]; + AVFilterLink *outlink = ctx->outputs[0]; + int w = s->size; + int h = s->size; + uint16_t *pixels; + + if ((s->f = ff_get_video_buffer(outlink, outlink->w, outlink->h)) == NULL) + return AVERROR(ENOMEM); + pixels = (uint16_t *)s->f->data[0]; + + tongue_outline(pixels, s->f->linesize[0] / 2, w, h, 65535, s->luv, 0, 0); + + fill_in_tongue(pixels, s->f->linesize[0] / 2, w, h, 65535, cs, s->luv, 0, 0, + s->full_chart, s->correct_gamma); + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + ChromascopeContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + int i = s->intensity * 65535; + int w = s->size; + int h = s->size; + AVFrame *out; + int ret, x, y; + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + out->pts = in->pts; + + if (!s->background) { + ret = draw_background(ctx); + if (ret < 0) + return ret; + s->background = 1; + } + for (y = 0; y < outlink->h; y++) { + memset(out->data[0] + y * out->linesize[0], 0, outlink->w * 8); + } + + if (in->format == AV_PIX_FMT_RGBA) { + for (y = 0; y < inlink->h; y++) { + for (x = 0; x < inlink->w; x++) { + const uint8_t* src = in->data[0] + in->linesize[0] * y + x * 4; + double r = src[0] / 255.; + double g = src[1] / 255.; + double b = src[2] / 255.; + double cx, cy, cz; + uint16_t *dst; + int wx, wy; + + rgb_to_xyz(r, g, b, &cx, &cy, &cz, s->m); + if (s->luv) { + double up, vp; + xy_to_upvp(cx, cy, &up, &vp); + cx = up; + cy = vp; + } + wx = (w - 1) * cx; + wy = (h - 1) - ((h - 1) * cy); + + if (wx < 0 || wx >= w || + wy < 0 || wy >= h) + continue; + + dst = (uint16_t *)(out->data[0] + wy * out->linesize[0] + wx * 8 + 0); + dst[0] = FFMIN(dst[0] + i, 65535); + dst[1] = FFMIN(dst[1] + i, 65535); + dst[2] = FFMIN(dst[2] + i, 65535); + dst[3] = 65535; + } + } + } else if (in->format == AV_PIX_FMT_XYZ12) { + for (y = 0; y < inlink->h; y++) { + for (x = 0; x < inlink->w; x++) { + const uint16_t* src = (uint16_t *)(in->data[0] + in->linesize[0] * y + x * 6); + double cx = src[0]; + double cy = src[1]; + double cz = src[2]; + double sum = cx + cy + cz; + uint16_t *dst; + int wx, wy; + + cx = cx / sum; + cy = cy / sum; + + if (s->luv) { + double up, vp; + xy_to_upvp(cx, cy, &up, &vp); + cx = up; + cy = vp; + } + wx = (w - 1) * cx; + wy = (h - 1) - ((h - 1) * cy); + + if (wx < 0 || wx >= w || + wy < 0 || wy >= h) + continue; + + dst = (uint16_t *)(out->data[0] + wy * out->linesize[0] + wx * 8 + 0); + dst[0] = FFMIN(dst[0] + i, 65535); + dst[1] = FFMIN(dst[1] + i, 65535); + dst[2] = FFMIN(dst[2] + i, 65535); + dst[3] = 65535; + } + } + } + + for (y = 0; y < outlink->h; y++) { + uint16_t *dst = (uint16_t *)(out->data[0] + y * out->linesize[0]); + uint16_t *src = (uint16_t *)(s->f->data[0] + y * s->f->linesize[0]); + for (x = 0; x < outlink->w; x++) { + if (dst[x * 4 + 0] == 0) { + dst[x * 4 + 0] = src[x * 4 + 0]; + dst[x * 4 + 1] = src[x * 4 + 1]; + dst[x * 4 + 2] = src[x * 4 + 2]; + dst[x * 4 + 3] = src[x * 4 + 3]; + } + } + } + + if (s->show_white) + plot_white_point((uint16_t *)out->data[0], out->linesize[0] / 2, + outlink->w, outlink->h, 65535, + s->color_system, s->luv); + + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static void av_cold uninit(AVFilterContext *ctx) +{ + ChromascopeContext *s = ctx->priv; + + av_frame_free(&s->f); +} + +static int config_input(AVFilterLink *inlink) +{ + ChromascopeContext *s = inlink->dst->priv; + + get_rgb2xyz_matrix(color_systems[s->color_system], s->m); + + return 0; +} + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_input, + }, + { NULL } +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, + { NULL } +}; + +AVFilter ff_vf_chromascope = { + .name = "chromascope", + .description = NULL_IF_CONFIG_SMALL("Video chromascope."), + .priv_size = sizeof(ChromascopeContext), + .priv_class = &chromascope_class, + .query_formats = query_formats, + .uninit = uninit, + .inputs = inputs, + .outputs = outputs, +}; -- 1.9.1
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel