• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
dewarp_display_mode_get_type(void)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 split in two "
94           "images displayed one below the other", "double-panorama"},
95     {GST_DEWARP_DISPLAY_QUAD_VIEW, "Dewarped image is split 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
dewarp_interpolation_mode_get_type(void)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_WITH_CODE (GstDewarp, gst_dewarp, GST_TYPE_OPENCV_VIDEO_FILTER,
133     GST_DEBUG_CATEGORY_INIT (gst_dewarp_debug, "dewarp", 0,
134         "Dewarp fisheye images");
135     );
136 GST_ELEMENT_REGISTER_DEFINE (dewarp, "dewarp", GST_RANK_NONE, GST_TYPE_DEWARP);
137 
138 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
139     GST_PAD_SINK,
140     GST_PAD_ALWAYS,
141     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGBA")));
142 
143 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
144     GST_PAD_SRC,
145     GST_PAD_ALWAYS,
146     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGBA")));
147 
148 static void gst_dewarp_set_property (GObject * object, guint prop_id,
149     const GValue * value, GParamSpec * pspec);
150 static void gst_dewarp_get_property (GObject * object, guint prop_id,
151     GValue * value, GParamSpec * pspec);
152 
153 static GstCaps *gst_dewarp_transform_caps (GstBaseTransform * trans,
154     GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps);
155 
156 static GstFlowReturn gst_dewarp_transform_frame (GstOpencvVideoFilter * btrans,
157     GstBuffer * buffer, cv::Mat img, GstBuffer * outbuf, cv::Mat outimg);
158 
159 static gboolean gst_dewarp_set_caps (GstOpencvVideoFilter * filter,
160     gint in_width, gint in_height, int in_cv_type,
161     gint out_width, gint out_height, int out_cv_type);
162 
163 static void
gst_dewarp_finalize(GObject * obj)164 gst_dewarp_finalize (GObject * obj)
165 {
166   GstDewarp *filter = GST_DEWARP (obj);
167 
168   filter->map_x.release ();
169   filter->map_y.release ();
170 
171   G_OBJECT_CLASS (gst_dewarp_parent_class)->finalize (obj);
172 }
173 
174 static void
gst_dewarp_class_init(GstDewarpClass * klass)175 gst_dewarp_class_init (GstDewarpClass * klass)
176 {
177   GObjectClass *gobject_class;
178   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
179   GstBaseTransformClass *basesrc_class = GST_BASE_TRANSFORM_CLASS (klass);
180   GstOpencvVideoFilterClass *cvfilter_class =
181       (GstOpencvVideoFilterClass *) klass;
182 
183   gobject_class = (GObjectClass *) klass;
184 
185   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_dewarp_finalize);
186   gobject_class->set_property = gst_dewarp_set_property;
187   gobject_class->get_property = gst_dewarp_get_property;
188 
189   basesrc_class->transform_caps = GST_DEBUG_FUNCPTR (gst_dewarp_transform_caps);
190   basesrc_class->transform_ip_on_passthrough = FALSE;
191   basesrc_class->passthrough_on_same_caps = TRUE;
192 
193   cvfilter_class->cv_trans_func =
194       GST_DEBUG_FUNCPTR (gst_dewarp_transform_frame);
195   cvfilter_class->cv_set_caps = GST_DEBUG_FUNCPTR (gst_dewarp_set_caps);
196 
197   g_object_class_install_property (gobject_class, PROP_X_CENTER,
198       g_param_spec_double ("x-center", "x center",
199           "X axis center of the fisheye image",
200           0.0, 1.0, DEFAULT_CENTER,
201           (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
202               G_PARAM_STATIC_STRINGS)));
203 
204   g_object_class_install_property (gobject_class, PROP_Y_CENTER,
205       g_param_spec_double ("y-center", "y center",
206           "Y axis center of the fisheye image",
207           0.0, 1.0, DEFAULT_CENTER,
208           (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
209               G_PARAM_STATIC_STRINGS)));
210 
211   g_object_class_install_property (gobject_class, PROP_INNER_RADIUS,
212       g_param_spec_double ("inner-radius", "inner radius",
213           "Inner radius of the fisheye image donut. If outer radius <= inner "
214           "radius the element will work in passthrough mode",
215           0.0, 1.0, DEFAULT_RADIUS,
216           (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
217               G_PARAM_STATIC_STRINGS)));
218 
219   g_object_class_install_property (gobject_class, PROP_OUTER_RADIUS,
220       g_param_spec_double ("outer-radius", "outer radius",
221           "Outer radius of the fisheye image donut. If outer radius <= inner "
222           "radius the element will work in passthrough mode",
223           0.0, 1.0, DEFAULT_RADIUS,
224           (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
225               G_PARAM_STATIC_STRINGS)));
226 
227   g_object_class_install_property (gobject_class, PROP_REMAP_X_CORRECTION,
228       g_param_spec_double ("x-remap-correction", "x remap correction",
229           "Correction factor for remapping on x axis. A correction is needed if "
230           "the fisheye image is not inside a circle",
231           0.1, 10.0, DEFAULT_REMAP_CORRECTION,
232           (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
233               G_PARAM_STATIC_STRINGS)));
234 
235   g_object_class_install_property (gobject_class, PROP_REMAP_Y_CORRECTION,
236       g_param_spec_double ("y-remap-correction", "y remap correction",
237           "Correction factor for remapping on y axis. A correction is needed if "
238           "the fisheye image is not inside a circle",
239           0.1, 10.0, DEFAULT_REMAP_CORRECTION,
240           (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
241               G_PARAM_STATIC_STRINGS)));
242 
243   g_object_class_install_property (gobject_class, PROP_INTERPOLATION_MODE,
244       g_param_spec_enum ("interpolation-method", "Interpolation method",
245           "Interpolation method to use",
246           GST_TYPE_DEWARP_INTERPOLATION_MODE, GST_DEWARP_INTER_LINEAR,
247           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
248 
249   g_object_class_install_property (gobject_class, PROP_DISPLAY_MODE,
250       g_param_spec_enum ("display-mode", "Display mode",
251           "How to display the dewarped image",
252           GST_TYPE_DEWARP_DISPLAY_MODE, GST_DEWARP_DISPLAY_PANORAMA,
253           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
254 
255   gst_element_class_set_static_metadata (element_class,
256       "Dewarp fisheye images",
257       "Filter/Effect/Video",
258       "Dewarp fisheye images", "Nicola Murino <nicola.murino@gmail.com>");
259 
260   gst_element_class_add_static_pad_template (element_class, &src_factory);
261   gst_element_class_add_static_pad_template (element_class, &sink_factory);
262 
263   gst_type_mark_as_plugin_api (GST_TYPE_DEWARP_DISPLAY_MODE, (GstPluginAPIFlags) 0);
264   gst_type_mark_as_plugin_api (GST_TYPE_DEWARP_INTERPOLATION_MODE, (GstPluginAPIFlags) 0);
265 }
266 
267 static void
gst_dewarp_init(GstDewarp * filter)268 gst_dewarp_init (GstDewarp * filter)
269 {
270   filter->x_center = DEFAULT_CENTER;
271   filter->y_center = DEFAULT_CENTER;
272   filter->inner_radius = DEFAULT_RADIUS;
273   filter->outer_radius = DEFAULT_RADIUS;
274   filter->remap_correction_x = DEFAULT_REMAP_CORRECTION;
275   filter->remap_correction_y = DEFAULT_REMAP_CORRECTION;
276   filter->display_mode = GST_DEWARP_DISPLAY_PANORAMA;
277   filter->interpolation_mode = GST_DEWARP_INTER_LINEAR;
278   filter->pad_sink_width = 0;
279   filter->pad_sink_height = 0;
280   filter->in_width = 0;
281   filter->in_height = 0;
282   filter->out_width = 0;
283   filter->out_height = 0;
284   filter->need_map_update = TRUE;
285 
286   gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter),
287       FALSE);
288 }
289 
290 static void
gst_dewarp_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)291 gst_dewarp_set_property (GObject * object, guint prop_id,
292     const GValue * value, GParamSpec * pspec)
293 {
294   gdouble v;
295   gboolean need_reconfigure;
296   int disp_mode;
297   GstDewarp *filter = GST_DEWARP (object);
298 
299   need_reconfigure = FALSE;
300 
301   GST_OBJECT_LOCK (filter);
302 
303   switch (prop_id) {
304     case PROP_X_CENTER:
305       v = g_value_get_double (value);
306       if (v != filter->x_center) {
307         filter->x_center = v;
308         filter->need_map_update = TRUE;
309         need_reconfigure = TRUE;
310         GST_LOG_OBJECT (filter, "x center set to %f", filter->x_center);
311       }
312       break;
313     case PROP_Y_CENTER:
314       v = g_value_get_double (value);
315       if (v != filter->y_center) {
316         filter->y_center = v;
317         filter->need_map_update = TRUE;
318         need_reconfigure = TRUE;
319         GST_LOG_OBJECT (filter, "y center set to %f", filter->y_center);
320       }
321       break;
322     case PROP_INNER_RADIUS:
323       v = g_value_get_double (value);
324       if (v != filter->inner_radius) {
325         filter->inner_radius = v;
326         filter->need_map_update = TRUE;
327         need_reconfigure = TRUE;
328         GST_LOG_OBJECT (filter, "inner radius set to %f",
329             filter->inner_radius);
330       }
331       break;
332     case PROP_OUTER_RADIUS:
333       v = g_value_get_double (value);
334       if (v != filter->outer_radius) {
335         filter->outer_radius = v;
336         filter->need_map_update = TRUE;
337         need_reconfigure = TRUE;
338         GST_LOG_OBJECT (filter, "outer radius set to %f",
339             filter->outer_radius);
340       }
341       break;
342     case PROP_REMAP_X_CORRECTION:
343       v = g_value_get_double (value);
344       if (v != filter->remap_correction_x) {
345         filter->remap_correction_x = v;
346         filter->need_map_update = TRUE;
347         need_reconfigure = TRUE;
348         GST_LOG_OBJECT (filter, "x remap correction set to %f",
349             filter->remap_correction_x);
350       }
351       break;
352     case PROP_REMAP_Y_CORRECTION:
353       v = g_value_get_double (value);
354       if (v != filter->remap_correction_y) {
355         filter->remap_correction_y = v;
356         filter->need_map_update = TRUE;
357         need_reconfigure = TRUE;
358         GST_LOG_OBJECT (filter, "y remap correction set to %f",
359             filter->remap_correction_y);
360       }
361       break;
362     case PROP_INTERPOLATION_MODE:
363       filter->interpolation_mode = g_value_get_enum (value);
364       GST_LOG_OBJECT (filter, "interpolation mode set to %" G_GINT32_FORMAT,
365           filter->interpolation_mode);
366       break;
367     case PROP_DISPLAY_MODE:
368       disp_mode = g_value_get_enum (value);
369       if (disp_mode != filter->display_mode) {
370         filter->display_mode = disp_mode;
371         need_reconfigure = TRUE;
372         GST_LOG_OBJECT (filter, "display mode set to %" G_GINT32_FORMAT,
373             filter->display_mode);
374       }
375       break;
376     default:
377       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
378       break;
379   }
380 
381   if (filter->need_map_update)
382     GST_LOG_OBJECT (filter, "need map update after property change");
383 
384   GST_OBJECT_UNLOCK (filter);
385 
386   if (need_reconfigure) {
387     GST_DEBUG_OBJECT (filter, "Reconfigure src after property change");
388     gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (filter));
389   } else {
390     GST_DEBUG_OBJECT (filter,
391         "No property value changed, reconfigure src is not" " needed");
392   }
393 }
394 
395 static void
gst_dewarp_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)396 gst_dewarp_get_property (GObject * object, guint prop_id,
397     GValue * value, GParamSpec * pspec)
398 {
399   GstDewarp *filter = GST_DEWARP (object);
400 
401   GST_OBJECT_LOCK (filter);
402 
403   switch (prop_id) {
404     case PROP_X_CENTER:
405       g_value_set_double (value, filter->x_center);
406       break;
407     case PROP_Y_CENTER:
408       g_value_set_double (value, filter->y_center);
409       break;
410     case PROP_INNER_RADIUS:
411       g_value_set_double (value, filter->inner_radius);
412       break;
413     case PROP_OUTER_RADIUS:
414       g_value_set_double (value, filter->outer_radius);
415       break;
416     case PROP_REMAP_X_CORRECTION:
417       g_value_set_double (value, filter->remap_correction_x);
418       break;
419     case PROP_REMAP_Y_CORRECTION:
420       g_value_set_double (value, filter->remap_correction_y);
421       break;
422     case PROP_INTERPOLATION_MODE:
423       g_value_set_enum (value, filter->interpolation_mode);
424       break;
425     case PROP_DISPLAY_MODE:
426       g_value_set_enum (value, filter->display_mode);
427       break;
428 
429     default:
430       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
431       break;
432   }
433 
434   GST_OBJECT_UNLOCK (filter);
435 }
436 
437 static void
gst_dewarp_update_map(GstDewarp * filter)438 gst_dewarp_update_map (GstDewarp * filter)
439 {
440   gdouble r1, r2, cx, cy;
441   gint x, y;
442   gint out_width, out_height;
443 
444   if (filter->display_mode == GST_DEWARP_DISPLAY_PANORAMA) {
445     out_width = filter->out_width;
446     out_height = filter->out_height;
447   } else {
448     out_width = filter->out_width * 2;
449     out_height = filter->out_height / 2;
450   }
451 
452   GST_DEBUG_OBJECT (filter,
453       "start update map out_width: %" G_GINT32_FORMAT " out height: %"
454       G_GINT32_FORMAT, out_width, out_height);
455 
456   r1 = filter->in_width * filter->inner_radius;
457   r2 = filter->in_width * filter->outer_radius;
458   cx = filter->x_center * filter->in_width;
459   cy = filter->y_center * filter->in_height;
460   cv::Size destSize (out_width, out_height);
461   filter->map_x.create (destSize, CV_32FC1);
462   filter->map_y.create (destSize, CV_32FC1);
463 
464   for (y = 0; y < out_height; y++) {
465     for (x = 0; x < out_width; x++) {
466       float r = ((float) (y) / (float) (out_height)) * (r2 - r1) + r1;
467       float theta = ((float) (x) / (float) (out_width)) * 2.0 * G_PI;
468       float xs = cx + r * sin (theta) * filter->remap_correction_x;
469       float ys = cy + r * cos (theta) * filter->remap_correction_y;
470       filter->map_x.at < float >(y, x) = xs;
471       filter->map_y.at < float >(y, x) = ys;
472     }
473   }
474 
475   filter->need_map_update = FALSE;
476 
477   GST_DEBUG_OBJECT (filter, "update map done");
478 }
479 
480 static void
gst_dewarp_calculate_dimensions(GstDewarp * filter,GstPadDirection direction,gint in_width,gint in_height,gint * out_width,gint * out_height)481 gst_dewarp_calculate_dimensions (GstDewarp * filter, GstPadDirection direction,
482     gint in_width, gint in_height, gint * out_width, gint * out_height)
483 {
484   if (filter->outer_radius <= filter->inner_radius) {
485     GST_LOG_OBJECT (filter,
486         "No dimensions conversion required, in width: %" G_GINT32_FORMAT
487         " in height: %" G_GINT32_FORMAT, in_width, in_height);
488     *out_width = in_width;
489     *out_height = in_height;
490   } else {
491     gdouble r1, r2;
492 
493     GST_LOG_OBJECT (filter,
494         "Calculate dimensions, in_width: %" G_GINT32_FORMAT
495         " in_height: %" G_GINT32_FORMAT " pad sink width: %" G_GINT32_FORMAT
496         " pad sink height: %" G_GINT32_FORMAT
497         " inner radius: %f, outer radius: %f, direction: %d", in_width,
498         in_height, filter->pad_sink_width, filter->pad_sink_height,
499         filter->inner_radius, filter->outer_radius, direction);
500 
501     r1 = in_width * filter->inner_radius;
502     r2 = in_width * filter->outer_radius;
503 
504     if (direction == GST_PAD_SINK) {
505       /* roundup is required to have integer results when we divide width, height
506        * in display mode different from GST_DEWARP_PANORAMA.
507        * Additionally some elements such as xvimagesink have problems with arbitrary
508        * dimensions, a roundup solves this issue too
509        */
510       *out_width = GST_ROUND_UP_8 ((gint) ((2.0 * G_PI) * ((r2 + r1) / 2.0)));
511       *out_height = GST_ROUND_UP_8 ((gint) (r2 - r1));
512 
513       if (filter->display_mode != GST_DEWARP_DISPLAY_PANORAMA) {
514         *out_width = *out_width / 2;
515         *out_height = *out_height * 2;
516       }
517 
518       /* if outer_radius and inner radius are very close then width and height
519          could be 0, we assume passthrough in this case
520        */
521       if (G_UNLIKELY (*out_width == 0) || G_UNLIKELY (*out_height == 0)) {
522         GST_WARNING_OBJECT (filter,
523             "Invalid calculated dimensions, width: %" G_GINT32_FORMAT
524             " height: %" G_GINT32_FORMAT, *out_width, *out_height);
525         *out_width = in_width;
526         *out_height = in_height;
527       }
528       filter->pad_sink_width = in_width;
529       filter->pad_sink_height = in_height;
530     } else {
531       if (filter->pad_sink_width > 0) {
532         *out_width = filter->pad_sink_width;
533       } else {
534         *out_width = in_width;
535       }
536       if (filter->pad_sink_height > 0) {
537         *out_height = filter->pad_sink_height;
538       } else {
539         *out_height = in_height;
540       }
541     }
542   }
543 
544   GST_LOG_OBJECT (filter,
545       "Calculated dimensions: width %" G_GINT32_FORMAT " => %" G_GINT32_FORMAT
546       ", height %" G_GINT32_FORMAT " => %" G_GINT32_FORMAT " direction: %d",
547       in_width, *out_width, in_height, *out_height, direction);
548 }
549 
550 static GstCaps *
gst_dewarp_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * filter_caps)551 gst_dewarp_transform_caps (GstBaseTransform * trans,
552     GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps)
553 {
554   GstDewarp *dewarp = GST_DEWARP (trans);
555 
556   GstCaps *ret;
557   gint width, height;
558   guint i;
559 
560   ret = gst_caps_copy (caps);
561 
562   GST_OBJECT_LOCK (dewarp);
563 
564   for (i = 0; i < gst_caps_get_size (ret); i++) {
565     GstStructure *structure = gst_caps_get_structure (ret, i);
566 
567     if (gst_structure_get_int (structure, "width", &width) &&
568         gst_structure_get_int (structure, "height", &height)) {
569       gint out_width, out_height;
570       gst_dewarp_calculate_dimensions (dewarp, direction, width, height,
571           &out_width, &out_height);
572       gst_structure_set (structure, "width", G_TYPE_INT, out_width, "height",
573           G_TYPE_INT, out_height, NULL);
574     }
575   }
576 
577   GST_OBJECT_UNLOCK (dewarp);
578 
579   if (filter_caps) {
580     GstCaps *intersection;
581 
582     GST_DEBUG_OBJECT (dewarp, "Using filter caps %" GST_PTR_FORMAT,
583         filter_caps);
584 
585     intersection =
586         gst_caps_intersect_full (filter_caps, ret, GST_CAPS_INTERSECT_FIRST);
587     gst_caps_unref (ret);
588     ret = intersection;
589 
590     GST_DEBUG_OBJECT (dewarp, "Intersection %" GST_PTR_FORMAT, ret);
591   }
592 
593   return ret;
594 }
595 
596 static gboolean
gst_dewarp_set_caps(GstOpencvVideoFilter * filter,gint in_width,gint in_height,int in_cv_type,gint out_width,gint out_height,int out_cv_type)597 gst_dewarp_set_caps (GstOpencvVideoFilter * filter,
598     gint in_width, gint in_height, int in_cv_type,
599     gint out_width, gint out_height, int out_cv_type)
600 {
601   GstDewarp *dewarp = GST_DEWARP (filter);
602 
603   GST_DEBUG_OBJECT (dewarp,
604       "Set new caps, in width: %" G_GINT32_FORMAT " in height: %"
605       G_GINT32_FORMAT " out width: %" G_GINT32_FORMAT " out height: %"
606       G_GINT32_FORMAT, in_width, in_height, out_width, out_height);
607 
608   GST_OBJECT_LOCK (dewarp);
609 
610   dewarp->in_width = in_width;
611   dewarp->in_height = in_height;
612   dewarp->out_width = out_width;
613   dewarp->out_height = out_height;
614   gst_dewarp_update_map (dewarp);
615 
616   GST_OBJECT_UNLOCK (dewarp);
617 
618   return TRUE;
619 }
620 
621 static GstFlowReturn
gst_dewarp_transform_frame(GstOpencvVideoFilter * btrans,GstBuffer * buffer,cv::Mat img,GstBuffer * outbuf,cv::Mat outimg)622 gst_dewarp_transform_frame (GstOpencvVideoFilter * btrans, GstBuffer * buffer,
623     cv::Mat img, GstBuffer * outbuf, cv::Mat outimg)
624 {
625   GstDewarp *filter = GST_DEWARP (btrans);
626   GstFlowReturn ret;
627 
628   GST_OBJECT_LOCK (filter);
629 
630   if (img.size ().width == filter->in_width
631       && img.size ().height == filter->in_height
632       && outimg.size ().width == filter->out_width
633       && outimg.size ().height == filter->out_height) {
634     cv::Mat fisheye_image, dewarped_image;
635     int inter_mode;
636 
637     if (filter->need_map_update) {
638       GST_LOG_OBJECT (filter, "map update is needed");
639       gst_dewarp_update_map (filter);
640     }
641 
642     switch (filter->interpolation_mode) {
643       case GST_DEWARP_INTER_NEAREST:
644         inter_mode = cv::INTER_NEAREST;
645         break;
646       case GST_DEWARP_INTER_LINEAR:
647         inter_mode = cv::INTER_LINEAR;
648         break;
649       case GST_DEWARP_INTER_CUBIC:
650         inter_mode = cv::INTER_CUBIC;
651         break;
652       case GST_DEWARP_INTER_LANCZOS4:
653         inter_mode = cv::INTER_LANCZOS4;
654         break;
655       default:
656         inter_mode = cv::INTER_LINEAR;
657         break;
658     }
659 
660     fisheye_image = img;
661     dewarped_image = outimg;
662 
663     if (filter->display_mode == GST_DEWARP_DISPLAY_PANORAMA) {
664       cv::remap (fisheye_image, dewarped_image, filter->map_x, filter->map_y,
665           inter_mode);
666     } else if (filter->display_mode == GST_DEWARP_DISPLAY_DOUBLE_PANORAMA) {
667       cv::Mat view1, view2, panorama_image, concatenated;
668       gint panorama_width, panorama_height;
669       panorama_width = filter->out_width * 2;
670       panorama_height = filter->out_height / 2;
671       cv::Size panoramaSize (panorama_width, panorama_height);
672       panorama_image.create (panoramaSize, fisheye_image.type ());
673       cv::remap (fisheye_image, panorama_image, filter->map_x, filter->map_y,
674           inter_mode);
675       view1 =
676           panorama_image (cv::Rect (0, 0, filter->out_width, panorama_height));
677       view2 =
678           panorama_image (cv::Rect (filter->out_width, 0, filter->out_width,
679               panorama_height));
680       cv::vconcat (view1, view2, concatenated);
681       concatenated.copyTo (dewarped_image);
682     } else if (filter->display_mode == GST_DEWARP_DISPLAY_QUAD_VIEW) {
683       cv::Mat view1, view2, view3, view4, concat1, concat2, panorama_image,
684           concatenated;
685       gint panorama_width, panorama_height;
686       gint view_width, view_height;
687       panorama_width = filter->out_width * 2;
688       panorama_height = filter->out_height / 2;
689       view_width = filter->out_width / 2;
690       view_height = filter->out_height / 2;
691       cv::Size panoramaSize (panorama_width, panorama_height);
692       panorama_image.create (panoramaSize, fisheye_image.type ());
693       cv::remap (fisheye_image, panorama_image, filter->map_x, filter->map_y,
694           inter_mode);
695       view1 = panorama_image (cv::Rect (0, 0, view_width, view_height));
696       view2 =
697           panorama_image (cv::Rect (view_width, 0, view_width, view_height));
698       view3 =
699           panorama_image (cv::Rect ((view_width * 2), 0, view_width,
700               view_height));
701       view4 =
702           panorama_image (cv::Rect ((view_width * 3), 0, view_width,
703               view_height));
704       cv::vconcat (view1, view2, concat1);
705       cv::vconcat (view3, view4, concat2);
706       cv::hconcat (concat1, concat2, concatenated);
707       concatenated.copyTo (dewarped_image);
708     }
709 
710     ret = GST_FLOW_OK;
711   } else {
712     GST_WARNING_OBJECT (filter, "Frame dropped, dimensions do not match");
713 
714     ret = GST_BASE_TRANSFORM_FLOW_DROPPED;
715   }
716 
717   GST_OBJECT_UNLOCK (filter);
718 
719   return ret;
720 }
721