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.

410 lines
17KB

  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/imgutils.h"
  21. #include "libavutil/opt.h"
  22. #include "libavutil/pixdesc.h"
  23. #include "avfilter.h"
  24. #include "filters.h"
  25. #include "formats.h"
  26. #include "internal.h"
  27. #include "video.h"
  28. typedef struct DedotContext {
  29. const AVClass *class;
  30. int m;
  31. float lt;
  32. float tl;
  33. float tc;
  34. float ct;
  35. const AVPixFmtDescriptor *desc;
  36. int depth;
  37. int max;
  38. int luma2d;
  39. int lumaT;
  40. int chromaT1;
  41. int chromaT2;
  42. int eof;
  43. int eof_frames;
  44. int nb_planes;
  45. int planewidth[4];
  46. int planeheight[4];
  47. AVFrame *frames[5];
  48. int (*dedotcrawl)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
  49. int (*derainbow)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
  50. } DedotContext;
  51. static int query_formats(AVFilterContext *ctx)
  52. {
  53. static const enum AVPixelFormat pixel_fmts[] = {
  54. AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
  55. AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
  56. AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
  57. AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
  58. AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
  59. AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
  60. AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
  61. AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
  62. AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
  63. AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
  64. AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
  65. AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
  66. AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
  67. AV_PIX_FMT_NONE
  68. };
  69. AVFilterFormats *formats = ff_make_format_list(pixel_fmts);
  70. if (!formats)
  71. return AVERROR(ENOMEM);
  72. return ff_set_common_formats(ctx, formats);
  73. }
  74. #define DEFINE_DEDOTCRAWL(name, type, div) \
  75. static int dedotcrawl##name(AVFilterContext *ctx, void *arg, \
  76. int jobnr, int nb_jobs) \
  77. { \
  78. DedotContext *s = ctx->priv; \
  79. AVFrame *out = arg; \
  80. int src_linesize = s->frames[2]->linesize[0] / div; \
  81. int dst_linesize = out->linesize[0] / div; \
  82. int p0_linesize = s->frames[0]->linesize[0] / div; \
  83. int p1_linesize = s->frames[1]->linesize[0] / div; \
  84. int p3_linesize = s->frames[3]->linesize[0] / div; \
  85. int p4_linesize = s->frames[4]->linesize[0] / div; \
  86. const int h = s->planeheight[0]; \
  87. int slice_start = (h * jobnr) / nb_jobs; \
  88. int slice_end = (h * (jobnr+1)) / nb_jobs; \
  89. type *p0 = (type *)s->frames[0]->data[0]; \
  90. type *p1 = (type *)s->frames[1]->data[0]; \
  91. type *p3 = (type *)s->frames[3]->data[0]; \
  92. type *p4 = (type *)s->frames[4]->data[0]; \
  93. type *src = (type *)s->frames[2]->data[0]; \
  94. type *dst = (type *)out->data[0]; \
  95. const int luma2d = s->luma2d; \
  96. const int lumaT = s->lumaT; \
  97. \
  98. if (!slice_start) { \
  99. slice_start++; \
  100. } \
  101. p0 += p0_linesize * slice_start; \
  102. p1 += p1_linesize * slice_start; \
  103. p3 += p3_linesize * slice_start; \
  104. p4 += p4_linesize * slice_start; \
  105. src += src_linesize * slice_start; \
  106. dst += dst_linesize * slice_start; \
  107. if (slice_end == h) { \
  108. slice_end--; \
  109. } \
  110. for (int y = slice_start; y < slice_end; y++) { \
  111. for (int x = 1; x < s->planewidth[0] - 1; x++) { \
  112. int above = src[x - src_linesize]; \
  113. int bellow = src[x + src_linesize]; \
  114. int cur = src[x]; \
  115. int left = src[x - 1]; \
  116. int right = src[x + 1]; \
  117. \
  118. if (FFABS(above + bellow - 2 * cur) <= luma2d && \
  119. FFABS(left + right - 2 * cur) <= luma2d) \
  120. continue; \
  121. \
  122. if (FFABS(cur - p0[x]) <= lumaT && \
  123. FFABS(cur - p4[x]) <= lumaT && \
  124. FFABS(p1[x] - p3[x]) <= lumaT) { \
  125. int diff1 = FFABS(cur - p1[x]); \
  126. int diff2 = FFABS(cur - p3[x]); \
  127. \
  128. if (diff1 < diff2) \
  129. dst[x] = (src[x] + p1[x] + 1) >> 1; \
  130. else \
  131. dst[x] = (src[x] + p3[x] + 1) >> 1; \
  132. } \
  133. } \
  134. \
  135. dst += dst_linesize; \
  136. src += src_linesize; \
  137. p0 += p0_linesize; \
  138. p1 += p1_linesize; \
  139. p3 += p3_linesize; \
  140. p4 += p4_linesize; \
  141. } \
  142. return 0; \
  143. }
  144. DEFINE_DEDOTCRAWL(8, uint8_t, 1)
  145. DEFINE_DEDOTCRAWL(16, uint16_t, 2)
  146. typedef struct ThreadData {
  147. AVFrame *out;
  148. int plane;
  149. } ThreadData;
  150. #define DEFINE_DERAINBOW(name, type, div) \
  151. static int derainbow##name(AVFilterContext *ctx, void *arg, \
  152. int jobnr, int nb_jobs) \
  153. { \
  154. DedotContext *s = ctx->priv; \
  155. ThreadData *td = arg; \
  156. AVFrame *out = td->out; \
  157. const int plane = td->plane; \
  158. const int h = s->planeheight[plane]; \
  159. int slice_start = (h * jobnr) / nb_jobs; \
  160. int slice_end = (h * (jobnr+1)) / nb_jobs; \
  161. int src_linesize = s->frames[2]->linesize[plane] / div; \
  162. int dst_linesize = out->linesize[plane] / div; \
  163. int p0_linesize = s->frames[0]->linesize[plane] / div; \
  164. int p1_linesize = s->frames[1]->linesize[plane] / div; \
  165. int p3_linesize = s->frames[3]->linesize[plane] / div; \
  166. int p4_linesize = s->frames[4]->linesize[plane] / div; \
  167. type *p0 = (type *)s->frames[0]->data[plane]; \
  168. type *p1 = (type *)s->frames[1]->data[plane]; \
  169. type *p3 = (type *)s->frames[3]->data[plane]; \
  170. type *p4 = (type *)s->frames[4]->data[plane]; \
  171. type *src = (type *)s->frames[2]->data[plane]; \
  172. type *dst = (type *)out->data[plane]; \
  173. const int chromaT1 = s->chromaT1; \
  174. const int chromaT2 = s->chromaT2; \
  175. \
  176. p0 += slice_start * p0_linesize; \
  177. p1 += slice_start * p1_linesize; \
  178. p3 += slice_start * p3_linesize; \
  179. p4 += slice_start * p4_linesize; \
  180. src += slice_start * src_linesize; \
  181. dst += slice_start * dst_linesize; \
  182. for (int y = slice_start; y < slice_end; y++) { \
  183. for (int x = 0; x < s->planewidth[plane]; x++) { \
  184. int cur = src[x]; \
  185. \
  186. if (FFABS(cur - p0[x]) <= chromaT1 && \
  187. FFABS(cur - p4[x]) <= chromaT1 && \
  188. FFABS(p1[x] - p3[x]) <= chromaT1 && \
  189. FFABS(cur - p1[x]) > chromaT2 && \
  190. FFABS(cur - p3[x]) > chromaT2) { \
  191. int diff1 = FFABS(cur - p1[x]); \
  192. int diff2 = FFABS(cur - p3[x]); \
  193. \
  194. if (diff1 < diff2) \
  195. dst[x] = (src[x] + p1[x] + 1) >> 1; \
  196. else \
  197. dst[x] = (src[x] + p3[x] + 1) >> 1; \
  198. } \
  199. } \
  200. \
  201. dst += dst_linesize; \
  202. src += src_linesize; \
  203. p0 += p0_linesize; \
  204. p1 += p1_linesize; \
  205. p3 += p3_linesize; \
  206. p4 += p4_linesize; \
  207. } \
  208. return 0; \
  209. }
  210. DEFINE_DERAINBOW(8, uint8_t, 1)
  211. DEFINE_DERAINBOW(16, uint16_t, 2)
  212. static int config_output(AVFilterLink *outlink)
  213. {
  214. AVFilterContext *ctx = outlink->src;
  215. DedotContext *s = ctx->priv;
  216. AVFilterLink *inlink = ctx->inputs[0];
  217. s->desc = av_pix_fmt_desc_get(outlink->format);
  218. if (!s->desc)
  219. return AVERROR_BUG;
  220. s->nb_planes = av_pix_fmt_count_planes(outlink->format);
  221. s->depth = s->desc->comp[0].depth;
  222. s->max = (1 << s->depth) - 1;
  223. s->luma2d = s->lt * s->max;
  224. s->lumaT = s->tl * s->max;
  225. s->chromaT1 = s->tc * s->max;
  226. s->chromaT2 = s->ct * s->max;
  227. s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, s->desc->log2_chroma_w);
  228. s->planewidth[0] = s->planewidth[3] = inlink->w;
  229. s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h);
  230. s->planeheight[0] = s->planeheight[3] = inlink->h;
  231. if (s->depth <= 8) {
  232. s->dedotcrawl = dedotcrawl8;
  233. s->derainbow = derainbow8;
  234. } else {
  235. s->dedotcrawl = dedotcrawl16;
  236. s->derainbow = derainbow16;
  237. }
  238. return 0;
  239. }
  240. static int activate(AVFilterContext *ctx)
  241. {
  242. AVFilterLink *inlink = ctx->inputs[0];
  243. AVFilterLink *outlink = ctx->outputs[0];
  244. DedotContext *s = ctx->priv;
  245. AVFrame *frame = NULL;
  246. int64_t pts;
  247. int status;
  248. int ret = 0;
  249. FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
  250. if (s->eof == 0) {
  251. ret = ff_inlink_consume_frame(inlink, &frame);
  252. if (ret < 0)
  253. return ret;
  254. }
  255. if (frame || s->eof_frames > 0) {
  256. AVFrame *out = NULL;
  257. if (frame) {
  258. for (int i = 2; i < 5; i++) {
  259. if (!s->frames[i])
  260. s->frames[i] = av_frame_clone(frame);
  261. }
  262. av_frame_free(&frame);
  263. } else {
  264. s->eof_frames--;
  265. s->frames[4] = av_frame_clone(s->frames[3]);
  266. }
  267. if (s->frames[0] &&
  268. s->frames[1] &&
  269. s->frames[2] &&
  270. s->frames[3] &&
  271. s->frames[4]) {
  272. out = av_frame_clone(s->frames[2]);
  273. if (out && !ctx->is_disabled) {
  274. ret = av_frame_make_writable(out);
  275. if (ret >= 0) {
  276. if (s->m & 1)
  277. ctx->internal->execute(ctx, s->dedotcrawl, out, NULL,
  278. FFMIN(s->planeheight[0],
  279. ff_filter_get_nb_threads(ctx)));
  280. if (s->m & 2) {
  281. ThreadData td;
  282. td.out = out; td.plane = 1;
  283. ctx->internal->execute(ctx, s->derainbow, &td, NULL,
  284. FFMIN(s->planeheight[1],
  285. ff_filter_get_nb_threads(ctx)));
  286. td.plane = 2;
  287. ctx->internal->execute(ctx, s->derainbow, &td, NULL,
  288. FFMIN(s->planeheight[2],
  289. ff_filter_get_nb_threads(ctx)));
  290. }
  291. }
  292. } else if (!out) {
  293. ret = AVERROR(ENOMEM);
  294. }
  295. }
  296. av_frame_free(&s->frames[0]);
  297. s->frames[0] = s->frames[1];
  298. s->frames[1] = s->frames[2];
  299. s->frames[2] = s->frames[3];
  300. s->frames[3] = s->frames[4];
  301. s->frames[4] = NULL;
  302. if (ret < 0)
  303. return ret;
  304. if (out)
  305. return ff_filter_frame(outlink, out);
  306. }
  307. if (s->eof) {
  308. if (s->eof_frames <= 0) {
  309. ff_outlink_set_status(outlink, AVERROR_EOF, s->frames[2]->pts);
  310. } else {
  311. ff_filter_set_ready(ctx, 10);
  312. }
  313. return 0;
  314. }
  315. if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) {
  316. if (status == AVERROR_EOF) {
  317. s->eof = 1;
  318. s->eof_frames = 2;
  319. ff_filter_set_ready(ctx, 10);
  320. return 0;
  321. }
  322. }
  323. FF_FILTER_FORWARD_WANTED(outlink, inlink);
  324. return FFERROR_NOT_READY;
  325. }
  326. static av_cold void uninit(AVFilterContext *ctx)
  327. {
  328. DedotContext *s = ctx->priv;
  329. for (int i = 0; i < 5; i++)
  330. av_frame_free(&s->frames[i]);
  331. }
  332. #define OFFSET(x) offsetof(DedotContext, x)
  333. #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
  334. static const AVOption dedot_options[] = {
  335. { "m", "set filtering mode", OFFSET( m), AV_OPT_TYPE_FLAGS, {.i64=3}, 0, 3, FLAGS, "m" },
  336. { "dotcrawl", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "m" },
  337. { "rainbows", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "m" },
  338. { "lt", "set spatial luma threshold", OFFSET(lt), AV_OPT_TYPE_FLOAT, {.dbl=.079}, 0, 1, FLAGS },
  339. { "tl", "set tolerance for temporal luma", OFFSET(tl), AV_OPT_TYPE_FLOAT, {.dbl=.079}, 0, 1, FLAGS },
  340. { "tc", "set tolerance for chroma temporal variation", OFFSET(tc), AV_OPT_TYPE_FLOAT, {.dbl=.058}, 0, 1, FLAGS },
  341. { "ct", "set temporal chroma threshold", OFFSET(ct), AV_OPT_TYPE_FLOAT, {.dbl=.019}, 0, 1, FLAGS },
  342. { NULL },
  343. };
  344. static const AVFilterPad inputs[] = {
  345. {
  346. .name = "default",
  347. .type = AVMEDIA_TYPE_VIDEO,
  348. },
  349. { NULL }
  350. };
  351. static const AVFilterPad outputs[] = {
  352. {
  353. .name = "default",
  354. .type = AVMEDIA_TYPE_VIDEO,
  355. .config_props = config_output,
  356. },
  357. { NULL }
  358. };
  359. AVFILTER_DEFINE_CLASS(dedot);
  360. AVFilter ff_vf_dedot = {
  361. .name = "dedot",
  362. .description = NULL_IF_CONFIG_SMALL("Reduce cross-luminance and cross-color."),
  363. .priv_size = sizeof(DedotContext),
  364. .priv_class = &dedot_class,
  365. .query_formats = query_formats,
  366. .activate = activate,
  367. .uninit = uninit,
  368. .inputs = inputs,
  369. .outputs = outputs,
  370. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
  371. };