| /* |
| * GStreamer |
| * Copyright (C) 2016 Prassel S.r.l |
| * Author: Nicola Murino <nicola.murino@gmail.com> |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| * |
| * Alternatively, the contents of this file may be used under the |
| * GNU Lesser General Public License Version 2.1 (the "LGPL"), in |
| * which case the following provisions apply instead of the ones |
| * mentioned above: |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| /** |
| * SECTION:element-dewarp |
| * |
| * Dewarp fisheye images |
| * |
| * <refsect2> |
| * <title>Example launch line</title> |
| * |[ |
| * gst-launch-1.0 videotestsrc ! videoconvert ! circle radius=0.1 height=80 ! dewarp outer-radius=0.35 inner-radius=0.1 ! videoconvert ! xvimagesink |
| * ]| |
| * </refsect2> |
| */ |
| |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include "gstdewarp.h" |
| #include <math.h> |
| |
| GST_DEBUG_CATEGORY_STATIC (gst_dewarp_debug); |
| #define GST_CAT_DEFAULT gst_dewarp_debug |
| |
| enum |
| { |
| PROP_0, |
| PROP_X_CENTER, |
| PROP_Y_CENTER, |
| PROP_INNER_RADIUS, |
| PROP_OUTER_RADIUS, |
| PROP_REMAP_X_CORRECTION, |
| PROP_REMAP_Y_CORRECTION, |
| PROP_DISPLAY_MODE, |
| PROP_INTERPOLATION_MODE |
| }; |
| |
| #define DEFAULT_CENTER 0.5 |
| #define DEFAULT_RADIUS 0.0 |
| #define DEFAULT_REMAP_CORRECTION 1.0 |
| |
| #define GST_TYPE_DEWARP_DISPLAY_MODE (dewarp_display_mode_get_type ()) |
| |
| static GType |
| dewarp_display_mode_get_type (void) |
| { |
| static GType dewarp_display_mode_type = 0; |
| static const GEnumValue dewarp_display_mode[] = { |
| {GST_DEWARP_DISPLAY_PANORAMA, "Single panorama image", "single-panorama"}, |
| {GST_DEWARP_DISPLAY_DOUBLE_PANORAMA, "Dewarped image is splitted in two " |
| "images displayed one below the other", "double-panorama"}, |
| {GST_DEWARP_DISPLAY_QUAD_VIEW, "Dewarped image is splitted in four images " |
| "dysplayed as a quad view", |
| "quad-view"}, |
| {0, NULL, NULL}, |
| }; |
| |
| if (!dewarp_display_mode_type) { |
| dewarp_display_mode_type = |
| g_enum_register_static ("GstDewarpDisplayMode", dewarp_display_mode); |
| } |
| return dewarp_display_mode_type; |
| } |
| |
| #define GST_TYPE_DEWARP_INTERPOLATION_MODE (dewarp_interpolation_mode_get_type ()) |
| |
| static GType |
| dewarp_interpolation_mode_get_type (void) |
| { |
| static GType dewarp_interpolation_mode_type = 0; |
| static const GEnumValue dewarp_interpolation_mode[] = { |
| {GST_DEWARP_INTER_NEAREST, "A nearest-neighbor interpolation", "nearest"}, |
| {GST_DEWARP_INTER_LINEAR, "A bilinear interpolation", "bilinear"}, |
| {GST_DEWARP_INTER_CUBIC, |
| "A bicubic interpolation over 4x4 pixel neighborhood", "bicubic"}, |
| {GST_DEWARP_INTER_LANCZOS4, |
| "A Lanczos interpolation over 8x8 pixel neighborhood", "Lanczos"}, |
| {0, NULL, NULL}, |
| }; |
| |
| if (!dewarp_interpolation_mode_type) { |
| dewarp_interpolation_mode_type = |
| g_enum_register_static ("GstDewarpInterpolationMode", |
| dewarp_interpolation_mode); |
| } |
| return dewarp_interpolation_mode_type; |
| } |
| |
| G_DEFINE_TYPE (GstDewarp, gst_dewarp, GST_TYPE_OPENCV_VIDEO_FILTER); |
| |
| static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGBA"))); |
| |
| static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", |
| GST_PAD_SRC, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGBA"))); |
| |
| static void gst_dewarp_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec); |
| static void gst_dewarp_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec); |
| |
| static GstCaps *gst_dewarp_transform_caps (GstBaseTransform * trans, |
| GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps); |
| |
| static GstFlowReturn gst_dewarp_transform_frame (GstOpencvVideoFilter * btrans, |
| GstBuffer * buffer, IplImage * img, GstBuffer * outbuf, IplImage * outimg); |
| |
| static gboolean gst_dewarp_set_caps (GstOpencvVideoFilter * filter, |
| gint in_width, gint in_height, gint in_depth, gint in_channels, |
| gint out_width, gint out_height, gint out_depth, gint out_channels); |
| |
| static void |
| gst_dewarp_finalize (GObject * obj) |
| { |
| GstDewarp *filter = GST_DEWARP (obj); |
| |
| if (filter->map_x) { |
| filter->map_x->release (); |
| delete filter->map_x; |
| } |
| |
| if (filter->map_y) { |
| filter->map_y->release (); |
| delete filter->map_y; |
| } |
| |
| G_OBJECT_CLASS (gst_dewarp_parent_class)->finalize (obj); |
| } |
| |
| static void |
| gst_dewarp_class_init (GstDewarpClass * klass) |
| { |
| GObjectClass *gobject_class; |
| GstElementClass *element_class = GST_ELEMENT_CLASS (klass); |
| GstBaseTransformClass *basesrc_class = GST_BASE_TRANSFORM_CLASS (klass); |
| GstOpencvVideoFilterClass *cvfilter_class = |
| (GstOpencvVideoFilterClass *) klass; |
| |
| gobject_class = (GObjectClass *) klass; |
| |
| gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_dewarp_finalize); |
| gobject_class->set_property = gst_dewarp_set_property; |
| gobject_class->get_property = gst_dewarp_get_property; |
| |
| basesrc_class->transform_caps = GST_DEBUG_FUNCPTR (gst_dewarp_transform_caps); |
| basesrc_class->transform_ip_on_passthrough = FALSE; |
| basesrc_class->passthrough_on_same_caps = TRUE; |
| |
| cvfilter_class->cv_trans_func = |
| GST_DEBUG_FUNCPTR (gst_dewarp_transform_frame); |
| cvfilter_class->cv_set_caps = GST_DEBUG_FUNCPTR (gst_dewarp_set_caps); |
| |
| g_object_class_install_property (gobject_class, PROP_X_CENTER, |
| g_param_spec_double ("x-center", "x center", |
| "X axis center of the fisheye image", |
| 0.0, 1.0, DEFAULT_CENTER, |
| (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | |
| G_PARAM_STATIC_STRINGS))); |
| |
| g_object_class_install_property (gobject_class, PROP_Y_CENTER, |
| g_param_spec_double ("y-center", "y center", |
| "Y axis center of the fisheye image", |
| 0.0, 1.0, DEFAULT_CENTER, |
| (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | |
| G_PARAM_STATIC_STRINGS))); |
| |
| g_object_class_install_property (gobject_class, PROP_INNER_RADIUS, |
| g_param_spec_double ("inner-radius", "inner radius", |
| "Inner radius of the fisheye image donut. If outer radius <= inner " |
| "radius the element will work in passthrough mode", |
| 0.0, 1.0, DEFAULT_RADIUS, |
| (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | |
| G_PARAM_STATIC_STRINGS))); |
| |
| g_object_class_install_property (gobject_class, PROP_OUTER_RADIUS, |
| g_param_spec_double ("outer-radius", "outer radius", |
| "Outer radius of the fisheye image donut. If outer radius <= inner " |
| "radius the element will work in passthrough mode", |
| 0.0, 1.0, DEFAULT_RADIUS, |
| (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | |
| G_PARAM_STATIC_STRINGS))); |
| |
| g_object_class_install_property (gobject_class, PROP_REMAP_X_CORRECTION, |
| g_param_spec_double ("x-remap-correction", "x remap correction", |
| "Correction factor for remapping on x axis. A correction is needed if " |
| "the fisheye image is not inside a circle", |
| 0.1, 10.0, DEFAULT_REMAP_CORRECTION, |
| (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | |
| G_PARAM_STATIC_STRINGS))); |
| |
| g_object_class_install_property (gobject_class, PROP_REMAP_Y_CORRECTION, |
| g_param_spec_double ("y-remap-correction", "y remap correction", |
| "Correction factor for remapping on y axis. A correction is needed if " |
| "the fisheye image is not inside a circle", |
| 0.1, 10.0, DEFAULT_REMAP_CORRECTION, |
| (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | |
| G_PARAM_STATIC_STRINGS))); |
| |
| g_object_class_install_property (gobject_class, PROP_INTERPOLATION_MODE, |
| g_param_spec_enum ("interpolation-method", "Interpolation method", |
| "Interpolation method to use", |
| GST_TYPE_DEWARP_INTERPOLATION_MODE, GST_DEWARP_INTER_LINEAR, |
| (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); |
| |
| g_object_class_install_property (gobject_class, PROP_DISPLAY_MODE, |
| g_param_spec_enum ("display-mode", "Display mode", |
| "How to display the dewarped image", |
| GST_TYPE_DEWARP_DISPLAY_MODE, GST_DEWARP_DISPLAY_PANORAMA, |
| (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); |
| |
| gst_element_class_set_static_metadata (element_class, |
| "Dewarp fisheye images", |
| "Filter/Effect/Video", |
| "Dewarp fisheye images", "Nicola Murino <nicola.murino@gmail.com>"); |
| |
| gst_element_class_add_static_pad_template (element_class, &src_factory); |
| gst_element_class_add_static_pad_template (element_class, &sink_factory); |
| |
| } |
| |
| static void |
| gst_dewarp_init (GstDewarp * filter) |
| { |
| filter->x_center = DEFAULT_CENTER; |
| filter->y_center = DEFAULT_CENTER; |
| filter->inner_radius = DEFAULT_RADIUS; |
| filter->outer_radius = DEFAULT_RADIUS; |
| filter->remap_correction_x = DEFAULT_REMAP_CORRECTION; |
| filter->remap_correction_y = DEFAULT_REMAP_CORRECTION; |
| filter->display_mode = GST_DEWARP_DISPLAY_PANORAMA; |
| filter->interpolation_mode = GST_DEWARP_INTER_LINEAR; |
| filter->pad_sink_width = 0; |
| filter->pad_sink_height = 0; |
| filter->in_width = 0; |
| filter->in_height = 0; |
| filter->out_width = 0; |
| filter->out_height = 0; |
| filter->need_map_update = TRUE; |
| |
| filter->map_x = new cv::Mat; |
| filter->map_y = new cv::Mat; |
| |
| gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter), |
| FALSE); |
| } |
| |
| static void |
| gst_dewarp_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| gdouble v; |
| gboolean need_reconfigure; |
| int disp_mode; |
| GstDewarp *filter = GST_DEWARP (object); |
| |
| need_reconfigure = FALSE; |
| |
| GST_OBJECT_LOCK (filter); |
| |
| switch (prop_id) { |
| case PROP_X_CENTER: |
| v = g_value_get_double (value); |
| if (v != filter->x_center) { |
| filter->x_center = v; |
| filter->need_map_update = TRUE; |
| need_reconfigure = TRUE; |
| GST_LOG_OBJECT (filter, "x center setted to %f", filter->x_center); |
| } |
| break; |
| case PROP_Y_CENTER: |
| v = g_value_get_double (value); |
| if (v != filter->y_center) { |
| filter->y_center = v; |
| filter->need_map_update = TRUE; |
| need_reconfigure = TRUE; |
| GST_LOG_OBJECT (filter, "y center setted to %f", filter->y_center); |
| } |
| break; |
| case PROP_INNER_RADIUS: |
| v = g_value_get_double (value); |
| if (v != filter->inner_radius) { |
| filter->inner_radius = v; |
| filter->need_map_update = TRUE; |
| need_reconfigure = TRUE; |
| GST_LOG_OBJECT (filter, "inner radius setted to %f", |
| filter->inner_radius); |
| } |
| break; |
| case PROP_OUTER_RADIUS: |
| v = g_value_get_double (value); |
| if (v != filter->outer_radius) { |
| filter->outer_radius = v; |
| filter->need_map_update = TRUE; |
| need_reconfigure = TRUE; |
| GST_LOG_OBJECT (filter, "outer radius setted to %f", |
| filter->outer_radius); |
| } |
| break; |
| case PROP_REMAP_X_CORRECTION: |
| v = g_value_get_double (value); |
| if (v != filter->remap_correction_x) { |
| filter->remap_correction_x = v; |
| filter->need_map_update = TRUE; |
| need_reconfigure = TRUE; |
| GST_LOG_OBJECT (filter, "x remap correction setted to %f", |
| filter->remap_correction_x); |
| } |
| break; |
| case PROP_REMAP_Y_CORRECTION: |
| v = g_value_get_double (value); |
| if (v != filter->remap_correction_y) { |
| filter->remap_correction_y = v; |
| filter->need_map_update = TRUE; |
| need_reconfigure = TRUE; |
| GST_LOG_OBJECT (filter, "y remap correction setted to %f", |
| filter->remap_correction_y); |
| } |
| break; |
| case PROP_INTERPOLATION_MODE: |
| filter->interpolation_mode = g_value_get_enum (value); |
| GST_LOG_OBJECT (filter, "interpolation mode setted to %" G_GINT32_FORMAT, |
| filter->interpolation_mode); |
| break; |
| case PROP_DISPLAY_MODE: |
| disp_mode = g_value_get_enum (value); |
| if (disp_mode != filter->display_mode) { |
| filter->display_mode = disp_mode; |
| need_reconfigure = TRUE; |
| GST_LOG_OBJECT (filter, "display mode setted to %" G_GINT32_FORMAT, |
| filter->display_mode); |
| } |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| |
| if (filter->need_map_update) |
| GST_LOG_OBJECT (filter, "need map update after property change"); |
| |
| GST_OBJECT_UNLOCK (filter); |
| |
| if (need_reconfigure) { |
| GST_DEBUG_OBJECT (filter, "Reconfigure src after property change"); |
| gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (filter)); |
| } else { |
| GST_DEBUG_OBJECT (filter, |
| "No property value changed, reconfigure src is not" " needed"); |
| } |
| } |
| |
| static void |
| gst_dewarp_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec) |
| { |
| GstDewarp *filter = GST_DEWARP (object); |
| |
| GST_OBJECT_LOCK (filter); |
| |
| switch (prop_id) { |
| case PROP_X_CENTER: |
| g_value_set_double (value, filter->x_center); |
| break; |
| case PROP_Y_CENTER: |
| g_value_set_double (value, filter->y_center); |
| break; |
| case PROP_INNER_RADIUS: |
| g_value_set_double (value, filter->inner_radius); |
| break; |
| case PROP_OUTER_RADIUS: |
| g_value_set_double (value, filter->outer_radius); |
| break; |
| case PROP_REMAP_X_CORRECTION: |
| g_value_set_double (value, filter->remap_correction_x); |
| break; |
| case PROP_REMAP_Y_CORRECTION: |
| g_value_set_double (value, filter->remap_correction_y); |
| break; |
| case PROP_INTERPOLATION_MODE: |
| g_value_set_enum (value, filter->interpolation_mode); |
| break; |
| case PROP_DISPLAY_MODE: |
| g_value_set_enum (value, filter->display_mode); |
| break; |
| |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| |
| GST_OBJECT_UNLOCK (filter); |
| } |
| |
| static void |
| gst_dewarp_update_map (GstDewarp * filter) |
| { |
| gdouble r1, r2, cx, cy; |
| gint x, y; |
| gint out_width, out_height; |
| |
| if (filter->display_mode == GST_DEWARP_DISPLAY_PANORAMA) { |
| out_width = filter->out_width; |
| out_height = filter->out_height; |
| } else { |
| out_width = filter->out_width * 2; |
| out_height = filter->out_height / 2; |
| } |
| |
| GST_DEBUG_OBJECT (filter, |
| "start update map out_width: %" G_GINT32_FORMAT " out height: %" |
| G_GINT32_FORMAT, out_width, out_height); |
| |
| r1 = filter->in_width * filter->inner_radius; |
| r2 = filter->in_width * filter->outer_radius; |
| cx = filter->x_center * filter->in_width; |
| cy = filter->y_center * filter->in_height; |
| cv::Size destSize (out_width, out_height); |
| filter->map_x->create (destSize, CV_32FC1); |
| filter->map_y->create (destSize, CV_32FC1); |
| |
| for (y = 0; y < out_height; y++) { |
| for (x = 0; x < out_width; x++) { |
| float r = ((float) (y) / (float) (out_height)) * (r2 - r1) + r1; |
| float theta = ((float) (x) / (float) (out_width)) * 2.0 * G_PI; |
| float xs = cx + r * sin (theta) * filter->remap_correction_x; |
| float ys = cy + r * cos (theta) * filter->remap_correction_y; |
| filter->map_x->at < float >(y, x) = xs; |
| filter->map_y->at < float >(y, x) = ys; |
| } |
| } |
| |
| filter->need_map_update = FALSE; |
| |
| GST_DEBUG_OBJECT (filter, "update map done"); |
| } |
| |
| static void |
| gst_dewarp_calculate_dimensions (GstDewarp * filter, GstPadDirection direction, |
| gint in_width, gint in_height, gint * out_width, gint * out_height) |
| { |
| if (filter->outer_radius <= filter->inner_radius) { |
| GST_LOG_OBJECT (filter, |
| "No dimensions conversion required, in width: %" G_GINT32_FORMAT |
| " in height: %" G_GINT32_FORMAT, in_width, in_height); |
| *out_width = in_width; |
| *out_height = in_height; |
| } else { |
| gdouble r1, r2; |
| |
| GST_LOG_OBJECT (filter, |
| "Calculate dimensions, in_width: %" G_GINT32_FORMAT |
| " in_height: %" G_GINT32_FORMAT " pad sink width: %" G_GINT32_FORMAT |
| " pad sink height: %" G_GINT32_FORMAT |
| " inner radius: %f, outer radius: %f, direction: %d", in_width, |
| in_height, filter->pad_sink_width, filter->pad_sink_height, |
| filter->inner_radius, filter->outer_radius, direction); |
| |
| r1 = in_width * filter->inner_radius; |
| r2 = in_width * filter->outer_radius; |
| |
| if (direction == GST_PAD_SINK) { |
| /* roundup is required to have integer results when we divide width, height |
| * in display mode different from GST_DEWARP_PANORAMA. |
| * Additionally some elements such as xvimagesink have problems with arbitrary |
| * dimensions, a roundup solves this issue too |
| */ |
| *out_width = GST_ROUND_UP_8 ((gint) ((2.0 * G_PI) * ((r2 + r1) / 2.0))); |
| *out_height = GST_ROUND_UP_8 ((gint) (r2 - r1)); |
| |
| if (filter->display_mode != GST_DEWARP_DISPLAY_PANORAMA) { |
| *out_width = *out_width / 2; |
| *out_height = *out_height * 2; |
| } |
| |
| /* if outer_radius and inner radius are very close then width and height |
| could be 0, we assume passtrough in this case |
| */ |
| if (G_UNLIKELY (*out_width == 0) || G_UNLIKELY (*out_height == 0)) { |
| GST_WARNING_OBJECT (filter, |
| "Invalid calculated dimensions, width: %" G_GINT32_FORMAT |
| " height: %" G_GINT32_FORMAT, *out_width, *out_height); |
| *out_width = in_width; |
| *out_height = in_height; |
| } |
| filter->pad_sink_width = in_width; |
| filter->pad_sink_height = in_height; |
| } else { |
| if (filter->pad_sink_width > 0) { |
| *out_width = filter->pad_sink_width; |
| } else { |
| *out_width = in_width; |
| } |
| if (filter->pad_sink_height > 0) { |
| *out_height = filter->pad_sink_height; |
| } else { |
| *out_height = in_height; |
| } |
| } |
| } |
| |
| GST_LOG_OBJECT (filter, |
| "Calculated dimensions: width %" G_GINT32_FORMAT " => %" G_GINT32_FORMAT |
| ", height %" G_GINT32_FORMAT " => %" G_GINT32_FORMAT " direction: %d", |
| in_width, *out_width, in_height, *out_height, direction); |
| } |
| |
| static GstCaps * |
| gst_dewarp_transform_caps (GstBaseTransform * trans, |
| GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps) |
| { |
| GstDewarp *dewarp = GST_DEWARP (trans); |
| |
| GstCaps *ret; |
| gint width, height; |
| guint i; |
| |
| ret = gst_caps_copy (caps); |
| |
| GST_OBJECT_LOCK (dewarp); |
| |
| for (i = 0; i < gst_caps_get_size (ret); i++) { |
| GstStructure *structure = gst_caps_get_structure (ret, i); |
| |
| if (gst_structure_get_int (structure, "width", &width) && |
| gst_structure_get_int (structure, "height", &height)) { |
| gint out_width, out_height; |
| gst_dewarp_calculate_dimensions (dewarp, direction, width, height, |
| &out_width, &out_height); |
| gst_structure_set (structure, "width", G_TYPE_INT, out_width, "height", |
| G_TYPE_INT, out_height, NULL); |
| } |
| } |
| |
| GST_OBJECT_UNLOCK (dewarp); |
| |
| if (filter_caps) { |
| GstCaps *intersection; |
| |
| GST_DEBUG_OBJECT (dewarp, "Using filter caps %" GST_PTR_FORMAT, |
| filter_caps); |
| |
| intersection = |
| gst_caps_intersect_full (filter_caps, ret, GST_CAPS_INTERSECT_FIRST); |
| gst_caps_unref (ret); |
| ret = intersection; |
| |
| GST_DEBUG_OBJECT (dewarp, "Intersection %" GST_PTR_FORMAT, ret); |
| } |
| |
| return ret; |
| } |
| |
| static gboolean |
| gst_dewarp_set_caps (GstOpencvVideoFilter * filter, |
| gint in_width, gint in_height, gint in_depth, gint in_channels, |
| gint out_width, gint out_height, gint out_depth, gint out_channels) |
| { |
| GstDewarp *dewarp = GST_DEWARP (filter); |
| |
| GST_DEBUG_OBJECT (dewarp, |
| "Set new caps, in width: %" G_GINT32_FORMAT " in height: %" |
| G_GINT32_FORMAT " out width: %" G_GINT32_FORMAT " out height: %" |
| G_GINT32_FORMAT, in_width, in_height, out_width, out_height); |
| |
| GST_OBJECT_LOCK (dewarp); |
| |
| dewarp->in_width = in_width; |
| dewarp->in_height = in_height; |
| dewarp->out_width = out_width; |
| dewarp->out_height = out_height; |
| gst_dewarp_update_map (dewarp); |
| |
| GST_OBJECT_UNLOCK (dewarp); |
| |
| return TRUE; |
| } |
| |
| static GstFlowReturn |
| gst_dewarp_transform_frame (GstOpencvVideoFilter * btrans, GstBuffer * buffer, |
| IplImage * img, GstBuffer * outbuf, IplImage * outimg) |
| { |
| GstDewarp *filter = GST_DEWARP (btrans); |
| GstFlowReturn ret; |
| |
| GST_OBJECT_LOCK (filter); |
| |
| if (img->width == filter->in_width |
| && img->height == filter->in_height |
| && outimg->width == filter->out_width |
| && outimg->height == filter->out_height) { |
| cv::Mat fisheye_image, dewarped_image; |
| int inter_mode; |
| |
| if (filter->need_map_update) { |
| GST_LOG_OBJECT (filter, "map update is needed"); |
| gst_dewarp_update_map (filter); |
| } |
| |
| switch (filter->interpolation_mode) { |
| case GST_DEWARP_INTER_NEAREST: |
| inter_mode = cv::INTER_NEAREST; |
| break; |
| case GST_DEWARP_INTER_LINEAR: |
| inter_mode = cv::INTER_LINEAR; |
| break; |
| case GST_DEWARP_INTER_CUBIC: |
| inter_mode = cv::INTER_CUBIC; |
| break; |
| case GST_DEWARP_INTER_LANCZOS4: |
| inter_mode = cv::INTER_LANCZOS4; |
| break; |
| default: |
| inter_mode = cv::INTER_LINEAR; |
| break; |
| } |
| |
| fisheye_image = cv::cvarrToMat (img, false); |
| dewarped_image = cv::cvarrToMat (outimg, false); |
| |
| if (filter->display_mode == GST_DEWARP_DISPLAY_PANORAMA) { |
| cv::remap (fisheye_image, dewarped_image, *filter->map_x, *filter->map_y, |
| inter_mode); |
| } else if (filter->display_mode == GST_DEWARP_DISPLAY_DOUBLE_PANORAMA) { |
| cv::Mat view1, view2, panorama_image, concatenated; |
| gint panorama_width, panorama_height; |
| panorama_width = filter->out_width * 2; |
| panorama_height = filter->out_height / 2; |
| cv::Size panoramaSize (panorama_width, panorama_height); |
| panorama_image.create (panoramaSize, fisheye_image.type ()); |
| cv::remap (fisheye_image, panorama_image, *filter->map_x, *filter->map_y, |
| inter_mode); |
| view1 = |
| panorama_image (cv::Rect (0, 0, filter->out_width, panorama_height)); |
| view2 = |
| panorama_image (cv::Rect (filter->out_width, 0, filter->out_width, |
| panorama_height)); |
| cv::vconcat (view1, view2, concatenated); |
| concatenated.copyTo (dewarped_image); |
| } else if (filter->display_mode == GST_DEWARP_DISPLAY_QUAD_VIEW) { |
| cv::Mat view1, view2, view3, view4, concat1, concat2, panorama_image, |
| concatenated; |
| gint panorama_width, panorama_height; |
| gint view_width, view_height; |
| panorama_width = filter->out_width * 2; |
| panorama_height = filter->out_height / 2; |
| view_width = filter->out_width / 2; |
| view_height = filter->out_height / 2; |
| cv::Size panoramaSize (panorama_width, panorama_height); |
| panorama_image.create (panoramaSize, fisheye_image.type ()); |
| cv::remap (fisheye_image, panorama_image, *filter->map_x, *filter->map_y, |
| inter_mode); |
| view1 = panorama_image (cv::Rect (0, 0, view_width, view_height)); |
| view2 = |
| panorama_image (cv::Rect (view_width, 0, view_width, view_height)); |
| view3 = |
| panorama_image (cv::Rect ((view_width * 2), 0, view_width, |
| view_height)); |
| view4 = |
| panorama_image (cv::Rect ((view_width * 3), 0, view_width, |
| view_height)); |
| cv::vconcat (view1, view2, concat1); |
| cv::vconcat (view3, view4, concat2); |
| cv::hconcat (concat1, concat2, concatenated); |
| concatenated.copyTo (dewarped_image); |
| } |
| |
| ret = GST_FLOW_OK; |
| } else { |
| GST_WARNING_OBJECT (filter, "Frame dropped, dimensions do not match"); |
| |
| ret = GST_BASE_TRANSFORM_FLOW_DROPPED; |
| } |
| |
| GST_OBJECT_UNLOCK (filter); |
| |
| return ret; |
| } |
| |
| gboolean |
| gst_dewarp_plugin_init (GstPlugin * plugin) |
| { |
| GST_DEBUG_CATEGORY_INIT (gst_dewarp_debug, "dewarp", |
| 0, "Dewarp fisheye images"); |
| |
| return gst_element_register (plugin, "dewarp", GST_RANK_NONE, |
| GST_TYPE_DEWARP); |
| } |