:: Re: [Frei0r] [PATCH] Add "colgate",…
Pàgina inicial
Delete this message
Reply to this message
Autor: Dan Dennedy
Data:  
A: Minimalistic plugin API for video effects
Assumpte: Re: [Frei0r] [PATCH] Add "colgate", a new color correction plugin.
On Wed, Sep 12, 2012 at 2:52 PM, Steinar H. Gunderson
<sgunderson@???> wrote:
> Hi,
>
> This is a new white balancing filter; the rationale should be quite well
> explained in the commit message and the comments. I'm including the patch
> verbatim here for easier review.
>
> I intend to add Kdenlive support when/if the patch is accepted into Frei0r
> (the Kdenlive patch is simple).
>
> /* Steinar */
>
> commit fc992af6a2870a87fe5a9269da42982902459cf7
> Author: Steinar H. Gunderson <sgunderson@???>
> Date: Mon Sep 10 22:34:01 2012 +0200
>
>     Add "colgate", a new color correction plugin.

>
>     colgate works in the LMS color space, and generally seems to give much
>     better results then balanc0r (generally it corrects color casts much
>     more precisely, and without changing the overall saturation or brightness
>     like balanc0r sometimes does).

>
>     I consider it to obsolete balanc0r, although balanc0r is kept for
>     compatibility reasons with older projects etc.

