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.

441 lines
14KB

  1. /*
  2. * Copyright (c) 2020 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/imgutils.h"
  21. #include "libavutil/pixdesc.h"
  22. #include "libavutil/opt.h"
  23. #include "avfilter.h"
  24. #include "formats.h"
  25. #include "internal.h"
  26. #include "video.h"
  27. typedef struct TMidEqualizerContext {
  28. const AVClass *class;
  29. int planes;
  30. int radius;
  31. float sigma;
  32. int plane_width[4], plane_height[4];
  33. int nb_frames;
  34. int depth;
  35. int f_frames;
  36. int l_frames;
  37. int del_frame;
  38. int cur_frame;
  39. int nb_planes;
  40. int histogram_size;
  41. float kernel[127];
  42. float *histogram[4][256];
  43. float *change[4];
  44. AVFrame **frames;
  45. void (*compute_histogram)(const uint8_t *ssrc, ptrdiff_t linesize,
  46. int w, int h, float *histogram, size_t hsize);
  47. void (*apply_contrast_change)(const uint8_t *src, ptrdiff_t src_linesize,
  48. uint8_t *dst, ptrdiff_t dst_linesize,
  49. int w, int h, float *change, float *orig);
  50. } TMidEqualizerContext;
  51. #define OFFSET(x) offsetof(TMidEqualizerContext, x)
  52. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  53. static const AVOption tmidequalizer_options[] = {
  54. { "radius", "set radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64=5}, 1, 127, FLAGS },
  55. { "sigma", "set sigma", OFFSET(sigma), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS },
  56. { "planes", "set planes", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=0xF}, 0, 0xF, FLAGS },
  57. { NULL }
  58. };
  59. AVFILTER_DEFINE_CLASS(tmidequalizer);
  60. static int query_formats(AVFilterContext *ctx)
  61. {
  62. static const enum AVPixelFormat pix_fmts[] = {
  63. AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
  64. AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
  65. AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
  66. AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
  67. AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
  68. AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
  69. AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14,
  70. AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
  71. AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
  72. AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12,
  73. AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
  74. AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14,
  75. AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
  76. AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
  77. AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12,
  78. AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12,
  79. AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
  80. AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
  81. AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16,
  82. AV_PIX_FMT_GRAY16,
  83. AV_PIX_FMT_NONE
  84. };
  85. return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
  86. }
  87. static void compute_contrast_function(const float *const histograms[256],
  88. const float *const kernel,
  89. int nb_frames, int radius, int hsize,
  90. float *f, int idx)
  91. {
  92. const float *const h1 = histograms[idx];
  93. int p2[256] = { 0 };
  94. for (int p1 = 0; p1 < hsize; p1++) {
  95. float weight = 1.f;
  96. float sum = p1 * weight;
  97. for (int j = 0; j < radius; j++) {
  98. const int nidx = ((idx - radius + j) % nb_frames);
  99. const float *const h2 = histograms[nidx < 0 ? nidx + nb_frames: nidx];
  100. int k = j;
  101. for (; p2[k] < hsize && h2[p2[k]] < h1[p1]; p2[k]++);
  102. if (p2[k] == hsize)
  103. p2[k]--;
  104. weight += kernel[j];
  105. sum += kernel[j] * p2[k];
  106. }
  107. for (int j = radius + 1; j < nb_frames; j++) {
  108. const int nidx = (idx - radius + j) % nb_frames;
  109. const float *const h2 = histograms[nidx < 0 ? nidx + nb_frames: nidx];
  110. int k = j;
  111. for (; p2[k] < hsize && h2[p2[k]] < h1[p1]; p2[k]++);
  112. if (p2[k] == hsize)
  113. p2[k]--;
  114. weight += kernel[j - radius - 1];
  115. sum += kernel[j - radius - 1] * p2[k];
  116. }
  117. f[p1] = sum / weight;
  118. }
  119. }
  120. static void apply_contrast_change8(const uint8_t *src, ptrdiff_t src_linesize,
  121. uint8_t *dst, ptrdiff_t dst_linesize,
  122. int w, int h, float *change, float *orig)
  123. {
  124. for (int y = 0; y < h; y++) {
  125. for (int x = 0; x < w; x++)
  126. dst[x] = lrintf(change[src[x]]);
  127. dst += dst_linesize;
  128. src += src_linesize;
  129. }
  130. }
  131. static void apply_contrast_change16(const uint8_t *ssrc, ptrdiff_t src_linesize,
  132. uint8_t *ddst, ptrdiff_t dst_linesize,
  133. int w, int h, float *change, float *orig)
  134. {
  135. const uint16_t *src = (const uint16_t *)ssrc;
  136. uint16_t *dst = (uint16_t *)ddst;
  137. for (int y = 0; y < h; y++) {
  138. for (int x = 0; x < w; x++)
  139. dst[x] = lrintf(change[src[x]]);
  140. dst += dst_linesize / 2;
  141. src += src_linesize / 2;
  142. }
  143. }
  144. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  145. {
  146. AVFilterContext *ctx = inlink->dst;
  147. TMidEqualizerContext *s = ctx->priv;
  148. AVFilterLink *outlink = ctx->outputs[0];
  149. AVFrame *out;
  150. int eof = 0;
  151. if (!in) {
  152. int idx = s->f_frames < s->nb_frames ? s->radius : s->del_frame ? s->del_frame - 1 : s->nb_frames - 1;
  153. if (s->f_frames < s->nb_frames) {
  154. s->l_frames = s->nb_frames - s->f_frames;
  155. } else {
  156. s->l_frames++;
  157. }
  158. in = av_frame_clone(s->frames[idx]);
  159. if (!in)
  160. return AVERROR(ENOMEM);
  161. eof = 1;
  162. }
  163. if (s->f_frames < s->nb_frames) {
  164. s->frames[s->f_frames] = in;
  165. for (int p = 0; p < s->nb_planes; p++) {
  166. s->compute_histogram(in->data[p], in->linesize[p],
  167. s->plane_width[p], s->plane_height[p],
  168. s->histogram[p][s->f_frames],
  169. s->histogram_size);
  170. }
  171. s->f_frames++;
  172. while (s->f_frames <= s->radius) {
  173. s->frames[s->f_frames] = av_frame_clone(in);
  174. if (!s->frames[s->f_frames])
  175. return AVERROR(ENOMEM);
  176. for (int p = 0; p < s->nb_planes; p++) {
  177. memcpy(s->histogram[p][s->f_frames],
  178. s->histogram[p][s->f_frames - 1],
  179. s->histogram_size * sizeof(float));
  180. }
  181. s->f_frames++;
  182. }
  183. if (!eof && s->f_frames < s->nb_frames) {
  184. return 0;
  185. } else {
  186. while (s->f_frames < s->nb_frames) {
  187. s->frames[s->f_frames] = av_frame_clone(in);
  188. if (!s->frames[s->f_frames])
  189. return AVERROR(ENOMEM);
  190. for (int p = 0; p < s->nb_planes; p++) {
  191. memcpy(s->histogram[p][s->f_frames],
  192. s->histogram[p][s->f_frames - 1],
  193. s->histogram_size * sizeof(float));
  194. }
  195. s->f_frames++;
  196. }
  197. }
  198. s->cur_frame = s->radius;
  199. s->del_frame = 0;
  200. } else {
  201. av_frame_free(&s->frames[s->del_frame]);
  202. s->frames[s->del_frame] = in;
  203. for (int p = 0; p < s->nb_planes; p++) {
  204. s->compute_histogram(in->data[p], in->linesize[p],
  205. s->plane_width[p], s->plane_height[p],
  206. s->histogram[p][s->del_frame],
  207. s->histogram_size);
  208. }
  209. s->del_frame++;
  210. if (s->del_frame >= s->nb_frames)
  211. s->del_frame = 0;
  212. }
  213. if (ctx->is_disabled) {
  214. const int idx = s->cur_frame;
  215. out = av_frame_clone(s->frames[idx]);
  216. if (!out)
  217. return AVERROR(ENOMEM);
  218. } else {
  219. const int idx = s->cur_frame;
  220. in = s->frames[idx];
  221. out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  222. if (!out)
  223. return AVERROR(ENOMEM);
  224. av_frame_copy_props(out, in);
  225. for (int p = 0; p < s->nb_planes; p++) {
  226. if (!((1 << p) & s->planes)) {
  227. av_image_copy_plane(out->data[p], out->linesize[p], in->data[p], in->linesize[p],
  228. s->plane_width[p] * (1 + (s->depth > 8)), s->plane_height[p]);
  229. continue;
  230. }
  231. compute_contrast_function((const float *const *)s->histogram[p], s->kernel,
  232. s->nb_frames, s->radius, s->histogram_size, s->change[p], idx);
  233. s->apply_contrast_change(in->data[p], in->linesize[p],
  234. out->data[p], out->linesize[p],
  235. s->plane_width[p], s->plane_height[p],
  236. s->change[p], s->histogram[p][idx]);
  237. }
  238. }
  239. s->cur_frame++;
  240. if (s->cur_frame >= s->nb_frames)
  241. s->cur_frame = 0;
  242. return ff_filter_frame(outlink, out);
  243. }
  244. static void compute_histogram8(const uint8_t *src, ptrdiff_t linesize,
  245. int w, int h, float *histogram, size_t hsize)
  246. {
  247. memset(histogram, 0, hsize * sizeof(*histogram));
  248. for (int y = 0; y < h; y++) {
  249. for (int x = 0; x < w; x++)
  250. histogram[src[x]] += 1;
  251. src += linesize;
  252. }
  253. for (int x = 0; x < hsize; x++)
  254. histogram[x] /= hsize;
  255. for (int x = 1; x < hsize; x++)
  256. histogram[x] += histogram[x-1];
  257. }
  258. static void compute_histogram16(const uint8_t *ssrc, ptrdiff_t linesize,
  259. int w, int h, float *histogram, size_t hsize)
  260. {
  261. const uint16_t *src = (const uint16_t *)ssrc;
  262. memset(histogram, 0, hsize * sizeof(*histogram));
  263. for (int y = 0; y < h; y++) {
  264. for (int x = 0; x < w; x++)
  265. histogram[src[x]] += 1;
  266. src += linesize / 2;
  267. }
  268. for (int x = 0; x < hsize; x++)
  269. histogram[x] /= hsize;
  270. for (int x = 1; x < hsize; x++)
  271. histogram[x] += histogram[x-1];
  272. }
  273. static int config_input(AVFilterLink *inlink)
  274. {
  275. AVFilterContext *ctx = inlink->dst;
  276. TMidEqualizerContext *s = ctx->priv;
  277. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
  278. float sigma = s->radius * s->sigma;
  279. int vsub, hsub;
  280. s->depth = desc->comp[0].depth;
  281. s->nb_frames = s->radius * 2 + 1;
  282. s->nb_planes = av_pix_fmt_count_planes(inlink->format);
  283. hsub = desc->log2_chroma_w;
  284. vsub = desc->log2_chroma_h;
  285. s->plane_height[0] = s->plane_height[3] = inlink->h;
  286. s->plane_width[0] = s->plane_width[3] = inlink->w;
  287. s->plane_height[1] = s->plane_height[2] = AV_CEIL_RSHIFT(inlink->h, vsub);
  288. s->plane_width[1] = s->plane_width[2] = AV_CEIL_RSHIFT(inlink->w, hsub);
  289. s->histogram_size = 1 << s->depth;
  290. for (int n = 0; n < s->radius; n++)
  291. s->kernel[n] = expf(-0.5 * (n + 1) * (n + 1) / (sigma * sigma));
  292. for (int p = 0; p < s->nb_planes; p++) {
  293. for (int n = 0; n < s->nb_frames; n++) {
  294. s->histogram[p][n] = av_calloc(s->histogram_size, sizeof(float));
  295. if (!s->histogram[p][n])
  296. return AVERROR(ENOMEM);
  297. }
  298. s->change[p] = av_calloc(s->histogram_size, sizeof(float));
  299. if (!s->change[p])
  300. return AVERROR(ENOMEM);
  301. }
  302. if (!s->frames)
  303. s->frames = av_calloc(s->nb_frames, sizeof(*s->frames));
  304. if (!s->frames)
  305. return AVERROR(ENOMEM);
  306. s->compute_histogram = s->depth <= 8 ? compute_histogram8 : compute_histogram16;
  307. s->apply_contrast_change = s->depth <= 8 ? apply_contrast_change8 : apply_contrast_change16;
  308. return 0;
  309. }
  310. static int request_frame(AVFilterLink *outlink)
  311. {
  312. AVFilterContext *ctx = outlink->src;
  313. TMidEqualizerContext *s = ctx->priv;
  314. int ret;
  315. ret = ff_request_frame(ctx->inputs[0]);
  316. if (ret == AVERROR_EOF && s->l_frames < s->radius) {
  317. ret = filter_frame(ctx->inputs[0], NULL);
  318. }
  319. return ret;
  320. }
  321. static void free_histograms(AVFilterContext *ctx, int x, int nb_frames)
  322. {
  323. TMidEqualizerContext *s = ctx->priv;
  324. for (int n = 0; n < nb_frames; n++)
  325. av_freep(&s->histogram[x][n]);
  326. av_freep(&s->change[x]);
  327. }
  328. static av_cold void uninit(AVFilterContext *ctx)
  329. {
  330. TMidEqualizerContext *s = ctx->priv;
  331. free_histograms(ctx, 0, s->nb_frames);
  332. free_histograms(ctx, 1, s->nb_frames);
  333. free_histograms(ctx, 2, s->nb_frames);
  334. free_histograms(ctx, 3, s->nb_frames);
  335. for (int i = 0; i < s->nb_frames && s->frames; i++)
  336. av_frame_free(&s->frames[i]);
  337. av_freep(&s->frames);
  338. }
  339. static const AVFilterPad tmidequalizer_inputs[] = {
  340. {
  341. .name = "default",
  342. .type = AVMEDIA_TYPE_VIDEO,
  343. .config_props = config_input,
  344. .filter_frame = filter_frame,
  345. },
  346. { NULL }
  347. };
  348. static const AVFilterPad tmidequalizer_outputs[] = {
  349. {
  350. .name = "default",
  351. .type = AVMEDIA_TYPE_VIDEO,
  352. .request_frame = request_frame,
  353. },
  354. { NULL }
  355. };
  356. AVFilter ff_vf_tmidequalizer = {
  357. .name = "tmidequalizer",
  358. .description = NULL_IF_CONFIG_SMALL("Apply Temporal Midway Equalization."),
  359. .priv_size = sizeof(TMidEqualizerContext),
  360. .uninit = uninit,
  361. .query_formats = query_formats,
  362. .inputs = tmidequalizer_inputs,
  363. .outputs = tmidequalizer_outputs,
  364. .priv_class = &tmidequalizer_class,
  365. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
  366. };