1 /*
2 * GStreamer
3 * Copyright (C) 2013 Miguel Casas-Sanchez <miguelecasassanchez@gmail.com>
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Alternatively, the contents of this file may be used under the
24 * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
25 * which case the following provisions apply instead of the ones
26 * mentioned above:
27 *
28 * This library is free software; you can redistribute it and/or
29 * modify it under the terms of the GNU Library General Public
30 * License as published by the Free Software Foundation; either
31 * version 2 of the License, or (at your option) any later version.
32 *
33 * This library is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36 * Library General Public License for more details.
37 *
38 * You should have received a copy of the GNU Library General Public
39 * License along with this library; if not, write to the
40 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
41 * Boston, MA 02110-1301, USA.
42 */
43 /*
44 * SECTION:element-disparity
45 *
46 * This element computes a disparity map from two stereo images, meaning each one coming from a
47 * different camera, both looking at the same scene and relatively close to each other - more on
48 * this below. The disparity map is a proxy of the depth of a scene as seen from the camera.
49 *
50 * Assumptions: Input images are stereo, rectified and aligned. If these conditions are not met,
51 * results can be poor. Both cameras should be looking parallel to maximize the overlapping
52 * stereo area, and should not have objects too close or too far. The algorithms implemented here
53 * run prefiltering stages to normalize brightness between the inputs, and to maximize texture.
54 *
55 * Note that in general is hard to find correspondences between soft textures, for instance a
56 * block of gloss blue colour. The output is a gray image with values close to white meaning
57 * closer to the cameras and darker far away. Black means that the pixels were not matched
58 * correctly (not found). The resulting depth map can be transformed into real world coordinates
59 * by means of OpenCV function (reprojectImageTo3D) but for this the camera matrixes need to
60 * be fully known.
61 *
62 * Algorithm 1 is the OpenCV Stereo Block Matching, similar to the one developed by Kurt Konolige
63 * [A] and that works by using small Sum-of-absolute-differenc (SAD) windows to find matching
64 * points between the left and right rectified images. This algorithm finds only strongly matching
65 * points between both images, this means normally strong textures. In soft textures, such as a
66 * single coloured wall (as opposed to, f.i. a hairy rug), not all pixels might have correspondence.
67 *
68 * Algorithm 2 is the Semi Global Matching (SGM) algorithm [B] which models the scene structure
69 * with a point-wise matching cost and an associated smoothness term. The energy minimization
70 * is then computed in a multitude of 1D lines. For each point, the disparity corresponding to
71 * the minimum aggregated cost is selected. In [B] the author proposes to use 8 or 16 different
72 * independent paths. The SGM approach works well near depth discontinuities, but produces less
73 * accurate results. Despite its relatively large memory footprint, this method is very fast and
74 * potentially robust to complicated textured regions.
75 *
76 * Algorithm 3 is the OpenCV implementation of a modification of the variational stereo
77 * correspondence algorithm, described in [C].
78 *
79 * Algorithm 4 is the Graph Cut stereo vision algorithm (GC) introduced in [D]; it is a global
80 * stereo vision method. It calculates depth discontinuities by minimizing an energy function
81 * combingin a point-wise matching cost and a smoothness term. The energy function is passed
82 * to graph and Graph Cut is used to find a lowest-energy cut. GC is computationally intensive due
83 * to its global nature and uses loads of memory, but it can deal with textureless regions and
84 * reflections better than other methods.
85 * Graphcut based technique is CPU intensive hence smaller framesizes are desired.
86 *
87 * Some test images can be found here: http://vision.stanford.edu/~birch/p2p/
88 *
89 * [A] K. Konolige. Small vision system. hardware and implementation. In Proc. International
90 * Symposium on Robotics Research, pages 111--116, Hayama, Japan, 1997.
91 * [B] H. Hirschmüller, “Accurate and efficient stereo processing by semi-global matching and
92 * mutual information,” in Proceedings of the IEEE Conference on Computer Vision and Pattern
93 * Recognition, 2005, pp. 807–814.
94 * [C] S. Kosov, T. Thormaehlen, H.-P. Seidel "Accurate Real-Time Disparity Estimation with
95 * Variational Methods" Proceedings of the 5th International Symposium on Visual Computing,
96 * Vegas, USA
97 * [D] Scharstein, D. & Szeliski, R. (2001). A taxonomy and evaluation of dense two-frame stereo
98 * correspondence algorithms, International Journal of Computer Vision 47: 7–42.
99 *
100 * <refsect2>
101 * <title>Example launch line</title>
102 * |[
103 * gst-launch-1.0 videotestsrc ! video/x-raw,width=320,height=240 ! videoconvert ! disp0.sink_right videotestsrc ! video/x-raw,width=320,height=240 ! videoconvert ! disp0.sink_left disparity name=disp0 ! videoconvert ! ximagesink
104 * ]|
105 * Another example, with two png files representing a classical stereo matching,
106 * downloadable from http://vision.middlebury.edu/stereo/submit/tsukuba/im4.png and
107 * im3.png. Note here they are downloaded in ~ (home).
108 * |[
109 gst-launch-1.0 multifilesrc location=~/im3.png ! pngdec ! videoconvert ! disp0.sink_right multifilesrc location=~/im4.png ! pngdec ! videoconvert ! disp0.sink_left disparity name=disp0 method=sbm disp0.src ! videoconvert ! ximagesink
110 * ]|
111 * Yet another example with two cameras, which should be the same model, aligned etc.
112 * |[
113 gst-launch-1.0 v4l2src device=/dev/video1 ! video/x-raw,width=320,height=240 ! videoconvert ! disp0.sink_right v4l2src device=/dev/video0 ! video/x-raw,width=320,height=240 ! videoconvert ! disp0.sink_left disparity name=disp0 method=sgbm disp0.src ! videoconvert ! ximagesink
114 * ]|
115 * </refsect2>
116 */
117
118 #ifdef HAVE_CONFIG_H
119 #include <config.h>
120 #endif
121
122 #include "gstdisparity.h"
123 #include <opencv2/imgproc.hpp>
124
125 GST_DEBUG_CATEGORY_STATIC (gst_disparity_debug);
126 #define GST_CAT_DEFAULT gst_disparity_debug
127
128 using namespace cv;
129 /* Filter signals and args */
130 enum
131 {
132 /* FILL ME */
133 LAST_SIGNAL
134 };
135
136 enum
137 {
138 PROP_0,
139 PROP_METHOD,
140 };
141
142 typedef enum
143 {
144 METHOD_SBM,
145 METHOD_SGBM
146 } GstDisparityMethod;
147
148 #define DEFAULT_METHOD METHOD_SGBM
149
150 #define GST_TYPE_DISPARITY_METHOD (gst_disparity_method_get_type ())
151 static GType
gst_disparity_method_get_type(void)152 gst_disparity_method_get_type (void)
153 {
154 static GType etype = 0;
155 if (etype == 0) {
156 static const GEnumValue values[] = {
157 {METHOD_SBM, "Global block matching algorithm", "sbm"},
158 {METHOD_SGBM, "Semi-global block matching algorithm", "sgbm"},
159 {0, NULL, NULL},
160 };
161 etype = g_enum_register_static ("GstDisparityMethod", values);
162 }
163 return etype;
164 }
165
166 /* the capabilities of the inputs and outputs.
167 */
168 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
169 GST_PAD_SINK,
170 GST_PAD_ALWAYS,
171 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB"))
172 );
173
174 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
175 GST_PAD_SRC,
176 GST_PAD_ALWAYS,
177 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB"))
178 );
179
180 G_DEFINE_TYPE (GstDisparity, gst_disparity, GST_TYPE_ELEMENT);
181
182 static void gst_disparity_finalize (GObject * object);
183 static void gst_disparity_set_property (GObject * object, guint prop_id,
184 const GValue * value, GParamSpec * pspec);
185 static void gst_disparity_get_property (GObject * object, guint prop_id,
186 GValue * value, GParamSpec * pspec);
187 static GstStateChangeReturn gst_disparity_change_state (GstElement * element,
188 GstStateChange transition);
189
190 static gboolean gst_disparity_handle_sink_event (GstPad * pad,
191 GstObject * parent, GstEvent * event);
192 static gboolean gst_disparity_handle_query (GstPad * pad,
193 GstObject * parent, GstQuery * query);
194 static GstFlowReturn gst_disparity_chain_right (GstPad * pad,
195 GstObject * parent, GstBuffer * buffer);
196 static GstFlowReturn gst_disparity_chain_left (GstPad * pad, GstObject * parent,
197 GstBuffer * buffer);
198
199 static void initialise_disparity (GstDisparity * fs, int width, int height,
200 int nchannels);
201 static int initialise_sbm (GstDisparity * filter);
202 static int run_sbm_iteration (GstDisparity * filter);
203 static int run_sgbm_iteration (GstDisparity * filter);
204
205 /* initialize the disparity's class */
206 static void
gst_disparity_class_init(GstDisparityClass * klass)207 gst_disparity_class_init (GstDisparityClass * klass)
208 {
209 GObjectClass *gobject_class;
210 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
211
212 gobject_class = (GObjectClass *) klass;
213
214 gobject_class->finalize = gst_disparity_finalize;
215 gobject_class->set_property = gst_disparity_set_property;
216 gobject_class->get_property = gst_disparity_get_property;
217
218
219 g_object_class_install_property (gobject_class, PROP_METHOD,
220 g_param_spec_enum ("method",
221 "Stereo matching method to use",
222 "Stereo matching method to use",
223 GST_TYPE_DISPARITY_METHOD, DEFAULT_METHOD,
224 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
225
226 element_class->change_state = gst_disparity_change_state;
227
228 gst_element_class_set_static_metadata (element_class,
229 "Stereo image disparity (depth) map calculation",
230 "Filter/Effect/Video",
231 "Calculates the stereo disparity map from two (sequences of) rectified and aligned stereo images",
232 "Miguel Casas-Sanchez <miguelecasassanchez@gmail.com>");
233
234 gst_element_class_add_static_pad_template (element_class, &src_factory);
235 gst_element_class_add_static_pad_template (element_class, &sink_factory);
236 }
237
238 /* initialize the new element
239 * instantiate pads and add them to element
240 * set pad callback functions
241 * initialize instance structure
242 */
243 static void
gst_disparity_init(GstDisparity * filter)244 gst_disparity_init (GstDisparity * filter)
245 {
246 filter->sinkpad_left =
247 gst_pad_new_from_static_template (&sink_factory, "sink_left");
248 gst_pad_set_event_function (filter->sinkpad_left,
249 GST_DEBUG_FUNCPTR (gst_disparity_handle_sink_event));
250 gst_pad_set_query_function (filter->sinkpad_left,
251 GST_DEBUG_FUNCPTR (gst_disparity_handle_query));
252 gst_pad_set_chain_function (filter->sinkpad_left,
253 GST_DEBUG_FUNCPTR (gst_disparity_chain_left));
254 GST_PAD_SET_PROXY_CAPS (filter->sinkpad_left);
255 gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad_left);
256
257 filter->sinkpad_right =
258 gst_pad_new_from_static_template (&sink_factory, "sink_right");
259 gst_pad_set_event_function (filter->sinkpad_right,
260 GST_DEBUG_FUNCPTR (gst_disparity_handle_sink_event));
261 gst_pad_set_query_function (filter->sinkpad_right,
262 GST_DEBUG_FUNCPTR (gst_disparity_handle_query));
263 gst_pad_set_chain_function (filter->sinkpad_right,
264 GST_DEBUG_FUNCPTR (gst_disparity_chain_right));
265 GST_PAD_SET_PROXY_CAPS (filter->sinkpad_right);
266 gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad_right);
267
268 filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
269 gst_pad_use_fixed_caps (filter->srcpad);
270 gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
271
272 g_mutex_init (&filter->lock);
273 g_cond_init (&filter->cond);
274
275 filter->method = DEFAULT_METHOD;
276 }
277
278 static void
gst_disparity_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)279 gst_disparity_set_property (GObject * object, guint prop_id,
280 const GValue * value, GParamSpec * pspec)
281 {
282 GstDisparity *filter = GST_DISPARITY (object);
283 switch (prop_id) {
284 case PROP_METHOD:
285 filter->method = g_value_get_enum (value);
286 break;
287 default:
288 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
289 break;
290 }
291 }
292
293 static void
gst_disparity_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)294 gst_disparity_get_property (GObject * object, guint prop_id,
295 GValue * value, GParamSpec * pspec)
296 {
297 GstDisparity *filter = GST_DISPARITY (object);
298
299 switch (prop_id) {
300 case PROP_METHOD:
301 g_value_set_enum (value, filter->method);
302 break;
303 default:
304 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
305 break;
306 }
307 }
308
309 /* GstElement vmethod implementations */
310 static GstStateChangeReturn
gst_disparity_change_state(GstElement * element,GstStateChange transition)311 gst_disparity_change_state (GstElement * element, GstStateChange transition)
312 {
313 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
314 GstDisparity *fs = GST_DISPARITY (element);
315 switch (transition) {
316 case GST_STATE_CHANGE_PAUSED_TO_READY:
317 g_mutex_lock (&fs->lock);
318 fs->flushing = true;
319 g_cond_signal (&fs->cond);
320 g_mutex_unlock (&fs->lock);
321 break;
322 case GST_STATE_CHANGE_READY_TO_PAUSED:
323 g_mutex_lock (&fs->lock);
324 fs->flushing = false;
325 g_mutex_unlock (&fs->lock);
326 break;
327 default:
328 break;
329 }
330
331 ret =
332 GST_ELEMENT_CLASS (gst_disparity_parent_class)->change_state (element,
333 transition);
334
335 switch (transition) {
336 case GST_STATE_CHANGE_PAUSED_TO_READY:
337 g_mutex_lock (&fs->lock);
338 fs->flushing = true;
339 g_cond_signal (&fs->cond);
340 g_mutex_unlock (&fs->lock);
341 break;
342 case GST_STATE_CHANGE_READY_TO_PAUSED:
343 g_mutex_lock (&fs->lock);
344 fs->flushing = false;
345 g_mutex_unlock (&fs->lock);
346 break;
347 default:
348 break;
349 }
350 return ret;
351 }
352
353 static gboolean
gst_disparity_handle_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)354 gst_disparity_handle_sink_event (GstPad * pad,
355 GstObject * parent, GstEvent * event)
356 {
357 gboolean ret = TRUE;
358 GstDisparity *fs = GST_DISPARITY (parent);
359
360 switch (GST_EVENT_TYPE (event)) {
361 case GST_EVENT_CAPS:
362 {
363 GstCaps *caps;
364 GstVideoInfo info;
365 gst_event_parse_caps (event, &caps);
366
367 /* Critical section since both pads handle event sinking simultaneously */
368 g_mutex_lock (&fs->lock);
369 gst_video_info_from_caps (&info, caps);
370
371 GST_INFO_OBJECT (pad, " Negotiating caps via event %" GST_PTR_FORMAT,
372 caps);
373 if (!gst_pad_has_current_caps (fs->srcpad)) {
374 /* Init image info (widht, height, etc) and all OpenCV matrices */
375 initialise_disparity (fs, info.width, info.height,
376 info.finfo->n_components);
377
378 /* Initialise and keep the caps. Force them on src pad */
379 fs->caps = gst_video_info_to_caps (&info);
380 gst_pad_set_caps (fs->srcpad, fs->caps);
381
382 } else if (!gst_caps_is_equal (fs->caps, caps)) {
383 ret = FALSE;
384 }
385 g_mutex_unlock (&fs->lock);
386
387 GST_INFO_OBJECT (pad,
388 " Negotiated caps (result %d) via event: %" GST_PTR_FORMAT, ret,
389 caps);
390 break;
391 }
392 default:
393 ret = gst_pad_event_default (pad, parent, event);
394 break;
395 }
396 return ret;
397 }
398
399 static gboolean
gst_disparity_handle_query(GstPad * pad,GstObject * parent,GstQuery * query)400 gst_disparity_handle_query (GstPad * pad, GstObject * parent, GstQuery * query)
401 {
402 GstDisparity *fs = GST_DISPARITY (parent);
403 gboolean ret = TRUE;
404 GstCaps *template_caps;
405 GstCaps *current_caps;
406
407 switch (GST_QUERY_TYPE (query)) {
408 case GST_QUERY_CAPS:
409 g_mutex_lock (&fs->lock);
410 current_caps = gst_pad_get_current_caps (fs->srcpad);
411 if (current_caps == NULL) {
412 template_caps = gst_pad_get_pad_template_caps (pad);
413 gst_query_set_caps_result (query, template_caps);
414 gst_caps_unref (template_caps);
415 } else {
416 gst_query_set_caps_result (query, current_caps);
417 gst_caps_unref (current_caps);
418 }
419 g_mutex_unlock (&fs->lock);
420 ret = TRUE;
421 break;
422 case GST_QUERY_ALLOCATION:
423 if (pad == fs->sinkpad_right)
424 ret = gst_pad_peer_query (fs->srcpad, query);
425 else
426 ret = FALSE;
427 break;
428 default:
429 ret = gst_pad_query_default (pad, parent, query);
430 break;
431 }
432 return ret;
433 }
434
435 static void
gst_disparity_finalize(GObject * object)436 gst_disparity_finalize (GObject * object)
437 {
438 GstDisparity *filter;
439
440 filter = GST_DISPARITY (object);
441
442 filter->cvRGB_right.release ();
443 filter->cvRGB_left.release ();
444 filter->cvGray_right.release ();
445 filter->cvGray_left.release ();
446 filter->cvGray_depth_map1.release ();
447 filter->cvGray_depth_map2.release ();
448 filter->cvGray_depth_map1_2.release ();
449 filter->img_right_as_cvMat_gray.release ();
450 filter->img_left_as_cvMat_gray.release ();
451 filter->depth_map_as_cvMat.release ();
452 filter->sbm.release ();
453 filter->sgbm.release ();
454
455 gst_caps_replace (&filter->caps, NULL);
456
457 g_cond_clear (&filter->cond);
458 g_mutex_clear (&filter->lock);
459 G_OBJECT_CLASS (gst_disparity_parent_class)->finalize (object);
460 }
461
462
463
464 static GstFlowReturn
gst_disparity_chain_left(GstPad * pad,GstObject * parent,GstBuffer * buffer)465 gst_disparity_chain_left (GstPad * pad, GstObject * parent, GstBuffer * buffer)
466 {
467 GstDisparity *fs;
468 GstMapInfo info;
469
470 fs = GST_DISPARITY (parent);
471 GST_DEBUG_OBJECT (pad, "processing frame from left");
472 g_mutex_lock (&fs->lock);
473 if (fs->flushing) {
474 g_mutex_unlock (&fs->lock);
475 return GST_FLOW_FLUSHING;
476 }
477 if (fs->buffer_left) {
478 GST_DEBUG_OBJECT (pad, " right is busy, wait and hold");
479 g_cond_wait (&fs->cond, &fs->lock);
480 GST_DEBUG_OBJECT (pad, " right is free, continuing");
481 if (fs->flushing) {
482 g_mutex_unlock (&fs->lock);
483 return GST_FLOW_FLUSHING;
484 }
485 }
486 fs->buffer_left = buffer;
487
488 if (!gst_buffer_map (buffer, &info, (GstMapFlags) GST_MAP_READWRITE)) {
489 return GST_FLOW_ERROR;
490 }
491 fs->cvRGB_left.data = (unsigned char *) info.data;
492 fs->cvRGB_left.datastart = (unsigned char *) info.data;
493
494 GST_DEBUG_OBJECT (pad, "signalled right");
495 g_cond_signal (&fs->cond);
496 g_mutex_unlock (&fs->lock);
497
498 return GST_FLOW_OK;
499 }
500
501 static GstFlowReturn
gst_disparity_chain_right(GstPad * pad,GstObject * parent,GstBuffer * buffer)502 gst_disparity_chain_right (GstPad * pad, GstObject * parent, GstBuffer * buffer)
503 {
504 GstDisparity *fs;
505 GstMapInfo info;
506 GstFlowReturn ret;
507
508 fs = GST_DISPARITY (parent);
509 GST_DEBUG_OBJECT (pad, "processing frame from right");
510 g_mutex_lock (&fs->lock);
511 if (fs->flushing) {
512 g_mutex_unlock (&fs->lock);
513 return GST_FLOW_FLUSHING;
514 }
515 if (fs->buffer_left == NULL) {
516 GST_DEBUG_OBJECT (pad, " left has not provided another frame yet, waiting");
517 g_cond_wait (&fs->cond, &fs->lock);
518 GST_DEBUG_OBJECT (pad, " left has just provided a frame, continuing");
519 if (fs->flushing) {
520 g_mutex_unlock (&fs->lock);
521 return GST_FLOW_FLUSHING;
522 }
523 }
524 if (!gst_buffer_map (buffer, &info, (GstMapFlags) GST_MAP_READWRITE)) {
525 g_mutex_unlock (&fs->lock);
526 return GST_FLOW_ERROR;
527 }
528
529 fs->cvRGB_right.data = (unsigned char *) info.data;
530 fs->cvRGB_right.datastart = (unsigned char *) info.data;
531
532 /* Here do the business */
533 GST_INFO_OBJECT (pad,
534 "comparing frames, %dB (%dx%d) %d channels", (int) info.size,
535 fs->width, fs->height, fs->actualChannels);
536
537 /* Stereo corresponding using semi-global block matching. According to OpenCV:
538 "" The class implements modified H. Hirschmuller algorithm HH08 . The main
539 differences between the implemented algorithm and the original one are:
540
541 - by default the algorithm is single-pass, i.e. instead of 8 directions we
542 only consider 5. Set fullDP=true to run the full variant of the algorithm
543 (which could consume a lot of memory)
544 - the algorithm matches blocks, not individual pixels (though, by setting
545 SADWindowSize=1 the blocks are reduced to single pixels)
546 - mutual information cost function is not implemented. Instead, we use a
547 simpler Birchfield-Tomasi sub-pixel metric from BT96 , though the color
548 images are supported as well.
549 - we include some pre- and post- processing steps from K. Konolige
550 algorithm FindStereoCorrespondenceBM , such as pre-filtering
551 ( CV_STEREO_BM_XSOBEL type) and post-filtering (uniqueness check, quadratic
552 interpolation and speckle filtering) ""
553 */
554 if (METHOD_SGBM == fs->method) {
555 cvtColor (fs->cvRGB_left, fs->cvGray_left, COLOR_RGB2GRAY);
556 cvtColor (fs->cvRGB_right, fs->cvGray_right, COLOR_RGB2GRAY);
557 run_sgbm_iteration (fs);
558 normalize (fs->cvGray_depth_map1, fs->cvGray_depth_map2, 0, 255,
559 NORM_MINMAX, fs->cvGray_depth_map2.type ());
560 cvtColor (fs->cvGray_depth_map2, fs->cvRGB_right, COLOR_GRAY2RGB);
561 }
562 /* Algorithm 1 is the OpenCV Stereo Block Matching, similar to the one
563 developed by Kurt Konolige [A] and that works by using small Sum-of-absolute-
564 differences (SAD) window. See the comments on top of the file.
565 */
566 else if (METHOD_SBM == fs->method) {
567 cvtColor (fs->cvRGB_left, fs->cvGray_left, COLOR_RGB2GRAY);
568 cvtColor (fs->cvRGB_right, fs->cvGray_right, COLOR_RGB2GRAY);
569 run_sbm_iteration (fs);
570 normalize (fs->cvGray_depth_map1, fs->cvGray_depth_map2, 0, 255,
571 NORM_MINMAX, fs->cvGray_depth_map2.type ());
572 cvtColor (fs->cvGray_depth_map2, fs->cvRGB_right, COLOR_GRAY2RGB);
573 }
574
575
576 GST_DEBUG_OBJECT (pad, " right has finished");
577 gst_buffer_unmap (fs->buffer_left, &info);
578 gst_buffer_unref (fs->buffer_left);
579 fs->buffer_left = NULL;
580 g_cond_signal (&fs->cond);
581 g_mutex_unlock (&fs->lock);
582
583 ret = gst_pad_push (fs->srcpad, buffer);
584 return ret;
585 }
586
587
588
589
590
591 /* entry point to initialize the plug-in
592 * initialize the plug-in itself
593 * register the element factories and other features
594 */
595 gboolean
gst_disparity_plugin_init(GstPlugin * disparity)596 gst_disparity_plugin_init (GstPlugin * disparity)
597 {
598 GST_DEBUG_CATEGORY_INIT (gst_disparity_debug, "disparity", 0,
599 "Stereo image disparity (depth) map calculation");
600 return gst_element_register (disparity, "disparity", GST_RANK_NONE,
601 GST_TYPE_DISPARITY);
602 }
603
604
605 static void
initialise_disparity(GstDisparity * fs,int width,int height,int nchannels)606 initialise_disparity (GstDisparity * fs, int width, int height, int nchannels)
607 {
608 int cv_type = CV_8UC3;
609 fs->width = width;
610 fs->height = height;
611 fs->actualChannels = nchannels;
612
613 fs->imgSize = Size (fs->width, fs->height);
614 if (fs->actualChannels == 1) {
615 cv_type = CV_8UC1;
616 } else if (fs->actualChannels == 2) {
617 cv_type = CV_8UC2;
618 }
619
620 fs->cvRGB_right.create (fs->imgSize, cv_type);
621 fs->cvRGB_left.create (fs->imgSize, cv_type);
622 fs->cvGray_right.create (fs->imgSize, CV_8UC1);
623 fs->cvGray_left.create (fs->imgSize, CV_8UC1);
624
625 fs->cvGray_depth_map1.create (fs->imgSize, CV_16SC1);
626 fs->cvGray_depth_map2.create (fs->imgSize, CV_8UC1);
627 fs->cvGray_depth_map1_2.create (fs->imgSize, CV_16SC1);
628
629 /* Stereo Block Matching methods */
630 initialise_sbm (fs);
631 }
632
633 int
initialise_sbm(GstDisparity * filter)634 initialise_sbm (GstDisparity * filter)
635 {
636 filter->img_right_as_cvMat_gray = Mat (filter->cvGray_right);
637 filter->img_left_as_cvMat_gray = Mat (filter->cvGray_left);
638 filter->depth_map_as_cvMat = Mat (filter->cvGray_depth_map1);
639
640 filter->sbm = StereoBM::create ();
641 filter->sgbm = StereoSGBM::create (1, 64, 3);
642
643 filter->sbm->setBlockSize (9);
644 filter->sbm->setNumDisparities (32);
645 filter->sbm->setPreFilterSize (9);
646 filter->sbm->setPreFilterCap (32);
647 filter->sbm->setMinDisparity (0);
648 filter->sbm->setTextureThreshold (0);
649 filter->sbm->setUniquenessRatio (0);
650 filter->sbm->setSpeckleWindowSize (0);
651 filter->sbm->setSpeckleRange (0);
652 filter->sbm->setDisp12MaxDiff (0);
653
654 filter->sgbm->setMinDisparity (1);
655 filter->sgbm->setNumDisparities (64);
656 filter->sgbm->setBlockSize (3);
657 filter->sgbm->setP1 (200);
658 filter->sgbm->setP2 (255);
659 filter->sgbm->setDisp12MaxDiff (0);
660 filter->sgbm->setPreFilterCap (0);
661 filter->sgbm->setUniquenessRatio (0);
662 filter->sgbm->setSpeckleWindowSize (0);
663 filter->sgbm->setSpeckleRange (0);
664 filter->sgbm->setMode (StereoSGBM::MODE_HH);
665
666 return (0);
667 }
668
669 int
run_sbm_iteration(GstDisparity * filter)670 run_sbm_iteration (GstDisparity * filter)
671 {
672 ((StereoBM *) filter->sbm)->compute (filter->img_left_as_cvMat_gray,
673 filter->img_right_as_cvMat_gray, filter->depth_map_as_cvMat);
674
675 return (0);
676 }
677
678 int
run_sgbm_iteration(GstDisparity * filter)679 run_sgbm_iteration (GstDisparity * filter)
680 {
681 ((StereoSGBM *) filter->sgbm)->compute (filter->img_left_as_cvMat_gray,
682 filter->img_right_as_cvMat_gray, filter->depth_map_as_cvMat);
683
684 return (0);
685 }
686