vf_colorbalance.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /*
  2. * Copyright (c) 2013 Paul B Mahol
  3. *
  4. * This file is part of FFmpeg.
  5. *
  6. * FFmpeg is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * FFmpeg is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with FFmpeg; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19. */
  20. #include "libavutil/opt.h"
  21. #include "libavutil/pixdesc.h"
  22. #include "avfilter.h"
  23. #include "drawutils.h"
  24. #include "formats.h"
  25. #include "internal.h"
  26. #include "video.h"
  27. #define R 0
  28. #define G 1
  29. #define B 2
  30. #define A 3
  31. typedef struct {
  32. double shadows;
  33. double midtones;
  34. double highlights;
  35. } Range;
  36. typedef struct {
  37. const AVClass *class;
  38. Range cyan_red;
  39. Range magenta_green;
  40. Range yellow_blue;
  41. uint8_t lut[3][256];
  42. uint8_t rgba_map[4];
  43. int step;
  44. } ColorBalanceContext;
  45. #define OFFSET(x) offsetof(ColorBalanceContext, x)
  46. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  47. static const AVOption colorbalance_options[] = {
  48. { "rs", "set red shadows", OFFSET(cyan_red.shadows), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
  49. { "gs", "set green shadows", OFFSET(magenta_green.shadows), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
  50. { "bs", "set blue shadows", OFFSET(yellow_blue.shadows), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
  51. { "rm", "set red midtones", OFFSET(cyan_red.midtones), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
  52. { "gm", "set green midtones", OFFSET(magenta_green.midtones), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
  53. { "bm", "set blue midtones", OFFSET(yellow_blue.midtones), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
  54. { "rh", "set red highlights", OFFSET(cyan_red.highlights), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
  55. { "gh", "set green highlights", OFFSET(magenta_green.highlights), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
  56. { "bh", "set blue highlights", OFFSET(yellow_blue.highlights), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
  57. { NULL }
  58. };
  59. AVFILTER_DEFINE_CLASS(colorbalance);
  60. static int query_formats(AVFilterContext *ctx)
  61. {
  62. static const enum AVPixelFormat pix_fmts[] = {
  63. AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
  64. AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA,
  65. AV_PIX_FMT_ABGR, AV_PIX_FMT_ARGB,
  66. AV_PIX_FMT_0BGR, AV_PIX_FMT_0RGB,
  67. AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0,
  68. AV_PIX_FMT_NONE
  69. };
  70. AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
  71. if (!fmts_list)
  72. return AVERROR(ENOMEM);
  73. return ff_set_common_formats(ctx, fmts_list);
  74. }
  75. static int config_output(AVFilterLink *outlink)
  76. {
  77. AVFilterContext *ctx = outlink->src;
  78. ColorBalanceContext *s = ctx->priv;
  79. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
  80. double *shadows, *midtones, *highlights, *buffer;
  81. int i, r, g, b;
  82. buffer = av_malloc(256 * 3 * sizeof(*buffer));
  83. if (!buffer)
  84. return AVERROR(ENOMEM);
  85. shadows = buffer + 256 * 0;
  86. midtones = buffer + 256 * 1;
  87. highlights = buffer + 256 * 2;
  88. for (i = 0; i < 256; i++) {
  89. double low = av_clipd((i - 85.0) / -64.0 + 0.5, 0, 1) * 178.5;
  90. double mid = av_clipd((i - 85.0) / 64.0 + 0.5, 0, 1) *
  91. av_clipd((i + 85.0 - 255.0) / -64.0 + 0.5, 0, 1) * 178.5;
  92. shadows[i] = low;
  93. midtones[i] = mid;
  94. highlights[255 - i] = low;
  95. }
  96. for (i = 0; i < 256; i++) {
  97. r = g = b = i;
  98. r = av_clip_uint8(r + s->cyan_red.shadows * shadows[r]);
  99. r = av_clip_uint8(r + s->cyan_red.midtones * midtones[r]);
  100. r = av_clip_uint8(r + s->cyan_red.highlights * highlights[r]);
  101. g = av_clip_uint8(g + s->magenta_green.shadows * shadows[g]);
  102. g = av_clip_uint8(g + s->magenta_green.midtones * midtones[g]);
  103. g = av_clip_uint8(g + s->magenta_green.highlights * highlights[g]);
  104. b = av_clip_uint8(b + s->yellow_blue.shadows * shadows[b]);
  105. b = av_clip_uint8(b + s->yellow_blue.midtones * midtones[b]);
  106. b = av_clip_uint8(b + s->yellow_blue.highlights * highlights[b]);
  107. s->lut[R][i] = r;
  108. s->lut[G][i] = g;
  109. s->lut[B][i] = b;
  110. }
  111. av_free(buffer);
  112. ff_fill_rgba_map(s->rgba_map, outlink->format);
  113. s->step = av_get_padded_bits_per_pixel(desc) >> 3;
  114. return 0;
  115. }
  116. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  117. {
  118. AVFilterContext *ctx = inlink->dst;
  119. ColorBalanceContext *s = ctx->priv;
  120. AVFilterLink *outlink = ctx->outputs[0];
  121. const uint8_t roffset = s->rgba_map[R];
  122. const uint8_t goffset = s->rgba_map[G];
  123. const uint8_t boffset = s->rgba_map[B];
  124. const uint8_t aoffset = s->rgba_map[A];
  125. const int step = s->step;
  126. const uint8_t *srcrow = in->data[0];
  127. uint8_t *dstrow;
  128. AVFrame *out;
  129. int i, j;
  130. if (av_frame_is_writable(in)) {
  131. out = in;
  132. } else {
  133. out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  134. if (!out) {
  135. av_frame_free(&in);
  136. return AVERROR(ENOMEM);
  137. }
  138. av_frame_copy_props(out, in);
  139. }
  140. dstrow = out->data[0];
  141. for (i = 0; i < outlink->h; i++) {
  142. const uint8_t *src = srcrow;
  143. uint8_t *dst = dstrow;
  144. for (j = 0; j < outlink->w * step; j += step) {
  145. dst[j + roffset] = s->lut[R][src[j + roffset]];
  146. dst[j + goffset] = s->lut[G][src[j + goffset]];
  147. dst[j + boffset] = s->lut[B][src[j + boffset]];
  148. if (in != out && step == 4)
  149. dst[j + aoffset] = src[j + aoffset];
  150. }
  151. srcrow += in->linesize[0];
  152. dstrow += out->linesize[0];
  153. }
  154. if (in != out)
  155. av_frame_free(&in);
  156. return ff_filter_frame(ctx->outputs[0], out);
  157. }
  158. static const AVFilterPad colorbalance_inputs[] = {
  159. {
  160. .name = "default",
  161. .type = AVMEDIA_TYPE_VIDEO,
  162. .filter_frame = filter_frame,
  163. },
  164. { NULL }
  165. };
  166. static const AVFilterPad colorbalance_outputs[] = {
  167. {
  168. .name = "default",
  169. .type = AVMEDIA_TYPE_VIDEO,
  170. .config_props = config_output,
  171. },
  172. { NULL }
  173. };
  174. AVFilter ff_vf_colorbalance = {
  175. .name = "colorbalance",
  176. .description = NULL_IF_CONFIG_SMALL("Adjust the color balance."),
  177. .priv_size = sizeof(ColorBalanceContext),
  178. .priv_class = &colorbalance_class,
  179. .query_formats = query_formats,
  180. .inputs = colorbalance_inputs,
  181. .outputs = colorbalance_outputs,
  182. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
  183. };