vf_neighbor.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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/imgutils.h"
  22. #include "libavutil/pixdesc.h"
  23. #include "libavutil/opt.h"
  24. #include "avfilter.h"
  25. #include "formats.h"
  26. #include "internal.h"
  27. #include "video.h"
  28. typedef struct NContext {
  29. const AVClass *class;
  30. int planeheight[4];
  31. int planewidth[4];
  32. int nb_planes;
  33. int threshold[4];
  34. int coordinates;
  35. uint8_t *buffer;
  36. void (*filter)(uint8_t *dst, const uint8_t *p1, int width,
  37. int threshold, const uint8_t *coordinates[], int coord);
  38. } NContext;
  39. static int query_formats(AVFilterContext *ctx)
  40. {
  41. static const enum AVPixelFormat pix_fmts[] = {
  42. AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P,
  43. AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ422P,AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P,
  44. AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
  45. AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE
  46. };
  47. return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
  48. }
  49. static av_cold void uninit(AVFilterContext *ctx)
  50. {
  51. NContext *s = ctx->priv;
  52. av_freep(&s->buffer);
  53. }
  54. static inline void line_copy8(uint8_t *line, const uint8_t *srcp, int width, int mergin)
  55. {
  56. int i;
  57. memcpy(line, srcp, width);
  58. for (i = mergin; i > 0; i--) {
  59. line[-i] = line[i];
  60. line[width - 1 + i] = line[width - 1 - i];
  61. }
  62. }
  63. static void erosion(uint8_t *dst, const uint8_t *p1, int width,
  64. int threshold, const uint8_t *coordinates[], int coord)
  65. {
  66. int x, i;
  67. for (x = 0; x < width; x++) {
  68. int min = p1[x];
  69. int limit = FFMAX(min - threshold, 0);
  70. for (i = 0; i < 8; i++) {
  71. if (coord & (1 << i)) {
  72. min = FFMIN(min, *(coordinates[i] + x));
  73. }
  74. min = FFMAX(min, limit);
  75. }
  76. dst[x] = min;
  77. }
  78. }
  79. static void dilation(uint8_t *dst, const uint8_t *p1, int width,
  80. int threshold, const uint8_t *coordinates[], int coord)
  81. {
  82. int x, i;
  83. for (x = 0; x < width; x++) {
  84. int max = p1[x];
  85. int limit = FFMIN(max + threshold, 255);
  86. for (i = 0; i < 8; i++) {
  87. if (coord & (1 << i)) {
  88. max = FFMAX(max, *(coordinates[i] + x));
  89. }
  90. max = FFMIN(max, limit);
  91. }
  92. dst[x] = max;
  93. }
  94. }
  95. static void deflate(uint8_t *dst, const uint8_t *p1, int width,
  96. int threshold, const uint8_t *coordinates[], int coord)
  97. {
  98. int x, i;
  99. for (x = 0; x < width; x++) {
  100. int sum = 0;
  101. int limit = FFMAX(p1[x] - threshold, 0);
  102. for (i = 0; i < 8; sum += *(coordinates[i++] + x));
  103. dst[x] = FFMAX(FFMIN(sum / 8, p1[x]), limit);
  104. }
  105. }
  106. static void inflate(uint8_t *dst, const uint8_t *p1, int width,
  107. int threshold, const uint8_t *coordinates[], int coord)
  108. {
  109. int x, i;
  110. for (x = 0; x < width; x++) {
  111. int sum = 0;
  112. int limit = FFMIN(p1[x] + threshold, 255);
  113. for (i = 0; i < 8; sum += *(coordinates[i++] + x));
  114. dst[x] = FFMIN(FFMAX(sum / 8, p1[x]), limit);
  115. }
  116. }
  117. static int config_input(AVFilterLink *inlink)
  118. {
  119. AVFilterContext *ctx = inlink->dst;
  120. NContext *s = ctx->priv;
  121. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
  122. int ret;
  123. if ((ret = av_image_fill_linesizes(s->planewidth, inlink->format, inlink->w)) < 0)
  124. return ret;
  125. s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
  126. s->planeheight[0] = s->planeheight[3] = inlink->h;
  127. s->nb_planes = av_pix_fmt_count_planes(inlink->format);
  128. s->buffer = av_malloc(3 * (s->planewidth[0] + 32));
  129. if (!s->buffer)
  130. return AVERROR(ENOMEM);
  131. if (!strcmp(ctx->filter->name, "erosion"))
  132. s->filter = erosion;
  133. else if (!strcmp(ctx->filter->name, "dilation"))
  134. s->filter = dilation;
  135. else if (!strcmp(ctx->filter->name, "deflate"))
  136. s->filter = deflate;
  137. else if (!strcmp(ctx->filter->name, "inflate"))
  138. s->filter = inflate;
  139. return 0;
  140. }
  141. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  142. {
  143. AVFilterContext *ctx = inlink->dst;
  144. AVFilterLink *outlink = ctx->outputs[0];
  145. NContext *s = ctx->priv;
  146. AVFrame *out;
  147. int plane, y;
  148. out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  149. if (!out) {
  150. av_frame_free(&in);
  151. return AVERROR(ENOMEM);
  152. }
  153. av_frame_copy_props(out, in);
  154. for (plane = 0; plane < s->nb_planes; plane++) {
  155. const int threshold = s->threshold[plane];
  156. if (threshold) {
  157. const uint8_t *src = in->data[plane];
  158. uint8_t *dst = out->data[plane];
  159. int stride = in->linesize[plane];
  160. int height = s->planeheight[plane];
  161. int width = s->planewidth[plane];
  162. uint8_t *p0 = s->buffer + 16;
  163. uint8_t *p1 = p0 + s->planewidth[0];
  164. uint8_t *p2 = p1 + s->planewidth[0];
  165. uint8_t *orig = p0, *end = p2;
  166. line_copy8(p0, src + stride, width, 1);
  167. line_copy8(p1, src, width, 1);
  168. for (y = 0; y < height; y++) {
  169. const uint8_t *coordinates[] = { p0 - 1, p0, p0 + 1,
  170. p1 - 1, p1 + 1,
  171. p2 - 1, p2, p2 + 1};
  172. src += stride * (y < height - 1 ? 1 : -1);
  173. line_copy8(p2, src, width, 1);
  174. s->filter(dst, p1, width, threshold, coordinates, s->coordinates);
  175. p0 = p1;
  176. p1 = p2;
  177. p2 = (p2 == end) ? orig: p2 + s->planewidth[0];
  178. dst += out->linesize[plane];
  179. }
  180. } else {
  181. av_image_copy_plane(out->data[plane], out->linesize[plane],
  182. in->data[plane], in->linesize[plane],
  183. s->planewidth[plane], s->planeheight[plane]);
  184. }
  185. }
  186. av_frame_free(&in);
  187. return ff_filter_frame(outlink, out);
  188. }
  189. static const AVFilterPad neighbor_inputs[] = {
  190. {
  191. .name = "default",
  192. .type = AVMEDIA_TYPE_VIDEO,
  193. .filter_frame = filter_frame,
  194. .config_props = config_input,
  195. },
  196. { NULL }
  197. };
  198. static const AVFilterPad neighbor_outputs[] = {
  199. {
  200. .name = "default",
  201. .type = AVMEDIA_TYPE_VIDEO,
  202. },
  203. { NULL }
  204. };
  205. #define OFFSET(x) offsetof(NContext, x)
  206. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  207. #define DEFINE_NEIGHBOR_FILTER(name_, description_) \
  208. AVFILTER_DEFINE_CLASS(name_); \
  209. \
  210. AVFilter ff_vf_##name_ = { \
  211. .name = #name_, \
  212. .description = NULL_IF_CONFIG_SMALL(description_), \
  213. .priv_size = sizeof(NContext), \
  214. .priv_class = &name_##_class, \
  215. .uninit = uninit, \
  216. .query_formats = query_formats, \
  217. .inputs = neighbor_inputs, \
  218. .outputs = neighbor_outputs, \
  219. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, \
  220. }
  221. #if CONFIG_EROSION_FILTER
  222. static const AVOption erosion_options[] = {
  223. { "threshold0", "set threshold for 1st plane", OFFSET(threshold[0]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  224. { "threshold1", "set threshold for 2nd plane", OFFSET(threshold[1]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  225. { "threshold2", "set threshold for 3rd plane", OFFSET(threshold[2]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  226. { "threshold3", "set threshold for 4th plane", OFFSET(threshold[3]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  227. { "coordinates", "set coordinates", OFFSET(coordinates), AV_OPT_TYPE_INT, {.i64=255}, 0, 255, FLAGS },
  228. { NULL }
  229. };
  230. DEFINE_NEIGHBOR_FILTER(erosion, "Apply erosion effect.");
  231. #endif /* CONFIG_EROSION_FILTER */
  232. #if CONFIG_DILATION_FILTER
  233. static const AVOption dilation_options[] = {
  234. { "threshold0", "set threshold for 1st plane", OFFSET(threshold[0]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  235. { "threshold1", "set threshold for 2nd plane", OFFSET(threshold[1]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  236. { "threshold2", "set threshold for 3rd plane", OFFSET(threshold[2]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  237. { "threshold3", "set threshold for 4th plane", OFFSET(threshold[3]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  238. { "coordinates", "set coordinates", OFFSET(coordinates), AV_OPT_TYPE_INT, {.i64=255}, 0, 255, FLAGS },
  239. { NULL }
  240. };
  241. DEFINE_NEIGHBOR_FILTER(dilation, "Apply dilation effect.");
  242. #endif /* CONFIG_DILATION_FILTER */
  243. #if CONFIG_DEFLATE_FILTER
  244. static const AVOption deflate_options[] = {
  245. { "threshold0", "set threshold for 1st plane", OFFSET(threshold[0]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  246. { "threshold1", "set threshold for 2nd plane", OFFSET(threshold[1]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  247. { "threshold2", "set threshold for 3rd plane", OFFSET(threshold[2]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  248. { "threshold3", "set threshold for 4th plane", OFFSET(threshold[3]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  249. { NULL }
  250. };
  251. DEFINE_NEIGHBOR_FILTER(deflate, "Apply deflate effect.");
  252. #endif /* CONFIG_DEFLATE_FILTER */
  253. #if CONFIG_INFLATE_FILTER
  254. static const AVOption inflate_options[] = {
  255. { "threshold0", "set threshold for 1st plane", OFFSET(threshold[0]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  256. { "threshold1", "set threshold for 2nd plane", OFFSET(threshold[1]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  257. { "threshold2", "set threshold for 3rd plane", OFFSET(threshold[2]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  258. { "threshold3", "set threshold for 4th plane", OFFSET(threshold[3]), AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
  259. { NULL }
  260. };
  261. DEFINE_NEIGHBOR_FILTER(inflate, "Apply inflate effect.");
  262. #endif /* CONFIG_INFLATE_FILTER */