vf_smartblur.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /*
  2. * Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at>
  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 smartblur filter to the input video
  24. * Ported from MPlayer libmpcodecs/vf_smartblur.c by Michael Niedermayer.
  25. */
  26. #include "libavutil/pixdesc.h"
  27. #include "libswscale/swscale.h"
  28. #include "avfilter.h"
  29. #include "formats.h"
  30. #include "internal.h"
  31. #define RADIUS_MIN 0.1
  32. #define RADIUS_MAX 5.0
  33. #define STRENGTH_MIN -1.0
  34. #define STRENGTH_MAX 1.0
  35. #define THRESHOLD_MIN -30
  36. #define THRESHOLD_MAX 30
  37. typedef struct {
  38. float radius;
  39. float strength;
  40. int threshold;
  41. float quality;
  42. struct SwsContext *filter_context;
  43. } FilterParam;
  44. typedef struct {
  45. FilterParam luma;
  46. FilterParam chroma;
  47. int hsub;
  48. int vsub;
  49. unsigned int sws_flags;
  50. } SmartblurContext;
  51. #define CHECK_PARAM(param, name, min, max, format, ret) \
  52. if (param < min || param > max) { \
  53. av_log(ctx, AV_LOG_ERROR, \
  54. "Invalid " #name " value " #format ": " \
  55. "must be included between range " #format " and " #format "\n",\
  56. param, min, max); \
  57. ret = AVERROR(EINVAL); \
  58. }
  59. static av_cold int init(AVFilterContext *ctx, const char *args)
  60. {
  61. SmartblurContext *sblur = ctx->priv;
  62. int n = 0, ret = 0;
  63. float lradius, lstrength, cradius, cstrength;
  64. int lthreshold, cthreshold;
  65. if (args)
  66. n = sscanf(args, "%f:%f:%d:%f:%f:%d",
  67. &lradius, &lstrength, &lthreshold,
  68. &cradius, &cstrength, &cthreshold);
  69. if (n != 3 && n != 6) {
  70. av_log(ctx, AV_LOG_ERROR,
  71. "Incorrect number of parameters or invalid syntax: "
  72. "must be luma_radius:luma_strength:luma_threshold"
  73. "[:chroma_radius:chroma_strength:chroma_threshold]\n");
  74. return AVERROR(EINVAL);
  75. }
  76. sblur->luma.radius = lradius;
  77. sblur->luma.strength = lstrength;
  78. sblur->luma.threshold = lthreshold;
  79. if (n == 3) {
  80. sblur->chroma.radius = sblur->luma.radius;
  81. sblur->chroma.strength = sblur->luma.strength;
  82. sblur->chroma.threshold = sblur->luma.threshold;
  83. } else {
  84. sblur->chroma.radius = cradius;
  85. sblur->chroma.strength = cstrength;
  86. sblur->chroma.threshold = cthreshold;
  87. }
  88. sblur->luma.quality = sblur->chroma.quality = 3.0;
  89. sblur->sws_flags = SWS_BICUBIC;
  90. CHECK_PARAM(lradius, luma radius, RADIUS_MIN, RADIUS_MAX, %0.1f, ret)
  91. CHECK_PARAM(lstrength, luma strength, STRENGTH_MIN, STRENGTH_MAX, %0.1f, ret)
  92. CHECK_PARAM(lthreshold, luma threshold, THRESHOLD_MIN, THRESHOLD_MAX, %d, ret)
  93. if (n != 3) {
  94. CHECK_PARAM(sblur->chroma.radius, chroma radius, RADIUS_MIN, RADIUS_MAX, %0.1f, ret)
  95. CHECK_PARAM(sblur->chroma.strength, chroma strength, STRENGTH_MIN, STRENGTH_MAX, %0.1f, ret)
  96. CHECK_PARAM(sblur->chroma.threshold, chroma threshold, THRESHOLD_MIN,THRESHOLD_MAX, %d, ret)
  97. }
  98. return ret;
  99. }
  100. static av_cold void uninit(AVFilterContext *ctx)
  101. {
  102. SmartblurContext *sblur = ctx->priv;
  103. sws_freeContext(sblur->luma.filter_context);
  104. sws_freeContext(sblur->chroma.filter_context);
  105. }
  106. static int query_formats(AVFilterContext *ctx)
  107. {
  108. static const enum PixelFormat pix_fmts[] = {
  109. PIX_FMT_YUV444P, PIX_FMT_YUV422P,
  110. PIX_FMT_YUV420P, PIX_FMT_YUV411P,
  111. PIX_FMT_YUV410P, PIX_FMT_YUV440P,
  112. PIX_FMT_GRAY8,
  113. PIX_FMT_NONE
  114. };
  115. ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
  116. return 0;
  117. }
  118. static int alloc_sws_context(FilterParam *f, int width, int height, unsigned int flags)
  119. {
  120. SwsVector *vec;
  121. SwsFilter sws_filter;
  122. vec = sws_getGaussianVec(f->radius, f->quality);
  123. if (!vec)
  124. return AVERROR(EINVAL);
  125. sws_scaleVec(vec, f->strength);
  126. vec->coeff[vec->length / 2] += 1.0 - f->strength;
  127. sws_filter.lumH = sws_filter.lumV = vec;
  128. sws_filter.chrH = sws_filter.chrV = NULL;
  129. f->filter_context = sws_getCachedContext(NULL,
  130. width, height, PIX_FMT_GRAY8,
  131. width, height, PIX_FMT_GRAY8,
  132. flags, &sws_filter, NULL, NULL);
  133. sws_freeVec(vec);
  134. if (!f->filter_context)
  135. return AVERROR(EINVAL);
  136. return 0;
  137. }
  138. static int config_props(AVFilterLink *inlink)
  139. {
  140. SmartblurContext *sblur = inlink->dst->priv;
  141. const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[inlink->format];
  142. sblur->hsub = desc->log2_chroma_w;
  143. sblur->vsub = desc->log2_chroma_h;
  144. alloc_sws_context(&sblur->luma, inlink->w, inlink->h, sblur->sws_flags);
  145. alloc_sws_context(&sblur->chroma,
  146. inlink->w >> sblur->hsub, inlink->h >> sblur->vsub,
  147. sblur->sws_flags);
  148. return 0;
  149. }
  150. static void blur(uint8_t *dst, const int dst_linesize,
  151. const uint8_t *src, const int src_linesize,
  152. const int w, const int h, const int threshold,
  153. struct SwsContext *filter_context)
  154. {
  155. int x, y;
  156. int orig, filtered;
  157. int diff;
  158. /* Declare arrays of 4 to get aligned data */
  159. const uint8_t* const src_array[4] = {src};
  160. uint8_t *dst_array[4] = {dst};
  161. int src_linesize_array[4] = {src_linesize};
  162. int dst_linesize_array[4] = {dst_linesize};
  163. sws_scale(filter_context, src_array, src_linesize_array,
  164. 0, h, dst_array, dst_linesize_array);
  165. if (threshold > 0) {
  166. for (y = 0; y < h; ++y) {
  167. for (x = 0; x < w; ++x) {
  168. orig = src[x + y * src_linesize];
  169. filtered = dst[x + y * dst_linesize];
  170. diff = orig - filtered;
  171. if (diff > 0) {
  172. if (diff > 2 * threshold)
  173. dst[x + y * dst_linesize] = orig;
  174. else if (diff > threshold)
  175. /* add 'diff' and substract 'threshold' from 'filtered' */
  176. dst[x + y * dst_linesize] = orig - threshold;
  177. } else {
  178. if (-diff > 2 * threshold)
  179. dst[x + y * dst_linesize] = orig;
  180. else if (-diff > threshold)
  181. /* add 'diff' and 'threshold' to 'filtered' */
  182. dst[x + y * dst_linesize] = orig + threshold;
  183. }
  184. }
  185. }
  186. } else if (threshold < 0) {
  187. for (y = 0; y < h; ++y) {
  188. for (x = 0; x < w; ++x) {
  189. orig = src[x + y * src_linesize];
  190. filtered = dst[x + y * dst_linesize];
  191. diff = orig - filtered;
  192. if (diff > 0) {
  193. if (diff <= -threshold)
  194. dst[x + y * dst_linesize] = orig;
  195. else if (diff <= -2 * threshold)
  196. /* substract 'diff' and 'threshold' from 'orig' */
  197. dst[x + y * dst_linesize] = filtered - threshold;
  198. } else {
  199. if (diff >= threshold)
  200. dst[x + y * dst_linesize] = orig;
  201. else if (diff >= 2 * threshold)
  202. /* add 'threshold' and substract 'diff' from 'orig' */
  203. dst[x + y * dst_linesize] = filtered + threshold;
  204. }
  205. }
  206. }
  207. }
  208. }
  209. static int end_frame(AVFilterLink *inlink)
  210. {
  211. SmartblurContext *sblur = inlink->dst->priv;
  212. AVFilterBufferRef *inpic = inlink->cur_buf;
  213. AVFilterBufferRef *outpic = inlink->dst->outputs[0]->out_buf;
  214. int cw = inlink->w >> sblur->hsub;
  215. int ch = inlink->h >> sblur->vsub;
  216. blur(outpic->data[0], outpic->linesize[0],
  217. inpic->data[0], inpic->linesize[0],
  218. inlink->w, inlink->h, sblur->luma.threshold,
  219. sblur->luma.filter_context);
  220. if (inpic->data[2]) {
  221. blur(outpic->data[1], outpic->linesize[1],
  222. inpic->data[1], inpic->linesize[1],
  223. cw, ch, sblur->chroma.threshold,
  224. sblur->chroma.filter_context);
  225. blur(outpic->data[2], outpic->linesize[2],
  226. inpic->data[2], inpic->linesize[2],
  227. cw, ch, sblur->chroma.threshold,
  228. sblur->chroma.filter_context);
  229. }
  230. return ff_end_frame(inlink->dst->outputs[0]);
  231. }
  232. AVFilter avfilter_vf_smartblur = {
  233. .name = "smartblur",
  234. .description = NULL_IF_CONFIG_SMALL("Blur the input video without impacting the outlines."),
  235. .priv_size = sizeof(SmartblurContext),
  236. .init = init,
  237. .uninit = uninit,
  238. .query_formats = query_formats,
  239. .inputs = (const AVFilterPad[]) {
  240. {
  241. .name = "default",
  242. .type = AVMEDIA_TYPE_VIDEO,
  243. .end_frame = end_frame,
  244. .config_props = config_props,
  245. .min_perms = AV_PERM_READ,
  246. },
  247. { .name = NULL }
  248. },
  249. .outputs = (const AVFilterPad[]) {
  250. {
  251. .name = "default",
  252. .type = AVMEDIA_TYPE_VIDEO,
  253. },
  254. { .name = NULL }
  255. }
  256. };