[FFmpeg-devel] [PATCH] colorbalance filter
Paul B Mahol
onemda at gmail.com
Mon Apr 15 16:35:43 CEST 2013
On 4/15/13, Clement Boesch <ubitux at gmail.com> wrote:
> On Mon, Apr 15, 2013 at 11:02:34AM +0000, Paul B Mahol wrote:
>> Signed-off-by: Paul B Mahol <onemda at gmail.com>
>> ---
>> doc/filters.texi | 25 ++++
>> libavfilter/Makefile | 1 +
>> libavfilter/allfilters.c | 1 +
>> libavfilter/vf_colorbalance.c | 282
>> ++++++++++++++++++++++++++++++++++++++++++
>> 4 files changed, 309 insertions(+)
>> create mode 100644 libavfilter/vf_colorbalance.c
>>
> [...]
>> +static int config_output(AVFilterLink *outlink)
>> +{
>> + AVFilterContext *ctx = outlink->src;
>> + ColorBalanceContext *cb = ctx->priv;
>> + AVFilterLink *inlink = ctx->inputs[0];
>> + double *add[3], *sub[3];
>> + double *coeff[3][3];
>> + double *buffer;
>> + int i, r, g, b;
>> +
>> + switch (inlink->format) {
>> + case AV_PIX_FMT_RGB24:
>> + buffer = av_malloc(256 * 3 * 2 * sizeof(double));
>> + if (!buffer)
>> + return AVERROR(ENOMEM);
>> +
>> + for (i = 0; i < 3; i++) {
>> + add[i] = buffer + 256 * i;
>> + sub[i] = buffer + 256 * (3 + i);
>> + }
>> +
>> + for (i = 0; i < 256; i++) {
>> + double low = (1.075 - 1 / (i / 16.0 + 1));
>> + double mid = 2.0 / 3.0 * (1 - ((i - 127.0) / 127.0) * ((i -
>> 127.0) / 127.0));
>> +
>> + add[0][i] = low;
>> + add[1][i] = mid;
>> + add[2][255 - i] = low;
>> + sub[0][255 - i] = low;
>> + sub[1][i] = mid;
>> + sub[2][i] = low;
>> + }
>> +
>> + coeff[0][0] = cb->cyan_red.shadows > 0 ? add[0] : sub[0];
>> + coeff[0][1] = cb->cyan_red.midtones > 0 ? add[1] : sub[1];
>> + coeff[0][2] = cb->cyan_red.highlights > 0 ? add[2] : sub[2];
>> + coeff[1][0] = cb->magenta_green.shadows > 0 ? add[0] : sub[0];
>> + coeff[1][1] = cb->magenta_green.midtones > 0 ? add[1] : sub[1];
>> + coeff[1][2] = cb->magenta_green.highlights > 0 ? add[2] :
>> sub[2];
>> + coeff[2][0] = cb->yellow_blue.shadows > 0 ? add[0] : sub[0];
>> + coeff[2][1] = cb->yellow_blue.midtones > 0 ? add[1] : sub[1];
>> + coeff[2][2] = cb->yellow_blue.highlights > 0 ? add[2] : sub[2];
>> +
>> + for (i = 0; i < 256; i++) {
>> + r = g = b = i;
>> +
>
>> + r += cb->cyan_red.shadows * coeff[0][0][r];
>> + r = av_clip_uint8(r);
>> + r += cb->cyan_red.midtones * coeff[0][1][r];
>> + r = av_clip_uint8(r);
>> + r += cb->cyan_red.highlights * coeff[0][2][r];
>> + r = av_clip_uint8(r);
>> +
>
> Possibly simpler:
>
> r = av_clip_uint8_t(r + cb->cyan_red.shadows * coeff[0][0][r]);
> r = av_clip_uint8_t(r + cb->cyan_red.midtones * coeff[0][1][r]);
> r = av_clip_uint8_t(r + cb->cyan_red.highlights * coeff[0][2][r]);
>
> ?
>
>> + g += cb->magenta_green.shadows * coeff[1][0][g];
>> + g = av_clip_uint8(g);
>> + g += cb->magenta_green.midtones * coeff[1][1][g];
>> + g = av_clip_uint8(g);
>> + g += cb->magenta_green.highlights * coeff[1][2][g];
>> + g = av_clip_uint8(g);
>> +
>> + b += cb->yellow_blue.shadows * coeff[2][0][b];
>> + b = av_clip_uint8(b);
>> + b += cb->yellow_blue.midtones * coeff[2][1][b];
>> + b = av_clip_uint8(b);
>> + b += cb->yellow_blue.highlights * coeff[2][2][b];
>> + b = av_clip_uint8(b);
>> +
>> + cb->lookup8[0][i] = r;
>> + cb->lookup8[1][i] = g;
>> + cb->lookup8[2][i] = b;
>> + }
>> + break;
>> + case AV_PIX_FMT_RGB48:
>> + buffer = av_malloc(256 * 256 * 3 * 2 * sizeof(double));
>> + if (!buffer)
>> + return AVERROR(ENOMEM);
>> +
>> + for (i = 0; i < 3; i++) {
>> + add[i] = buffer + 256 * 256 * i;
>> + sub[i] = buffer + 256 * 256 * (3 + i);
>> + }
>> +
>> + for (i = 0; i < 256 * 256; i++) {
>
>> + double low = (100.075 - 100 / (i / 256.0 + 100));
>
> 255.0?
>
>> + double mid = 2.0 / 3.0 * (100 - ((i - 32767.0) / 32767.0) *
>> ((i - 32767.0) / 32767.0));
>> +
>> + add[0][i] = low;
>> + add[1][i] = mid;
>> + add[2][65535 - i] = low;
>> + sub[0][65535 - i] = low;
>> + sub[1][i] = mid;
>> + sub[2][i] = low;
>> + }
>> +
>> + coeff[0][0] = cb->cyan_red.shadows > 0 ? add[0] : sub[0];
>> + coeff[0][1] = cb->cyan_red.midtones > 0 ? add[1] : sub[1];
>> + coeff[0][2] = cb->cyan_red.highlights > 0 ? add[2] : sub[2];
>> + coeff[1][0] = cb->magenta_green.shadows > 0 ? add[0] : sub[0];
>> + coeff[1][1] = cb->magenta_green.midtones > 0 ? add[1] : sub[1];
>> + coeff[1][2] = cb->magenta_green.highlights > 0 ? add[2] :
>> sub[2];
>> + coeff[2][0] = cb->yellow_blue.shadows > 0 ? add[0] : sub[0];
>> + coeff[2][1] = cb->yellow_blue.midtones > 0 ? add[1] : sub[1];
>> + coeff[2][2] = cb->yellow_blue.highlights > 0 ? add[2] : sub[2];
>> +
>> + for (i = 0; i < 256 * 256; i++) {
>> + r = g = b = i;
>> +
>> + r += cb->cyan_red.shadows * coeff[0][0][r];
>> + r = av_clip_uint16(r);
>> + r += cb->cyan_red.midtones * coeff[0][1][r];
>> + r = av_clip_uint16(r);
>> + r += cb->cyan_red.highlights * coeff[0][2][r];
>> + r = av_clip_uint16(r);
>> +
>> + g += cb->magenta_green.shadows * coeff[1][0][g];
>> + g = av_clip_uint16(g);
>> + g += cb->magenta_green.midtones * coeff[1][1][g];
>> + g = av_clip_uint16(g);
>> + g += cb->magenta_green.highlights * coeff[1][2][g];
>> + g = av_clip_uint16(g);
>> +
>> + b += cb->yellow_blue.shadows * coeff[2][0][b];
>> + b = av_clip_uint16(b);
>> + b += cb->yellow_blue.midtones * coeff[2][1][b];
>> + b = av_clip_uint16(b);
>> + b += cb->yellow_blue.highlights * coeff[2][2][b];
>> + b = av_clip_uint16(b);
>> +
>> + cb->lookup16[0][i] = r;
>> + cb->lookup16[1][i] = g;
>> + cb->lookup16[2][i] = b;
>> + }
>> + break;
>
> Some parts look definitely factorizable between the two formats.
>
>> + default:
>> + av_assert0(0);
>> + }
>> +
>> + av_free(buffer);
>> +
>> + return 0;
>> +}
>> +
>> +static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
>> +{
>> + AVFilterContext *ctx = inlink->dst;
>> + ColorBalanceContext *cb = ctx->priv;
>> + AVFilterLink *outlink = ctx->outputs[0];
>> + uint8_t *row = buf->data[0];
>> + int i, j;
>> +
>> + switch (outlink->format) {
>> + case AV_PIX_FMT_RGB24:
>> + for (i = 0; i < outlink->h; i++) {
>> + uint8_t *ptr = row;
>> +
>> + for (j = 0; j < outlink->w * 3; j += 3) {
>> + ptr[j + 0] = cb->lookup8[0][ptr[j + 0]];
>> + ptr[j + 1] = cb->lookup8[1][ptr[j + 1]];
>> + ptr[j + 2] = cb->lookup8[2][ptr[j + 2]];
>> + }
>> +
>> + row += buf->linesize[0];
>> + }
>> + break;
>> + case AV_PIX_FMT_RGB48:
>> + for (i = 0; i < outlink->h; i++) {
>> + uint16_t *ptr = (uint16_t *)row;
>> +
>> + for (j = 0; j < outlink->w * 3; j += 3) {
>> + ptr[j + 0] = cb->lookup16[0][ptr[j + 0]];
>> + ptr[j + 1] = cb->lookup16[1][ptr[j + 1]];
>> + ptr[j + 2] = cb->lookup16[2][ptr[j + 2]];
>> + }
>> +
>> + row += buf->linesize[0];
>> + }
>> + break;
>> + default:
>> + av_assert0(0);
>> + }
>> +
>
> It's a simple LUT table, so you can add a direct path easily.
>
>> + return ff_filter_frame(ctx->outputs[0], buf);
>> +}
>> +
>> +static const AVFilterPad colorbalance_inputs[] = {
>> + {
>> + .name = "default",
>> + .type = AVMEDIA_TYPE_VIDEO,
>> + .filter_frame = filter_frame,
>
>> + .needs_writable = 1,
>
> ...and then avoid the need for this.
Isn't point of that to not write additional code with no any gain.
It should not be used only if not using direct patch may produce additional
speed gain (in other words - less to copy).
Otherwise its just pointless, because .needs_writable will just do
it for as.
>
>> + },
>> + { NULL }
>> +};
>> +
>> +static const AVFilterPad colorbalance_outputs[] = {
>> + {
>> + .name = "default",
>> + .type = AVMEDIA_TYPE_VIDEO,
>> + .config_props = config_output,
>> + },
>> + { NULL }
>> +};
>> +
>> +AVFilter avfilter_vf_colorbalance = {
>> + .name = "colorbalance",
>> + .description = NULL_IF_CONFIG_SMALL("Adjust the color balance."),
>> + .priv_size = sizeof(ColorBalanceContext),
>> + .query_formats = query_formats,
>> + .inputs = colorbalance_inputs,
>> + .outputs = colorbalance_outputs,
>> + .priv_class = &colorbalance_class,
>> +};
>
> A FATE test would be welcome too.
>
> --
> Clement B.
>
More information about the ffmpeg-devel
mailing list