3 * Copyright (C) 2013 Miguel Casas-Sanchez <miguelecasassanchez@gmail.com>
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:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
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.
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
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.
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.
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.
45 * SECTION:element-retinex
47 * Basic and multiscale retinex for colour image enhancement, see article:
49 * Rahman, Zia-ur, Daniel J. Jobson, and Glenn A. Woodell. "Multi-scale retinex for
50 * color image enhancement." Image Processing, 1996. Proceedings., International
51 * Conference on. Vol. 3. IEEE, 1996.
53 * ## Example launch line
56 * gst-launch-1.0 videotestsrc ! videoconvert ! retinex ! videoconvert ! xvimagesink
64 #include "gstretinex.h"
65 #include <opencv2/imgproc.hpp>
67 GST_DEBUG_CATEGORY_STATIC (gst_retinex_debug);
68 #define GST_CAT_DEFAULT gst_retinex_debug
71 /* Filter signals and args */
90 #define DEFAULT_METHOD METHOD_BASIC
91 #define DEFAULT_SCALES 3
93 #define GST_TYPE_RETINEX_METHOD (gst_retinex_method_get_type ())
95 gst_retinex_method_get_type (void)
97 static GType etype = 0;
99 static const GEnumValue values[] = {
100 {METHOD_BASIC, "Basic retinex restoration", "basic"},
101 {METHOD_MULTISCALE, "Mutiscale retinex restoration", "multiscale"},
104 etype = g_enum_register_static ("GstRetinexMethod", values);
109 G_DEFINE_TYPE (GstRetinex, gst_retinex, GST_TYPE_OPENCV_VIDEO_FILTER);
110 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
113 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB")));
115 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
118 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB")));
121 static void gst_retinex_set_property (GObject * object, guint prop_id,
122 const GValue * value, GParamSpec * pspec);
123 static void gst_retinex_get_property (GObject * object, guint prop_id,
124 GValue * value, GParamSpec * pspec);
126 static GstFlowReturn gst_retinex_transform_ip (GstOpencvVideoFilter * filter,
127 GstBuffer * buff, Mat img);
128 static gboolean gst_retinex_set_caps (GstOpencvVideoFilter * btrans,
129 gint in_width, gint in_height, int in_cv_type,
130 gint out_width, gint out_height, int out_cv_type);
132 static void gst_retinex_finalize (GObject * object);
134 /* initialize the retinex's class */
136 gst_retinex_class_init (GstRetinexClass * klass)
138 GObjectClass *gobject_class = (GObjectClass *) klass;
139 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
140 GstOpencvVideoFilterClass *cvbasefilter_class =
141 (GstOpencvVideoFilterClass *) klass;
143 gobject_class->finalize = gst_retinex_finalize;
144 gobject_class->set_property = gst_retinex_set_property;
145 gobject_class->get_property = gst_retinex_get_property;
147 cvbasefilter_class->cv_trans_ip_func = gst_retinex_transform_ip;
148 cvbasefilter_class->cv_set_caps = gst_retinex_set_caps;
150 g_object_class_install_property (gobject_class, PROP_METHOD,
151 g_param_spec_enum ("method",
152 "Retinex method to use",
153 "Retinex method to use",
154 GST_TYPE_RETINEX_METHOD, DEFAULT_METHOD,
155 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
157 g_object_class_install_property (gobject_class, PROP_SCALES,
158 g_param_spec_int ("scales", "scales",
159 "Amount of gaussian filters (scales) used in multiscale retinex", 1,
161 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
163 gst_element_class_set_static_metadata (element_class,
164 "Retinex image colour enhancement", "Filter/Effect/Video",
165 "Multiscale retinex for colour image enhancement",
166 "Miguel Casas-Sanchez <miguelecasassanchez@gmail.com>");
168 gst_element_class_add_static_pad_template (element_class, &src_factory);
169 gst_element_class_add_static_pad_template (element_class, &sink_factory);
172 /* initialize the new element
173 * instantiate pads and add them to element
174 * set pad callback functions
175 * initialize instance structure
178 gst_retinex_init (GstRetinex * filter)
180 filter->method = DEFAULT_METHOD;
181 filter->scales = DEFAULT_SCALES;
182 filter->current_scales = 0;
183 gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter),
188 gst_retinex_finalize (GObject * object)
191 filter = GST_RETINEX (object);
193 filter->cvA.release ();
194 filter->cvB.release ();
195 filter->cvC.release ();
196 filter->cvD.release ();
197 g_free (filter->weights);
198 filter->weights = NULL;
199 g_free (filter->sigmas);
200 filter->sigmas = NULL;
202 G_OBJECT_CLASS (gst_retinex_parent_class)->finalize (object);
206 gst_retinex_set_property (GObject * object, guint prop_id,
207 const GValue * value, GParamSpec * pspec)
209 GstRetinex *retinex = GST_RETINEX (object);
213 retinex->method = g_value_get_enum (value);
216 retinex->scales = g_value_get_int (value);
219 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
225 gst_retinex_get_property (GObject * object, guint prop_id,
226 GValue * value, GParamSpec * pspec)
228 GstRetinex *filter = GST_RETINEX (object);
232 g_value_set_enum (value, filter->method);
235 g_value_set_int (value, filter->scales);
238 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
244 gst_retinex_set_caps (GstOpencvVideoFilter * filter, gint in_width,
245 gint in_height, int in_cv_type, gint out_width,
246 gint out_height, int out_cv_type)
248 GstRetinex *retinex = GST_RETINEX (filter);
251 size = Size (in_width, in_height);
253 retinex->cvA.create (size, CV_32FC3);
254 retinex->cvB.create (size, CV_32FC3);
255 retinex->cvC.create (size, CV_32FC3);
256 retinex->cvD.create (size, CV_32FC3);
262 gst_retinex_transform_ip (GstOpencvVideoFilter * filter, GstBuffer * buf,
265 GstRetinex *retinex = GST_RETINEX (filter);
271 /* Basic retinex restoration. The image and a filtered image are converted
272 to the log domain and subtracted.
273 O = Log(I) - Log(H(I))
274 where O is the output, H is a gaussian 2d filter and I is the input image. */
275 if (METHOD_BASIC == retinex->method) {
276 /* Compute log image */
277 img.convertTo (retinex->cvA, retinex->cvA.type ());
278 log (retinex->cvA, retinex->cvB);
280 /* Compute log of blurred image */
281 filter_size = (int) floor (sigma * 6) / 2;
282 filter_size = filter_size * 2 + 1;
284 img.convertTo (retinex->cvD, retinex->cvD.type ());
285 GaussianBlur (retinex->cvD, retinex->cvD, Size (filter_size, filter_size),
287 log (retinex->cvD, retinex->cvC);
289 /* Compute difference */
290 subtract (retinex->cvB, retinex->cvC, retinex->cvA);
293 retinex->cvA.convertTo (img, img.type (), (float) gain, (float) offset);
295 /* Multiscale retinex restoration. The image and a set of filtered images are
296 converted to the log domain and subtracted from the original with some set
297 of weights. Typically called with three equally weighted scales of fine,
298 medium and wide standard deviations.
299 O = Log(I) - sum_i [ wi * Log(H(I)) ]
300 where O is the output, H is a gaussian 2d filter and I is the input image
301 sum_i means summatory on var i with i in [0..scales) and wi are the weights */
302 else if (METHOD_MULTISCALE == retinex->method) {
305 /* allocate or reallocate the weights and sigmas according to scales */
306 if (retinex->current_scales != retinex->scales || !retinex->sigmas) {
308 (double *) g_realloc (retinex->weights,
309 sizeof (double) * retinex->scales);
311 (double *) g_realloc (retinex->sigmas,
312 sizeof (double) * retinex->scales);
313 for (i = 0; i < retinex->scales; i++) {
314 retinex->weights[i] = 1.0 / (double) retinex->scales;
315 retinex->sigmas[i] = 10.0 + 4.0 * (double) retinex->scales;
317 retinex->current_scales = retinex->scales;
320 /* Compute log image */
321 img.convertTo (retinex->cvA, retinex->cvA.type ());
322 log (retinex->cvA, retinex->cvB);
324 /* Filter at each scale */
325 for (i = 0; i < retinex->scales; i++) {
326 filter_size = (int) floor (retinex->sigmas[i] * 6) / 2;
327 filter_size = filter_size * 2 + 1;
329 img.convertTo (retinex->cvD, retinex->cvD.type ());
330 GaussianBlur (retinex->cvD, retinex->cvD, Size (filter_size, filter_size),
332 log (retinex->cvD, retinex->cvC);
334 /* Compute weighted difference */
335 retinex->cvC.convertTo (retinex->cvC, -1, retinex->weights[i], 0.0);
336 subtract (retinex->cvB, retinex->cvC, retinex->cvB);
340 retinex->cvB.convertTo (img, img.type (), (float) gain, (float) offset);
346 /* entry point to initialize the plug-in
347 * initialize the plug-in itself
348 * register the element factories and other features
351 gst_retinex_plugin_init (GstPlugin * plugin)
353 /* debug category for fltering log messages
356 GST_DEBUG_CATEGORY_INIT (gst_retinex_debug, "retinex",
357 0, "Multiscale retinex for colour image enhancement");
359 return gst_element_register (plugin, "retinex", GST_RANK_NONE,