2 * Copyright (c) 2012-2013 Oka Motofumi (chikuzen.mo at gmail dot com)
3 * Copyright (c) 2015 Paul B Mahol
5 * This file is part of FFmpeg.
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.
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.
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
22 #include "config_components.h"
24 #include "libavutil/avstring.h"
25 #include "libavutil/imgutils.h"
26 #include "libavutil/intreadwrite.h"
27 #include "libavutil/mem.h"
28 #include "libavutil/mem_internal.h"
29 #include "libavutil/opt.h"
30 #include "libavutil/pixdesc.h"
32 #include "convolution.h"
36 #define OFFSET(x) offsetof(ConvolutionContext, x)
37 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
39 static const AVOption convolution_options
[] = {
40 { "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
},
41 { "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
},
42 { "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
},
43 { "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
},
44 { "0rdiv", "set rdiv for 1st plane", OFFSET(user_rdiv
[0]), AV_OPT_TYPE_FLOAT
, {.dbl
=0.0}, 0.0, INT_MAX
, FLAGS
},
45 { "1rdiv", "set rdiv for 2nd plane", OFFSET(user_rdiv
[1]), AV_OPT_TYPE_FLOAT
, {.dbl
=0.0}, 0.0, INT_MAX
, FLAGS
},
46 { "2rdiv", "set rdiv for 3rd plane", OFFSET(user_rdiv
[2]), AV_OPT_TYPE_FLOAT
, {.dbl
=0.0}, 0.0, INT_MAX
, FLAGS
},
47 { "3rdiv", "set rdiv for 4th plane", OFFSET(user_rdiv
[3]), AV_OPT_TYPE_FLOAT
, {.dbl
=0.0}, 0.0, INT_MAX
, FLAGS
},
48 { "0bias", "set bias for 1st plane", OFFSET(bias
[0]), AV_OPT_TYPE_FLOAT
, {.dbl
=0.0}, 0.0, INT_MAX
, FLAGS
},
49 { "1bias", "set bias for 2nd plane", OFFSET(bias
[1]), AV_OPT_TYPE_FLOAT
, {.dbl
=0.0}, 0.0, INT_MAX
, FLAGS
},
50 { "2bias", "set bias for 3rd plane", OFFSET(bias
[2]), AV_OPT_TYPE_FLOAT
, {.dbl
=0.0}, 0.0, INT_MAX
, FLAGS
},
51 { "3bias", "set bias for 4th plane", OFFSET(bias
[3]), AV_OPT_TYPE_FLOAT
, {.dbl
=0.0}, 0.0, INT_MAX
, FLAGS
},
52 { "0mode", "set matrix mode for 1st plane", OFFSET(mode
[0]), AV_OPT_TYPE_INT
, {.i64
=MATRIX_SQUARE
}, 0, MATRIX_NBMODES
-1, FLAGS
, .unit
= "mode" },
53 { "1mode", "set matrix mode for 2nd plane", OFFSET(mode
[1]), AV_OPT_TYPE_INT
, {.i64
=MATRIX_SQUARE
}, 0, MATRIX_NBMODES
-1, FLAGS
, .unit
= "mode" },
54 { "2mode", "set matrix mode for 3rd plane", OFFSET(mode
[2]), AV_OPT_TYPE_INT
, {.i64
=MATRIX_SQUARE
}, 0, MATRIX_NBMODES
-1, FLAGS
, .unit
= "mode" },
55 { "3mode", "set matrix mode for 4th plane", OFFSET(mode
[3]), AV_OPT_TYPE_INT
, {.i64
=MATRIX_SQUARE
}, 0, MATRIX_NBMODES
-1, FLAGS
, .unit
= "mode" },
56 { "square", "square matrix", 0, AV_OPT_TYPE_CONST
, {.i64
=MATRIX_SQUARE
}, 0, 0, FLAGS
, .unit
= "mode" },
57 { "row", "single row matrix", 0, AV_OPT_TYPE_CONST
, {.i64
=MATRIX_ROW
} , 0, 0, FLAGS
, .unit
= "mode" },
58 { "column", "single column matrix", 0, AV_OPT_TYPE_CONST
, {.i64
=MATRIX_COLUMN
}, 0, 0, FLAGS
, .unit
= "mode" },
62 AVFILTER_DEFINE_CLASS(convolution
);
64 static const int same3x3
[9] = {0, 0, 0,
68 static const int same5x5
[25] = {0, 0, 0, 0, 0,
74 static const int same7x7
[49] = {0, 0, 0, 0, 0, 0, 0,
82 static const enum AVPixelFormat pix_fmts
[] = {
83 AV_PIX_FMT_YUVA444P
, AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV440P
,
84 AV_PIX_FMT_YUVJ444P
, AV_PIX_FMT_YUVJ440P
,
85 AV_PIX_FMT_YUVA422P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUVA420P
, AV_PIX_FMT_YUV420P
,
86 AV_PIX_FMT_YUVJ422P
, AV_PIX_FMT_YUVJ420P
,
87 AV_PIX_FMT_YUVJ411P
, AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
,
88 AV_PIX_FMT_YUV420P9
, AV_PIX_FMT_YUV422P9
, AV_PIX_FMT_YUV444P9
,
89 AV_PIX_FMT_YUV420P10
, AV_PIX_FMT_YUV422P10
, AV_PIX_FMT_YUV444P10
,
90 AV_PIX_FMT_YUV420P12
, AV_PIX_FMT_YUV422P12
, AV_PIX_FMT_YUV444P12
, AV_PIX_FMT_YUV440P12
,
91 AV_PIX_FMT_YUV420P14
, AV_PIX_FMT_YUV422P14
, AV_PIX_FMT_YUV444P14
,
92 AV_PIX_FMT_YUV420P16
, AV_PIX_FMT_YUV422P16
, AV_PIX_FMT_YUV444P16
,
93 AV_PIX_FMT_YUVA420P9
, AV_PIX_FMT_YUVA422P9
, AV_PIX_FMT_YUVA444P9
,
94 AV_PIX_FMT_YUVA420P10
, AV_PIX_FMT_YUVA422P10
, AV_PIX_FMT_YUVA444P10
,
95 AV_PIX_FMT_YUVA422P12
, AV_PIX_FMT_YUVA444P12
,
96 AV_PIX_FMT_YUVA420P16
, AV_PIX_FMT_YUVA422P16
, AV_PIX_FMT_YUVA444P16
,
97 AV_PIX_FMT_GBRP
, AV_PIX_FMT_GBRP9
, AV_PIX_FMT_GBRP10
,
98 AV_PIX_FMT_GBRP12
, AV_PIX_FMT_GBRP14
, AV_PIX_FMT_GBRP16
,
99 AV_PIX_FMT_GBRAP
, AV_PIX_FMT_GBRAP10
, AV_PIX_FMT_GBRAP12
, AV_PIX_FMT_GBRAP16
,
100 AV_PIX_FMT_GRAY8
, AV_PIX_FMT_GRAY9
, AV_PIX_FMT_GRAY10
, AV_PIX_FMT_GRAY12
, AV_PIX_FMT_GRAY14
, AV_PIX_FMT_GRAY16
,
104 typedef struct ThreadData
{
108 static void filter16_prewitt(uint8_t *dstp
, int width
,
109 float scale
, float delta
, const int *const matrix
,
110 const uint8_t *c
[], int peak
, int radius
,
111 int dstride
, int stride
, int size
)
113 uint16_t *dst
= (uint16_t *)dstp
;
116 for (x
= 0; x
< width
; x
++) {
117 float suma
= AV_RN16A(&c
[0][2 * x
]) * -1 + AV_RN16A(&c
[1][2 * x
]) * -1 + AV_RN16A(&c
[2][2 * x
]) * -1 +
118 AV_RN16A(&c
[6][2 * x
]) * 1 + AV_RN16A(&c
[7][2 * x
]) * 1 + AV_RN16A(&c
[8][2 * x
]) * 1;
119 float sumb
= AV_RN16A(&c
[0][2 * x
]) * -1 + AV_RN16A(&c
[2][2 * x
]) * 1 + AV_RN16A(&c
[3][2 * x
]) * -1 +
120 AV_RN16A(&c
[5][2 * x
]) * 1 + AV_RN16A(&c
[6][2 * x
]) * -1 + AV_RN16A(&c
[8][2 * x
]) * 1;
122 dst
[x
] = av_clip(sqrtf(suma
*suma
+ sumb
*sumb
) * scale
+ delta
, 0, peak
);
126 static void filter16_roberts(uint8_t *dstp
, int width
,
127 float scale
, float delta
, const int *const matrix
,
128 const uint8_t *c
[], int peak
, int radius
,
129 int dstride
, int stride
, int size
)
131 uint16_t *dst
= (uint16_t *)dstp
;
134 for (x
= 0; x
< width
; x
++) {
135 float suma
= AV_RN16A(&c
[0][2 * x
]) * 1 + AV_RN16A(&c
[1][2 * x
]) * -1;
136 float sumb
= AV_RN16A(&c
[4][2 * x
]) * 1 + AV_RN16A(&c
[3][2 * x
]) * -1;
138 dst
[x
] = av_clip(sqrtf(suma
*suma
+ sumb
*sumb
) * scale
+ delta
, 0, peak
);
142 static void filter16_scharr(uint8_t *dstp
, int width
,
143 float scale
, float delta
, const int *const matrix
,
144 const uint8_t *c
[], int peak
, int radius
,
145 int dstride
, int stride
, int size
)
147 uint16_t *dst
= (uint16_t *)dstp
;
150 for (x
= 0; x
< width
; x
++) {
151 float suma
= AV_RN16A(&c
[0][2 * x
]) * -47 + AV_RN16A(&c
[1][2 * x
]) * -162 + AV_RN16A(&c
[2][2 * x
]) * -47 +
152 AV_RN16A(&c
[6][2 * x
]) * 47 + AV_RN16A(&c
[7][2 * x
]) * 162 + AV_RN16A(&c
[8][2 * x
]) * 47;
153 float sumb
= AV_RN16A(&c
[0][2 * x
]) * -47 + AV_RN16A(&c
[2][2 * x
]) * 47 + AV_RN16A(&c
[3][2 * x
]) * -162 +
154 AV_RN16A(&c
[5][2 * x
]) * 162 + AV_RN16A(&c
[6][2 * x
]) * -47 + AV_RN16A(&c
[8][2 * x
]) * 47;
158 dst
[x
] = av_clip(sqrtf(suma
*suma
+ sumb
*sumb
) * scale
+ delta
, 0, peak
);
162 static void filter16_kirsch(uint8_t *dstp
, int width
,
163 float scale
, float delta
, const int *const matrix
,
164 const uint8_t *c
[], int peak
, int radius
,
165 int dstride
, int stride
, int size
)
167 uint16_t *dst
= (uint16_t *)dstp
;
168 const uint16_t *c0
= (const uint16_t *)c
[0], *c1
= (const uint16_t *)c
[1], *c2
= (const uint16_t *)c
[2];
169 const uint16_t *c3
= (const uint16_t *)c
[3], *c5
= (const uint16_t *)c
[5];
170 const uint16_t *c6
= (const uint16_t *)c
[6], *c7
= (const uint16_t *)c
[7], *c8
= (const uint16_t *)c
[8];
173 for (x
= 0; x
< width
; x
++) {
174 int sum0
= c0
[x
] * 5 + c1
[x
] * 5 + c2
[x
] * 5 +
175 c3
[x
] * -3 + c5
[x
] * -3 +
176 c6
[x
] * -3 + c7
[x
] * -3 + c8
[x
] * -3;
177 int sum1
= c0
[x
] * -3 + c1
[x
] * 5 + c2
[x
] * 5 +
178 c3
[x
] * 5 + c5
[x
] * -3 +
179 c6
[x
] * -3 + c7
[x
] * -3 + c8
[x
] * -3;
180 int sum2
= c0
[x
] * -3 + c1
[x
] * -3 + c2
[x
] * 5 +
181 c3
[x
] * 5 + c5
[x
] * 5 +
182 c6
[x
] * -3 + c7
[x
] * -3 + c8
[x
] * -3;
183 int sum3
= c0
[x
] * -3 + c1
[x
] * -3 + c2
[x
] * -3 +
184 c3
[x
] * 5 + c5
[x
] * 5 +
185 c6
[x
] * 5 + c7
[x
] * -3 + c8
[x
] * -3;
186 int sum4
= c0
[x
] * -3 + c1
[x
] * -3 + c2
[x
] * -3 +
187 c3
[x
] * -3 + c5
[x
] * 5 +
188 c6
[x
] * 5 + c7
[x
] * 5 + c8
[x
] * -3;
189 int sum5
= c0
[x
] * -3 + c1
[x
] * -3 + c2
[x
] * -3 +
190 c3
[x
] * -3 + c5
[x
] * -3 +
191 c6
[x
] * 5 + c7
[x
] * 5 + c8
[x
] * 5;
192 int sum6
= c0
[x
] * 5 + c1
[x
] * -3 + c2
[x
] * -3 +
193 c3
[x
] * -3 + c5
[x
] * -3 +
194 c6
[x
] * -3 + c7
[x
] * 5 + c8
[x
] * 5;
195 int sum7
= c0
[x
] * 5 + c1
[x
] * 5 + c2
[x
] * -3 +
196 c3
[x
] * -3 + c5
[x
] * -3 +
197 c6
[x
] * -3 + c7
[x
] * -3 + c8
[x
] * 5;
199 sum0
= FFMAX(sum0
, sum1
);
200 sum2
= FFMAX(sum2
, sum3
);
201 sum4
= FFMAX(sum4
, sum5
);
202 sum6
= FFMAX(sum6
, sum7
);
203 sum0
= FFMAX(sum0
, sum2
);
204 sum4
= FFMAX(sum4
, sum6
);
205 sum0
= FFMAX(sum0
, sum4
);
207 dst
[x
] = av_clip(FFABS(sum0
) * scale
+ delta
, 0, peak
);
211 static void filter_prewitt(uint8_t *dst
, int width
,
212 float scale
, float delta
, const int *const matrix
,
213 const uint8_t *c
[], int peak
, int radius
,
214 int dstride
, int stride
, int size
)
216 const uint8_t *c0
= c
[0], *c1
= c
[1], *c2
= c
[2];
217 const uint8_t *c3
= c
[3], *c5
= c
[5];
218 const uint8_t *c6
= c
[6], *c7
= c
[7], *c8
= c
[8];
221 for (x
= 0; x
< width
; x
++) {
222 float suma
= c0
[x
] * -1 + c1
[x
] * -1 + c2
[x
] * -1 +
223 c6
[x
] * 1 + c7
[x
] * 1 + c8
[x
] * 1;
224 float sumb
= c0
[x
] * -1 + c2
[x
] * 1 + c3
[x
] * -1 +
225 c5
[x
] * 1 + c6
[x
] * -1 + c8
[x
] * 1;
227 dst
[x
] = av_clip_uint8(sqrtf(suma
*suma
+ sumb
*sumb
) * scale
+ delta
);
231 static void filter_roberts(uint8_t *dst
, int width
,
232 float scale
, float delta
, const int *const matrix
,
233 const uint8_t *c
[], int peak
, int radius
,
234 int dstride
, int stride
, int size
)
238 for (x
= 0; x
< width
; x
++) {
239 float suma
= c
[0][x
] * 1 + c
[1][x
] * -1;
240 float sumb
= c
[4][x
] * 1 + c
[3][x
] * -1;
242 dst
[x
] = av_clip_uint8(sqrtf(suma
*suma
+ sumb
*sumb
) * scale
+ delta
);
246 static void filter_scharr(uint8_t *dst
, int width
,
247 float scale
, float delta
, const int *const matrix
,
248 const uint8_t *c
[], int peak
, int radius
,
249 int dstride
, int stride
, int size
)
251 const uint8_t *c0
= c
[0], *c1
= c
[1], *c2
= c
[2];
252 const uint8_t *c3
= c
[3], *c5
= c
[5];
253 const uint8_t *c6
= c
[6], *c7
= c
[7], *c8
= c
[8];
256 for (x
= 0; x
< width
; x
++) {
257 float suma
= c0
[x
] * -47 + c1
[x
] * -162 + c2
[x
] * -47 +
258 c6
[x
] * 47 + c7
[x
] * 162 + c8
[x
] * 47;
259 float sumb
= c0
[x
] * -47 + c2
[x
] * 47 + c3
[x
] * -162 +
260 c5
[x
] * 162 + c6
[x
] * -47 + c8
[x
] * 47;
264 dst
[x
] = av_clip_uint8(sqrtf(suma
*suma
+ sumb
*sumb
) * scale
+ delta
);
268 static void filter_kirsch(uint8_t *dst
, int width
,
269 float scale
, float delta
, const int *const matrix
,
270 const uint8_t *c
[], int peak
, int radius
,
271 int dstride
, int stride
, int size
)
273 const uint8_t *c0
= c
[0], *c1
= c
[1], *c2
= c
[2];
274 const uint8_t *c3
= c
[3], *c5
= c
[5];
275 const uint8_t *c6
= c
[6], *c7
= c
[7], *c8
= c
[8];
278 for (x
= 0; x
< width
; x
++) {
279 int sum0
= c0
[x
] * 5 + c1
[x
] * 5 + c2
[x
] * 5 +
280 c3
[x
] * -3 + c5
[x
] * -3 +
281 c6
[x
] * -3 + c7
[x
] * -3 + c8
[x
] * -3;
282 int sum1
= c0
[x
] * -3 + c1
[x
] * 5 + c2
[x
] * 5 +
283 c3
[x
] * 5 + c5
[x
] * -3 +
284 c6
[x
] * -3 + c7
[x
] * -3 + c8
[x
] * -3;
285 int sum2
= c0
[x
] * -3 + c1
[x
] * -3 + c2
[x
] * 5 +
286 c3
[x
] * 5 + c5
[x
] * 5 +
287 c6
[x
] * -3 + c7
[x
] * -3 + c8
[x
] * -3;
288 int sum3
= c0
[x
] * -3 + c1
[x
] * -3 + c2
[x
] * -3 +
289 c3
[x
] * 5 + c5
[x
] * 5 +
290 c6
[x
] * 5 + c7
[x
] * -3 + c8
[x
] * -3;
291 int sum4
= c0
[x
] * -3 + c1
[x
] * -3 + c2
[x
] * -3 +
292 c3
[x
] * -3 + c5
[x
] * 5 +
293 c6
[x
] * 5 + c7
[x
] * 5 + c8
[x
] * -3;
294 int sum5
= c0
[x
] * -3 + c1
[x
] * -3 + c2
[x
] * -3 +
295 c3
[x
] * -3 + c5
[x
] * -3 +
296 c6
[x
] * 5 + c7
[x
] * 5 + c8
[x
] * 5;
297 int sum6
= c0
[x
] * 5 + c1
[x
] * -3 + c2
[x
] * -3 +
298 c3
[x
] * -3 + c5
[x
] * -3 +
299 c6
[x
] * -3 + c7
[x
] * 5 + c8
[x
] * 5;
300 int sum7
= c0
[x
] * 5 + c1
[x
] * 5 + c2
[x
] * -3 +
301 c3
[x
] * -3 + c5
[x
] * -3 +
302 c6
[x
] * -3 + c7
[x
] * -3 + c8
[x
] * 5;
304 sum0
= FFMAX(sum0
, sum1
);
305 sum2
= FFMAX(sum2
, sum3
);
306 sum4
= FFMAX(sum4
, sum5
);
307 sum6
= FFMAX(sum6
, sum7
);
308 sum0
= FFMAX(sum0
, sum2
);
309 sum4
= FFMAX(sum4
, sum6
);
310 sum0
= FFMAX(sum0
, sum4
);
312 dst
[x
] = av_clip_uint8(FFABS(sum0
) * scale
+ delta
);
316 static void filter16_3x3(uint8_t *dstp
, int width
,
317 float rdiv
, float bias
, const int *const matrix
,
318 const uint8_t *c
[], int peak
, int radius
,
319 int dstride
, int stride
, int size
)
321 uint16_t *dst
= (uint16_t *)dstp
;
324 for (x
= 0; x
< width
; x
++) {
325 int sum
= AV_RN16A(&c
[0][2 * x
]) * matrix
[0] +
326 AV_RN16A(&c
[1][2 * x
]) * matrix
[1] +
327 AV_RN16A(&c
[2][2 * x
]) * matrix
[2] +
328 AV_RN16A(&c
[3][2 * x
]) * matrix
[3] +
329 AV_RN16A(&c
[4][2 * x
]) * matrix
[4] +
330 AV_RN16A(&c
[5][2 * x
]) * matrix
[5] +
331 AV_RN16A(&c
[6][2 * x
]) * matrix
[6] +
332 AV_RN16A(&c
[7][2 * x
]) * matrix
[7] +
333 AV_RN16A(&c
[8][2 * x
]) * matrix
[8];
334 sum
= (int)(sum
* rdiv
+ bias
+ 0.5f
);
335 dst
[x
] = av_clip(sum
, 0, peak
);
339 static void filter16_5x5(uint8_t *dstp
, int width
,
340 float rdiv
, float bias
, const int *const matrix
,
341 const uint8_t *c
[], int peak
, int radius
,
342 int dstride
, int stride
, int size
)
344 uint16_t *dst
= (uint16_t *)dstp
;
347 for (x
= 0; x
< width
; x
++) {
350 for (i
= 0; i
< 25; i
++)
351 sum
+= AV_RN16A(&c
[i
][2 * x
]) * matrix
[i
];
353 sum
= (int)(sum
* rdiv
+ bias
+ 0.5f
);
354 dst
[x
] = av_clip(sum
, 0, peak
);
358 static void filter16_7x7(uint8_t *dstp
, int width
,
359 float rdiv
, float bias
, const int *const matrix
,
360 const uint8_t *c
[], int peak
, int radius
,
361 int dstride
, int stride
, int size
)
363 uint16_t *dst
= (uint16_t *)dstp
;
366 for (x
= 0; x
< width
; x
++) {
369 for (i
= 0; i
< 49; i
++)
370 sum
+= AV_RN16A(&c
[i
][2 * x
]) * matrix
[i
];
372 sum
= (int)(sum
* rdiv
+ bias
+ 0.5f
);
373 dst
[x
] = av_clip(sum
, 0, peak
);
377 static void filter16_row(uint8_t *dstp
, int width
,
378 float rdiv
, float bias
, const int *const matrix
,
379 const uint8_t *c
[], int peak
, int radius
,
380 int dstride
, int stride
, int size
)
382 uint16_t *dst
= (uint16_t *)dstp
;
385 for (x
= 0; x
< width
; x
++) {
388 for (i
= 0; i
< 2 * radius
+ 1; i
++)
389 sum
+= AV_RN16A(&c
[i
][2 * x
]) * matrix
[i
];
391 sum
= (int)(sum
* rdiv
+ bias
+ 0.5f
);
392 dst
[x
] = av_clip(sum
, 0, peak
);
396 static void filter16_column(uint8_t *dstp
, int height
,
397 float rdiv
, float bias
, const int *const matrix
,
398 const uint8_t *c
[], int peak
, int radius
,
399 int dstride
, int stride
, int size
)
401 DECLARE_ALIGNED(64, int, sum
)[16];
402 uint16_t *dst
= (uint16_t *)dstp
;
403 const int width
= FFMIN(16, size
);
405 for (int y
= 0; y
< height
; y
++) {
407 memset(sum
, 0, sizeof(sum
));
408 for (int i
= 0; i
< 2 * radius
+ 1; i
++) {
409 for (int off16
= 0; off16
< width
; off16
++)
410 sum
[off16
] += AV_RN16A(&c
[i
][0 + y
* stride
+ off16
* 2]) * matrix
[i
];
413 for (int off16
= 0; off16
< width
; off16
++) {
414 sum
[off16
] = (int)(sum
[off16
] * rdiv
+ bias
+ 0.5f
);
415 dst
[off16
] = av_clip(sum
[off16
], 0, peak
);
421 static void filter_7x7(uint8_t *dst
, int width
,
422 float rdiv
, float bias
, const int *const matrix
,
423 const uint8_t *c
[], int peak
, int radius
,
424 int dstride
, int stride
, int size
)
428 for (x
= 0; x
< width
; x
++) {
431 for (i
= 0; i
< 49; i
++)
432 sum
+= c
[i
][x
] * matrix
[i
];
434 sum
= (int)(sum
* rdiv
+ bias
+ 0.5f
);
435 dst
[x
] = av_clip_uint8(sum
);
439 static void filter_5x5(uint8_t *dst
, int width
,
440 float rdiv
, float bias
, const int *const matrix
,
441 const uint8_t *c
[], int peak
, int radius
,
442 int dstride
, int stride
, int size
)
446 for (x
= 0; x
< width
; x
++) {
449 for (i
= 0; i
< 25; i
++)
450 sum
+= c
[i
][x
] * matrix
[i
];
452 sum
= (int)(sum
* rdiv
+ bias
+ 0.5f
);
453 dst
[x
] = av_clip_uint8(sum
);
457 static void filter_3x3(uint8_t *dst
, int width
,
458 float rdiv
, float bias
, const int *const matrix
,
459 const uint8_t *c
[], int peak
, int radius
,
460 int dstride
, int stride
, int size
)
462 const uint8_t *c0
= c
[0], *c1
= c
[1], *c2
= c
[2];
463 const uint8_t *c3
= c
[3], *c4
= c
[4], *c5
= c
[5];
464 const uint8_t *c6
= c
[6], *c7
= c
[7], *c8
= c
[8];
467 for (x
= 0; x
< width
; x
++) {
468 int sum
= c0
[x
] * matrix
[0] + c1
[x
] * matrix
[1] + c2
[x
] * matrix
[2] +
469 c3
[x
] * matrix
[3] + c4
[x
] * matrix
[4] + c5
[x
] * matrix
[5] +
470 c6
[x
] * matrix
[6] + c7
[x
] * matrix
[7] + c8
[x
] * matrix
[8];
471 sum
= (int)(sum
* rdiv
+ bias
+ 0.5f
);
472 dst
[x
] = av_clip_uint8(sum
);
476 static void filter_row(uint8_t *dst
, int width
,
477 float rdiv
, float bias
, const int *const matrix
,
478 const uint8_t *c
[], int peak
, int radius
,
479 int dstride
, int stride
, int size
)
483 for (x
= 0; x
< width
; x
++) {
486 for (i
= 0; i
< 2 * radius
+ 1; i
++)
487 sum
+= c
[i
][x
] * matrix
[i
];
489 sum
= (int)(sum
* rdiv
+ bias
+ 0.5f
);
490 dst
[x
] = av_clip_uint8(sum
);
494 static void filter_column(uint8_t *dst
, int height
,
495 float rdiv
, float bias
, const int *const matrix
,
496 const uint8_t *c
[], int peak
, int radius
,
497 int dstride
, int stride
, int size
)
499 DECLARE_ALIGNED(64, int, sum
)[16];
501 for (int y
= 0; y
< height
; y
++) {
502 memset(sum
, 0, sizeof(sum
));
504 for (int i
= 0; i
< 2 * radius
+ 1; i
++) {
505 for (int off16
= 0; off16
< 16; off16
++)
506 sum
[off16
] += c
[i
][0 + y
* stride
+ off16
] * matrix
[i
];
509 for (int off16
= 0; off16
< 16; off16
++) {
510 sum
[off16
] = (int)(sum
[off16
] * rdiv
+ bias
+ 0.5f
);
511 dst
[off16
] = av_clip_uint8(sum
[off16
]);
517 static void setup_5x5(int radius
, const uint8_t *c
[], const uint8_t *src
, int stride
,
518 int x
, int w
, int y
, int h
, int bpc
)
522 for (i
= 0; i
< 25; i
++) {
523 int xoff
= FFABS(x
+ ((i
% 5) - 2));
524 int yoff
= FFABS(y
+ (i
/ 5) - 2);
526 xoff
= xoff
>= w
? 2 * w
- 1 - xoff
: xoff
;
527 yoff
= yoff
>= h
? 2 * h
- 1 - yoff
: yoff
;
529 c
[i
] = src
+ xoff
* bpc
+ yoff
* stride
;
533 static void setup_7x7(int radius
, const uint8_t *c
[], const uint8_t *src
, int stride
,
534 int x
, int w
, int y
, int h
, int bpc
)
538 for (i
= 0; i
< 49; i
++) {
539 int xoff
= FFABS(x
+ ((i
% 7) - 3));
540 int yoff
= FFABS(y
+ (i
/ 7) - 3);
542 xoff
= xoff
>= w
? 2 * w
- 1 - xoff
: xoff
;
543 yoff
= yoff
>= h
? 2 * h
- 1 - yoff
: yoff
;
545 c
[i
] = src
+ xoff
* bpc
+ yoff
* stride
;
549 static void setup_row(int radius
, const uint8_t *c
[], const uint8_t *src
, int stride
,
550 int x
, int w
, int y
, int h
, int bpc
)
554 for (i
= 0; i
< radius
* 2 + 1; i
++) {
555 int xoff
= FFABS(x
+ i
- radius
);
557 xoff
= xoff
>= w
? 2 * w
- 1 - xoff
: xoff
;
559 c
[i
] = src
+ xoff
* bpc
+ y
* stride
;
563 static void setup_column(int radius
, const uint8_t *c
[], const uint8_t *src
, int stride
,
564 int x
, int w
, int y
, int h
, int bpc
)
568 for (i
= 0; i
< radius
* 2 + 1; i
++) {
569 int xoff
= FFABS(x
+ i
- radius
);
571 xoff
= xoff
>= h
? 2 * h
- 1 - xoff
: xoff
;
573 c
[i
] = src
+ y
* bpc
+ xoff
* stride
;
577 static int filter_slice(AVFilterContext
*ctx
, void *arg
, int jobnr
, int nb_jobs
)
579 ConvolutionContext
*s
= ctx
->priv
;
580 ThreadData
*td
= arg
;
581 AVFrame
*in
= td
->in
;
582 AVFrame
*out
= td
->out
;
585 for (plane
= 0; plane
< s
->nb_planes
; plane
++) {
586 const int mode
= s
->mode
[plane
];
587 const int bpc
= s
->bpc
;
588 const int radius
= s
->size
[plane
] / 2;
589 const int height
= s
->planeheight
[plane
];
590 const int width
= s
->planewidth
[plane
];
591 const int stride
= in
->linesize
[plane
];
592 const int dstride
= out
->linesize
[plane
];
593 const int sizeh
= mode
== MATRIX_COLUMN
? width
: height
;
594 const int sizew
= mode
== MATRIX_COLUMN
? height
: width
;
595 const int slice_start
= (sizeh
* jobnr
) / nb_jobs
;
596 const int slice_end
= (sizeh
* (jobnr
+1)) / nb_jobs
;
597 const float rdiv
= s
->rdiv
[plane
];
598 const float bias
= s
->bias
[plane
];
599 const uint8_t *src
= in
->data
[plane
];
600 const int dst_pos
= slice_start
* (mode
== MATRIX_COLUMN
? bpc
: dstride
);
601 uint8_t *dst
= out
->data
[plane
] + dst_pos
;
602 const int *matrix
= s
->matrix
[plane
];
603 const int step
= mode
== MATRIX_COLUMN
? 16 : 1;
604 const uint8_t *c
[49];
607 if (s
->copy
[plane
]) {
608 if (mode
== MATRIX_COLUMN
)
609 av_image_copy_plane(dst
, dstride
, src
+ slice_start
* bpc
, stride
,
610 (slice_end
- slice_start
) * bpc
, height
);
612 av_image_copy_plane(dst
, dstride
, src
+ slice_start
* stride
, stride
,
613 width
* bpc
, slice_end
- slice_start
);
616 for (y
= slice_start
; y
< slice_end
; y
+= step
) {
617 const int xoff
= mode
== MATRIX_COLUMN
? (y
- slice_start
) * bpc
: radius
* bpc
;
618 const int yoff
= mode
== MATRIX_COLUMN
? radius
* dstride
: 0;
620 for (x
= 0; x
< radius
; x
++) {
621 const int xoff
= mode
== MATRIX_COLUMN
? (y
- slice_start
) * bpc
: x
* bpc
;
622 const int yoff
= mode
== MATRIX_COLUMN
? x
* dstride
: 0;
624 s
->setup
[plane
](radius
, c
, src
, stride
, x
, width
, y
, height
, bpc
);
625 s
->filter
[plane
](dst
+ yoff
+ xoff
, 1, rdiv
,
626 bias
, matrix
, c
, s
->max
, radius
,
627 dstride
, stride
, slice_end
- step
);
629 s
->setup
[plane
](radius
, c
, src
, stride
, radius
, width
, y
, height
, bpc
);
630 s
->filter
[plane
](dst
+ yoff
+ xoff
, sizew
- 2 * radius
,
631 rdiv
, bias
, matrix
, c
, s
->max
, radius
,
632 dstride
, stride
, slice_end
- step
);
633 for (x
= sizew
- radius
; x
< sizew
; x
++) {
634 const int xoff
= mode
== MATRIX_COLUMN
? (y
- slice_start
) * bpc
: x
* bpc
;
635 const int yoff
= mode
== MATRIX_COLUMN
? x
* dstride
: 0;
637 s
->setup
[plane
](radius
, c
, src
, stride
, x
, width
, y
, height
, bpc
);
638 s
->filter
[plane
](dst
+ yoff
+ xoff
, 1, rdiv
,
639 bias
, matrix
, c
, s
->max
, radius
,
640 dstride
, stride
, slice_end
- step
);
642 if (mode
!= MATRIX_COLUMN
)
650 static int param_init(AVFilterContext
*ctx
)
652 ConvolutionContext
*s
= ctx
->priv
;
653 AVFilterLink
*inlink
= ctx
->inputs
[0];
654 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
657 s
->depth
= desc
->comp
[0].depth
;
658 s
->max
= (1 << s
->depth
) - 1;
660 s
->planewidth
[1] = s
->planewidth
[2] = AV_CEIL_RSHIFT(inlink
->w
, desc
->log2_chroma_w
);
661 s
->planewidth
[0] = s
->planewidth
[3] = inlink
->w
;
662 s
->planeheight
[1] = s
->planeheight
[2] = AV_CEIL_RSHIFT(inlink
->h
, desc
->log2_chroma_h
);
663 s
->planeheight
[0] = s
->planeheight
[3] = inlink
->h
;
665 s
->nb_planes
= av_pix_fmt_count_planes(inlink
->format
);
666 s
->nb_threads
= ff_filter_get_nb_threads(ctx
);
667 s
->bpc
= (s
->depth
+ 7) / 8;
669 if (!strcmp(ctx
->filter
->name
, "convolution")) {
670 for (i
= 0; i
< 4; i
++) {
671 int *matrix
= (int *)s
->matrix
[i
];
672 char *orig
, *p
, *arg
, *saveptr
= NULL
;
675 p
= orig
= av_strdup(s
->matrix_str
[i
]);
677 s
->matrix_length
[i
] = 0;
678 s
->rdiv
[i
] = s
->user_rdiv
[i
];
681 while (s
->matrix_length
[i
] < 49) {
682 if (!(arg
= av_strtok(p
, " |", &saveptr
)))
686 sscanf(arg
, "%d", &matrix
[s
->matrix_length
[i
]]);
687 sum
+= matrix
[s
->matrix_length
[i
]];
688 s
->matrix_length
[i
]++;
692 if (!(s
->matrix_length
[i
] & 1)) {
693 av_log(ctx
, AV_LOG_ERROR
, "number of matrix elements must be odd\n");
694 return AVERROR(EINVAL
);
698 if (s
->mode
[i
] == MATRIX_ROW
) {
699 s
->filter
[i
] = filter_row
;
700 s
->setup
[i
] = setup_row
;
701 s
->size
[i
] = s
->matrix_length
[i
];
702 } else if (s
->mode
[i
] == MATRIX_COLUMN
) {
703 s
->filter
[i
] = filter_column
;
704 s
->setup
[i
] = setup_column
;
705 s
->size
[i
] = s
->matrix_length
[i
];
706 } else if (s
->matrix_length
[i
] == 9) {
709 if (!memcmp(matrix
, same3x3
, sizeof(same3x3
))) {
712 s
->filter
[i
] = filter_3x3
;
715 s
->setup
[i
] = setup_3x3
;
716 } else if (s
->matrix_length
[i
] == 25) {
718 if (!memcmp(matrix
, same5x5
, sizeof(same5x5
))) {
721 s
->filter
[i
] = filter_5x5
;
724 s
->setup
[i
] = setup_5x5
;
725 } else if (s
->matrix_length
[i
] == 49) {
727 if (!memcmp(matrix
, same7x7
, sizeof(same7x7
))) {
730 s
->filter
[i
] = filter_7x7
;
733 s
->setup
[i
] = setup_7x7
;
735 return AVERROR(EINVAL
);
741 s
->rdiv
[i
] = 1. / sum
;
743 if (s
->copy
[i
] && (s
->rdiv
[i
] != 1. || s
->bias
[i
] != 0.))
746 } else if (!strcmp(ctx
->filter
->name
, "prewitt")) {
747 for (i
= 0; i
< 4; i
++) {
748 s
->filter
[i
] = filter_prewitt
;
749 s
->copy
[i
] = !((1 << i
) & s
->planes
);
751 s
->setup
[i
] = setup_3x3
;
752 s
->rdiv
[i
] = s
->scale
;
753 s
->bias
[i
] = s
->delta
;
755 } else if (!strcmp(ctx
->filter
->name
, "roberts")) {
756 for (i
= 0; i
< 4; i
++) {
757 s
->filter
[i
] = filter_roberts
;
758 s
->copy
[i
] = !((1 << i
) & s
->planes
);
760 s
->setup
[i
] = setup_3x3
;
761 s
->rdiv
[i
] = s
->scale
;
762 s
->bias
[i
] = s
->delta
;
764 #if CONFIG_SOBEL_FILTER
765 } else if (!strcmp(ctx
->filter
->name
, "sobel")) {
766 ff_sobel_init(s
, s
->depth
, s
->nb_planes
);
768 } else if (!strcmp(ctx
->filter
->name
, "kirsch")) {
769 for (i
= 0; i
< 4; i
++) {
770 s
->filter
[i
] = filter_kirsch
;
771 s
->copy
[i
] = !((1 << i
) & s
->planes
);
773 s
->setup
[i
] = setup_3x3
;
774 s
->rdiv
[i
] = s
->scale
;
775 s
->bias
[i
] = s
->delta
;
777 } else if (!strcmp(ctx
->filter
->name
, "scharr")) {
778 for (i
= 0; i
< 4; i
++) {
779 s
->filter
[i
] = filter_scharr
;
780 s
->copy
[i
] = !((1 << i
) & s
->planes
);
782 s
->setup
[i
] = setup_3x3
;
783 s
->rdiv
[i
] = s
->scale
;
784 s
->bias
[i
] = s
->delta
;
788 if (!strcmp(ctx
->filter
->name
, "convolution")) {
790 for (p
= 0; p
< s
->nb_planes
; p
++) {
791 if (s
->mode
[p
] == MATRIX_ROW
)
792 s
->filter
[p
] = filter16_row
;
793 else if (s
->mode
[p
] == MATRIX_COLUMN
)
794 s
->filter
[p
] = filter16_column
;
795 else if (s
->size
[p
] == 3)
796 s
->filter
[p
] = filter16_3x3
;
797 else if (s
->size
[p
] == 5)
798 s
->filter
[p
] = filter16_5x5
;
799 else if (s
->size
[p
] == 7)
800 s
->filter
[p
] = filter16_7x7
;
803 #if CONFIG_CONVOLUTION_FILTER && ARCH_X86_64
804 ff_convolution_init_x86(s
);
806 } else if (!strcmp(ctx
->filter
->name
, "prewitt")) {
808 for (p
= 0; p
< s
->nb_planes
; p
++)
809 s
->filter
[p
] = filter16_prewitt
;
810 } else if (!strcmp(ctx
->filter
->name
, "roberts")) {
812 for (p
= 0; p
< s
->nb_planes
; p
++)
813 s
->filter
[p
] = filter16_roberts
;
814 } else if (!strcmp(ctx
->filter
->name
, "kirsch")) {
816 for (p
= 0; p
< s
->nb_planes
; p
++)
817 s
->filter
[p
] = filter16_kirsch
;
818 } else if (!strcmp(ctx
->filter
->name
, "scharr")) {
820 for (p
= 0; p
< s
->nb_planes
; p
++)
821 s
->filter
[p
] = filter16_scharr
;
827 static int config_input(AVFilterLink
*inlink
)
829 AVFilterContext
*ctx
= inlink
->dst
;
830 return param_init(ctx
);
833 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
835 AVFilterContext
*ctx
= inlink
->dst
;
836 ConvolutionContext
*s
= ctx
->priv
;
837 AVFilterLink
*outlink
= ctx
->outputs
[0];
841 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
844 return AVERROR(ENOMEM
);
846 av_frame_copy_props(out
, in
);
850 ff_filter_execute(ctx
, filter_slice
, &td
, NULL
,
851 FFMIN3(s
->planeheight
[1], s
->planewidth
[1], s
->nb_threads
));
854 return ff_filter_frame(outlink
, out
);
857 static int process_command(AVFilterContext
*ctx
, const char *cmd
, const char *args
,
858 char *res
, int res_len
, int flags
)
862 ret
= ff_filter_process_command(ctx
, cmd
, args
, res
, res_len
, flags
);
866 return param_init(ctx
);
869 static const AVFilterPad convolution_inputs
[] = {
872 .type
= AVMEDIA_TYPE_VIDEO
,
873 .config_props
= config_input
,
874 .filter_frame
= filter_frame
,
878 #if CONFIG_CONVOLUTION_FILTER
880 const FFFilter ff_vf_convolution
= {
881 .p
.name
= "convolution",
882 .p
.description
= NULL_IF_CONFIG_SMALL("Apply convolution filter."),
883 .p
.priv_class
= &convolution_class
,
884 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
| AVFILTER_FLAG_SLICE_THREADS
,
885 .priv_size
= sizeof(ConvolutionContext
),
886 FILTER_INPUTS(convolution_inputs
),
887 FILTER_OUTPUTS(ff_video_default_filterpad
),
888 FILTER_PIXFMTS_ARRAY(pix_fmts
),
889 .process_command
= process_command
,
892 #endif /* CONFIG_CONVOLUTION_FILTER */
894 static const AVOption common_options
[] = {
895 { "planes", "set planes to filter", OFFSET(planes
), AV_OPT_TYPE_INT
, {.i64
=15}, 0, 15, FLAGS
},
896 { "scale", "set scale", OFFSET(scale
), AV_OPT_TYPE_FLOAT
, {.dbl
=1.0}, 0.0, 65535, FLAGS
},
897 { "delta", "set delta", OFFSET(delta
), AV_OPT_TYPE_FLOAT
, {.dbl
=0}, -65535, 65535, FLAGS
},
901 AVFILTER_DEFINE_CLASS_EXT(common
, "kirsch/prewitt/roberts/scharr/sobel",
904 #if CONFIG_PREWITT_FILTER
906 const FFFilter ff_vf_prewitt
= {
908 .p
.description
= NULL_IF_CONFIG_SMALL("Apply prewitt operator."),
909 .p
.priv_class
= &common_class
,
910 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
| AVFILTER_FLAG_SLICE_THREADS
,
911 .priv_size
= sizeof(ConvolutionContext
),
912 FILTER_INPUTS(convolution_inputs
),
913 FILTER_OUTPUTS(ff_video_default_filterpad
),
914 FILTER_PIXFMTS_ARRAY(pix_fmts
),
915 .process_command
= process_command
,
918 #endif /* CONFIG_PREWITT_FILTER */
920 #if CONFIG_SOBEL_FILTER
922 const FFFilter ff_vf_sobel
= {
924 .p
.description
= NULL_IF_CONFIG_SMALL("Apply sobel operator."),
925 .p
.priv_class
= &common_class
,
926 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
| AVFILTER_FLAG_SLICE_THREADS
,
927 .priv_size
= sizeof(ConvolutionContext
),
928 FILTER_INPUTS(convolution_inputs
),
929 FILTER_OUTPUTS(ff_video_default_filterpad
),
930 FILTER_PIXFMTS_ARRAY(pix_fmts
),
931 .process_command
= process_command
,
934 #endif /* CONFIG_SOBEL_FILTER */
936 #if CONFIG_ROBERTS_FILTER
938 const FFFilter ff_vf_roberts
= {
940 .p
.description
= NULL_IF_CONFIG_SMALL("Apply roberts cross operator."),
941 .p
.priv_class
= &common_class
,
942 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
| AVFILTER_FLAG_SLICE_THREADS
,
943 .priv_size
= sizeof(ConvolutionContext
),
944 FILTER_INPUTS(convolution_inputs
),
945 FILTER_OUTPUTS(ff_video_default_filterpad
),
946 FILTER_PIXFMTS_ARRAY(pix_fmts
),
947 .process_command
= process_command
,
950 #endif /* CONFIG_ROBERTS_FILTER */
952 #if CONFIG_KIRSCH_FILTER
954 const FFFilter ff_vf_kirsch
= {
956 .p
.description
= NULL_IF_CONFIG_SMALL("Apply kirsch operator."),
957 .p
.priv_class
= &common_class
,
958 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
| AVFILTER_FLAG_SLICE_THREADS
,
959 .priv_size
= sizeof(ConvolutionContext
),
960 FILTER_INPUTS(convolution_inputs
),
961 FILTER_OUTPUTS(ff_video_default_filterpad
),
962 FILTER_PIXFMTS_ARRAY(pix_fmts
),
963 .process_command
= process_command
,
966 #endif /* CONFIG_KIRSCH_FILTER */
968 #if CONFIG_SCHARR_FILTER
970 const FFFilter ff_vf_scharr
= {
972 .p
.description
= NULL_IF_CONFIG_SMALL("Apply scharr operator."),
973 .p
.priv_class
= &common_class
,
974 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
| AVFILTER_FLAG_SLICE_THREADS
,
975 .priv_size
= sizeof(ConvolutionContext
),
976 FILTER_INPUTS(convolution_inputs
),
977 FILTER_OUTPUTS(ff_video_default_filterpad
),
978 FILTER_PIXFMTS_ARRAY(pix_fmts
),
979 .process_command
= process_command
,
982 #endif /* CONFIG_SCHARR_FILTER */