vf_convolution.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /*
  2. * Copyright (c) 2012-2013 Oka Motofumi (chikuzen.mo at gmail dot com)
  3. * Copyright (c) 2015 Paul B Mahol
  4. *
  5. * This file is part of FFmpeg.
  6. *
  7. * FFmpeg is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (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 GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with FFmpeg; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. #include "libavutil/avstring.h"
  22. #include "libavutil/imgutils.h"
  23. #include "libavutil/opt.h"
  24. #include "libavutil/pixdesc.h"
  25. #include "avfilter.h"
  26. #include "formats.h"
  27. #include "internal.h"
  28. #include "video.h"
  29. typedef struct ConvolutionContext {
  30. const AVClass *class;
  31. char *matrix_str[4];
  32. float rdiv[4];
  33. float bias[4];
  34. int bstride;
  35. uint8_t *buffer;
  36. int nb_planes;
  37. int planewidth[4];
  38. int planeheight[4];
  39. int matrix[4][25];
  40. int matrix_length[4];
  41. int copy[4];
  42. void (*filter[4])(struct ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane);
  43. } ConvolutionContext;
  44. #define OFFSET(x) offsetof(ConvolutionContext, x)
  45. #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
  46. static const AVOption convolution_options[] = {
  47. { "0m", "set matrix for 1st plane", OFFSET(matrix_str[0]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS },
  48. { "1m", "set matrix for 2nd plane", OFFSET(matrix_str[1]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS },
  49. { "2m", "set matrix for 3rd plane", OFFSET(matrix_str[2]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS },
  50. { "3m", "set matrix for 4th plane", OFFSET(matrix_str[3]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS },
  51. { "0rdiv", "set rdiv for 1st plane", OFFSET(rdiv[0]), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, INT_MAX, FLAGS},
  52. { "1rdiv", "set rdiv for 2nd plane", OFFSET(rdiv[1]), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, INT_MAX, FLAGS},
  53. { "2rdiv", "set rdiv for 3rd plane", OFFSET(rdiv[2]), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, INT_MAX, FLAGS},
  54. { "3rdiv", "set rdiv for 4th plane", OFFSET(rdiv[3]), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, INT_MAX, FLAGS},
  55. { "0bias", "set bias for 1st plane", OFFSET(bias[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
  56. { "1bias", "set bias for 2nd plane", OFFSET(bias[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
  57. { "2bias", "set bias for 3rd plane", OFFSET(bias[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
  58. { "3bias", "set bias for 4th plane", OFFSET(bias[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
  59. { NULL }
  60. };
  61. AVFILTER_DEFINE_CLASS(convolution);
  62. static const int same3x3[9] = {0, 0, 0,
  63. 0, 1, 0,
  64. 0, 0, 0};
  65. static const int same5x5[25] = {0, 0, 0, 0, 0,
  66. 0, 0, 0, 0, 0,
  67. 0, 0, 1, 0, 0,
  68. 0, 0, 0, 0, 0,
  69. 0, 0, 0, 0, 0};
  70. static int query_formats(AVFilterContext *ctx)
  71. {
  72. static const enum AVPixelFormat pix_fmts[] = {
  73. AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P,
  74. AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P,
  75. AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
  76. AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P,
  77. AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV440P,
  78. AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
  79. AV_PIX_FMT_GRAY8,
  80. AV_PIX_FMT_NONE
  81. };
  82. return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
  83. }
  84. static int config_input(AVFilterLink *inlink)
  85. {
  86. ConvolutionContext *s = inlink->dst->priv;
  87. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
  88. int ret;
  89. if ((ret = av_image_fill_linesizes(s->planewidth, inlink->format, inlink->w)) < 0)
  90. return ret;
  91. s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
  92. s->planeheight[0] = s->planeheight[3] = inlink->h;
  93. s->nb_planes = av_pix_fmt_count_planes(inlink->format);
  94. s->bstride = s->planewidth[0] + 32;
  95. s->buffer = av_malloc(5 * s->bstride);
  96. if (!s->buffer)
  97. return AVERROR(ENOMEM);
  98. return 0;
  99. }
  100. static inline void line_copy8(uint8_t *line, const uint8_t *srcp, int width, int mergin)
  101. {
  102. int i;
  103. memcpy(line, srcp, width);
  104. for (i = mergin; i > 0; i--) {
  105. line[-i] = line[i];
  106. line[width - 1 + i] = line[width - 1 - i];
  107. }
  108. }
  109. static void filter_3x3(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
  110. {
  111. const uint8_t *src = in->data[plane];
  112. uint8_t *dst = out->data[plane];
  113. const int stride = in->linesize[plane];
  114. const int bstride = s->bstride;
  115. const int height = s->planeheight[plane];
  116. const int width = s->planewidth[plane];
  117. uint8_t *p0 = s->buffer + 16;
  118. uint8_t *p1 = p0 + bstride;
  119. uint8_t *p2 = p1 + bstride;
  120. uint8_t *orig = p0, *end = p2;
  121. const int *matrix = s->matrix[plane];
  122. const float rdiv = s->rdiv[plane];
  123. const float bias = s->bias[plane];
  124. int y, x;
  125. line_copy8(p0, src + stride, width, 1);
  126. line_copy8(p1, src, width, 1);
  127. for (y = 0; y < height; y++) {
  128. src += stride * (y < height - 1 ? 1 : -1);
  129. line_copy8(p2, src, width, 1);
  130. for (x = 0; x < width; x++) {
  131. int sum = p0[x - 1] * matrix[0] +
  132. p0[x] * matrix[1] +
  133. p0[x + 1] * matrix[2] +
  134. p1[x - 1] * matrix[3] +
  135. p1[x] * matrix[4] +
  136. p1[x + 1] * matrix[5] +
  137. p2[x - 1] * matrix[6] +
  138. p2[x] * matrix[7] +
  139. p2[x + 1] * matrix[8];
  140. sum = (int)(sum * rdiv + bias + 0.5f);
  141. dst[x] = av_clip_uint8(sum);
  142. }
  143. p0 = p1;
  144. p1 = p2;
  145. p2 = (p2 == end) ? orig: p2 + bstride;
  146. dst += out->linesize[plane];
  147. }
  148. }
  149. static void filter_5x5(ConvolutionContext *s, AVFrame *in, AVFrame *out, int plane)
  150. {
  151. const uint8_t *src = in->data[plane];
  152. uint8_t *dst = out->data[plane];
  153. const int stride = in->linesize[plane];
  154. const int bstride = s->bstride;
  155. const int height = s->planeheight[plane];
  156. const int width = s->planewidth[plane];
  157. uint8_t *p0 = s->buffer + 16;
  158. uint8_t *p1 = p0 + bstride;
  159. uint8_t *p2 = p1 + bstride;
  160. uint8_t *p3 = p2 + bstride;
  161. uint8_t *p4 = p3 + bstride;
  162. uint8_t *orig = p0, *end = p4;
  163. const int *matrix = s->matrix[plane];
  164. float rdiv = s->rdiv[plane];
  165. float bias = s->bias[plane];
  166. int y, x, i;
  167. line_copy8(p0, src + 2 * stride, width, 2);
  168. line_copy8(p1, src + stride, width, 2);
  169. line_copy8(p2, src, width, 2);
  170. src += stride;
  171. line_copy8(p3, src, width, 2);
  172. for (y = 0; y < height; y++) {
  173. uint8_t *array[] = {
  174. p0 - 2, p0 - 1, p0, p0 + 1, p0 + 2,
  175. p1 - 2, p1 - 1, p1, p1 + 1, p1 + 2,
  176. p2 - 2, p2 - 1, p2, p2 + 1, p2 + 2,
  177. p3 - 2, p3 - 1, p3, p3 + 1, p3 + 2,
  178. p4 - 2, p4 - 1, p4, p4 + 1, p4 + 2
  179. };
  180. src += stride * (y < height - 2 ? 1 : -1);
  181. line_copy8(p4, src, width, 2);
  182. for (x = 0; x < width; x++) {
  183. int sum = 0;
  184. for (i = 0; i < 25; i++) {
  185. sum += *(array[i] + x) * matrix[i];
  186. }
  187. sum = (int)(sum * rdiv + bias + 0.5f);
  188. dst[x] = av_clip_uint8(sum);
  189. }
  190. p0 = p1;
  191. p1 = p2;
  192. p2 = p3;
  193. p3 = p4;
  194. p4 = (p4 == end) ? orig: p4 + bstride;
  195. dst += out->linesize[plane];
  196. }
  197. }
  198. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  199. {
  200. ConvolutionContext *s = inlink->dst->priv;
  201. AVFilterLink *outlink = inlink->dst->outputs[0];
  202. AVFrame *out;
  203. int plane;
  204. out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  205. if (!out) {
  206. av_frame_free(&in);
  207. return AVERROR(ENOMEM);
  208. }
  209. av_frame_copy_props(out, in);
  210. for (plane = 0; plane < s->nb_planes; plane++) {
  211. if (s->copy[plane]) {
  212. av_image_copy_plane(out->data[plane], out->linesize[plane],
  213. in->data[plane], in->linesize[plane],
  214. s->planewidth[plane],
  215. s->planeheight[plane]);
  216. continue;
  217. }
  218. s->filter[plane](s, in, out, plane);
  219. }
  220. av_frame_free(&in);
  221. return ff_filter_frame(outlink, out);
  222. }
  223. static av_cold int init(AVFilterContext *ctx)
  224. {
  225. ConvolutionContext *s = ctx->priv;
  226. int i;
  227. for (i = 0; i < 4; i++) {
  228. int *matrix = (int *)s->matrix[i];
  229. char *p, *arg, *saveptr = NULL;
  230. p = s->matrix_str[i];
  231. while (s->matrix_length[i] < 25) {
  232. if (!(arg = av_strtok(p, " ", &saveptr)))
  233. break;
  234. p = NULL;
  235. sscanf(arg, "%d", &matrix[s->matrix_length[i]]);
  236. s->matrix_length[i]++;
  237. }
  238. if (s->matrix_length[i] == 9) {
  239. if (!memcmp(matrix, same3x3, sizeof(same3x3)))
  240. s->copy[i] = 1;
  241. else
  242. s->filter[i] = filter_3x3;
  243. } else if (s->matrix_length[i] == 25) {
  244. if (!memcmp(matrix, same5x5, sizeof(same5x5)))
  245. s->copy[i] = 1;
  246. else
  247. s->filter[i] = filter_5x5;
  248. } else {
  249. return AVERROR(EINVAL);
  250. }
  251. }
  252. return 0;
  253. }
  254. static av_cold void uninit(AVFilterContext *ctx)
  255. {
  256. ConvolutionContext *s = ctx->priv;
  257. av_freep(&s->buffer);
  258. }
  259. static const AVFilterPad convolution_inputs[] = {
  260. {
  261. .name = "default",
  262. .type = AVMEDIA_TYPE_VIDEO,
  263. .config_props = config_input,
  264. .filter_frame = filter_frame,
  265. },
  266. { NULL }
  267. };
  268. static const AVFilterPad convolution_outputs[] = {
  269. {
  270. .name = "default",
  271. .type = AVMEDIA_TYPE_VIDEO,
  272. },
  273. { NULL }
  274. };
  275. AVFilter ff_vf_convolution = {
  276. .name = "convolution",
  277. .description = NULL_IF_CONFIG_SMALL("Apply convolution filter."),
  278. .priv_size = sizeof(ConvolutionContext),
  279. .priv_class = &convolution_class,
  280. .init = init,
  281. .uninit = uninit,
  282. .query_formats = query_formats,
  283. .inputs = convolution_inputs,
  284. .outputs = convolution_outputs,
  285. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
  286. };