2 * Copyright (c) 2010 Stefano Sabatini
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 * libopencv wrapper functions
26 #include <opencv/cv.h>
27 #include <opencv/cxcore.h>
28 #include "libavutil/avstring.h"
29 #include "libavutil/common.h"
30 #include "libavutil/file.h"
31 #include "libavutil/opt.h"
37 static void fill_iplimage_from_frame(IplImage
*img
, const AVFrame
*frame
, enum AVPixelFormat pixfmt
)
40 int depth
, channels_nb
;
42 if (pixfmt
== AV_PIX_FMT_GRAY8
) { depth
= IPL_DEPTH_8U
; channels_nb
= 1; }
43 else if (pixfmt
== AV_PIX_FMT_BGRA
) { depth
= IPL_DEPTH_8U
; channels_nb
= 4; }
44 else if (pixfmt
== AV_PIX_FMT_BGR24
) { depth
= IPL_DEPTH_8U
; channels_nb
= 3; }
47 tmpimg
= cvCreateImageHeader((CvSize
){frame
->width
, frame
->height
}, depth
, channels_nb
);
49 img
->imageData
= img
->imageDataOrigin
= frame
->data
[0];
50 img
->dataOrder
= IPL_DATA_ORDER_PIXEL
;
51 img
->origin
= IPL_ORIGIN_TL
;
52 img
->widthStep
= frame
->linesize
[0];
55 static void fill_frame_from_iplimage(AVFrame
*frame
, const IplImage
*img
, enum AVPixelFormat pixfmt
)
57 frame
->linesize
[0] = img
->widthStep
;
58 frame
->data
[0] = img
->imageData
;
61 static int query_formats(AVFilterContext
*ctx
)
63 static const enum AVPixelFormat pix_fmts
[] = {
64 AV_PIX_FMT_BGR24
, AV_PIX_FMT_BGRA
, AV_PIX_FMT_GRAY8
, AV_PIX_FMT_NONE
67 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
71 typedef struct OCVContext
{
75 int (*init
)(AVFilterContext
*ctx
, const char *args
);
76 void (*uninit
)(AVFilterContext
*ctx
);
77 void (*end_frame_filter
)(AVFilterContext
*ctx
, IplImage
*inimg
, IplImage
*outimg
);
81 typedef struct SmoothContext
{
84 double param3
, param4
;
87 static av_cold
int smooth_init(AVFilterContext
*ctx
, const char *args
)
89 OCVContext
*s
= ctx
->priv
;
90 SmoothContext
*smooth
= s
->priv
;
91 char type_str
[128] = "gaussian";
99 sscanf(args
, "%127[^|]|%d|%d|%lf|%lf", type_str
, &smooth
->param1
, &smooth
->param2
, &smooth
->param3
, &smooth
->param4
);
101 if (!strcmp(type_str
, "blur" )) smooth
->type
= CV_BLUR
;
102 else if (!strcmp(type_str
, "blur_no_scale")) smooth
->type
= CV_BLUR_NO_SCALE
;
103 else if (!strcmp(type_str
, "median" )) smooth
->type
= CV_MEDIAN
;
104 else if (!strcmp(type_str
, "gaussian" )) smooth
->type
= CV_GAUSSIAN
;
105 else if (!strcmp(type_str
, "bilateral" )) smooth
->type
= CV_BILATERAL
;
107 av_log(ctx
, AV_LOG_ERROR
, "Smoothing type '%s' unknown.\n", type_str
);
108 return AVERROR(EINVAL
);
111 if (smooth
->param1
< 0 || !(smooth
->param1
%2)) {
112 av_log(ctx
, AV_LOG_ERROR
,
113 "Invalid value '%d' for param1, it has to be a positive odd number\n",
115 return AVERROR(EINVAL
);
117 if ((smooth
->type
== CV_BLUR
|| smooth
->type
== CV_BLUR_NO_SCALE
|| smooth
->type
== CV_GAUSSIAN
) &&
118 (smooth
->param2
< 0 || (smooth
->param2
&& !(smooth
->param2
%2)))) {
119 av_log(ctx
, AV_LOG_ERROR
,
120 "Invalid value '%d' for param2, it has to be zero or a positive odd number\n",
122 return AVERROR(EINVAL
);
125 av_log(ctx
, AV_LOG_VERBOSE
, "type:%s param1:%d param2:%d param3:%f param4:%f\n",
126 type_str
, smooth
->param1
, smooth
->param2
, smooth
->param3
, smooth
->param4
);
130 static void smooth_end_frame_filter(AVFilterContext
*ctx
, IplImage
*inimg
, IplImage
*outimg
)
132 OCVContext
*s
= ctx
->priv
;
133 SmoothContext
*smooth
= s
->priv
;
134 cvSmooth(inimg
, outimg
, smooth
->type
, smooth
->param1
, smooth
->param2
, smooth
->param3
, smooth
->param4
);
137 static int read_shape_from_file(int *cols
, int *rows
, int **values
, const char *filename
,
140 uint8_t *buf
, *p
, *pend
;
144 if ((ret
= av_file_map(filename
, &buf
, &size
, 0, log_ctx
)) < 0)
147 /* prescan file to get the number of lines and the maximum width */
149 for (i
= 0; i
< size
; i
++) {
150 if (buf
[i
] == '\n') {
151 if (*rows
== INT_MAX
) {
152 av_log(log_ctx
, AV_LOG_ERROR
, "Overflow on the number of rows in the file\n");
153 return AVERROR_INVALIDDATA
;
156 *cols
= FFMAX(*cols
, w
);
158 } else if (w
== INT_MAX
) {
159 av_log(log_ctx
, AV_LOG_ERROR
, "Overflow on the number of columns in the file\n");
160 return AVERROR_INVALIDDATA
;
164 if (*rows
> (SIZE_MAX
/ sizeof(int) / *cols
)) {
165 av_log(log_ctx
, AV_LOG_ERROR
, "File with size %dx%d is too big\n",
167 return AVERROR_INVALIDDATA
;
169 if (!(*values
= av_mallocz_array(sizeof(int) * *rows
, *cols
)))
170 return AVERROR(ENOMEM
);
175 for (i
= 0; i
< *rows
; i
++) {
177 if (p
> pend
|| *p
== '\n') {
181 (*values
)[*cols
*i
+ j
] = !!av_isgraph(*(p
++));
184 av_file_unmap(buf
, size
);
189 if (!(line
= av_malloc(*cols
+ 1)))
190 return AVERROR(ENOMEM
);
191 for (i
= 0; i
< *rows
; i
++) {
192 for (j
= 0; j
< *cols
; j
++)
193 line
[j
] = (*values
)[i
* *cols
+ j
] ? '@' : ' ';
195 av_log(log_ctx
, AV_LOG_DEBUG
, "%3d: %s\n", i
, line
);
204 static int parse_iplconvkernel(IplConvKernel
**kernel
, char *buf
, void *log_ctx
)
206 char shape_filename
[128] = "", shape_str
[32] = "rect";
207 int cols
= 0, rows
= 0, anchor_x
= 0, anchor_y
= 0, shape
= CV_SHAPE_RECT
;
208 int *values
= NULL
, ret
;
210 sscanf(buf
, "%dx%d+%dx%d/%32[^=]=%127s", &cols
, &rows
, &anchor_x
, &anchor_y
, shape_str
, shape_filename
);
212 if (!strcmp(shape_str
, "rect" )) shape
= CV_SHAPE_RECT
;
213 else if (!strcmp(shape_str
, "cross" )) shape
= CV_SHAPE_CROSS
;
214 else if (!strcmp(shape_str
, "ellipse")) shape
= CV_SHAPE_ELLIPSE
;
215 else if (!strcmp(shape_str
, "custom" )) {
216 shape
= CV_SHAPE_CUSTOM
;
217 if ((ret
= read_shape_from_file(&cols
, &rows
, &values
, shape_filename
, log_ctx
)) < 0)
220 av_log(log_ctx
, AV_LOG_ERROR
,
221 "Shape unspecified or type '%s' unknown.\n", shape_str
);
222 return AVERROR(EINVAL
);
225 if (rows
<= 0 || cols
<= 0) {
226 av_log(log_ctx
, AV_LOG_ERROR
,
227 "Invalid non-positive values for shape size %dx%d\n", cols
, rows
);
228 return AVERROR(EINVAL
);
231 if (anchor_x
< 0 || anchor_y
< 0 || anchor_x
>= cols
|| anchor_y
>= rows
) {
232 av_log(log_ctx
, AV_LOG_ERROR
,
233 "Shape anchor %dx%d is not inside the rectangle with size %dx%d.\n",
234 anchor_x
, anchor_y
, cols
, rows
);
235 return AVERROR(EINVAL
);
238 *kernel
= cvCreateStructuringElementEx(cols
, rows
, anchor_x
, anchor_y
, shape
, values
);
241 return AVERROR(ENOMEM
);
243 av_log(log_ctx
, AV_LOG_VERBOSE
, "Structuring element: w:%d h:%d x:%d y:%d shape:%s\n",
244 rows
, cols
, anchor_x
, anchor_y
, shape_str
);
248 typedef struct DilateContext
{
250 IplConvKernel
*kernel
;
253 static av_cold
int dilate_init(AVFilterContext
*ctx
, const char *args
)
255 OCVContext
*s
= ctx
->priv
;
256 DilateContext
*dilate
= s
->priv
;
257 char default_kernel_str
[] = "3x3+0x0/rect";
259 const char *buf
= args
;
263 kernel_str
= av_get_token(&buf
, "|");
265 kernel_str
= av_strdup(default_kernel_str
);
267 return AVERROR(ENOMEM
);
268 if ((ret
= parse_iplconvkernel(&dilate
->kernel
, kernel_str
, ctx
)) < 0)
272 if (!buf
|| sscanf(buf
, "|%d", &dilate
->nb_iterations
) != 1)
273 dilate
->nb_iterations
= 1;
274 av_log(ctx
, AV_LOG_VERBOSE
, "iterations_nb:%d\n", dilate
->nb_iterations
);
275 if (dilate
->nb_iterations
<= 0) {
276 av_log(ctx
, AV_LOG_ERROR
, "Invalid non-positive value '%d' for nb_iterations\n",
277 dilate
->nb_iterations
);
278 return AVERROR(EINVAL
);
283 static av_cold
void dilate_uninit(AVFilterContext
*ctx
)
285 OCVContext
*s
= ctx
->priv
;
286 DilateContext
*dilate
= s
->priv
;
288 cvReleaseStructuringElement(&dilate
->kernel
);
291 static void dilate_end_frame_filter(AVFilterContext
*ctx
, IplImage
*inimg
, IplImage
*outimg
)
293 OCVContext
*s
= ctx
->priv
;
294 DilateContext
*dilate
= s
->priv
;
295 cvDilate(inimg
, outimg
, dilate
->kernel
, dilate
->nb_iterations
);
298 static void erode_end_frame_filter(AVFilterContext
*ctx
, IplImage
*inimg
, IplImage
*outimg
)
300 OCVContext
*s
= ctx
->priv
;
301 DilateContext
*dilate
= s
->priv
;
302 cvErode(inimg
, outimg
, dilate
->kernel
, dilate
->nb_iterations
);
305 typedef struct OCVFilterEntry
{
308 int (*init
)(AVFilterContext
*ctx
, const char *args
);
309 void (*uninit
)(AVFilterContext
*ctx
);
310 void (*end_frame_filter
)(AVFilterContext
*ctx
, IplImage
*inimg
, IplImage
*outimg
);
313 static OCVFilterEntry ocv_filter_entries
[] = {
314 { "dilate", sizeof(DilateContext
), dilate_init
, dilate_uninit
, dilate_end_frame_filter
},
315 { "erode", sizeof(DilateContext
), dilate_init
, dilate_uninit
, erode_end_frame_filter
},
316 { "smooth", sizeof(SmoothContext
), smooth_init
, NULL
, smooth_end_frame_filter
},
319 static av_cold
int init(AVFilterContext
*ctx
)
321 OCVContext
*s
= ctx
->priv
;
325 av_log(ctx
, AV_LOG_ERROR
, "No libopencv filter name specified\n");
326 return AVERROR(EINVAL
);
328 for (i
= 0; i
< FF_ARRAY_ELEMS(ocv_filter_entries
); i
++) {
329 OCVFilterEntry
*entry
= &ocv_filter_entries
[i
];
330 if (!strcmp(s
->name
, entry
->name
)) {
331 s
->init
= entry
->init
;
332 s
->uninit
= entry
->uninit
;
333 s
->end_frame_filter
= entry
->end_frame_filter
;
335 if (!(s
->priv
= av_mallocz(entry
->priv_size
)))
336 return AVERROR(ENOMEM
);
337 return s
->init(ctx
, s
->params
);
341 av_log(ctx
, AV_LOG_ERROR
, "No libopencv filter named '%s'\n", s
->name
);
342 return AVERROR(EINVAL
);
345 static av_cold
void uninit(AVFilterContext
*ctx
)
347 OCVContext
*s
= ctx
->priv
;
354 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
356 AVFilterContext
*ctx
= inlink
->dst
;
357 OCVContext
*s
= ctx
->priv
;
358 AVFilterLink
*outlink
= inlink
->dst
->outputs
[0];
360 IplImage inimg
, outimg
;
362 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
365 return AVERROR(ENOMEM
);
367 av_frame_copy_props(out
, in
);
369 fill_iplimage_from_frame(&inimg
, in
, inlink
->format
);
370 fill_iplimage_from_frame(&outimg
, out
, inlink
->format
);
371 s
->end_frame_filter(ctx
, &inimg
, &outimg
);
372 fill_frame_from_iplimage(out
, &outimg
, inlink
->format
);
376 return ff_filter_frame(outlink
, out
);
379 #define OFFSET(x) offsetof(OCVContext, x)
380 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
381 static const AVOption ocv_options
[] = {
382 { "filter_name", NULL
, OFFSET(name
), AV_OPT_TYPE_STRING
, .flags
= FLAGS
},
383 { "filter_params", NULL
, OFFSET(params
), AV_OPT_TYPE_STRING
, .flags
= FLAGS
},
387 AVFILTER_DEFINE_CLASS(ocv
);
389 static const AVFilterPad avfilter_vf_ocv_inputs
[] = {
392 .type
= AVMEDIA_TYPE_VIDEO
,
393 .filter_frame
= filter_frame
,
398 static const AVFilterPad avfilter_vf_ocv_outputs
[] = {
401 .type
= AVMEDIA_TYPE_VIDEO
,
406 AVFilter ff_vf_ocv
= {
408 .description
= NULL_IF_CONFIG_SMALL("Apply transform using libopencv."),
409 .priv_size
= sizeof(OCVContext
),
410 .priv_class
= &ocv_class
,
411 .query_formats
= query_formats
,
414 .inputs
= avfilter_vf_ocv_inputs
,
415 .outputs
= avfilter_vf_ocv_outputs
,