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