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