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.

982 lines
33KB

  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_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
  97. AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, 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_roberts(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. p1[x ] * -1;
  205. int sumb = p0[x ] * 1 +
  206. p1[x - 1] * -1;
  207. dst[x] = av_clip(sqrt(suma*suma + sumb*sumb) * scale + delta, 0, peak);
  208. }
  209. p0 = p1;
  210. p1 = p2;
  211. p2 = (p2 == end) ? orig: p2 + bstride;
  212. dst += out->linesize[plane] / 2;
  213. }
  214. return 0;
  215. }
  216. static int filter16_sobel(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  217. {
  218. ConvolutionContext *s = ctx->priv;
  219. ThreadData *td = arg;
  220. AVFrame *in = td->in;
  221. AVFrame *out = td->out;
  222. const int plane = td->plane;
  223. const int peak = (1 << s->depth) - 1;
  224. const int stride = in->linesize[plane] / 2;
  225. const int bstride = s->bstride;
  226. const int height = s->planeheight[plane];
  227. const int width = s->planewidth[plane];
  228. const int slice_start = (height * jobnr) / nb_jobs;
  229. const int slice_end = (height * (jobnr+1)) / nb_jobs;
  230. const uint16_t *src = (const uint16_t *)in->data[plane] + slice_start * stride;
  231. uint16_t *dst = (uint16_t *)out->data[plane] + slice_start * (out->linesize[plane] / 2);
  232. const float scale = s->scale;
  233. const float delta = s->delta;
  234. uint16_t *p0 = (uint16_t *)s->bptrs[jobnr] + 16;
  235. uint16_t *p1 = p0 + bstride;
  236. uint16_t *p2 = p1 + bstride;
  237. uint16_t *orig = p0, *end = p2;
  238. int y, x;
  239. line_copy16(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
  240. line_copy16(p1, src, width, 1);
  241. for (y = slice_start; y < slice_end; y++) {
  242. src += stride * (y < height - 1 ? 1 : -1);
  243. line_copy16(p2, src, width, 1);
  244. for (x = 0; x < width; x++) {
  245. int suma = p0[x - 1] * -1 +
  246. p0[x] * -2 +
  247. p0[x + 1] * -1 +
  248. p2[x - 1] * 1 +
  249. p2[x] * 2 +
  250. p2[x + 1] * 1;
  251. int sumb = p0[x - 1] * -1 +
  252. p0[x + 1] * 1 +
  253. p1[x - 1] * -2 +
  254. p1[x + 1] * 2 +
  255. p2[x - 1] * -1 +
  256. p2[x + 1] * 1;
  257. dst[x] = av_clip(sqrt(suma*suma + sumb*sumb) * scale + delta, 0, peak);
  258. }
  259. p0 = p1;
  260. p1 = p2;
  261. p2 = (p2 == end) ? orig: p2 + bstride;
  262. dst += out->linesize[plane] / 2;
  263. }
  264. return 0;
  265. }
  266. static int filter_prewitt(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  267. {
  268. ConvolutionContext *s = ctx->priv;
  269. ThreadData *td = arg;
  270. AVFrame *in = td->in;
  271. AVFrame *out = td->out;
  272. const int plane = td->plane;
  273. const int stride = in->linesize[plane];
  274. const int bstride = s->bstride;
  275. const int height = s->planeheight[plane];
  276. const int width = s->planewidth[plane];
  277. const int slice_start = (height * jobnr) / nb_jobs;
  278. const int slice_end = (height * (jobnr+1)) / nb_jobs;
  279. const uint8_t *src = in->data[plane] + slice_start * stride;
  280. uint8_t *dst = out->data[plane] + slice_start * out->linesize[plane];
  281. const float scale = s->scale;
  282. const float delta = s->delta;
  283. uint8_t *p0 = s->bptrs[jobnr] + 16;
  284. uint8_t *p1 = p0 + bstride;
  285. uint8_t *p2 = p1 + bstride;
  286. uint8_t *orig = p0, *end = p2;
  287. int y, x;
  288. line_copy8(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
  289. line_copy8(p1, src, width, 1);
  290. for (y = slice_start; y < slice_end; y++) {
  291. src += stride * (y < height - 1 ? 1 : -1);
  292. line_copy8(p2, src, width, 1);
  293. for (x = 0; x < width; x++) {
  294. int suma = p0[x - 1] * -1 +
  295. p0[x] * -1 +
  296. p0[x + 1] * -1 +
  297. p2[x - 1] * 1 +
  298. p2[x] * 1 +
  299. p2[x + 1] * 1;
  300. int sumb = p0[x - 1] * -1 +
  301. p0[x + 1] * 1 +
  302. p1[x - 1] * -1 +
  303. p1[x + 1] * 1 +
  304. p2[x - 1] * -1 +
  305. p2[x + 1] * 1;
  306. dst[x] = av_clip_uint8(sqrt(suma*suma + sumb*sumb) * scale + delta);
  307. }
  308. p0 = p1;
  309. p1 = p2;
  310. p2 = (p2 == end) ? orig: p2 + bstride;
  311. dst += out->linesize[plane];
  312. }
  313. return 0;
  314. }
  315. static int filter_roberts(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  316. {
  317. ConvolutionContext *s = ctx->priv;
  318. ThreadData *td = arg;
  319. AVFrame *in = td->in;
  320. AVFrame *out = td->out;
  321. const int plane = td->plane;
  322. const int stride = in->linesize[plane];
  323. const int bstride = s->bstride;
  324. const int height = s->planeheight[plane];
  325. const int width = s->planewidth[plane];
  326. const int slice_start = (height * jobnr) / nb_jobs;
  327. const int slice_end = (height * (jobnr+1)) / nb_jobs;
  328. const uint8_t *src = in->data[plane] + slice_start * stride;
  329. uint8_t *dst = out->data[plane] + slice_start * out->linesize[plane];
  330. const float scale = s->scale;
  331. const float delta = s->delta;
  332. uint8_t *p0 = s->bptrs[jobnr] + 16;
  333. uint8_t *p1 = p0 + bstride;
  334. uint8_t *p2 = p1 + bstride;
  335. uint8_t *orig = p0, *end = p2;
  336. int y, x;
  337. line_copy8(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
  338. line_copy8(p1, src, width, 1);
  339. for (y = slice_start; y < slice_end; y++) {
  340. src += stride * (y < height - 1 ? 1 : -1);
  341. line_copy8(p2, src, width, 1);
  342. for (x = 0; x < width; x++) {
  343. int suma = p0[x - 1] * 1 +
  344. p1[x ] * -1;
  345. int sumb = p0[x ] * 1 +
  346. p1[x - 1] * -1;
  347. dst[x] = av_clip_uint8(sqrt(suma*suma + sumb*sumb) * scale + delta);
  348. }
  349. p0 = p1;
  350. p1 = p2;
  351. p2 = (p2 == end) ? orig: p2 + bstride;
  352. dst += out->linesize[plane];
  353. }
  354. return 0;
  355. }
  356. static int filter_sobel(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  357. {
  358. ConvolutionContext *s = ctx->priv;
  359. ThreadData *td = arg;
  360. AVFrame *in = td->in;
  361. AVFrame *out = td->out;
  362. const int plane = td->plane;
  363. const int stride = in->linesize[plane];
  364. const int bstride = s->bstride;
  365. const int height = s->planeheight[plane];
  366. const int width = s->planewidth[plane];
  367. const int slice_start = (height * jobnr) / nb_jobs;
  368. const int slice_end = (height * (jobnr+1)) / nb_jobs;
  369. const uint8_t *src = in->data[plane] + slice_start * stride;
  370. uint8_t *dst = out->data[plane] + slice_start * out->linesize[plane];
  371. const float scale = s->scale;
  372. const float delta = s->delta;
  373. uint8_t *p0 = s->bptrs[jobnr] + 16;
  374. uint8_t *p1 = p0 + bstride;
  375. uint8_t *p2 = p1 + bstride;
  376. uint8_t *orig = p0, *end = p2;
  377. int y, x;
  378. line_copy8(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
  379. line_copy8(p1, src, width, 1);
  380. for (y = slice_start; y < slice_end; y++) {
  381. src += stride * (y < height - 1 ? 1 : -1);
  382. line_copy8(p2, src, width, 1);
  383. for (x = 0; x < width; x++) {
  384. int suma = p0[x - 1] * -1 +
  385. p0[x] * -2 +
  386. p0[x + 1] * -1 +
  387. p2[x - 1] * 1 +
  388. p2[x] * 2 +
  389. p2[x + 1] * 1;
  390. int sumb = p0[x - 1] * -1 +
  391. p0[x + 1] * 1 +
  392. p1[x - 1] * -2 +
  393. p1[x + 1] * 2 +
  394. p2[x - 1] * -1 +
  395. p2[x + 1] * 1;
  396. dst[x] = av_clip_uint8(sqrt(suma*suma + sumb*sumb) * scale + delta);
  397. }
  398. p0 = p1;
  399. p1 = p2;
  400. p2 = (p2 == end) ? orig: p2 + bstride;
  401. dst += out->linesize[plane];
  402. }
  403. return 0;
  404. }
  405. static int filter16_3x3(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  406. {
  407. ConvolutionContext *s = ctx->priv;
  408. ThreadData *td = arg;
  409. AVFrame *in = td->in;
  410. AVFrame *out = td->out;
  411. const int plane = td->plane;
  412. const int peak = (1 << s->depth) - 1;
  413. const int stride = in->linesize[plane] / 2;
  414. const int bstride = s->bstride;
  415. const int height = s->planeheight[plane];
  416. const int width = s->planewidth[plane];
  417. const int slice_start = (height * jobnr) / nb_jobs;
  418. const int slice_end = (height * (jobnr+1)) / nb_jobs;
  419. const uint16_t *src = (const uint16_t *)in->data[plane] + slice_start * stride;
  420. uint16_t *dst = (uint16_t *)out->data[plane] + slice_start * (out->linesize[plane] / 2);
  421. uint16_t *p0 = (uint16_t *)s->bptrs[jobnr] + 16;
  422. uint16_t *p1 = p0 + bstride;
  423. uint16_t *p2 = p1 + bstride;
  424. uint16_t *orig = p0, *end = p2;
  425. const int *matrix = s->matrix[plane];
  426. const float rdiv = s->rdiv[plane];
  427. const float bias = s->bias[plane];
  428. int y, x;
  429. line_copy16(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
  430. line_copy16(p1, src, width, 1);
  431. for (y = slice_start; y < slice_end; y++) {
  432. src += stride * (y < height - 1 ? 1 : -1);
  433. line_copy16(p2, src, width, 1);
  434. for (x = 0; x < width; x++) {
  435. int sum = p0[x - 1] * matrix[0] +
  436. p0[x] * matrix[1] +
  437. p0[x + 1] * matrix[2] +
  438. p1[x - 1] * matrix[3] +
  439. p1[x] * matrix[4] +
  440. p1[x + 1] * matrix[5] +
  441. p2[x - 1] * matrix[6] +
  442. p2[x] * matrix[7] +
  443. p2[x + 1] * matrix[8];
  444. sum = (int)(sum * rdiv + bias + 0.5f);
  445. dst[x] = av_clip(sum, 0, peak);
  446. }
  447. p0 = p1;
  448. p1 = p2;
  449. p2 = (p2 == end) ? orig: p2 + bstride;
  450. dst += out->linesize[plane] / 2;
  451. }
  452. return 0;
  453. }
  454. static int filter16_5x5(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  455. {
  456. ConvolutionContext *s = ctx->priv;
  457. ThreadData *td = arg;
  458. AVFrame *in = td->in;
  459. AVFrame *out = td->out;
  460. const int plane = td->plane;
  461. const int peak = (1 << s->depth) - 1;
  462. const int stride = in->linesize[plane] / 2;
  463. const int bstride = s->bstride;
  464. const int height = s->planeheight[plane];
  465. const int width = s->planewidth[plane];
  466. const int slice_start = (height * jobnr) / nb_jobs;
  467. const int slice_end = (height * (jobnr+1)) / nb_jobs;
  468. const uint16_t *src = (const uint16_t *)in->data[plane] + slice_start * stride;
  469. uint16_t *dst = (uint16_t *)out->data[plane] + slice_start * (out->linesize[plane] / 2);
  470. uint16_t *p0 = (uint16_t *)s->bptrs[jobnr] + 16;
  471. uint16_t *p1 = p0 + bstride;
  472. uint16_t *p2 = p1 + bstride;
  473. uint16_t *p3 = p2 + bstride;
  474. uint16_t *p4 = p3 + bstride;
  475. uint16_t *orig = p0, *end = p4;
  476. const int *matrix = s->matrix[plane];
  477. float rdiv = s->rdiv[plane];
  478. float bias = s->bias[plane];
  479. int y, x, i;
  480. line_copy16(p0, src + 2 * stride * (slice_start < 2 ? 1 : -1), width, 2);
  481. line_copy16(p1, src + stride * (slice_start == 0 ? 1 : -1), width, 2);
  482. line_copy16(p2, src, width, 2);
  483. src += stride;
  484. line_copy16(p3, src, width, 2);
  485. for (y = slice_start; y < slice_end; y++) {
  486. uint16_t *array[] = {
  487. p0 - 2, p0 - 1, p0, p0 + 1, p0 + 2,
  488. p1 - 2, p1 - 1, p1, p1 + 1, p1 + 2,
  489. p2 - 2, p2 - 1, p2, p2 + 1, p2 + 2,
  490. p3 - 2, p3 - 1, p3, p3 + 1, p3 + 2,
  491. p4 - 2, p4 - 1, p4, p4 + 1, p4 + 2
  492. };
  493. src += stride * (y < height - 2 ? 1 : -1);
  494. line_copy16(p4, src, width, 2);
  495. for (x = 0; x < width; x++) {
  496. int sum = 0;
  497. for (i = 0; i < 25; i++) {
  498. sum += *(array[i] + x) * matrix[i];
  499. }
  500. sum = (int)(sum * rdiv + bias + 0.5f);
  501. dst[x] = av_clip(sum, 0, peak);
  502. }
  503. p0 = p1;
  504. p1 = p2;
  505. p2 = p3;
  506. p3 = p4;
  507. p4 = (p4 == end) ? orig: p4 + bstride;
  508. dst += out->linesize[plane] / 2;
  509. }
  510. return 0;
  511. }
  512. static int filter_3x3(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  513. {
  514. ConvolutionContext *s = ctx->priv;
  515. ThreadData *td = arg;
  516. AVFrame *in = td->in;
  517. AVFrame *out = td->out;
  518. const int plane = td->plane;
  519. const int stride = in->linesize[plane];
  520. const int bstride = s->bstride;
  521. const int height = s->planeheight[plane];
  522. const int width = s->planewidth[plane];
  523. const int slice_start = (height * jobnr) / nb_jobs;
  524. const int slice_end = (height * (jobnr+1)) / nb_jobs;
  525. const uint8_t *src = in->data[plane] + slice_start * stride;
  526. uint8_t *dst = out->data[plane] + slice_start * out->linesize[plane];
  527. uint8_t *p0 = s->bptrs[jobnr] + 16;
  528. uint8_t *p1 = p0 + bstride;
  529. uint8_t *p2 = p1 + bstride;
  530. uint8_t *orig = p0, *end = p2;
  531. const int *matrix = s->matrix[plane];
  532. const float rdiv = s->rdiv[plane];
  533. const float bias = s->bias[plane];
  534. int y, x;
  535. line_copy8(p0, src + stride * (slice_start == 0 ? 1 : -1), width, 1);
  536. line_copy8(p1, src, width, 1);
  537. for (y = slice_start; y < slice_end; y++) {
  538. src += stride * (y < height - 1 ? 1 : -1);
  539. line_copy8(p2, src, width, 1);
  540. for (x = 0; x < width; x++) {
  541. int sum = p0[x - 1] * matrix[0] +
  542. p0[x] * matrix[1] +
  543. p0[x + 1] * matrix[2] +
  544. p1[x - 1] * matrix[3] +
  545. p1[x] * matrix[4] +
  546. p1[x + 1] * matrix[5] +
  547. p2[x - 1] * matrix[6] +
  548. p2[x] * matrix[7] +
  549. p2[x + 1] * matrix[8];
  550. sum = (int)(sum * rdiv + bias + 0.5f);
  551. dst[x] = av_clip_uint8(sum);
  552. }
  553. p0 = p1;
  554. p1 = p2;
  555. p2 = (p2 == end) ? orig: p2 + bstride;
  556. dst += out->linesize[plane];
  557. }
  558. return 0;
  559. }
  560. static int filter_5x5(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  561. {
  562. ConvolutionContext *s = ctx->priv;
  563. ThreadData *td = arg;
  564. AVFrame *in = td->in;
  565. AVFrame *out = td->out;
  566. const int plane = td->plane;
  567. const int stride = in->linesize[plane];
  568. const int bstride = s->bstride;
  569. const int height = s->planeheight[plane];
  570. const int width = s->planewidth[plane];
  571. const int slice_start = (height * jobnr) / nb_jobs;
  572. const int slice_end = (height * (jobnr+1)) / nb_jobs;
  573. const uint8_t *src = in->data[plane] + slice_start * stride;
  574. uint8_t *dst = out->data[plane] + slice_start * out->linesize[plane];
  575. uint8_t *p0 = s->bptrs[jobnr] + 16;
  576. uint8_t *p1 = p0 + bstride;
  577. uint8_t *p2 = p1 + bstride;
  578. uint8_t *p3 = p2 + bstride;
  579. uint8_t *p4 = p3 + bstride;
  580. uint8_t *orig = p0, *end = p4;
  581. const int *matrix = s->matrix[plane];
  582. float rdiv = s->rdiv[plane];
  583. float bias = s->bias[plane];
  584. int y, x, i;
  585. line_copy8(p0, src + 2 * stride * (slice_start < 2 ? 1 : -1), width, 2);
  586. line_copy8(p1, src + stride * (slice_start == 0 ? 1 : -1), width, 2);
  587. line_copy8(p2, src, width, 2);
  588. src += stride;
  589. line_copy8(p3, src, width, 2);
  590. for (y = slice_start; y < slice_end; y++) {
  591. uint8_t *array[] = {
  592. p0 - 2, p0 - 1, p0, p0 + 1, p0 + 2,
  593. p1 - 2, p1 - 1, p1, p1 + 1, p1 + 2,
  594. p2 - 2, p2 - 1, p2, p2 + 1, p2 + 2,
  595. p3 - 2, p3 - 1, p3, p3 + 1, p3 + 2,
  596. p4 - 2, p4 - 1, p4, p4 + 1, p4 + 2
  597. };
  598. src += stride * (y < height - 2 ? 1 : -1);
  599. line_copy8(p4, src, width, 2);
  600. for (x = 0; x < width; x++) {
  601. int sum = 0;
  602. for (i = 0; i < 25; i++) {
  603. sum += *(array[i] + x) * matrix[i];
  604. }
  605. sum = (int)(sum * rdiv + bias + 0.5f);
  606. dst[x] = av_clip_uint8(sum);
  607. }
  608. p0 = p1;
  609. p1 = p2;
  610. p2 = p3;
  611. p3 = p4;
  612. p4 = (p4 == end) ? orig: p4 + bstride;
  613. dst += out->linesize[plane];
  614. }
  615. return 0;
  616. }
  617. static int config_input(AVFilterLink *inlink)
  618. {
  619. AVFilterContext *ctx = inlink->dst;
  620. ConvolutionContext *s = ctx->priv;
  621. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
  622. int p;
  623. s->depth = desc->comp[0].depth;
  624. s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
  625. s->planewidth[0] = s->planewidth[3] = inlink->w;
  626. s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
  627. s->planeheight[0] = s->planeheight[3] = inlink->h;
  628. s->nb_planes = av_pix_fmt_count_planes(inlink->format);
  629. s->nb_threads = ff_filter_get_nb_threads(ctx);
  630. s->bptrs = av_calloc(s->nb_threads, sizeof(*s->bptrs));
  631. if (!s->bptrs)
  632. return AVERROR(ENOMEM);
  633. s->bstride = s->planewidth[0] + 32;
  634. s->bpc = (s->depth + 7) / 8;
  635. s->buffer = av_malloc_array(5 * s->bstride * s->nb_threads, s->bpc);
  636. if (!s->buffer)
  637. return AVERROR(ENOMEM);
  638. for (p = 0; p < s->nb_threads; p++) {
  639. s->bptrs[p] = s->buffer + 5 * s->bstride * s->bpc * p;
  640. }
  641. if (!strcmp(ctx->filter->name, "convolution")) {
  642. if (s->depth > 8) {
  643. for (p = 0; p < s->nb_planes; p++) {
  644. if (s->size[p] == 3)
  645. s->filter[p] = filter16_3x3;
  646. else if (s->size[p] == 5)
  647. s->filter[p] = filter16_5x5;
  648. }
  649. }
  650. } else if (!strcmp(ctx->filter->name, "prewitt")) {
  651. if (s->depth > 8)
  652. for (p = 0; p < s->nb_planes; p++)
  653. s->filter[p] = filter16_prewitt;
  654. } else if (!strcmp(ctx->filter->name, "roberts")) {
  655. if (s->depth > 8)
  656. for (p = 0; p < s->nb_planes; p++)
  657. s->filter[p] = filter16_roberts;
  658. } else if (!strcmp(ctx->filter->name, "sobel")) {
  659. if (s->depth > 8)
  660. for (p = 0; p < s->nb_planes; p++)
  661. s->filter[p] = filter16_sobel;
  662. }
  663. return 0;
  664. }
  665. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  666. {
  667. AVFilterContext *ctx = inlink->dst;
  668. ConvolutionContext *s = ctx->priv;
  669. AVFilterLink *outlink = ctx->outputs[0];
  670. AVFrame *out;
  671. int plane;
  672. out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  673. if (!out) {
  674. av_frame_free(&in);
  675. return AVERROR(ENOMEM);
  676. }
  677. av_frame_copy_props(out, in);
  678. for (plane = 0; plane < s->nb_planes; plane++) {
  679. ThreadData td;
  680. if (s->copy[plane]) {
  681. av_image_copy_plane(out->data[plane], out->linesize[plane],
  682. in->data[plane], in->linesize[plane],
  683. s->planewidth[plane] * s->bpc,
  684. s->planeheight[plane]);
  685. continue;
  686. }
  687. td.in = in;
  688. td.out = out;
  689. td.plane = plane;
  690. ctx->internal->execute(ctx, s->filter[plane], &td, NULL, FFMIN(s->planeheight[plane], s->nb_threads));
  691. }
  692. av_frame_free(&in);
  693. return ff_filter_frame(outlink, out);
  694. }
  695. static av_cold int init(AVFilterContext *ctx)
  696. {
  697. ConvolutionContext *s = ctx->priv;
  698. int i;
  699. if (!strcmp(ctx->filter->name, "convolution")) {
  700. for (i = 0; i < 4; i++) {
  701. int *matrix = (int *)s->matrix[i];
  702. char *p, *arg, *saveptr = NULL;
  703. p = s->matrix_str[i];
  704. while (s->matrix_length[i] < 25) {
  705. if (!(arg = av_strtok(p, " ", &saveptr)))
  706. break;
  707. p = NULL;
  708. sscanf(arg, "%d", &matrix[s->matrix_length[i]]);
  709. s->matrix_length[i]++;
  710. }
  711. if (s->matrix_length[i] == 9) {
  712. s->size[i] = 3;
  713. if (!memcmp(matrix, same3x3, sizeof(same3x3)))
  714. s->copy[i] = 1;
  715. else
  716. s->filter[i] = filter_3x3;
  717. } else if (s->matrix_length[i] == 25) {
  718. s->size[i] = 5;
  719. if (!memcmp(matrix, same5x5, sizeof(same5x5)))
  720. s->copy[i] = 1;
  721. else
  722. s->filter[i] = filter_5x5;
  723. } else {
  724. return AVERROR(EINVAL);
  725. }
  726. }
  727. } else if (!strcmp(ctx->filter->name, "prewitt")) {
  728. for (i = 0; i < 4; i++) {
  729. if ((1 << i) & s->planes)
  730. s->filter[i] = filter_prewitt;
  731. else
  732. s->copy[i] = 1;
  733. }
  734. } else if (!strcmp(ctx->filter->name, "roberts")) {
  735. for (i = 0; i < 4; i++) {
  736. if ((1 << i) & s->planes)
  737. s->filter[i] = filter_roberts;
  738. else
  739. s->copy[i] = 1;
  740. }
  741. } else if (!strcmp(ctx->filter->name, "sobel")) {
  742. for (i = 0; i < 4; i++) {
  743. if ((1 << i) & s->planes)
  744. s->filter[i] = filter_sobel;
  745. else
  746. s->copy[i] = 1;
  747. }
  748. }
  749. return 0;
  750. }
  751. static av_cold void uninit(AVFilterContext *ctx)
  752. {
  753. ConvolutionContext *s = ctx->priv;
  754. av_freep(&s->bptrs);
  755. av_freep(&s->buffer);
  756. }
  757. static const AVFilterPad convolution_inputs[] = {
  758. {
  759. .name = "default",
  760. .type = AVMEDIA_TYPE_VIDEO,
  761. .config_props = config_input,
  762. .filter_frame = filter_frame,
  763. },
  764. { NULL }
  765. };
  766. static const AVFilterPad convolution_outputs[] = {
  767. {
  768. .name = "default",
  769. .type = AVMEDIA_TYPE_VIDEO,
  770. },
  771. { NULL }
  772. };
  773. #if CONFIG_CONVOLUTION_FILTER
  774. AVFilter ff_vf_convolution = {
  775. .name = "convolution",
  776. .description = NULL_IF_CONFIG_SMALL("Apply convolution filter."),
  777. .priv_size = sizeof(ConvolutionContext),
  778. .priv_class = &convolution_class,
  779. .init = init,
  780. .uninit = uninit,
  781. .query_formats = query_formats,
  782. .inputs = convolution_inputs,
  783. .outputs = convolution_outputs,
  784. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
  785. };
  786. #endif /* CONFIG_CONVOLUTION_FILTER */
  787. #if CONFIG_PREWITT_FILTER
  788. static const AVOption prewitt_options[] = {
  789. { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15}, 0, 15, FLAGS},
  790. { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, 65535, FLAGS},
  791. { "delta", "set delta", OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
  792. { NULL }
  793. };
  794. AVFILTER_DEFINE_CLASS(prewitt);
  795. AVFilter ff_vf_prewitt = {
  796. .name = "prewitt",
  797. .description = NULL_IF_CONFIG_SMALL("Apply prewitt operator."),
  798. .priv_size = sizeof(ConvolutionContext),
  799. .priv_class = &prewitt_class,
  800. .init = init,
  801. .uninit = uninit,
  802. .query_formats = query_formats,
  803. .inputs = convolution_inputs,
  804. .outputs = convolution_outputs,
  805. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
  806. };
  807. #endif /* CONFIG_PREWITT_FILTER */
  808. #if CONFIG_SOBEL_FILTER
  809. static const AVOption sobel_options[] = {
  810. { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15}, 0, 15, FLAGS},
  811. { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, 65535, FLAGS},
  812. { "delta", "set delta", OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
  813. { NULL }
  814. };
  815. AVFILTER_DEFINE_CLASS(sobel);
  816. AVFilter ff_vf_sobel = {
  817. .name = "sobel",
  818. .description = NULL_IF_CONFIG_SMALL("Apply sobel operator."),
  819. .priv_size = sizeof(ConvolutionContext),
  820. .priv_class = &sobel_class,
  821. .init = init,
  822. .uninit = uninit,
  823. .query_formats = query_formats,
  824. .inputs = convolution_inputs,
  825. .outputs = convolution_outputs,
  826. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
  827. };
  828. #endif /* CONFIG_SOBEL_FILTER */
  829. #if CONFIG_ROBERTS_FILTER
  830. static const AVOption roberts_options[] = {
  831. { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15}, 0, 15, FLAGS},
  832. { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, 65535, FLAGS},
  833. { "delta", "set delta", OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
  834. { NULL }
  835. };
  836. AVFILTER_DEFINE_CLASS(roberts);
  837. AVFilter ff_vf_roberts = {
  838. .name = "roberts",
  839. .description = NULL_IF_CONFIG_SMALL("Apply roberts cross operator."),
  840. .priv_size = sizeof(ConvolutionContext),
  841. .priv_class = &roberts_class,
  842. .init = init,
  843. .uninit = uninit,
  844. .query_formats = query_formats,
  845. .inputs = convolution_inputs,
  846. .outputs = convolution_outputs,
  847. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
  848. };
  849. #endif /* CONFIG_ROBERTS_FILTER */