>
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 42eea28..8bcf96d 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -32,6 +32,7 @@ plugin_LTLIBRARIES = \
>         c0rners.la \
>         cartoon.la \
>         cluster.la \
> +       colgate.la \
>         coloradj_RGB.la \
>         colordistance.la \
>         color_only.la \
> @@ -165,6 +166,7 @@ bw0r_la_SOURCES = filter/bw0r/bw0r.c
>  c0rners_la_SOURCES = filter/c0rners/c0rners.c filter/c0rners/interp.h
>  cartoon_la_SOURCES = filter/cartoon/cartoon.cpp
>  cluster_la_SOURCES = filter/cluster/cluster.c
> +colgate_la_SOURCES = filter/colgate/colgate.c
>  coloradj_RGB_la_SOURCES = filter/coloradj/coloradj_RGB.c
>  colordistance_la_SOURCES = filter/colordistance/colordistance.c
>  contrast0r_la_SOURCES = filter/contrast0r/contrast0r.c
> diff --git a/src/filter/CMakeLists.txt b/src/filter/CMakeLists.txt
> index 55e59c2..2de4a6d 100644
> --- a/src/filter/CMakeLists.txt
> +++ b/src/filter/CMakeLists.txt
> @@ -19,6 +19,7 @@ add_subdirectory (brightness)
>  add_subdirectory (bw0r)
>  add_subdirectory (cartoon)
>  add_subdirectory (cluster)
> +add_subdirectory (colgate)
>  add_subdirectory (coloradj)
>  add_subdirectory (colordistance)
>  add_subdirectory (contrast0r)
> diff --git a/src/filter/colgate/CMakeLists.txt b/src/filter/colgate/CMakeLists.txt
> new file mode 100644
> index 0000000..f7b0183
> --- /dev/null
> +++ b/src/filter/colgate/CMakeLists.txt
> @@ -0,0 +1,12 @@
> +set (SOURCES colgate.c)
> +set (TARGET colgate)
> +
> +if (MSVC)
> +  set_source_files_properties (colgate.c PROPERTIES LANGUAGE CXX)
> +  set (SOURCES ${SOURCES} ${FREI0R_DEF})
> +endif (MSVC)
> +
> +add_library (${TARGET}  MODULE ${SOURCES})
> +set_target_properties (${TARGET} PROPERTIES PREFIX "")
> +
> +install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR})
> diff --git a/src/filter/colgate/colgate.c b/src/filter/colgate/colgate.c
> new file mode 100644
> index 0000000..9291b55
> --- /dev/null
> +++ b/src/filter/colgate/colgate.c
> @@ -0,0 +1,493 @@
> +/* colgate.c
> + * Copyright (C) 2012 Steinar H. Gunderson <sgunderson@???>


I have not fully reviewed this or tested it yet, but I just want to
confirm for now if most of this code is really yours or adapted from
somewhere else.

> + *
> + * Color correction in LMS color space.
> + *
> + * This plugin is intended for the same uses as the balanc0r plugin,
> + * but differs from balanc0r in two important aspects:
> + *
> + *   1. It operates in the LMS color space, which is a better approximation
> + *      to the human color sensation than RGB is. This allows for more
> + *      natural color changes. (Note that this inevitably requires that we
> + *      we work in linear color space, not the nonlinear space pixels are
> + *      typically stored in.)
> + *
> + *   2. Its choice of neutral color is not limited by the Planckian locus
> + *      (typically expressed in Kelvins) like balanc0r is; you can choose
> + *      any color, even those that are not typically regarded as “white”.
> + *      If you want a color cast (e.g. a warmer scene), this can be
> + *      adjusted with a separate control for color temperature.
> + *
> + * Color is a very complex topic, and this plugin makes no claims to
> + * being 100% correct in any way; however, the results appear visually
> + * much more meaningful to me than the balanc0r plugin does, although it
> + * also uses significantly more CPU time.
> + *
> + * frei0r plugins are not given any meaningful information about input or
> + * output color space. We assume sRGB, since that typically matches people's
> + * viewing devices pretty well. sRGB in turn very closely matches ITU-R Rec.
> + * BT.709, the standard color space for HDTV; they share the same RGB
> + * primaries, and have a similar gamma (2.35 or 2.4, versus sRGB's curve that
> + * is approximately a gamma curve at 2.2). SDTV uses a different color space,
> + * ITU-R Rec. BT.601, which has somewhat different primaries and a gamma of
> + * 2.2, but in practice, sRGB should be an okay approximation for this as well.
> + *
> + * The color matrices used are typically from Wikipedia, and the inverses
> + * are computed by Octave if nothing else is mentioned.
> + *
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +/*
> + * We use fixed point, since conversion back and forth to floating-point is
> + * slow. (This also enables us to use LUTs in an efficient way for lookup
> + * to and from sRGB, as opposed to a pow()-based solution, which is very slow.)
> + *
> + * We need to think a bit about the range and precision of the different
> + * elements to get the best results here, since linear RGB can span quite
> + * some range. So:
> + *
> + *  - Input pixels (which are in linear RGB, always 0..1) are stored as 1.15.
> + *  - Matrix elements are s4.10.
> + *
> + * Matrix elements up to +/- 16 should give us some headroom; extreme color
> + * adjustments typically have elements of 5-6.
> + *
> + * Our standard operation is input_pixel * matrix_element, which becomes s5.25.
> + * We add three of them, which would seem to be able to overflow, but doesn't,
> + * since the largest pixel is 1.0 (represented as 2^15):
> + *
> + *   3 * 2^15 * (2^14 - 1) ~= 3 * 2^29 < 2^31.
> + *
> + * It _is_ larger than 2^30, though, so we don't have any bits to spare here.
> + */
> +#define INPUT_PIXEL_BITS 15
> +#define MATRIX_ELEMENT_FRAC_BITS 10
> +#define MATRIX_ELEMENT_BITS (4 + MATRIX_ELEMENT_FRAC_BITS)
> +
> +#include <stdlib.h>
> +#include <assert.h>
> +#include <math.h>
> +#include <stdio.h>
> +
> +#include "frei0r.h"
> +#include "frei0r_math.h"
> +
> +enum ParamIndex {
> +       NEUTRAL_COLOR,
> +       COLOR_TEMPERATURE,
> +};
> +
> +// Row major (opposite of OpenGL).
> +typedef float Matrix3x3[9];
> +
> +// Same, but with elements in s4.10 (see above).
> +typedef int Matrix3x3fp[9];
> +
> +typedef struct colgate_instance
> +{
> +       unsigned width;
> +       unsigned height;
> +       f0r_param_color_t neutral_color;
> +       double color_temperature;
> +       Matrix3x3fp corr_matrix;
> +} colgate_instance_t;
> +
> +// Assumes input value in [0..255]; output value is normalized.
> +static float convert_srgb_to_linear_rgb(float x)
> +{
> +       if (x < 255.0f * 0.04045f) {
> +               return x * (1.0f / (255.0f * 12.92f));
> +       } else {
> +               return pow((x + 255.0f * 0.055) * (1.0 / (255.0f * 1.055f)), 2.4);
> +       }
> +}
> +
> +// Assumes normalized input value; output value in [0..255].
> +static float convert_linear_rgb_to_srgb(float x)
> +{
> +       if (x < 0.0031308f) {
> +               return (255.0f * 12.92f) * x;
> +       } else {
> +               return ((255.0f * 1.055f) * pow(x, 1.0f / 2.4f)) - (0.055 * 255.0f);
> +       }
> +}
> +
> +/*
> + * For sRGB -> linear, we always have exact values when we look up,
> + * so we can do with a 8-bit LUT. For the other way, we need at least
> + * 13 bits to be able to distinguish all input values; we go for 14
> + * to get some extra accuracy. This results in an 16 kB LUT, but we
> + * generally don't need the L1 cache for a lot of other things anyway,
> + * so hopefully the LUT can mostly stay in L1 cache.
> + */
> +#define REVERSE_LUT_BITS 14
> +#define REVERSE_LUT_SIZE (1 << REVERSE_LUT_BITS)
> +
> +static int srgb_to_linear_rgb_lut[256];
> +static uint8_t linear_rgb_to_srgb_lut[REVERSE_LUT_SIZE];
> +
> +static void fill_srgb_luts()
> +{
> +       int i;
> +       for (i = 0; i < 256; ++i) {
> +               srgb_to_linear_rgb_lut[i] = lrintf(convert_srgb_to_linear_rgb(i) * (float)(1 << INPUT_PIXEL_BITS));
> +       }
> +       for (i = 0; i < REVERSE_LUT_SIZE; ++i) {
> +               // Subtract 0.5 to compensate for the fact that we don't round
> +               // (which, for our purposes, would entail _adding_ 0.5) at lookup time.
> +               float x = (i - 0.5) / (float)(REVERSE_LUT_SIZE);
> +               int srgb = lrintf(convert_linear_rgb_to_srgb(x));
> +               assert(srgb >= 0 && srgb <= 255);
> +               linear_rgb_to_srgb_lut[i] = srgb;
> +       }
> +}
> +
> +// Multiply two 3x3 matrices.
> +static void multiply_3x3_matrices(const Matrix3x3 a, const Matrix3x3 b, Matrix3x3 result)
> +{
> +       result[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6];
> +       result[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6];
> +       result[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6];
> +
> +       result[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7];
> +       result[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7];
> +       result[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7];
> +
> +       result[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8];
> +       result[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8];
> +       result[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8];
> +}
> +
> +// Multiply a 3x3 matrix with a three-element float vector.
> +static void multiply_3x3_matrix_float3(const Matrix3x3 M, float x0, float x1, float x2, float *y0, float *y1, float *y2)
> +{
> +       *y0 = M[0] * x0 + M[1] * x1 + M[2] * x2;
> +       *y1 = M[3] * x0 + M[4] * x1 + M[5] * x2;
> +       *y2 = M[6] * x0 + M[7] * x1 + M[8] * x2;
> +}
> +
> +// Same as above, but for fixed-point. Results come back unscaled.
> +static inline void multiply_3x3_matrix_fp3(const Matrix3x3fp M, int x0, int x1, int x2, int *y0, int *y1, int *y2)
> +{
> +       *y0 = M[0] * x0 + M[1] * x1 + M[2] * x2;
> +       *y1 = M[3] * x0 + M[4] * x1 + M[5] * x2;
> +       *y2 = M[6] * x0 + M[7] * x1 + M[8] * x2;
> +}
> +
> +// Convert a linear RGB value in s6.25 fixed-point to sRGB (between 0 to 255, inclusive).
> +static inline uint8_t convert_linear_rgb_to_srgb_fp(int x)
> +{
> +       if (x < 0) {
> +               return 0;
> +       }
> +       if (x >= (REVERSE_LUT_SIZE << (INPUT_PIXEL_BITS + MATRIX_ELEMENT_FRAC_BITS - REVERSE_LUT_BITS))) {
> +               return 255;
> +       }
> +       return linear_rgb_to_srgb_lut[((unsigned)x) >> (INPUT_PIXEL_BITS + MATRIX_ELEMENT_FRAC_BITS - REVERSE_LUT_BITS)];
> +}
> +
> +// Temperature is in Kelvin. Formula from http://en.wikipedia.org/wiki/Planckian_locus#Approximation .
> +void convert_color_temperature_to_xyz(float T, float *x, float *y, float *z)
> +{
> +       double invT = 1.0 / T;
> +       double xc, yc;
> +
> +       if (T <= 4000.0f) {
> +               xc = ((-0.2661239e9 * invT - 0.2343580e6) * invT + 0.8776956e3) * invT + 0.179910;
> +       } else {
> +               xc = ((-3.0258469e9 * invT + 2.1070379e6) * invT + 0.2226347e3) * invT + 0.240390;
> +       }
> +
> +       if (T <= 2222.0f) {
> +               yc = ((-1.1063814 * xc - 1.34811020) * xc + 2.18555832) * xc - 0.20219683;
> +       } else if (T <= 4000.0f) {
> +               yc = ((-0.9549476 * xc - 1.37418593) * xc + 2.09137015) * xc - 0.16748867;
> +       } else {
> +               yc = (( 3.0817580 * xc - 5.87338670) * xc + 3.75112997) * xc - 0.37001483;
> +       }
> +
> +       *x = xc;
> +       *y = yc;
> +       *z = 1.0 - xc - yc;
> +}
> +
> +// sRGB primaries.
> +static const Matrix3x3 rgb_to_xyz_matrix = {
> +       0.4124, 0.3576, 0.1805,
> +       0.2126, 0.7152, 0.0722,
> +       0.0193, 0.1192, 0.9505,
> +};
> +static const Matrix3x3 xyz_to_rgb_matrix = {
> +        3.240625, -1.537208, -0.498629,
> +       -0.968931,  1.875756,  0.041518,
> +        0.055710, -0.204021,  1.056996,
> +};
> +
> +static void convert_linear_rgb_to_linear_xyz(float r, float g, float b, float *x, float *y, float *z)
> +{
> +       multiply_3x3_matrix_float3(rgb_to_xyz_matrix, r, g, b, x, y, z);
> +}
> +
> +static void convert_linear_xyz_to_linear_rgb(float x, float y, float z, float *r, float *g, float *b)
> +{
> +       multiply_3x3_matrix_float3(xyz_to_rgb_matrix, x, y, z, r, g, b);
> +}
> +
> +/*
> + * There are several different LMS spaces, at least according to Wikipedia.
> + * Through practical testing, I've found most of them (like the CIECAM02 model)
> + * to yield a result that is too reddish in practice. This is the RLAB space,
> + * normalized to D65, which means that the standard D65 illuminant
> + * (x=0.31271, y=0.32902, z=1-y-x) gives L=M=S under this transformation.
> + * This makes sense because sRGB (which is used to derive those XYZ values
> + * in the first place) assumes the D65 illuminant, and so the D65 illuminant
> + * also gives R=G=B in sRGB.
> + */
> +static const Matrix3x3 xyz_to_lms_matrix = {
> +        0.4002, 0.7076, -0.0808,
> +       -0.2263, 1.1653,  0.0457,
> +           0.0,    0.0,  0.9182,
> +};
> +static const Matrix3x3 lms_to_xyz_matrix = {
> +       1.86007, -1.12948,  0.21990,
> +       0.36122,  0.63880, -0.00001,
> +       0.00000,  0.00000,  1.08909,
> +};
> +
> +static void convert_linear_xyz_to_linear_lms(float x, float y, float z, float *l, float *m, float *s)
> +{
> +       multiply_3x3_matrix_float3(xyz_to_lms_matrix, x, y, z, l, m, s);
> +}
> +
> +static void convert_linear_lms_to_linear_xyz(float l, float m, float s, float *x, float *y, float *z)
> +{
> +       multiply_3x3_matrix_float3(lms_to_xyz_matrix, l, m, s, x, y, z);
> +}
> +
> +/*
> + * For a given reference color (given in XYZ space),
> + * compute scaling factors for L, M and S. What we want at the output is equal L, M and S
> + * for the reference color (making it a neutral illuminant), or sL ref_L = sM ref_M = sS ref_S.
> + * This removes two degrees of freedom for our system, and we only need to find fL.
> + *
> + * A reasonable last constraint would be to preserve Y, approximately the brightness,
> + * for the reference color. Since L'=M'=S' and the Y row of the LMS-to-XYZ matrix
> + * sums to unity, we know that Y'=L', and it's easy to find the fL that sets Y'=Y.
> + */
> +static void compute_lms_scaling_factors(float x, float y, float z, float *scale_l, float *scale_m, float *scale_s)
> +{
> +       float l, m, s;
> +       convert_linear_xyz_to_linear_lms(x, y, z, &l, &m, &s);
> +
> +       *scale_l = y / l;
> +       *scale_m = *scale_l * (l / m);
> +       *scale_s = *scale_l * (l / s);
> +}
> +
> +static void compute_correction_matrix(colgate_instance_t *o)
> +{
> +       int i;
> +
> +       /*
> +        * Find out what the given neutral color would be in LMS space,
> +        * and use that value to build a correction factor for each component
> +        * so that the neutral color really becomes gray (in LMS).
> +        */
> +       float ref_r = o->neutral_color.r * 255.0f;
> +       float ref_g = o->neutral_color.g * 255.0f;
> +       float ref_b = o->neutral_color.b * 255.0f;
> +
> +       float linear_r = convert_srgb_to_linear_rgb(ref_r);
> +       float linear_g = convert_srgb_to_linear_rgb(ref_g);
> +       float linear_b = convert_srgb_to_linear_rgb(ref_b);
> +
> +       float x, y, z;
> +       convert_linear_rgb_to_linear_xyz(linear_r, linear_g, linear_b, &x, &y, &z);
> +
> +       float l, m, s;
> +       convert_linear_xyz_to_linear_lms(x, y, z, &l, &m, &s);
> +
> +       float l_scale, m_scale, s_scale;
> +       compute_lms_scaling_factors(x, y, z, &l_scale, &m_scale, &s_scale);
> +
> +       /*
> +        * Now apply the color balance. Simply put, we find the chromacity point
> +        * for the desired white temperature, see what LMS scaling factors they
> +        * would have given us, and then reverse that transform. For T=6500K,
> +        * the default, this gives us nearly an identity transform (but only nearly,
> +        * since the D65 illuminant does not exactly match the results of T=6500K);
> +        * we normalize so that T=6500K really is a no-op.
> +        */
> +       float white_x, white_y, white_z, l_scale_white, m_scale_white, s_scale_white;
> +       convert_color_temperature_to_xyz(o->color_temperature, &white_x, &white_y, &white_z);
> +       compute_lms_scaling_factors(white_x, white_y, white_z, &l_scale_white, &m_scale_white, &s_scale_white);
> +
> +       float ref_x, ref_y, ref_z, l_scale_ref, m_scale_ref, s_scale_ref;
> +       convert_color_temperature_to_xyz(6500.0f, &ref_x, &ref_y, &ref_z);
> +       compute_lms_scaling_factors(ref_x, ref_y, ref_z, &l_scale_ref, &m_scale_ref, &s_scale_ref);
> +
> +       l_scale *= l_scale_ref / l_scale_white;
> +       m_scale *= m_scale_ref / m_scale_white;
> +       s_scale *= s_scale_ref / s_scale_white;
> +
> +       /*
> +        * Concatenate all the different linear operations into a single 3x3 matrix,
> +        * which is then converted to fixed point and stored. Note that since we
> +        * postmultiply our vectors, the order of the matrices has to be the opposite
> +        * of the execution order.
> +        */
> +       Matrix3x3 temp, temp2, corr_matrix;
> +       Matrix3x3 lms_scale_matrix = {
> +               l_scale,    0.0f,    0.0f,
> +                  0.0f, m_scale,    0.0f,
> +                  0.0f,    0.0f, s_scale,
> +       };
> +       multiply_3x3_matrices(xyz_to_rgb_matrix, lms_to_xyz_matrix, temp);
> +       multiply_3x3_matrices(temp, lms_scale_matrix, temp2);
> +       multiply_3x3_matrices(temp2, xyz_to_lms_matrix, temp);
> +       multiply_3x3_matrices(temp, rgb_to_xyz_matrix, corr_matrix);
> +
> +       for (i = 0; i < 9; ++i) {
> +               o->corr_matrix[i] = lrintf(corr_matrix[i] * (float)(1 << MATRIX_ELEMENT_FRAC_BITS));
> +               if (o->corr_matrix[i] < -(1 << MATRIX_ELEMENT_BITS)) {
> +                       o->corr_matrix[i] = -(1 << MATRIX_ELEMENT_BITS);
> +               }
> +               if (o->corr_matrix[i] > (1 << MATRIX_ELEMENT_BITS) - 1) {
> +                       o->corr_matrix[i] = (1 << MATRIX_ELEMENT_BITS) - 1;
> +               }
> +       }
> +}
> +
> +int f0r_init()
> +{
> +       fill_srgb_luts();
> +       return 1;
> +}
> +
> +void f0r_deinit()
> +{
> +}
> +
> +void f0r_get_plugin_info(f0r_plugin_info_t *colordistance_info)
> +{
> +       colordistance_info->name = "White Balance (LMS space)";
> +       colordistance_info->author = "Steinar H. Gunderson";
> +       colordistance_info->plugin_type = F0R_PLUGIN_TYPE_FILTER;
> +       colordistance_info->color_model = F0R_COLOR_MODEL_RGBA8888;
> +       colordistance_info->frei0r_version = FREI0R_MAJOR_VERSION;
> +       colordistance_info->major_version = 0;
> +       colordistance_info->minor_version = 1;
> +       colordistance_info->num_params = 2;
> +       colordistance_info->explanation = "Do simple color correction, in a physically meaningful way";
> +}
> +
> +void f0r_get_param_info(f0r_param_info_t *info, int param_index)
> +{
> +       switch (param_index) {
> +       case NEUTRAL_COLOR:
> +               info->name = "Neutral Color";
> +               info->type = F0R_PARAM_COLOR;
> +               info->explanation = "Choose a color from the source image that should be white.";
> +               break;
> +
> +       case COLOR_TEMPERATURE:
> +               info->name = "Color Temperature";
> +               info->type = F0R_PARAM_DOUBLE;
> +               info->explanation = "Choose an output color temperature, if different from 6500 K.";
> +               break;
> +       }
> +
> +}
> +
> +f0r_instance_t f0r_construct(unsigned width, unsigned height)
> +{
> +       colgate_instance_t *inst = (colgate_instance_t *)calloc(1, sizeof(*inst));
> +       inst->width = width;
> +       inst->height = height;
> +       inst->neutral_color.r = 0.5;
> +       inst->neutral_color.g = 0.5;
> +       inst->neutral_color.b = 0.5;
> +       inst->color_temperature = 6500.0;
> +       compute_correction_matrix(inst);
> +       return (f0r_instance_t)inst;
> +}
> +
> +void f0r_destruct(f0r_instance_t instance)
> +{
> +       free(instance);
> +}
> +
> +void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int param_index)
> +{
> +       assert(instance);
> +       colgate_instance_t *inst = (colgate_instance_t *)instance;
> +
> +       switch (param_index) {
> +       case NEUTRAL_COLOR:
> +               inst->neutral_color = *((f0r_param_color_t *)param);
> +               compute_correction_matrix(inst);
> +               break;
> +
> +       case COLOR_TEMPERATURE:
> +               inst->color_temperature = *((double *)param);
> +               if (inst->color_temperature < 1000.0 || inst->color_temperature > 15000.0) {
> +                       inst->color_temperature = 6500.0;
> +               }
> +               compute_correction_matrix(inst);
> +               break;
> +       }
> +}
> +
> +void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_index)
> +{
> +       assert(instance);
> +       colgate_instance_t *inst = (colgate_instance_t*)instance;
> +
> +       switch (param_index) {
> +       case NEUTRAL_COLOR:
> +               *((f0r_param_color_t *)param) = inst->neutral_color;
> +               break;
> +
> +       case COLOR_TEMPERATURE:
> +               *((double *)param) = inst->color_temperature;
> +               break;
> +       }
> +}
> +
> +void f0r_update(f0r_instance_t instance, double time, const uint32_t *inframe, uint32_t *outframe)
> +{
> +       assert(instance);
> +       colgate_instance_t *inst = (colgate_instance_t *)instance;
> +       unsigned len = inst->width * inst->height;
> +       unsigned char *dst = (unsigned char *)outframe;
> +       const unsigned char *src = (unsigned char *)inframe;
> +       unsigned i;
> +
> +       for (i = 0; i < len; ++i) {
> +               int r = srgb_to_linear_rgb_lut[*src++];
> +               int g = srgb_to_linear_rgb_lut[*src++];
> +               int b = srgb_to_linear_rgb_lut[*src++];
> +
> +               int new_r, new_g, new_b;
> +               multiply_3x3_matrix_fp3(inst->corr_matrix, r, g, b, &new_r, &new_g, &new_b);
> +
> +               *dst++ = convert_linear_rgb_to_srgb_fp(new_r);
> +               *dst++ = convert_linear_rgb_to_srgb_fp(new_g);
> +               *dst++ = convert_linear_rgb_to_srgb_fp(new_b);
> +               *dst++ = *src++;  // Copy alpha.
> +       }
> +}
> diff --git a/src/filter/coloradj/readme b/src/filter/coloradj/readme
> index 76f80f5..d60dd80 100755
> --- a/src/filter/coloradj/readme
> +++ b/src/filter/coloradj/readme
> @@ -1,7 +1,7 @@
>  coloradj*

>
> These plugins are for manual color adjustment.
> -For (semi)automatic color correction, use "balanc0r" and/or
> +For (semi)automatic color correction, use "balanc0r", "colgate" and/or
> "three_point_balance" plugins.
>
> --
> Homepage: http://www.sesse.net/
> _______________________________________________
> Frei0r mailing list - http://frei0r.dyne.org
> Free video plugins, minimal and cross-platform.
> http://mailinglists.dyne.org/cgi-bin/mailman/listinfo/frei0r




--
+-DRD-+