You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

203 lines
6.2KB

  1. /*
  2. * Copyright (c) 2003 Michael Niedermayer
  3. * Copyright (c) 2012 Jeremy Tran
  4. *
  5. * This file is part of FFmpeg.
  6. *
  7. * FFmpeg is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * FFmpeg is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. */
  21. /**
  22. * @file
  23. * Apply a hue/saturation filter to the input video
  24. * Ported from MPlayer libmpcodecs/vf_hue.c.
  25. */
  26. #include "libavutil/imgutils.h"
  27. #include "libavutil/pixdesc.h"
  28. #include "avfilter.h"
  29. #include "formats.h"
  30. #include "internal.h"
  31. #include "video.h"
  32. typedef struct {
  33. float hue;
  34. float saturation;
  35. int hsub;
  36. int vsub;
  37. int32_t hue_sin;
  38. int32_t hue_cos;
  39. } HueContext;
  40. static av_cold int init(AVFilterContext *ctx, const char *args)
  41. {
  42. HueContext *hue = ctx->priv;
  43. float h = 0, s = 1;
  44. int n;
  45. char c1 = 0, c2 = 0;
  46. if (args) {
  47. n = sscanf(args, "%f%c%f%c", &h, &c1, &s, &c2);
  48. if (n != 0 && n != 1 && (n != 3 || c1 != ':')) {
  49. av_log(ctx, AV_LOG_ERROR,
  50. "Invalid syntax for argument '%s': "
  51. "must be in the form 'hue[:saturation]'\n", args);
  52. return AVERROR(EINVAL);
  53. }
  54. }
  55. if (s < -10 || s > 10) {
  56. av_log(ctx, AV_LOG_ERROR,
  57. "Invalid value for saturation %0.1f: "
  58. "must be included between range -10 and +10\n", s);
  59. return AVERROR(EINVAL);
  60. }
  61. /* Convert angle from degree to radian */
  62. hue->hue = h * M_PI / 180;
  63. hue->saturation = s;
  64. return 0;
  65. }
  66. static int query_formats(AVFilterContext *ctx)
  67. {
  68. static const enum PixelFormat pix_fmts[] = {
  69. PIX_FMT_YUV444P, PIX_FMT_YUV422P,
  70. PIX_FMT_YUV420P, PIX_FMT_YUV411P,
  71. PIX_FMT_YUV410P, PIX_FMT_YUV440P,
  72. PIX_FMT_YUVA420P,
  73. PIX_FMT_NONE
  74. };
  75. ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
  76. return 0;
  77. }
  78. static int config_props(AVFilterLink *inlink)
  79. {
  80. HueContext *hue = inlink->dst->priv;
  81. const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[inlink->format];
  82. hue->hsub = desc->log2_chroma_w;
  83. hue->vsub = desc->log2_chroma_h;
  84. /*
  85. * Scale the value to the norm of the resulting (U,V) vector, that is
  86. * the saturation.
  87. * This will be useful in the process_chrominance function.
  88. */
  89. hue->hue_sin = rint(sin(hue->hue) * (1 << 16) * hue->saturation);
  90. hue->hue_cos = rint(cos(hue->hue) * (1 << 16) * hue->saturation);
  91. return 0;
  92. }
  93. static void process_chrominance(uint8_t *udst, uint8_t *vdst, const int dst_linesize,
  94. uint8_t *usrc, uint8_t *vsrc, const int src_linesize,
  95. int w, int h,
  96. const int32_t c, const int32_t s)
  97. {
  98. int32_t u, v, new_u, new_v;
  99. int i;
  100. /*
  101. * If we consider U and V as the components of a 2D vector then its angle
  102. * is the hue and the norm is the saturation
  103. */
  104. while (h--) {
  105. for (i = 0; i < w; i++) {
  106. /* Normalize the components from range [16;140] to [-112;112] */
  107. u = usrc[i] - 128;
  108. v = vsrc[i] - 128;
  109. /*
  110. * Apply the rotation of the vector : (c * u) - (s * v)
  111. * (s * u) + (c * v)
  112. * De-normalize the components (without forgetting to scale 128
  113. * by << 16)
  114. * Finally scale back the result by >> 16
  115. */
  116. new_u = ((c * u) - (s * v) + (1 << 15) + (128 << 16)) >> 16;
  117. new_v = ((s * u) + (c * v) + (1 << 15) + (128 << 16)) >> 16;
  118. /* Prevent a potential overflow */
  119. udst[i] = av_clip_uint8_c(new_u);
  120. vdst[i] = av_clip_uint8_c(new_v);
  121. }
  122. usrc += src_linesize;
  123. vsrc += src_linesize;
  124. udst += dst_linesize;
  125. vdst += dst_linesize;
  126. }
  127. }
  128. static int draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
  129. {
  130. HueContext *hue = inlink->dst->priv;
  131. AVFilterBufferRef *inpic = inlink->cur_buf;
  132. AVFilterBufferRef *outpic = inlink->dst->outputs[0]->out_buf;
  133. uint8_t *inrow[3], *outrow[3]; // 0 : Y, 1 : U, 2 : V
  134. int plane;
  135. inrow[0] = inpic->data[0] + y * inpic->linesize[0];
  136. outrow[0] = outpic->data[0] + y * outpic->linesize[0];
  137. for (plane = 1; plane < 3; plane++) {
  138. inrow[plane] = inpic->data[plane] + (y >> hue->vsub) * inpic->linesize[plane];
  139. outrow[plane] = outpic->data[plane] + (y >> hue->vsub) * outpic->linesize[plane];
  140. }
  141. av_image_copy_plane(outrow[0], outpic->linesize[0],
  142. inrow[0], inpic->linesize[0],
  143. inlink->w, inlink->h);
  144. process_chrominance(outrow[1], outrow[2], outpic->linesize[1],
  145. inrow[1], inrow[2], inpic->linesize[1],
  146. inlink->w >> hue->hsub, inlink->h >> hue->vsub,
  147. hue->hue_cos, hue->hue_sin);
  148. return ff_draw_slice(inlink->dst->outputs[0], y, h, slice_dir);
  149. }
  150. AVFilter avfilter_vf_hue = {
  151. .name = "hue",
  152. .description = NULL_IF_CONFIG_SMALL("Adjust the hue and saturation of the input video."),
  153. .priv_size = sizeof(HueContext),
  154. .init = init,
  155. .query_formats = query_formats,
  156. .inputs = (const AVFilterPad[]) {
  157. {
  158. .name = "default",
  159. .type = AVMEDIA_TYPE_VIDEO,
  160. .draw_slice = draw_slice,
  161. .config_props = config_props,
  162. .min_perms = AV_PERM_READ,
  163. },
  164. { .name = NULL }
  165. },
  166. .outputs = (const AVFilterPad[]) {
  167. {
  168. .name = "default",
  169. .type = AVMEDIA_TYPE_VIDEO,
  170. },
  171. { .name = NULL }
  172. }
  173. };