• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2010 Stefano Sabatini
3  *
4  * This file is part of FFmpeg.
5  *
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.
10  *
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.
15  *
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
19  */
20 
21 /**
22  * @file
23  * libopencv wrapper functions
24  */
25 
26 #include "config.h"
27 #if HAVE_OPENCV2_CORE_CORE_C_H
28 #include <opencv2/core/core_c.h>
29 #include <opencv2/imgproc/imgproc_c.h>
30 #else
31 #include <opencv/cv.h>
32 #include <opencv/cxcore.h>
33 #endif
34 #include "libavutil/avstring.h"
35 #include "libavutil/common.h"
36 #include "libavutil/file.h"
37 #include "libavutil/opt.h"
38 #include "avfilter.h"
39 #include "formats.h"
40 #include "internal.h"
41 #include "video.h"
42 
fill_iplimage_from_frame(IplImage * img,const AVFrame * frame,enum AVPixelFormat pixfmt)43 static void fill_iplimage_from_frame(IplImage *img, const AVFrame *frame, enum AVPixelFormat pixfmt)
44 {
45     IplImage *tmpimg;
46     int depth, channels_nb;
47 
48     if      (pixfmt == AV_PIX_FMT_GRAY8) { depth = IPL_DEPTH_8U;  channels_nb = 1; }
49     else if (pixfmt == AV_PIX_FMT_BGRA)  { depth = IPL_DEPTH_8U;  channels_nb = 4; }
50     else if (pixfmt == AV_PIX_FMT_BGR24) { depth = IPL_DEPTH_8U;  channels_nb = 3; }
51     else return;
52 
53     tmpimg = cvCreateImageHeader((CvSize){frame->width, frame->height}, depth, channels_nb);
54     *img = *tmpimg;
55     img->imageData = img->imageDataOrigin = frame->data[0];
56     img->dataOrder = IPL_DATA_ORDER_PIXEL;
57     img->origin    = IPL_ORIGIN_TL;
58     img->widthStep = frame->linesize[0];
59 }
60 
fill_frame_from_iplimage(AVFrame * frame,const IplImage * img,enum AVPixelFormat pixfmt)61 static void fill_frame_from_iplimage(AVFrame *frame, const IplImage *img, enum AVPixelFormat pixfmt)
62 {
63     frame->linesize[0] = img->widthStep;
64     frame->data[0]     = img->imageData;
65 }
66 
67 typedef struct OCVContext {
68     const AVClass *class;
69     char *name;
70     char *params;
71     int (*init)(AVFilterContext *ctx, const char *args);
72     void (*uninit)(AVFilterContext *ctx);
73     void (*end_frame_filter)(AVFilterContext *ctx, IplImage *inimg, IplImage *outimg);
74     void *priv;
75 } OCVContext;
76 
77 typedef struct SmoothContext {
78     int type;
79     int    param1, param2;
80     double param3, param4;
81 } SmoothContext;
82 
smooth_init(AVFilterContext * ctx,const char * args)83 static av_cold int smooth_init(AVFilterContext *ctx, const char *args)
84 {
85     OCVContext *s = ctx->priv;
86     SmoothContext *smooth = s->priv;
87     char type_str[128] = "gaussian";
88 
89     smooth->param1 = 3;
90     smooth->param2 = 0;
91     smooth->param3 = 0.0;
92     smooth->param4 = 0.0;
93 
94     if (args)
95         sscanf(args, "%127[^|]|%d|%d|%lf|%lf", type_str, &smooth->param1, &smooth->param2, &smooth->param3, &smooth->param4);
96 
97     if      (!strcmp(type_str, "blur"         )) smooth->type = CV_BLUR;
98     else if (!strcmp(type_str, "blur_no_scale")) smooth->type = CV_BLUR_NO_SCALE;
99     else if (!strcmp(type_str, "median"       )) smooth->type = CV_MEDIAN;
100     else if (!strcmp(type_str, "gaussian"     )) smooth->type = CV_GAUSSIAN;
101     else if (!strcmp(type_str, "bilateral"    )) smooth->type = CV_BILATERAL;
102     else {
103         av_log(ctx, AV_LOG_ERROR, "Smoothing type '%s' unknown.\n", type_str);
104         return AVERROR(EINVAL);
105     }
106 
107     if (smooth->param1 < 0 || !(smooth->param1%2)) {
108         av_log(ctx, AV_LOG_ERROR,
109                "Invalid value '%d' for param1, it has to be a positive odd number\n",
110                smooth->param1);
111         return AVERROR(EINVAL);
112     }
113     if ((smooth->type == CV_BLUR || smooth->type == CV_BLUR_NO_SCALE || smooth->type == CV_GAUSSIAN) &&
114         (smooth->param2 < 0 || (smooth->param2 && !(smooth->param2%2)))) {
115         av_log(ctx, AV_LOG_ERROR,
116                "Invalid value '%d' for param2, it has to be zero or a positive odd number\n",
117                smooth->param2);
118         return AVERROR(EINVAL);
119     }
120 
121     av_log(ctx, AV_LOG_VERBOSE, "type:%s param1:%d param2:%d param3:%f param4:%f\n",
122            type_str, smooth->param1, smooth->param2, smooth->param3, smooth->param4);
123     return 0;
124 }
125 
smooth_end_frame_filter(AVFilterContext * ctx,IplImage * inimg,IplImage * outimg)126 static void smooth_end_frame_filter(AVFilterContext *ctx, IplImage *inimg, IplImage *outimg)
127 {
128     OCVContext *s = ctx->priv;
129     SmoothContext *smooth = s->priv;
130     cvSmooth(inimg, outimg, smooth->type, smooth->param1, smooth->param2, smooth->param3, smooth->param4);
131 }
132 
read_shape_from_file(int * cols,int * rows,int ** values,const char * filename,void * log_ctx)133 static int read_shape_from_file(int *cols, int *rows, int **values, const char *filename,
134                                 void *log_ctx)
135 {
136     uint8_t *buf, *p, *pend;
137     size_t size;
138     int ret, i, j, w;
139 
140     if ((ret = av_file_map(filename, &buf, &size, 0, log_ctx)) < 0)
141         return ret;
142 
143     /* prescan file to get the number of lines and the maximum width */
144     w = 0;
145     for (i = 0; i < size; i++) {
146         if (buf[i] == '\n') {
147             if (*rows == INT_MAX) {
148                 av_log(log_ctx, AV_LOG_ERROR, "Overflow on the number of rows in the file\n");
149                 ret = AVERROR_INVALIDDATA;
150                 goto end;
151             }
152             ++(*rows);
153             *cols = FFMAX(*cols, w);
154             w = 0;
155         } else if (w == INT_MAX) {
156             av_log(log_ctx, AV_LOG_ERROR, "Overflow on the number of columns in the file\n");
157             return AVERROR_INVALIDDATA;
158         }
159         w++;
160     }
161     if (*rows > (SIZE_MAX / sizeof(int) / *cols)) {
162         av_log(log_ctx, AV_LOG_ERROR, "File with size %dx%d is too big\n",
163                *rows, *cols);
164         ret = AVERROR_INVALIDDATA;
165         goto end;
166     }
167     if (!(*values = av_calloc(sizeof(int) * *rows, *cols))) {
168         ret = AVERROR(ENOMEM);
169         goto end;
170     }
171 
172     /* fill *values */
173     p    = buf;
174     pend = buf + size-1;
175     for (i = 0; i < *rows; i++) {
176         for (j = 0;; j++) {
177             if (p > pend || *p == '\n') {
178                 p++;
179                 break;
180             } else
181                 (*values)[*cols*i + j] = !!av_isgraph(*(p++));
182         }
183     }
184 
185 end:
186     av_file_unmap(buf, size);
187 
188 #ifdef DEBUG
189     {
190         char *line;
191         if (!(line = av_malloc(*cols + 1)))
192             return AVERROR(ENOMEM);
193         for (i = 0; i < *rows; i++) {
194             for (j = 0; j < *cols; j++)
195                 line[j] = (*values)[i * *cols + j] ? '@' : ' ';
196             line[j] = 0;
197             av_log(log_ctx, AV_LOG_DEBUG, "%3d: %s\n", i, line);
198         }
199         av_free(line);
200     }
201 #endif
202 
203     return 0;
204 }
205 
parse_iplconvkernel(IplConvKernel ** kernel,char * buf,void * log_ctx)206 static int parse_iplconvkernel(IplConvKernel **kernel, char *buf, void *log_ctx)
207 {
208     char shape_filename[128] = "", shape_str[32] = "rect";
209     int cols = 0, rows = 0, anchor_x = 0, anchor_y = 0, shape = CV_SHAPE_RECT;
210     int *values = NULL, ret = 0;
211 
212     sscanf(buf, "%dx%d+%dx%d/%32[^=]=%127s", &cols, &rows, &anchor_x, &anchor_y, shape_str, shape_filename);
213 
214     if      (!strcmp(shape_str, "rect"   )) shape = CV_SHAPE_RECT;
215     else if (!strcmp(shape_str, "cross"  )) shape = CV_SHAPE_CROSS;
216     else if (!strcmp(shape_str, "ellipse")) shape = CV_SHAPE_ELLIPSE;
217     else if (!strcmp(shape_str, "custom" )) {
218         shape = CV_SHAPE_CUSTOM;
219         if ((ret = read_shape_from_file(&cols, &rows, &values, shape_filename, log_ctx)) < 0)
220             return ret;
221     } else {
222         av_log(log_ctx, AV_LOG_ERROR,
223                "Shape unspecified or type '%s' unknown.\n", shape_str);
224         ret = AVERROR(EINVAL);
225         goto out;
226     }
227 
228     if (rows <= 0 || cols <= 0) {
229         av_log(log_ctx, AV_LOG_ERROR,
230                "Invalid non-positive values for shape size %dx%d\n", cols, rows);
231         ret = AVERROR(EINVAL);
232         goto out;
233     }
234 
235     if (anchor_x < 0 || anchor_y < 0 || anchor_x >= cols || anchor_y >= rows) {
236         av_log(log_ctx, AV_LOG_ERROR,
237                "Shape anchor %dx%d is not inside the rectangle with size %dx%d.\n",
238                anchor_x, anchor_y, cols, rows);
239         ret = AVERROR(EINVAL);
240         goto out;
241     }
242 
243     *kernel = cvCreateStructuringElementEx(cols, rows, anchor_x, anchor_y, shape, values);
244     if (!*kernel) {
245         ret = AVERROR(ENOMEM);
246         goto out;
247     }
248 
249     av_log(log_ctx, AV_LOG_VERBOSE, "Structuring element: w:%d h:%d x:%d y:%d shape:%s\n",
250            rows, cols, anchor_x, anchor_y, shape_str);
251 out:
252     av_freep(&values);
253     return ret;
254 }
255 
256 typedef struct DilateContext {
257     int nb_iterations;
258     IplConvKernel *kernel;
259 } DilateContext;
260 
dilate_init(AVFilterContext * ctx,const char * args)261 static av_cold int dilate_init(AVFilterContext *ctx, const char *args)
262 {
263     OCVContext *s = ctx->priv;
264     DilateContext *dilate = s->priv;
265     char default_kernel_str[] = "3x3+0x0/rect";
266     char *kernel_str = NULL;
267     const char *buf = args;
268     int ret;
269 
270     if (args) {
271         kernel_str = av_get_token(&buf, "|");
272 
273         if (!kernel_str)
274             return AVERROR(ENOMEM);
275     }
276 
277     ret = parse_iplconvkernel(&dilate->kernel,
278                               (!kernel_str || !*kernel_str) ? default_kernel_str
279                                                             : kernel_str,
280                               ctx);
281     av_free(kernel_str);
282     if (ret < 0)
283         return ret;
284 
285     if (!buf || sscanf(buf, "|%d", &dilate->nb_iterations) != 1)
286         dilate->nb_iterations = 1;
287     av_log(ctx, AV_LOG_VERBOSE, "iterations_nb:%d\n", dilate->nb_iterations);
288     if (dilate->nb_iterations <= 0) {
289         av_log(ctx, AV_LOG_ERROR, "Invalid non-positive value '%d' for nb_iterations\n",
290                dilate->nb_iterations);
291         return AVERROR(EINVAL);
292     }
293     return 0;
294 }
295 
dilate_uninit(AVFilterContext * ctx)296 static av_cold void dilate_uninit(AVFilterContext *ctx)
297 {
298     OCVContext *s = ctx->priv;
299     DilateContext *dilate = s->priv;
300 
301     cvReleaseStructuringElement(&dilate->kernel);
302 }
303 
dilate_end_frame_filter(AVFilterContext * ctx,IplImage * inimg,IplImage * outimg)304 static void dilate_end_frame_filter(AVFilterContext *ctx, IplImage *inimg, IplImage *outimg)
305 {
306     OCVContext *s = ctx->priv;
307     DilateContext *dilate = s->priv;
308     cvDilate(inimg, outimg, dilate->kernel, dilate->nb_iterations);
309 }
310 
erode_end_frame_filter(AVFilterContext * ctx,IplImage * inimg,IplImage * outimg)311 static void erode_end_frame_filter(AVFilterContext *ctx, IplImage *inimg, IplImage *outimg)
312 {
313     OCVContext *s = ctx->priv;
314     DilateContext *dilate = s->priv;
315     cvErode(inimg, outimg, dilate->kernel, dilate->nb_iterations);
316 }
317 
318 typedef struct OCVFilterEntry {
319     const char *name;
320     size_t priv_size;
321     int  (*init)(AVFilterContext *ctx, const char *args);
322     void (*uninit)(AVFilterContext *ctx);
323     void (*end_frame_filter)(AVFilterContext *ctx, IplImage *inimg, IplImage *outimg);
324 } OCVFilterEntry;
325 
326 static const OCVFilterEntry ocv_filter_entries[] = {
327     { "dilate", sizeof(DilateContext), dilate_init, dilate_uninit, dilate_end_frame_filter },
328     { "erode",  sizeof(DilateContext), dilate_init, dilate_uninit, erode_end_frame_filter  },
329     { "smooth", sizeof(SmoothContext), smooth_init, NULL, smooth_end_frame_filter },
330 };
331 
init(AVFilterContext * ctx)332 static av_cold int init(AVFilterContext *ctx)
333 {
334     OCVContext *s = ctx->priv;
335     int i;
336 
337     if (!s->name) {
338         av_log(ctx, AV_LOG_ERROR, "No libopencv filter name specified\n");
339         return AVERROR(EINVAL);
340     }
341     for (i = 0; i < FF_ARRAY_ELEMS(ocv_filter_entries); i++) {
342         const OCVFilterEntry *entry = &ocv_filter_entries[i];
343         if (!strcmp(s->name, entry->name)) {
344             s->init             = entry->init;
345             s->uninit           = entry->uninit;
346             s->end_frame_filter = entry->end_frame_filter;
347 
348             if (!(s->priv = av_mallocz(entry->priv_size)))
349                 return AVERROR(ENOMEM);
350             return s->init(ctx, s->params);
351         }
352     }
353 
354     av_log(ctx, AV_LOG_ERROR, "No libopencv filter named '%s'\n", s->name);
355     return AVERROR(EINVAL);
356 }
357 
uninit(AVFilterContext * ctx)358 static av_cold void uninit(AVFilterContext *ctx)
359 {
360     OCVContext *s = ctx->priv;
361 
362     if (s->uninit)
363         s->uninit(ctx);
364     av_freep(&s->priv);
365 }
366 
filter_frame(AVFilterLink * inlink,AVFrame * in)367 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
368 {
369     AVFilterContext *ctx = inlink->dst;
370     OCVContext *s = ctx->priv;
371     AVFilterLink *outlink= inlink->dst->outputs[0];
372     AVFrame *out;
373     IplImage inimg, outimg;
374 
375     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
376     if (!out) {
377         av_frame_free(&in);
378         return AVERROR(ENOMEM);
379     }
380     av_frame_copy_props(out, in);
381 
382     fill_iplimage_from_frame(&inimg , in , inlink->format);
383     fill_iplimage_from_frame(&outimg, out, inlink->format);
384     s->end_frame_filter(ctx, &inimg, &outimg);
385     fill_frame_from_iplimage(out, &outimg, inlink->format);
386 
387     av_frame_free(&in);
388 
389     return ff_filter_frame(outlink, out);
390 }
391 
392 #define OFFSET(x) offsetof(OCVContext, x)
393 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
394 static const AVOption ocv_options[] = {
395     { "filter_name",   NULL, OFFSET(name),   AV_OPT_TYPE_STRING, .flags = FLAGS },
396     { "filter_params", NULL, OFFSET(params), AV_OPT_TYPE_STRING, .flags = FLAGS },
397     { NULL }
398 };
399 
400 AVFILTER_DEFINE_CLASS(ocv);
401 
402 static const AVFilterPad avfilter_vf_ocv_inputs[] = {
403     {
404         .name         = "default",
405         .type         = AVMEDIA_TYPE_VIDEO,
406         .filter_frame = filter_frame,
407     },
408 };
409 
410 static const AVFilterPad avfilter_vf_ocv_outputs[] = {
411     {
412         .name = "default",
413         .type = AVMEDIA_TYPE_VIDEO,
414     },
415 };
416 
417 const AVFilter ff_vf_ocv = {
418     .name          = "ocv",
419     .description   = NULL_IF_CONFIG_SMALL("Apply transform using libopencv."),
420     .priv_size     = sizeof(OCVContext),
421     .priv_class    = &ocv_class,
422     .init          = init,
423     .uninit        = uninit,
424     FILTER_INPUTS(avfilter_vf_ocv_inputs),
425     FILTER_OUTPUTS(avfilter_vf_ocv_outputs),
426     FILTER_PIXFMTS(AV_PIX_FMT_BGR24, AV_PIX_FMT_BGRA, AV_PIX_FMT_GRAY8),
427 };
428