vf_convolution.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  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/intreadwrite.h"
  24. #include "libavutil/opt.h"
  25. #include "libavutil/pixdesc.h"
  26. #include "avfilter.h"
  27. #include "formats.h"
  28. #include "internal.h"
  29. #include "video.h"
  30. enum MatrixMode {
  31. MATRIX_SQUARE,
  32. MATRIX_ROW,
  33. MATRIX_COLUMN,
  34. MATRIX_NBMODES,
  35. };
  36. typedef struct ConvolutionContext {
  37. const AVClass *class;
  38. char *matrix_str[4];
  39. float rdiv[4];
  40. float bias[4];
  41. int mode[4];
  42. float scale;
  43. float delta;
  44. int planes;
  45. int size[4];
  46. int depth;
  47. int max;
  48. int bpc;
  49. int nb_planes;
  50. int nb_threads;
  51. int planewidth[4];
  52. int planeheight[4];
  53. int matrix[4][49];
  54. int matrix_length[4];
  55. int copy[4];
  56. void (*setup[4])(int radius, const uint8_t *c[], const uint8_t *src, int stride,
  57. int x, int width, int y, int height, int bpc);
  58. void (*filter[4])(uint8_t *dst, int width,
  59. float rdiv, float bias, const int *const matrix,
  60. const uint8_t *c[], int peak, int radius,
  61. int dstride, int stride);
  62. } ConvolutionContext;
  63. #define OFFSET(x) offsetof(ConvolutionContext, x)
  64. #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
  65. static const AVOption convolution_options[] = {
  66. { "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 },
  67. { "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 },
  68. { "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 },
  69. { "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 },
  70. { "0rdiv", "set rdiv for 1st plane", OFFSET(rdiv[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
  71. { "1rdiv", "set rdiv for 2nd plane", OFFSET(rdiv[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
  72. { "2rdiv", "set rdiv for 3rd plane", OFFSET(rdiv[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
  73. { "3rdiv", "set rdiv for 4th plane", OFFSET(rdiv[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
  74. { "0bias", "set bias for 1st plane", OFFSET(bias[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
  75. { "1bias", "set bias for 2nd plane", OFFSET(bias[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
  76. { "2bias", "set bias for 3rd plane", OFFSET(bias[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
  77. { "3bias", "set bias for 4th plane", OFFSET(bias[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
  78. { "0mode", "set matrix mode for 1st plane", OFFSET(mode[0]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
  79. { "1mode", "set matrix mode for 2nd plane", OFFSET(mode[1]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
  80. { "2mode", "set matrix mode for 3rd plane", OFFSET(mode[2]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
  81. { "3mode", "set matrix mode for 4th plane", OFFSET(mode[3]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
  82. { "square", "square matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_SQUARE}, 0, 0, FLAGS, "mode" },
  83. { "row", "single row matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_ROW} , 0, 0, FLAGS, "mode" },
  84. { "column", "single column matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_COLUMN}, 0, 0, FLAGS, "mode" },
  85. { NULL }
  86. };
  87. AVFILTER_DEFINE_CLASS(convolution);
  88. static const int same3x3[9] = {0, 0, 0,
  89. 0, 1, 0,
  90. 0, 0, 0};
  91. static const int same5x5[25] = {0, 0, 0, 0, 0,
  92. 0, 0, 0, 0, 0,
  93. 0, 0, 1, 0, 0,
  94. 0, 0, 0, 0, 0,
  95. 0, 0, 0, 0, 0};
  96. static const int same7x7[49] = {0, 0, 0, 0, 0, 0, 0,
  97. 0, 0, 0, 0, 0, 0, 0,
  98. 0, 0, 0, 0, 0, 0, 0,
  99. 0, 0, 0, 1, 0, 0, 0,
  100. 0, 0, 0, 0, 0, 0, 0,
  101. 0, 0, 0, 0, 0, 0, 0,
  102. 0, 0, 0, 0, 0, 0, 0};
  103. static int query_formats(AVFilterContext *ctx)
  104. {
  105. static const enum AVPixelFormat pix_fmts[] = {
  106. AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
  107. AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
  108. AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
  109. AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
  110. AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
  111. AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
  112. AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
  113. AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
  114. AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
  115. AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
  116. AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
  117. AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
  118. AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
  119. AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
  120. AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
  121. AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
  122. AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
  123. AV_PIX_FMT_NONE
  124. };
  125. return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
  126. }
  127. typedef struct ThreadData {
  128. AVFrame *in, *out;
  129. } ThreadData;
  130. static void filter16_prewitt(uint8_t *dstp, int width,
  131. float scale, float delta, const int *const matrix,
  132. const uint8_t *c[], int peak, int radius,
  133. int dstride, int stride)
  134. {
  135. uint16_t *dst = (uint16_t *)dstp;
  136. int x;
  137. for (x = 0; x < width; x++) {
  138. int suma = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[1][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) * -1 +
  139. AV_RN16A(&c[6][2 * x]) * 1 + AV_RN16A(&c[7][2 * x]) * 1 + AV_RN16A(&c[8][2 * x]) * 1;
  140. int sumb = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) * 1 + AV_RN16A(&c[3][2 * x]) * -1 +
  141. AV_RN16A(&c[5][2 * x]) * 1 + AV_RN16A(&c[6][2 * x]) * -1 + AV_RN16A(&c[8][2 * x]) * 1;
  142. dst[x] = av_clip(sqrtf(suma*suma + sumb*sumb) * scale + delta, 0, peak);
  143. }
  144. }
  145. static void filter16_roberts(uint8_t *dstp, int width,
  146. float scale, float delta, const int *const matrix,
  147. const uint8_t *c[], int peak, int radius,
  148. int dstride, int stride)
  149. {
  150. uint16_t *dst = (uint16_t *)dstp;
  151. int x;
  152. for (x = 0; x < width; x++) {
  153. int suma = AV_RN16A(&c[0][2 * x]) * 1 + AV_RN16A(&c[1][2 * x]) * -1;
  154. int sumb = AV_RN16A(&c[4][2 * x]) * 1 + AV_RN16A(&c[3][2 * x]) * -1;
  155. dst[x] = av_clip(sqrtf(suma*suma + sumb*sumb) * scale + delta, 0, peak);
  156. }
  157. }
  158. static void filter16_sobel(uint8_t *dstp, int width,
  159. float scale, float delta, const int *const matrix,
  160. const uint8_t *c[], int peak, int radius,
  161. int dstride, int stride)
  162. {
  163. uint16_t *dst = (uint16_t *)dstp;
  164. int x;
  165. for (x = 0; x < width; x++) {
  166. int suma = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[1][2 * x]) * -2 + AV_RN16A(&c[2][2 * x]) * -1 +
  167. AV_RN16A(&c[6][2 * x]) * 1 + AV_RN16A(&c[7][2 * x]) * 2 + AV_RN16A(&c[8][2 * x]) * 1;
  168. int sumb = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) * 1 + AV_RN16A(&c[3][2 * x]) * -2 +
  169. AV_RN16A(&c[5][2 * x]) * 2 + AV_RN16A(&c[6][2 * x]) * -1 + AV_RN16A(&c[8][2 * x]) * 1;
  170. dst[x] = av_clip(sqrtf(suma*suma + sumb*sumb) * scale + delta, 0, peak);
  171. }
  172. }
  173. static void filter_prewitt(uint8_t *dst, int width,
  174. float scale, float delta, const int *const matrix,
  175. const uint8_t *c[], int peak, int radius,
  176. int dstride, int stride)
  177. {
  178. const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2];
  179. const uint8_t *c3 = c[3], *c5 = c[5];
  180. const uint8_t *c6 = c[6], *c7 = c[7], *c8 = c[8];
  181. int x;
  182. for (x = 0; x < width; x++) {
  183. int suma = c0[x] * -1 + c1[x] * -1 + c2[x] * -1 +
  184. c6[x] * 1 + c7[x] * 1 + c8[x] * 1;
  185. int sumb = c0[x] * -1 + c2[x] * 1 + c3[x] * -1 +
  186. c5[x] * 1 + c6[x] * -1 + c8[x] * 1;
  187. dst[x] = av_clip_uint8(sqrtf(suma*suma + sumb*sumb) * scale + delta);
  188. }
  189. }
  190. static void filter_roberts(uint8_t *dst, int width,
  191. float scale, float delta, const int *const matrix,
  192. const uint8_t *c[], int peak, int radius,
  193. int dstride, int stride)
  194. {
  195. int x;
  196. for (x = 0; x < width; x++) {
  197. int suma = c[0][x] * 1 + c[1][x] * -1;
  198. int sumb = c[4][x] * 1 + c[3][x] * -1;
  199. dst[x] = av_clip_uint8(sqrtf(suma*suma + sumb*sumb) * scale + delta);
  200. }
  201. }
  202. static void filter_sobel(uint8_t *dst, int width,
  203. float scale, float delta, const int *const matrix,
  204. const uint8_t *c[], int peak, int radius,
  205. int dstride, int stride)
  206. {
  207. const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2];
  208. const uint8_t *c3 = c[3], *c5 = c[5];
  209. const uint8_t *c6 = c[6], *c7 = c[7], *c8 = c[8];
  210. int x;
  211. for (x = 0; x < width; x++) {
  212. int suma = c0[x] * -1 + c1[x] * -2 + c2[x] * -1 +
  213. c6[x] * 1 + c7[x] * 2 + c8[x] * 1;
  214. int sumb = c0[x] * -1 + c2[x] * 1 + c3[x] * -2 +
  215. c5[x] * 2 + c6[x] * -1 + c8[x] * 1;
  216. dst[x] = av_clip_uint8(sqrtf(suma*suma + sumb*sumb) * scale + delta);
  217. }
  218. }
  219. static void filter16_3x3(uint8_t *dstp, int width,
  220. float rdiv, float bias, const int *const matrix,
  221. const uint8_t *c[], int peak, int radius,
  222. int dstride, int stride)
  223. {
  224. uint16_t *dst = (uint16_t *)dstp;
  225. int x;
  226. for (x = 0; x < width; x++) {
  227. int sum = AV_RN16A(&c[0][2 * x]) * matrix[0] +
  228. AV_RN16A(&c[1][2 * x]) * matrix[1] +
  229. AV_RN16A(&c[2][2 * x]) * matrix[2] +
  230. AV_RN16A(&c[3][2 * x]) * matrix[3] +
  231. AV_RN16A(&c[4][2 * x]) * matrix[4] +
  232. AV_RN16A(&c[5][2 * x]) * matrix[5] +
  233. AV_RN16A(&c[6][2 * x]) * matrix[6] +
  234. AV_RN16A(&c[7][2 * x]) * matrix[7] +
  235. AV_RN16A(&c[8][2 * x]) * matrix[8];
  236. sum = (int)(sum * rdiv + bias + 0.5f);
  237. dst[x] = av_clip(sum, 0, peak);
  238. }
  239. }
  240. static void filter16_5x5(uint8_t *dstp, int width,
  241. float rdiv, float bias, const int *const matrix,
  242. const uint8_t *c[], int peak, int radius,
  243. int dstride, int stride)
  244. {
  245. uint16_t *dst = (uint16_t *)dstp;
  246. int x;
  247. for (x = 0; x < width; x++) {
  248. int i, sum = 0;
  249. for (i = 0; i < 25; i++)
  250. sum += AV_RN16A(&c[i][2 * x]) * matrix[i];
  251. sum = (int)(sum * rdiv + bias + 0.5f);
  252. dst[x] = av_clip(sum, 0, peak);
  253. }
  254. }
  255. static void filter16_7x7(uint8_t *dstp, int width,
  256. float rdiv, float bias, const int *const matrix,
  257. const uint8_t *c[], int peak, int radius,
  258. int dstride, int stride)
  259. {
  260. uint16_t *dst = (uint16_t *)dstp;
  261. int x;
  262. for (x = 0; x < width; x++) {
  263. int i, sum = 0;
  264. for (i = 0; i < 49; i++)
  265. sum += AV_RN16A(&c[i][2 * x]) * matrix[i];
  266. sum = (int)(sum * rdiv + bias + 0.5f);
  267. dst[x] = av_clip(sum, 0, peak);
  268. }
  269. }
  270. static void filter16_row(uint8_t *dstp, int width,
  271. float rdiv, float bias, const int *const matrix,
  272. const uint8_t *c[], int peak, int radius,
  273. int dstride, int stride)
  274. {
  275. uint16_t *dst = (uint16_t *)dstp;
  276. int x;
  277. for (x = 0; x < width; x++) {
  278. int i, sum = 0;
  279. for (i = 0; i < 2 * radius + 1; i++)
  280. sum += AV_RN16A(&c[i][2 * x]) * matrix[i];
  281. sum = (int)(sum * rdiv + bias + 0.5f);
  282. dst[x] = av_clip(sum, 0, peak);
  283. }
  284. }
  285. static void filter16_column(uint8_t *dstp, int height,
  286. float rdiv, float bias, const int *const matrix,
  287. const uint8_t *c[], int peak, int radius,
  288. int dstride, int stride)
  289. {
  290. uint16_t *dst = (uint16_t *)dstp;
  291. int y;
  292. for (y = 0; y < height; y++) {
  293. int i, sum = 0;
  294. for (i = 0; i < 2 * radius + 1; i++)
  295. sum += AV_RN16A(&c[i][0 + y * stride]) * matrix[i];
  296. sum = (int)(sum * rdiv + bias + 0.5f);
  297. dst[0] = av_clip(sum, 0, peak);
  298. dst += dstride / 2;
  299. }
  300. }
  301. static void filter_7x7(uint8_t *dst, int width,
  302. float rdiv, float bias, const int *const matrix,
  303. const uint8_t *c[], int peak, int radius,
  304. int dstride, int stride)
  305. {
  306. int x;
  307. for (x = 0; x < width; x++) {
  308. int i, sum = 0;
  309. for (i = 0; i < 49; i++)
  310. sum += c[i][x] * matrix[i];
  311. sum = (int)(sum * rdiv + bias + 0.5f);
  312. dst[x] = av_clip_uint8(sum);
  313. }
  314. }
  315. static void filter_5x5(uint8_t *dst, int width,
  316. float rdiv, float bias, const int *const matrix,
  317. const uint8_t *c[], int peak, int radius,
  318. int dstride, int stride)
  319. {
  320. int x;
  321. for (x = 0; x < width; x++) {
  322. int i, sum = 0;
  323. for (i = 0; i < 25; i++)
  324. sum += c[i][x] * matrix[i];
  325. sum = (int)(sum * rdiv + bias + 0.5f);
  326. dst[x] = av_clip_uint8(sum);
  327. }
  328. }
  329. static void filter_3x3(uint8_t *dst, int width,
  330. float rdiv, float bias, const int *const matrix,
  331. const uint8_t *c[], int peak, int radius,
  332. int dstride, int stride)
  333. {
  334. const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2];
  335. const uint8_t *c3 = c[3], *c4 = c[4], *c5 = c[5];
  336. const uint8_t *c6 = c[6], *c7 = c[7], *c8 = c[8];
  337. int x;
  338. for (x = 0; x < width; x++) {
  339. int sum = c0[x] * matrix[0] + c1[x] * matrix[1] + c2[x] * matrix[2] +
  340. c3[x] * matrix[3] + c4[x] * matrix[4] + c5[x] * matrix[5] +
  341. c6[x] * matrix[6] + c7[x] * matrix[7] + c8[x] * matrix[8];
  342. sum = (int)(sum * rdiv + bias + 0.5f);
  343. dst[x] = av_clip_uint8(sum);
  344. }
  345. }
  346. static void filter_row(uint8_t *dst, int width,
  347. float rdiv, float bias, const int *const matrix,
  348. const uint8_t *c[], int peak, int radius,
  349. int dstride, int stride)
  350. {
  351. int x;
  352. for (x = 0; x < width; x++) {
  353. int i, sum = 0;
  354. for (i = 0; i < 2 * radius + 1; i++)
  355. sum += c[i][x] * matrix[i];
  356. sum = (int)(sum * rdiv + bias + 0.5f);
  357. dst[x] = av_clip_uint8(sum);
  358. }
  359. }
  360. static void filter_column(uint8_t *dst, int height,
  361. float rdiv, float bias, const int *const matrix,
  362. const uint8_t *c[], int peak, int radius,
  363. int dstride, int stride)
  364. {
  365. int y;
  366. for (y = 0; y < height; y++) {
  367. int i, sum = 0;
  368. for (i = 0; i < 2 * radius + 1; i++)
  369. sum += c[i][0 + y * stride] * matrix[i];
  370. sum = (int)(sum * rdiv + bias + 0.5f);
  371. dst[0] = av_clip_uint8(sum);
  372. dst += dstride;
  373. }
  374. }
  375. static void setup_3x3(int radius, const uint8_t *c[], const uint8_t *src, int stride,
  376. int x, int w, int y, int h, int bpc)
  377. {
  378. int i;
  379. for (i = 0; i < 9; i++) {
  380. int xoff = FFABS(x + ((i % 3) - 1));
  381. int yoff = FFABS(y + (i / 3) - 1);
  382. xoff = xoff >= w ? 2 * w - 1 - xoff : xoff;
  383. yoff = yoff >= h ? 2 * h - 1 - yoff : yoff;
  384. c[i] = src + xoff * bpc + yoff * stride;
  385. }
  386. }
  387. static void setup_5x5(int radius, const uint8_t *c[], const uint8_t *src, int stride,
  388. int x, int w, int y, int h, int bpc)
  389. {
  390. int i;
  391. for (i = 0; i < 25; i++) {
  392. int xoff = FFABS(x + ((i % 5) - 2));
  393. int yoff = FFABS(y + (i / 5) - 2);
  394. xoff = xoff >= w ? 2 * w - 1 - xoff : xoff;
  395. yoff = yoff >= h ? 2 * h - 1 - yoff : yoff;
  396. c[i] = src + xoff * bpc + yoff * stride;
  397. }
  398. }
  399. static void setup_7x7(int radius, const uint8_t *c[], const uint8_t *src, int stride,
  400. int x, int w, int y, int h, int bpc)
  401. {
  402. int i;
  403. for (i = 0; i < 49; i++) {
  404. int xoff = FFABS(x + ((i % 7) - 3));
  405. int yoff = FFABS(y + (i / 7) - 3);
  406. xoff = xoff >= w ? 2 * w - 1 - xoff : xoff;
  407. yoff = yoff >= h ? 2 * h - 1 - yoff : yoff;
  408. c[i] = src + xoff * bpc + yoff * stride;
  409. }
  410. }
  411. static void setup_row(int radius, const uint8_t *c[], const uint8_t *src, int stride,
  412. int x, int w, int y, int h, int bpc)
  413. {
  414. int i;
  415. for (i = 0; i < radius * 2 + 1; i++) {
  416. int xoff = FFABS(x + i - radius);
  417. xoff = xoff >= w ? 2 * w - 1 - xoff : xoff;
  418. c[i] = src + xoff * bpc + y * stride;
  419. }
  420. }
  421. static void setup_column(int radius, const uint8_t *c[], const uint8_t *src, int stride,
  422. int x, int w, int y, int h, int bpc)
  423. {
  424. int i;
  425. for (i = 0; i < radius * 2 + 1; i++) {
  426. int xoff = FFABS(x + i - radius);
  427. xoff = xoff >= h ? 2 * h - 1 - xoff : xoff;
  428. c[i] = src + y * bpc + xoff * stride;
  429. }
  430. }
  431. static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  432. {
  433. ConvolutionContext *s = ctx->priv;
  434. ThreadData *td = arg;
  435. AVFrame *in = td->in;
  436. AVFrame *out = td->out;
  437. int plane;
  438. for (plane = 0; plane < s->nb_planes; plane++) {
  439. const int mode = s->mode[plane];
  440. const int bpc = s->bpc;
  441. const int radius = s->size[plane] / 2;
  442. const int height = s->planeheight[plane];
  443. const int width = s->planewidth[plane];
  444. const int stride = in->linesize[plane];
  445. const int dstride = out->linesize[plane];
  446. const int sizeh = mode == MATRIX_COLUMN ? width : height;
  447. const int sizew = mode == MATRIX_COLUMN ? height : width;
  448. const int slice_start = (sizeh * jobnr) / nb_jobs;
  449. const int slice_end = (sizeh * (jobnr+1)) / nb_jobs;
  450. const float rdiv = s->rdiv[plane];
  451. const float bias = s->bias[plane];
  452. const uint8_t *src = in->data[plane];
  453. const int dst_pos = slice_start * (mode == MATRIX_COLUMN ? bpc : dstride);
  454. uint8_t *dst = out->data[plane] + dst_pos;
  455. const int *matrix = s->matrix[plane];
  456. const uint8_t *c[49];
  457. int y, x;
  458. if (s->copy[plane]) {
  459. if (mode == MATRIX_COLUMN)
  460. av_image_copy_plane(dst, dstride, src + slice_start * bpc, stride,
  461. (slice_end - slice_start) * bpc, height);
  462. else
  463. av_image_copy_plane(dst, dstride, src + slice_start * stride, stride,
  464. width * bpc, slice_end - slice_start);
  465. continue;
  466. }
  467. for (y = slice_start; y < slice_end; y++) {
  468. const int xoff = mode == MATRIX_COLUMN ? (y - slice_start) * bpc : radius * bpc;
  469. const int yoff = mode == MATRIX_COLUMN ? radius * stride : 0;
  470. for (x = 0; x < radius; x++) {
  471. const int xoff = mode == MATRIX_COLUMN ? (y - slice_start) * bpc : x * bpc;
  472. const int yoff = mode == MATRIX_COLUMN ? x * stride : 0;
  473. s->setup[plane](radius, c, src, stride, x, width, y, height, bpc);
  474. s->filter[plane](dst + yoff + xoff, 1, rdiv,
  475. bias, matrix, c, s->max, radius,
  476. dstride, stride);
  477. }
  478. s->setup[plane](radius, c, src, stride, radius, width, y, height, bpc);
  479. s->filter[plane](dst + yoff + xoff, sizew - 2 * radius,
  480. rdiv, bias, matrix, c, s->max, radius,
  481. dstride, stride);
  482. for (x = sizew - radius; x < sizew; x++) {
  483. const int xoff = mode == MATRIX_COLUMN ? (y - slice_start) * bpc : x * bpc;
  484. const int yoff = mode == MATRIX_COLUMN ? x * stride : 0;
  485. s->setup[plane](radius, c, src, stride, x, width, y, height, bpc);
  486. s->filter[plane](dst + yoff + xoff, 1, rdiv,
  487. bias, matrix, c, s->max, radius,
  488. dstride, stride);
  489. }
  490. if (mode != MATRIX_COLUMN)
  491. dst += dstride;
  492. }
  493. }
  494. return 0;
  495. }
  496. static int config_input(AVFilterLink *inlink)
  497. {
  498. AVFilterContext *ctx = inlink->dst;
  499. ConvolutionContext *s = ctx->priv;
  500. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
  501. int p;
  502. s->depth = desc->comp[0].depth;
  503. s->max = (1 << s->depth) - 1;
  504. s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
  505. s->planewidth[0] = s->planewidth[3] = inlink->w;
  506. s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
  507. s->planeheight[0] = s->planeheight[3] = inlink->h;
  508. s->nb_planes = av_pix_fmt_count_planes(inlink->format);
  509. s->nb_threads = ff_filter_get_nb_threads(ctx);
  510. s->bpc = (s->depth + 7) / 8;
  511. if (!strcmp(ctx->filter->name, "convolution")) {
  512. if (s->depth > 8) {
  513. for (p = 0; p < s->nb_planes; p++) {
  514. if (s->mode[p] == MATRIX_ROW)
  515. s->filter[p] = filter16_row;
  516. else if (s->mode[p] == MATRIX_COLUMN)
  517. s->filter[p] = filter16_column;
  518. else if (s->size[p] == 3)
  519. s->filter[p] = filter16_3x3;
  520. else if (s->size[p] == 5)
  521. s->filter[p] = filter16_5x5;
  522. else if (s->size[p] == 7)
  523. s->filter[p] = filter16_7x7;
  524. }
  525. }
  526. } else if (!strcmp(ctx->filter->name, "prewitt")) {
  527. if (s->depth > 8)
  528. for (p = 0; p < s->nb_planes; p++)
  529. s->filter[p] = filter16_prewitt;
  530. } else if (!strcmp(ctx->filter->name, "roberts")) {
  531. if (s->depth > 8)
  532. for (p = 0; p < s->nb_planes; p++)
  533. s->filter[p] = filter16_roberts;
  534. } else if (!strcmp(ctx->filter->name, "sobel")) {
  535. if (s->depth > 8)
  536. for (p = 0; p < s->nb_planes; p++)
  537. s->filter[p] = filter16_sobel;
  538. }
  539. return 0;
  540. }
  541. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  542. {
  543. AVFilterContext *ctx = inlink->dst;
  544. ConvolutionContext *s = ctx->priv;
  545. AVFilterLink *outlink = ctx->outputs[0];
  546. AVFrame *out;
  547. ThreadData td;
  548. out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  549. if (!out) {
  550. av_frame_free(&in);
  551. return AVERROR(ENOMEM);
  552. }
  553. av_frame_copy_props(out, in);
  554. td.in = in;
  555. td.out = out;
  556. ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN3(s->planeheight[1], s->planewidth[1], s->nb_threads));
  557. av_frame_free(&in);
  558. return ff_filter_frame(outlink, out);
  559. }
  560. static av_cold int init(AVFilterContext *ctx)
  561. {
  562. ConvolutionContext *s = ctx->priv;
  563. int i;
  564. if (!strcmp(ctx->filter->name, "convolution")) {
  565. for (i = 0; i < 4; i++) {
  566. int *matrix = (int *)s->matrix[i];
  567. char *p, *arg, *saveptr = NULL;
  568. float sum = 0;
  569. p = s->matrix_str[i];
  570. while (s->matrix_length[i] < 49) {
  571. if (!(arg = av_strtok(p, " ", &saveptr)))
  572. break;
  573. p = NULL;
  574. sscanf(arg, "%d", &matrix[s->matrix_length[i]]);
  575. sum += matrix[s->matrix_length[i]];
  576. s->matrix_length[i]++;
  577. }
  578. if (!(s->matrix_length[i] & 1)) {
  579. av_log(ctx, AV_LOG_ERROR, "number of matrix elements must be odd\n");
  580. return AVERROR(EINVAL);
  581. }
  582. if (s->mode[i] == MATRIX_ROW) {
  583. s->filter[i] = filter_row;
  584. s->setup[i] = setup_row;
  585. s->size[i] = s->matrix_length[i];
  586. } else if (s->mode[i] == MATRIX_COLUMN) {
  587. s->filter[i] = filter_column;
  588. s->setup[i] = setup_column;
  589. s->size[i] = s->matrix_length[i];
  590. } else if (s->matrix_length[i] == 9) {
  591. s->size[i] = 3;
  592. if (!memcmp(matrix, same3x3, sizeof(same3x3)))
  593. s->copy[i] = 1;
  594. else
  595. s->filter[i] = filter_3x3;
  596. s->setup[i] = setup_3x3;
  597. } else if (s->matrix_length[i] == 25) {
  598. s->size[i] = 5;
  599. if (!memcmp(matrix, same5x5, sizeof(same5x5)))
  600. s->copy[i] = 1;
  601. else
  602. s->filter[i] = filter_5x5;
  603. s->setup[i] = setup_5x5;
  604. } else if (s->matrix_length[i] == 49) {
  605. s->size[i] = 7;
  606. if (!memcmp(matrix, same7x7, sizeof(same7x7)))
  607. s->copy[i] = 1;
  608. else
  609. s->filter[i] = filter_7x7;
  610. s->setup[i] = setup_7x7;
  611. } else {
  612. return AVERROR(EINVAL);
  613. }
  614. if (sum == 0)
  615. sum = 1;
  616. if (s->rdiv[i] == 0)
  617. s->rdiv[i] = 1. / sum;
  618. if (s->copy[i] && (s->rdiv[i] != 1. || s->bias[i] != 0.))
  619. s->copy[i] = 0;
  620. }
  621. } else if (!strcmp(ctx->filter->name, "prewitt")) {
  622. for (i = 0; i < 4; i++) {
  623. if ((1 << i) & s->planes)
  624. s->filter[i] = filter_prewitt;
  625. else
  626. s->copy[i] = 1;
  627. s->size[i] = 3;
  628. s->setup[i] = setup_3x3;
  629. s->rdiv[i] = s->scale;
  630. s->bias[i] = s->delta;
  631. }
  632. } else if (!strcmp(ctx->filter->name, "roberts")) {
  633. for (i = 0; i < 4; i++) {
  634. if ((1 << i) & s->planes)
  635. s->filter[i] = filter_roberts;
  636. else
  637. s->copy[i] = 1;
  638. s->size[i] = 3;
  639. s->setup[i] = setup_3x3;
  640. s->rdiv[i] = s->scale;
  641. s->bias[i] = s->delta;
  642. }
  643. } else if (!strcmp(ctx->filter->name, "sobel")) {
  644. for (i = 0; i < 4; i++) {
  645. if ((1 << i) & s->planes)
  646. s->filter[i] = filter_sobel;
  647. else
  648. s->copy[i] = 1;
  649. s->size[i] = 3;
  650. s->setup[i] = setup_3x3;
  651. s->rdiv[i] = s->scale;
  652. s->bias[i] = s->delta;
  653. }
  654. }
  655. return 0;
  656. }
  657. static const AVFilterPad convolution_inputs[] = {
  658. {
  659. .name = "default",
  660. .type = AVMEDIA_TYPE_VIDEO,
  661. .config_props = config_input,
  662. .filter_frame = filter_frame,
  663. },
  664. { NULL }
  665. };
  666. static const AVFilterPad convolution_outputs[] = {
  667. {
  668. .name = "default",
  669. .type = AVMEDIA_TYPE_VIDEO,
  670. },
  671. { NULL }
  672. };
  673. #if CONFIG_CONVOLUTION_FILTER
  674. AVFilter ff_vf_convolution = {
  675. .name = "convolution",
  676. .description = NULL_IF_CONFIG_SMALL("Apply convolution filter."),
  677. .priv_size = sizeof(ConvolutionContext),
  678. .priv_class = &convolution_class,
  679. .init = init,
  680. .query_formats = query_formats,
  681. .inputs = convolution_inputs,
  682. .outputs = convolution_outputs,
  683. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
  684. };
  685. #endif /* CONFIG_CONVOLUTION_FILTER */
  686. #if CONFIG_PREWITT_FILTER
  687. static const AVOption prewitt_options[] = {
  688. { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15}, 0, 15, FLAGS},
  689. { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, 65535, FLAGS},
  690. { "delta", "set delta", OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
  691. { NULL }
  692. };
  693. AVFILTER_DEFINE_CLASS(prewitt);
  694. AVFilter ff_vf_prewitt = {
  695. .name = "prewitt",
  696. .description = NULL_IF_CONFIG_SMALL("Apply prewitt operator."),
  697. .priv_size = sizeof(ConvolutionContext),
  698. .priv_class = &prewitt_class,
  699. .init = init,
  700. .query_formats = query_formats,
  701. .inputs = convolution_inputs,
  702. .outputs = convolution_outputs,
  703. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
  704. };
  705. #endif /* CONFIG_PREWITT_FILTER */
  706. #if CONFIG_SOBEL_FILTER
  707. static const AVOption sobel_options[] = {
  708. { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15}, 0, 15, FLAGS},
  709. { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, 65535, FLAGS},
  710. { "delta", "set delta", OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
  711. { NULL }
  712. };
  713. AVFILTER_DEFINE_CLASS(sobel);
  714. AVFilter ff_vf_sobel = {
  715. .name = "sobel",
  716. .description = NULL_IF_CONFIG_SMALL("Apply sobel operator."),
  717. .priv_size = sizeof(ConvolutionContext),
  718. .priv_class = &sobel_class,
  719. .init = init,
  720. .query_formats = query_formats,
  721. .inputs = convolution_inputs,
  722. .outputs = convolution_outputs,
  723. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
  724. };
  725. #endif /* CONFIG_SOBEL_FILTER */
  726. #if CONFIG_ROBERTS_FILTER
  727. static const AVOption roberts_options[] = {
  728. { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15}, 0, 15, FLAGS},
  729. { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, 65535, FLAGS},
  730. { "delta", "set delta", OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
  731. { NULL }
  732. };
  733. AVFILTER_DEFINE_CLASS(roberts);
  734. AVFilter ff_vf_roberts = {
  735. .name = "roberts",
  736. .description = NULL_IF_CONFIG_SMALL("Apply roberts cross operator."),
  737. .priv_size = sizeof(ConvolutionContext),
  738. .priv_class = &roberts_class,
  739. .init = init,
  740. .query_formats = query_formats,
  741. .inputs = convolution_inputs,
  742. .outputs = convolution_outputs,
  743. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
  744. };
  745. #endif /* CONFIG_ROBERTS_FILTER */