3c123c573f02e8d073a730a642171e8a24db7ef8
[platform/upstream/gstreamer.git] / ext / opencv / gstdewarp.cpp
1 /*
2  * GStreamer
3  * Copyright (C) 2016 - 2018 Prassel S.r.l
4  *  Author: Nicola Murino <nicola.murino@gmail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  *
24  * Alternatively, the contents of this file may be used under the
25  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
26  * which case the following provisions apply instead of the ones
27  * mentioned above:
28  *
29  * This library is free software; you can redistribute it and/or
30  * modify it under the terms of the GNU Library General Public
31  * License as published by the Free Software Foundation; either
32  * version 2 of the License, or (at your option) any later version.
33  *
34  * This library is distributed in the hope that it will be useful,
35  * but WITHOUT ANY WARRANTY; without even the implied warranty of
36  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
37  * Library General Public License for more details.
38  *
39  * You should have received a copy of the GNU Library General Public
40  * License along with this library; if not, write to the
41  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
42  * Boston, MA 02110-1301, USA.
43  */
44
45 /**
46  * SECTION:element-dewarp
47  *
48  * Dewarp fisheye images
49  *
50  * ## Example launch line
51  *
52  * |[
53  * gst-launch-1.0 videotestsrc ! videoconvert ! circle radius=0.1 height=80  ! dewarp outer-radius=0.35 inner-radius=0.1 ! videoconvert ! xvimagesink
54  * ]|
55  */
56
57
58 #ifdef HAVE_CONFIG_H
59 #include <config.h>
60 #endif
61
62 #include "gstdewarp.h"
63 #include <math.h>
64
65 GST_DEBUG_CATEGORY_STATIC (gst_dewarp_debug);
66 #define GST_CAT_DEFAULT gst_dewarp_debug
67
68 enum
69 {
70   PROP_0,
71   PROP_X_CENTER,
72   PROP_Y_CENTER,
73   PROP_INNER_RADIUS,
74   PROP_OUTER_RADIUS,
75   PROP_REMAP_X_CORRECTION,
76   PROP_REMAP_Y_CORRECTION,
77   PROP_DISPLAY_MODE,
78   PROP_INTERPOLATION_MODE
79 };
80
81 #define DEFAULT_CENTER                  0.5
82 #define DEFAULT_RADIUS                  0.0
83 #define DEFAULT_REMAP_CORRECTION        1.0
84
85 #define GST_TYPE_DEWARP_DISPLAY_MODE (dewarp_display_mode_get_type ())
86
87 static GType
88 dewarp_display_mode_get_type (void)
89 {
90   static GType dewarp_display_mode_type = 0;
91   static const GEnumValue dewarp_display_mode[] = {
92     {GST_DEWARP_DISPLAY_PANORAMA, "Single panorama image", "single-panorama"},
93     {GST_DEWARP_DISPLAY_DOUBLE_PANORAMA, "Dewarped image is splitted in two "
94           "images displayed one below the other", "double-panorama"},
95     {GST_DEWARP_DISPLAY_QUAD_VIEW, "Dewarped image is splitted in four images "
96           "dysplayed as a quad view",
97         "quad-view"},
98     {0, NULL, NULL},
99   };
100
101   if (!dewarp_display_mode_type) {
102     dewarp_display_mode_type =
103         g_enum_register_static ("GstDewarpDisplayMode", dewarp_display_mode);
104   }
105   return dewarp_display_mode_type;
106 }
107
108 #define GST_TYPE_DEWARP_INTERPOLATION_MODE (dewarp_interpolation_mode_get_type ())
109
110 static GType
111 dewarp_interpolation_mode_get_type (void)
112 {
113   static GType dewarp_interpolation_mode_type = 0;
114   static const GEnumValue dewarp_interpolation_mode[] = {
115     {GST_DEWARP_INTER_NEAREST, "A nearest-neighbor interpolation", "nearest"},
116     {GST_DEWARP_INTER_LINEAR, "A bilinear interpolation", "bilinear"},
117     {GST_DEWARP_INTER_CUBIC,
118         "A bicubic interpolation over 4x4 pixel neighborhood", "bicubic"},
119     {GST_DEWARP_INTER_LANCZOS4,
120         "A Lanczos interpolation over 8x8 pixel neighborhood", "Lanczos"},
121     {0, NULL, NULL},
122   };
123
124   if (!dewarp_interpolation_mode_type) {
125     dewarp_interpolation_mode_type =
126         g_enum_register_static ("GstDewarpInterpolationMode",
127         dewarp_interpolation_mode);
128   }
129   return dewarp_interpolation_mode_type;
130 }
131
132 G_DEFINE_TYPE (GstDewarp, gst_dewarp, GST_TYPE_OPENCV_VIDEO_FILTER);
133
134 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
135     GST_PAD_SINK,
136     GST_PAD_ALWAYS,
137     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGBA")));
138
139 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
140     GST_PAD_SRC,
141     GST_PAD_ALWAYS,
142     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGBA")));
143
144 static void gst_dewarp_set_property (GObject * object, guint prop_id,
145     const GValue * value, GParamSpec * pspec);
146 static void gst_dewarp_get_property (GObject * object, guint prop_id,
147     GValue * value, GParamSpec * pspec);
148
149 static GstCaps *gst_dewarp_transform_caps (GstBaseTransform * trans,
150     GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps);
151
152 static GstFlowReturn gst_dewarp_transform_frame (GstOpencvVideoFilter * btrans,
153     GstBuffer * buffer, cv::Mat img, GstBuffer * outbuf, cv::Mat outimg);
154
155 static gboolean gst_dewarp_set_caps (GstOpencvVideoFilter * filter,
156     gint in_width, gint in_height, int in_cv_type,
157     gint out_width, gint out_height, int out_cv_type);
158
159 static void
160 gst_dewarp_finalize (GObject * obj)
161 {
162   GstDewarp *filter = GST_DEWARP (obj);
163
164   filter->map_x.release ();
165   filter->map_y.release ();
166
167   G_OBJECT_CLASS (gst_dewarp_parent_class)->finalize (obj);
168 }
169
170 static void
171 gst_dewarp_class_init (GstDewarpClass * klass)
172 {
173   GObjectClass *gobject_class;
174   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
175   GstBaseTransformClass *basesrc_class = GST_BASE_TRANSFORM_CLASS (klass);
176   GstOpencvVideoFilterClass *cvfilter_class =
177       (GstOpencvVideoFilterClass *) klass;
178
179   gobject_class = (GObjectClass *) klass;
180
181   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_dewarp_finalize);
182   gobject_class->set_property = gst_dewarp_set_property;
183   gobject_class->get_property = gst_dewarp_get_property;
184
185   basesrc_class->transform_caps = GST_DEBUG_FUNCPTR (gst_dewarp_transform_caps);
186   basesrc_class->transform_ip_on_passthrough = FALSE;
187   basesrc_class->passthrough_on_same_caps = TRUE;
188
189   cvfilter_class->cv_trans_func =
190       GST_DEBUG_FUNCPTR (gst_dewarp_transform_frame);
191   cvfilter_class->cv_set_caps = GST_DEBUG_FUNCPTR (gst_dewarp_set_caps);
192
193   g_object_class_install_property (gobject_class, PROP_X_CENTER,
194       g_param_spec_double ("x-center", "x center",
195           "X axis center of the fisheye image",
196           0.0, 1.0, DEFAULT_CENTER,
197           (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
198               G_PARAM_STATIC_STRINGS)));
199
200   g_object_class_install_property (gobject_class, PROP_Y_CENTER,
201       g_param_spec_double ("y-center", "y center",
202           "Y axis center of the fisheye image",
203           0.0, 1.0, DEFAULT_CENTER,
204           (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
205               G_PARAM_STATIC_STRINGS)));
206
207   g_object_class_install_property (gobject_class, PROP_INNER_RADIUS,
208       g_param_spec_double ("inner-radius", "inner radius",
209           "Inner radius of the fisheye image donut. If outer radius <= inner "
210           "radius the element will work in passthrough mode",
211           0.0, 1.0, DEFAULT_RADIUS,
212           (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
213               G_PARAM_STATIC_STRINGS)));
214
215   g_object_class_install_property (gobject_class, PROP_OUTER_RADIUS,
216       g_param_spec_double ("outer-radius", "outer radius",
217           "Outer radius of the fisheye image donut. If outer radius <= inner "
218           "radius the element will work in passthrough mode",
219           0.0, 1.0, DEFAULT_RADIUS,
220           (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
221               G_PARAM_STATIC_STRINGS)));
222
223   g_object_class_install_property (gobject_class, PROP_REMAP_X_CORRECTION,
224       g_param_spec_double ("x-remap-correction", "x remap correction",
225           "Correction factor for remapping on x axis. A correction is needed if "
226           "the fisheye image is not inside a circle",
227           0.1, 10.0, DEFAULT_REMAP_CORRECTION,
228           (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
229               G_PARAM_STATIC_STRINGS)));
230
231   g_object_class_install_property (gobject_class, PROP_REMAP_Y_CORRECTION,
232       g_param_spec_double ("y-remap-correction", "y remap correction",
233           "Correction factor for remapping on y axis. A correction is needed if "
234           "the fisheye image is not inside a circle",
235           0.1, 10.0, DEFAULT_REMAP_CORRECTION,
236           (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
237               G_PARAM_STATIC_STRINGS)));
238
239   g_object_class_install_property (gobject_class, PROP_INTERPOLATION_MODE,
240       g_param_spec_enum ("interpolation-method", "Interpolation method",
241           "Interpolation method to use",
242           GST_TYPE_DEWARP_INTERPOLATION_MODE, GST_DEWARP_INTER_LINEAR,
243           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
244
245   g_object_class_install_property (gobject_class, PROP_DISPLAY_MODE,
246       g_param_spec_enum ("display-mode", "Display mode",
247           "How to display the dewarped image",
248           GST_TYPE_DEWARP_DISPLAY_MODE, GST_DEWARP_DISPLAY_PANORAMA,
249           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
250
251   gst_element_class_set_static_metadata (element_class,
252       "Dewarp fisheye images",
253       "Filter/Effect/Video",
254       "Dewarp fisheye images", "Nicola Murino <nicola.murino@gmail.com>");
255
256   gst_element_class_add_static_pad_template (element_class, &src_factory);
257   gst_element_class_add_static_pad_template (element_class, &sink_factory);
258
259 }
260
261 static void
262 gst_dewarp_init (GstDewarp * filter)
263 {
264   filter->x_center = DEFAULT_CENTER;
265   filter->y_center = DEFAULT_CENTER;
266   filter->inner_radius = DEFAULT_RADIUS;
267   filter->outer_radius = DEFAULT_RADIUS;
268   filter->remap_correction_x = DEFAULT_REMAP_CORRECTION;
269   filter->remap_correction_y = DEFAULT_REMAP_CORRECTION;
270   filter->display_mode = GST_DEWARP_DISPLAY_PANORAMA;
271   filter->interpolation_mode = GST_DEWARP_INTER_LINEAR;
272   filter->pad_sink_width = 0;
273   filter->pad_sink_height = 0;
274   filter->in_width = 0;
275   filter->in_height = 0;
276   filter->out_width = 0;
277   filter->out_height = 0;
278   filter->need_map_update = TRUE;
279
280   gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter),
281       FALSE);
282 }
283
284 static void
285 gst_dewarp_set_property (GObject * object, guint prop_id,
286     const GValue * value, GParamSpec * pspec)
287 {
288   gdouble v;
289   gboolean need_reconfigure;
290   int disp_mode;
291   GstDewarp *filter = GST_DEWARP (object);
292
293   need_reconfigure = FALSE;
294
295   GST_OBJECT_LOCK (filter);
296
297   switch (prop_id) {
298     case PROP_X_CENTER:
299       v = g_value_get_double (value);
300       if (v != filter->x_center) {
301         filter->x_center = v;
302         filter->need_map_update = TRUE;
303         need_reconfigure = TRUE;
304         GST_LOG_OBJECT (filter, "x center setted to %f", filter->x_center);
305       }
306       break;
307     case PROP_Y_CENTER:
308       v = g_value_get_double (value);
309       if (v != filter->y_center) {
310         filter->y_center = v;
311         filter->need_map_update = TRUE;
312         need_reconfigure = TRUE;
313         GST_LOG_OBJECT (filter, "y center setted to %f", filter->y_center);
314       }
315       break;
316     case PROP_INNER_RADIUS:
317       v = g_value_get_double (value);
318       if (v != filter->inner_radius) {
319         filter->inner_radius = v;
320         filter->need_map_update = TRUE;
321         need_reconfigure = TRUE;
322         GST_LOG_OBJECT (filter, "inner radius setted to %f",
323             filter->inner_radius);
324       }
325       break;
326     case PROP_OUTER_RADIUS:
327       v = g_value_get_double (value);
328       if (v != filter->outer_radius) {
329         filter->outer_radius = v;
330         filter->need_map_update = TRUE;
331         need_reconfigure = TRUE;
332         GST_LOG_OBJECT (filter, "outer radius setted to %f",
333             filter->outer_radius);
334       }
335       break;
336     case PROP_REMAP_X_CORRECTION:
337       v = g_value_get_double (value);
338       if (v != filter->remap_correction_x) {
339         filter->remap_correction_x = v;
340         filter->need_map_update = TRUE;
341         need_reconfigure = TRUE;
342         GST_LOG_OBJECT (filter, "x remap correction setted to %f",
343             filter->remap_correction_x);
344       }
345       break;
346     case PROP_REMAP_Y_CORRECTION:
347       v = g_value_get_double (value);
348       if (v != filter->remap_correction_y) {
349         filter->remap_correction_y = v;
350         filter->need_map_update = TRUE;
351         need_reconfigure = TRUE;
352         GST_LOG_OBJECT (filter, "y remap correction setted to %f",
353             filter->remap_correction_y);
354       }
355       break;
356     case PROP_INTERPOLATION_MODE:
357       filter->interpolation_mode = g_value_get_enum (value);
358       GST_LOG_OBJECT (filter, "interpolation mode setted to %" G_GINT32_FORMAT,
359           filter->interpolation_mode);
360       break;
361     case PROP_DISPLAY_MODE:
362       disp_mode = g_value_get_enum (value);
363       if (disp_mode != filter->display_mode) {
364         filter->display_mode = disp_mode;
365         need_reconfigure = TRUE;
366         GST_LOG_OBJECT (filter, "display mode setted to %" G_GINT32_FORMAT,
367             filter->display_mode);
368       }
369       break;
370     default:
371       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372       break;
373   }
374
375   if (filter->need_map_update)
376     GST_LOG_OBJECT (filter, "need map update after property change");
377
378   GST_OBJECT_UNLOCK (filter);
379
380   if (need_reconfigure) {
381     GST_DEBUG_OBJECT (filter, "Reconfigure src after property change");
382     gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (filter));
383   } else {
384     GST_DEBUG_OBJECT (filter,
385         "No property value changed, reconfigure src is not" " needed");
386   }
387 }
388
389 static void
390 gst_dewarp_get_property (GObject * object, guint prop_id,
391     GValue * value, GParamSpec * pspec)
392 {
393   GstDewarp *filter = GST_DEWARP (object);
394
395   GST_OBJECT_LOCK (filter);
396
397   switch (prop_id) {
398     case PROP_X_CENTER:
399       g_value_set_double (value, filter->x_center);
400       break;
401     case PROP_Y_CENTER:
402       g_value_set_double (value, filter->y_center);
403       break;
404     case PROP_INNER_RADIUS:
405       g_value_set_double (value, filter->inner_radius);
406       break;
407     case PROP_OUTER_RADIUS:
408       g_value_set_double (value, filter->outer_radius);
409       break;
410     case PROP_REMAP_X_CORRECTION:
411       g_value_set_double (value, filter->remap_correction_x);
412       break;
413     case PROP_REMAP_Y_CORRECTION:
414       g_value_set_double (value, filter->remap_correction_y);
415       break;
416     case PROP_INTERPOLATION_MODE:
417       g_value_set_enum (value, filter->interpolation_mode);
418       break;
419     case PROP_DISPLAY_MODE:
420       g_value_set_enum (value, filter->display_mode);
421       break;
422
423     default:
424       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
425       break;
426   }
427
428   GST_OBJECT_UNLOCK (filter);
429 }
430
431 static void
432 gst_dewarp_update_map (GstDewarp * filter)
433 {
434   gdouble r1, r2, cx, cy;
435   gint x, y;
436   gint out_width, out_height;
437
438   if (filter->display_mode == GST_DEWARP_DISPLAY_PANORAMA) {
439     out_width = filter->out_width;
440     out_height = filter->out_height;
441   } else {
442     out_width = filter->out_width * 2;
443     out_height = filter->out_height / 2;
444   }
445
446   GST_DEBUG_OBJECT (filter,
447       "start update map out_width: %" G_GINT32_FORMAT " out height: %"
448       G_GINT32_FORMAT, out_width, out_height);
449
450   r1 = filter->in_width * filter->inner_radius;
451   r2 = filter->in_width * filter->outer_radius;
452   cx = filter->x_center * filter->in_width;
453   cy = filter->y_center * filter->in_height;
454   cv::Size destSize (out_width, out_height);
455   filter->map_x.create (destSize, CV_32FC1);
456   filter->map_y.create (destSize, CV_32FC1);
457
458   for (y = 0; y < out_height; y++) {
459     for (x = 0; x < out_width; x++) {
460       float r = ((float) (y) / (float) (out_height)) * (r2 - r1) + r1;
461       float theta = ((float) (x) / (float) (out_width)) * 2.0 * G_PI;
462       float xs = cx + r * sin (theta) * filter->remap_correction_x;
463       float ys = cy + r * cos (theta) * filter->remap_correction_y;
464       filter->map_x.at < float >(y, x) = xs;
465       filter->map_y.at < float >(y, x) = ys;
466     }
467   }
468
469   filter->need_map_update = FALSE;
470
471   GST_DEBUG_OBJECT (filter, "update map done");
472 }
473
474 static void
475 gst_dewarp_calculate_dimensions (GstDewarp * filter, GstPadDirection direction,
476     gint in_width, gint in_height, gint * out_width, gint * out_height)
477 {
478   if (filter->outer_radius <= filter->inner_radius) {
479     GST_LOG_OBJECT (filter,
480         "No dimensions conversion required, in width: %" G_GINT32_FORMAT
481         " in height: %" G_GINT32_FORMAT, in_width, in_height);
482     *out_width = in_width;
483     *out_height = in_height;
484   } else {
485     gdouble r1, r2;
486
487     GST_LOG_OBJECT (filter,
488         "Calculate dimensions, in_width: %" G_GINT32_FORMAT
489         " in_height: %" G_GINT32_FORMAT " pad sink width: %" G_GINT32_FORMAT
490         " pad sink height: %" G_GINT32_FORMAT
491         " inner radius: %f, outer radius: %f, direction: %d", in_width,
492         in_height, filter->pad_sink_width, filter->pad_sink_height,
493         filter->inner_radius, filter->outer_radius, direction);
494
495     r1 = in_width * filter->inner_radius;
496     r2 = in_width * filter->outer_radius;
497
498     if (direction == GST_PAD_SINK) {
499       /* roundup is required to have integer results when we divide width, height
500        * in display mode different from GST_DEWARP_PANORAMA.
501        * Additionally some elements such as xvimagesink have problems with arbitrary
502        * dimensions, a roundup solves this issue too
503        */
504       *out_width = GST_ROUND_UP_8 ((gint) ((2.0 * G_PI) * ((r2 + r1) / 2.0)));
505       *out_height = GST_ROUND_UP_8 ((gint) (r2 - r1));
506
507       if (filter->display_mode != GST_DEWARP_DISPLAY_PANORAMA) {
508         *out_width = *out_width / 2;
509         *out_height = *out_height * 2;
510       }
511
512       /* if outer_radius and inner radius are very close then width and height
513          could be 0, we assume passtrough in this case
514        */
515       if (G_UNLIKELY (*out_width == 0) || G_UNLIKELY (*out_height == 0)) {
516         GST_WARNING_OBJECT (filter,
517             "Invalid calculated dimensions, width: %" G_GINT32_FORMAT
518             " height: %" G_GINT32_FORMAT, *out_width, *out_height);
519         *out_width = in_width;
520         *out_height = in_height;
521       }
522       filter->pad_sink_width = in_width;
523       filter->pad_sink_height = in_height;
524     } else {
525       if (filter->pad_sink_width > 0) {
526         *out_width = filter->pad_sink_width;
527       } else {
528         *out_width = in_width;
529       }
530       if (filter->pad_sink_height > 0) {
531         *out_height = filter->pad_sink_height;
532       } else {
533         *out_height = in_height;
534       }
535     }
536   }
537
538   GST_LOG_OBJECT (filter,
539       "Calculated dimensions: width %" G_GINT32_FORMAT " => %" G_GINT32_FORMAT
540       ", height %" G_GINT32_FORMAT " => %" G_GINT32_FORMAT " direction: %d",
541       in_width, *out_width, in_height, *out_height, direction);
542 }
543
544 static GstCaps *
545 gst_dewarp_transform_caps (GstBaseTransform * trans,
546     GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps)
547 {
548   GstDewarp *dewarp = GST_DEWARP (trans);
549
550   GstCaps *ret;
551   gint width, height;
552   guint i;
553
554   ret = gst_caps_copy (caps);
555
556   GST_OBJECT_LOCK (dewarp);
557
558   for (i = 0; i < gst_caps_get_size (ret); i++) {
559     GstStructure *structure = gst_caps_get_structure (ret, i);
560
561     if (gst_structure_get_int (structure, "width", &width) &&
562         gst_structure_get_int (structure, "height", &height)) {
563       gint out_width, out_height;
564       gst_dewarp_calculate_dimensions (dewarp, direction, width, height,
565           &out_width, &out_height);
566       gst_structure_set (structure, "width", G_TYPE_INT, out_width, "height",
567           G_TYPE_INT, out_height, NULL);
568     }
569   }
570
571   GST_OBJECT_UNLOCK (dewarp);
572
573   if (filter_caps) {
574     GstCaps *intersection;
575
576     GST_DEBUG_OBJECT (dewarp, "Using filter caps %" GST_PTR_FORMAT,
577         filter_caps);
578
579     intersection =
580         gst_caps_intersect_full (filter_caps, ret, GST_CAPS_INTERSECT_FIRST);
581     gst_caps_unref (ret);
582     ret = intersection;
583
584     GST_DEBUG_OBJECT (dewarp, "Intersection %" GST_PTR_FORMAT, ret);
585   }
586
587   return ret;
588 }
589
590 static gboolean
591 gst_dewarp_set_caps (GstOpencvVideoFilter * filter,
592     gint in_width, gint in_height, int in_cv_type,
593     gint out_width, gint out_height, int out_cv_type)
594 {
595   GstDewarp *dewarp = GST_DEWARP (filter);
596
597   GST_DEBUG_OBJECT (dewarp,
598       "Set new caps, in width: %" G_GINT32_FORMAT " in height: %"
599       G_GINT32_FORMAT " out width: %" G_GINT32_FORMAT " out height: %"
600       G_GINT32_FORMAT, in_width, in_height, out_width, out_height);
601
602   GST_OBJECT_LOCK (dewarp);
603
604   dewarp->in_width = in_width;
605   dewarp->in_height = in_height;
606   dewarp->out_width = out_width;
607   dewarp->out_height = out_height;
608   gst_dewarp_update_map (dewarp);
609
610   GST_OBJECT_UNLOCK (dewarp);
611
612   return TRUE;
613 }
614
615 static GstFlowReturn
616 gst_dewarp_transform_frame (GstOpencvVideoFilter * btrans, GstBuffer * buffer,
617     cv::Mat img, GstBuffer * outbuf, cv::Mat outimg)
618 {
619   GstDewarp *filter = GST_DEWARP (btrans);
620   GstFlowReturn ret;
621
622   GST_OBJECT_LOCK (filter);
623
624   if (img.size ().width == filter->in_width
625       && img.size ().height == filter->in_height
626       && outimg.size ().width == filter->out_width
627       && outimg.size ().height == filter->out_height) {
628     cv::Mat fisheye_image, dewarped_image;
629     int inter_mode;
630
631     if (filter->need_map_update) {
632       GST_LOG_OBJECT (filter, "map update is needed");
633       gst_dewarp_update_map (filter);
634     }
635
636     switch (filter->interpolation_mode) {
637       case GST_DEWARP_INTER_NEAREST:
638         inter_mode = cv::INTER_NEAREST;
639         break;
640       case GST_DEWARP_INTER_LINEAR:
641         inter_mode = cv::INTER_LINEAR;
642         break;
643       case GST_DEWARP_INTER_CUBIC:
644         inter_mode = cv::INTER_CUBIC;
645         break;
646       case GST_DEWARP_INTER_LANCZOS4:
647         inter_mode = cv::INTER_LANCZOS4;
648         break;
649       default:
650         inter_mode = cv::INTER_LINEAR;
651         break;
652     }
653
654     fisheye_image = img;
655     dewarped_image = outimg;
656
657     if (filter->display_mode == GST_DEWARP_DISPLAY_PANORAMA) {
658       cv::remap (fisheye_image, dewarped_image, filter->map_x, filter->map_y,
659           inter_mode);
660     } else if (filter->display_mode == GST_DEWARP_DISPLAY_DOUBLE_PANORAMA) {
661       cv::Mat view1, view2, panorama_image, concatenated;
662       gint panorama_width, panorama_height;
663       panorama_width = filter->out_width * 2;
664       panorama_height = filter->out_height / 2;
665       cv::Size panoramaSize (panorama_width, panorama_height);
666       panorama_image.create (panoramaSize, fisheye_image.type ());
667       cv::remap (fisheye_image, panorama_image, filter->map_x, filter->map_y,
668           inter_mode);
669       view1 =
670           panorama_image (cv::Rect (0, 0, filter->out_width, panorama_height));
671       view2 =
672           panorama_image (cv::Rect (filter->out_width, 0, filter->out_width,
673               panorama_height));
674       cv::vconcat (view1, view2, concatenated);
675       concatenated.copyTo (dewarped_image);
676     } else if (filter->display_mode == GST_DEWARP_DISPLAY_QUAD_VIEW) {
677       cv::Mat view1, view2, view3, view4, concat1, concat2, panorama_image,
678           concatenated;
679       gint panorama_width, panorama_height;
680       gint view_width, view_height;
681       panorama_width = filter->out_width * 2;
682       panorama_height = filter->out_height / 2;
683       view_width = filter->out_width / 2;
684       view_height = filter->out_height / 2;
685       cv::Size panoramaSize (panorama_width, panorama_height);
686       panorama_image.create (panoramaSize, fisheye_image.type ());
687       cv::remap (fisheye_image, panorama_image, filter->map_x, filter->map_y,
688           inter_mode);
689       view1 = panorama_image (cv::Rect (0, 0, view_width, view_height));
690       view2 =
691           panorama_image (cv::Rect (view_width, 0, view_width, view_height));
692       view3 =
693           panorama_image (cv::Rect ((view_width * 2), 0, view_width,
694               view_height));
695       view4 =
696           panorama_image (cv::Rect ((view_width * 3), 0, view_width,
697               view_height));
698       cv::vconcat (view1, view2, concat1);
699       cv::vconcat (view3, view4, concat2);
700       cv::hconcat (concat1, concat2, concatenated);
701       concatenated.copyTo (dewarped_image);
702     }
703
704     ret = GST_FLOW_OK;
705   } else {
706     GST_WARNING_OBJECT (filter, "Frame dropped, dimensions do not match");
707
708     ret = GST_BASE_TRANSFORM_FLOW_DROPPED;
709   }
710
711   GST_OBJECT_UNLOCK (filter);
712
713   return ret;
714 }
715
716 gboolean
717 gst_dewarp_plugin_init (GstPlugin * plugin)
718 {
719   GST_DEBUG_CATEGORY_INIT (gst_dewarp_debug, "dewarp",
720       0, "Dewarp fisheye images");
721
722   return gst_element_register (plugin, "dewarp", GST_RANK_NONE,
723       GST_TYPE_DEWARP);
724 }