vf_colorbalance.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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. ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
  71. return 0;
  72. }
  73. static int config_output(AVFilterLink *outlink)
  74. {
  75. AVFilterContext *ctx = outlink->src;
  76. ColorBalanceContext *cb = ctx->priv;
  77. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
  78. double *shadows, *midtones, *highlights, *buffer;
  79. int i, r, g, b;
  80. buffer = av_malloc(256 * 3 * sizeof(*buffer));
  81. if (!buffer)
  82. return AVERROR(ENOMEM);
  83. shadows = buffer + 256 * 0;
  84. midtones = buffer + 256 * 1;
  85. highlights = buffer + 256 * 2;
  86. for (i = 0; i < 256; i++) {
  87. double low = av_clipd((i - 85.0) / -64.0 + 0.5, 0, 1) * 178.5;
  88. double mid = av_clipd((i - 85.0) / 64.0 + 0.5, 0, 1) *
  89. av_clipd((i + 85.0 - 255.0) / -64.0 + 0.5, 0, 1) * 178.5;
  90. shadows[i] = low;
  91. midtones[i] = mid;
  92. highlights[255 - i] = low;
  93. }
  94. for (i = 0; i < 256; i++) {
  95. r = g = b = i;
  96. r = av_clip_uint8(r + cb->cyan_red.shadows * shadows[r]);
  97. r = av_clip_uint8(r + cb->cyan_red.midtones * midtones[r]);
  98. r = av_clip_uint8(r + cb->cyan_red.highlights * highlights[r]);
  99. g = av_clip_uint8(g + cb->magenta_green.shadows * shadows[g]);
  100. g = av_clip_uint8(g + cb->magenta_green.midtones * midtones[g]);
  101. g = av_clip_uint8(g + cb->magenta_green.highlights * highlights[g]);
  102. b = av_clip_uint8(b + cb->yellow_blue.shadows * shadows[b]);
  103. b = av_clip_uint8(b + cb->yellow_blue.midtones * midtones[b]);
  104. b = av_clip_uint8(b + cb->yellow_blue.highlights * highlights[b]);
  105. cb->lut[R][i] = r;
  106. cb->lut[G][i] = g;
  107. cb->lut[B][i] = b;
  108. }
  109. av_free(buffer);
  110. ff_fill_rgba_map(cb->rgba_map, outlink->format);
  111. cb->step = av_get_padded_bits_per_pixel(desc) >> 3;
  112. return 0;
  113. }
  114. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  115. {
  116. AVFilterContext *ctx = inlink->dst;
  117. ColorBalanceContext *cb = ctx->priv;
  118. AVFilterLink *outlink = ctx->outputs[0];
  119. const uint8_t roffset = cb->rgba_map[R];
  120. const uint8_t goffset = cb->rgba_map[G];
  121. const uint8_t boffset = cb->rgba_map[B];
  122. const uint8_t aoffset = cb->rgba_map[A];
  123. const int step = cb->step;
  124. const uint8_t *srcrow = in->data[0];
  125. uint8_t *dstrow;
  126. AVFrame *out;
  127. int i, j;
  128. if (av_frame_is_writable(in)) {
  129. out = in;
  130. } else {
  131. out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  132. if (!out) {
  133. av_frame_free(&in);
  134. return AVERROR(ENOMEM);
  135. }
  136. av_frame_copy_props(out, in);
  137. }
  138. dstrow = out->data[0];
  139. for (i = 0; i < outlink->h; i++) {
  140. const uint8_t *src = srcrow;
  141. uint8_t *dst = dstrow;
  142. for (j = 0; j < outlink->w * step; j += step) {
  143. dst[j + roffset] = cb->lut[R][src[j + roffset]];
  144. dst[j + goffset] = cb->lut[G][src[j + goffset]];
  145. dst[j + boffset] = cb->lut[B][src[j + boffset]];
  146. if (in != out && step == 4)
  147. dst[j + aoffset] = src[j + aoffset];
  148. }
  149. srcrow += in->linesize[0];
  150. dstrow += out->linesize[0];
  151. }
  152. if (in != out)
  153. av_frame_free(&in);
  154. return ff_filter_frame(ctx->outputs[0], out);
  155. }
  156. static const AVFilterPad colorbalance_inputs[] = {
  157. {
  158. .name = "default",
  159. .type = AVMEDIA_TYPE_VIDEO,
  160. .filter_frame = filter_frame,
  161. },
  162. { NULL }
  163. };
  164. static const AVFilterPad colorbalance_outputs[] = {
  165. {
  166. .name = "default",
  167. .type = AVMEDIA_TYPE_VIDEO,
  168. .config_props = config_output,
  169. },
  170. { NULL }
  171. };
  172. AVFilter ff_vf_colorbalance = {
  173. .name = "colorbalance",
  174. .description = NULL_IF_CONFIG_SMALL("Adjust the color balance."),
  175. .priv_size = sizeof(ColorBalanceContext),
  176. .priv_class = &colorbalance_class,
  177. .query_formats = query_formats,
  178. .inputs = colorbalance_inputs,
  179. .outputs = colorbalance_outputs,
  180. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
  181. };