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.

392 lines
15KB

  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 "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. typedef struct VibranceContext {
  31. const AVClass *class;
  32. float intensity;
  33. float balance[3];
  34. float lcoeffs[3];
  35. int alternate;
  36. int step;
  37. int depth;
  38. uint8_t rgba_map[4];
  39. int (*do_slice)(AVFilterContext *s, void *arg,
  40. int jobnr, int nb_jobs);
  41. } VibranceContext;
  42. static inline float lerpf(float v0, float v1, float f)
  43. {
  44. return v0 + (v1 - v0) * f;
  45. }
  46. static int vibrance_slice8(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
  47. {
  48. VibranceContext *s = avctx->priv;
  49. AVFrame *frame = arg;
  50. const int width = frame->width;
  51. const int height = frame->height;
  52. const float scale = 1.f / 255.f;
  53. const float gc = s->lcoeffs[0];
  54. const float bc = s->lcoeffs[1];
  55. const float rc = s->lcoeffs[2];
  56. const float intensity = s->intensity;
  57. const float alternate = s->alternate ? 1.f : -1.f;
  58. const float gintensity = intensity * s->balance[0];
  59. const float bintensity = intensity * s->balance[1];
  60. const float rintensity = intensity * s->balance[2];
  61. const float sgintensity = alternate * FFSIGN(gintensity);
  62. const float sbintensity = alternate * FFSIGN(bintensity);
  63. const float srintensity = alternate * FFSIGN(rintensity);
  64. const int slice_start = (height * jobnr) / nb_jobs;
  65. const int slice_end = (height * (jobnr + 1)) / nb_jobs;
  66. const int glinesize = frame->linesize[0];
  67. const int blinesize = frame->linesize[1];
  68. const int rlinesize = frame->linesize[2];
  69. uint8_t *gptr = frame->data[0] + slice_start * glinesize;
  70. uint8_t *bptr = frame->data[1] + slice_start * blinesize;
  71. uint8_t *rptr = frame->data[2] + slice_start * rlinesize;
  72. for (int y = slice_start; y < slice_end; y++) {
  73. for (int x = 0; x < width; x++) {
  74. float g = gptr[x] * scale;
  75. float b = bptr[x] * scale;
  76. float r = rptr[x] * scale;
  77. float max_color = FFMAX3(r, g, b);
  78. float min_color = FFMIN3(r, g, b);
  79. float color_saturation = max_color - min_color;
  80. float luma = g * gc + r * rc + b * bc;
  81. const float cg = 1.f + gintensity * (1.f - sgintensity * color_saturation);
  82. const float cb = 1.f + bintensity * (1.f - sbintensity * color_saturation);
  83. const float cr = 1.f + rintensity * (1.f - srintensity * color_saturation);
  84. g = lerpf(luma, g, cg);
  85. b = lerpf(luma, b, cb);
  86. r = lerpf(luma, r, cr);
  87. gptr[x] = av_clip_uint8(g * 255.f);
  88. bptr[x] = av_clip_uint8(b * 255.f);
  89. rptr[x] = av_clip_uint8(r * 255.f);
  90. }
  91. gptr += glinesize;
  92. bptr += blinesize;
  93. rptr += rlinesize;
  94. }
  95. return 0;
  96. }
  97. static int vibrance_slice16(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
  98. {
  99. VibranceContext *s = avctx->priv;
  100. AVFrame *frame = arg;
  101. const int depth = s->depth;
  102. const float max = (1 << depth) - 1;
  103. const float scale = 1.f / max;
  104. const float gc = s->lcoeffs[0];
  105. const float bc = s->lcoeffs[1];
  106. const float rc = s->lcoeffs[2];
  107. const int width = frame->width;
  108. const int height = frame->height;
  109. const float intensity = s->intensity;
  110. const float alternate = s->alternate ? 1.f : -1.f;
  111. const float gintensity = intensity * s->balance[0];
  112. const float bintensity = intensity * s->balance[1];
  113. const float rintensity = intensity * s->balance[2];
  114. const float sgintensity = alternate * FFSIGN(gintensity);
  115. const float sbintensity = alternate * FFSIGN(bintensity);
  116. const float srintensity = alternate * FFSIGN(rintensity);
  117. const int slice_start = (height * jobnr) / nb_jobs;
  118. const int slice_end = (height * (jobnr + 1)) / nb_jobs;
  119. const int glinesize = frame->linesize[0] / 2;
  120. const int blinesize = frame->linesize[1] / 2;
  121. const int rlinesize = frame->linesize[2] / 2;
  122. uint16_t *gptr = (uint16_t *)frame->data[0] + slice_start * glinesize;
  123. uint16_t *bptr = (uint16_t *)frame->data[1] + slice_start * blinesize;
  124. uint16_t *rptr = (uint16_t *)frame->data[2] + slice_start * rlinesize;
  125. for (int y = slice_start; y < slice_end; y++) {
  126. for (int x = 0; x < width; x++) {
  127. float g = gptr[x] * scale;
  128. float b = bptr[x] * scale;
  129. float r = rptr[x] * scale;
  130. float max_color = FFMAX3(r, g, b);
  131. float min_color = FFMIN3(r, g, b);
  132. float color_saturation = max_color - min_color;
  133. float luma = g * gc + r * rc + b * bc;
  134. const float cg = 1.f + gintensity * (1.f - sgintensity * color_saturation);
  135. const float cb = 1.f + bintensity * (1.f - sbintensity * color_saturation);
  136. const float cr = 1.f + rintensity * (1.f - srintensity * color_saturation);
  137. g = lerpf(luma, g, cg);
  138. b = lerpf(luma, b, cb);
  139. r = lerpf(luma, r, cr);
  140. gptr[x] = av_clip_uintp2_c(g * max, depth);
  141. bptr[x] = av_clip_uintp2_c(b * max, depth);
  142. rptr[x] = av_clip_uintp2_c(r * max, depth);
  143. }
  144. gptr += glinesize;
  145. bptr += blinesize;
  146. rptr += rlinesize;
  147. }
  148. return 0;
  149. }
  150. static int vibrance_slice8p(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
  151. {
  152. VibranceContext *s = avctx->priv;
  153. AVFrame *frame = arg;
  154. const int step = s->step;
  155. const int width = frame->width;
  156. const int height = frame->height;
  157. const float scale = 1.f / 255.f;
  158. const float gc = s->lcoeffs[0];
  159. const float bc = s->lcoeffs[1];
  160. const float rc = s->lcoeffs[2];
  161. const uint8_t roffset = s->rgba_map[R];
  162. const uint8_t goffset = s->rgba_map[G];
  163. const uint8_t boffset = s->rgba_map[B];
  164. const float intensity = s->intensity;
  165. const float alternate = s->alternate ? 1.f : -1.f;
  166. const float gintensity = intensity * s->balance[0];
  167. const float bintensity = intensity * s->balance[1];
  168. const float rintensity = intensity * s->balance[2];
  169. const float sgintensity = alternate * FFSIGN(gintensity);
  170. const float sbintensity = alternate * FFSIGN(bintensity);
  171. const float srintensity = alternate * FFSIGN(rintensity);
  172. const int slice_start = (height * jobnr) / nb_jobs;
  173. const int slice_end = (height * (jobnr + 1)) / nb_jobs;
  174. const int linesize = frame->linesize[0];
  175. uint8_t *ptr = frame->data[0] + slice_start * linesize;
  176. for (int y = slice_start; y < slice_end; y++) {
  177. for (int x = 0; x < width; x++) {
  178. float g = ptr[x * step + goffset] * scale;
  179. float b = ptr[x * step + boffset] * scale;
  180. float r = ptr[x * step + roffset] * scale;
  181. float max_color = FFMAX3(r, g, b);
  182. float min_color = FFMIN3(r, g, b);
  183. float color_saturation = max_color - min_color;
  184. float luma = g * gc + r * rc + b * bc;
  185. const float cg = 1.f + gintensity * (1.f - sgintensity * color_saturation);
  186. const float cb = 1.f + bintensity * (1.f - sbintensity * color_saturation);
  187. const float cr = 1.f + rintensity * (1.f - srintensity * color_saturation);
  188. g = lerpf(luma, g, cg);
  189. b = lerpf(luma, b, cb);
  190. r = lerpf(luma, r, cr);
  191. ptr[x * step + goffset] = av_clip_uint8(g * 255.f);
  192. ptr[x * step + boffset] = av_clip_uint8(b * 255.f);
  193. ptr[x * step + roffset] = av_clip_uint8(r * 255.f);
  194. }
  195. ptr += linesize;
  196. }
  197. return 0;
  198. }
  199. static int vibrance_slice16p(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
  200. {
  201. VibranceContext *s = avctx->priv;
  202. AVFrame *frame = arg;
  203. const int step = s->step;
  204. const int depth = s->depth;
  205. const float max = (1 << depth) - 1;
  206. const float scale = 1.f / max;
  207. const float gc = s->lcoeffs[0];
  208. const float bc = s->lcoeffs[1];
  209. const float rc = s->lcoeffs[2];
  210. const uint8_t roffset = s->rgba_map[R];
  211. const uint8_t goffset = s->rgba_map[G];
  212. const uint8_t boffset = s->rgba_map[B];
  213. const int width = frame->width;
  214. const int height = frame->height;
  215. const float intensity = s->intensity;
  216. const float alternate = s->alternate ? 1.f : -1.f;
  217. const float gintensity = intensity * s->balance[0];
  218. const float bintensity = intensity * s->balance[1];
  219. const float rintensity = intensity * s->balance[2];
  220. const float sgintensity = alternate * FFSIGN(gintensity);
  221. const float sbintensity = alternate * FFSIGN(bintensity);
  222. const float srintensity = alternate * FFSIGN(rintensity);
  223. const int slice_start = (height * jobnr) / nb_jobs;
  224. const int slice_end = (height * (jobnr + 1)) / nb_jobs;
  225. const int linesize = frame->linesize[0] / 2;
  226. uint16_t *ptr = (uint16_t *)frame->data[0] + slice_start * linesize;
  227. for (int y = slice_start; y < slice_end; y++) {
  228. for (int x = 0; x < width; x++) {
  229. float g = ptr[x * step + goffset] * scale;
  230. float b = ptr[x * step + boffset] * scale;
  231. float r = ptr[x * step + roffset] * scale;
  232. float max_color = FFMAX3(r, g, b);
  233. float min_color = FFMIN3(r, g, b);
  234. float color_saturation = max_color - min_color;
  235. float luma = g * gc + r * rc + b * bc;
  236. const float cg = 1.f + gintensity * (1.f - sgintensity * color_saturation);
  237. const float cb = 1.f + bintensity * (1.f - sbintensity * color_saturation);
  238. const float cr = 1.f + rintensity * (1.f - srintensity * color_saturation);
  239. g = lerpf(luma, g, cg);
  240. b = lerpf(luma, b, cb);
  241. r = lerpf(luma, r, cr);
  242. ptr[x * step + goffset] = av_clip_uintp2_c(g * max, depth);
  243. ptr[x * step + boffset] = av_clip_uintp2_c(b * max, depth);
  244. ptr[x * step + roffset] = av_clip_uintp2_c(r * max, depth);
  245. }
  246. ptr += linesize;
  247. }
  248. return 0;
  249. }
  250. static int filter_frame(AVFilterLink *link, AVFrame *frame)
  251. {
  252. AVFilterContext *avctx = link->dst;
  253. VibranceContext *s = avctx->priv;
  254. int res;
  255. if (res = avctx->internal->execute(avctx, s->do_slice, frame, NULL,
  256. FFMIN(frame->height, ff_filter_get_nb_threads(avctx))))
  257. return res;
  258. return ff_filter_frame(avctx->outputs[0], frame);
  259. }
  260. static av_cold int query_formats(AVFilterContext *avctx)
  261. {
  262. static const enum AVPixelFormat pixel_fmts[] = {
  263. AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
  264. AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA,
  265. AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR,
  266. AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR,
  267. AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0,
  268. AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
  269. AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12,
  270. AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
  271. AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
  272. AV_PIX_FMT_RGB48, AV_PIX_FMT_BGR48,
  273. AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64,
  274. AV_PIX_FMT_NONE
  275. };
  276. AVFilterFormats *formats = NULL;
  277. formats = ff_make_format_list(pixel_fmts);
  278. if (!formats)
  279. return AVERROR(ENOMEM);
  280. return ff_set_common_formats(avctx, formats);
  281. }
  282. static av_cold int config_input(AVFilterLink *inlink)
  283. {
  284. AVFilterContext *avctx = inlink->dst;
  285. VibranceContext *s = avctx->priv;
  286. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
  287. int planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR;
  288. s->step = desc->nb_components;
  289. if (inlink->format == AV_PIX_FMT_RGB0 ||
  290. inlink->format == AV_PIX_FMT_0RGB ||
  291. inlink->format == AV_PIX_FMT_BGR0 ||
  292. inlink->format == AV_PIX_FMT_0BGR)
  293. s->step = 4;
  294. s->depth = desc->comp[0].depth;
  295. s->do_slice = s->depth <= 8 ? vibrance_slice8 : vibrance_slice16;
  296. if (!planar)
  297. s->do_slice = s->depth <= 8 ? vibrance_slice8p : vibrance_slice16p;
  298. ff_fill_rgba_map(s->rgba_map, inlink->format);
  299. return 0;
  300. }
  301. static const AVFilterPad vibrance_inputs[] = {
  302. {
  303. .name = "default",
  304. .type = AVMEDIA_TYPE_VIDEO,
  305. .needs_writable = 1,
  306. .filter_frame = filter_frame,
  307. .config_props = config_input,
  308. },
  309. { NULL }
  310. };
  311. static const AVFilterPad vibrance_outputs[] = {
  312. {
  313. .name = "default",
  314. .type = AVMEDIA_TYPE_VIDEO,
  315. },
  316. { NULL }
  317. };
  318. #define OFFSET(x) offsetof(VibranceContext, x)
  319. #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
  320. static const AVOption vibrance_options[] = {
  321. { "intensity", "set the intensity value", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0}, -2, 2, VF },
  322. { "rbal", "set the red balance value", OFFSET(balance[2]), AV_OPT_TYPE_FLOAT, {.dbl=1}, -10, 10, VF },
  323. { "gbal", "set the green balance value", OFFSET(balance[0]), AV_OPT_TYPE_FLOAT, {.dbl=1}, -10, 10, VF },
  324. { "bbal", "set the blue balance value", OFFSET(balance[1]), AV_OPT_TYPE_FLOAT, {.dbl=1}, -10, 10, VF },
  325. { "rlum", "set the red luma coefficient", OFFSET(lcoeffs[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.072186}, 0, 1, VF },
  326. { "glum", "set the green luma coefficient", OFFSET(lcoeffs[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.715158}, 0, 1, VF },
  327. { "blum", "set the blue luma coefficient", OFFSET(lcoeffs[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.212656}, 0, 1, VF },
  328. { "alternate", "use alternate colors", OFFSET(alternate), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, VF },
  329. { NULL }
  330. };
  331. AVFILTER_DEFINE_CLASS(vibrance);
  332. AVFilter ff_vf_vibrance = {
  333. .name = "vibrance",
  334. .description = NULL_IF_CONFIG_SMALL("Boost or alter saturation."),
  335. .priv_size = sizeof(VibranceContext),
  336. .priv_class = &vibrance_class,
  337. .query_formats = query_formats,
  338. .inputs = vibrance_inputs,
  339. .outputs = vibrance_outputs,
  340. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
  341. .process_command = ff_filter_process_command,
  342. };