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.

371 lines
18KB

  1. /*
  2. * Original copyright (c) 2002 Remi Guyomarch <rguyom@pobox.com>
  3. * Port copyright (c) 2010 Daniel G. Taylor <dan@programmer-art.org>
  4. * Relicensed to the LGPL with permission from Remi Guyomarch.
  5. *
  6. * This file is part of FFmpeg.
  7. *
  8. * FFmpeg is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * FFmpeg is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with FFmpeg; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. */
  22. /**
  23. * @file
  24. * blur / sharpen filter, ported to FFmpeg from MPlayer
  25. * libmpcodecs/unsharp.c.
  26. *
  27. * This code is based on:
  28. *
  29. * An Efficient algorithm for Gaussian blur using finite-state machines
  30. * Frederick M. Waltz and John W. V. Miller
  31. *
  32. * SPIE Conf. on Machine Vision Systems for Inspection and Metrology VII
  33. * Originally published Boston, Nov 98
  34. *
  35. * http://www.engin.umd.umich.edu/~jwvm/ece581/21_GBlur.pdf
  36. */
  37. #include "avfilter.h"
  38. #include "formats.h"
  39. #include "internal.h"
  40. #include "video.h"
  41. #include "libavutil/common.h"
  42. #include "libavutil/imgutils.h"
  43. #include "libavutil/mem.h"
  44. #include "libavutil/opt.h"
  45. #include "libavutil/pixdesc.h"
  46. #include "unsharp.h"
  47. typedef struct TheadData {
  48. UnsharpFilterParam *fp;
  49. uint8_t *dst;
  50. const uint8_t *src;
  51. int dst_stride;
  52. int src_stride;
  53. int width;
  54. int height;
  55. } ThreadData;
  56. #define DEF_UNSHARP_SLICE_FUNC(name, nbits) \
  57. static int name##_##nbits(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
  58. { \
  59. ThreadData *td = arg; \
  60. UnsharpFilterParam *fp = td->fp; \
  61. UnsharpContext *s = ctx->priv; \
  62. uint32_t **sc = fp->sc; \
  63. uint32_t *sr = fp->sr; \
  64. const uint##nbits##_t *src2 = NULL; \
  65. const int amount = fp->amount; \
  66. const int steps_x = fp->steps_x; \
  67. const int steps_y = fp->steps_y; \
  68. const int scalebits = fp->scalebits; \
  69. const int32_t halfscale = fp->halfscale; \
  70. \
  71. uint##nbits##_t *dst = (uint##nbits##_t*)td->dst; \
  72. const uint##nbits##_t *src = (const uint##nbits##_t *)td->src; \
  73. int dst_stride = td->dst_stride; \
  74. int src_stride = td->src_stride; \
  75. const int width = td->width; \
  76. const int height = td->height; \
  77. const int sc_offset = jobnr * 2 * steps_y; \
  78. const int sr_offset = jobnr * (MAX_MATRIX_SIZE - 1); \
  79. const int slice_start = (height * jobnr) / nb_jobs; \
  80. const int slice_end = (height * (jobnr+1)) / nb_jobs; \
  81. \
  82. int32_t res; \
  83. int x, y, z; \
  84. uint32_t tmp1, tmp2; \
  85. \
  86. if (!amount) { \
  87. av_image_copy_plane(td->dst + slice_start * dst_stride, dst_stride, \
  88. td->src + slice_start * src_stride, src_stride, \
  89. width * s->bps, slice_end - slice_start); \
  90. return 0; \
  91. } \
  92. \
  93. for (y = 0; y < 2 * steps_y; y++) \
  94. memset(sc[sc_offset + y], 0, sizeof(sc[y][0]) * (width + 2 * steps_x)); \
  95. \
  96. dst_stride = dst_stride / s->bps; \
  97. src_stride = src_stride / s->bps; \
  98. /* if this is not the first tile, we start from (slice_start - steps_y) */ \
  99. /* so we can get smooth result at slice boundary */ \
  100. if (slice_start > steps_y) { \
  101. src += (slice_start - steps_y) * src_stride; \
  102. dst += (slice_start - steps_y) * dst_stride; \
  103. } \
  104. \
  105. for (y = -steps_y + slice_start; y < steps_y + slice_end; y++) { \
  106. if (y < height) \
  107. src2 = src; \
  108. \
  109. memset(sr + sr_offset, 0, sizeof(sr[0]) * (2 * steps_x - 1)); \
  110. for (x = -steps_x; x < width + steps_x; x++) { \
  111. tmp1 = x <= 0 ? src2[0] : x >= width ? src2[width-1] : src2[x]; \
  112. for (z = 0; z < steps_x * 2; z += 2) { \
  113. tmp2 = sr[sr_offset + z + 0] + tmp1; sr[sr_offset + z + 0] = tmp1; \
  114. tmp1 = sr[sr_offset + z + 1] + tmp2; sr[sr_offset + z + 1] = tmp2; \
  115. } \
  116. for (z = 0; z < steps_y * 2; z += 2) { \
  117. tmp2 = sc[sc_offset + z + 0][x + steps_x] + tmp1; \
  118. sc[sc_offset + z + 0][x + steps_x] = tmp1; \
  119. tmp1 = sc[sc_offset + z + 1][x + steps_x] + tmp2; \
  120. sc[sc_offset + z + 1][x + steps_x] = tmp2; \
  121. } \
  122. if (x >= steps_x && y >= (steps_y + slice_start)) { \
  123. const uint##nbits##_t *srx = src - steps_y * src_stride + x - steps_x; \
  124. uint##nbits##_t *dsx = dst - steps_y * dst_stride + x - steps_x; \
  125. \
  126. res = (int32_t)*srx + ((((int32_t) * srx - \
  127. (int32_t)((tmp1 + halfscale) >> scalebits)) * amount) >> (8+nbits)); \
  128. *dsx = av_clip_uint##nbits(res); \
  129. } \
  130. } \
  131. if (y >= 0) { \
  132. dst += dst_stride; \
  133. src += src_stride; \
  134. } \
  135. } \
  136. return 0; \
  137. }
  138. DEF_UNSHARP_SLICE_FUNC(unsharp_slice, 16)
  139. DEF_UNSHARP_SLICE_FUNC(unsharp_slice, 8)
  140. static int apply_unsharp_c(AVFilterContext *ctx, AVFrame *in, AVFrame *out)
  141. {
  142. AVFilterLink *inlink = ctx->inputs[0];
  143. UnsharpContext *s = ctx->priv;
  144. int i, plane_w[3], plane_h[3];
  145. UnsharpFilterParam *fp[3];
  146. ThreadData td;
  147. plane_w[0] = inlink->w;
  148. plane_w[1] = plane_w[2] = AV_CEIL_RSHIFT(inlink->w, s->hsub);
  149. plane_h[0] = inlink->h;
  150. plane_h[1] = plane_h[2] = AV_CEIL_RSHIFT(inlink->h, s->vsub);
  151. fp[0] = &s->luma;
  152. fp[1] = fp[2] = &s->chroma;
  153. for (i = 0; i < 3; i++) {
  154. td.fp = fp[i];
  155. td.dst = out->data[i];
  156. td.src = in->data[i];
  157. td.width = plane_w[i];
  158. td.height = plane_h[i];
  159. td.dst_stride = out->linesize[i];
  160. td.src_stride = in->linesize[i];
  161. ctx->internal->execute(ctx, s->unsharp_slice, &td, NULL, FFMIN(plane_h[i], s->nb_threads));
  162. }
  163. return 0;
  164. }
  165. static void set_filter_param(UnsharpFilterParam *fp, int msize_x, int msize_y, float amount)
  166. {
  167. fp->msize_x = msize_x;
  168. fp->msize_y = msize_y;
  169. fp->amount = amount * 65536.0;
  170. fp->steps_x = msize_x / 2;
  171. fp->steps_y = msize_y / 2;
  172. fp->scalebits = (fp->steps_x + fp->steps_y) * 2;
  173. fp->halfscale = 1 << (fp->scalebits - 1);
  174. }
  175. static av_cold int init(AVFilterContext *ctx)
  176. {
  177. UnsharpContext *s = ctx->priv;
  178. set_filter_param(&s->luma, s->lmsize_x, s->lmsize_y, s->lamount);
  179. set_filter_param(&s->chroma, s->cmsize_x, s->cmsize_y, s->camount);
  180. if (s->luma.scalebits >= 26 || s->chroma.scalebits >= 26) {
  181. av_log(ctx, AV_LOG_ERROR, "luma or chroma matrix size too big\n");
  182. return AVERROR(EINVAL);
  183. }
  184. s->apply_unsharp = apply_unsharp_c;
  185. return 0;
  186. }
  187. static int query_formats(AVFilterContext *ctx)
  188. {
  189. static const enum AVPixelFormat pix_fmts[] = {
  190. AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV410P,
  191. AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,
  192. AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
  193. AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV440P10,
  194. AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
  195. AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
  196. AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_NONE
  197. };
  198. AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
  199. if (!fmts_list)
  200. return AVERROR(ENOMEM);
  201. return ff_set_common_formats(ctx, fmts_list);
  202. }
  203. static int init_filter_param(AVFilterContext *ctx, UnsharpFilterParam *fp, const char *effect_type, int width)
  204. {
  205. int z;
  206. UnsharpContext *s = ctx->priv;
  207. const char *effect = fp->amount == 0 ? "none" : fp->amount < 0 ? "blur" : "sharpen";
  208. if (!(fp->msize_x & fp->msize_y & 1)) {
  209. av_log(ctx, AV_LOG_ERROR,
  210. "Invalid even size for %s matrix size %dx%d\n",
  211. effect_type, fp->msize_x, fp->msize_y);
  212. return AVERROR(EINVAL);
  213. }
  214. av_log(ctx, AV_LOG_VERBOSE, "effect:%s type:%s msize_x:%d msize_y:%d amount:%0.2f\n",
  215. effect, effect_type, fp->msize_x, fp->msize_y, fp->amount / 65535.0);
  216. fp->sr = av_malloc_array((MAX_MATRIX_SIZE - 1) * s->nb_threads, sizeof(uint32_t));
  217. fp->sc = av_mallocz_array(2 * fp->steps_y * s->nb_threads, sizeof(uint32_t *));
  218. if (!fp->sr || !fp->sc)
  219. return AVERROR(ENOMEM);
  220. for (z = 0; z < 2 * fp->steps_y * s->nb_threads; z++)
  221. if (!(fp->sc[z] = av_malloc_array(width + 2 * fp->steps_x,
  222. sizeof(*(fp->sc[z])))))
  223. return AVERROR(ENOMEM);
  224. return 0;
  225. }
  226. static int config_input(AVFilterLink *inlink)
  227. {
  228. UnsharpContext *s = inlink->dst->priv;
  229. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
  230. int ret;
  231. s->hsub = desc->log2_chroma_w;
  232. s->vsub = desc->log2_chroma_h;
  233. s->bitdepth = desc->comp[0].depth;
  234. s->bps = s->bitdepth > 8 ? 2 : 1;
  235. s->unsharp_slice = s->bitdepth > 8 ? unsharp_slice_16 : unsharp_slice_8;
  236. // ensure (height / nb_threads) > 4 * steps_y,
  237. // so that we don't have too much overlap between two threads
  238. s->nb_threads = FFMIN(ff_filter_get_nb_threads(inlink->dst),
  239. inlink->h / (4 * s->luma.steps_y));
  240. ret = init_filter_param(inlink->dst, &s->luma, "luma", inlink->w);
  241. if (ret < 0)
  242. return ret;
  243. ret = init_filter_param(inlink->dst, &s->chroma, "chroma", AV_CEIL_RSHIFT(inlink->w, s->hsub));
  244. if (ret < 0)
  245. return ret;
  246. return 0;
  247. }
  248. static void free_filter_param(UnsharpFilterParam *fp, int nb_threads)
  249. {
  250. int z;
  251. if (fp->sc) {
  252. for (z = 0; z < 2 * fp->steps_y * nb_threads; z++)
  253. av_freep(&fp->sc[z]);
  254. av_freep(&fp->sc);
  255. }
  256. av_freep(&fp->sr);
  257. }
  258. static av_cold void uninit(AVFilterContext *ctx)
  259. {
  260. UnsharpContext *s = ctx->priv;
  261. free_filter_param(&s->luma, s->nb_threads);
  262. free_filter_param(&s->chroma, s->nb_threads);
  263. }
  264. static int filter_frame(AVFilterLink *link, AVFrame *in)
  265. {
  266. UnsharpContext *s = link->dst->priv;
  267. AVFilterLink *outlink = link->dst->outputs[0];
  268. AVFrame *out;
  269. int ret = 0;
  270. out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  271. if (!out) {
  272. av_frame_free(&in);
  273. return AVERROR(ENOMEM);
  274. }
  275. av_frame_copy_props(out, in);
  276. ret = s->apply_unsharp(link->dst, in, out);
  277. av_frame_free(&in);
  278. if (ret < 0) {
  279. av_frame_free(&out);
  280. return ret;
  281. }
  282. return ff_filter_frame(outlink, out);
  283. }
  284. #define OFFSET(x) offsetof(UnsharpContext, x)
  285. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  286. #define MIN_SIZE 3
  287. #define MAX_SIZE 23
  288. static const AVOption unsharp_options[] = {
  289. { "luma_msize_x", "set luma matrix horizontal size", OFFSET(lmsize_x), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS },
  290. { "lx", "set luma matrix horizontal size", OFFSET(lmsize_x), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS },
  291. { "luma_msize_y", "set luma matrix vertical size", OFFSET(lmsize_y), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS },
  292. { "ly", "set luma matrix vertical size", OFFSET(lmsize_y), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS },
  293. { "luma_amount", "set luma effect strength", OFFSET(lamount), AV_OPT_TYPE_FLOAT, { .dbl = 1 }, -2, 5, FLAGS },
  294. { "la", "set luma effect strength", OFFSET(lamount), AV_OPT_TYPE_FLOAT, { .dbl = 1 }, -2, 5, FLAGS },
  295. { "chroma_msize_x", "set chroma matrix horizontal size", OFFSET(cmsize_x), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS },
  296. { "cx", "set chroma matrix horizontal size", OFFSET(cmsize_x), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS },
  297. { "chroma_msize_y", "set chroma matrix vertical size", OFFSET(cmsize_y), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS },
  298. { "cy", "set chroma matrix vertical size", OFFSET(cmsize_y), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS },
  299. { "chroma_amount", "set chroma effect strength", OFFSET(camount), AV_OPT_TYPE_FLOAT, { .dbl = 0 }, -2, 5, FLAGS },
  300. { "ca", "set chroma effect strength", OFFSET(camount), AV_OPT_TYPE_FLOAT, { .dbl = 0 }, -2, 5, FLAGS },
  301. { "opencl", "ignored", OFFSET(opencl), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
  302. { NULL }
  303. };
  304. AVFILTER_DEFINE_CLASS(unsharp);
  305. static const AVFilterPad avfilter_vf_unsharp_inputs[] = {
  306. {
  307. .name = "default",
  308. .type = AVMEDIA_TYPE_VIDEO,
  309. .filter_frame = filter_frame,
  310. .config_props = config_input,
  311. },
  312. { NULL }
  313. };
  314. static const AVFilterPad avfilter_vf_unsharp_outputs[] = {
  315. {
  316. .name = "default",
  317. .type = AVMEDIA_TYPE_VIDEO,
  318. },
  319. { NULL }
  320. };
  321. AVFilter ff_vf_unsharp = {
  322. .name = "unsharp",
  323. .description = NULL_IF_CONFIG_SMALL("Sharpen or blur the input video."),
  324. .priv_size = sizeof(UnsharpContext),
  325. .priv_class = &unsharp_class,
  326. .init = init,
  327. .uninit = uninit,
  328. .query_formats = query_formats,
  329. .inputs = avfilter_vf_unsharp_inputs,
  330. .outputs = avfilter_vf_unsharp_outputs,
  331. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
  332. };