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.

848 lines
28KB

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