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.

241 lines
8.5KB

  1. /*
  2. * Copyright (c) 2018 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/imgutils.h"
  22. #include "avfilter.h"
  23. #include "formats.h"
  24. #include "internal.h"
  25. #include "video.h"
  26. typedef struct VibranceContext {
  27. const AVClass *class;
  28. float intensity;
  29. float balance[3];
  30. float lcoeffs[3];
  31. int depth;
  32. int (*do_slice)(AVFilterContext *s, void *arg,
  33. int jobnr, int nb_jobs);
  34. } VibranceContext;
  35. static inline float lerpf(float v0, float v1, float f)
  36. {
  37. return v0 + (v1 - v0) * f;
  38. }
  39. static int vibrance_slice8(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
  40. {
  41. VibranceContext *s = avctx->priv;
  42. AVFrame *frame = arg;
  43. const int width = frame->width;
  44. const int height = frame->height;
  45. const float gc = s->lcoeffs[0];
  46. const float bc = s->lcoeffs[1];
  47. const float rc = s->lcoeffs[2];
  48. const float intensity = s->intensity;
  49. const float gintensity = intensity * s->balance[0];
  50. const float bintensity = intensity * s->balance[1];
  51. const float rintensity = intensity * s->balance[2];
  52. const int slice_start = (height * jobnr) / nb_jobs;
  53. const int slice_end = (height * (jobnr + 1)) / nb_jobs;
  54. const int glinesize = frame->linesize[0];
  55. const int blinesize = frame->linesize[1];
  56. const int rlinesize = frame->linesize[2];
  57. uint8_t *gptr = frame->data[0] + slice_start * glinesize;
  58. uint8_t *bptr = frame->data[1] + slice_start * blinesize;
  59. uint8_t *rptr = frame->data[2] + slice_start * rlinesize;
  60. for (int y = slice_start; y < slice_end; y++) {
  61. for (int x = 0; x < width; x++) {
  62. float g = gptr[x] / 255.f;
  63. float b = bptr[x] / 255.f;
  64. float r = rptr[x] / 255.f;
  65. float max_color = FFMAX3(r, g, b);
  66. float min_color = FFMIN3(r, g, b);
  67. float color_saturation = max_color - min_color;
  68. float luma = g * gc + r * rc + b * bc;
  69. const float cg = 1.f + gintensity * (1.f - FFSIGN(gintensity) * color_saturation);
  70. const float cb = 1.f + bintensity * (1.f - FFSIGN(bintensity) * color_saturation);
  71. const float cr = 1.f + rintensity * (1.f - FFSIGN(rintensity) * color_saturation);
  72. g = lerpf(luma, g, cg);
  73. b = lerpf(luma, b, cb);
  74. r = lerpf(luma, r, cr);
  75. gptr[x] = av_clip_uint8(g * 255.f);
  76. bptr[x] = av_clip_uint8(b * 255.f);
  77. rptr[x] = av_clip_uint8(r * 255.f);
  78. }
  79. gptr += glinesize;
  80. bptr += blinesize;
  81. rptr += rlinesize;
  82. }
  83. return 0;
  84. }
  85. static int vibrance_slice16(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
  86. {
  87. VibranceContext *s = avctx->priv;
  88. AVFrame *frame = arg;
  89. const int depth = s->depth;
  90. const float max = (1 << depth) - 1;
  91. const float gc = s->lcoeffs[0];
  92. const float bc = s->lcoeffs[1];
  93. const float rc = s->lcoeffs[2];
  94. const int width = frame->width;
  95. const int height = frame->height;
  96. const float intensity = s->intensity;
  97. const float gintensity = intensity * s->balance[0];
  98. const float bintensity = intensity * s->balance[1];
  99. const float rintensity = intensity * s->balance[2];
  100. const int slice_start = (height * jobnr) / nb_jobs;
  101. const int slice_end = (height * (jobnr + 1)) / nb_jobs;
  102. const int glinesize = frame->linesize[0] / 2;
  103. const int blinesize = frame->linesize[1] / 2;
  104. const int rlinesize = frame->linesize[2] / 2;
  105. uint16_t *gptr = (uint16_t *)frame->data[0] + slice_start * glinesize;
  106. uint16_t *bptr = (uint16_t *)frame->data[1] + slice_start * blinesize;
  107. uint16_t *rptr = (uint16_t *)frame->data[2] + slice_start * rlinesize;
  108. for (int y = slice_start; y < slice_end; y++) {
  109. for (int x = 0; x < width; x++) {
  110. float g = gptr[x] / max;
  111. float b = bptr[x] / max;
  112. float r = rptr[x] / max;
  113. float max_color = FFMAX3(r, g, b);
  114. float min_color = FFMIN3(r, g, b);
  115. float color_saturation = max_color - min_color;
  116. float luma = g * gc + r * rc + b * bc;
  117. const float cg = 1.f + gintensity * (1.f - FFSIGN(gintensity) * color_saturation);
  118. const float cb = 1.f + bintensity * (1.f - FFSIGN(bintensity) * color_saturation);
  119. const float cr = 1.f + rintensity * (1.f - FFSIGN(rintensity) * color_saturation);
  120. g = lerpf(luma, g, cg);
  121. b = lerpf(luma, b, cb);
  122. r = lerpf(luma, r, cr);
  123. gptr[x] = av_clip_uintp2_c(g * max, depth);
  124. bptr[x] = av_clip_uintp2_c(b * max, depth);
  125. rptr[x] = av_clip_uintp2_c(r * max, depth);
  126. }
  127. gptr += glinesize;
  128. bptr += blinesize;
  129. rptr += rlinesize;
  130. }
  131. return 0;
  132. }
  133. static int filter_frame(AVFilterLink *link, AVFrame *frame)
  134. {
  135. AVFilterContext *avctx = link->dst;
  136. VibranceContext *s = avctx->priv;
  137. int res;
  138. if (res = avctx->internal->execute(avctx, s->do_slice, frame, NULL,
  139. FFMIN(frame->height, ff_filter_get_nb_threads(avctx))))
  140. return res;
  141. return ff_filter_frame(avctx->outputs[0], frame);
  142. }
  143. static av_cold int query_formats(AVFilterContext *avctx)
  144. {
  145. static const enum AVPixelFormat pixel_fmts[] = {
  146. AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
  147. AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12,
  148. AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
  149. AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
  150. AV_PIX_FMT_NONE
  151. };
  152. AVFilterFormats *formats = NULL;
  153. formats = ff_make_format_list(pixel_fmts);
  154. if (!formats)
  155. return AVERROR(ENOMEM);
  156. return ff_set_common_formats(avctx, formats);
  157. }
  158. static av_cold int config_input(AVFilterLink *inlink)
  159. {
  160. AVFilterContext *avctx = inlink->dst;
  161. VibranceContext *s = avctx->priv;
  162. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
  163. s->depth = desc->comp[0].depth;
  164. s->do_slice = s->depth <= 8 ? vibrance_slice8 : vibrance_slice16;
  165. return 0;
  166. }
  167. static const AVFilterPad vibrance_inputs[] = {
  168. {
  169. .name = "default",
  170. .type = AVMEDIA_TYPE_VIDEO,
  171. .needs_writable = 1,
  172. .filter_frame = filter_frame,
  173. .config_props = config_input,
  174. },
  175. { NULL }
  176. };
  177. static const AVFilterPad vibrance_outputs[] = {
  178. {
  179. .name = "default",
  180. .type = AVMEDIA_TYPE_VIDEO,
  181. },
  182. { NULL }
  183. };
  184. #define OFFSET(x) offsetof(VibranceContext, x)
  185. #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  186. static const AVOption vibrance_options[] = {
  187. { "intensity", "set the intensity value", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0}, -2, 2, VF },
  188. { "rbal", "set the red balance value", OFFSET(balance[2]), AV_OPT_TYPE_FLOAT, {.dbl=1}, -10, 10, VF },
  189. { "gbal", "set the green balance value", OFFSET(balance[0]), AV_OPT_TYPE_FLOAT, {.dbl=1}, -10, 10, VF },
  190. { "bbal", "set the blue balance value", OFFSET(balance[1]), AV_OPT_TYPE_FLOAT, {.dbl=1}, -10, 10, VF },
  191. { "rlum", "set the red luma coefficient", OFFSET(lcoeffs[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.072186}, 0, 1, VF },
  192. { "glum", "set the green luma coefficient", OFFSET(lcoeffs[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.715158}, 0, 1, VF },
  193. { "blum", "set the blue luma coefficient", OFFSET(lcoeffs[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.212656}, 0, 1, VF },
  194. { NULL }
  195. };
  196. AVFILTER_DEFINE_CLASS(vibrance);
  197. AVFilter ff_vf_vibrance = {
  198. .name = "vibrance",
  199. .description = NULL_IF_CONFIG_SMALL("Boost or alter saturation."),
  200. .priv_size = sizeof(VibranceContext),
  201. .priv_class = &vibrance_class,
  202. .query_formats = query_formats,
  203. .inputs = vibrance_inputs,
  204. .outputs = vibrance_outputs,
  205. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
  206. };