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