vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e
@@ -0,0 +1,3 @@
|
||||
set(the_description "Biologically inspired algorithms")
|
||||
ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef)
|
||||
ocv_define_module(bioinspired opencv_core OPTIONAL opencv_highgui WRAP java objc python)
|
||||
@@ -0,0 +1,6 @@
|
||||
Biologically inspired vision models and derivated tools
|
||||
=======================================================
|
||||
|
||||
1. A biological retina model for image spatio-temporal noise and luminance changes enhancement
|
||||
2. A transient areas (spatio-temporal events) segmentation tool to use at the output of the Retina
|
||||
3. High Dynamic Range (HDR >8bit images) tone mapping to (conversion to 8bit) use cas of the retina
|
||||
@@ -0,0 +1,56 @@
|
||||
@article{Benoit2010,
|
||||
title={Using human visual system modeling for bio-inspired low level image processing},
|
||||
author={Benoit, Alexandre and Caplier, Alice and Durette, Barth{\'e}l{\'e}my and H{\'e}rault, Jeanny},
|
||||
journal={Computer vision and Image understanding},
|
||||
volume={114},
|
||||
number={7},
|
||||
pages={758--773},
|
||||
year={2010},
|
||||
publisher={Elsevier}
|
||||
}
|
||||
|
||||
@INPROCEEDINGS{Benoit2014,
|
||||
author={Strat, S.T. and Benoit, A. and Lambert, P.},
|
||||
booktitle={Signal Processing Conference (EUSIPCO), 2014 Proceedings of the 22nd European},
|
||||
title={Retina enhanced bag of words descriptors for video classification},
|
||||
year={2014},
|
||||
month={Sept},
|
||||
pages={1307-1311}
|
||||
}
|
||||
|
||||
@inproceedings{Strat2013,
|
||||
title={Retina enhanced SIFT descriptors for video indexing},
|
||||
author={Strat, Sabin Tiberius and Benoit, Alexandre and Lambert, Patrick},
|
||||
booktitle={Content-Based Multimedia Indexing (CBMI), 2013 11th International Workshop on},
|
||||
pages={201--206},
|
||||
year={2013},
|
||||
organization={IEEE}
|
||||
}
|
||||
|
||||
@book{Herault2010,
|
||||
title={Vision: Images, Signals and Neural Networks-Models of Neural Processing in Visual Perception},
|
||||
author={Jeanny, Herault},
|
||||
year={2010},
|
||||
publisher={World Scientific}
|
||||
}
|
||||
|
||||
@inproceedings{Chaix2007,
|
||||
title={Efficient demosaicing through recursive filtering},
|
||||
author={De Lavar{\`e}ne, Brice Chaix and Alleysson, David and Durette, Barth{\'e}l{\'e}my and H{\'e}rault, Jeanny},
|
||||
booktitle={Image Processing, 2007. ICIP 2007. IEEE International Conference on},
|
||||
volume={2},
|
||||
pages={II--189},
|
||||
year={2007},
|
||||
organization={IEEE}
|
||||
}
|
||||
|
||||
@article{Meylan2007,
|
||||
title={Model of retinal local adaptation for the tone mapping of color filter array images},
|
||||
author={Meylan, Laurence and Alleysson, David and S{\"u}sstrunk, Sabine},
|
||||
journal={JOSA A},
|
||||
volume={24},
|
||||
number={9},
|
||||
pages={2807--2816},
|
||||
year={2007},
|
||||
publisher={Optical Society of America}
|
||||
}
|
||||
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 894 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 390 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,505 @@
|
||||
Bioinspired Module Retina Introduction {#bioinspired_retina}
|
||||
======================================
|
||||
|
||||
Retina class overview
|
||||
---------------------
|
||||
|
||||
@note do not forget that the retina model is included in the following namespace : cv::bioinspired with C++ and in cv.bioinspired with Python
|
||||
|
||||
### Introduction
|
||||
|
||||
This class provides the main controls of the Gipsa/Listic labs human retina model. This is a non
|
||||
separable spatio-temporal filter modelling the two main retina information channels :
|
||||
|
||||
- foveal vision for detailed color vision : the parvocellular pathway.
|
||||
- peripheral vision for sensitive transient signals detection (motion and events) : the magnocellular pathway.
|
||||
|
||||
This model originates from Jeanny Herault work @cite Herault2010 . It has been
|
||||
involved in Alexandre Benoit phd and his current research @cite Benoit2010, @cite Benoit2014 . He
|
||||
currently maintains this module within OpenCV. It includes the work of other Jeanny's phd student
|
||||
such as @cite Chaix2007 and the log polar transformations of Barthelemy Durette described in Jeanny's
|
||||
book.
|
||||
|
||||
More into details here is an overview of the retina properties that are implemented here :
|
||||
- regarding luminance and details enhancement :
|
||||
- local logarithmic luminance compression (at the entry point by photoreceptors and at the output by ganglion cells).
|
||||
- spectral whitening at the Outer Plexiform Layer level (photoreceptors and horizontal cells spatio-temporal filtering).
|
||||
|
||||
The former behavior compresses luminance range and allows very bright areas and very dark ones to be visible on the same picture with lots of details. The latter reduces low frequency luminance energy (mean luminance) and enhances mid-frequencies (details). Applied all together, retina well prepares visual signals prior high level analysis. Those properties are really interesting with videos where light changes are dramatically reduced with an interesting temporal consistency.
|
||||
|
||||
- regarding noise filtering :
|
||||
- high frequency spatial and temporal noise is filtered out. Both outputs Parvo and Magno pathways benefit from this. Noise reduction benefits from the non separable spatio-temporal filtering.
|
||||
- at the Parvo output, static textures are enhanced and noise is filtered (on videos, temporal noise is nicely removed). However, as human behaviors, moving textures are smoothed. Then, moving object details can be only enhanced if the retina tracks it and keeps it static from its point of view.
|
||||
- at Magno output, it allows a cleaner detection of events (motion, changes) with reduced noise errors even in difficult lighting conditions. As a compromise, the Magno output is a low spatial frequency signal and allows events' blobs to be reliably extracted (check the TransientAreasSegmentationModule module for that).
|
||||
|
||||
### Use
|
||||
|
||||
This model can be used as a preprocessing stage in the aim of :
|
||||
|
||||
- performing texture analysis with enhanced signal to noise ratio and enhanced details which are robust
|
||||
against input images luminance ranges (check out the parvocellular retina channel output, by
|
||||
using the provided **getParvo** methods)
|
||||
- performing motion analysis that is also taking advantage of the previously cited properties (check out the
|
||||
magnocellular retina channel output, by using the provided **getMagno** methods)
|
||||
- general image/video sequence description using either one or both channels. An example of the
|
||||
use of Retina in a Bag of Words approach is given in @cite Benoit2014 .
|
||||
|
||||
@note
|
||||
- For ease of use in computer vision applications, the two retina channels are applied
|
||||
on all the input images. This does not follow the real retina topology but it is practical from an image processing point of view. If retina mapping (foveal and parafoveal vision) is required, use the log sampling capabilities proposed within the class.
|
||||
- Please do not hesitate to contribute by extending the retina description, code, use cases for complementary explanations and demonstrations.
|
||||
|
||||
### Use case illustrations
|
||||
|
||||
#### Image preprocessing using the Parvocellular pathway (parvo retina output)
|
||||
|
||||
As a preliminary presentation, let's start with a visual example. We propose to apply the filter on
|
||||
a low quality color jpeg image with backlight problems. Here is the considered input... *"Well,i could see more with my eyes than what i captured with my camera..."*
|
||||
|
||||

|
||||
|
||||
Below, the retina foveal model applied on the entire image with default parameters. Details are enforced whatever the local luminance is. Here there contours
|
||||
are strongly enforced but the noise level is kept low. Halo effects are voluntary visible with this configuration. See parameters discussion
|
||||
below and increase horizontalCellsGain near 1 to remove them.
|
||||
|
||||

|
||||
|
||||
Below, a second retina foveal model output applied on the entire image with a parameters setup
|
||||
focused on naturalness perception. *"Hey, i now recognize my cat, looking at the mountains at the
|
||||
end of the day !"*. Here contours are enforced, luminance is corrected but halos are avoided with
|
||||
this configuration. The backlight effect is corrected and highlight details are still preserved.
|
||||
Then, even on a low quality jpeg image, if some luminance's information remains, the retina is able to
|
||||
reconstruct a proper visual signal. Such configuration is also useful for High Dynamic Range
|
||||
(*HDR*) images compression to 8bit images as discussed in @cite Benoit2010 and in the demonstration
|
||||
codes discussed below. As shown at the end of the page, parameter changes from defaults are :
|
||||
|
||||
- horizontalCellsGain=0.3
|
||||
- photoreceptorsLocalAdaptationSensitivity=ganglioncellsSensitivity=0.89.
|
||||
|
||||

|
||||
|
||||
As observed in this preliminary demo, the retina can be settled up with various parameters, by
|
||||
default, as shown on the figure above, the retina strongly reduces mean luminance energy and
|
||||
enforces all details of the visual scene. Luminance energy and halo effects can be modulated
|
||||
(exaggerated to cancelled as shown on the two examples). In order to use your own parameters, you can
|
||||
use at least one time the *write(String fs)* method which will write a proper XML file with all
|
||||
default parameters. Then, tweak it on your own and reload them at any time using method
|
||||
*setup(String fs)*. These methods update a *Retina::RetinaParameters* member structure that is
|
||||
described hereafter. XML parameters file samples are shown at the end of the page.
|
||||
|
||||
#### Tone mapping processing capability using the Parvocellular pathway (parvo retina output)
|
||||
|
||||
This retina model naturally handles luminance range compression. Local adaptation stages and spectral whitening contribute
|
||||
to luminance range compression. In addition, high frequency noise that often corrupts tone mapped images is removed at early stages of the
|
||||
process thus leading to natural perception and noise free tone mapping.
|
||||
|
||||
Compared to the demos shown above, setup differences are the following ones: (see bioinspired/samples/OpenEXRimages_HDR_Retina_toneMapping.cpp for more details)
|
||||
* load HDR images (OpenEXR format is supported by OpenCV) and cut histogram borders at ~5% and 95% to eliminate salt&pepper like pixel's corruption.
|
||||
* apply retina with default parameters along with the following changes (generic parameters used for the presented illustrations of the section) :
|
||||
* retina Hcells gain =0.4 (the main change compared to the default configuration : it strongly reduces halo effects)
|
||||
* localAdaptation_photoreceptors=0.99 (a little higher than default value to enforce local adaptation)
|
||||
* localAdaptation_Gcells=0.95 (also slightly higher than default for local adaptation enforcement)
|
||||
* get the parvo output using the *getParvo()* method.
|
||||
|
||||
Have a look at the end of this page to see how to specify these parameters in a configuration file.
|
||||
|
||||
The following two illustrations show the effect of such configuration on 2 image samples.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
#### Motion and event detection using the Magnocellular pathway (magno retina output)
|
||||
|
||||
Spatio-temporal events can be easily detected using *magno* output of the retina (use the *getMagno()* method). Its energy linearly increases with motion speed.
|
||||
An event blob detector is proposed with the TransientAreasSegmentationModule class also provided in the bioinspired module. The basic idea is to detect local energy drops with regard of the neighborhood and then to apply a threshold. Such process has been used in a bag of words description of videos on the TRECVid challenge @cite Benoit2014 and only allows video frames description on transient areas.
|
||||
|
||||
We present here some illustrations of the retina outputs on some examples taken from http://changedetection.net/ with RGB and thermal videos.
|
||||
|
||||
@note here, we use the default retina setup that generates halos around strong edges. Note that temporal constants allow a temporal effect to be visible on moting objects (useful for still image illustrations of a video). Halos can be removed by increasing retina Hcells gain while temporal effects can be reduced by decreasing temporal constant values.
|
||||
Also take into account that the two retina outputs are rescaled in range [0:255] such that magno output can show a lot of "noise" when nothing moves while drawing it. However, its energy remains low if you retrieve it using *getMagnoRAW* getter instead.
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Literature
|
||||
|
||||
For more information, refer to the following papers :
|
||||
|
||||
- Model description : @cite Benoit2010
|
||||
|
||||
- Model use in a Bag of Words approach : @cite Benoit2014
|
||||
|
||||
- Please have a look at the reference work of Jeanny Herault that you can read in his book : @cite Herault2010
|
||||
|
||||
This retina filter code includes the research contributions of phd/research colleagues from which
|
||||
code has been redrawn by the author :
|
||||
|
||||
- take a look at the *retinacolor.hpp* module to discover Brice Chaix de Lavarene phD color
|
||||
mosaicing/demosaicing and his reference paper: @cite Chaix2007
|
||||
|
||||
- take a look at *imagelogpolprojection.hpp* to discover retina spatial log sampling which
|
||||
originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is
|
||||
also proposed and originates from Jeanny's discussions. More informations in the above cited
|
||||
Jeanny Heraults's book.
|
||||
|
||||
- Meylan&al work on HDR tone mapping that is implemented as a specific method within the model : @cite Meylan2007
|
||||
|
||||
|
||||
Retina programming interfaces
|
||||
-----------------------------
|
||||
|
||||
The proposed class allows the [Gipsa](http://www.gipsa-lab.inpg.fr) (preliminary work) /
|
||||
[Listic](http://www.listic.univ-savoie.fr) labs retina model to be used.
|
||||
It can be applied on still images, images sequences and video sequences.
|
||||
|
||||
Here is an overview of the Retina interface, allocate one instance with the *Retina::create()*
|
||||
functions (C++, Java, Python) :
|
||||
|
||||
@code{.cpp}
|
||||
namespace cv{namespace bioinspired{
|
||||
|
||||
class Retina : public Algorithm
|
||||
{
|
||||
public:
|
||||
// parameters setup instance
|
||||
struct RetinaParameters; // this class is detailed later
|
||||
|
||||
// main method for input frame processing (all use method, can also perform High Dynamic Range tone mapping)
|
||||
void run (InputArray inputImage);
|
||||
|
||||
// specific method aiming at correcting luminance only (faster High Dynamic Range tone mapping)
|
||||
void applyFastToneMapping(InputArray inputImage, OutputArray outputToneMappedImage)
|
||||
|
||||
// output buffers retrieval methods
|
||||
// -> foveal color vision details channel with luminance and noise correction
|
||||
void getParvo (OutputArray retinaOutput_parvo);
|
||||
void getParvoRAW (OutputArray retinaOutput_parvo);// retrieve original output buffers without any normalisation
|
||||
const Mat getParvoRAW () const;// retrieve original output buffers without any normalisation
|
||||
// -> peripheral monochrome motion and events (transient information) channel
|
||||
void getMagno (OutputArray retinaOutput_magno);
|
||||
void getMagnoRAW (OutputArray retinaOutput_magno); // retrieve original output buffers without any normalisation
|
||||
const Mat getMagnoRAW () const;// retrieve original output buffers without any normalisation
|
||||
|
||||
// reset retina buffers... equivalent to closing your eyes for some seconds
|
||||
void clearBuffers ();
|
||||
|
||||
// retrieve input and output buffers sizes
|
||||
Size getInputSize ();
|
||||
Size getOutputSize ();
|
||||
|
||||
// setup methods with specific parameters specification of global xml config file loading/write
|
||||
void setup (String retinaParameterFile="", const bool applyDefaultSetupOnFailure=true);
|
||||
void setup (FileStorage &fs, const bool applyDefaultSetupOnFailure=true);
|
||||
void setup (RetinaParameters newParameters);
|
||||
struct Retina::RetinaParameters getParameters ();
|
||||
const String printSetup ();
|
||||
virtual void write (String fs) const;
|
||||
virtual void write (FileStorage &fs) const;
|
||||
void setupOPLandIPLParvoChannel (const bool colorMode=true, const bool normaliseOutput=true, const float photoreceptorsLocalAdaptationSensitivity=0.7, const float photoreceptorsTemporalConstant=0.5, const float photoreceptorsSpatialConstant=0.53, const float horizontalCellsGain=0, const float HcellsTemporalConstant=1, const float HcellsSpatialConstant=7, const float ganglionCellsSensitivity=0.7);
|
||||
void setupIPLMagnoChannel (const bool normaliseOutput=true, const float parasolCells_beta=0, const float parasolCells_tau=0, const float parasolCells_k=7, const float amacrinCellsTemporalCutFrequency=1.2, const float V0CompressionParameter=0.95, const float localAdaptintegration_tau=0, const float localAdaptintegration_k=7);
|
||||
void setColorSaturation (const bool saturateColors=true, const float colorSaturationValue=4.0);
|
||||
void activateMovingContoursProcessing (const bool activate);
|
||||
void activateContoursProcessing (const bool activate);
|
||||
};
|
||||
|
||||
// Allocators
|
||||
cv::Ptr<Retina> Retina::create (Size inputSize);
|
||||
cv::Ptr<Retina> Retina::create (Size inputSize, const bool colorMode, RETINA_COLORSAMPLINGMETHOD colorSamplingMethod=RETINA_COLOR_BAYER, const bool useRetinaLogSampling=false, const double reductionFactor=1.0, const double samplingStrength=10.0);
|
||||
}} // cv and bioinspired namespaces end
|
||||
@endcode
|
||||
|
||||
### Setting up Retina
|
||||
|
||||
#### Managing the configuration file
|
||||
|
||||
When using the *Retina::write* and *Retina::load* methods, you create or load a XML file that stores Retina configuration.
|
||||
|
||||
The default configuration is presented below.
|
||||
|
||||
@code{.xml}
|
||||
<?xml version="1.0"?>
|
||||
<opencv_storage>
|
||||
<OPLandIPLparvo>
|
||||
<colorMode>1</colorMode>
|
||||
<normaliseOutput>1</normaliseOutput>
|
||||
<photoreceptorsLocalAdaptationSensitivity>7.5e-01</photoreceptorsLocalAdaptationSensitivity>
|
||||
<photoreceptorsTemporalConstant>9.0e-01</photoreceptorsTemporalConstant>
|
||||
<photoreceptorsSpatialConstant>5.7e-01</photoreceptorsSpatialConstant>
|
||||
<horizontalCellsGain>0.01</horizontalCellsGain>
|
||||
<hcellsTemporalConstant>0.5</hcellsTemporalConstant>
|
||||
<hcellsSpatialConstant>7.</hcellsSpatialConstant>
|
||||
<ganglionCellsSensitivity>7.5e-01</ganglionCellsSensitivity></OPLandIPLparvo>
|
||||
<IPLmagno>
|
||||
<normaliseOutput>1</normaliseOutput>
|
||||
<parasolCells_beta>0.</parasolCells_beta>
|
||||
<parasolCells_tau>0.</parasolCells_tau>
|
||||
<parasolCells_k>7.</parasolCells_k>
|
||||
<amacrinCellsTemporalCutFrequency>2.0e+00</amacrinCellsTemporalCutFrequency>
|
||||
<V0CompressionParameter>9.5e-01</V0CompressionParameter>
|
||||
<localAdaptintegration_tau>0.</localAdaptintegration_tau>
|
||||
<localAdaptintegration_k>7.</localAdaptintegration_k></IPLmagno>
|
||||
</opencv_storage>
|
||||
@endcode
|
||||
|
||||
Here are some words about all those parameters, tweak them as you wish to amplify or moderate retina effects (contours enforcement, halos effects, motion sensitivity, motion blurring, etc.)
|
||||
|
||||
#### Basic parameters
|
||||
|
||||
The simplest parameters are as follows :
|
||||
|
||||
- **colorMode** : let the retina process color information (if 1) or gray scale images (if 0). In
|
||||
that last case, only the first channels of the input will be processed.
|
||||
- **normaliseOutput** : each channel has such parameter: if the value is set to 1, then the considered
|
||||
channel's output is rescaled between 0 and 255. Be aware at this case of the Magnocellular output
|
||||
level (motion/transient channel detection). Residual noise will also be rescaled !
|
||||
|
||||
**Note :** using color requires color channels multiplexing/demultipexing which also demands more
|
||||
processing. You can expect much faster processing using gray levels : it would require around 30
|
||||
product per pixel for all of the retina processes and it has recently been parallelized for multicore
|
||||
architectures.
|
||||
|
||||
#### Photo-receptors parameters
|
||||
|
||||
The following parameters act on the entry point of the retina - photo-receptors - and has impact on all
|
||||
of the following processes. These sensors are low pass spatio-temporal filters that smooth temporal and
|
||||
spatial data and also adjust their sensitivity to local luminance,thus, leads to improving details extraction
|
||||
and high frequency noise canceling.
|
||||
|
||||
- **photoreceptorsLocalAdaptationSensitivity** between 0 and 1. Values close to 1 allow high
|
||||
luminance log compression's effect at the photo-receptors level. Values closer to 0 provide a more
|
||||
linear sensitivity. Increased alone, it can burn the *Parvo (details channel)* output image. If
|
||||
adjusted in collaboration with **ganglionCellsSensitivity**,images can be very contrasted
|
||||
whatever the local luminance there is... at the cost of a naturalness decrease.
|
||||
- **photoreceptorsTemporalConstant** this setups the temporal constant of the low pass filter
|
||||
effect at the entry of the retina. High value leads to strong temporal smoothing effect : moving
|
||||
objects are blurred and can disappear while static object are favored. But when starting the
|
||||
retina processing, stable state is reached later.
|
||||
- **photoreceptorsSpatialConstant** specifies the spatial constant related to photo-receptors' low
|
||||
pass filter's effect. Those parameters specify the minimum value of the spatial signal period allowed
|
||||
in what follows. Typically, this filter should cut high frequency noise. On the other hand, a 0 value
|
||||
cuts none of the noise while higher values start to cut high spatial frequencies, and progressively
|
||||
lower frequencies... Be aware to not go to high levels if you want to see some details of the input images !
|
||||
A good compromise for color images is a 0.53 value since such choice won't affect too much the color spectrum.
|
||||
Higher values would lead to gray and blurred output images.
|
||||
|
||||
#### Horizontal cells parameters
|
||||
|
||||
This parameter set tunes the neural network connected to the photo-receptors, the horizontal cells.
|
||||
It modulates photo-receptors sensitivity and completes the processing for final spectral whitening
|
||||
(part of the spatial band pass effect thus favoring visual details enhancement).
|
||||
|
||||
- **horizontalCellsGain** here is a critical parameter ! If you are not interested with the mean
|
||||
luminance and want just to focus on details enhancement, then, set this parameterto zero. However, if
|
||||
you want to keep some environment luminance's data, let some low spatial frequencies pass into the system and set a
|
||||
higher value (\<1).
|
||||
- **hcellsTemporalConstant** similar to photo-receptors, this parameter acts on the temporal constant of a
|
||||
low pass temporal filter that smoothes input data. Here, a high value generates a high retina
|
||||
after effect while a lower value makes the retina more reactive. This value should be lower than
|
||||
**photoreceptorsTemporalConstant** to limit strong retina after effects.
|
||||
- **hcellsSpatialConstant** is the spatial constant of these cells filter's low pass one.
|
||||
It specifies the lowest spatial frequency allowed in what follows. Visually, a high value leads
|
||||
to very low spatial frequencies processing and leads to salient halo effects. Lower values
|
||||
reduce this effect but has the limit of not go lower than the value of
|
||||
**photoreceptorsSpatialConstant**. Those 2 parameters actually specify the spatial band-pass of
|
||||
the retina.
|
||||
|
||||
**NOTE** Once the processing managed by the previous parameters is done, input data is cleaned from noise
|
||||
and luminance is already partly enhanced. The following parameters act on the last processing stages
|
||||
of the two outing retina signals.
|
||||
|
||||
#### Parvo (details channel) dedicated parameter
|
||||
|
||||
- **ganglionCellsSensitivity** specifies the strength of the final local adaptation occurring at
|
||||
the output of this details' dedicated channel. Parameter values remain between 0 and 1. Low value
|
||||
tend to give a linear response while higher values enforce the remaining low contrasted areas.
|
||||
|
||||
**Note :** this parameter can correct eventual burned images by favoring low energetic details of
|
||||
the visual scene, even in bright areas.
|
||||
|
||||
#### IPL Magno (motion/transient channel) parameters
|
||||
|
||||
Once image's information are cleaned, this channel acts as a high pass temporal filter that
|
||||
selects only the signals related to transient signals (events, motion, etc.). A low pass spatial filter
|
||||
smoothes extracted transient data while a final logarithmic compression enhances low transient events
|
||||
thus enhancing event sensitivity.
|
||||
|
||||
- **parasolCells_beta** generally set to zero, can be considered as an amplifier gain at the
|
||||
entry point of this processing stage. Generally set to 0.
|
||||
- **parasolCells_tau** the temporal smoothing effect that can be added
|
||||
- **parasolCells_k** the spatial constant of the spatial filtering effect, set it at a high value
|
||||
to favor low spatial frequency signals that are lower subject for residual noise.
|
||||
- **amacrinCellsTemporalCutFrequency** specifies the temporal constant of the high pass filter.
|
||||
High values let slow transient events to be selected.
|
||||
- **V0CompressionParameter** specifies the strength of the log compression. Similar behaviors to
|
||||
previous description but here enforces sensitivity of transient events.
|
||||
- **localAdaptintegration_tau** generally set to 0, has no real use actually in here.
|
||||
- **localAdaptintegration_k** specifies the size of the area on which local adaptation is
|
||||
performed. Low values lead to short range local adaptation (higher sensitivity to noise), high
|
||||
values secure log compression.
|
||||
|
||||
|
||||
### Demos and experiments !
|
||||
|
||||
|
||||
#### First time experiments
|
||||
|
||||
Here are some code snippets to shortly show how to use Retina with default parameters (with halo effects). Next section redirects to more complete demos provided with the main retina class.
|
||||
|
||||
Here is presented how to process a webcam stream with the following steps :
|
||||
- load a frist input image to get its size
|
||||
- allocate a retina instance with appropriate input size
|
||||
- loop over grabbed frames :
|
||||
- grab a new frame
|
||||
- run on a frame
|
||||
- call the two output getters
|
||||
- display retina outputs
|
||||
|
||||
C++ version (see bioinspired/samples/basicRetina.cpp) :
|
||||
|
||||
@code{.cpp}
|
||||
// include bioinspired module and OpenCV core utilities
|
||||
#include "opencv2/bioinspired.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
// main function
|
||||
int main(int argc, char* argv[]) {
|
||||
// declare the retina input buffer.
|
||||
cv::Mat inputFrame;
|
||||
// setup webcam reader and grab a first frame to get its size
|
||||
cv::VideoCapture videoCapture(0);
|
||||
videoCapture>>inputFrame;
|
||||
|
||||
// allocate a retina instance with input size equal to the one of the loaded image
|
||||
cv::Ptr<cv::bioinspired::Retina> myRetina = cv::bioinspired::createRetina(inputFrame.size());
|
||||
|
||||
/* retina parameters management methods use sample
|
||||
-> save current (here default) retina parameters to a xml file (you may use it only one time to get the file and modify it)
|
||||
*/
|
||||
myRetina->write("RetinaDefaultParameters.xml");
|
||||
|
||||
// -> load parameters if file exists
|
||||
myRetina->setup("RetinaSpecificParameters.xml");
|
||||
|
||||
// reset all retina buffers (open your eyes)
|
||||
myRetina->clearBuffers();
|
||||
|
||||
// declare retina output buffers
|
||||
cv::Mat retinaOutput_parvo;
|
||||
cv::Mat retinaOutput_magno;
|
||||
|
||||
//main processing loop
|
||||
while(true){
|
||||
// if using video stream, then, grabbing a new frame, else, input remains the same
|
||||
if (videoCapture.isOpened())
|
||||
videoCapture>>inputFrame;
|
||||
else
|
||||
break;
|
||||
imshow('input frame', inputImage)
|
||||
// run retina on the input image
|
||||
myRetina->run(inputFrame);
|
||||
// grab retina outputs
|
||||
myRetina->getParvo(retinaOutput_parvo);
|
||||
myRetina->getMagno(retinaOutput_magno);
|
||||
// draw retina outputs
|
||||
cv::imshow("retina input", inputFrame);
|
||||
cv::imshow("Retina Parvo", retinaOutput_parvo);
|
||||
cv::imshow("Retina Magno", retinaOutput_magno);
|
||||
cv::waitKey(5);
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
Compile this C++ code with the following command :
|
||||
@code{.sh}
|
||||
// compile
|
||||
g++ basicRetina.cpp -o basicRetina -lopencv_core -lopencv_highgui -lopencv_bioinspired -lopencv_videoio -lopencv_imgcodecs
|
||||
@endcode
|
||||
|
||||
|
||||
Python version
|
||||
|
||||
@code{.py}
|
||||
#import OpenCV module
|
||||
import cv2 as cv
|
||||
|
||||
#setup webcam reader
|
||||
videoHandler = cv.VideoCapture(0)
|
||||
succeed, inputImage=videoHandler.read()
|
||||
|
||||
#allocate a retina instance with input size equal to the one of the loaded image
|
||||
retina = cv.bioinspired_Retina.create((inputImage.shape[1], inputImage.shape[0]))
|
||||
|
||||
#retina parameters management methods use sample
|
||||
#-> save current (here default) retina parameters to a xml file (you may use it only one time to get the file and modify it)
|
||||
retina.write('retinaParams.xml')
|
||||
#-> load retina parameters from a xml file : here we load the default parameters that we just wrote to file
|
||||
retina.setup('retinaParams.xml')
|
||||
|
||||
#main processing loop
|
||||
stillProcess=True
|
||||
while stillProcess is True:
|
||||
|
||||
#grab a new frame and display it
|
||||
stillProcess, inputImage=videoHandler.read()
|
||||
cv.imshow('input frame', inputImage)
|
||||
|
||||
#run retina on the input image
|
||||
retina.run(inputImage)
|
||||
|
||||
#grab retina outputs
|
||||
retinaOut_parvo=retina.getParvo()
|
||||
retinaOut_magno=retina.getMagno()
|
||||
|
||||
#draw retina outputs
|
||||
cv.imshow('retina parvo out', retinaOut_parvo)
|
||||
cv.imshow('retina magno out', retinaOut_magno)
|
||||
|
||||
#wait a little to let the time for figures to be drawn
|
||||
cv.waitKey(2)
|
||||
@endcode
|
||||
|
||||
|
||||
|
||||
#### More complete demos
|
||||
|
||||
@note Complementary to the following examples, have a look at the Retina tutorial in the
|
||||
tutorial/contrib section for complementary explanations.**
|
||||
|
||||
Take a look at the provided C++ examples provided with OpenCV :
|
||||
|
||||
- **samples/cpp/retinademo.cpp** shows how to use the retina module for details enhancement (Parvo channel output) and transient maps observation (Magno channel output). You can play with images, video sequences and webcam video.
|
||||
Typical uses are (provided your OpenCV installation is situated in folder *OpenCVReleaseFolder*)
|
||||
- image processing : **OpenCVReleaseFolder/bin/retinademo -image myPicture.jpg**
|
||||
- video processing : **OpenCVReleaseFolder/bin/retinademo -video myMovie.avi**
|
||||
- webcam processing: **OpenCVReleaseFolder/bin/retinademo -video**
|
||||
|
||||
@note This demo generates the file *RetinaDefaultParameters.xml* which contains the
|
||||
default parameters of the retina. Then, rename this as *RetinaSpecificParameters.xml*, adjust
|
||||
the parameters the way you want and reload the program to check the effect.
|
||||
|
||||
- **samples/cpp/OpenEXRimages\_HDR\_Retina\_toneMapping.cpp** shows how to use the retina to
|
||||
perform High Dynamic Range (HDR) luminance compression
|
||||
|
||||
Then, take a HDR image using bracketing with your camera and generate an OpenEXR image and
|
||||
then process it using the demo.
|
||||
|
||||
Typical use, assuming that you have the OpenEXR image such as *memorial.exr* (present in the
|
||||
samples/cpp/ folder)
|
||||
|
||||
- **OpenCVReleaseFolder/bin/OpenEXRimages\_HDR\_Retina\_toneMapping memorial.exr [optional:
|
||||
'fast']**
|
||||
|
||||
Note that some sliders are made available to allow you to play with luminance compression.
|
||||
|
||||
If not using the 'fast' option, then, tone mapping is performed using the full retina model
|
||||
@cite Benoit2010 . It includes spectral whitening that allows luminance energy to be reduced.
|
||||
When using the 'fast' option, a simpler method is used, it is an adaptation of the
|
||||
algorithm presented in @cite Meylan2007 . This method also gives good results and it is faster to
|
||||
process but it sometimes requires some more parameters adjustement.
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OPENCV_BIOINSPIRED_HPP__
|
||||
#define __OPENCV_BIOINSPIRED_HPP__
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/bioinspired/retina.hpp"
|
||||
#include "opencv2/bioinspired/retinafasttonemapping.hpp"
|
||||
#include "opencv2/bioinspired/transientareassegmentationmodule.hpp"
|
||||
|
||||
/** @defgroup bioinspired Biologically inspired vision models and derivated tools
|
||||
|
||||
The module provides biological visual systems models (human visual system and others). It also
|
||||
provides derivated objects that take advantage of those bio-inspired models.
|
||||
|
||||
@ref bioinspired_retina
|
||||
|
||||
*/
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifdef __OPENCV_BUILD
|
||||
#error this is a compatibility header which should not be used inside the OpenCV library
|
||||
#endif
|
||||
|
||||
#include "opencv2/bioinspired.hpp"
|
||||
@@ -0,0 +1,409 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2015
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2015, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __OPENCV_BIOINSPIRED_RETINA_HPP__
|
||||
#define __OPENCV_BIOINSPIRED_RETINA_HPP__
|
||||
|
||||
/**
|
||||
@file
|
||||
@date Jul 19, 2011
|
||||
@author Alexandre Benoit
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp" // for all OpenCV core functionalities access, including cv::Exception support
|
||||
|
||||
|
||||
namespace cv{
|
||||
namespace bioinspired{
|
||||
|
||||
//! @addtogroup bioinspired
|
||||
//! @{
|
||||
|
||||
enum {
|
||||
RETINA_COLOR_RANDOM, //!< each pixel position is either R, G or B in a random choice
|
||||
RETINA_COLOR_DIAGONAL,//!< color sampling is RGBRGBRGB..., line 2 BRGBRGBRG..., line 3, GBRGBRGBR...
|
||||
RETINA_COLOR_BAYER//!< standard bayer sampling
|
||||
};
|
||||
|
||||
|
||||
/** @brief retina model parameters structure
|
||||
|
||||
For better clarity, check explenations on the comments of methods : setupOPLandIPLParvoChannel and setupIPLMagnoChannel
|
||||
|
||||
Here is the default configuration file of the retina module. It gives results such as the first
|
||||
retina output shown on the top of this page.
|
||||
|
||||
@include default_retina_config.xml
|
||||
|
||||
Here is the 'realistic" setup used to obtain the second retina output shown on the top of this page.
|
||||
|
||||
@include realistic_retina_config.xml
|
||||
|
||||
*/
|
||||
struct RetinaParameters{
|
||||
//! Outer Plexiform Layer (OPL) and Inner Plexiform Layer Parvocellular (IplParvo) parameters
|
||||
struct OPLandIplParvoParameters{
|
||||
OPLandIplParvoParameters():colorMode(true),
|
||||
normaliseOutput(true),
|
||||
photoreceptorsLocalAdaptationSensitivity(0.75f),
|
||||
photoreceptorsTemporalConstant(0.9f),
|
||||
photoreceptorsSpatialConstant(0.53f),
|
||||
horizontalCellsGain(0.01f),
|
||||
hcellsTemporalConstant(0.5f),
|
||||
hcellsSpatialConstant(7.f),
|
||||
ganglionCellsSensitivity(0.75f) { } // default setup
|
||||
bool colorMode, normaliseOutput;
|
||||
float photoreceptorsLocalAdaptationSensitivity, photoreceptorsTemporalConstant, photoreceptorsSpatialConstant, horizontalCellsGain, hcellsTemporalConstant, hcellsSpatialConstant, ganglionCellsSensitivity;
|
||||
};
|
||||
//! Inner Plexiform Layer Magnocellular channel (IplMagno)
|
||||
struct IplMagnoParameters{
|
||||
IplMagnoParameters():
|
||||
normaliseOutput(true),
|
||||
parasolCells_beta(0.f),
|
||||
parasolCells_tau(0.f),
|
||||
parasolCells_k(7.f),
|
||||
amacrinCellsTemporalCutFrequency(2.0f),
|
||||
V0CompressionParameter(0.95f),
|
||||
localAdaptintegration_tau(0.f),
|
||||
localAdaptintegration_k(7.f) { } // default setup
|
||||
bool normaliseOutput;
|
||||
float parasolCells_beta, parasolCells_tau, parasolCells_k, amacrinCellsTemporalCutFrequency, V0CompressionParameter, localAdaptintegration_tau, localAdaptintegration_k;
|
||||
};
|
||||
OPLandIplParvoParameters OPLandIplParvo;
|
||||
IplMagnoParameters IplMagno;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** @brief class which allows the Gipsa/Listic Labs model to be used with OpenCV.
|
||||
|
||||
This retina model allows spatio-temporal image processing (applied on still images, video sequences).
|
||||
As a summary, these are the retina model properties:
|
||||
- It applies a spectral whithening (mid-frequency details enhancement)
|
||||
- high frequency spatio-temporal noise reduction
|
||||
- low frequency luminance to be reduced (luminance range compression)
|
||||
- local logarithmic luminance compression allows details to be enhanced in low light conditions
|
||||
|
||||
USE : this model can be used basically for spatio-temporal video effects but also for :
|
||||
_using the getParvo method output matrix : texture analysiswith enhanced signal to noise ratio and enhanced details robust against input images luminance ranges
|
||||
_using the getMagno method output matrix : motion analysis also with the previously cited properties
|
||||
|
||||
for more information, reer to the following papers :
|
||||
Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
|
||||
The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
more informations in the above cited Jeanny Heraults's book.
|
||||
*/
|
||||
class CV_EXPORTS_W Retina : public Algorithm {
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/** @brief Retreive retina input buffer size
|
||||
@return the retina input buffer size
|
||||
*/
|
||||
CV_WRAP virtual Size getInputSize()=0;
|
||||
|
||||
/** @brief Retreive retina output buffer size that can be different from the input if a spatial log
|
||||
transformation is applied
|
||||
@return the retina output buffer size
|
||||
*/
|
||||
CV_WRAP virtual Size getOutputSize()=0;
|
||||
|
||||
/** @brief Try to open an XML retina parameters file to adjust current retina instance setup
|
||||
|
||||
- if the xml file does not exist, then default setup is applied
|
||||
- warning, Exceptions are thrown if read XML file is not valid
|
||||
@param retinaParameterFile the parameters filename
|
||||
@param applyDefaultSetupOnFailure set to true if an error must be thrown on error
|
||||
|
||||
You can retrieve the current parameters structure using the method Retina::getParameters and update
|
||||
it before running method Retina::setup.
|
||||
*/
|
||||
CV_WRAP virtual void setup(String retinaParameterFile="", const bool applyDefaultSetupOnFailure=true)=0;
|
||||
|
||||
/** @overload
|
||||
@param fs the open Filestorage which contains retina parameters
|
||||
@param applyDefaultSetupOnFailure set to true if an error must be thrown on error
|
||||
*/
|
||||
virtual void setup(cv::FileStorage &fs, const bool applyDefaultSetupOnFailure=true)=0;
|
||||
|
||||
/** @overload
|
||||
@param newParameters a parameters structures updated with the new target configuration.
|
||||
*/
|
||||
virtual void setup(RetinaParameters newParameters)=0;
|
||||
|
||||
/**
|
||||
@return the current parameters setup
|
||||
*/
|
||||
virtual RetinaParameters getParameters()=0;
|
||||
|
||||
/** @brief Outputs a string showing the used parameters setup
|
||||
@return a string which contains formated parameters information
|
||||
*/
|
||||
CV_WRAP virtual String printSetup()=0;
|
||||
|
||||
/** @brief Write xml/yml formated parameters information
|
||||
@param fs the filename of the xml file that will be open and writen with formatted parameters
|
||||
information
|
||||
*/
|
||||
CV_WRAP virtual void write( String fs ) const=0;
|
||||
|
||||
/** @overload */
|
||||
virtual void write( FileStorage& fs ) const CV_OVERRIDE = 0;
|
||||
|
||||
/** @brief Setup the OPL and IPL parvo channels (see biologocal model)
|
||||
|
||||
OPL is referred as Outer Plexiform Layer of the retina, it allows the spatio-temporal filtering
|
||||
which withens the spectrum and reduces spatio-temporal noise while attenuating global luminance
|
||||
(low frequency energy) IPL parvo is the OPL next processing stage, it refers to a part of the
|
||||
Inner Plexiform layer of the retina, it allows high contours sensitivity in foveal vision. See
|
||||
reference papers for more informations.
|
||||
for more informations, please have a look at the paper Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
@param colorMode specifies if (true) color is processed of not (false) to then processing gray
|
||||
level image
|
||||
@param normaliseOutput specifies if (true) output is rescaled between 0 and 255 of not (false)
|
||||
@param photoreceptorsLocalAdaptationSensitivity the photoreceptors sensitivity renage is 0-1
|
||||
(more log compression effect when value increases)
|
||||
@param photoreceptorsTemporalConstant the time constant of the first order low pass filter of
|
||||
the photoreceptors, use it to cut high temporal frequencies (noise or fast motion), unit is
|
||||
frames, typical value is 1 frame
|
||||
@param photoreceptorsSpatialConstant the spatial constant of the first order low pass filter of
|
||||
the photoreceptors, use it to cut high spatial frequencies (noise or thick contours), unit is
|
||||
pixels, typical value is 1 pixel
|
||||
@param horizontalCellsGain gain of the horizontal cells network, if 0, then the mean value of
|
||||
the output is zero, if the parameter is near 1, then, the luminance is not filtered and is
|
||||
still reachable at the output, typicall value is 0
|
||||
@param HcellsTemporalConstant the time constant of the first order low pass filter of the
|
||||
horizontal cells, use it to cut low temporal frequencies (local luminance variations), unit is
|
||||
frames, typical value is 1 frame, as the photoreceptors
|
||||
@param HcellsSpatialConstant the spatial constant of the first order low pass filter of the
|
||||
horizontal cells, use it to cut low spatial frequencies (local luminance), unit is pixels,
|
||||
typical value is 5 pixel, this value is also used for local contrast computing when computing
|
||||
the local contrast adaptation at the ganglion cells level (Inner Plexiform Layer parvocellular
|
||||
channel model)
|
||||
@param ganglionCellsSensitivity the compression strengh of the ganglion cells local adaptation
|
||||
output, set a value between 0.6 and 1 for best results, a high value increases more the low
|
||||
value sensitivity... and the output saturates faster, recommended value: 0.7
|
||||
*/
|
||||
CV_WRAP virtual void setupOPLandIPLParvoChannel(const bool colorMode=true, const bool normaliseOutput = true, const float photoreceptorsLocalAdaptationSensitivity=0.7f, const float photoreceptorsTemporalConstant=0.5f, const float photoreceptorsSpatialConstant=0.53f, const float horizontalCellsGain=0.f, const float HcellsTemporalConstant=1.f, const float HcellsSpatialConstant=7.f, const float ganglionCellsSensitivity=0.7f)=0;
|
||||
|
||||
/** @brief Set parameters values for the Inner Plexiform Layer (IPL) magnocellular channel
|
||||
|
||||
this channel processes signals output from OPL processing stage in peripheral vision, it allows
|
||||
motion information enhancement. It is decorrelated from the details channel. See reference
|
||||
papers for more details.
|
||||
|
||||
@param normaliseOutput specifies if (true) output is rescaled between 0 and 255 of not (false)
|
||||
@param parasolCells_beta the low pass filter gain used for local contrast adaptation at the
|
||||
IPL level of the retina (for ganglion cells local adaptation), typical value is 0
|
||||
@param parasolCells_tau the low pass filter time constant used for local contrast adaptation
|
||||
at the IPL level of the retina (for ganglion cells local adaptation), unit is frame, typical
|
||||
value is 0 (immediate response)
|
||||
@param parasolCells_k the low pass filter spatial constant used for local contrast adaptation
|
||||
at the IPL level of the retina (for ganglion cells local adaptation), unit is pixels, typical
|
||||
value is 5
|
||||
@param amacrinCellsTemporalCutFrequency the time constant of the first order high pass fiter of
|
||||
the magnocellular way (motion information channel), unit is frames, typical value is 1.2
|
||||
@param V0CompressionParameter the compression strengh of the ganglion cells local adaptation
|
||||
output, set a value between 0.6 and 1 for best results, a high value increases more the low
|
||||
value sensitivity... and the output saturates faster, recommended value: 0.95
|
||||
@param localAdaptintegration_tau specifies the temporal constant of the low pas filter
|
||||
involved in the computation of the local "motion mean" for the local adaptation computation
|
||||
@param localAdaptintegration_k specifies the spatial constant of the low pas filter involved
|
||||
in the computation of the local "motion mean" for the local adaptation computation
|
||||
*/
|
||||
CV_WRAP virtual void setupIPLMagnoChannel(const bool normaliseOutput = true, const float parasolCells_beta=0.f, const float parasolCells_tau=0.f, const float parasolCells_k=7.f, const float amacrinCellsTemporalCutFrequency=1.2f, const float V0CompressionParameter=0.95f, const float localAdaptintegration_tau=0.f, const float localAdaptintegration_k=7.f)=0;
|
||||
|
||||
/** @brief Method which allows retina to be applied on an input image,
|
||||
|
||||
after run, encapsulated retina module is ready to deliver its outputs using dedicated
|
||||
acccessors, see getParvo and getMagno methods
|
||||
@param inputImage the input Mat image to be processed, can be gray level or BGR coded in any
|
||||
format (from 8bit to 16bits)
|
||||
*/
|
||||
CV_WRAP virtual void run(InputArray inputImage)=0;
|
||||
|
||||
/** @brief Method which processes an image in the aim to correct its luminance correct
|
||||
backlight problems, enhance details in shadows.
|
||||
|
||||
This method is designed to perform High Dynamic Range image tone mapping (compress \>8bit/pixel
|
||||
images to 8bit/pixel). This is a simplified version of the Retina Parvocellular model
|
||||
(simplified version of the run/getParvo methods call) since it does not include the
|
||||
spatio-temporal filter modelling the Outer Plexiform Layer of the retina that performs spectral
|
||||
whitening and many other stuff. However, it works great for tone mapping and in a faster way.
|
||||
|
||||
Check the demos and experiments section to see examples and the way to perform tone mapping
|
||||
using the original retina model and the method.
|
||||
|
||||
@param inputImage the input image to process (should be coded in float format : CV_32F,
|
||||
CV_32FC1, CV_32F_C3, CV_32F_C4, the 4th channel won't be considered).
|
||||
@param outputToneMappedImage the output 8bit/channel tone mapped image (CV_8U or CV_8UC3 format).
|
||||
*/
|
||||
CV_WRAP virtual void applyFastToneMapping(InputArray inputImage, OutputArray outputToneMappedImage)=0;
|
||||
|
||||
/** @brief Accessor of the details channel of the retina (models foveal vision).
|
||||
|
||||
Warning, getParvoRAW methods return buffers that are not rescaled within range [0;255] while
|
||||
the non RAW method allows a normalized matrix to be retrieved.
|
||||
|
||||
@param retinaOutput_parvo the output buffer (reallocated if necessary), format can be :
|
||||
- a Mat, this output is rescaled for standard 8bits image processing use in OpenCV
|
||||
- RAW methods actually return a 1D matrix (encoding is R1, R2, ... Rn, G1, G2, ..., Gn, B1,
|
||||
B2, ...Bn), this output is the original retina filter model output, without any
|
||||
quantification or rescaling.
|
||||
@see getParvoRAW
|
||||
*/
|
||||
CV_WRAP virtual void getParvo(OutputArray retinaOutput_parvo)=0;
|
||||
|
||||
/** @brief Accessor of the details channel of the retina (models foveal vision).
|
||||
@see getParvo
|
||||
*/
|
||||
CV_WRAP virtual void getParvoRAW(OutputArray retinaOutput_parvo)=0;
|
||||
|
||||
/** @brief Accessor of the motion channel of the retina (models peripheral vision).
|
||||
|
||||
Warning, getMagnoRAW methods return buffers that are not rescaled within range [0;255] while
|
||||
the non RAW method allows a normalized matrix to be retrieved.
|
||||
@param retinaOutput_magno the output buffer (reallocated if necessary), format can be :
|
||||
- a Mat, this output is rescaled for standard 8bits image processing use in OpenCV
|
||||
- RAW methods actually return a 1D matrix (encoding is M1, M2,... Mn), this output is the
|
||||
original retina filter model output, without any quantification or rescaling.
|
||||
@see getMagnoRAW
|
||||
*/
|
||||
CV_WRAP virtual void getMagno(OutputArray retinaOutput_magno)=0;
|
||||
|
||||
/** @brief Accessor of the motion channel of the retina (models peripheral vision).
|
||||
@see getMagno
|
||||
*/
|
||||
CV_WRAP virtual void getMagnoRAW(OutputArray retinaOutput_magno)=0;
|
||||
|
||||
/** @overload */
|
||||
CV_WRAP virtual Mat getMagnoRAW() const=0;
|
||||
/** @overload */
|
||||
CV_WRAP virtual Mat getParvoRAW() const=0;
|
||||
|
||||
/** @brief Activate color saturation as the final step of the color demultiplexing process -\> this
|
||||
saturation is a sigmoide function applied to each channel of the demultiplexed image.
|
||||
@param saturateColors boolean that activates color saturation (if true) or desactivate (if false)
|
||||
@param colorSaturationValue the saturation factor : a simple factor applied on the chrominance
|
||||
buffers
|
||||
*/
|
||||
CV_WRAP virtual void setColorSaturation(const bool saturateColors=true, const float colorSaturationValue=4.0f)=0;
|
||||
|
||||
/** @brief Clears all retina buffers
|
||||
|
||||
(equivalent to opening the eyes after a long period of eye close ;o) whatchout the temporal
|
||||
transition occuring just after this method call.
|
||||
*/
|
||||
CV_WRAP virtual void clearBuffers()=0;
|
||||
|
||||
/** @brief Activate/desactivate the Magnocellular pathway processing (motion information extraction), by
|
||||
default, it is activated
|
||||
@param activate true if Magnocellular output should be activated, false if not... if activated,
|
||||
the Magnocellular output can be retrieved using the **getMagno** methods
|
||||
*/
|
||||
CV_WRAP virtual void activateMovingContoursProcessing(const bool activate)=0;
|
||||
|
||||
/** @brief Activate/desactivate the Parvocellular pathway processing (contours information extraction), by
|
||||
default, it is activated
|
||||
@param activate true if Parvocellular (contours information extraction) output should be
|
||||
activated, false if not... if activated, the Parvocellular output can be retrieved using the
|
||||
Retina::getParvo methods
|
||||
*/
|
||||
CV_WRAP virtual void activateContoursProcessing(const bool activate)=0;
|
||||
|
||||
/** @overload */
|
||||
CV_WRAP static Ptr<Retina> create(Size inputSize);
|
||||
/** @brief Constructors from standardized interfaces : retreive a smart pointer to a Retina instance
|
||||
|
||||
@param inputSize the input frame size
|
||||
@param colorMode the chosen processing mode : with or without color processing
|
||||
@param colorSamplingMethod specifies which kind of color sampling will be used :
|
||||
- cv::bioinspired::RETINA_COLOR_RANDOM: each pixel position is either R, G or B in a random choice
|
||||
- cv::bioinspired::RETINA_COLOR_DIAGONAL: color sampling is RGBRGBRGB..., line 2 BRGBRGBRG..., line 3, GBRGBRGBR...
|
||||
- cv::bioinspired::RETINA_COLOR_BAYER: standard bayer sampling
|
||||
@param useRetinaLogSampling activate retina log sampling, if true, the 2 following parameters can
|
||||
be used
|
||||
@param reductionFactor only usefull if param useRetinaLogSampling=true, specifies the reduction
|
||||
factor of the output frame (as the center (fovea) is high resolution and corners can be
|
||||
underscaled, then a reduction of the output is allowed without precision leak
|
||||
@param samplingStrength only usefull if param useRetinaLogSampling=true, specifies the strength of
|
||||
the log scale that is applied
|
||||
*/
|
||||
CV_WRAP static Ptr<Retina> create(Size inputSize, const bool colorMode,
|
||||
int colorSamplingMethod=RETINA_COLOR_BAYER,
|
||||
const bool useRetinaLogSampling=false,
|
||||
const float reductionFactor=1.0f, const float samplingStrength=10.0f);
|
||||
};
|
||||
|
||||
//! @}
|
||||
|
||||
}
|
||||
}
|
||||
#endif /* __OPENCV_BIOINSPIRED_RETINA_HPP__ */
|
||||
@@ -0,0 +1,138 @@
|
||||
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2013
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** This class is based on image processing tools of the author and already used within the Retina class (this is the same code as method retina::applyFastToneMapping, but in an independent class, it is light from a memory requirement point of view). It implements an adaptation of the efficient tone mapping algorithm propose by David Alleyson, Sabine Susstruck and Laurence Meylan's work, please cite:
|
||||
** -> Meylan L., Alleysson D., and Susstrunk S., A Model of Retinal Local Adaptation for the Tone Mapping of Color Filter Array Images, Journal of Optical Society of America, A, Vol. 24, N 9, September, 1st, 2007, pp. 2807-2816
|
||||
**
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __OPENCV_BIOINSPIRED_RETINAFASTTONEMAPPING_HPP__
|
||||
#define __OPENCV_BIOINSPIRED_RETINAFASTTONEMAPPING_HPP__
|
||||
|
||||
/**
|
||||
@file
|
||||
@date May 26, 2013
|
||||
@author Alexandre Benoit
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp" // for all OpenCV core functionalities access, including cv::Exception support
|
||||
|
||||
namespace cv{
|
||||
namespace bioinspired{
|
||||
|
||||
//! @addtogroup bioinspired
|
||||
//! @{
|
||||
|
||||
/** @brief a wrapper class which allows the tone mapping algorithm of Meylan&al(2007) to be used with OpenCV.
|
||||
|
||||
This algorithm is already implemented in thre Retina class (retina::applyFastToneMapping) but used it does not require all the retina model to be allocated. This allows a light memory use for low memory devices (smartphones, etc.
|
||||
As a summary, these are the model properties:
|
||||
- 2 stages of local luminance adaptation with a different local neighborhood for each.
|
||||
- first stage models the retina photorecetors local luminance adaptation
|
||||
- second stage models th ganglion cells local information adaptation
|
||||
- compared to the initial publication, this class uses spatio-temporal low pass filters instead of spatial only filters.
|
||||
this can help noise robustness and temporal stability for video sequence use cases.
|
||||
|
||||
for more information, read to the following papers :
|
||||
Meylan L., Alleysson D., and Susstrunk S., A Model of Retinal Local Adaptation for the Tone Mapping of Color Filter Array Images, Journal of Optical Society of America, A, Vol. 24, N 9, September, 1st, 2007, pp. 2807-2816Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
regarding spatio-temporal filter and the bigger retina model :
|
||||
Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
*/
|
||||
class CV_EXPORTS_W RetinaFastToneMapping : public Algorithm
|
||||
{
|
||||
public:
|
||||
|
||||
/** @brief applies a luminance correction (initially High Dynamic Range (HDR) tone mapping)
|
||||
|
||||
using only the 2 local adaptation stages of the retina parvocellular channel : photoreceptors
|
||||
level and ganlion cells level. Spatio temporal filtering is applied but limited to temporal
|
||||
smoothing and eventually high frequencies attenuation. This is a lighter method than the one
|
||||
available using the regular retina::run method. It is then faster but it does not include
|
||||
complete temporal filtering nor retina spectral whitening. Then, it can have a more limited
|
||||
effect on images with a very high dynamic range. This is an adptation of the original still
|
||||
image HDR tone mapping algorithm of David Alleyson, Sabine Susstruck and Laurence Meylan's
|
||||
work, please cite: -> Meylan L., Alleysson D., and Susstrunk S., A Model of Retinal Local
|
||||
Adaptation for the Tone Mapping of Color Filter Array Images, Journal of Optical Society of
|
||||
America, A, Vol. 24, N 9, September, 1st, 2007, pp. 2807-2816
|
||||
|
||||
@param inputImage the input image to process RGB or gray levels
|
||||
@param outputToneMappedImage the output tone mapped image
|
||||
*/
|
||||
CV_WRAP virtual void applyFastToneMapping(InputArray inputImage, OutputArray outputToneMappedImage)=0;
|
||||
|
||||
/** @brief updates tone mapping behaviors by adjusing the local luminance computation area
|
||||
|
||||
@param photoreceptorsNeighborhoodRadius the first stage local adaptation area
|
||||
@param ganglioncellsNeighborhoodRadius the second stage local adaptation area
|
||||
@param meanLuminanceModulatorK the factor applied to modulate the meanLuminance information
|
||||
(default is 1, see reference paper)
|
||||
*/
|
||||
CV_WRAP virtual void setup(const float photoreceptorsNeighborhoodRadius=3.f, const float ganglioncellsNeighborhoodRadius=1.f, const float meanLuminanceModulatorK=1.f)=0;
|
||||
|
||||
CV_WRAP static Ptr<RetinaFastToneMapping> create(Size inputSize);
|
||||
};
|
||||
|
||||
|
||||
//! @}
|
||||
|
||||
}
|
||||
}
|
||||
#endif /* __OPENCV_BIOINSPIRED_RETINAFASTTONEMAPPING_HPP__ */
|
||||
@@ -0,0 +1,204 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models.
|
||||
** TransientAreasSegmentationModule Use: extract areas that present spatio-temporal changes.
|
||||
** => It should be used at the output of the cv::bioinspired::Retina::getMagnoRAW() output that enhances spatio-temporal changes
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2015
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Strat, S.T.; Benoit, A.; Lambert, P., "Retina enhanced bag of words descriptors for video classification," Signal Processing Conference (EUSIPCO), 2014 Proceedings of the 22nd European , vol., no., pp.1307,1311, 1-5 Sept. 2014 (http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=6952461&isnumber=6951911)
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2015, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SEGMENTATIONMODULE_HPP_
|
||||
#define SEGMENTATIONMODULE_HPP_
|
||||
|
||||
/**
|
||||
@file
|
||||
@date 2007-2013
|
||||
@author Alexandre BENOIT, benoit.alexandre.vision@gmail.com
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp" // for all OpenCV core functionalities access, including cv::Exception support
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
//! @addtogroup bioinspired
|
||||
//! @{
|
||||
|
||||
/** @brief parameter structure that stores the transient events detector setup parameters
|
||||
*/
|
||||
struct SegmentationParameters{ // CV_EXPORTS_W_MAP to export to python native dictionnaries
|
||||
// default structure instance construction with default values
|
||||
SegmentationParameters():
|
||||
thresholdON(100),
|
||||
thresholdOFF(100),
|
||||
localEnergy_temporalConstant(0.5),
|
||||
localEnergy_spatialConstant(5),
|
||||
neighborhoodEnergy_temporalConstant(1),
|
||||
neighborhoodEnergy_spatialConstant(15),
|
||||
contextEnergy_temporalConstant(1),
|
||||
contextEnergy_spatialConstant(75){};
|
||||
// all properties list
|
||||
float thresholdON;
|
||||
float thresholdOFF;
|
||||
//! the time constant of the first order low pass filter, use it to cut high temporal frequencies (noise or fast motion), unit is frames, typical value is 0.5 frame
|
||||
float localEnergy_temporalConstant;
|
||||
//! the spatial constant of the first order low pass filter, use it to cut high spatial frequencies (noise or thick contours), unit is pixels, typical value is 5 pixel
|
||||
float localEnergy_spatialConstant;
|
||||
//! local neighborhood energy filtering parameters : the aim is to get information about the energy neighborhood to perform a center surround energy analysis
|
||||
float neighborhoodEnergy_temporalConstant;
|
||||
float neighborhoodEnergy_spatialConstant;
|
||||
//! context neighborhood energy filtering parameters : the aim is to get information about the energy on a wide neighborhood area to filtered out local effects
|
||||
float contextEnergy_temporalConstant;
|
||||
float contextEnergy_spatialConstant;
|
||||
};
|
||||
|
||||
/** @brief class which provides a transient/moving areas segmentation module
|
||||
|
||||
perform a locally adapted segmentation by using the retina magno input data Based on Alexandre
|
||||
BENOIT thesis: "Le système visuel humain au secours de la vision par ordinateur"
|
||||
|
||||
3 spatio temporal filters are used:
|
||||
- a first one which filters the noise and local variations of the input motion energy
|
||||
- a second (more powerfull low pass spatial filter) which gives the neighborhood motion energy the
|
||||
segmentation consists in the comparison of these both outputs, if the local motion energy is higher
|
||||
to the neighborhood otion energy, then the area is considered as moving and is segmented
|
||||
- a stronger third low pass filter helps decision by providing a smooth information about the
|
||||
"motion context" in a wider area
|
||||
*/
|
||||
|
||||
class CV_EXPORTS_W TransientAreasSegmentationModule: public Algorithm
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
/** @brief return the sze of the manage input and output images
|
||||
*/
|
||||
CV_WRAP virtual Size getSize()=0;
|
||||
|
||||
/** @brief try to open an XML segmentation parameters file to adjust current segmentation instance setup
|
||||
|
||||
- if the xml file does not exist, then default setup is applied
|
||||
- warning, Exceptions are thrown if read XML file is not valid
|
||||
@param segmentationParameterFile : the parameters filename
|
||||
@param applyDefaultSetupOnFailure : set to true if an error must be thrown on error
|
||||
*/
|
||||
CV_WRAP virtual void setup(String segmentationParameterFile="", const bool applyDefaultSetupOnFailure=true)=0;
|
||||
|
||||
/** @brief try to open an XML segmentation parameters file to adjust current segmentation instance setup
|
||||
|
||||
- if the xml file does not exist, then default setup is applied
|
||||
- warning, Exceptions are thrown if read XML file is not valid
|
||||
@param fs : the open Filestorage which contains segmentation parameters
|
||||
@param applyDefaultSetupOnFailure : set to true if an error must be thrown on error
|
||||
*/
|
||||
virtual void setup(cv::FileStorage &fs, const bool applyDefaultSetupOnFailure=true)=0;
|
||||
|
||||
/** @brief try to open an XML segmentation parameters file to adjust current segmentation instance setup
|
||||
|
||||
- if the xml file does not exist, then default setup is applied
|
||||
- warning, Exceptions are thrown if read XML file is not valid
|
||||
@param newParameters : a parameters structures updated with the new target configuration
|
||||
*/
|
||||
virtual void setup(SegmentationParameters newParameters)=0;
|
||||
|
||||
/** @brief return the current parameters setup
|
||||
*/
|
||||
virtual SegmentationParameters getParameters()=0;
|
||||
|
||||
/** @brief parameters setup display method
|
||||
@return a string which contains formatted parameters information
|
||||
*/
|
||||
CV_WRAP virtual String printSetup()=0;
|
||||
|
||||
/** @brief write xml/yml formated parameters information
|
||||
@param fs : the filename of the xml file that will be open and writen with formatted parameters information
|
||||
*/
|
||||
CV_WRAP virtual void write( String fs ) const=0;
|
||||
|
||||
/** @brief write xml/yml formated parameters information
|
||||
@param fs : a cv::Filestorage object ready to be filled
|
||||
*/
|
||||
virtual void write( cv::FileStorage& fs ) const CV_OVERRIDE = 0;
|
||||
|
||||
/** @brief main processing method, get result using methods getSegmentationPicture()
|
||||
@param inputToSegment : the image to process, it must match the instance buffer size !
|
||||
@param channelIndex : the channel to process in case of multichannel images
|
||||
*/
|
||||
CV_WRAP virtual void run(InputArray inputToSegment, const int channelIndex=0)=0;
|
||||
|
||||
/** @brief access function
|
||||
return the last segmentation result: a boolean picture which is resampled between 0 and 255 for a display purpose
|
||||
*/
|
||||
CV_WRAP virtual void getSegmentationPicture(OutputArray transientAreas)=0;
|
||||
|
||||
/** @brief cleans all the buffers of the instance
|
||||
*/
|
||||
CV_WRAP virtual void clearAllBuffers()=0;
|
||||
|
||||
/** @brief allocator
|
||||
@param inputSize : size of the images input to segment (output will be the same size)
|
||||
*/
|
||||
CV_WRAP static Ptr<TransientAreasSegmentationModule> create(Size inputSize);
|
||||
};
|
||||
|
||||
//! @}
|
||||
|
||||
}} // namespaces end : cv and bioinspired
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"AdditionalImports" : {
|
||||
"*" : [ "\"bioinspired.hpp\"" ]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#include "../perf_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_perf.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
///////////////////////// Retina ////////////////////////
|
||||
|
||||
typedef tuple<bool, int, double, double> RetinaParams;
|
||||
typedef TestBaseWithParam<RetinaParams> RetinaFixture;
|
||||
|
||||
OCL_PERF_TEST_P(RetinaFixture, Retina,
|
||||
::testing::Combine(testing::Bool(), testing::Values((int)cv::bioinspired::RETINA_COLOR_BAYER),
|
||||
testing::Values(1.0, 0.5), testing::Values(10.0, 5.0)))
|
||||
{
|
||||
RetinaParams params = GetParam();
|
||||
bool colorMode = get<0>(params), useLogSampling = false;
|
||||
int colorSamplingMethod = get<1>(params);
|
||||
float reductionFactor = static_cast<float>(get<2>(params));
|
||||
float samplingStrength = static_cast<float>(get<3>(params));
|
||||
|
||||
Mat input = imread(getDataPath("cv/shared/lena.png"), colorMode);
|
||||
ASSERT_FALSE(input.empty());
|
||||
|
||||
UMat ocl_parvo, ocl_magno;
|
||||
|
||||
{
|
||||
Ptr<cv::bioinspired::Retina> retina = cv::bioinspired::Retina::create(
|
||||
input.size(), colorMode, colorSamplingMethod, useLogSampling,
|
||||
reductionFactor, samplingStrength);
|
||||
|
||||
OCL_TEST_CYCLE()
|
||||
{
|
||||
retina->run(input);
|
||||
retina->getParvo(ocl_parvo);
|
||||
retina->getMagno(ocl_magno);
|
||||
}
|
||||
}
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,45 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors as is and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
CV_PERF_TEST_MAIN(bioinspired)
|
||||
@@ -0,0 +1,14 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#ifndef __OPENCV_TEST_PRECOMP_HPP__
|
||||
#define __OPENCV_TEST_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/opencv_modules.hpp"
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/ts/ts_perf.hpp"
|
||||
#include "opencv2/bioinspired.hpp"
|
||||
|
||||
using namespace perf;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,306 @@
|
||||
|
||||
//============================================================================
|
||||
// Name : OpenEXRimages_HDR_Retina_toneMapping.cpp
|
||||
// Author : Alexandre Benoit (benoit.alexandre.vision@gmail.com)
|
||||
// Version : 0.1
|
||||
// Copyright : Alexandre Benoit, LISTIC Lab, july 2011
|
||||
// Description : HighDynamicRange retina tone mapping with the help of the Gipsa/Listic's retina in C++, Ansi-style
|
||||
//============================================================================
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#include "opencv2/bioinspired.hpp" // retina based algorithms
|
||||
#include "opencv2/imgproc.hpp" // cvCvtcolor function
|
||||
#include "opencv2/imgcodecs.hpp" // imread
|
||||
#include "opencv2/highgui.hpp" // display
|
||||
|
||||
static void help(std::string errorMessage)
|
||||
{
|
||||
std::cout<<"Program init error : "<<errorMessage<<std::endl;
|
||||
std::cout<<"\nProgram call procedure : ./OpenEXRimages_HDR_Retina_toneMapping [OpenEXR image to process]"<<std::endl;
|
||||
std::cout<<"\t[OpenEXR image to process] : the input HDR image to process, must be an OpenEXR format, see http://www.openexr.com/ to get some samples or create your own using camera bracketing and Photoshop or equivalent software for OpenEXR image synthesis"<<std::endl;
|
||||
std::cout<<"\nExamples:"<<std::endl;
|
||||
std::cout<<"\t-Image processing : ./OpenEXRimages_HDR_Retina_toneMapping memorial.exr"<<std::endl;
|
||||
}
|
||||
|
||||
// simple procedure for 1D curve tracing
|
||||
static void drawPlot(const cv::Mat curve, const std::string figureTitle, const int lowerLimit, const int upperLimit)
|
||||
{
|
||||
//std::cout<<"curve size(h,w) = "<<curve.size().height<<", "<<curve.size().width<<std::endl;
|
||||
cv::Mat displayedCurveImage = cv::Mat::ones(200, curve.size().height, CV_8U);
|
||||
|
||||
cv::Mat windowNormalizedCurve;
|
||||
normalize(curve, windowNormalizedCurve, 0, 200, cv::NORM_MINMAX, CV_32F);
|
||||
|
||||
displayedCurveImage = cv::Scalar::all(255); // set a white background
|
||||
int binW = cvRound((double)displayedCurveImage.cols/curve.size().height);
|
||||
|
||||
for( int i = 0; i < curve.size().height; i++ )
|
||||
rectangle( displayedCurveImage, cv::Point(i*binW, displayedCurveImage.rows),
|
||||
cv::Point((i+1)*binW, displayedCurveImage.rows - cvRound(windowNormalizedCurve.at<float>(i))),
|
||||
cv::Scalar::all(0), -1, 8, 0 );
|
||||
rectangle( displayedCurveImage, cv::Point(0, 0),
|
||||
cv::Point((lowerLimit)*binW, 200),
|
||||
cv::Scalar::all(128), -1, 8, 0 );
|
||||
rectangle( displayedCurveImage, cv::Point(displayedCurveImage.cols, 0),
|
||||
cv::Point((upperLimit)*binW, 200),
|
||||
cv::Scalar::all(128), -1, 8, 0 );
|
||||
|
||||
cv::imshow(figureTitle, displayedCurveImage);
|
||||
}
|
||||
/*
|
||||
* objective : get the gray level map of the input image and rescale it to the range [0-255]
|
||||
*/
|
||||
static void rescaleGrayLevelMat(const cv::Mat &inputMat, cv::Mat &outputMat, const float histogramClippingLimit)
|
||||
{
|
||||
|
||||
// adjust output matrix wrt the input size but single channel
|
||||
std::cout<<"Input image rescaling with histogram edges cutting (in order to eliminate bad pixels created during the HDR image creation) :"<<std::endl;
|
||||
//std::cout<<"=> image size (h,w,channels) = "<<inputMat.size().height<<", "<<inputMat.size().width<<", "<<inputMat.channels()<<std::endl;
|
||||
//std::cout<<"=> pixel coding (nbchannel, bytes per channel) = "<<inputMat.elemSize()/inputMat.elemSize1()<<", "<<inputMat.elemSize1()<<std::endl;
|
||||
|
||||
// rescale between 0-255, keeping floating point values
|
||||
cv::normalize(inputMat, outputMat, 0.0, 255.0, cv::NORM_MINMAX);
|
||||
|
||||
// extract a 8bit image that will be used for histogram edge cut
|
||||
cv::Mat intGrayImage;
|
||||
if (inputMat.channels()==1)
|
||||
{
|
||||
outputMat.convertTo(intGrayImage, CV_8U);
|
||||
}else
|
||||
{
|
||||
cv::Mat rgbIntImg;
|
||||
outputMat.convertTo(rgbIntImg, CV_8UC3);
|
||||
cvtColor(rgbIntImg, intGrayImage, cv::COLOR_BGR2GRAY);
|
||||
}
|
||||
|
||||
// get histogram density probability in order to cut values under above edges limits (here 5-95%)... useful for HDR pixel errors cancellation
|
||||
cv::Mat dst, hist;
|
||||
int histSize = 256;
|
||||
calcHist(&intGrayImage, 1, 0, cv::Mat(), hist, 1, &histSize, 0);
|
||||
cv::Mat normalizedHist;
|
||||
normalize(hist, normalizedHist, 1, 0, cv::NORM_L1, CV_32F); // normalize histogram so that its sum equals 1
|
||||
|
||||
double min_val, max_val;
|
||||
minMaxLoc(normalizedHist, &min_val, &max_val);
|
||||
//std::cout<<"Hist max,min = "<<max_val<<", "<<min_val<<std::endl;
|
||||
|
||||
// compute density probability
|
||||
cv::Mat denseProb=cv::Mat::zeros(normalizedHist.size(), CV_32F);
|
||||
denseProb.at<float>(0)=normalizedHist.at<float>(0);
|
||||
int histLowerLimit=0, histUpperLimit=0;
|
||||
for (int i=1;i<normalizedHist.size().height;++i)
|
||||
{
|
||||
denseProb.at<float>(i)=denseProb.at<float>(i-1)+normalizedHist.at<float>(i);
|
||||
//std::cout<<normalizedHist.at<float>(i)<<", "<<denseProb.at<float>(i)<<std::endl;
|
||||
if ( denseProb.at<float>(i)<histogramClippingLimit)
|
||||
histLowerLimit=i;
|
||||
if ( denseProb.at<float>(i)<1-histogramClippingLimit)
|
||||
histUpperLimit=i;
|
||||
}
|
||||
// deduce min and max admitted gray levels
|
||||
float minInputValue = (float)histLowerLimit/histSize*255;
|
||||
float maxInputValue = (float)histUpperLimit/histSize*255;
|
||||
|
||||
std::cout<<"=> Histogram limits "
|
||||
<<"\n\t"<<histogramClippingLimit*100<<"% index = "<<histLowerLimit<<" => normalizedHist value = "<<denseProb.at<float>(histLowerLimit)<<" => input gray level = "<<minInputValue
|
||||
<<"\n\t"<<(1-histogramClippingLimit)*100<<"% index = "<<histUpperLimit<<" => normalizedHist value = "<<denseProb.at<float>(histUpperLimit)<<" => input gray level = "<<maxInputValue
|
||||
<<std::endl;
|
||||
//drawPlot(denseProb, "input histogram density probability", histLowerLimit, histUpperLimit);
|
||||
drawPlot(normalizedHist, "input histogram", histLowerLimit, histUpperLimit);
|
||||
|
||||
// rescale image range [minInputValue-maxInputValue] to [0-255]
|
||||
outputMat-=minInputValue;
|
||||
outputMat*=255.0/(maxInputValue-minInputValue);
|
||||
// cut original histogram and back project to original image
|
||||
cv::threshold( outputMat, outputMat, 255.0, 255.0, 2 ); //THRESH_TRUNC, clips values above 255
|
||||
cv::threshold( outputMat, outputMat, 0.0, 0.0, 3 ); //THRESH_TOZERO, clips values under 0
|
||||
|
||||
}
|
||||
// basic callback method for interface management
|
||||
cv::Mat inputImage;
|
||||
cv::Mat imageInputRescaled;
|
||||
int histogramClippingValue;
|
||||
static void callBack_rescaleGrayLevelMat(int, void*)
|
||||
{
|
||||
std::cout<<"Histogram clipping value changed, current value = "<<histogramClippingValue<<std::endl;
|
||||
rescaleGrayLevelMat(inputImage, imageInputRescaled, (float)(histogramClippingValue/100.0));
|
||||
normalize(imageInputRescaled, imageInputRescaled, 0.0, 255.0, cv::NORM_MINMAX);
|
||||
}
|
||||
|
||||
cv::Ptr<cv::bioinspired::Retina> retina;
|
||||
int retinaHcellsGain;
|
||||
int localAdaptation_photoreceptors, localAdaptation_Gcells;
|
||||
static void callBack_updateRetinaParams(int, void*)
|
||||
{
|
||||
retina->setupOPLandIPLParvoChannel(true, true, (float)(localAdaptation_photoreceptors/200.0), 0.5f, 0.43f, (float)retinaHcellsGain, 1.f, 7.f, (float)(localAdaptation_Gcells/200.0));
|
||||
}
|
||||
|
||||
int colorSaturationFactor;
|
||||
static void callback_saturateColors(int, void*)
|
||||
{
|
||||
retina->setColorSaturation(true, (float)colorSaturationFactor);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// welcome message
|
||||
std::cout<<"*********************************************************************************"<<std::endl;
|
||||
std::cout<<"* Retina demonstration for High Dynamic Range compression (tone-mapping) : demonstrates the use of a wrapper class of the Gipsa/Listic Labs retina model."<<std::endl;
|
||||
std::cout<<"* This retina model allows spatio-temporal image processing (applied on still images, video sequences)."<<std::endl;
|
||||
std::cout<<"* This demo focuses demonstration of the dynamic compression capabilities of the model"<<std::endl;
|
||||
std::cout<<"* => the main application is tone mapping of HDR images (i.e. see on a 8bit display a more than 8bits coded (up to 16bits) image with details in high and low luminance ranges"<<std::endl;
|
||||
std::cout<<"* The retina model still have the following properties:"<<std::endl;
|
||||
std::cout<<"* => It applies a spectral whithening (mid-frequency details enhancement)"<<std::endl;
|
||||
std::cout<<"* => high frequency spatio-temporal noise reduction"<<std::endl;
|
||||
std::cout<<"* => low frequency luminance to be reduced (luminance range compression)"<<std::endl;
|
||||
std::cout<<"* => local logarithmic luminance compression allows details to be enhanced in low light conditions\n"<<std::endl;
|
||||
std::cout<<"* for more information, reer to the following papers :"<<std::endl;
|
||||
std::cout<<"* Benoit A., Caplier A., Durette B., Herault, J., \"USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING\", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011"<<std::endl;
|
||||
std::cout<<"* Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891."<<std::endl;
|
||||
std::cout<<"* => reports comments/remarks at benoit.alexandre.vision@gmail.com"<<std::endl;
|
||||
std::cout<<"* => more informations and papers at : http://sites.google.com/site/benoitalexandrevision/"<<std::endl;
|
||||
std::cout<<"*********************************************************************************"<<std::endl;
|
||||
std::cout<<"** WARNING : this sample requires OpenCV to be configured with OpenEXR support **"<<std::endl;
|
||||
std::cout<<"*********************************************************************************"<<std::endl;
|
||||
std::cout<<"*** You can use free tools to generate OpenEXR images from images sets : ***"<<std::endl;
|
||||
std::cout<<"*** => 1. take a set of photos from the same viewpoint using bracketing ***"<<std::endl;
|
||||
std::cout<<"*** => 2. generate an OpenEXR image with tools like qtpfsgui.sourceforge.net ***"<<std::endl;
|
||||
std::cout<<"*** => 3. apply tone mapping with this program ***"<<std::endl;
|
||||
std::cout<<"*********************************************************************************"<<std::endl;
|
||||
|
||||
// basic input arguments checking
|
||||
if (argc<2)
|
||||
{
|
||||
help("bad number of parameter");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool useLogSampling = !strcmp(argv[argc-1], "log"); // check if user wants retina log sampling processing
|
||||
int chosenMethod=0;
|
||||
if (!strcmp(argv[argc-1], "fast"))
|
||||
{
|
||||
chosenMethod=1;
|
||||
std::cout<<"Using fast method (no spectral whithning), adaptation of Meylan&al 2008 method"<<std::endl;
|
||||
}
|
||||
|
||||
std::string inputImageName=argv[1];
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// checking input media type (still image, video file, live video acquisition)
|
||||
std::cout<<"RetinaDemo: processing image "<<inputImageName<<std::endl;
|
||||
// image processing case
|
||||
// declare the retina input buffer... that will be fed differently in regard of the input media
|
||||
inputImage = cv::imread(inputImageName, -1); // load image in RGB mode
|
||||
std::cout<<"=> image size (h,w) = "<<inputImage.size().height<<", "<<inputImage.size().width<<std::endl;
|
||||
if (!inputImage.total())
|
||||
{
|
||||
help("could not load image, program end");
|
||||
return -1;
|
||||
}
|
||||
// rescale between 0 and 1
|
||||
normalize(inputImage, inputImage, 0.0, 1.0, cv::NORM_MINMAX);
|
||||
cv::Mat gammaTransformedImage;
|
||||
cv::pow(inputImage, 1./5, gammaTransformedImage); // apply gamma curve: img = img ** (1./5)
|
||||
imshow("EXR image original image, 16bits=>8bits linear rescaling ", inputImage);
|
||||
imshow("EXR image with basic processing : 16bits=>8bits with gamma correction", gammaTransformedImage);
|
||||
if (inputImage.empty())
|
||||
{
|
||||
help("Input image could not be loaded, aborting");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Program start in a try/catch safety context (Retina may throw errors)
|
||||
try
|
||||
{
|
||||
/* create a retina instance with default parameters setup, uncomment the initialisation you wanna test
|
||||
* -> if the last parameter is 'log', then activate log sampling (favour foveal vision and subsamples peripheral vision)
|
||||
*/
|
||||
if (useLogSampling)
|
||||
{
|
||||
retina = cv::bioinspired::Retina::create(inputImage.size(),true, cv::bioinspired::RETINA_COLOR_BAYER, true, 2.0, 10.0);
|
||||
}
|
||||
else// -> else allocate "classical" retina :
|
||||
retina = cv::bioinspired::Retina::create(inputImage.size());
|
||||
|
||||
// create a fast retina tone mapper (Meyla&al algorithm)
|
||||
std::cout<<"Allocating fast tone mapper..."<<std::endl;
|
||||
//cv::Ptr<cv::RetinaFastToneMapping> fastToneMapper=createRetinaFastToneMapping(inputImage.size());
|
||||
std::cout<<"Fast tone mapper allocated"<<std::endl;
|
||||
|
||||
// save default retina parameters file in order to let you see this and maybe modify it and reload using method "setup"
|
||||
retina->write("RetinaDefaultParameters.xml");
|
||||
|
||||
// desactivate Magnocellular pathway processing (motion information extraction) since it is not useful here
|
||||
retina->activateMovingContoursProcessing(false);
|
||||
|
||||
// declare retina output buffers
|
||||
cv::Mat retinaOutput_parvo;
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// prepare displays and interactions
|
||||
histogramClippingValue=0; // default value... updated with interface slider
|
||||
//inputRescaleMat = inputImage;
|
||||
//outputRescaleMat = imageInputRescaled;
|
||||
cv::namedWindow("Processing configuration",1);
|
||||
cv::createTrackbar("histogram edges clipping limit", "Processing configuration",&histogramClippingValue,50,callBack_rescaleGrayLevelMat);
|
||||
|
||||
colorSaturationFactor=3;
|
||||
cv::createTrackbar("Color saturation", "Processing configuration", &colorSaturationFactor,5,callback_saturateColors);
|
||||
|
||||
retinaHcellsGain=40;
|
||||
cv::createTrackbar("Hcells gain", "Processing configuration",&retinaHcellsGain,100,callBack_updateRetinaParams);
|
||||
|
||||
localAdaptation_photoreceptors=197;
|
||||
localAdaptation_Gcells=190;
|
||||
cv::createTrackbar("Ph sensitivity", "Processing configuration", &localAdaptation_photoreceptors,199,callBack_updateRetinaParams);
|
||||
cv::createTrackbar("Gcells sensitivity", "Processing configuration", &localAdaptation_Gcells,199,callBack_updateRetinaParams);
|
||||
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// apply default parameters of user interaction variables
|
||||
rescaleGrayLevelMat(inputImage, imageInputRescaled, (float)histogramClippingValue/100);
|
||||
retina->setColorSaturation(true,(float)colorSaturationFactor);
|
||||
callBack_updateRetinaParams(1,NULL); // first call for default parameters setup
|
||||
|
||||
// processing loop with stop condition
|
||||
bool continueProcessing=true;
|
||||
while(continueProcessing)
|
||||
{
|
||||
// run retina filter
|
||||
if (!chosenMethod)
|
||||
{
|
||||
retina->run(imageInputRescaled);
|
||||
// Retrieve and display retina output
|
||||
retina->getParvo(retinaOutput_parvo);
|
||||
cv::imshow("Retina input image (with cut edges histogram for basic pixels error avoidance)", imageInputRescaled/255.0);
|
||||
cv::imshow("Retina Parvocellular pathway output : 16bit=>8bit image retina tonemapping", retinaOutput_parvo);
|
||||
cv::imwrite("HDRinput.jpg",imageInputRescaled/255.0);
|
||||
cv::imwrite("RetinaToneMapping.jpg",retinaOutput_parvo);
|
||||
}
|
||||
else
|
||||
{
|
||||
// apply the simplified hdr tone mapping method
|
||||
cv::Mat fastToneMappingOutput;
|
||||
retina->applyFastToneMapping(imageInputRescaled, fastToneMappingOutput);
|
||||
cv::imshow("Retina fast tone mapping output : 16bit=>8bit image retina tonemapping", fastToneMappingOutput);
|
||||
}
|
||||
/*cv::Mat fastToneMappingOutput_specificObject;
|
||||
fastToneMapper->setup(3.f, 1.5f, 1.f);
|
||||
fastToneMapper->applyFastToneMapping(imageInputRescaled, fastToneMappingOutput_specificObject);
|
||||
cv::imshow("### Retina fast tone mapping output : 16bit=>8bit image retina tonemapping", fastToneMappingOutput_specificObject);
|
||||
*/
|
||||
cv::waitKey(10);
|
||||
}
|
||||
} catch(const cv::Exception& e)
|
||||
{
|
||||
std::cerr<<"Error using Retina : "<<e.what()<<std::endl;
|
||||
}
|
||||
|
||||
// Program end message
|
||||
std::cout<<"Retina demo end"<<std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
|
||||
//============================================================================
|
||||
// Name : OpenEXRimages_HDR_Retina_toneMapping.cpp
|
||||
// Author : Alexandre Benoit (benoit.alexandre.vision@gmail.com)
|
||||
// Version : 0.1
|
||||
// Copyright : Alexandre Benoit, LISTIC Lab, july 2011
|
||||
// Description : HighDynamicRange retina tone mapping with the help of the Gipsa/Listic's retina in C++, Ansi-style
|
||||
//============================================================================
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#include "opencv2/bioinspired.hpp" // retina based algorithms
|
||||
#include "opencv2/imgproc.hpp" // cvCvtcolor function
|
||||
#include "opencv2/highgui.hpp" // display
|
||||
|
||||
static void help(std::string errorMessage)
|
||||
{
|
||||
std::cout<<"Program init error : "<<errorMessage<<std::endl;
|
||||
std::cout<<"\nProgram call procedure : ./OpenEXRimages_HDR_Retina_toneMapping [OpenEXR image to process]"<<std::endl;
|
||||
std::cout<<"\t[OpenEXR image to process] : the input HDR image to process, must be an OpenEXR format, see http://www.openexr.com/ to get some samples or create your own using camera bracketing and Photoshop or equivalent software for OpenEXR image synthesis"<<std::endl;
|
||||
std::cout<<"\nExamples:"<<std::endl;
|
||||
std::cout<<"\t-Image processing : ./OpenEXRimages_HDR_Retina_toneMapping memorial.exr"<<std::endl;
|
||||
}
|
||||
|
||||
// simple procedure for 1D curve tracing
|
||||
static void drawPlot(const cv::Mat curve, const std::string figureTitle, const int lowerLimit, const int upperLimit)
|
||||
{
|
||||
//std::cout<<"curve size(h,w) = "<<curve.size().height<<", "<<curve.size().width<<std::endl;
|
||||
cv::Mat displayedCurveImage = cv::Mat::ones(200, curve.size().height, CV_8U);
|
||||
|
||||
cv::Mat windowNormalizedCurve;
|
||||
normalize(curve, windowNormalizedCurve, 0, 200, cv::NORM_MINMAX, CV_32F);
|
||||
|
||||
displayedCurveImage = cv::Scalar::all(255); // set a white background
|
||||
int binW = cvRound((double)displayedCurveImage.cols/curve.size().height);
|
||||
|
||||
for( int i = 0; i < curve.size().height; i++ )
|
||||
rectangle( displayedCurveImage, cv::Point(i*binW, displayedCurveImage.rows),
|
||||
cv::Point((i+1)*binW, displayedCurveImage.rows - cvRound(windowNormalizedCurve.at<float>(i))),
|
||||
cv::Scalar::all(0), -1, 8, 0 );
|
||||
rectangle( displayedCurveImage, cv::Point(0, 0),
|
||||
cv::Point((lowerLimit)*binW, 200),
|
||||
cv::Scalar::all(128), -1, 8, 0 );
|
||||
rectangle( displayedCurveImage, cv::Point(displayedCurveImage.cols, 0),
|
||||
cv::Point((upperLimit)*binW, 200),
|
||||
cv::Scalar::all(128), -1, 8, 0 );
|
||||
|
||||
cv::imshow(figureTitle, displayedCurveImage);
|
||||
}
|
||||
/*
|
||||
* objective : get the gray level map of the input image and rescale it to the range [0-255]
|
||||
*/
|
||||
static void rescaleGrayLevelMat(const cv::Mat &inputMat, cv::Mat &outputMat, const float histogramClippingLimit)
|
||||
{
|
||||
|
||||
// adjust output matrix wrt the input size but single channel
|
||||
std::cout<<"Input image rescaling with histogram edges cutting (in order to eliminate bad pixels created during the HDR image creation) :"<<std::endl;
|
||||
//std::cout<<"=> image size (h,w,channels) = "<<inputMat.size().height<<", "<<inputMat.size().width<<", "<<inputMat.channels()<<std::endl;
|
||||
//std::cout<<"=> pixel coding (nbchannel, bytes per channel) = "<<inputMat.elemSize()/inputMat.elemSize1()<<", "<<inputMat.elemSize1()<<std::endl;
|
||||
|
||||
// rescale between 0-255, keeping floating point values
|
||||
cv::normalize(inputMat, outputMat, 0.0, 255.0, cv::NORM_MINMAX);
|
||||
|
||||
// extract a 8bit image that will be used for histogram edge cut
|
||||
cv::Mat intGrayImage;
|
||||
if (inputMat.channels()==1)
|
||||
{
|
||||
outputMat.convertTo(intGrayImage, CV_8U);
|
||||
}else
|
||||
{
|
||||
cv::Mat rgbIntImg;
|
||||
outputMat.convertTo(rgbIntImg, CV_8UC3);
|
||||
cvtColor(rgbIntImg, intGrayImage, cv::COLOR_BGR2GRAY);
|
||||
}
|
||||
|
||||
// get histogram density probability in order to cut values under above edges limits (here 5-95%)... useful for HDR pixel errors cancellation
|
||||
cv::Mat dst, hist;
|
||||
int histSize = 256;
|
||||
calcHist(&intGrayImage, 1, 0, cv::Mat(), hist, 1, &histSize, 0);
|
||||
cv::Mat normalizedHist;
|
||||
normalize(hist, normalizedHist, 1, 0, cv::NORM_L1, CV_32F); // normalize histogram so that its sum equals 1
|
||||
|
||||
double min_val, max_val;
|
||||
minMaxLoc(normalizedHist, &min_val, &max_val);
|
||||
//std::cout<<"Hist max,min = "<<max_val<<", "<<min_val<<std::endl;
|
||||
|
||||
// compute density probability
|
||||
cv::Mat denseProb=cv::Mat::zeros(normalizedHist.size(), CV_32F);
|
||||
denseProb.at<float>(0)=normalizedHist.at<float>(0);
|
||||
int histLowerLimit=0, histUpperLimit=0;
|
||||
for (int i=1;i<normalizedHist.size().height;++i)
|
||||
{
|
||||
denseProb.at<float>(i)=denseProb.at<float>(i-1)+normalizedHist.at<float>(i);
|
||||
//std::cout<<normalizedHist.at<float>(i)<<", "<<denseProb.at<float>(i)<<std::endl;
|
||||
if ( denseProb.at<float>(i)<histogramClippingLimit)
|
||||
histLowerLimit=i;
|
||||
if ( denseProb.at<float>(i)<1-histogramClippingLimit)
|
||||
histUpperLimit=i;
|
||||
}
|
||||
// deduce min and max admitted gray levels
|
||||
float minInputValue = (float)histLowerLimit/histSize*255;
|
||||
float maxInputValue = (float)histUpperLimit/histSize*255;
|
||||
|
||||
std::cout<<"=> Histogram limits "
|
||||
<<"\n\t"<<histogramClippingLimit*100<<"% index = "<<histLowerLimit<<" => normalizedHist value = "<<denseProb.at<float>(histLowerLimit)<<" => input gray level = "<<minInputValue
|
||||
<<"\n\t"<<(1-histogramClippingLimit)*100<<"% index = "<<histUpperLimit<<" => normalizedHist value = "<<denseProb.at<float>(histUpperLimit)<<" => input gray level = "<<maxInputValue
|
||||
<<std::endl;
|
||||
//drawPlot(denseProb, "input histogram density probability", histLowerLimit, histUpperLimit);
|
||||
drawPlot(normalizedHist, "input histogram", histLowerLimit, histUpperLimit);
|
||||
|
||||
// rescale image range [minInputValue-maxInputValue] to [0-255]
|
||||
outputMat-=minInputValue;
|
||||
outputMat*=255.0/(maxInputValue-minInputValue);
|
||||
// cut original histogram and back project to original image
|
||||
cv::threshold( outputMat, outputMat, 255.0, 255.0, 2 ); //THRESH_TRUNC, clips values above 255
|
||||
cv::threshold( outputMat, outputMat, 0.0, 0.0, 3 ); //THRESH_TOZERO, clips values under 0
|
||||
|
||||
}
|
||||
// basic callback method for interface management
|
||||
cv::Mat inputImage;
|
||||
cv::Mat imageInputRescaled;
|
||||
int histogramClippingValue;
|
||||
static void callBack_rescaleGrayLevelMat(int, void*)
|
||||
{
|
||||
std::cout<<"Histogram clipping value changed, current value = "<<histogramClippingValue<<std::endl;
|
||||
rescaleGrayLevelMat(inputImage, imageInputRescaled, (float)(histogramClippingValue/100.0));
|
||||
normalize(imageInputRescaled, imageInputRescaled, 0.0, 255.0, cv::NORM_MINMAX);
|
||||
}
|
||||
|
||||
cv::Ptr<cv::bioinspired::Retina> retina;
|
||||
int retinaHcellsGain;
|
||||
int localAdaptation_photoreceptors, localAdaptation_Gcells;
|
||||
static void callBack_updateRetinaParams(int, void*)
|
||||
{
|
||||
retina->setupOPLandIPLParvoChannel(true, true, (float)(localAdaptation_photoreceptors/200.0), 0.5f, 0.43f, (float)retinaHcellsGain, 1.f, 7.f, (float)(localAdaptation_Gcells/200.0));
|
||||
}
|
||||
|
||||
int colorSaturationFactor;
|
||||
static void callback_saturateColors(int, void*)
|
||||
{
|
||||
retina->setColorSaturation(true, (float)colorSaturationFactor);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// welcome message
|
||||
std::cout<<"*********************************************************************************"<<std::endl;
|
||||
std::cout<<"* Retina demonstration for High Dynamic Range compression (tone-mapping) : demonstrates the use of a wrapper class of the Gipsa/Listic Labs retina model."<<std::endl;
|
||||
std::cout<<"* This retina model allows spatio-temporal image processing (applied on still images, video sequences)."<<std::endl;
|
||||
std::cout<<"* This demo focuses demonstration of the dynamic compression capabilities of the model"<<std::endl;
|
||||
std::cout<<"* => the main application is tone mapping of HDR images (i.e. see on a 8bit display a more than 8bits coded (up to 16bits) image with details in high and low luminance ranges"<<std::endl;
|
||||
std::cout<<"* The retina model still have the following properties:"<<std::endl;
|
||||
std::cout<<"* => It applies a spectral whithening (mid-frequency details enhancement)"<<std::endl;
|
||||
std::cout<<"* => high frequency spatio-temporal noise reduction"<<std::endl;
|
||||
std::cout<<"* => low frequency luminance to be reduced (luminance range compression)"<<std::endl;
|
||||
std::cout<<"* => local logarithmic luminance compression allows details to be enhanced in low light conditions\n"<<std::endl;
|
||||
std::cout<<"* for more information, reer to the following papers :"<<std::endl;
|
||||
std::cout<<"* Benoit A., Caplier A., Durette B., Herault, J., \"USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING\", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011"<<std::endl;
|
||||
std::cout<<"* Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891."<<std::endl;
|
||||
std::cout<<"* => reports comments/remarks at benoit.alexandre.vision@gmail.com"<<std::endl;
|
||||
std::cout<<"* => more informations and papers at : http://sites.google.com/site/benoitalexandrevision/"<<std::endl;
|
||||
std::cout<<"*********************************************************************************"<<std::endl;
|
||||
std::cout<<"** WARNING : this sample requires OpenCV to be configured with OpenEXR support **"<<std::endl;
|
||||
std::cout<<"*********************************************************************************"<<std::endl;
|
||||
std::cout<<"*** You can use free tools to generate OpenEXR images from images sets : ***"<<std::endl;
|
||||
std::cout<<"*** => 1. take a set of photos from the same viewpoint using bracketing ***"<<std::endl;
|
||||
std::cout<<"*** => 2. generate an OpenEXR image with tools like qtpfsgui.sourceforge.net ***"<<std::endl;
|
||||
std::cout<<"*** => 3. apply tone mapping with this program ***"<<std::endl;
|
||||
std::cout<<"*********************************************************************************"<<std::endl;
|
||||
|
||||
// basic input arguments checking
|
||||
if (argc<2)
|
||||
{
|
||||
help("bad number of parameter");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool useLogSampling = !strcmp(argv[argc-1], "log"); // check if user wants retina log sampling processing
|
||||
int chosenMethod=0;
|
||||
if (!strcmp(argv[argc-1], "fast"))
|
||||
{
|
||||
chosenMethod=1;
|
||||
std::cout<<"Using fast method (no spectral whithning), adaptation of Meylan&al 2008 method"<<std::endl;
|
||||
}
|
||||
|
||||
std::string inputImageName=argv[1];
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// checking input media type (still image, video file, live video acquisition)
|
||||
std::cout<<"RetinaDemo: processing image "<<inputImageName<<std::endl;
|
||||
// image processing case
|
||||
// declare the retina input buffer... that will be fed differently in regard of the input media
|
||||
inputImage = cv::imread(inputImageName, -1); // load image in RGB mode
|
||||
std::cout<<"=> image size (h,w) = "<<inputImage.size().height<<", "<<inputImage.size().width<<std::endl;
|
||||
if (!inputImage.total())
|
||||
{
|
||||
help("could not load image, program end");
|
||||
return -1;
|
||||
}
|
||||
// rescale between 0 and 1
|
||||
normalize(inputImage, inputImage, 0.0, 1.0, cv::NORM_MINMAX);
|
||||
cv::Mat gammaTransformedImage;
|
||||
cv::pow(inputImage, 1./5, gammaTransformedImage); // apply gamma curve: img = img ** (1./5)
|
||||
imshow("EXR image original image, 16bits=>8bits linear rescaling ", inputImage);
|
||||
imshow("EXR image with basic processing : 16bits=>8bits with gamma correction", gammaTransformedImage);
|
||||
if (inputImage.empty())
|
||||
{
|
||||
help("Input image could not be loaded, aborting");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Program start in a try/catch safety context (Retina may throw errors)
|
||||
try
|
||||
{
|
||||
/* create a retina instance with default parameters setup, uncomment the initialisation you wanna test
|
||||
* -> if the last parameter is 'log', then activate log sampling (favour foveal vision and subsamples peripheral vision)
|
||||
*/
|
||||
if (useLogSampling)
|
||||
{
|
||||
retina = cv::bioinspired::createRetina(inputImage.size(),true, cv::bioinspired::RETINA_COLOR_BAYER, true, 2.0, 10.0);
|
||||
}
|
||||
else// -> else allocate "classical" retina :
|
||||
retina = cv::bioinspired::createRetina(inputImage.size());
|
||||
|
||||
// create a fast retina tone mapper (Meyla&al algorithm)
|
||||
std::cout<<"Allocating fast tone mapper..."<<std::endl;
|
||||
//cv::Ptr<cv::RetinaFastToneMapping> fastToneMapper=createRetinaFastToneMapping(inputImage.size());
|
||||
std::cout<<"Fast tone mapper allocated"<<std::endl;
|
||||
|
||||
// save default retina parameters file in order to let you see this and maybe modify it and reload using method "setup"
|
||||
retina->write("RetinaDefaultParameters.xml");
|
||||
|
||||
// desactivate Magnocellular pathway processing (motion information extraction) since it is not useful here
|
||||
retina->activateMovingContoursProcessing(false);
|
||||
|
||||
// declare retina output buffers
|
||||
cv::Mat retinaOutput_parvo;
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// prepare displays and interactions
|
||||
histogramClippingValue=0; // default value... updated with interface slider
|
||||
//inputRescaleMat = inputImage;
|
||||
//outputRescaleMat = imageInputRescaled;
|
||||
cv::namedWindow("Processing configuration",1);
|
||||
cv::createTrackbar("histogram edges clipping limit", "Processing configuration",&histogramClippingValue,50,callBack_rescaleGrayLevelMat);
|
||||
|
||||
colorSaturationFactor=3;
|
||||
cv::createTrackbar("Color saturation", "Processing configuration", &colorSaturationFactor,5,callback_saturateColors);
|
||||
|
||||
retinaHcellsGain=40;
|
||||
cv::createTrackbar("Hcells gain", "Processing configuration",&retinaHcellsGain,100,callBack_updateRetinaParams);
|
||||
|
||||
localAdaptation_photoreceptors=197;
|
||||
localAdaptation_Gcells=190;
|
||||
cv::createTrackbar("Ph sensitivity", "Processing configuration", &localAdaptation_photoreceptors,199,callBack_updateRetinaParams);
|
||||
cv::createTrackbar("Gcells sensitivity", "Processing configuration", &localAdaptation_Gcells,199,callBack_updateRetinaParams);
|
||||
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// apply default parameters of user interaction variables
|
||||
rescaleGrayLevelMat(inputImage, imageInputRescaled, (float)histogramClippingValue/100);
|
||||
retina->setColorSaturation(true,(float)colorSaturationFactor);
|
||||
callBack_updateRetinaParams(1,NULL); // first call for default parameters setup
|
||||
|
||||
// processing loop with stop condition
|
||||
bool continueProcessing=true;
|
||||
while(continueProcessing)
|
||||
{
|
||||
// run retina filter
|
||||
if (!chosenMethod)
|
||||
{
|
||||
retina->run(imageInputRescaled);
|
||||
// Retrieve and display retina output
|
||||
retina->getParvo(retinaOutput_parvo);
|
||||
cv::imshow("Retina input image (with cut edges histogram for basic pixels error avoidance)", imageInputRescaled/255.0);
|
||||
cv::imshow("Retina Parvocellular pathway output : 16bit=>8bit image retina tonemapping", retinaOutput_parvo);
|
||||
cv::imwrite("HDRinput.jpg",imageInputRescaled/255.0);
|
||||
cv::imwrite("RetinaToneMapping.jpg",retinaOutput_parvo);
|
||||
}
|
||||
else
|
||||
{
|
||||
// apply the simplified hdr tone mapping method
|
||||
cv::Mat fastToneMappingOutput;
|
||||
retina->applyFastToneMapping(imageInputRescaled, fastToneMappingOutput);
|
||||
cv::imshow("Retina fast tone mapping output : 16bit=>8bit image retina tonemapping", fastToneMappingOutput);
|
||||
}
|
||||
/*cv::Mat fastToneMappingOutput_specificObject;
|
||||
fastToneMapper->setup(3.f, 1.5f, 1.f);
|
||||
fastToneMapper->applyFastToneMapping(imageInputRescaled, fastToneMappingOutput_specificObject);
|
||||
cv::imshow("### Retina fast tone mapping output : 16bit=>8bit image retina tonemapping", fastToneMappingOutput_specificObject);
|
||||
*/
|
||||
cv::waitKey(10);
|
||||
}
|
||||
}catch(const cv::Exception& e)
|
||||
{
|
||||
std::cerr<<"Error using Retina : "<<e.what()<<std::endl;
|
||||
}
|
||||
|
||||
// Program end message
|
||||
std::cout<<"Retina demo end"<<std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
|
||||
//============================================================================
|
||||
// Name : OpenEXRimages_HDR_Retina_toneMapping_video.cpp
|
||||
// Author : Alexandre Benoit (benoit.alexandre.vision@gmail.com)
|
||||
// Version : 0.2
|
||||
// Copyright : Alexandre Benoit, LISTIC Lab, december 2011
|
||||
// Description : HighDynamicRange retina tone mapping for image sequences with the help of the Gipsa/Listic's retina in C++, Ansi-style
|
||||
// Known issues: the input OpenEXR sequences can have bad computed pixels that should be removed
|
||||
// => a simple method consists of cutting histogram edges (a slider for this on the UI is provided)
|
||||
// => however, in image sequences, this histogramm cut must be done in an elegant way from frame to frame... still not done...
|
||||
//============================================================================
|
||||
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <cstring>
|
||||
|
||||
#include "opencv2/bioinspired.hpp" // retina based algorithms
|
||||
#include "opencv2/imgproc.hpp" // cvCvtcolor function
|
||||
#include "opencv2/highgui.hpp" // display
|
||||
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
static void help(std::string errorMessage)
|
||||
{
|
||||
std::cout<<"Program init error : "<<errorMessage<<std::endl;
|
||||
std::cout<<"\nProgram call procedure : ./OpenEXRimages_HDR_Retina_toneMapping [OpenEXR image sequence to process] [OPTIONNAL start frame] [OPTIONNAL end frame]"<<std::endl;
|
||||
std::cout<<"\t[OpenEXR image sequence to process] : std::sprintf style ready prototype filename of the input HDR images to process, must be an OpenEXR format, see http://www.openexr.com/ to get some samples or create your own using camera bracketing and Photoshop or equivalent software for OpenEXR image synthesis"<<std::endl;
|
||||
std::cout<<"\t\t => WARNING : image index number of digits cannot exceed 10"<<std::endl;
|
||||
std::cout<<"\t[start frame] : the starting frame tat should be considered"<<std::endl;
|
||||
std::cout<<"\t[end frame] : the ending frame tat should be considered"<<std::endl;
|
||||
std::cout<<"\nExamples:"<<std::endl;
|
||||
std::cout<<"\t-Image processing : ./OpenEXRimages_HDR_Retina_toneMapping_video memorial%3d.exr 20 45"<<std::endl;
|
||||
std::cout<<"\t-Image processing : ./OpenEXRimages_HDR_Retina_toneMapping_video memorial%3d.exr 20 45 log"<<std::endl;
|
||||
std::cout<<"\t ==> to process images from memorial020d.exr to memorial045d.exr"<<std::endl;
|
||||
|
||||
}
|
||||
|
||||
// simple procedure for 1D curve tracing
|
||||
static void drawPlot(const cv::Mat curve, const std::string figureTitle, const int lowerLimit, const int upperLimit)
|
||||
{
|
||||
//std::cout<<"curve size(h,w) = "<<curve.size().height<<", "<<curve.size().width<<std::endl;
|
||||
cv::Mat displayedCurveImage = cv::Mat::ones(200, curve.size().height, CV_8U);
|
||||
|
||||
cv::Mat windowNormalizedCurve;
|
||||
normalize(curve, windowNormalizedCurve, 0, 200, cv::NORM_MINMAX, CV_32F);
|
||||
|
||||
displayedCurveImage = cv::Scalar::all(255); // set a white background
|
||||
int binW = cvRound((double)displayedCurveImage.cols/curve.size().height);
|
||||
|
||||
for( int i = 0; i < curve.size().height; i++ )
|
||||
rectangle( displayedCurveImage, cv::Point(i*binW, displayedCurveImage.rows),
|
||||
cv::Point((i+1)*binW, displayedCurveImage.rows - cvRound(windowNormalizedCurve.at<float>(i))),
|
||||
cv::Scalar::all(0), -1, 8, 0 );
|
||||
rectangle( displayedCurveImage, cv::Point(0, 0),
|
||||
cv::Point((lowerLimit)*binW, 200),
|
||||
cv::Scalar::all(128), -1, 8, 0 );
|
||||
rectangle( displayedCurveImage, cv::Point(displayedCurveImage.cols, 0),
|
||||
cv::Point((upperLimit)*binW, 200),
|
||||
cv::Scalar::all(128), -1, 8, 0 );
|
||||
|
||||
cv::imshow(figureTitle, displayedCurveImage);
|
||||
}
|
||||
|
||||
/*
|
||||
* objective : get the gray level map of the input image and rescale it to the range [0-255] if rescale0_255=TRUE, simply trunks else
|
||||
*/
|
||||
static void rescaleGrayLevelMat(const cv::Mat &inputMat, cv::Mat &outputMat, const float histogramClippingLimit, const bool rescale0_255)
|
||||
{
|
||||
// adjust output matrix wrt the input size but single channel
|
||||
std::cout<<"Input image rescaling with histogram edges cutting (in order to eliminate bad pixels created during the HDR image creation) :"<<std::endl;
|
||||
//std::cout<<"=> image size (h,w,channels) = "<<inputMat.size().height<<", "<<inputMat.size().width<<", "<<inputMat.channels()<<std::endl;
|
||||
//std::cout<<"=> pixel coding (nbchannel, bytes per channel) = "<<inputMat.elemSize()/inputMat.elemSize1()<<", "<<inputMat.elemSize1()<<std::endl;
|
||||
|
||||
// get min and max values to use afterwards if no 0-255 rescaling is used
|
||||
double maxInput, minInput, histNormRescalefactor=1.f;
|
||||
double histNormOffset=0.f;
|
||||
minMaxLoc(inputMat, &minInput, &maxInput);
|
||||
histNormRescalefactor=255.f/(maxInput-minInput);
|
||||
histNormOffset=minInput;
|
||||
std::cout<<"Hist max,min = "<<maxInput<<", "<<minInput<<" => scale, offset = "<<histNormRescalefactor<<", "<<histNormOffset<<std::endl;
|
||||
// rescale between 0-255, keeping floating point values
|
||||
cv::Mat normalisedImage;
|
||||
cv::normalize(inputMat, normalisedImage, 0.f, 255.f, cv::NORM_MINMAX);
|
||||
if (rescale0_255)
|
||||
normalisedImage.copyTo(outputMat);
|
||||
// extract a 8bit image that will be used for histogram edge cut
|
||||
cv::Mat intGrayImage;
|
||||
if (inputMat.channels()==1)
|
||||
{
|
||||
normalisedImage.convertTo(intGrayImage, CV_8U);
|
||||
}else
|
||||
{
|
||||
cv::Mat rgbIntImg;
|
||||
normalisedImage.convertTo(rgbIntImg, CV_8UC3);
|
||||
cvtColor(rgbIntImg, intGrayImage, cv::COLOR_BGR2GRAY);
|
||||
}
|
||||
|
||||
// get histogram density probability in order to cut values under above edges limits (here 5-95%)... usefull for HDR pixel errors cancellation
|
||||
cv::Mat dst, hist;
|
||||
int histSize = 256;
|
||||
calcHist(&intGrayImage, 1, 0, cv::Mat(), hist, 1, &histSize, 0);
|
||||
cv::Mat normalizedHist;
|
||||
|
||||
normalize(hist, normalizedHist, 1.f, 0.f, cv::NORM_L1, CV_32F); // normalize histogram so that its sum equals 1
|
||||
|
||||
// compute density probability
|
||||
cv::Mat denseProb=cv::Mat::zeros(normalizedHist.size(), CV_32F);
|
||||
denseProb.at<float>(0)=normalizedHist.at<float>(0);
|
||||
int histLowerLimit=0, histUpperLimit=0;
|
||||
for (int i=1;i<normalizedHist.size().height;++i)
|
||||
{
|
||||
denseProb.at<float>(i)=denseProb.at<float>(i-1)+normalizedHist.at<float>(i);
|
||||
//std::cout<<normalizedHist.at<float>(i)<<", "<<denseProb.at<float>(i)<<std::endl;
|
||||
if ( denseProb.at<float>(i)<histogramClippingLimit)
|
||||
histLowerLimit=i;
|
||||
if ( denseProb.at<float>(i)<1.f-histogramClippingLimit)
|
||||
histUpperLimit=i;
|
||||
}
|
||||
// deduce min and max admitted gray levels
|
||||
float minInputValue = (float)histLowerLimit/histSize*255.f;
|
||||
float maxInputValue = (float)histUpperLimit/histSize*255.f;
|
||||
|
||||
std::cout<<"=> Histogram limits "
|
||||
<<"\n\t"<<histogramClippingLimit*100.f<<"% index = "<<histLowerLimit<<" => normalizedHist value = "<<denseProb.at<float>(histLowerLimit)<<" => input gray level = "<<minInputValue
|
||||
<<"\n\t"<<(1.f-histogramClippingLimit)*100.f<<"% index = "<<histUpperLimit<<" => normalizedHist value = "<<denseProb.at<float>(histUpperLimit)<<" => input gray level = "<<maxInputValue
|
||||
<<std::endl;
|
||||
//drawPlot(denseProb, "input histogram density probability", histLowerLimit, histUpperLimit);
|
||||
drawPlot(normalizedHist, "input histogram", histLowerLimit, histUpperLimit);
|
||||
|
||||
if(rescale0_255) // rescale between 0-255 if asked to
|
||||
{
|
||||
cv::threshold( outputMat, outputMat, maxInputValue, maxInputValue, 2 ); //THRESH_TRUNC, clips values above maxInputValue
|
||||
cv::threshold( outputMat, outputMat, minInputValue, minInputValue, 3 ); //THRESH_TOZERO, clips values under minInputValue
|
||||
// rescale image range [minInputValue-maxInputValue] to [0-255]
|
||||
outputMat-=minInputValue;
|
||||
outputMat*=255.f/(maxInputValue-minInputValue);
|
||||
}else
|
||||
{
|
||||
inputMat.copyTo(outputMat);
|
||||
// update threshold in the initial input image range
|
||||
maxInputValue=(float)((maxInputValue-255.f)/histNormRescalefactor+maxInput);
|
||||
minInputValue=(float)(minInputValue/histNormRescalefactor+minInput);
|
||||
std::cout<<"===> Input Hist clipping values (max,min) = "<<maxInputValue<<", "<<minInputValue<<std::endl;
|
||||
cv::threshold( outputMat, outputMat, maxInputValue, maxInputValue, 2 ); //THRESH_TRUNC, clips values above maxInputValue
|
||||
cv::threshold( outputMat, outputMat, minInputValue, minInputValue, 3 ); //
|
||||
}
|
||||
}
|
||||
|
||||
// basic callback method for interface management
|
||||
cv::Mat inputImage;
|
||||
cv::Mat imageInputRescaled;
|
||||
float globalRescalefactor=1;
|
||||
cv::Scalar globalOffset=0;
|
||||
int histogramClippingValue;
|
||||
static void callBack_rescaleGrayLevelMat(int, void*)
|
||||
{
|
||||
std::cout<<"Histogram clipping value changed, current value = "<<histogramClippingValue<<std::endl;
|
||||
// rescale and process
|
||||
inputImage+=globalOffset;
|
||||
inputImage*=globalRescalefactor;
|
||||
inputImage+=cv::Scalar(50, 50, 50, 50); // WARNING value linked to the hardcoded value (200.0) used in the globalRescalefactor in order to center on the 128 mean value... experimental but... basic compromise
|
||||
rescaleGrayLevelMat(inputImage, imageInputRescaled, (float)histogramClippingValue/100.f, true);
|
||||
|
||||
}
|
||||
|
||||
cv::Ptr<cv::bioinspired::Retina> retina;
|
||||
int retinaHcellsGain;
|
||||
int localAdaptation_photoreceptors, localAdaptation_Gcells;
|
||||
static void callBack_updateRetinaParams(int, void*)
|
||||
{
|
||||
retina->setupOPLandIPLParvoChannel(true, true, (float)(localAdaptation_photoreceptors/200.0), 0.5f, 0.43f, (float)retinaHcellsGain, 1.f, 7.f, (float)(localAdaptation_Gcells/200.0));
|
||||
}
|
||||
|
||||
int colorSaturationFactor;
|
||||
static void callback_saturateColors(int, void*)
|
||||
{
|
||||
retina->setColorSaturation(true, (float)colorSaturationFactor);
|
||||
}
|
||||
|
||||
// loadNewFrame : loads a n image wrt filename parameters. it also manages image rescaling/histogram edges cutting (acts differently at first image i.e. if firstTimeread=true)
|
||||
static void loadNewFrame(const std::string filenamePrototype, const int currentFileIndex, const bool firstTimeread)
|
||||
{
|
||||
char *currentImageName=NULL;
|
||||
currentImageName = (char*)malloc(sizeof(char)*filenamePrototype.size()+10);
|
||||
|
||||
// grab the first frame
|
||||
sprintf(currentImageName, filenamePrototype.c_str(), currentFileIndex);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// checking input media type (still image, video file, live video acquisition)
|
||||
std::cout<<"RetinaDemo: reading image : "<<currentImageName<<std::endl;
|
||||
// image processing case
|
||||
// declare the retina input buffer... that will be fed differently in regard of the input media
|
||||
inputImage = cv::imread(currentImageName, -1); // load image in RGB mode
|
||||
std::cout<<"=> image size (h,w) = "<<inputImage.size().height<<", "<<inputImage.size().width<<std::endl;
|
||||
if (inputImage.empty())
|
||||
{
|
||||
help("could not load image, program end");
|
||||
return;;
|
||||
}
|
||||
|
||||
// rescaling/histogram clipping stage
|
||||
// rescale between 0 and 1
|
||||
// TODO : take care of this step !!! maybe disable of do this in a nicer way ... each successive image should get the same transformation... but it depends on the initial image format
|
||||
double maxInput, minInput;
|
||||
minMaxLoc(inputImage, &minInput, &maxInput);
|
||||
std::cout<<"ORIGINAL IMAGE pixels values range (max,min) : "<<maxInput<<", "<<minInput<<std::endl;
|
||||
|
||||
if (firstTimeread)
|
||||
{
|
||||
/* the first time, get the pixel values range and rougthly update scaling value
|
||||
in order to center values around 128 and getting a range close to [0-255],
|
||||
=> actually using a little less in order to let some more flexibility in range evolves...
|
||||
*/
|
||||
double maxInput1, minInput1;
|
||||
minMaxLoc(inputImage, &minInput1, &maxInput1);
|
||||
std::cout<<"FIRST IMAGE pixels values range (max,min) : "<<maxInput1<<", "<<minInput1<<std::endl;
|
||||
globalRescalefactor=(float)(50.0/(maxInput1-minInput1)); // less than 255 for flexibility... experimental value to be carefull about
|
||||
double channelOffset = -1.5*minInput;
|
||||
globalOffset= cv::Scalar(channelOffset, channelOffset, channelOffset, channelOffset);
|
||||
}
|
||||
// call the generic input image rescaling callback
|
||||
callBack_rescaleGrayLevelMat(1,NULL);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// welcome message
|
||||
std::cout<<"*********************************************************************************"<<std::endl;
|
||||
std::cout<<"* Retina demonstration for High Dynamic Range compression (tone-mapping) : demonstrates the use of a wrapper class of the Gipsa/Listic Labs retina model."<<std::endl;
|
||||
std::cout<<"* This retina model allows spatio-temporal image processing (applied on still images, video sequences)."<<std::endl;
|
||||
std::cout<<"* This demo focuses demonstration of the dynamic compression capabilities of the model"<<std::endl;
|
||||
std::cout<<"* => the main application is tone mapping of HDR images (i.e. see on a 8bit display a more than 8bits coded (up to 16bits) image with details in high and low luminance ranges"<<std::endl;
|
||||
std::cout<<"* The retina model still have the following properties:"<<std::endl;
|
||||
std::cout<<"* => It applies a spectral whithening (mid-frequency details enhancement)"<<std::endl;
|
||||
std::cout<<"* => high frequency spatio-temporal noise reduction"<<std::endl;
|
||||
std::cout<<"* => low frequency luminance to be reduced (luminance range compression)"<<std::endl;
|
||||
std::cout<<"* => local logarithmic luminance compression allows details to be enhanced in low light conditions\n"<<std::endl;
|
||||
std::cout<<"* for more information, reer to the following papers :"<<std::endl;
|
||||
std::cout<<"* Benoit A., Caplier A., Durette B., Herault, J., \"USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING\", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011"<<std::endl;
|
||||
std::cout<<"* Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891."<<std::endl;
|
||||
std::cout<<"* => reports comments/remarks at benoit.alexandre.vision@gmail.com"<<std::endl;
|
||||
std::cout<<"* => more informations and papers at : http://sites.google.com/site/benoitalexandrevision/"<<std::endl;
|
||||
std::cout<<"*********************************************************************************"<<std::endl;
|
||||
std::cout<<"** WARNING : this sample requires OpenCV to be configured with OpenEXR support **"<<std::endl;
|
||||
std::cout<<"*********************************************************************************"<<std::endl;
|
||||
std::cout<<"*** You can use free tools to generate OpenEXR images from images sets : ***"<<std::endl;
|
||||
std::cout<<"*** => 1. take a set of photos from the same viewpoint using bracketing ***"<<std::endl;
|
||||
std::cout<<"*** => 2. generate an OpenEXR image with tools like qtpfsgui.sourceforge.net ***"<<std::endl;
|
||||
std::cout<<"*** => 3. apply tone mapping with this program ***"<<std::endl;
|
||||
std::cout<<"*********************************************************************************"<<std::endl;
|
||||
|
||||
// basic input arguments checking
|
||||
if (argc<4)
|
||||
{
|
||||
help("bad number of parameter");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool useLogSampling = !strcmp(argv[argc-1], "log"); // check if user wants retina log sampling processing
|
||||
|
||||
int startFrameIndex=0, endFrameIndex=0, currentFrameIndex=0;
|
||||
sscanf(argv[2], "%d", &startFrameIndex);
|
||||
sscanf(argv[3], "%d", &endFrameIndex);
|
||||
std::string inputImageNamePrototype(argv[1]);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// checking input media type (still image, video file, live video acquisition)
|
||||
std::cout<<"RetinaDemo: setting up system with first image..."<<std::endl;
|
||||
loadNewFrame(inputImageNamePrototype, startFrameIndex, true);
|
||||
|
||||
if (inputImage.empty())
|
||||
{
|
||||
help("could not load image, program end");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Program start in a try/catch safety context (Retina may throw errors)
|
||||
try
|
||||
{
|
||||
/* create a retina instance with default parameters setup, uncomment the initialisation you wanna test
|
||||
* -> if the last parameter is 'log', then activate log sampling (favour foveal vision and subsamples peripheral vision)
|
||||
*/
|
||||
if (useLogSampling)
|
||||
{
|
||||
retina = cv::bioinspired::createRetina(inputImage.size(),true, cv::bioinspired::RETINA_COLOR_BAYER, true, 2.0, 10.0);
|
||||
}
|
||||
else// -> else allocate "classical" retina :
|
||||
retina = cv::bioinspired::createRetina(inputImage.size());
|
||||
|
||||
// save default retina parameters file in order to let you see this and maybe modify it and reload using method "setup"
|
||||
retina->write("RetinaDefaultParameters.xml");
|
||||
|
||||
// desactivate Magnocellular pathway processing (motion information extraction) since it is not usefull here
|
||||
retina->activateMovingContoursProcessing(false);
|
||||
|
||||
// declare retina output buffers
|
||||
cv::Mat retinaOutput_parvo;
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// prepare displays and interactions
|
||||
histogramClippingValue=0; // default value... updated with interface slider
|
||||
|
||||
std::string retinaInputCorrected("Retina input image (with cut edges histogram for basic pixels error avoidance)");
|
||||
cv::namedWindow(retinaInputCorrected,1);
|
||||
cv::createTrackbar("histogram edges clipping limit", "Retina input image (with cut edges histogram for basic pixels error avoidance)",&histogramClippingValue,50,callBack_rescaleGrayLevelMat);
|
||||
|
||||
std::string RetinaParvoWindow("Retina Parvocellular pathway output : 16bit=>8bit image retina tonemapping");
|
||||
cv::namedWindow(RetinaParvoWindow, 1);
|
||||
colorSaturationFactor=3;
|
||||
cv::createTrackbar("Color saturation", "Retina Parvocellular pathway output : 16bit=>8bit image retina tonemapping", &colorSaturationFactor,5,callback_saturateColors);
|
||||
|
||||
retinaHcellsGain=40;
|
||||
cv::createTrackbar("Hcells gain", "Retina Parvocellular pathway output : 16bit=>8bit image retina tonemapping",&retinaHcellsGain,100,callBack_updateRetinaParams);
|
||||
|
||||
localAdaptation_photoreceptors=197;
|
||||
localAdaptation_Gcells=190;
|
||||
cv::createTrackbar("Ph sensitivity", "Retina Parvocellular pathway output : 16bit=>8bit image retina tonemapping", &localAdaptation_photoreceptors,199,callBack_updateRetinaParams);
|
||||
cv::createTrackbar("Gcells sensitivity", "Retina Parvocellular pathway output : 16bit=>8bit image retina tonemapping", &localAdaptation_Gcells,199,callBack_updateRetinaParams);
|
||||
|
||||
std::string powerTransformedInput("EXR image with basic processing : 16bits=>8bits with gamma correction");
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// apply default parameters of user interaction variables
|
||||
callBack_updateRetinaParams(1,NULL); // first call for default parameters setup
|
||||
callback_saturateColors(1, NULL);
|
||||
|
||||
// processing loop with stop condition
|
||||
currentFrameIndex=startFrameIndex;
|
||||
while(currentFrameIndex <= endFrameIndex)
|
||||
{
|
||||
loadNewFrame(inputImageNamePrototype, currentFrameIndex, false);
|
||||
|
||||
if (inputImage.empty())
|
||||
{
|
||||
std::cout<<"Could not load new image (index = "<<currentFrameIndex<<"), program end"<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
// display input & process standard power transformation
|
||||
imshow("EXR image original image, 16bits=>8bits linear rescaling ", imageInputRescaled);
|
||||
cv::Mat gammaTransformedImage;
|
||||
cv::pow(imageInputRescaled, 1./5, gammaTransformedImage); // apply gamma curve: img = img ** (1./5)
|
||||
imshow(powerTransformedInput, gammaTransformedImage);
|
||||
// run retina filter
|
||||
retina->run(imageInputRescaled);
|
||||
// Retrieve and display retina output
|
||||
retina->getParvo(retinaOutput_parvo);
|
||||
cv::imshow(retinaInputCorrected, imageInputRescaled/255.f);
|
||||
cv::imshow(RetinaParvoWindow, retinaOutput_parvo);
|
||||
cv::waitKey(4);
|
||||
// jump to next frame
|
||||
++currentFrameIndex;
|
||||
}
|
||||
}catch(const cv::Exception& e)
|
||||
{
|
||||
std::cerr<<"Error using Retina : "<<e.what()<<std::endl;
|
||||
}
|
||||
|
||||
// Program end message
|
||||
std::cout<<"Retina demo end"<<std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
//============================================================================
|
||||
// Name : retinademo.cpp
|
||||
// Author : Alexandre Benoit, benoit.alexandre.vision@gmail.com
|
||||
// Version : 0.1
|
||||
// Copyright : LISTIC/GIPSA French Labs, july 2011
|
||||
// Description : Gipsa/LISTIC Labs retina demo in C++, Ansi-style
|
||||
//============================================================================
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#include "opencv2/bioinspired.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
static void help(std::string errorMessage)
|
||||
{
|
||||
std::cout<<"Program init error : "<<errorMessage<<std::endl;
|
||||
std::cout<<"\nProgram call procedure : retinaDemo [processing mode] [Optional : media target] [Optional LAST parameter: \"log\" to activate retina log sampling]"<<std::endl;
|
||||
std::cout<<"\t[processing mode] :"<<std::endl;
|
||||
std::cout<<"\t -image : for still image processing"<<std::endl;
|
||||
std::cout<<"\t -video : for video stream processing"<<std::endl;
|
||||
std::cout<<"\t[Optional : media target] :"<<std::endl;
|
||||
std::cout<<"\t if processing an image or video file, then, specify the path and filename of the target to process"<<std::endl;
|
||||
std::cout<<"\t leave empty if processing video stream coming from a connected video device"<<std::endl;
|
||||
std::cout<<"\t[Optional : activate retina log sampling] : an optional last parameter can be specified for retina spatial log sampling"<<std::endl;
|
||||
std::cout<<"\t set \"log\" without quotes to activate this sampling, output frame size will be divided by 4"<<std::endl;
|
||||
std::cout<<"\nExamples:"<<std::endl;
|
||||
std::cout<<"\t-Image processing : ./retinaDemo -image lena.jpg"<<std::endl;
|
||||
std::cout<<"\t-Image processing with log sampling : ./retinaDemo -image lena.jpg log"<<std::endl;
|
||||
std::cout<<"\t-Video processing : ./retinaDemo -video myMovie.mp4"<<std::endl;
|
||||
std::cout<<"\t-Live video processing : ./retinaDemo -video"<<std::endl;
|
||||
std::cout<<"\nPlease start again with new parameters"<<std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// welcome message
|
||||
std::cout<<"****************************************************"<<std::endl;
|
||||
std::cout<<"* Retina demonstration : demonstrates the use of is a wrapper class of the Gipsa/Listic Labs retina model."<<std::endl;
|
||||
std::cout<<"* This retina model allows spatio-temporal image processing (applied on still images, video sequences)."<<std::endl;
|
||||
std::cout<<"* As a summary, these are the retina model properties:"<<std::endl;
|
||||
std::cout<<"* => It applies a spectral whithening (mid-frequency details enhancement)"<<std::endl;
|
||||
std::cout<<"* => high frequency spatio-temporal noise reduction"<<std::endl;
|
||||
std::cout<<"* => low frequency luminance to be reduced (luminance range compression)"<<std::endl;
|
||||
std::cout<<"* => local logarithmic luminance compression allows details to be enhanced in low light conditions\n"<<std::endl;
|
||||
std::cout<<"* for more information, reer to the following papers :"<<std::endl;
|
||||
std::cout<<"* Benoit A., Caplier A., Durette B., Herault, J., \"USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING\", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011"<<std::endl;
|
||||
std::cout<<"* Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891."<<std::endl;
|
||||
std::cout<<"* => reports comments/remarks at benoit.alexandre.vision@gmail.com"<<std::endl;
|
||||
std::cout<<"* => more informations and papers at : http://sites.google.com/site/benoitalexandrevision/"<<std::endl;
|
||||
std::cout<<"****************************************************"<<std::endl;
|
||||
std::cout<<" NOTE : this program generates the default retina parameters file 'RetinaDefaultParameters.xml'"<<std::endl;
|
||||
std::cout<<" => you can use this to fine tune parameters and load them if you save to file 'RetinaSpecificParameters.xml'"<<std::endl;
|
||||
|
||||
// basic input arguments checking
|
||||
if (argc<2)
|
||||
{
|
||||
help("bad number of parameter");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool useLogSampling = !strcmp(argv[argc-1], "log"); // check if user wants retina log sampling processing
|
||||
|
||||
std::string inputMediaType=argv[1];
|
||||
|
||||
// declare the retina input buffer... that will be fed differently in regard of the input media
|
||||
cv::Mat inputFrame;
|
||||
cv::VideoCapture videoCapture; // in case a video media is used, its manager is declared here
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// checking input media type (still image, video file, live video acquisition)
|
||||
if (!strcmp(inputMediaType.c_str(), "-image") && argc >= 3)
|
||||
{
|
||||
std::cout<<"RetinaDemo: processing image "<<argv[2]<<std::endl;
|
||||
// image processing case
|
||||
inputFrame = cv::imread(std::string(argv[2]), 1); // load image in RGB mode
|
||||
}else
|
||||
if (!strcmp(inputMediaType.c_str(), "-video"))
|
||||
{
|
||||
if (argc == 2 || (argc == 3 && useLogSampling)) // attempt to grab images from a video capture device
|
||||
{
|
||||
videoCapture.open(0);
|
||||
}else// attempt to grab images from a video filestream
|
||||
{
|
||||
std::cout<<"RetinaDemo: processing video stream "<<argv[2]<<std::endl;
|
||||
videoCapture.open(argv[2]);
|
||||
}
|
||||
|
||||
// grab a first frame to check if everything is ok
|
||||
videoCapture>>inputFrame;
|
||||
}else
|
||||
{
|
||||
// bad command parameter
|
||||
help("bad command parameter");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (inputFrame.empty())
|
||||
{
|
||||
help("Input media could not be loaded, aborting");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Program start in a try/catch safety context (Retina may throw errors)
|
||||
try
|
||||
{
|
||||
// create a retina instance with default parameters setup, uncomment the initialisation you wanna test
|
||||
cv::Ptr<cv::bioinspired::Retina> myRetina;
|
||||
|
||||
// if the last parameter is 'log', then activate log sampling (favour foveal vision and subsamples peripheral vision)
|
||||
if (useLogSampling)
|
||||
{
|
||||
myRetina = cv::bioinspired::createRetina(inputFrame.size(), true, cv::bioinspired::RETINA_COLOR_BAYER, true, 2.0, 10.0);
|
||||
}
|
||||
else// -> else allocate "classical" retina :
|
||||
myRetina = cv::bioinspired::createRetina(inputFrame.size());
|
||||
|
||||
// save default retina parameters file in order to let you see this and maybe modify it and reload using method "setup"
|
||||
myRetina->write("RetinaDefaultParameters.xml");
|
||||
|
||||
// load parameters if file exists
|
||||
myRetina->setup("RetinaSpecificParameters.xml");
|
||||
myRetina->clearBuffers();
|
||||
|
||||
// declare retina output buffers
|
||||
cv::Mat retinaOutput_parvo;
|
||||
cv::Mat retinaOutput_magno;
|
||||
|
||||
// processing loop with stop condition
|
||||
bool continueProcessing=true; // FIXME : not yet managed during process...
|
||||
while(continueProcessing)
|
||||
{
|
||||
// if using video stream, then, grabbing a new frame, else, input remains the same
|
||||
if (videoCapture.isOpened())
|
||||
videoCapture>>inputFrame;
|
||||
|
||||
// run retina filter
|
||||
myRetina->run(inputFrame);
|
||||
// Retrieve and display retina output
|
||||
myRetina->getParvo(retinaOutput_parvo);
|
||||
myRetina->getMagno(retinaOutput_magno);
|
||||
cv::imshow("retina input", inputFrame);
|
||||
cv::imshow("Retina Parvo", retinaOutput_parvo);
|
||||
cv::imshow("Retina Magno", retinaOutput_magno);
|
||||
|
||||
cv::waitKey(5);
|
||||
}
|
||||
}catch(const cv::Exception& e)
|
||||
{
|
||||
std::cerr<<"Error using Retina : "<<e.what()<<std::endl;
|
||||
}
|
||||
|
||||
// Program end message
|
||||
std::cout<<"Retina demo end"<<std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
//============================================================================
|
||||
// Name : retina_tutorial.cpp
|
||||
// Author : Alexandre Benoit, benoit.alexandre.vision@gmail.com
|
||||
// Version : 0.1
|
||||
// Copyright : LISTIC/GIPSA French Labs, july 2012
|
||||
// Description : Gipsa/LISTIC Labs retina demo in C++, Ansi-style
|
||||
//============================================================================
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#include "opencv2/bioinspired.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
static void help(std::string errorMessage)
|
||||
{
|
||||
std::cout<<"Program init error : "<<errorMessage<<std::endl;
|
||||
std::cout<<"\nProgram call procedure : retinaDemo [processing mode] [Optional : media target] [Optional LAST parameter: \"log\" to activate retina log sampling]"<<std::endl;
|
||||
std::cout<<"\t[processing mode] :"<<std::endl;
|
||||
std::cout<<"\t -image : for still image processing"<<std::endl;
|
||||
std::cout<<"\t -video : for video stream processing"<<std::endl;
|
||||
std::cout<<"\t[Optional : media target] :"<<std::endl;
|
||||
std::cout<<"\t if processing an image or video file, then, specify the path and filename of the target to process"<<std::endl;
|
||||
std::cout<<"\t leave empty if processing video stream coming from a connected video device"<<std::endl;
|
||||
std::cout<<"\t[Optional : activate retina log sampling] : an optional last parameter can be specified for retina spatial log sampling"<<std::endl;
|
||||
std::cout<<"\t set \"log\" without quotes to activate this sampling, output frame size will be divided by 4"<<std::endl;
|
||||
std::cout<<"\nExamples:"<<std::endl;
|
||||
std::cout<<"\t-Image processing : ./retinaDemo -image lena.jpg"<<std::endl;
|
||||
std::cout<<"\t-Image processing with log sampling : ./retinaDemo -image lena.jpg log"<<std::endl;
|
||||
std::cout<<"\t-Video processing : ./retinaDemo -video myMovie.mp4"<<std::endl;
|
||||
std::cout<<"\t-Live video processing : ./retinaDemo -video"<<std::endl;
|
||||
std::cout<<"\nPlease start again with new parameters"<<std::endl;
|
||||
std::cout<<"****************************************************"<<std::endl;
|
||||
std::cout<<" NOTE : this program generates the default retina parameters file 'RetinaDefaultParameters.xml'"<<std::endl;
|
||||
std::cout<<" => you can use this to fine tune parameters and load them if you save to file 'RetinaSpecificParameters.xml'"<<std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// welcome message
|
||||
std::cout<<"****************************************************"<<std::endl;
|
||||
std::cout<<"* Retina demonstration : demonstrates the use of is a wrapper class of the Gipsa/Listic Labs retina model."<<std::endl;
|
||||
std::cout<<"* This demo will try to load the file 'RetinaSpecificParameters.xml' (if exists).\nTo create it, copy the autogenerated template 'RetinaDefaultParameters.xml'.\nThen tweak it with your own retina parameters."<<std::endl;
|
||||
// basic input arguments checking
|
||||
if (argc<2)
|
||||
{
|
||||
help("bad number of parameter");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool useLogSampling = !strcmp(argv[argc-1], "log"); // check if user wants retina log sampling processing
|
||||
|
||||
std::string inputMediaType=argv[1];
|
||||
|
||||
// declare the retina input buffer... that will be fed differently in regard of the input media
|
||||
cv::Mat inputFrame;
|
||||
cv::VideoCapture videoCapture; // in case a video media is used, its manager is declared here
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// checking input media type (still image, video file, live video acquisition)
|
||||
if (!strcmp(inputMediaType.c_str(), "-image") && argc >= 3)
|
||||
{
|
||||
std::cout<<"RetinaDemo: processing image "<<argv[2]<<std::endl;
|
||||
// image processing case
|
||||
inputFrame = cv::imread(std::string(argv[2]), 1); // load image in RGB mode
|
||||
}else
|
||||
if (!strcmp(inputMediaType.c_str(), "-video"))
|
||||
{
|
||||
if (argc == 2 || (argc == 3 && useLogSampling)) // attempt to grab images from a video capture device
|
||||
{
|
||||
videoCapture.open(0);
|
||||
}else// attempt to grab images from a video filestream
|
||||
{
|
||||
std::cout<<"RetinaDemo: processing video stream "<<argv[2]<<std::endl;
|
||||
videoCapture.open(argv[2]);
|
||||
}
|
||||
|
||||
// grab a first frame to check if everything is ok
|
||||
videoCapture>>inputFrame;
|
||||
}else
|
||||
{
|
||||
// bad command parameter
|
||||
help("bad command parameter");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (inputFrame.empty())
|
||||
{
|
||||
help("Input media could not be loaded, aborting");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Program start in a try/catch safety context (Retina may throw errors)
|
||||
try
|
||||
{
|
||||
// create a retina instance with default parameters setup, uncomment the initialisation you wanna test
|
||||
cv::Ptr<cv::bioinspired::Retina> myRetina;
|
||||
|
||||
// if the last parameter is 'log', then activate log sampling (favour foveal vision and subsamples peripheral vision)
|
||||
if (useLogSampling)
|
||||
{
|
||||
myRetina = cv::bioinspired::createRetina(inputFrame.size(), true, cv::bioinspired::RETINA_COLOR_BAYER, true, 2.0, 10.0);
|
||||
}
|
||||
else// -> else allocate "classical" retina :
|
||||
{
|
||||
myRetina = cv::bioinspired::createRetina(inputFrame.size());
|
||||
}
|
||||
|
||||
// save default retina parameters file in order to let you see this and maybe modify it and reload using method "setup"
|
||||
myRetina->write("RetinaDefaultParameters.xml");
|
||||
|
||||
// load parameters if file exists
|
||||
myRetina->setup("RetinaSpecificParameters.xml");
|
||||
|
||||
// reset all retina buffers (imagine you close your eyes for a long time)
|
||||
myRetina->clearBuffers();
|
||||
|
||||
// declare retina output buffers
|
||||
cv::Mat retinaOutput_parvo;
|
||||
cv::Mat retinaOutput_magno;
|
||||
|
||||
// processing loop with no stop condition
|
||||
for(;;)
|
||||
{
|
||||
// if using video stream, then, grabbing a new frame, else, input remains the same
|
||||
if (videoCapture.isOpened())
|
||||
videoCapture>>inputFrame;
|
||||
|
||||
// run retina filter on the loaded input frame
|
||||
myRetina->run(inputFrame);
|
||||
// Retrieve and display retina output
|
||||
myRetina->getParvo(retinaOutput_parvo);
|
||||
myRetina->getMagno(retinaOutput_magno);
|
||||
cv::imshow("retina input", inputFrame);
|
||||
cv::imshow("Retina Parvo", retinaOutput_parvo);
|
||||
cv::imshow("Retina Magno", retinaOutput_magno);
|
||||
cv::waitKey(10);
|
||||
}
|
||||
}catch(const cv::Exception& e)
|
||||
{
|
||||
std::cerr<<"Error using Retina or end of video sequence reached : "<<e.what()<<std::endl;
|
||||
}
|
||||
|
||||
// Program end message
|
||||
std::cout<<"Retina demo end"<<std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<opencv_storage>
|
||||
<OPLandIPLparvo>
|
||||
<colorMode>1</colorMode>
|
||||
<normaliseOutput>1</normaliseOutput>
|
||||
<photoreceptorsLocalAdaptationSensitivity>7.5e-01</photoreceptorsLocalAdaptationSensitivity>
|
||||
<photoreceptorsTemporalConstant>9.0e-01</photoreceptorsTemporalConstant>
|
||||
<photoreceptorsSpatialConstant>5.3e-01</photoreceptorsSpatialConstant>
|
||||
<horizontalCellsGain>0.01</horizontalCellsGain>
|
||||
<hcellsTemporalConstant>0.5</hcellsTemporalConstant>
|
||||
<hcellsSpatialConstant>7.</hcellsSpatialConstant>
|
||||
<ganglionCellsSensitivity>7.5e-01</ganglionCellsSensitivity>
|
||||
</OPLandIPLparvo>
|
||||
<IPLmagno>
|
||||
<normaliseOutput>1</normaliseOutput>
|
||||
<parasolCells_beta>0.</parasolCells_beta>
|
||||
<parasolCells_tau>0.</parasolCells_tau>
|
||||
<parasolCells_k>7.</parasolCells_k>
|
||||
<amacrinCellsTemporalCutFrequency>2.0e+00</amacrinCellsTemporalCutFrequency>
|
||||
<V0CompressionParameter>9.5e-01</V0CompressionParameter>
|
||||
<localAdaptintegration_tau>0.</localAdaptintegration_tau>
|
||||
<localAdaptintegration_k>7.</localAdaptintegration_k>
|
||||
</IPLmagno>
|
||||
</opencv_storage>
|
||||
@@ -0,0 +1,119 @@
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/ocl.hpp"
|
||||
#include "opencv2/bioinspired.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::ocl;
|
||||
using namespace std;
|
||||
|
||||
const int total_loop_count = 50;
|
||||
|
||||
static void help(CommandLineParser cmd, const String& errorMessage)
|
||||
{
|
||||
cout << errorMessage << endl;
|
||||
cout << "Avaible options:" << endl;
|
||||
cmd.printMessage();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
//set this to save kernel compile time from second time you run
|
||||
ocl::setBinaryDiskCache();
|
||||
const char* keys =
|
||||
"{ h | help | false | print help message }"
|
||||
"{ c | cpu | false | use cpu (original version) or gpu(OpenCL) to process the image }"
|
||||
"{ i | image | cat.jpg | specify the input image }";
|
||||
|
||||
CommandLineParser cmd(argc, argv, keys);
|
||||
|
||||
if(cmd.get<bool>("help"))
|
||||
{
|
||||
help(cmd, "Usage: ./retina_ocl [options]");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
String fname = cmd.get<String>("i");
|
||||
bool useCPU = cmd.get<bool>("c");
|
||||
|
||||
cv::Mat input = imread(fname);
|
||||
if(input.empty())
|
||||
{
|
||||
help(cmd, "Error opening: " + fname);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Program start in a try/catch safety context (Retina may throw errors)
|
||||
try
|
||||
{
|
||||
// create a retina instance with default parameters setup, uncomment the initialisation you wanna test
|
||||
cv::Ptr<cv::bioinspired::Retina> oclRetina;
|
||||
cv::Ptr<cv::bioinspired::Retina> retina;
|
||||
// declare retina output buffers
|
||||
cv::ocl::oclMat retina_parvo_ocl;
|
||||
cv::ocl::oclMat retina_magno_ocl;
|
||||
cv::Mat retina_parvo;
|
||||
cv::Mat retina_magno;
|
||||
|
||||
if(useCPU)
|
||||
{
|
||||
retina = cv::bioinspired::createRetina(input.size());
|
||||
retina->clearBuffers();
|
||||
}
|
||||
else
|
||||
{
|
||||
oclRetina = cv::bioinspired::createRetina_OCL(input.size());
|
||||
oclRetina->clearBuffers();
|
||||
}
|
||||
|
||||
int64 temp_time = 0, total_time = 0;
|
||||
|
||||
int loop_counter = 0;
|
||||
for(; loop_counter <= total_loop_count; ++loop_counter)
|
||||
{
|
||||
if(useCPU)
|
||||
{
|
||||
temp_time = cv::getTickCount();
|
||||
retina->run(input);
|
||||
retina->getParvo(retina_parvo);
|
||||
retina->getMagno(retina_magno);
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::ocl::oclMat input_ocl(input);
|
||||
temp_time = cv::getTickCount();
|
||||
oclRetina->run(input_ocl);
|
||||
oclRetina->getParvo(retina_parvo_ocl);
|
||||
oclRetina->getMagno(retina_magno_ocl);
|
||||
}
|
||||
// will not count the first loop, which is considered as warm-up period
|
||||
if(loop_counter > 0)
|
||||
{
|
||||
temp_time = (cv::getTickCount() - temp_time);
|
||||
total_time += temp_time;
|
||||
printf("Frame id %2d: %3.4fms\n", loop_counter, (double)temp_time / cv::getTickFrequency() * 1000.0);
|
||||
}
|
||||
if(!useCPU)
|
||||
{
|
||||
retina_parvo = retina_parvo_ocl;
|
||||
retina_magno = retina_magno_ocl;
|
||||
}
|
||||
cv::imshow("retina input", input);
|
||||
cv::imshow("Retina Parvo", retina_parvo);
|
||||
cv::imshow("Retina Magno", retina_magno);
|
||||
cv::waitKey(10);
|
||||
}
|
||||
printf("Average: %.4fms\n", (double)total_time / total_loop_count / cv::getTickFrequency() * 1000.0);
|
||||
}
|
||||
catch(const cv::Exception& e)
|
||||
{
|
||||
std::cerr << "Error using Retina : " << e.what() << std::endl;
|
||||
}
|
||||
// Program end message
|
||||
std::cout << "Retina demo end" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<opencv_storage>
|
||||
<OPLandIPLparvo>
|
||||
<colorMode>1</colorMode>
|
||||
<normaliseOutput>1</normaliseOutput>
|
||||
<photoreceptorsLocalAdaptationSensitivity>8.9e-01</photoreceptorsLocalAdaptationSensitivity>
|
||||
<photoreceptorsTemporalConstant>9.0e-01</photoreceptorsTemporalConstant>
|
||||
<photoreceptorsSpatialConstant>5.3e-01</photoreceptorsSpatialConstant>
|
||||
<horizontalCellsGain>0.3</horizontalCellsGain>
|
||||
<hcellsTemporalConstant>0.5</hcellsTemporalConstant>
|
||||
<hcellsSpatialConstant>7.</hcellsSpatialConstant>
|
||||
<ganglionCellsSensitivity>8.9e-01</ganglionCellsSensitivity>
|
||||
</OPLandIPLparvo>
|
||||
<IPLmagno>
|
||||
<normaliseOutput>1</normaliseOutput>
|
||||
<parasolCells_beta>0.</parasolCells_beta>
|
||||
<parasolCells_tau>0.</parasolCells_tau>
|
||||
<parasolCells_k>7.</parasolCells_k>
|
||||
<amacrinCellsTemporalCutFrequency>2.0e+00</amacrinCellsTemporalCutFrequency>
|
||||
<V0CompressionParameter>9.5e-01</V0CompressionParameter>
|
||||
<localAdaptintegration_tau>0.</localAdaptintegration_tau>
|
||||
<localAdaptintegration_k>7.</localAdaptintegration_k>
|
||||
</IPLmagno>
|
||||
</opencv_storage>
|
||||
@@ -0,0 +1,145 @@
|
||||
//============================================================================
|
||||
// Name : retinademo.cpp
|
||||
// Author : Alexandre Benoit, benoit.alexandre.vision@gmail.com
|
||||
// Version : 0.1
|
||||
// Copyright : LISTIC/GIPSA French Labs, july 2011
|
||||
// Description : Gipsa/LISTIC Labs retina demo in C++, Ansi-style
|
||||
//============================================================================
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#include "opencv2/bioinspired.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/core/ocl.hpp"
|
||||
|
||||
const std::string keys =
|
||||
"{image | | Input from image file }"
|
||||
"{video | | Input from video file }"
|
||||
"{camera | 0 | Index of input camera. If image or video is not specified, camera 0 will be used }"
|
||||
"{log | | Activate retina log sampling }"
|
||||
"{ocl | | Use OpenCL acceleration if possible }"
|
||||
"{help | | Print help}";
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// welcome message
|
||||
std::cout<<"****************************************************"<<std::endl
|
||||
<<"* Retina demonstration : demonstrates the use of is a wrapper class of the Gipsa/Listic Labs retina model."<<std::endl
|
||||
<<"* This retina model allows spatio-temporal image processing (applied on still images, video sequences)."<<std::endl
|
||||
<<"* As a summary, these are the retina model properties:"<<std::endl
|
||||
<<"* => It applies a spectral whithening (mid-frequency details enhancement)"<<std::endl
|
||||
<<"* => high frequency spatio-temporal noise reduction"<<std::endl
|
||||
<<"* => low frequency luminance to be reduced (luminance range compression)"<<std::endl
|
||||
<<"* => local logarithmic luminance compression allows details to be enhanced in low light conditions\n"<<std::endl
|
||||
<<"* for more information, reer to the following papers :"<<std::endl
|
||||
<<"* Benoit A., Caplier A., Durette B., Herault, J., \"USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING\", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011"<<std::endl
|
||||
<<"* Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891."<<std::endl
|
||||
<<"* => reports comments/remarks at benoit.alexandre.vision@gmail.com"<<std::endl
|
||||
<<"* => more informations and papers at : http://sites.google.com/site/benoitalexandrevision/"<<std::endl
|
||||
<<"****************************************************"<<std::endl
|
||||
<<" NOTE : this program generates the default retina parameters file 'RetinaDefaultParameters.xml'"<<std::endl
|
||||
<<" => you can use this to fine tune parameters and load them if you save to file 'RetinaSpecificParameters.xml'"<<std::endl;
|
||||
|
||||
cv::CommandLineParser parser(argc, argv, keys);
|
||||
if(!parser.check() || parser.has("help")) {
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool useLogSampling = parser.has("log"); // check if user wants retina log sampling processing
|
||||
bool useOCL = parser.has("ocl");
|
||||
cv::ocl::setUseOpenCL(useOCL);
|
||||
if(useOCL && !cv::ocl::useOpenCL())
|
||||
{
|
||||
std::cout << "Failed to enable OpenCL\n";
|
||||
}
|
||||
|
||||
// declare the retina input buffer... that will be fed differently in regard of the input media
|
||||
cv::Mat inputFrame;
|
||||
cv::VideoCapture videoCapture; // in case a video media is used, its manager is declared here
|
||||
|
||||
if(parser.has("video"))
|
||||
videoCapture.open(parser.get<cv::String>("video"));
|
||||
else if(parser.has("image"))
|
||||
inputFrame = cv::imread(parser.get<cv::String>("image"));
|
||||
else
|
||||
videoCapture.open(parser.get<int>("camera"));
|
||||
|
||||
if (videoCapture.isOpened())
|
||||
{
|
||||
videoCapture >> inputFrame;
|
||||
}
|
||||
|
||||
if(inputFrame.empty())
|
||||
{
|
||||
std::cout << "Failed to open media source\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Program start in a try/catch safety context (Retina may throw errors)
|
||||
try
|
||||
{
|
||||
// create a retina instance with default parameters setup, uncomment the initialisation you wanna test
|
||||
cv::Ptr<cv::bioinspired::Retina> myRetina;
|
||||
|
||||
// if the last parameter is 'log', then activate log sampling (favour foveal vision and subsamples peripheral vision)
|
||||
if (useLogSampling)
|
||||
{
|
||||
myRetina = cv::bioinspired::Retina::create(inputFrame.size(),
|
||||
true, cv::bioinspired::RETINA_COLOR_BAYER, true, 2.0, 10.0);
|
||||
}
|
||||
else// -> else allocate "classical" retina :
|
||||
myRetina = cv::bioinspired::Retina::create(inputFrame.size());
|
||||
|
||||
// save default retina parameters file in order to let you see this and maybe modify it and reload using method "setup"
|
||||
myRetina->write("RetinaDefaultParameters.xml");
|
||||
|
||||
// load parameters if file exists
|
||||
myRetina->setup("RetinaSpecificParameters.xml");
|
||||
myRetina->clearBuffers();
|
||||
|
||||
// declare retina output buffers
|
||||
cv::UMat retinaOutput_parvo;
|
||||
cv::UMat retinaOutput_magno;
|
||||
|
||||
// processing loop with stop condition
|
||||
int64 totalTime = 0;
|
||||
int64 totalFrames = 0;
|
||||
while(true)
|
||||
{
|
||||
// if using video stream, then, grabbing a new frame, else, input remains the same
|
||||
if (videoCapture.isOpened())
|
||||
videoCapture>>inputFrame;
|
||||
if(inputFrame.empty())
|
||||
break;
|
||||
|
||||
// run retina filter
|
||||
int64 frameTime = cv::getTickCount();
|
||||
myRetina->run(inputFrame);
|
||||
// Retrieve and display retina output
|
||||
frameTime = cv::getTickCount() - frameTime;
|
||||
totalTime += frameTime;
|
||||
totalFrames++;
|
||||
myRetina->getParvo(retinaOutput_parvo);
|
||||
myRetina->getMagno(retinaOutput_magno);
|
||||
cv::imshow("retina input", inputFrame);
|
||||
cv::imshow("Retina Parvo", retinaOutput_parvo);
|
||||
cv::imshow("Retina Magno", retinaOutput_magno);
|
||||
|
||||
int key = cv::waitKey(5);
|
||||
if(key == 'q')
|
||||
break;
|
||||
}
|
||||
std::cout << "\nMean frame processing time: " << (totalTime / cv::getTickFrequency()) / totalFrames << " s" << std::endl;
|
||||
std::cout << "Retina demo end" << std::endl;
|
||||
}
|
||||
catch(const cv::Exception& e)
|
||||
{
|
||||
std::cerr<<"Error using Retina : "<<e.what()<<std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,888 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2011
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include "basicretinafilter.hpp"
|
||||
#include <cmath>
|
||||
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
// @author Alexandre BENOIT, benoit.alexandre.vision@gmail.com, LISTIC : www.listic.univ-savoie.fr Gipsa-Lab, France: www.gipsa-lab.inpg.fr/
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// BASIC RETINA FILTER
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
// Constructor and Desctructor of the basic retina filter
|
||||
BasicRetinaFilter::BasicRetinaFilter(const unsigned int NBrows, const unsigned int NBcolumns, const unsigned int parametersListSize, const bool useProgressiveFilter)
|
||||
:_filterOutput(NBrows, NBcolumns),
|
||||
_localBuffer(NBrows*NBcolumns),
|
||||
_filteringCoeficientsTable(3*parametersListSize),
|
||||
_progressiveSpatialConstant(0),// pointer to a local table containing local spatial constant (allocated with the object)
|
||||
_progressiveGain(0)
|
||||
{
|
||||
#ifdef T_BASIC_RETINA_ELEMENT_DEBUG
|
||||
std::cout<<"BasicRetinaFilter::BasicRetinaFilter: new filter, size="<<NBrows<<", "<<NBcolumns<<std::endl;
|
||||
#endif
|
||||
_halfNBrows=_filterOutput.getNBrows()/2;
|
||||
_halfNBcolumns=_filterOutput.getNBcolumns()/2;
|
||||
|
||||
if (useProgressiveFilter)
|
||||
{
|
||||
#ifdef T_BASIC_RETINA_ELEMENT_DEBUG
|
||||
std::cout<<"BasicRetinaFilter::BasicRetinaFilter: _progressiveSpatialConstant_Tbuffer"<<std::endl;
|
||||
#endif
|
||||
_progressiveSpatialConstant.resize(_filterOutput.size());
|
||||
#ifdef T_BASIC_RETINA_ELEMENT_DEBUG
|
||||
std::cout<<"BasicRetinaFilter::BasicRetinaFilter: new _progressiveGain_Tbuffer"<<NBrows<<", "<<NBcolumns<<std::endl;
|
||||
#endif
|
||||
_progressiveGain.resize(_filterOutput.size());
|
||||
}
|
||||
#ifdef T_BASIC_RETINA_ELEMENT_DEBUG
|
||||
std::cout<<"BasicRetinaFilter::BasicRetinaFilter: new filter, size="<<NBrows<<", "<<NBcolumns<<std::endl;
|
||||
#endif
|
||||
|
||||
// set default values
|
||||
_maxInputValue=256.0;
|
||||
|
||||
// reset all buffers
|
||||
clearAllBuffers();
|
||||
|
||||
#ifdef T_BASIC_RETINA_ELEMENT_DEBUG
|
||||
std::cout<<"BasicRetinaFilter::Init BasicRetinaElement at specified frame size OK, size="<<this->size()<<std::endl;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
BasicRetinaFilter::~BasicRetinaFilter()
|
||||
{
|
||||
|
||||
#ifdef BASIC_RETINA_ELEMENT_DEBUG
|
||||
std::cout<<"BasicRetinaFilter::BasicRetinaElement Deleted OK"<<std::endl;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////
|
||||
// functions of the basic filter
|
||||
////////////////////////////////////
|
||||
|
||||
|
||||
// resize all allocated buffers
|
||||
void BasicRetinaFilter::resize(const unsigned int NBrows, const unsigned int NBcolumns)
|
||||
{
|
||||
|
||||
std::cout<<"BasicRetinaFilter::resize( "<<NBrows<<", "<<NBcolumns<<")"<<std::endl;
|
||||
|
||||
// resizing buffers
|
||||
_filterOutput.resizeBuffer(NBrows, NBcolumns);
|
||||
|
||||
// updating variables
|
||||
_halfNBrows=_filterOutput.getNBrows()/2;
|
||||
_halfNBcolumns=_filterOutput.getNBcolumns()/2;
|
||||
|
||||
_localBuffer.resize(_filterOutput.size());
|
||||
// in case of spatial adapted filter
|
||||
if (_progressiveSpatialConstant.size()>0)
|
||||
{
|
||||
_progressiveSpatialConstant.resize(_filterOutput.size());
|
||||
_progressiveGain.resize(_filterOutput.size());
|
||||
}
|
||||
// reset buffers
|
||||
clearAllBuffers();
|
||||
}
|
||||
|
||||
// Change coefficients table
|
||||
void BasicRetinaFilter::setLPfilterParameters(const float beta, const float tau, const float desired_k, const unsigned int filterIndex)
|
||||
{
|
||||
float _beta = beta+tau;
|
||||
float k=desired_k;
|
||||
// check if the spatial constant is correct (avoid 0 value to avoid division by 0)
|
||||
if (desired_k<=0)
|
||||
{
|
||||
k=0.001f;
|
||||
std::cerr<<"BasicRetinaFilter::spatial constant of the low pass filter must be superior to zero !!! correcting parameter setting to 0,001"<<std::endl;
|
||||
}
|
||||
|
||||
float _alpha = k*k;
|
||||
float _mu = 0.8f;
|
||||
unsigned int tableOffset=filterIndex*3;
|
||||
if (k<=0)
|
||||
{
|
||||
std::cerr<<"BasicRetinaFilter::spatial filtering coefficient must be superior to zero, correcting value to 0.01"<<std::endl;
|
||||
_alpha=0.0001f;
|
||||
}
|
||||
|
||||
float _temp = (1.0f+_beta)/(2.0f*_mu*_alpha);
|
||||
float a = _filteringCoeficientsTable[tableOffset] = 1.0f + _temp - (float)std::sqrt( (1.0f+_temp)*(1.0f+_temp) - 1.0f);
|
||||
_filteringCoeficientsTable[1+tableOffset]=(1.0f-a)*(1.0f-a)*(1.0f-a)*(1.0f-a)/(1.0f+_beta);
|
||||
_filteringCoeficientsTable[2+tableOffset] =tau;
|
||||
|
||||
//std::cout<<"BasicRetinaFilter::normal:"<<(1.0-a)*(1.0-a)*(1.0-a)*(1.0-a)/(1.0+_beta)<<" -> old:"<<(1-a)*(1-a)*(1-a)*(1-a)/(1+_beta)<<std::endl;
|
||||
|
||||
//std::cout<<"BasicRetinaFilter::a="<<a<<", gain="<<_filteringCoeficientsTable[1+tableOffset]<<", tau="<<tau<<std::endl;
|
||||
}
|
||||
|
||||
void BasicRetinaFilter::setProgressiveFilterConstants_CentredAccuracy(const float beta, const float tau, const float alpha0, const unsigned int filterIndex)
|
||||
{
|
||||
// check if dedicated buffers are already allocated, if not create them
|
||||
if (_progressiveSpatialConstant.size()!=_filterOutput.size())
|
||||
{
|
||||
_progressiveSpatialConstant.resize(_filterOutput.size());
|
||||
_progressiveGain.resize(_filterOutput.size());
|
||||
}
|
||||
|
||||
float _beta = beta+tau;
|
||||
float _mu=0.8f;
|
||||
if (alpha0<=0)
|
||||
{
|
||||
std::cerr<<"BasicRetinaFilter::spatial filtering coefficient must be superior to zero, correcting value to 0.01"<<std::endl;
|
||||
//alpha0=0.0001;
|
||||
}
|
||||
|
||||
unsigned int tableOffset=filterIndex*3;
|
||||
|
||||
float _alpha=0.8f;
|
||||
float _temp = (1.0f+_beta)/(2.0f*_mu*_alpha);
|
||||
float a=_filteringCoeficientsTable[tableOffset] = 1.0f + _temp - (float)std::sqrt( (1.0f+_temp)*(1.0f+_temp) - 1.0f);
|
||||
_filteringCoeficientsTable[tableOffset+1]=(1.0f-a)*(1.0f-a)*(1.0f-a)*(1.0f-a)/(1.0f+_beta);
|
||||
_filteringCoeficientsTable[tableOffset+2] =tau;
|
||||
|
||||
float commonFactor=alpha0/(float)std::sqrt(_halfNBcolumns*_halfNBcolumns+_halfNBrows*_halfNBrows+1.0f);
|
||||
//memset(_progressiveSpatialConstant, 255, _filterOutput.getNBpixels());
|
||||
for (unsigned int idColumn=0;idColumn<_halfNBcolumns; ++idColumn)
|
||||
for (unsigned int idRow=0;idRow<_halfNBrows; ++idRow)
|
||||
{
|
||||
// computing local spatial constant
|
||||
float localSpatialConstantValue=commonFactor*std::sqrt((float)(idColumn*idColumn)+(float)(idRow*idRow));
|
||||
if (localSpatialConstantValue>1.0f)
|
||||
localSpatialConstantValue=1.0f;
|
||||
|
||||
_progressiveSpatialConstant[_halfNBcolumns-1+idColumn+_filterOutput.getNBcolumns()*(_halfNBrows-1+idRow)]=localSpatialConstantValue;
|
||||
_progressiveSpatialConstant[_halfNBcolumns-1-idColumn+_filterOutput.getNBcolumns()*(_halfNBrows-1+idRow)]=localSpatialConstantValue;
|
||||
_progressiveSpatialConstant[_halfNBcolumns-1+idColumn+_filterOutput.getNBcolumns()*(_halfNBrows-1-idRow)]=localSpatialConstantValue;
|
||||
_progressiveSpatialConstant[_halfNBcolumns-1-idColumn+_filterOutput.getNBcolumns()*(_halfNBrows-1-idRow)]=localSpatialConstantValue;
|
||||
|
||||
// computing local gain
|
||||
float localGain=(1-localSpatialConstantValue)*(1-localSpatialConstantValue)*(1-localSpatialConstantValue)*(1-localSpatialConstantValue)/(1+_beta);
|
||||
_progressiveGain[_halfNBcolumns-1+idColumn+_filterOutput.getNBcolumns()*(_halfNBrows-1+idRow)]=localGain;
|
||||
_progressiveGain[_halfNBcolumns-1-idColumn+_filterOutput.getNBcolumns()*(_halfNBrows-1+idRow)]=localGain;
|
||||
_progressiveGain[_halfNBcolumns-1+idColumn+_filterOutput.getNBcolumns()*(_halfNBrows-1-idRow)]=localGain;
|
||||
_progressiveGain[_halfNBcolumns-1-idColumn+_filterOutput.getNBcolumns()*(_halfNBrows-1-idRow)]=localGain;
|
||||
|
||||
//std::cout<<commonFactor<<", "<<std::sqrt((_halfNBcolumns-1-idColumn)+(_halfNBrows-idRow-1))<<", "<<(_halfNBcolumns-1-idColumn)<<", "<<(_halfNBrows-idRow-1)<<", "<<localSpatialConstantValue<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void BasicRetinaFilter::setProgressiveFilterConstants_CustomAccuracy(const float beta, const float tau, const float k, const std::valarray<float> &accuracyMap, const unsigned int filterIndex)
|
||||
{
|
||||
|
||||
if (accuracyMap.size()!=_filterOutput.size())
|
||||
{
|
||||
std::cerr<<"BasicRetinaFilter::setProgressiveFilterConstants_CustomAccuracy: error: input accuracy map does not match filter size, init skept"<<std::endl;
|
||||
return ;
|
||||
}
|
||||
|
||||
// check if dedicated buffers are already allocated, if not create them
|
||||
if (_progressiveSpatialConstant.size()!=_filterOutput.size())
|
||||
{
|
||||
_progressiveSpatialConstant.resize(accuracyMap.size());
|
||||
_progressiveGain.resize(accuracyMap.size());
|
||||
}
|
||||
|
||||
float _beta = beta+tau;
|
||||
float _alpha=k*k;
|
||||
float _mu=0.8f;
|
||||
if (k<=0)
|
||||
{
|
||||
std::cerr<<"BasicRetinaFilter::spatial filtering coefficient must be superior to zero, correcting value to 0.01"<<std::endl;
|
||||
//alpha0=0.0001;
|
||||
}
|
||||
unsigned int tableOffset=filterIndex*3;
|
||||
float _temp = (1.0f+_beta)/(2.0f*_mu*_alpha);
|
||||
float a=_filteringCoeficientsTable[tableOffset] = 1.0f + _temp - (float)std::sqrt( (1.0f+_temp)*(1.0f+_temp) - 1.0f);
|
||||
_filteringCoeficientsTable[tableOffset+1]=(1.0f-a)*(1.0f-a)*(1.0f-a)*(1.0f-a)/(1.0f+_beta);
|
||||
_filteringCoeficientsTable[tableOffset+2] =tau;
|
||||
|
||||
//memset(_progressiveSpatialConstant, 255, _filterOutput.getNBpixels());
|
||||
for (unsigned int idColumn=0;idColumn<_filterOutput.getNBcolumns(); ++idColumn)
|
||||
for (unsigned int idRow=0;idRow<_filterOutput.getNBrows(); ++idRow)
|
||||
{
|
||||
// computing local spatial constant
|
||||
unsigned int index=idColumn+idRow*_filterOutput.getNBcolumns();
|
||||
float localSpatialConstantValue=_a*accuracyMap[index];
|
||||
if (localSpatialConstantValue>1)
|
||||
localSpatialConstantValue=1;
|
||||
|
||||
_progressiveSpatialConstant[index]=localSpatialConstantValue;
|
||||
|
||||
// computing local gain
|
||||
float localGain=(1.0f-localSpatialConstantValue)*(1.0f-localSpatialConstantValue)*(1.0f-localSpatialConstantValue)*(1.0f-localSpatialConstantValue)/(1.0f+_beta);
|
||||
_progressiveGain[index]=localGain;
|
||||
|
||||
//std::cout<<commonFactor<<", "<<std::sqrt((_halfNBcolumns-1-idColumn)+(_halfNBrows-idRow-1))<<", "<<(_halfNBcolumns-1-idColumn)<<", "<<(_halfNBrows-idRow-1)<<", "<<localSpatialConstantValue<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
/// Local luminance adaptation functions
|
||||
// run local adaptation filter and save result in _filterOutput
|
||||
const std::valarray<float> &BasicRetinaFilter::runFilter_LocalAdapdation(const std::valarray<float> &inputFrame, const std::valarray<float> &localLuminance)
|
||||
{
|
||||
_localLuminanceAdaptation(get_data(inputFrame), get_data(localLuminance), &_filterOutput[0]);
|
||||
return _filterOutput;
|
||||
}
|
||||
// run local adaptation filter at a specific output adress
|
||||
void BasicRetinaFilter::runFilter_LocalAdapdation(const std::valarray<float> &inputFrame, const std::valarray<float> &localLuminance, std::valarray<float> &outputFrame)
|
||||
{
|
||||
_localLuminanceAdaptation(get_data(inputFrame), get_data(localLuminance), &outputFrame[0]);
|
||||
}
|
||||
// run local adaptation filter and save result in _filterOutput with autonomous low pass filtering before adaptation
|
||||
const std::valarray<float> &BasicRetinaFilter::runFilter_LocalAdapdation_autonomous(const std::valarray<float> &inputFrame)
|
||||
{
|
||||
_spatiotemporalLPfilter(get_data(inputFrame), &_filterOutput[0]);
|
||||
_localLuminanceAdaptation(get_data(inputFrame), &_filterOutput[0], &_filterOutput[0]);
|
||||
return _filterOutput;
|
||||
}
|
||||
// run local adaptation filter at a specific output adress with autonomous low pass filtering before adaptation
|
||||
void BasicRetinaFilter::runFilter_LocalAdapdation_autonomous(const std::valarray<float> &inputFrame, std::valarray<float> &outputFrame)
|
||||
{
|
||||
_spatiotemporalLPfilter(get_data(inputFrame), &_filterOutput[0]);
|
||||
_localLuminanceAdaptation(get_data(inputFrame), &_filterOutput[0], &outputFrame[0]);
|
||||
}
|
||||
|
||||
// local luminance adaptation of the input in regard of localLuminance buffer, the input is rewrited and becomes the output
|
||||
void BasicRetinaFilter::_localLuminanceAdaptation(float *inputOutputFrame, const float *localLuminance)
|
||||
{
|
||||
_localLuminanceAdaptation(inputOutputFrame, localLuminance, inputOutputFrame, false);
|
||||
|
||||
/* const float *localLuminancePTR=localLuminance;
|
||||
float *inputOutputFramePTR=inputOutputFrame;
|
||||
|
||||
for (unsigned int IDpixel=0 ; IDpixel<_filterOutput.getNBpixels() ; ++IDpixel, ++inputOutputFramePTR)
|
||||
{
|
||||
float X0=*(localLuminancePTR++)*_localLuminanceFactor+_localLuminanceAddon;
|
||||
*(inputOutputFramePTR) = (_maxInputValue+X0)**inputOutputFramePTR/(*inputOutputFramePTR +X0+0.00000000001);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// local luminance adaptation of the input in regard of localLuminance buffer
|
||||
void BasicRetinaFilter::_localLuminanceAdaptation(const float *inputFrame, const float *localLuminance, float *outputFrame, const bool updateLuminanceMean)
|
||||
{
|
||||
if (updateLuminanceMean)
|
||||
{ float meanLuminance=0;
|
||||
const float *luminancePTR=inputFrame;
|
||||
for (unsigned int i=0;i<_filterOutput.getNBpixels();++i)
|
||||
meanLuminance+=*(luminancePTR++);
|
||||
meanLuminance/=_filterOutput.getNBpixels();
|
||||
//float tempMeanValue=meanLuminance+_meanInputValue*_tau;
|
||||
updateCompressionParameter(meanLuminance);
|
||||
}
|
||||
#ifdef MAKE_PARALLEL
|
||||
cv::parallel_for_(cv::Range(0,_filterOutput.getNBpixels()), Parallel_localAdaptation(localLuminance, inputFrame, outputFrame, _localLuminanceFactor, _localLuminanceAddon, _maxInputValue));
|
||||
#else
|
||||
//std::cout<<meanLuminance<<std::endl;
|
||||
const float *localLuminancePTR=localLuminance;
|
||||
const float *inputFramePTR=inputFrame;
|
||||
float *outputFramePTR=outputFrame;
|
||||
for (unsigned int IDpixel=0 ; IDpixel<_filterOutput.getNBpixels() ; ++IDpixel, ++inputFramePTR, ++outputFramePTR)
|
||||
{
|
||||
float X0=*(localLuminancePTR++)*_localLuminanceFactor+_localLuminanceAddon;
|
||||
// TODO : the following line can lead to a divide by zero ! A small offset is added, take care if the offset is too large in case of High Dynamic Range images which can use very small values...
|
||||
*(outputFramePTR) = (_maxInputValue+X0)**inputFramePTR/(*inputFramePTR +X0+0.00000000001);
|
||||
//std::cout<<"BasicRetinaFilter::inputFrame[IDpixel]=%f, X0=%f, outputFrame[IDpixel]=%f\n", inputFrame[IDpixel], X0, outputFrame[IDpixel]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// local adaptation applied on a range of values which can be positive and negative
|
||||
void BasicRetinaFilter::_localLuminanceAdaptationPosNegValues(const float *inputFrame, const float *localLuminance, float *outputFrame)
|
||||
{
|
||||
const float *localLuminancePTR=localLuminance;
|
||||
const float *inputFramePTR=inputFrame;
|
||||
float *outputFramePTR=outputFrame;
|
||||
float factor=_maxInputValue*2.0f/(float)CV_PI;
|
||||
for (unsigned int IDpixel=0 ; IDpixel<_filterOutput.getNBpixels() ; ++IDpixel, ++inputFramePTR)
|
||||
{
|
||||
float X0=*(localLuminancePTR++)*_localLuminanceFactor+_localLuminanceAddon;
|
||||
*(outputFramePTR++) = factor*atan(*inputFramePTR/X0);//(_maxInputValue+X0)**inputFramePTR/(*inputFramePTR +X0);
|
||||
//std::cout<<"BasicRetinaFilter::inputFrame[IDpixel]=%f, X0=%f, outputFrame[IDpixel]=%f\n", inputFrame[IDpixel], X0, outputFrame[IDpixel]);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
/// Spatio temporal Low Pass filter functions
|
||||
// run LP filter and save result in the basic retina element buffer
|
||||
const std::valarray<float> &BasicRetinaFilter::runFilter_LPfilter(const std::valarray<float> &inputFrame, const unsigned int filterIndex)
|
||||
{
|
||||
_spatiotemporalLPfilter(get_data(inputFrame), &_filterOutput[0], filterIndex);
|
||||
return _filterOutput;
|
||||
}
|
||||
|
||||
// run LP filter for a new frame input and save result at a specific output adress
|
||||
void BasicRetinaFilter::runFilter_LPfilter(const std::valarray<float> &inputFrame, std::valarray<float> &outputFrame, const unsigned int filterIndex)
|
||||
{
|
||||
_spatiotemporalLPfilter(get_data(inputFrame), &outputFrame[0], filterIndex);
|
||||
}
|
||||
|
||||
// run LP filter on the input data and rewrite it
|
||||
void BasicRetinaFilter::runFilter_LPfilter_Autonomous(std::valarray<float> &inputOutputFrame, const unsigned int filterIndex)
|
||||
{
|
||||
unsigned int coefTableOffset=filterIndex*3;
|
||||
|
||||
/**********/
|
||||
_a=_filteringCoeficientsTable[coefTableOffset];
|
||||
_gain=_filteringCoeficientsTable[1+coefTableOffset];
|
||||
_tau=_filteringCoeficientsTable[2+coefTableOffset];
|
||||
|
||||
// launch the serie of 1D directional filters in order to compute the 2D low pass filter
|
||||
_horizontalCausalFilter(&inputOutputFrame[0], 0, _filterOutput.getNBrows());
|
||||
_horizontalAnticausalFilter(&inputOutputFrame[0], 0, _filterOutput.getNBrows());
|
||||
_verticalCausalFilter(&inputOutputFrame[0], 0, _filterOutput.getNBcolumns());
|
||||
_verticalAnticausalFilter_multGain(&inputOutputFrame[0], 0, _filterOutput.getNBcolumns());
|
||||
|
||||
}
|
||||
// run LP filter for a new frame input and save result at a specific output adress
|
||||
void BasicRetinaFilter::_spatiotemporalLPfilter(const float *inputFrame, float *outputFrame, const unsigned int filterIndex)
|
||||
{
|
||||
unsigned int coefTableOffset=filterIndex*3;
|
||||
/**********/
|
||||
_a=_filteringCoeficientsTable[coefTableOffset];
|
||||
_gain=_filteringCoeficientsTable[1+coefTableOffset];
|
||||
_tau=_filteringCoeficientsTable[2+coefTableOffset];
|
||||
|
||||
// launch the serie of 1D directional filters in order to compute the 2D low pass filter
|
||||
_horizontalCausalFilter_addInput(inputFrame, outputFrame, 0,_filterOutput.getNBrows());
|
||||
_horizontalAnticausalFilter(outputFrame, 0, _filterOutput.getNBrows());
|
||||
_verticalCausalFilter(outputFrame, 0, _filterOutput.getNBcolumns());
|
||||
_verticalAnticausalFilter_multGain(outputFrame, 0, _filterOutput.getNBcolumns());
|
||||
|
||||
}
|
||||
|
||||
// run SQUARING LP filter for a new frame input and save result at a specific output adress
|
||||
float BasicRetinaFilter::_squaringSpatiotemporalLPfilter(const float *inputFrame, float *outputFrame, const unsigned int filterIndex)
|
||||
{
|
||||
unsigned int coefTableOffset=filterIndex*3;
|
||||
/**********/
|
||||
_a=_filteringCoeficientsTable[coefTableOffset];
|
||||
_gain=_filteringCoeficientsTable[1+coefTableOffset];
|
||||
_tau=_filteringCoeficientsTable[2+coefTableOffset];
|
||||
|
||||
// launch the serie of 1D directional filters in order to compute the 2D low pass filter
|
||||
|
||||
_squaringHorizontalCausalFilter(inputFrame, outputFrame, 0, _filterOutput.getNBrows());
|
||||
_horizontalAnticausalFilter(outputFrame, 0, _filterOutput.getNBrows());
|
||||
_verticalCausalFilter(outputFrame, 0, _filterOutput.getNBcolumns());
|
||||
return _verticalAnticausalFilter_returnMeanValue(outputFrame, 0, _filterOutput.getNBcolumns());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// standard version of the 1D low pass filters
|
||||
|
||||
// horizontal causal filter which adds the input inside
|
||||
void BasicRetinaFilter::_horizontalCausalFilter(float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd)
|
||||
{
|
||||
|
||||
|
||||
//#pragma omp parallel for
|
||||
for (unsigned int IDrow=IDrowStart; IDrow<IDrowEnd; ++IDrow)
|
||||
{
|
||||
float* outputPTR=outputFrame+(IDrowStart+IDrow)*_filterOutput.getNBcolumns();
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBcolumns(); ++index)
|
||||
{
|
||||
result = *(outputPTR)+ _a* result;
|
||||
*(outputPTR++) = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
// horizontal causal filter which adds the input inside
|
||||
void BasicRetinaFilter::_horizontalCausalFilter_addInput(const float *inputFrame, float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd)
|
||||
{
|
||||
#ifdef MAKE_PARALLEL
|
||||
cv::parallel_for_(cv::Range(IDrowStart,IDrowEnd), Parallel_horizontalCausalFilter_addInput(inputFrame, outputFrame, IDrowStart, _filterOutput.getNBcolumns(), _a, _tau));
|
||||
#else
|
||||
for (unsigned int IDrow=IDrowStart; IDrow<IDrowEnd; ++IDrow)
|
||||
{
|
||||
float* outputPTR=outputFrame+(IDrowStart+IDrow)*_filterOutput.getNBcolumns();
|
||||
const float* inputPTR=inputFrame+(IDrowStart+IDrow)*_filterOutput.getNBcolumns();
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBcolumns(); ++index)
|
||||
{
|
||||
result = *(inputPTR++) + _tau**(outputPTR)+ _a* result;
|
||||
*(outputPTR++) = result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// horizontal anticausal filter (basic way, no add on)
|
||||
void BasicRetinaFilter::_horizontalAnticausalFilter(float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd)
|
||||
{
|
||||
|
||||
#ifdef MAKE_PARALLEL
|
||||
cv::parallel_for_(cv::Range(IDrowStart,IDrowEnd), Parallel_horizontalAnticausalFilter(outputFrame, IDrowEnd, _filterOutput.getNBcolumns(), _a ));
|
||||
#else
|
||||
for (unsigned int IDrow=IDrowStart; IDrow<IDrowEnd; ++IDrow)
|
||||
{
|
||||
float* outputPTR=outputFrame+(IDrowEnd-IDrow)*(_filterOutput.getNBcolumns())-1;
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBcolumns(); ++index)
|
||||
{
|
||||
result = *(outputPTR)+ _a* result;
|
||||
*(outputPTR--) = result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// horizontal anticausal filter which multiplies the output by _gain
|
||||
void BasicRetinaFilter::_horizontalAnticausalFilter_multGain(float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd)
|
||||
{
|
||||
|
||||
//#pragma omp parallel for
|
||||
for (unsigned int IDrow=IDrowStart; IDrow<IDrowEnd; ++IDrow)
|
||||
{
|
||||
float* outputPTR=outputFrame+(IDrowEnd-IDrow)*(_filterOutput.getNBcolumns())-1;
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBcolumns(); ++index)
|
||||
{
|
||||
result = *(outputPTR)+ _a* result;
|
||||
*(outputPTR--) = _gain*result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vertical anticausal filter
|
||||
void BasicRetinaFilter::_verticalCausalFilter(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd)
|
||||
{
|
||||
#ifdef MAKE_PARALLEL
|
||||
cv::parallel_for_(cv::Range(IDcolumnStart,IDcolumnEnd), Parallel_verticalCausalFilter(outputFrame, _filterOutput.getNBrows(), _filterOutput.getNBcolumns(), _a ));
|
||||
#else
|
||||
for (unsigned int IDcolumn=IDcolumnStart; IDcolumn<IDcolumnEnd; ++IDcolumn)
|
||||
{
|
||||
float result=0;
|
||||
float *outputPTR=outputFrame+IDcolumn;
|
||||
|
||||
for (unsigned int index=0; index<_filterOutput.getNBrows(); ++index)
|
||||
{
|
||||
result = *(outputPTR) + _a * result;
|
||||
*(outputPTR) = result;
|
||||
outputPTR+=_filterOutput.getNBcolumns();
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// vertical anticausal filter (basic way, no add on)
|
||||
void BasicRetinaFilter::_verticalAnticausalFilter(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd)
|
||||
{
|
||||
float* offset=outputFrame+_filterOutput.getNBpixels()-_filterOutput.getNBcolumns();
|
||||
//#pragma omp parallel for
|
||||
for (unsigned int IDcolumn=IDcolumnStart; IDcolumn<IDcolumnEnd; ++IDcolumn)
|
||||
{
|
||||
float result=0;
|
||||
float *outputPTR=offset+IDcolumn;
|
||||
|
||||
for (unsigned int index=0; index<_filterOutput.getNBrows(); ++index)
|
||||
{
|
||||
result = *(outputPTR) + _a * result;
|
||||
*(outputPTR) = result;
|
||||
outputPTR-=_filterOutput.getNBcolumns();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vertical anticausal filter which multiplies the output by _gain
|
||||
void BasicRetinaFilter::_verticalAnticausalFilter_multGain(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd)
|
||||
{
|
||||
#ifdef MAKE_PARALLEL
|
||||
cv::parallel_for_(cv::Range(IDcolumnStart,IDcolumnEnd), Parallel_verticalAnticausalFilter_multGain(outputFrame, _filterOutput.getNBrows(), _filterOutput.getNBcolumns(), _a, _gain ));
|
||||
#else
|
||||
float* offset=outputFrame+_filterOutput.getNBpixels()-_filterOutput.getNBcolumns();
|
||||
//#pragma omp parallel for
|
||||
for (unsigned int IDcolumn=IDcolumnStart; IDcolumn<IDcolumnEnd; ++IDcolumn)
|
||||
{
|
||||
float result=0;
|
||||
float *outputPTR=offset+IDcolumn;
|
||||
|
||||
for (unsigned int index=0; index<_filterOutput.getNBrows(); ++index)
|
||||
{
|
||||
result = *(outputPTR) + _a * result;
|
||||
*(outputPTR) = _gain*result;
|
||||
outputPTR-=_filterOutput.getNBcolumns();
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
// specific modifications of 1D filters
|
||||
|
||||
// -> squaring horizontal causal filter
|
||||
void BasicRetinaFilter::_squaringHorizontalCausalFilter(const float *inputFrame, float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd)
|
||||
{
|
||||
float* outputPTR=outputFrame+IDrowStart*_filterOutput.getNBcolumns();
|
||||
const float* inputPTR=inputFrame+IDrowStart*_filterOutput.getNBcolumns();
|
||||
for (unsigned int IDrow=IDrowStart; IDrow<IDrowEnd; ++IDrow)
|
||||
{
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBcolumns(); ++index)
|
||||
{
|
||||
result = *(inputPTR)**(inputPTR) + _tau**(outputPTR)+ _a* result;
|
||||
*(outputPTR++) = result;
|
||||
++inputPTR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vertical anticausal filter that returns the mean value of its result
|
||||
float BasicRetinaFilter::_verticalAnticausalFilter_returnMeanValue(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd)
|
||||
{
|
||||
float meanValue=0;
|
||||
float* offset=outputFrame+_filterOutput.getNBpixels()-_filterOutput.getNBcolumns();
|
||||
for (unsigned int IDcolumn=IDcolumnStart; IDcolumn<IDcolumnEnd; ++IDcolumn)
|
||||
{
|
||||
float result=0;
|
||||
float *outputPTR=offset+IDcolumn;
|
||||
|
||||
for (unsigned int index=0; index<_filterOutput.getNBrows(); ++index)
|
||||
{
|
||||
result = *(outputPTR) + _a * result;
|
||||
*(outputPTR) = _gain*result;
|
||||
meanValue+=*(outputPTR);
|
||||
outputPTR-=_filterOutput.getNBcolumns();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return meanValue/(float)_filterOutput.getNBpixels();
|
||||
}
|
||||
|
||||
// LP filter with integration in specific areas (regarding true values of a binary parameters image)
|
||||
void BasicRetinaFilter::_localSquaringSpatioTemporalLPfilter(const float *inputFrame, float *LPfilterOutput, const unsigned int *integrationAreas, const unsigned int filterIndex)
|
||||
{
|
||||
unsigned int coefTableOffset=filterIndex*3;
|
||||
_a=_filteringCoeficientsTable[coefTableOffset+0];
|
||||
_gain=_filteringCoeficientsTable[coefTableOffset+1];
|
||||
_tau=_filteringCoeficientsTable[coefTableOffset+2];
|
||||
// launch the serie of 1D directional filters in order to compute the 2D low pass filter
|
||||
|
||||
_local_squaringHorizontalCausalFilter(inputFrame, LPfilterOutput, 0, _filterOutput.getNBrows(), integrationAreas);
|
||||
_local_horizontalAnticausalFilter(LPfilterOutput, 0, _filterOutput.getNBrows(), integrationAreas);
|
||||
_local_verticalCausalFilter(LPfilterOutput, 0, _filterOutput.getNBcolumns(), integrationAreas);
|
||||
_local_verticalAnticausalFilter_multGain(LPfilterOutput, 0, _filterOutput.getNBcolumns(), integrationAreas);
|
||||
|
||||
}
|
||||
|
||||
// LP filter on specific parts of the picture instead of all the image
|
||||
// same functions (some of them) but take a binary flag to allow integration, false flag means, no data change at the output...
|
||||
|
||||
// this function take an image in input and squares it befor computing
|
||||
void BasicRetinaFilter::_local_squaringHorizontalCausalFilter(const float *inputFrame, float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd, const unsigned int *integrationAreas)
|
||||
{
|
||||
float* outputPTR=outputFrame+IDrowStart*_filterOutput.getNBcolumns();
|
||||
const float* inputPTR=inputFrame+IDrowStart*_filterOutput.getNBcolumns();
|
||||
const unsigned int *integrationAreasPTR=integrationAreas;
|
||||
for (unsigned int IDrow=IDrowStart; IDrow<IDrowEnd; ++IDrow)
|
||||
{
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBcolumns(); ++index)
|
||||
{
|
||||
if (*(integrationAreasPTR++))
|
||||
result = *(inputPTR)**(inputPTR) + _tau**(outputPTR)+ _a* result;
|
||||
else
|
||||
result=0;
|
||||
*(outputPTR++) = result;
|
||||
++inputPTR;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BasicRetinaFilter::_local_horizontalAnticausalFilter(float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd, const unsigned int *integrationAreas)
|
||||
{
|
||||
|
||||
float* outputPTR=outputFrame+IDrowEnd*(_filterOutput.getNBcolumns())-1;
|
||||
const unsigned int *integrationAreasPTR=integrationAreas;
|
||||
|
||||
for (unsigned int IDrow=IDrowStart; IDrow<IDrowEnd; ++IDrow)
|
||||
{
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBcolumns(); ++index)
|
||||
{
|
||||
if (*(integrationAreasPTR++))
|
||||
result = *(outputPTR)+ _a* result;
|
||||
else
|
||||
result=0;
|
||||
*(outputPTR--) = result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void BasicRetinaFilter::_local_verticalCausalFilter(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd, const unsigned int *integrationAreas)
|
||||
{
|
||||
const unsigned int *integrationAreasPTR=integrationAreas;
|
||||
|
||||
for (unsigned int IDcolumn=IDcolumnStart; IDcolumn<IDcolumnEnd; ++IDcolumn)
|
||||
{
|
||||
float result=0;
|
||||
float *outputPTR=outputFrame+IDcolumn;
|
||||
|
||||
for (unsigned int index=0; index<_filterOutput.getNBrows(); ++index)
|
||||
{
|
||||
if (*(integrationAreasPTR++))
|
||||
result = *(outputPTR)+ _a* result;
|
||||
else
|
||||
result=0;
|
||||
*(outputPTR) = result;
|
||||
outputPTR+=_filterOutput.getNBcolumns();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// this functions affects _gain at the output
|
||||
void BasicRetinaFilter::_local_verticalAnticausalFilter_multGain(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd, const unsigned int *integrationAreas)
|
||||
{
|
||||
const unsigned int *integrationAreasPTR=integrationAreas;
|
||||
float* offset=outputFrame+_filterOutput.getNBpixels()-_filterOutput.getNBcolumns();
|
||||
|
||||
for (unsigned int IDcolumn=IDcolumnStart; IDcolumn<IDcolumnEnd; ++IDcolumn)
|
||||
{
|
||||
float result=0;
|
||||
float *outputPTR=offset+IDcolumn;
|
||||
|
||||
for (unsigned int index=0; index<_filterOutput.getNBrows(); ++index)
|
||||
{
|
||||
if (*(integrationAreasPTR++))
|
||||
result = *(outputPTR)+ _a* result;
|
||||
else
|
||||
result=0;
|
||||
*(outputPTR) = _gain*result;
|
||||
outputPTR-=_filterOutput.getNBcolumns();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// run LP filter for a new frame input and save result at a specific output adress
|
||||
// -> USE IRREGULAR SPATIAL CONSTANT
|
||||
|
||||
// irregular filter computed from a buffer and rewrites it
|
||||
void BasicRetinaFilter::_spatiotemporalLPfilter_Irregular(float *inputOutputFrame, const unsigned int filterIndex)
|
||||
{
|
||||
if (_progressiveGain.size()==0)
|
||||
{
|
||||
std::cerr<<"BasicRetinaFilter::runProgressiveFilter: cannot perform filtering, no progressive filter settled up"<<std::endl;
|
||||
return;
|
||||
}
|
||||
unsigned int coefTableOffset=filterIndex*3;
|
||||
/**********/
|
||||
//_a=_filteringCoeficientsTable[coefTableOffset];
|
||||
_tau=_filteringCoeficientsTable[2+coefTableOffset];
|
||||
|
||||
// launch the serie of 1D directional filters in order to compute the 2D low pass filter
|
||||
_horizontalCausalFilter_Irregular(inputOutputFrame, 0, (int)_filterOutput.getNBrows());
|
||||
_horizontalAnticausalFilter_Irregular(inputOutputFrame, 0, (int)_filterOutput.getNBrows(), &_progressiveSpatialConstant[0]);
|
||||
_verticalCausalFilter_Irregular(inputOutputFrame, 0, (int)_filterOutput.getNBcolumns(), &_progressiveSpatialConstant[0]);
|
||||
_verticalAnticausalFilter_Irregular_multGain(inputOutputFrame, 0, (int)_filterOutput.getNBcolumns());
|
||||
|
||||
}
|
||||
// irregular filter computed from a buffer and puts result on another
|
||||
void BasicRetinaFilter::_spatiotemporalLPfilter_Irregular(const float *inputFrame, float *outputFrame, const unsigned int filterIndex)
|
||||
{
|
||||
if (_progressiveGain.size()==0)
|
||||
{
|
||||
std::cerr<<"BasicRetinaFilter::runProgressiveFilter: cannot perform filtering, no progressive filter settled up"<<std::endl;
|
||||
return;
|
||||
}
|
||||
unsigned int coefTableOffset=filterIndex*3;
|
||||
/**********/
|
||||
//_a=_filteringCoeficientsTable[coefTableOffset];
|
||||
_tau=_filteringCoeficientsTable[2+coefTableOffset];
|
||||
|
||||
// launch the serie of 1D directional filters in order to compute the 2D low pass filter
|
||||
_horizontalCausalFilter_Irregular_addInput(inputFrame, outputFrame, 0, (int)_filterOutput.getNBrows());
|
||||
_horizontalAnticausalFilter_Irregular(outputFrame, 0, (int)_filterOutput.getNBrows(), &_progressiveSpatialConstant[0]);
|
||||
_verticalCausalFilter_Irregular(outputFrame, 0, (int)_filterOutput.getNBcolumns(), &_progressiveSpatialConstant[0]);
|
||||
_verticalAnticausalFilter_Irregular_multGain(outputFrame, 0, (int)_filterOutput.getNBcolumns());
|
||||
|
||||
}
|
||||
// 1D filters with irregular spatial constant
|
||||
// horizontal causal filter wich runs on its input buffer
|
||||
void BasicRetinaFilter::_horizontalCausalFilter_Irregular(float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd)
|
||||
{
|
||||
float* outputPTR=outputFrame+IDrowStart*_filterOutput.getNBcolumns();
|
||||
const float* spatialConstantPTR=&_progressiveSpatialConstant[0]+IDrowStart*_filterOutput.getNBcolumns();
|
||||
for (unsigned int IDrow=IDrowStart; IDrow<IDrowEnd; ++IDrow)
|
||||
{
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBcolumns(); ++index)
|
||||
{
|
||||
result = *(outputPTR)+ *(spatialConstantPTR++)* result;
|
||||
*(outputPTR++) = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// horizontal causal filter with add input
|
||||
void BasicRetinaFilter::_horizontalCausalFilter_Irregular_addInput(const float *inputFrame, float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd)
|
||||
{
|
||||
float* outputPTR=outputFrame+IDrowStart*_filterOutput.getNBcolumns();
|
||||
const float* inputPTR=inputFrame+IDrowStart*_filterOutput.getNBcolumns();
|
||||
const float* spatialConstantPTR=&_progressiveSpatialConstant[0]+IDrowStart*_filterOutput.getNBcolumns();
|
||||
for (unsigned int IDrow=IDrowStart; IDrow<IDrowEnd; ++IDrow)
|
||||
{
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBcolumns(); ++index)
|
||||
{
|
||||
result = *(inputPTR++) + _tau**(outputPTR)+ *(spatialConstantPTR++)* result;
|
||||
*(outputPTR++) = result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// horizontal anticausal filter (basic way, no add on)
|
||||
void BasicRetinaFilter::_horizontalAnticausalFilter_Irregular(float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd, const float *spatialConstantBuffer)
|
||||
{
|
||||
#ifdef MAKE_PARALLEL
|
||||
cv::parallel_for_(cv::Range(IDrowStart,IDrowEnd), Parallel_horizontalAnticausalFilter_Irregular(outputFrame, spatialConstantBuffer, IDrowEnd, _filterOutput.getNBcolumns()));
|
||||
#else
|
||||
float* outputPTR=outputFrame+IDrowEnd*(_filterOutput.getNBcolumns())-1;
|
||||
const float* spatialConstantPTR=spatialConstantBuffer+IDrowEnd*(_filterOutput.getNBcolumns())-1;
|
||||
|
||||
for (unsigned int IDrow=IDrowStart; IDrow<IDrowEnd; ++IDrow)
|
||||
{
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBcolumns(); ++index)
|
||||
{
|
||||
result = *(outputPTR)+ *(spatialConstantPTR--)* result;
|
||||
*(outputPTR--) = result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// vertical anticausal filter
|
||||
void BasicRetinaFilter::_verticalCausalFilter_Irregular(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd, const float *spatialConstantBuffer)
|
||||
{
|
||||
#ifdef MAKE_PARALLEL
|
||||
cv::parallel_for_(cv::Range(IDcolumnStart,IDcolumnEnd), Parallel_verticalCausalFilter_Irregular(outputFrame, spatialConstantBuffer, _filterOutput.getNBrows(), _filterOutput.getNBcolumns()));
|
||||
#else
|
||||
for (unsigned int IDcolumn=IDcolumnStart; IDcolumn<IDcolumnEnd; ++IDcolumn)
|
||||
{
|
||||
float result=0;
|
||||
float *outputPTR=outputFrame+IDcolumn;
|
||||
const float *spatialConstantPTR=spatialConstantBuffer+IDcolumn;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBrows(); ++index)
|
||||
{
|
||||
result = *(outputPTR) + *(spatialConstantPTR) * result;
|
||||
*(outputPTR) = result;
|
||||
outputPTR+=_filterOutput.getNBcolumns();
|
||||
spatialConstantPTR+=_filterOutput.getNBcolumns();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// vertical anticausal filter which multiplies the output by _gain
|
||||
void BasicRetinaFilter::_verticalAnticausalFilter_Irregular_multGain(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd)
|
||||
{
|
||||
float* outputOffset=outputFrame+_filterOutput.getNBpixels()-_filterOutput.getNBcolumns();
|
||||
const float* constantOffset=&_progressiveSpatialConstant[0]+_filterOutput.getNBpixels()-_filterOutput.getNBcolumns();
|
||||
const float* gainOffset=&_progressiveGain[0]+_filterOutput.getNBpixels()-_filterOutput.getNBcolumns();
|
||||
for (unsigned int IDcolumn=IDcolumnStart; IDcolumn<IDcolumnEnd; ++IDcolumn)
|
||||
{
|
||||
float result=0;
|
||||
float *outputPTR=outputOffset+IDcolumn;
|
||||
const float *spatialConstantPTR=constantOffset+IDcolumn;
|
||||
const float *progressiveGainPTR=gainOffset+IDcolumn;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBrows(); ++index)
|
||||
{
|
||||
result = *(outputPTR) + *(spatialConstantPTR) * result;
|
||||
*(outputPTR) = *(progressiveGainPTR)*result;
|
||||
outputPTR-=_filterOutput.getNBcolumns();
|
||||
spatialConstantPTR-=_filterOutput.getNBcolumns();
|
||||
progressiveGainPTR-=_filterOutput.getNBcolumns();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
@@ -0,0 +1,678 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2011
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef BASICRETINAELEMENT_HPP_
|
||||
#define BASICRETINAELEMENT_HPP_
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
||||
/**
|
||||
* @class BasicRetinaFilter
|
||||
* @brief Brief overview, this class provides tools for low level image processing:
|
||||
* --> this class is able to perform:
|
||||
* -> first order Low pass optimized filtering
|
||||
* -> local luminance adaptation (able to correct back light problems and contrast enhancement)
|
||||
* -> progressive low pass filter filtering (higher filtering on the borders than on the center)
|
||||
* -> image data between 0 and 255 resampling with different options, linear rescaling, sigmoide)
|
||||
*
|
||||
* NOTE : initially the retina model was based on double format scalar values but
|
||||
* a good memory/precision compromise is float...
|
||||
* also the double format precision does not make so much sense from a biological point of view (neurons value coding is not so precise)
|
||||
*
|
||||
* TYPICAL USE:
|
||||
*
|
||||
* // create object at a specified picture size
|
||||
* BasicRetinaFilter *_photoreceptorsPrefilter;
|
||||
* _photoreceptorsPrefilter =new BasicRetinaFilter(sizeRows, sizeWindows);
|
||||
*
|
||||
* // init gain, spatial and temporal parameters:
|
||||
* _photoreceptorsPrefilter->setCoefficientsTable(gain,temporalConstant, spatialConstant);
|
||||
*
|
||||
* // during program execution, call the filter for local luminance correction or low pass filtering for an input picture called "FrameBuffer":
|
||||
* _photoreceptorsPrefilter->runFilter_LocalAdapdation(FrameBuffer);
|
||||
* // or (Low pass first order filter)
|
||||
* _photoreceptorsPrefilter->runFilter_LPfilter(FrameBuffer);
|
||||
* // get output frame and its size:
|
||||
* const unsigned int output_nbRows=_photoreceptorsPrefilter->getNBrows();
|
||||
* const unsigned int output_nbColumns=_photoreceptorsPrefilter->getNBcolumns();
|
||||
* const double *outputFrame=_photoreceptorsPrefilter->getOutput();
|
||||
*
|
||||
* // at the end of the program, destroy object:
|
||||
* delete _photoreceptorsPrefilter;
|
||||
|
||||
* @author Alexandre BENOIT, benoit.alexandre.vision@gmail.com, LISTIC : www.listic.univ-savoie.fr, Gipsa-Lab, France: www.gipsa-lab.inpg.fr/
|
||||
* Creation date 2007
|
||||
* synthesis of the work described in Alexandre BENOIT thesis: "Le systeme visuel humain au secours de la vision par ordinateur"
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include "templatebuffer.hpp"
|
||||
|
||||
//#define __BASIC_RETINA_ELEMENT_DEBUG
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
class BasicRetinaFilter
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* constructor of the base bio-inspired toolbox, parameters are only linked to imae input size and number of filtering capabilities of the object
|
||||
* @param NBrows: number of rows of the input image
|
||||
* @param NBcolumns: number of columns of the input image
|
||||
* @param parametersListSize: specifies the number of parameters set (each parameters set represents a specific low pass spatio-temporal filter)
|
||||
* @param useProgressiveFilter: specifies if the filter has irreguar (progressive) filtering capabilities (this can be activated later using setProgressiveFilterConstants_xxx methods)
|
||||
*/
|
||||
BasicRetinaFilter(const unsigned int NBrows, const unsigned int NBcolumns, const unsigned int parametersListSize=1, const bool useProgressiveFilter=false);
|
||||
|
||||
/**
|
||||
* standrad destructore
|
||||
*/
|
||||
~BasicRetinaFilter();
|
||||
|
||||
/**
|
||||
* function which clears the output buffer of the object
|
||||
*/
|
||||
inline void clearOutputBuffer() { _filterOutput = 0; }
|
||||
|
||||
/**
|
||||
* function which clears the secondary buffer of the object
|
||||
*/
|
||||
inline void clearSecondaryBuffer() { _localBuffer = 0; }
|
||||
|
||||
/**
|
||||
* function which clears the output and the secondary buffer of the object
|
||||
*/
|
||||
inline void clearAllBuffers() { clearOutputBuffer(); clearSecondaryBuffer(); }
|
||||
|
||||
/**
|
||||
* resize basic retina filter object (resize all allocated buffers
|
||||
* @param NBrows: the new height size
|
||||
* @param NBcolumns: the new width size
|
||||
*/
|
||||
void resize(const unsigned int NBrows, const unsigned int NBcolumns);
|
||||
|
||||
/**
|
||||
* forbiden method inherited from parent std::valarray
|
||||
* prefer not to use this method since the filter matrix become vectors
|
||||
*/
|
||||
void resize(const unsigned int) { std::cerr<<"error, not accessible method"<<std::endl; }
|
||||
|
||||
/**
|
||||
* low pass filter call and run (models the homogeneous cells network at the retina level, for example horizontal cells or photoreceptors)
|
||||
* @param inputFrame: the input image to be processed
|
||||
* @param filterIndex: the offset which specifies the parameter set that should be used for the filtering
|
||||
* @return the processed image, the output is reachable later by using function getOutput()
|
||||
*/
|
||||
const std::valarray<float> &runFilter_LPfilter(const std::valarray<float> &inputFrame, const unsigned int filterIndex=0); // run the LP filter for a new frame input and save result in _filterOutput
|
||||
|
||||
/**
|
||||
* low pass filter call and run (models the homogeneous cells network at the retina level, for example horizontal cells or photoreceptors)
|
||||
* @param inputFrame: the input image to be processed
|
||||
* @param outputFrame: the output buffer in which the result is writed
|
||||
* @param filterIndex: the offset which specifies the parameter set that should be used for the filtering
|
||||
*/
|
||||
void runFilter_LPfilter(const std::valarray<float> &inputFrame, std::valarray<float> &outputFrame, const unsigned int filterIndex=0); // run LP filter on a specific output adress
|
||||
|
||||
/**
|
||||
* low pass filter call and run (models the homogeneous cells network at the retina level, for example horizontal cells or photoreceptors)
|
||||
* @param inputOutputFrame: the input image to be processed on which the result is rewrited
|
||||
* @param filterIndex: the offset which specifies the parameter set that should be used for the filtering
|
||||
*/
|
||||
void runFilter_LPfilter_Autonomous(std::valarray<float> &inputOutputFrame, const unsigned int filterIndex=0);// run LP filter on the input data and rewrite it
|
||||
|
||||
/**
|
||||
* local luminance adaptation call and run (contrast enhancement property of the photoreceptors)
|
||||
* @param inputOutputFrame: the input image to be processed
|
||||
* @param localLuminance: an image which represents the local luminance of the inputFrame parameter, in general, it is its low pass spatial filtering
|
||||
* @return the processed image, the output is reachable later by using function getOutput()
|
||||
*/
|
||||
const std::valarray<float> &runFilter_LocalAdapdation(const std::valarray<float> &inputOutputFrame, const std::valarray<float> &localLuminance);// run local adaptation filter and save result in _filterOutput
|
||||
|
||||
/**
|
||||
* local luminance adaptation call and run (contrast enhancement property of the photoreceptors)
|
||||
* @param inputFrame: the input image to be processed
|
||||
* @param localLuminance: an image which represents the local luminance of the inputFrame parameter, in general, it is its low pass spatial filtering
|
||||
* @param outputFrame: the output buffer in which the result is writed
|
||||
*/
|
||||
void runFilter_LocalAdapdation(const std::valarray<float> &inputFrame, const std::valarray<float> &localLuminance, std::valarray<float> &outputFrame); // run local adaptation filter on a specific output adress
|
||||
|
||||
/**
|
||||
* local luminance adaptation call and run (contrast enhancement property of the photoreceptors)
|
||||
* @param inputFrame: the input image to be processed
|
||||
* @return the processed image, the output is reachable later by using function getOutput()
|
||||
*/
|
||||
const std::valarray<float> &runFilter_LocalAdapdation_autonomous(const std::valarray<float> &inputFrame);// run local adaptation filter and save result in _filterOutput
|
||||
|
||||
/**
|
||||
* local luminance adaptation call and run (contrast enhancement property of the photoreceptors)
|
||||
* @param inputFrame: the input image to be processed
|
||||
* @param outputFrame: the output buffer in which the result is writen
|
||||
*/
|
||||
void runFilter_LocalAdapdation_autonomous(const std::valarray<float> &inputFrame, std::valarray<float> &outputFrame); // run local adaptation filter on a specific output adress
|
||||
|
||||
/**
|
||||
* run low pass filtering with progressive parameters (models the retina log sampling of the photoreceptors and its low pass filtering effect consequence: more powerfull low pass filtering effect on the corners)
|
||||
* @param inputFrame: the input image to be processed
|
||||
* @param filterIndex: the index which specifies the parameter set that should be used for the filtering
|
||||
* @return the processed image, the output is reachable later by using function getOutput() if outputFrame is NULL
|
||||
*/
|
||||
inline void runProgressiveFilter(std::valarray<float> &inputFrame, const unsigned int filterIndex=0) { _spatiotemporalLPfilter_Irregular(&inputFrame[0], filterIndex); }
|
||||
|
||||
/**
|
||||
* run low pass filtering with progressive parameters (models the retina log sampling of the photoreceptors and its low pass filtering effect consequence: more powerfull low pass filtering effect on the corners)
|
||||
* @param inputFrame: the input image to be processed
|
||||
* @param outputFrame: the output buffer in which the result is writen
|
||||
* @param filterIndex: the index which specifies the parameter set that should be used for the filtering
|
||||
*/
|
||||
inline void runProgressiveFilter(const std::valarray<float> &inputFrame,
|
||||
std::valarray<float> &outputFrame,
|
||||
const unsigned int filterIndex=0)
|
||||
{_spatiotemporalLPfilter_Irregular(get_data(inputFrame), &outputFrame[0], filterIndex); }
|
||||
|
||||
/**
|
||||
* first order spatio-temporal low pass filter setup function
|
||||
* @param beta: gain of the filter (generally set to zero)
|
||||
* @param tau: time constant of the filter (unit is frame for video processing)
|
||||
* @param k: spatial constant of the filter (unit is pixels)
|
||||
* @param filterIndex: the index which specifies the parameter set that should be used for the filtering
|
||||
*/
|
||||
void setLPfilterParameters(const float beta, const float tau, const float k, const unsigned int filterIndex=0); // change the parameters of the filter
|
||||
|
||||
/**
|
||||
* first order spatio-temporal low pass filter setup function
|
||||
* @param beta: gain of the filter (generally set to zero)
|
||||
* @param tau: time constant of the filter (unit is frame for video processing)
|
||||
* @param alpha0: spatial constant of the filter (unit is pixels) on the border of the image
|
||||
* @param filterIndex: the index which specifies the parameter set that should be used for the filtering
|
||||
*/
|
||||
void setProgressiveFilterConstants_CentredAccuracy(const float beta, const float tau, const float alpha0, const unsigned int filterIndex=0);
|
||||
|
||||
/**
|
||||
* first order spatio-temporal low pass filter setup function
|
||||
* @param beta: gain of the filter (generally set to zero)
|
||||
* @param tau: time constant of the filter (unit is frame for video processing)
|
||||
* @param alpha0: spatial constant of the filter (unit is pixels) on the border of the image
|
||||
* @param accuracyMap an image (float format) which values range is between 0 and 1, where 0 means, apply no filtering and 1 means apply the filtering as specified in the parameters set, intermediate values allow to smooth variations of the filtering strength
|
||||
* @param filterIndex: the index which specifies the parameter set that should be used for the filtering
|
||||
*/
|
||||
void setProgressiveFilterConstants_CustomAccuracy(const float beta, const float tau, const float alpha0, const std::valarray<float> &accuracyMap, const unsigned int filterIndex=0);
|
||||
|
||||
/**
|
||||
* local luminance adaptation setup, this function should be applied for normal local adaptation (not for tone mapping operation)
|
||||
* @param v0: compression effect for the local luminance adaptation processing, set a value between 0.6 and 0.9 for best results, a high value yields to a high compression effect
|
||||
* @param maxInputValue: the maximum amplitude value measured after local adaptation processing (c.f. function runFilter_LocalAdapdation & runFilter_LocalAdapdation_autonomous)
|
||||
* @param meanLuminance: the a priori meann luminance of the input data (should be 128 for 8bits images but can vary greatly in case of High Dynamic Range Images (HDRI)
|
||||
*/
|
||||
void setV0CompressionParameter(const float v0, const float maxInputValue, const float)
|
||||
{
|
||||
_v0=v0*maxInputValue;
|
||||
_localLuminanceFactor=v0;
|
||||
_localLuminanceAddon=maxInputValue*(1.0f-v0);
|
||||
_maxInputValue=maxInputValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* update local luminance adaptation setup, initial maxInputValue is kept. This function should be applied for normal local adaptation (not for tone mapping operation)
|
||||
* @param v0: compression effect for the local luminance adaptation processing, set a value between 0.6 and 0.9 for best results, a high value yields to a high compression effect
|
||||
* @param meanLuminance: the a priori meann luminance of the input data (should be 128 for 8bits images but can vary greatly in case of High Dynamic Range Images (HDRI)
|
||||
*/
|
||||
void setV0CompressionParameter(const float v0, const float meanLuminance) { this->setV0CompressionParameter(v0, _maxInputValue, meanLuminance); }
|
||||
|
||||
/**
|
||||
* local luminance adaptation setup, this function should be applied for normal local adaptation (not for tone mapping operation)
|
||||
* @param v0: compression effect for the local luminance adaptation processing, set a value between 0.6 and 0.9 for best results, a high value yields to a high compression effect
|
||||
*/
|
||||
void setV0CompressionParameter(const float v0)
|
||||
{
|
||||
_v0=v0*_maxInputValue;
|
||||
_localLuminanceFactor=v0;
|
||||
_localLuminanceAddon=_maxInputValue*(1.0f-v0);
|
||||
}
|
||||
|
||||
/**
|
||||
* local luminance adaptation setup, this function should be applied for local adaptation applied to tone mapping operation
|
||||
* @param v0: compression effect for the local luminance adaptation processing, set a value between 0.6 and 0.9 for best results, a high value yields to a high compression effect
|
||||
* @param maxInputValue: the maximum amplitude value measured after local adaptation processing (c.f. function runFilter_LocalAdapdation & runFilter_LocalAdapdation_autonomous)
|
||||
* @param meanLuminance: the a priori meann luminance of the input data (should be 128 for 8bits images but can vary greatly in case of High Dynamic Range Images (HDRI)
|
||||
*/
|
||||
void setV0CompressionParameterToneMapping(const float v0, const float maxInputValue, const float meanLuminance=128.0f)
|
||||
{
|
||||
_v0=v0*maxInputValue;
|
||||
_localLuminanceFactor=1.0f;
|
||||
_localLuminanceAddon=meanLuminance*v0;
|
||||
_maxInputValue=maxInputValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* update compression parameters while keeping v0 parameter value
|
||||
* @param meanLuminance the input frame mean luminance
|
||||
*/
|
||||
inline void updateCompressionParameter(const float meanLuminance)
|
||||
{
|
||||
_localLuminanceFactor=1;
|
||||
_localLuminanceAddon=meanLuminance*_v0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the v0 compression parameter used to compute the local adaptation
|
||||
*/
|
||||
float getV0CompressionParameter() { return _v0/_maxInputValue; }
|
||||
|
||||
/**
|
||||
* @return the output result of the object
|
||||
*/
|
||||
inline const std::valarray<float> &getOutput() const { return _filterOutput; }
|
||||
|
||||
/**
|
||||
* @return number of rows of the filter
|
||||
*/
|
||||
inline unsigned int getNBrows() { return _filterOutput.getNBrows(); }
|
||||
|
||||
/**
|
||||
* @return number of columns of the filter
|
||||
*/
|
||||
inline unsigned int getNBcolumns() { return _filterOutput.getNBcolumns(); }
|
||||
|
||||
/**
|
||||
* @return number of pixels of the filter
|
||||
*/
|
||||
inline unsigned int getNBpixels() { return _filterOutput.getNBpixels(); }
|
||||
|
||||
/**
|
||||
* force filter output to be normalized between 0 and maxValue
|
||||
* @param maxValue: the maximum output value that is required
|
||||
*/
|
||||
inline void normalizeGrayOutput_0_maxOutputValue(const float maxValue) { _filterOutput.normalizeGrayOutput_0_maxOutputValue(maxValue); }
|
||||
|
||||
/**
|
||||
* force filter output to be normalized around 0 and rescaled with a sigmoide effect (extrem values saturation)
|
||||
* @param maxValue: the maximum output value that is required
|
||||
*/
|
||||
inline void normalizeGrayOutputCentredSigmoide() { _filterOutput.normalizeGrayOutputCentredSigmoide(); }
|
||||
|
||||
/**
|
||||
* force filter output to be normalized : data centering and std normalisation
|
||||
* @param maxValue: the maximum output value that is required
|
||||
*/
|
||||
inline void centerReductImageLuminance() { _filterOutput.centerReductImageLuminance(); }
|
||||
|
||||
/**
|
||||
* @return the maximum input buffer value
|
||||
*/
|
||||
inline float getMaxInputValue() { return _maxInputValue; }
|
||||
|
||||
/**
|
||||
* @return the maximum input buffer value
|
||||
*/
|
||||
inline void setMaxInputValue(const float newMaxInputValue) { this->_maxInputValue=newMaxInputValue; }
|
||||
|
||||
protected:
|
||||
|
||||
/////////////////////////
|
||||
// data buffers
|
||||
TemplateBuffer<float> _filterOutput; // primary buffer (contains processing outputs)
|
||||
std::valarray<float> _localBuffer; // local secondary buffer
|
||||
/////////////////////////
|
||||
// PARAMETERS
|
||||
unsigned int _halfNBrows;
|
||||
unsigned int _halfNBcolumns;
|
||||
|
||||
// parameters buffers
|
||||
std::valarray <float>_filteringCoeficientsTable;
|
||||
std::valarray <float>_progressiveSpatialConstant;// pointer to a local table containing local spatial constant (allocated with the object)
|
||||
std::valarray <float>_progressiveGain;// pointer to a local table containing local spatial constant (allocated with the object)
|
||||
|
||||
// local adaptation filtering parameters
|
||||
float _v0; //value used for local luminance adaptation function
|
||||
float _maxInputValue;
|
||||
float _meanInputValue;
|
||||
float _localLuminanceFactor;
|
||||
float _localLuminanceAddon;
|
||||
|
||||
// protected data related to standard low pass filters parameters
|
||||
float _a;
|
||||
float _tau;
|
||||
float _gain;
|
||||
|
||||
/////////////////////////
|
||||
// FILTERS METHODS
|
||||
|
||||
// Basic low pass spation temporal low pass filter used by each retina filters
|
||||
void _spatiotemporalLPfilter(const float *inputFrame, float *LPfilterOutput, const unsigned int coefTableOffset=0);
|
||||
float _squaringSpatiotemporalLPfilter(const float *inputFrame, float *outputFrame, const unsigned int filterIndex=0);
|
||||
|
||||
// LP filter with an irregular spatial filtering
|
||||
|
||||
// -> rewrites the input buffer
|
||||
void _spatiotemporalLPfilter_Irregular(float *inputOutputFrame, const unsigned int filterIndex=0);
|
||||
// writes the output on another buffer
|
||||
void _spatiotemporalLPfilter_Irregular(const float *inputFrame, float *outputFrame, const unsigned int filterIndex=0);
|
||||
// LP filter that squares the input and computes the output ONLY on the areas where the integrationAreas map are TRUE
|
||||
void _localSquaringSpatioTemporalLPfilter(const float *inputFrame, float *LPfilterOutput, const unsigned int *integrationAreas, const unsigned int filterIndex=0);
|
||||
|
||||
// local luminance adaptation of the input in regard of localLuminance buffer
|
||||
void _localLuminanceAdaptation(const float *inputFrame, const float *localLuminance, float *outputFrame, const bool updateLuminanceMean=true);
|
||||
// local luminance adaptation of the input in regard of localLuminance buffer, the input is rewrited and becomes the output
|
||||
void _localLuminanceAdaptation(float *inputOutputFrame, const float *localLuminance);
|
||||
// local adaptation applied on a range of values which can be positive and negative
|
||||
void _localLuminanceAdaptationPosNegValues(const float *inputFrame, const float *localLuminance, float *outputFrame);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// 1D directional filters used for the 2D low pass filtering
|
||||
|
||||
// 1D filters with image input
|
||||
void _horizontalCausalFilter_addInput(const float *inputFrame, float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd);
|
||||
// 1D filters with image input that is squared in the function // parallelized with TBB
|
||||
void _squaringHorizontalCausalFilter(const float *inputFrame, float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd);
|
||||
// vertical anticausal filter that returns the mean value of its result
|
||||
float _verticalAnticausalFilter_returnMeanValue(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd);
|
||||
|
||||
// most simple functions: only perform 1D filtering with output=input (no add on)
|
||||
void _horizontalCausalFilter(float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd);
|
||||
void _horizontalAnticausalFilter(float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd); // parallelized with TBB
|
||||
void _verticalCausalFilter(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd); // parallelized with TBB
|
||||
void _verticalAnticausalFilter(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd);
|
||||
|
||||
// perform 1D filtering with output with varrying spatial coefficient
|
||||
void _horizontalCausalFilter_Irregular(float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd);
|
||||
void _horizontalCausalFilter_Irregular_addInput(const float *inputFrame, float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd);
|
||||
void _horizontalAnticausalFilter_Irregular(float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd, const float *spatialConstantBuffer); // parallelized with TBB
|
||||
void _verticalCausalFilter_Irregular(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd, const float *spatialConstantBuffer); // parallelized with TBB
|
||||
void _verticalAnticausalFilter_Irregular_multGain(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd);
|
||||
|
||||
|
||||
// 1D filters in which the output is multiplied by _gain
|
||||
void _verticalAnticausalFilter_multGain(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd); // this functions affects _gain at the output // parallelized with TBB
|
||||
void _horizontalAnticausalFilter_multGain(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd); // this functions affects _gain at the output
|
||||
|
||||
// LP filter on specific parts of the picture instead of all the image
|
||||
// same functions (some of them) but take a binary flag to allow integration, false flag means, 0 at the output...
|
||||
void _local_squaringHorizontalCausalFilter(const float *inputFrame, float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd, const unsigned int *integrationAreas);
|
||||
void _local_horizontalAnticausalFilter(float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd, const unsigned int *integrationAreas);
|
||||
void _local_verticalCausalFilter(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd, const unsigned int *integrationAreas);
|
||||
void _local_verticalAnticausalFilter_multGain(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd, const unsigned int *integrationAreas); // this functions affects _gain at the output
|
||||
|
||||
#ifdef MAKE_PARALLEL
|
||||
/******************************************************
|
||||
** IF some parallelizing thread methods are available, then, main loops are parallelized using these functors
|
||||
** ==> main idea paralellise main filters loops, then, only the most used methods are parallelized... TODO : increase the number of parallelised methods as necessary
|
||||
** ==> functors names = Parallel_$$$ where $$$= the name of the serial method that is parallelised
|
||||
** ==> functors constructors can differ from the parameters used with their related serial functions
|
||||
*/
|
||||
|
||||
#define _DEBUG_TBB // define DEBUG_TBB in order to display additionnal data on stdout
|
||||
class Parallel_horizontalAnticausalFilter: public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
float *outputFrame;
|
||||
unsigned int IDrowEnd, nbColumns;
|
||||
float filterParam_a;
|
||||
public:
|
||||
// constructor which takes the input image pointer reference reference and limits
|
||||
Parallel_horizontalAnticausalFilter(float *bufferToProcess, const unsigned int idEnd, const unsigned int nbCols, const float a )
|
||||
:outputFrame(bufferToProcess), IDrowEnd(idEnd), nbColumns(nbCols), filterParam_a(a)
|
||||
{
|
||||
#ifdef DEBUG_TBB
|
||||
std::cout<<"Parallel_horizontalAnticausalFilter::Parallel_horizontalAnticausalFilter :"
|
||||
<<"\n\t idEnd="<<IDrowEnd
|
||||
<<"\n\t nbCols="<<nbColumns
|
||||
<<"\n\t filterParam="<<filterParam_a
|
||||
<<std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void operator()( const Range& r ) const CV_OVERRIDE {
|
||||
|
||||
#ifdef DEBUG_TBB
|
||||
std::cout<<"Parallel_horizontalAnticausalFilter::operator() :"
|
||||
<<"\n\t range size="<<r.size()
|
||||
<<"\n\t first index="<<r.start
|
||||
//<<"\n\t last index="<<filterParam
|
||||
<<std::endl;
|
||||
#endif
|
||||
for (int IDrow=r.start; IDrow!=r.end; ++IDrow)
|
||||
{
|
||||
float* outputPTR=outputFrame+(IDrowEnd-IDrow)*(nbColumns)-1;
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<nbColumns; ++index)
|
||||
{
|
||||
result = *(outputPTR)+ filterParam_a* result;
|
||||
*(outputPTR--) = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Parallel_horizontalCausalFilter_addInput: public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
const float *inputFrame;
|
||||
float *outputFrame;
|
||||
unsigned int IDrowStart, nbColumns;
|
||||
float filterParam_a, filterParam_tau;
|
||||
public:
|
||||
Parallel_horizontalCausalFilter_addInput(const float *bufferToAddAsInputProcess, float *bufferToProcess, const unsigned int idStart, const unsigned int nbCols, const float a, const float tau)
|
||||
:inputFrame(bufferToAddAsInputProcess), outputFrame(bufferToProcess), IDrowStart(idStart), nbColumns(nbCols), filterParam_a(a), filterParam_tau(tau){}
|
||||
|
||||
virtual void operator()( const Range& r ) const CV_OVERRIDE {
|
||||
for (int IDrow=r.start; IDrow!=r.end; ++IDrow)
|
||||
{
|
||||
float* outputPTR=outputFrame+(IDrowStart+IDrow)*nbColumns;
|
||||
const float* inputPTR=inputFrame+(IDrowStart+IDrow)*nbColumns;
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<nbColumns; ++index)
|
||||
{
|
||||
result = *(inputPTR++) + filterParam_tau**(outputPTR)+ filterParam_a* result;
|
||||
*(outputPTR++) = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Parallel_verticalCausalFilter: public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
float *outputFrame;
|
||||
unsigned int nbRows, nbColumns;
|
||||
float filterParam_a;
|
||||
public:
|
||||
Parallel_verticalCausalFilter(float *bufferToProcess, const unsigned int nbRws, const unsigned int nbCols, const float a )
|
||||
:outputFrame(bufferToProcess), nbRows(nbRws), nbColumns(nbCols), filterParam_a(a){}
|
||||
|
||||
virtual void operator()( const Range& r ) const CV_OVERRIDE {
|
||||
for (int IDcolumn=r.start; IDcolumn!=r.end; ++IDcolumn)
|
||||
{
|
||||
float result=0;
|
||||
float *outputPTR=outputFrame+IDcolumn;
|
||||
|
||||
for (unsigned int index=0; index<nbRows; ++index)
|
||||
{
|
||||
result = *(outputPTR) + filterParam_a * result;
|
||||
*(outputPTR) = result;
|
||||
outputPTR+=nbColumns;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Parallel_verticalAnticausalFilter_multGain: public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
float *outputFrame;
|
||||
unsigned int nbRows, nbColumns;
|
||||
float filterParam_a, filterParam_gain;
|
||||
public:
|
||||
Parallel_verticalAnticausalFilter_multGain(float *bufferToProcess, const unsigned int nbRws, const unsigned int nbCols, const float a, const float gain)
|
||||
:outputFrame(bufferToProcess), nbRows(nbRws), nbColumns(nbCols), filterParam_a(a), filterParam_gain(gain){}
|
||||
|
||||
virtual void operator()( const Range& r ) const CV_OVERRIDE {
|
||||
float* offset=outputFrame+nbColumns*nbRows-nbColumns;
|
||||
for (int IDcolumn=r.start; IDcolumn!=r.end; ++IDcolumn)
|
||||
{
|
||||
float result=0;
|
||||
float *outputPTR=offset+IDcolumn;
|
||||
|
||||
for (unsigned int index=0; index<nbRows; ++index)
|
||||
{
|
||||
result = *(outputPTR) + filterParam_a * result;
|
||||
*(outputPTR) = filterParam_gain*result;
|
||||
outputPTR-=nbColumns;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Parallel_localAdaptation: public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
const float *localLuminance, *inputFrame;
|
||||
float *outputFrame;
|
||||
float localLuminanceFactor, localLuminanceAddon, maxInputValue;
|
||||
public:
|
||||
Parallel_localAdaptation(const float *localLum, const float *inputImg, float *bufferToProcess, const float localLuminanceFact, const float localLuminanceAdd, const float maxInputVal)
|
||||
:localLuminance(localLum), inputFrame(inputImg),outputFrame(bufferToProcess), localLuminanceFactor(localLuminanceFact), localLuminanceAddon(localLuminanceAdd), maxInputValue(maxInputVal) {}
|
||||
|
||||
virtual void operator()( const Range& r ) const CV_OVERRIDE {
|
||||
const float *localLuminancePTR=localLuminance+r.start;
|
||||
const float *inputFramePTR=inputFrame+r.start;
|
||||
float *outputFramePTR=outputFrame+r.start;
|
||||
for (int IDpixel=r.start ; IDpixel!=r.end ; ++IDpixel, ++inputFramePTR, ++outputFramePTR)
|
||||
{
|
||||
float X0=*(localLuminancePTR++)*localLuminanceFactor+localLuminanceAddon;
|
||||
// TODO : the following line can lead to a divide by zero ! A small offset is added, take care if the offset is too large in case of High Dynamic Range images which can use very small values...
|
||||
*(outputFramePTR) = (maxInputValue+X0)**inputFramePTR/(*inputFramePTR +X0+0.00000000001f);
|
||||
//std::cout<<"BasicRetinaFilter::inputFrame[IDpixel]=%f, X0=%f, outputFrame[IDpixel]=%f\n", inputFrame[IDpixel], X0, outputFrame[IDpixel]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////
|
||||
/// Specific filtering methods which manage non const spatial filtering parameter (used By retinacolor and LogProjectors)
|
||||
class Parallel_horizontalAnticausalFilter_Irregular: public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
float *outputFrame;
|
||||
const float *spatialConstantBuffer;
|
||||
unsigned int IDrowEnd, nbColumns;
|
||||
public:
|
||||
Parallel_horizontalAnticausalFilter_Irregular(float *bufferToProcess, const float *spatialConst, const unsigned int idEnd, const unsigned int nbCols)
|
||||
:outputFrame(bufferToProcess), spatialConstantBuffer(spatialConst), IDrowEnd(idEnd), nbColumns(nbCols){}
|
||||
|
||||
virtual void operator()( const Range& r ) const CV_OVERRIDE {
|
||||
|
||||
for (int IDrow=r.start; IDrow!=r.end; ++IDrow)
|
||||
{
|
||||
float* outputPTR=outputFrame+(IDrowEnd-IDrow)*(nbColumns)-1;
|
||||
const float* spatialConstantPTR=spatialConstantBuffer+(IDrowEnd-IDrow)*(nbColumns)-1;
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<nbColumns; ++index)
|
||||
{
|
||||
result = *(outputPTR)+ *(spatialConstantPTR--)* result;
|
||||
*(outputPTR--) = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Parallel_verticalCausalFilter_Irregular: public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
float *outputFrame;
|
||||
const float *spatialConstantBuffer;
|
||||
unsigned int nbRows, nbColumns;
|
||||
public:
|
||||
Parallel_verticalCausalFilter_Irregular(float *bufferToProcess, const float *spatialConst, const unsigned int nbRws, const unsigned int nbCols)
|
||||
:outputFrame(bufferToProcess), spatialConstantBuffer(spatialConst), nbRows(nbRws), nbColumns(nbCols){}
|
||||
|
||||
virtual void operator()( const Range& r ) const CV_OVERRIDE {
|
||||
for (int IDcolumn=r.start; IDcolumn!=r.end; ++IDcolumn)
|
||||
{
|
||||
float result=0;
|
||||
float *outputPTR=outputFrame+IDcolumn;
|
||||
const float* spatialConstantPTR=spatialConstantBuffer+IDcolumn;
|
||||
for (unsigned int index=0; index<nbRows; ++index)
|
||||
{
|
||||
result = *(outputPTR) + *(spatialConstantPTR) * result;
|
||||
*(outputPTR) = result;
|
||||
outputPTR+=nbColumns;
|
||||
spatialConstantPTR+=nbColumns;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
#endif
|
||||
@@ -0,0 +1,451 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2011
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "imagelogpolprojection.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
// @author Alexandre BENOIT, benoit.alexandre.vision@gmail.com, LISTIC : www.listic.univ-savoie.fr, Gipsa-Lab, France: www.gipsa-lab.inpg.fr/
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
// constructor
|
||||
ImageLogPolProjection::ImageLogPolProjection(const unsigned int nbRows, const unsigned int nbColumns, const PROJECTIONTYPE projection, const bool colorModeCapable)
|
||||
:BasicRetinaFilter(nbRows, nbColumns),
|
||||
_sampledFrame(0),
|
||||
_tempBuffer(_localBuffer),
|
||||
_transformTable(0),
|
||||
_irregularLPfilteredFrame(_filterOutput)
|
||||
{
|
||||
_inputDoubleNBpixels=nbRows*nbColumns*2;
|
||||
_selectedProjection = projection;
|
||||
_reductionFactor=0;
|
||||
_initOK=false;
|
||||
_usefullpixelIndex=0;
|
||||
_colorModeCapable=colorModeCapable;
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"ImageLogPolProjection::allocating"<<std::endl;
|
||||
#endif
|
||||
if (_colorModeCapable)
|
||||
{
|
||||
_tempBuffer.resize(nbRows*nbColumns*3);
|
||||
}
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"ImageLogPolProjection::done"<<std::endl;
|
||||
#endif
|
||||
|
||||
clearAllBuffers();
|
||||
}
|
||||
|
||||
// destructor
|
||||
ImageLogPolProjection::~ImageLogPolProjection()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// reset buffers method
|
||||
void ImageLogPolProjection::clearAllBuffers()
|
||||
{
|
||||
_sampledFrame=0;
|
||||
_tempBuffer=0;
|
||||
BasicRetinaFilter::clearAllBuffers();
|
||||
}
|
||||
|
||||
/**
|
||||
* resize retina color filter object (resize all allocated buffers)
|
||||
* @param NBrows: the new height size
|
||||
* @param NBcolumns: the new width size
|
||||
*/
|
||||
void ImageLogPolProjection::resize(const unsigned int NBrows, const unsigned int NBcolumns)
|
||||
{
|
||||
BasicRetinaFilter::resize(NBrows, NBcolumns);
|
||||
initProjection(_reductionFactor, _samplingStrength);
|
||||
|
||||
// reset buffers method
|
||||
clearAllBuffers();
|
||||
|
||||
}
|
||||
|
||||
// init functions depending on the projection type
|
||||
bool ImageLogPolProjection::initProjection(const double reductionFactor, const double samplingStrength)
|
||||
{
|
||||
switch(_selectedProjection)
|
||||
{
|
||||
case RETINALOGPROJECTION:
|
||||
return _initLogRetinaSampling(reductionFactor, samplingStrength);
|
||||
break;
|
||||
case CORTEXLOGPOLARPROJECTION:
|
||||
return _initLogPolarCortexSampling(reductionFactor, samplingStrength);
|
||||
break;
|
||||
default:
|
||||
std::cout<<"ImageLogPolProjection::no projection setted up... performing default retina projection... take care"<<std::endl;
|
||||
return _initLogRetinaSampling(reductionFactor, samplingStrength);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// -> private init functions dedicated to each projection
|
||||
bool ImageLogPolProjection::_initLogRetinaSampling(const double reductionFactor, const double samplingStrength)
|
||||
{
|
||||
_initOK=false;
|
||||
|
||||
if (_selectedProjection!=RETINALOGPROJECTION)
|
||||
{
|
||||
std::cerr<<"ImageLogPolProjection::initLogRetinaSampling: could not initialize logPolar projection for a log projection system\n -> you probably chose the wrong init function, use initLogPolarCortexSampling() instead"<<std::endl;
|
||||
return false;
|
||||
}
|
||||
if (reductionFactor<1.0)
|
||||
{
|
||||
std::cerr<<"ImageLogPolProjection::initLogRetinaSampling: reduction factor must be superior to 0, skeeping initialisation..."<<std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// compute image output size
|
||||
_outputNBrows=predictOutputSize(this->getNBrows(), reductionFactor);
|
||||
_outputNBcolumns=predictOutputSize(this->getNBcolumns(), reductionFactor);
|
||||
_outputNBpixels=_outputNBrows*_outputNBcolumns;
|
||||
_outputDoubleNBpixels=_outputNBrows*_outputNBcolumns*2;
|
||||
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"ImageLogPolProjection::initLogRetinaSampling: Log resampled image resampling factor: "<<reductionFactor<<", strength:"<<samplingStrength<<std::endl;
|
||||
std::cout<<"ImageLogPolProjection::initLogRetinaSampling: Log resampled image size: "<<_outputNBrows<<"*"<<_outputNBcolumns<<std::endl;
|
||||
#endif
|
||||
|
||||
// setup progressive prefilter that will be applied BEFORE log sampling
|
||||
setProgressiveFilterConstants_CentredAccuracy(0.f, 0.f, 0.99f);
|
||||
|
||||
// (re)create the image output buffer and transform table if the reduction factor changed
|
||||
_sampledFrame.resize(_outputNBpixels*(1+(unsigned int)_colorModeCapable*2));
|
||||
|
||||
// specifiying new reduction factor after preliminar checks
|
||||
_reductionFactor=reductionFactor;
|
||||
_samplingStrength=samplingStrength;
|
||||
|
||||
// compute the rlim for symetric rows/columns sampling, then, the rlim is based on the smallest dimension
|
||||
_minDimension=(double)(_filterOutput.getNBrows() < _filterOutput.getNBcolumns() ? _filterOutput.getNBrows() : _filterOutput.getNBcolumns());
|
||||
|
||||
// input frame dimensions dependent log sampling:
|
||||
//double rlim=1.0/reductionFactor*(minDimension/2.0+samplingStrength);
|
||||
|
||||
// input frame dimensions INdependent log sampling:
|
||||
_azero=(1.0+reductionFactor*std::sqrt(samplingStrength))/(reductionFactor*reductionFactor*samplingStrength-1.0);
|
||||
_alim=(1.0+_azero)/reductionFactor;
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"ImageLogPolProjection::initLogRetinaSampling: rlim= "<<rlim<<std::endl;
|
||||
std::cout<<"ImageLogPolProjection::initLogRetinaSampling: alim= "<<alim<<std::endl;
|
||||
#endif
|
||||
|
||||
// get half frame size
|
||||
unsigned int halfOutputRows = _outputNBrows/2-1;
|
||||
unsigned int halfOutputColumns = _outputNBcolumns/2-1;
|
||||
unsigned int halfInputRows = _filterOutput.getNBrows()/2-1;
|
||||
unsigned int halfInputColumns = _filterOutput.getNBcolumns()/2-1;
|
||||
|
||||
// computing log sampling matrix by computing quarters of images
|
||||
// the original new image center (_filterOutput.getNBrows()/2, _filterOutput.getNBcolumns()/2) being at coordinate (_filterOutput.getNBrows()/(2*_reductionFactor), _filterOutput.getNBcolumns()/(2*_reductionFactor))
|
||||
|
||||
// -> use a temporary transform table which is bigger than the final one, we only report pixels coordinates that are included in the sampled picture
|
||||
std::valarray<unsigned int> tempTransformTable(2*_outputNBpixels); // the structure would be: (pixelInputCoordinate n)(pixelOutputCoordinate n)(pixelInputCoordinate n+1)(pixelOutputCoordinate n+1)
|
||||
_usefullpixelIndex=0;
|
||||
|
||||
double rMax=0;
|
||||
halfInputRows<halfInputColumns ? rMax=(double)(halfInputRows*halfInputRows):rMax=(double)(halfInputColumns*halfInputColumns);
|
||||
|
||||
for (unsigned int idRow=0;idRow<halfOutputRows; ++idRow)
|
||||
{
|
||||
for (unsigned int idColumn=0;idColumn<halfOutputColumns; ++idColumn)
|
||||
{
|
||||
// get the pixel position in the original picture
|
||||
|
||||
// -> input frame dimensions dependent log sampling:
|
||||
//double scale = samplingStrength/(rlim-(double)std::sqrt(idRow*idRow+idColumn*idColumn));
|
||||
|
||||
// -> input frame dimensions INdependent log sampling:
|
||||
double scale=getOriginalRadiusLength((double)std::sqrt((double)(idRow*idRow+idColumn*idColumn)));
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"ImageLogPolProjection::initLogRetinaSampling: scale= "<<scale<<std::endl;
|
||||
std::cout<<"ImageLogPolProjection::initLogRetinaSampling: scale2= "<<scale2<<std::endl;
|
||||
#endif
|
||||
if (scale < 0) ///check it later
|
||||
scale = 10000;
|
||||
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
// std::cout<<"ImageLogPolProjection::initLogRetinaSampling: scale= "<<scale<<std::endl;
|
||||
#endif
|
||||
|
||||
unsigned int u=(unsigned int)floor((double)idRow*scale);
|
||||
unsigned int v=(unsigned int)floor((double)idColumn*scale);
|
||||
|
||||
// manage border effects
|
||||
double length=u*u+v*v;
|
||||
double radiusRatio=std::sqrt(rMax/length);
|
||||
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"ImageLogPolProjection::(inputH, inputW)="<<halfInputRows<<", "<<halfInputColumns<<", Rmax2="<<rMax<<std::endl;
|
||||
std::cout<<"before ==> ImageLogPolProjection::(u, v)="<<u<<", "<<v<<", r="<<u*u+v*v<<std::endl;
|
||||
std::cout<<"ratio ="<<radiusRatio<<std::endl;
|
||||
#endif
|
||||
|
||||
if (radiusRatio < 1.0)
|
||||
{
|
||||
u=(unsigned int)floor(radiusRatio*double(u));
|
||||
v=(unsigned int)floor(radiusRatio*double(v));
|
||||
}
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"after ==> ImageLogPolProjection::(u, v)="<<u<<", "<<v<<", r="<<u*u+v*v<<std::endl;
|
||||
std::cout<<"ImageLogPolProjection::("<<(halfOutputRows-idRow)<<", "<<idColumn+halfOutputColumns<<") <- ("<<halfInputRows-u<<", "<<v+halfInputColumns<<")"<<std::endl;
|
||||
std::cout<<(halfOutputRows-idRow)+(halfOutputColumns+idColumn)*_outputNBrows<<" -> "<<(halfInputRows-u)+_filterOutput.getNBrows()*(halfInputColumns+v)<<std::endl;
|
||||
#endif
|
||||
|
||||
if ((u<halfInputRows)&&(v<halfInputColumns))
|
||||
{
|
||||
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"*** VALID ***"<<std::endl;
|
||||
#endif
|
||||
|
||||
// set pixel coordinate of the input picture in the transform table at the current log sampled pixel
|
||||
// 1st quadrant
|
||||
tempTransformTable[_usefullpixelIndex++]=(halfOutputColumns+idColumn)+(halfOutputRows-idRow)*_outputNBcolumns;
|
||||
tempTransformTable[_usefullpixelIndex++]=_filterOutput.getNBcolumns()*(halfInputRows-u)+(halfInputColumns+v);
|
||||
// 2nd quadrant
|
||||
tempTransformTable[_usefullpixelIndex++]=(halfOutputColumns+idColumn)+(halfOutputRows+idRow)*_outputNBcolumns;
|
||||
tempTransformTable[_usefullpixelIndex++]=_filterOutput.getNBcolumns()*(halfInputRows+u)+(halfInputColumns+v);
|
||||
// 3rd quadrant
|
||||
tempTransformTable[_usefullpixelIndex++]=(halfOutputColumns-idColumn)+(halfOutputRows-idRow)*_outputNBcolumns;
|
||||
tempTransformTable[_usefullpixelIndex++]=_filterOutput.getNBcolumns()*(halfInputRows-u)+(halfInputColumns-v);
|
||||
// 4td quadrant
|
||||
tempTransformTable[_usefullpixelIndex++]=(halfOutputColumns-idColumn)+(halfOutputRows+idRow)*_outputNBcolumns;
|
||||
tempTransformTable[_usefullpixelIndex++]=_filterOutput.getNBcolumns()*(halfInputRows+u)+(halfInputColumns-v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (re)creating and filling the transform table
|
||||
_transformTable.resize(_usefullpixelIndex);
|
||||
memcpy(&_transformTable[0], &tempTransformTable[0], sizeof(unsigned int)*_usefullpixelIndex);
|
||||
|
||||
// reset all buffers
|
||||
clearAllBuffers();
|
||||
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"ImageLogPolProjection::initLogRetinaSampling: init done successfully"<<std::endl;
|
||||
#endif
|
||||
_initOK=true;
|
||||
return _initOK;
|
||||
}
|
||||
|
||||
bool ImageLogPolProjection::_initLogPolarCortexSampling(const double reductionFactor, const double)
|
||||
{
|
||||
_initOK=false;
|
||||
|
||||
if (_selectedProjection!=CORTEXLOGPOLARPROJECTION)
|
||||
{
|
||||
std::cerr<<"ImageLogPolProjection::could not initialize log projection for a logPolar projection system\n -> you probably chose the wrong init function, use initLogRetinaSampling() instead"<<std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reductionFactor<1.0)
|
||||
{
|
||||
std::cerr<<"ImageLogPolProjection::reduction factor must be superior to 0, skeeping initialisation..."<<std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// compute the smallest image size
|
||||
unsigned int minDimension=(_filterOutput.getNBrows() < _filterOutput.getNBcolumns() ? _filterOutput.getNBrows() : _filterOutput.getNBcolumns());
|
||||
// specifiying new reduction factor after preliminar checks
|
||||
_reductionFactor=reductionFactor;
|
||||
// compute image output size
|
||||
_outputNBrows=(unsigned int)((double)minDimension/reductionFactor);
|
||||
_outputNBcolumns=(unsigned int)((double)minDimension/reductionFactor);
|
||||
_outputNBpixels=_outputNBrows*_outputNBcolumns;
|
||||
_outputDoubleNBpixels=_outputNBrows*_outputNBcolumns*2;
|
||||
|
||||
// get half frame size
|
||||
//unsigned int halfOutputRows = _outputNBrows/2-1;
|
||||
//unsigned int halfOutputColumns = _outputNBcolumns/2-1;
|
||||
unsigned int halfInputRows = _filterOutput.getNBrows()/2-1;
|
||||
unsigned int halfInputColumns = _filterOutput.getNBcolumns()/2-1;
|
||||
|
||||
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"ImageLogPolProjection::Log resampled image size: "<<_outputNBrows<<"*"<<_outputNBcolumns<<std::endl;
|
||||
#endif
|
||||
|
||||
// setup progressive prefilter that will be applied BEFORE log sampling
|
||||
setProgressiveFilterConstants_CentredAccuracy(0.f, 0.f, 0.99f);
|
||||
|
||||
// (re)create the image output buffer and transform table if the reduction factor changed
|
||||
_sampledFrame.resize(_outputNBpixels*(1+(unsigned int)_colorModeCapable*2));
|
||||
|
||||
// create the radius and orientation axis and fill them, radius E [0;1], orientation E[-pi, pi]
|
||||
std::valarray<double> radiusAxis(_outputNBcolumns);
|
||||
double radiusStep=2.30/(double)_outputNBcolumns;
|
||||
for (unsigned int i=0;i<_outputNBcolumns;++i)
|
||||
{
|
||||
radiusAxis[i]=i*radiusStep;
|
||||
}
|
||||
std::valarray<double> orientationAxis(_outputNBrows);
|
||||
double orientationStep=-2.0*CV_PI/(double)_outputNBrows;
|
||||
for (unsigned int io=0;io<_outputNBrows;++io)
|
||||
{
|
||||
orientationAxis[io]=io*orientationStep;
|
||||
}
|
||||
// -> use a temporay transform table which is bigger than the final one, we only report pixels coordinates that are included in the sampled picture
|
||||
std::valarray<unsigned int> tempTransformTable(2*_outputNBpixels); // the structure would be: (pixelInputCoordinate n)(pixelOutputCoordinate n)(pixelInputCoordinate n+1)(pixelOutputCoordinate n+1)
|
||||
_usefullpixelIndex=0;
|
||||
|
||||
//std::cout<<"ImageLogPolProjection::Starting cortex projection"<<std::endl;
|
||||
// compute transformation, get theta and Radius in reagrd of the output sampled pixel
|
||||
double diagonalLength=std::sqrt((double)(_outputNBcolumns*_outputNBcolumns+_outputNBrows*_outputNBrows));
|
||||
for (unsigned int radiusIndex=0;radiusIndex<_outputNBcolumns;++radiusIndex)
|
||||
for(unsigned int orientationIndex=0;orientationIndex<_outputNBrows;++orientationIndex)
|
||||
{
|
||||
double x=1.0+sinh(radiusAxis[radiusIndex])*cos(orientationAxis[orientationIndex]);
|
||||
double y=sinh(radiusAxis[radiusIndex])*sin(orientationAxis[orientationIndex]);
|
||||
// get the input picture coordinate
|
||||
double R=diagonalLength*std::sqrt(x*x+y*y)/(5.0+std::sqrt(x*x+y*y));
|
||||
double theta=atan2(y,x);
|
||||
// convert input polar coord into cartesian/C compatble coordinate
|
||||
unsigned int columnIndex=(unsigned int)(cos(theta)*R)+halfInputColumns;
|
||||
unsigned int rowIndex=(unsigned int)(sin(theta)*R)+halfInputRows;
|
||||
//std::cout<<"ImageLogPolProjection::R="<<R<<" / Theta="<<theta<<" / (x, y)="<<columnIndex<<", "<<rowIndex<<std::endl;
|
||||
if ((columnIndex<_filterOutput.getNBcolumns())&&(columnIndex>0)&&(rowIndex<_filterOutput.getNBrows())&&(rowIndex>0))
|
||||
{
|
||||
// set coordinate
|
||||
tempTransformTable[_usefullpixelIndex++]=radiusIndex+orientationIndex*_outputNBcolumns;
|
||||
tempTransformTable[_usefullpixelIndex++]= columnIndex+rowIndex*_filterOutput.getNBcolumns();
|
||||
}
|
||||
}
|
||||
|
||||
// (re)creating and filling the transform table
|
||||
_transformTable.resize(_usefullpixelIndex);
|
||||
memcpy(&_transformTable[0], &tempTransformTable[0], sizeof(unsigned int)*_usefullpixelIndex);
|
||||
|
||||
// reset all buffers
|
||||
clearAllBuffers();
|
||||
_initOK=true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// action function
|
||||
std::valarray<float> &ImageLogPolProjection::runProjection(const std::valarray<float> &inputFrame, const bool colorMode)
|
||||
{
|
||||
if (_colorModeCapable&&colorMode)
|
||||
{
|
||||
// progressive filtering and storage of the result in _tempBuffer
|
||||
_spatiotemporalLPfilter_Irregular(get_data(inputFrame), &_irregularLPfilteredFrame[0]);
|
||||
_spatiotemporalLPfilter_Irregular(&_irregularLPfilteredFrame[0], &_tempBuffer[0]); // warning, temporal issue may occur, if the temporal constant is not NULL !!!
|
||||
|
||||
_spatiotemporalLPfilter_Irregular(get_data(inputFrame)+_filterOutput.getNBpixels(), &_irregularLPfilteredFrame[0]);
|
||||
_spatiotemporalLPfilter_Irregular(&_irregularLPfilteredFrame[0], &_tempBuffer[0]+_filterOutput.getNBpixels());
|
||||
|
||||
_spatiotemporalLPfilter_Irregular(get_data(inputFrame)+_filterOutput.getNBpixels()*2, &_irregularLPfilteredFrame[0]);
|
||||
_spatiotemporalLPfilter_Irregular(&_irregularLPfilteredFrame[0], &_tempBuffer[0]+_filterOutput.getNBpixels()*2);
|
||||
|
||||
// applying image projection/resampling
|
||||
unsigned int *transformTablePTR=&_transformTable[0];
|
||||
for (unsigned int i=0 ; i<_usefullpixelIndex ; i+=2, transformTablePTR+=2)
|
||||
{
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"ImageLogPolProjection::i:"<<i<<"output(max="<<_outputNBpixels<<")="<<_transformTable[i]<<" / intput(max="<<_filterOutput.getNBpixels()<<")="<<_transformTable[i+1]<<std::endl;
|
||||
#endif
|
||||
_sampledFrame[*(transformTablePTR)]=_tempBuffer[*(transformTablePTR+1)];
|
||||
_sampledFrame[*(transformTablePTR)+_outputNBpixels]=_tempBuffer[*(transformTablePTR+1)+_filterOutput.getNBpixels()];
|
||||
_sampledFrame[*(transformTablePTR)+_outputDoubleNBpixels]=_tempBuffer[*(transformTablePTR+1)+_inputDoubleNBpixels];
|
||||
}
|
||||
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"ImageLogPolProjection::runProjection: color image projection OK"<<std::endl;
|
||||
#endif
|
||||
//normalizeGrayOutput_0_maxOutputValue(_sampledFrame, _outputNBpixels);
|
||||
}else
|
||||
{
|
||||
_spatiotemporalLPfilter_Irregular(get_data(inputFrame), &_irregularLPfilteredFrame[0]);
|
||||
_spatiotemporalLPfilter_Irregular(&_irregularLPfilteredFrame[0], &_irregularLPfilteredFrame[0]);
|
||||
// applying image projection/resampling
|
||||
unsigned int *transformTablePTR=&_transformTable[0];
|
||||
for (unsigned int i=0 ; i<_usefullpixelIndex ; i+=2, transformTablePTR+=2)
|
||||
{
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"i:"<<i<<"output(max="<<_outputNBpixels<<")="<<_transformTable[i]<<" / intput(max="<<_filterOutput.getNBpixels()<<")="<<_transformTable[i+1]<<std::endl;
|
||||
#endif
|
||||
_sampledFrame[*(transformTablePTR)]=_irregularLPfilteredFrame[*(transformTablePTR+1)];
|
||||
}
|
||||
//normalizeGrayOutput_0_maxOutputValue(_sampledFrame, _outputNBpixels);
|
||||
#ifdef IMAGELOGPOLPROJECTION_DEBUG
|
||||
std::cout<<"ImageLogPolProjection::runProjection: gray level image projection OK"<<std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
return _sampledFrame;
|
||||
}
|
||||
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
@@ -0,0 +1,244 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2011
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef IMAGELOGPOLPROJECTION_H_
|
||||
#define IMAGELOGPOLPROJECTION_H_
|
||||
|
||||
/**
|
||||
* @class ImageLogPolProjection
|
||||
* @brief class able to perform a log sampling of an image input (models the log sampling of the photoreceptors of the retina)
|
||||
* or a log polar projection which models the retina information projection on the primary visual cortex: a linear projection in the center for detail analysis and a log projection of the borders (low spatial frequency motion information in general)
|
||||
*
|
||||
* collaboration: Barthelemy DURETTE who experimented the retina log projection
|
||||
-> "Traitement visuels Bio mimtiques pour la supplance perceptive", internal technical report, May 2005, Gipsa-lab/DIS, Grenoble, FRANCE
|
||||
*
|
||||
* * TYPICAL USE:
|
||||
*
|
||||
* // create object, here for a log sampling (keyword:RETINALOGPROJECTION): (dynamic object allocation sample)
|
||||
* ImageLogPolProjection *imageSamplingTool;
|
||||
* imageSamplingTool = new ImageLogPolProjection(frameSizeRows, frameSizeColumns, RETINALOGPROJECTION);
|
||||
*
|
||||
* // init log projection:
|
||||
* imageSamplingTool->initProjection(1.0, 15.0);
|
||||
*
|
||||
* // during program execution, call the log transform applied to a frame called "FrameBuffer" :
|
||||
* imageSamplingTool->runProjection(FrameBuffer);
|
||||
* // get output frame and its size:
|
||||
* const unsigned int logSampledFrame_nbRows=imageSamplingTool->getOutputNBrows();
|
||||
* const unsigned int logSampledFrame_nbColumns=imageSamplingTool->getOutputNBcolumns();
|
||||
* const double *logSampledFrame=imageSamplingTool->getSampledFrame();
|
||||
*
|
||||
* // at the end of the program, destroy object:
|
||||
* delete imageSamplingTool;
|
||||
*
|
||||
* @author Alexandre BENOIT, benoit.alexandre.vision@gmail.com, LISTIC : www.listic.univ-savoie.fr, Gipsa-Lab, France: www.gipsa-lab.inpg.fr/
|
||||
* Creation date 2007
|
||||
*/
|
||||
|
||||
//#define __IMAGELOGPOLPROJECTION_DEBUG // used for std output debug information
|
||||
|
||||
#include "basicretinafilter.hpp"
|
||||
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
|
||||
class ImageLogPolProjection:public BasicRetinaFilter
|
||||
{
|
||||
public:
|
||||
|
||||
enum PROJECTIONTYPE{RETINALOGPROJECTION, CORTEXLOGPOLARPROJECTION};
|
||||
|
||||
/**
|
||||
* constructor, just specifies the image input size and the projection type, no projection initialisation is done
|
||||
* -> use initLogRetinaSampling() or initLogPolarCortexSampling() for that
|
||||
* @param nbRows: number of rows of the input image
|
||||
* @param nbColumns: number of columns of the input image
|
||||
* @param projection: the type of projection, RETINALOGPROJECTION or CORTEXLOGPOLARPROJECTION
|
||||
* @param colorMode: specifies if the projection is applied on a grayscale image (false) or color images (3 layers) (true)
|
||||
*/
|
||||
ImageLogPolProjection(const unsigned int nbRows, const unsigned int nbColumns, const PROJECTIONTYPE projection, const bool colorMode=false);
|
||||
|
||||
/**
|
||||
* standard destructor
|
||||
*/
|
||||
virtual ~ImageLogPolProjection();
|
||||
|
||||
/**
|
||||
* function that clears all buffers of the object
|
||||
*/
|
||||
void clearAllBuffers();
|
||||
|
||||
/**
|
||||
* resize retina color filter object (resize all allocated buffers)
|
||||
* @param NBrows: the new height size
|
||||
* @param NBcolumns: the new width size
|
||||
*/
|
||||
void resize(const unsigned int NBrows, const unsigned int NBcolumns);
|
||||
|
||||
/**
|
||||
* init function depending on the projection type
|
||||
* @param reductionFactor: the size reduction factor of the ouptup image in regard of the size of the input image, must be superior to 1
|
||||
* @param samplingStrength: specifies the strength of the log compression effect (magnifying coefficient)
|
||||
* @return true if the init was performed without any errors
|
||||
*/
|
||||
bool initProjection(const double reductionFactor, const double samplingStrength);
|
||||
|
||||
/**
|
||||
* main funtion of the class: run projection function
|
||||
* @param inputFrame: the input frame to be processed
|
||||
* @param colorMode: the input buffer color mode: false=gray levels, true = 3 color channels mode
|
||||
* @return the output frame
|
||||
*/
|
||||
std::valarray<float> &runProjection(const std::valarray<float> &inputFrame, const bool colorMode=false);
|
||||
|
||||
/**
|
||||
* @return the numbers of rows (height) of the images OUTPUTS of the object
|
||||
*/
|
||||
inline unsigned int getOutputNBrows() { return _outputNBrows; }
|
||||
|
||||
/**
|
||||
* @return the numbers of columns (width) of the images OUTPUTS of the object
|
||||
*/
|
||||
inline unsigned int getOutputNBcolumns() { return _outputNBcolumns; }
|
||||
|
||||
/**
|
||||
* main funtion of the class: run projection function
|
||||
* @param size: one of the input frame initial dimensions to be processed
|
||||
* @return the output frame dimension
|
||||
*/
|
||||
inline static unsigned int predictOutputSize(const unsigned int size, const double reductionFactor){return (unsigned int)((double)size/reductionFactor); }
|
||||
|
||||
/**
|
||||
* @return the output of the filter which applies an irregular Low Pass spatial filter to the imag input (see function
|
||||
*/
|
||||
inline const std::valarray<float> &getIrregularLPfilteredInputFrame() const { return _irregularLPfilteredFrame; }
|
||||
|
||||
/**
|
||||
* function which allows to retrieve the output frame which was updated after the "runProjection(...) function BasicRetinaFilter::runProgressiveFilter(...)
|
||||
* @return the projection result
|
||||
*/
|
||||
inline const std::valarray<float> &getSampledFrame() const { return _sampledFrame; }
|
||||
|
||||
/**
|
||||
* function which allows gives the tranformation table, its size is (getNBrows()*getNBcolumns()*2)
|
||||
* @return the transformation matrix [outputPixIndex_i, inputPixIndex_i, outputPixIndex_i+1, inputPixIndex_i+1....]
|
||||
*/
|
||||
inline const std::valarray<unsigned int> &getSamplingMap() const { return _transformTable; }
|
||||
|
||||
inline double getOriginalRadiusLength(const double projectedRadiusLength)
|
||||
{ return _azero/(_alim-projectedRadiusLength*2.0/_minDimension); }
|
||||
|
||||
// unsigned int getInputPixelIndex(const unsigned int ){ return _transformTable[index*2+1]};
|
||||
|
||||
private:
|
||||
PROJECTIONTYPE _selectedProjection;
|
||||
|
||||
// size of the image output
|
||||
unsigned int _outputNBrows;
|
||||
unsigned int _outputNBcolumns;
|
||||
unsigned int _outputNBpixels;
|
||||
unsigned int _outputDoubleNBpixels;
|
||||
unsigned int _inputDoubleNBpixels;
|
||||
|
||||
// is the object able to manage color flag
|
||||
bool _colorModeCapable;
|
||||
// sampling strength factor
|
||||
double _samplingStrength;
|
||||
// sampling reduction factor
|
||||
double _reductionFactor;
|
||||
|
||||
// log sampling parameters
|
||||
double _azero;
|
||||
double _alim;
|
||||
double _minDimension;
|
||||
|
||||
// template buffers
|
||||
std::valarray<float>_sampledFrame;
|
||||
std::valarray<float>&_tempBuffer;
|
||||
std::valarray<unsigned int>_transformTable;
|
||||
|
||||
std::valarray<float> &_irregularLPfilteredFrame; // just a reference for easier understanding
|
||||
unsigned int _usefullpixelIndex;
|
||||
|
||||
// init transformation tables
|
||||
bool _computeLogProjection();
|
||||
bool _computeLogPolarProjection();
|
||||
|
||||
// specifies if init was done correctly
|
||||
bool _initOK;
|
||||
// private init projections functions called by "initProjection(...)" function
|
||||
bool _initLogRetinaSampling(const double reductionFactor, const double samplingStrength);
|
||||
bool _initLogPolarCortexSampling(const double reductionFactor, const double samplingStrength);
|
||||
|
||||
ImageLogPolProjection(const ImageLogPolProjection&);
|
||||
ImageLogPolProjection& operator=(const ImageLogPolProjection&);
|
||||
|
||||
};
|
||||
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
#endif /*IMAGELOGPOLPROJECTION_H_*/
|
||||
@@ -0,0 +1,212 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2011
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "magnoretinafilter.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
// Constructor and Desctructor of the OPL retina filter
|
||||
MagnoRetinaFilter::MagnoRetinaFilter(const unsigned int NBrows, const unsigned int NBcolumns)
|
||||
:BasicRetinaFilter(NBrows, NBcolumns, 2),
|
||||
_previousInput_ON(NBrows*NBcolumns),
|
||||
_previousInput_OFF(NBrows*NBcolumns),
|
||||
_amacrinCellsTempOutput_ON(NBrows*NBcolumns),
|
||||
_amacrinCellsTempOutput_OFF(NBrows*NBcolumns),
|
||||
_magnoXOutputON(NBrows*NBcolumns),
|
||||
_magnoXOutputOFF(NBrows*NBcolumns),
|
||||
_localProcessBufferON(NBrows*NBcolumns),
|
||||
_localProcessBufferOFF(NBrows*NBcolumns)
|
||||
{
|
||||
_magnoYOutput=&_filterOutput;
|
||||
_magnoYsaturated=&_localBuffer;
|
||||
|
||||
|
||||
clearAllBuffers();
|
||||
|
||||
#ifdef IPL_RETINA_ELEMENT_DEBUG
|
||||
std::cout<<"MagnoRetinaFilter::Init IPL retina filter at specified frame size OK"<<std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
MagnoRetinaFilter::~MagnoRetinaFilter()
|
||||
{
|
||||
#ifdef IPL_RETINA_ELEMENT_DEBUG
|
||||
std::cout<<"MagnoRetinaFilter::Delete IPL retina filter OK"<<std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
// function that clears all buffers of the object
|
||||
void MagnoRetinaFilter::clearAllBuffers()
|
||||
{
|
||||
BasicRetinaFilter::clearAllBuffers();
|
||||
_previousInput_ON=0;
|
||||
_previousInput_OFF=0;
|
||||
_amacrinCellsTempOutput_ON=0;
|
||||
_amacrinCellsTempOutput_OFF=0;
|
||||
_magnoXOutputON=0;
|
||||
_magnoXOutputOFF=0;
|
||||
_localProcessBufferON=0;
|
||||
_localProcessBufferOFF=0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* resize retina magno filter object (resize all allocated buffers
|
||||
* @param NBrows: the new height size
|
||||
* @param NBcolumns: the new width size
|
||||
*/
|
||||
void MagnoRetinaFilter::resize(const unsigned int NBrows, const unsigned int NBcolumns)
|
||||
{
|
||||
BasicRetinaFilter::resize(NBrows, NBcolumns);
|
||||
_previousInput_ON.resize(NBrows*NBcolumns);
|
||||
_previousInput_OFF.resize(NBrows*NBcolumns);
|
||||
_amacrinCellsTempOutput_ON.resize(NBrows*NBcolumns);
|
||||
_amacrinCellsTempOutput_OFF.resize(NBrows*NBcolumns);
|
||||
_magnoXOutputON.resize(NBrows*NBcolumns);
|
||||
_magnoXOutputOFF.resize(NBrows*NBcolumns);
|
||||
_localProcessBufferON.resize(NBrows*NBcolumns);
|
||||
_localProcessBufferOFF.resize(NBrows*NBcolumns);
|
||||
|
||||
// to be sure, relink buffers
|
||||
_magnoYOutput=&_filterOutput;
|
||||
_magnoYsaturated=&_localBuffer;
|
||||
|
||||
// reset all buffers
|
||||
clearAllBuffers();
|
||||
}
|
||||
|
||||
void MagnoRetinaFilter::setCoefficientsTable(const float parasolCells_beta, const float parasolCells_tau, const float parasolCells_k, const float amacrinCellsTemporalCutFrequency, const float localAdaptIntegration_tau, const float localAdaptIntegration_k )
|
||||
{
|
||||
_temporalCoefficient=(float)std::exp(-1.0f/amacrinCellsTemporalCutFrequency);
|
||||
// the first set of parameters is dedicated to the low pass filtering property of the ganglion cells
|
||||
BasicRetinaFilter::setLPfilterParameters(parasolCells_beta, parasolCells_tau, parasolCells_k, 0);
|
||||
// the second set of parameters is dedicated to the ganglion cells output intergartion for their local adaptation property
|
||||
BasicRetinaFilter::setLPfilterParameters(0, localAdaptIntegration_tau, localAdaptIntegration_k, 1);
|
||||
}
|
||||
|
||||
void MagnoRetinaFilter::_amacrineCellsComputing(const float *OPL_ON, const float *OPL_OFF)
|
||||
{
|
||||
#ifdef MAKE_PARALLEL
|
||||
cv::parallel_for_(cv::Range(0,_filterOutput.getNBpixels()), Parallel_amacrineCellsComputing(OPL_ON, OPL_OFF, &_previousInput_ON[0], &_previousInput_OFF[0], &_amacrinCellsTempOutput_ON[0], &_amacrinCellsTempOutput_OFF[0], _temporalCoefficient));
|
||||
#else
|
||||
const float *OPL_ON_PTR=OPL_ON;
|
||||
const float *OPL_OFF_PTR=OPL_OFF;
|
||||
float *previousInput_ON_PTR= &_previousInput_ON[0];
|
||||
float *previousInput_OFF_PTR= &_previousInput_OFF[0];
|
||||
float *amacrinCellsTempOutput_ON_PTR= &_amacrinCellsTempOutput_ON[0];
|
||||
float *amacrinCellsTempOutput_OFF_PTR= &_amacrinCellsTempOutput_OFF[0];
|
||||
|
||||
for (unsigned int IDpixel=0 ; IDpixel<this->getNBpixels(); ++IDpixel)
|
||||
{
|
||||
|
||||
/* Compute ON and OFF amacrin cells high pass temporal filter */
|
||||
float magnoXonPixelResult = _temporalCoefficient*(*amacrinCellsTempOutput_ON_PTR+ *OPL_ON_PTR-*previousInput_ON_PTR);
|
||||
*(amacrinCellsTempOutput_ON_PTR++)=((float)(magnoXonPixelResult>0))*magnoXonPixelResult;
|
||||
|
||||
float magnoXoffPixelResult = _temporalCoefficient*(*amacrinCellsTempOutput_OFF_PTR+ *OPL_OFF_PTR-*previousInput_OFF_PTR);
|
||||
*(amacrinCellsTempOutput_OFF_PTR++)=((float)(magnoXoffPixelResult>0))*magnoXoffPixelResult;
|
||||
|
||||
/* prepare next loop */
|
||||
*(previousInput_ON_PTR++)=*(OPL_ON_PTR++);
|
||||
*(previousInput_OFF_PTR++)=*(OPL_OFF_PTR++);
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// launch filter that runs all the IPL filter
|
||||
const std::valarray<float> &MagnoRetinaFilter::runFilter(const std::valarray<float> &OPL_ON, const std::valarray<float> &OPL_OFF)
|
||||
{
|
||||
// Compute the high pass temporal filter
|
||||
_amacrineCellsComputing(get_data(OPL_ON), get_data(OPL_OFF));
|
||||
|
||||
// apply low pass filtering on ON and OFF ways after temporal high pass filtering
|
||||
_spatiotemporalLPfilter(&_amacrinCellsTempOutput_ON[0], &_magnoXOutputON[0], 0);
|
||||
_spatiotemporalLPfilter(&_amacrinCellsTempOutput_OFF[0], &_magnoXOutputOFF[0], 0);
|
||||
|
||||
// local adaptation of the ganglion cells to the local contrast of the moving contours
|
||||
_spatiotemporalLPfilter(&_magnoXOutputON[0], &_localProcessBufferON[0], 1);
|
||||
_localLuminanceAdaptation(&_magnoXOutputON[0], &_localProcessBufferON[0]);
|
||||
_spatiotemporalLPfilter(&_magnoXOutputOFF[0], &_localProcessBufferOFF[0], 1);
|
||||
_localLuminanceAdaptation(&_magnoXOutputOFF[0], &_localProcessBufferOFF[0]);
|
||||
|
||||
/* Compute MagnoY */
|
||||
float *magnoYOutput= &(*_magnoYOutput)[0];
|
||||
float *magnoXOutputON_PTR= &_magnoXOutputON[0];
|
||||
float *magnoXOutputOFF_PTR= &_magnoXOutputOFF[0];
|
||||
for (unsigned int IDpixel=0 ; IDpixel<_filterOutput.getNBpixels() ; ++IDpixel)
|
||||
*(magnoYOutput++)=*(magnoXOutputON_PTR++)+*(magnoXOutputOFF_PTR++);
|
||||
|
||||
return (*_magnoYOutput);
|
||||
}
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
@@ -0,0 +1,246 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2011
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MagnoRetinaFilter_H_
|
||||
#define MagnoRetinaFilter_H_
|
||||
|
||||
/**
|
||||
* @class MagnoRetinaFilter
|
||||
* @brief class which describes the magnocellular channel of the retina:
|
||||
* -> performs a moving contours extraction with powerfull local data enhancement
|
||||
*
|
||||
* TYPICAL USE:
|
||||
*
|
||||
* // create object at a specified picture size
|
||||
* MagnoRetinaFilter *movingContoursExtractor;
|
||||
* movingContoursExtractor =new MagnoRetinaFilter(frameSizeRows, frameSizeColumns);
|
||||
*
|
||||
* // init gain, spatial and temporal parameters:
|
||||
* movingContoursExtractor->setCoefficientsTable(0, 0.7, 5, 3);
|
||||
*
|
||||
* // during program execution, call the filter for contours extraction for an input picture called "FrameBuffer":
|
||||
* movingContoursExtractor->runfilter(FrameBuffer);
|
||||
*
|
||||
* // get the output frame, check in the class description below for more outputs:
|
||||
* const float *movingContours=movingContoursExtractor->getMagnoYsaturated();
|
||||
*
|
||||
* // at the end of the program, destroy object:
|
||||
* delete movingContoursExtractor;
|
||||
|
||||
* @author Alexandre BENOIT, benoit.alexandre.vision@gmail.com, LISTIC : www.listic.univ-savoie.fr, Gipsa-Lab, France: www.gipsa-lab.inpg.fr/
|
||||
* Creation date 2007
|
||||
* Based on Alexandre BENOIT thesis: "Le système visuel humain au secours de la vision par ordinateur"
|
||||
*/
|
||||
|
||||
#include "basicretinafilter.hpp"
|
||||
|
||||
//#define _IPL_RETINA_ELEMENT_DEBUG
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
class MagnoRetinaFilter: public BasicRetinaFilter
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* constructor parameters are only linked to image input size
|
||||
* @param NBrows: number of rows of the input image
|
||||
* @param NBcolumns: number of columns of the input image
|
||||
*/
|
||||
MagnoRetinaFilter(const unsigned int NBrows, const unsigned int NBcolumns);
|
||||
|
||||
|
||||
/**
|
||||
* destructor
|
||||
*/
|
||||
virtual ~MagnoRetinaFilter();
|
||||
|
||||
/**
|
||||
* function that clears all buffers of the object
|
||||
*/
|
||||
void clearAllBuffers();
|
||||
|
||||
/**
|
||||
* resize retina magno filter object (resize all allocated buffers)
|
||||
* @param NBrows: the new height size
|
||||
* @param NBcolumns: the new width size
|
||||
*/
|
||||
void resize(const unsigned int NBrows, const unsigned int NBcolumns);
|
||||
|
||||
/**
|
||||
* set parameters values
|
||||
* @param parasolCells_beta: the low pass filter gain used for local contrast adaptation at the IPL level of the retina (for ganglion cells local adaptation), typical value is 0
|
||||
* @param parasolCells_tau: the low pass filter time constant used for local contrast adaptation at the IPL level of the retina (for ganglion cells local adaptation), unit is frame, typical value is 0 (immediate response)
|
||||
* @param parasolCells_k: the low pass filter spatial constant used for local contrast adaptation at the IPL level of the retina (for ganglion cells local adaptation), unit is pixels, typical value is 5
|
||||
* @param amacrinCellsTemporalCutFrequency: the time constant of the first order high pass fiter of the magnocellular way (motion information channel), unit is frames, tipicall value is 5
|
||||
* @param localAdaptIntegration_tau: specifies the temporal constant of the low pas filter involved in the computation of the local "motion mean" for the local adaptation computation
|
||||
* @param localAdaptIntegration_k: specifies the spatial constant of the low pas filter involved in the computation of the local "motion mean" for the local adaptation computation
|
||||
*/
|
||||
void setCoefficientsTable(const float parasolCells_beta, const float parasolCells_tau, const float parasolCells_k, const float amacrinCellsTemporalCutFrequency, const float localAdaptIntegration_tau, const float localAdaptIntegration_k);
|
||||
|
||||
/**
|
||||
* launch filter that runs all the IPL magno filter (model of the magnocellular channel of the Inner Plexiform Layer of the retina)
|
||||
* @param OPL_ON: the output of the bipolar ON cells of the retina (available from the ParvoRetinaFilter class (getBipolarCellsON() function)
|
||||
* @param OPL_OFF: the output of the bipolar OFF cells of the retina (available from the ParvoRetinaFilter class (getBipolarCellsOFF() function)
|
||||
* @return the processed result without post-processing
|
||||
*/
|
||||
const std::valarray<float> &runFilter(const std::valarray<float> &OPL_ON, const std::valarray<float> &OPL_OFF);
|
||||
|
||||
/**
|
||||
* @return the Magnocellular ON channel filtering output
|
||||
*/
|
||||
inline const std::valarray<float> &getMagnoON() const { return _magnoXOutputON; }
|
||||
|
||||
/**
|
||||
* @return the Magnocellular OFF channel filtering output
|
||||
*/
|
||||
inline const std::valarray<float> &getMagnoOFF() const { return _magnoXOutputOFF; }
|
||||
|
||||
/**
|
||||
* @return the Magnocellular Y (sum of the ON and OFF magno channels) filtering output
|
||||
*/
|
||||
inline const std::valarray<float> &getMagnoYsaturated() const { return *_magnoYsaturated; }
|
||||
|
||||
/**
|
||||
* applies an image normalization which saturates the high output values by the use of an assymetric sigmoide
|
||||
*/
|
||||
inline void normalizeGrayOutputNearZeroCentreredSigmoide()
|
||||
{ _filterOutput.normalizeGrayOutputNearZeroCentreredSigmoide(&(*_magnoYOutput)[0], &(*_magnoYsaturated)[0]); }
|
||||
|
||||
/**
|
||||
* @return the horizontal cells' temporal constant
|
||||
*/
|
||||
inline float getTemporalConstant() { return _filteringCoeficientsTable[2]; }
|
||||
|
||||
private:
|
||||
|
||||
// related pointers to these buffers
|
||||
std::valarray<float> _previousInput_ON;
|
||||
std::valarray<float> _previousInput_OFF;
|
||||
std::valarray<float> _amacrinCellsTempOutput_ON;
|
||||
std::valarray<float> _amacrinCellsTempOutput_OFF;
|
||||
std::valarray<float> _magnoXOutputON;
|
||||
std::valarray<float> _magnoXOutputOFF;
|
||||
std::valarray<float> _localProcessBufferON;
|
||||
std::valarray<float> _localProcessBufferOFF;
|
||||
// reference to parent buffers and allow better readability
|
||||
TemplateBuffer<float> *_magnoYOutput;
|
||||
std::valarray<float> *_magnoYsaturated;
|
||||
|
||||
// varialbles
|
||||
float _temporalCoefficient;
|
||||
|
||||
// amacrine cells filter : high pass temporal filter
|
||||
void _amacrineCellsComputing(const float *ONinput, const float *OFFinput);
|
||||
#ifdef MAKE_PARALLEL
|
||||
/******************************************************
|
||||
** IF some parallelizing thread methods are available, then, main loops are parallelized using these functors
|
||||
** ==> main idea paralellise main filters loops, then, only the most used methods are parallelized... TODO : increase the number of parallelised methods as necessary
|
||||
** ==> functors names = Parallel_$$$ where $$$= the name of the serial method that is parallelised
|
||||
** ==> functors constructors can differ from the parameters used with their related serial functions
|
||||
*/
|
||||
class Parallel_amacrineCellsComputing: public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
const float *OPL_ON, *OPL_OFF;
|
||||
float *previousInput_ON, *previousInput_OFF, *amacrinCellsTempOutput_ON, *amacrinCellsTempOutput_OFF;
|
||||
float temporalCoefficient;
|
||||
public:
|
||||
Parallel_amacrineCellsComputing(const float *OPL_ON_PTR, const float *OPL_OFF_PTR, float *previousInput_ON_PTR, float *previousInput_OFF_PTR, float *amacrinCellsTempOutput_ON_PTR, float *amacrinCellsTempOutput_OFF_PTR, float temporalCoefficientVal)
|
||||
:OPL_ON(OPL_ON_PTR), OPL_OFF(OPL_OFF_PTR), previousInput_ON(previousInput_ON_PTR), previousInput_OFF(previousInput_OFF_PTR), amacrinCellsTempOutput_ON(amacrinCellsTempOutput_ON_PTR), amacrinCellsTempOutput_OFF(amacrinCellsTempOutput_OFF_PTR), temporalCoefficient(temporalCoefficientVal) {}
|
||||
|
||||
virtual void operator()( const Range& r ) const CV_OVERRIDE {
|
||||
const float *OPL_ON_PTR=OPL_ON+r.start;
|
||||
const float *OPL_OFF_PTR=OPL_OFF+r.start;
|
||||
float *previousInput_ON_PTR= previousInput_ON+r.start;
|
||||
float *previousInput_OFF_PTR= previousInput_OFF+r.start;
|
||||
float *amacrinCellsTempOutput_ON_PTR= amacrinCellsTempOutput_ON+r.start;
|
||||
float *amacrinCellsTempOutput_OFF_PTR= amacrinCellsTempOutput_OFF+r.start;
|
||||
|
||||
for (int IDpixel=r.start ; IDpixel!=r.end; ++IDpixel)
|
||||
{
|
||||
|
||||
/* Compute ON and OFF amacrin cells high pass temporal filter */
|
||||
float magnoXonPixelResult = temporalCoefficient*(*amacrinCellsTempOutput_ON_PTR+ *OPL_ON_PTR-*previousInput_ON_PTR);
|
||||
*(amacrinCellsTempOutput_ON_PTR++)=((float)(magnoXonPixelResult>0))*magnoXonPixelResult;
|
||||
|
||||
float magnoXoffPixelResult = temporalCoefficient*(*amacrinCellsTempOutput_OFF_PTR+ *OPL_OFF_PTR-*previousInput_OFF_PTR);
|
||||
*(amacrinCellsTempOutput_OFF_PTR++)=((float)(magnoXoffPixelResult>0))*magnoXoffPixelResult;
|
||||
|
||||
/* prepare next loop */
|
||||
*(previousInput_ON_PTR++)=*(OPL_ON_PTR++);
|
||||
*(previousInput_OFF_PTR++)=*(OPL_OFF_PTR++);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
|
||||
#endif /*MagnoRetinaFilter_H_*/
|
||||
@@ -0,0 +1,788 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2010-2013, Multicoreware, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Peng Xiao, pengxiao@multicorewareinc.com
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors as is and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
//data (which is float) is aligend in 32 bytes
|
||||
#define WIDTH_MULTIPLE (32 >> 2)
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
//------------------------------------------------------
|
||||
// basicretinafilter
|
||||
//////////////// _spatiotemporalLPfilter ////////////////
|
||||
//_horizontalCausalFilter_addInput
|
||||
kernel void horizontalCausalFilter_addInput(
|
||||
global const float * input,
|
||||
global float * output,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row,
|
||||
const int in_offset,
|
||||
const int out_offset,
|
||||
const float _tau,
|
||||
const float _a
|
||||
)
|
||||
{
|
||||
int gid = get_global_id(0);
|
||||
if(gid >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
global const float * iptr =
|
||||
input + mad24(gid, elements_per_row, in_offset / 4);
|
||||
global float * optr =
|
||||
output + mad24(gid, elements_per_row, out_offset / 4);
|
||||
|
||||
float res;
|
||||
float4 in_v4, out_v4, sum_v4, res_v4 = (float4)(0);
|
||||
//vectorize to increase throughput
|
||||
for(int i = 0; i < cols / 4; ++i, iptr += 4, optr += 4)
|
||||
{
|
||||
in_v4 = vload4(0, iptr);
|
||||
out_v4 = vload4(0, optr) * _tau;
|
||||
sum_v4 = in_v4 + out_v4;
|
||||
|
||||
res_v4.x = sum_v4.x + _a * res_v4.w;
|
||||
res_v4.y = sum_v4.y + _a * res_v4.x;
|
||||
res_v4.z = sum_v4.z + _a * res_v4.y;
|
||||
res_v4.w = sum_v4.w + _a * res_v4.z;
|
||||
|
||||
vstore4(res_v4, 0, optr);
|
||||
}
|
||||
|
||||
optr = output + mad24(gid + 1, elements_per_row, -4 + out_offset / 4);
|
||||
res_v4 = (float4)(0);
|
||||
for(int i = 0; i < elements_per_row / 4; ++i, optr -= 4)
|
||||
{
|
||||
// shift left, `offset` is type `size_t` so it cannot be negative
|
||||
out_v4 = vload4(0, optr);
|
||||
|
||||
res_v4.w = out_v4.w + _a * res_v4.x;
|
||||
res_v4.z = out_v4.z + _a * res_v4.w;
|
||||
res_v4.y = out_v4.y + _a * res_v4.z;
|
||||
res_v4.x = out_v4.x + _a * res_v4.y;
|
||||
|
||||
vstore4(res_v4, 0, optr);
|
||||
}
|
||||
}
|
||||
|
||||
//_verticalCausalFilter
|
||||
kernel void verticalCausalFilter(
|
||||
global float * output,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row,
|
||||
const int out_offset,
|
||||
const float _a,
|
||||
const float _gain
|
||||
)
|
||||
{
|
||||
int gid = get_global_id(0) * 2;
|
||||
if(gid >= cols)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
global float * optr = output + gid + out_offset / 4;
|
||||
float2 input;
|
||||
float2 result = (float2)0;
|
||||
for(int i = 0; i < rows; ++i, optr += elements_per_row)
|
||||
{
|
||||
input = vload2(0, optr);
|
||||
result = input + _a * result;
|
||||
vstore2(result, 0, optr);
|
||||
}
|
||||
|
||||
optr = output + (rows - 1) * elements_per_row + gid + out_offset / 4;
|
||||
result = (float2)0;
|
||||
for(int i = 0; i < rows; ++i, optr -= elements_per_row)
|
||||
{
|
||||
input = vload2(0, optr);
|
||||
result = input + _a * result;
|
||||
vstore2(_gain * result, 0, optr);
|
||||
}
|
||||
}
|
||||
|
||||
kernel void verticalCausalFilter_multichannel(
|
||||
global float * output,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row,
|
||||
const int out_offset,
|
||||
const float _a,
|
||||
const float _gain
|
||||
)
|
||||
{
|
||||
int gid = get_global_id(0) * 2;
|
||||
if(gid >= cols)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
global float * optr[3];
|
||||
float2 input[3];
|
||||
float2 result[3] = { (float2)0, (float2)0, (float2)0 };
|
||||
|
||||
optr[0] = output + gid + out_offset / 4;
|
||||
optr[1] = output + gid + out_offset / 4 + rows * elements_per_row;
|
||||
optr[2] = output + gid + out_offset / 4 + 2 * rows * elements_per_row;
|
||||
|
||||
for(int i = 0; i < rows; ++i)
|
||||
{
|
||||
input[0] = vload2(0, optr[0]);
|
||||
input[1] = vload2(0, optr[1]);
|
||||
input[2] = vload2(0, optr[2]);
|
||||
|
||||
result[0] = input[0] + _a * result[0];
|
||||
result[1] = input[1] + _a * result[1];
|
||||
result[2] = input[2] + _a * result[2];
|
||||
|
||||
vstore2(result[0], 0, optr[0]);
|
||||
vstore2(result[1], 0, optr[1]);
|
||||
vstore2(result[2], 0, optr[2]);
|
||||
|
||||
optr[0] += elements_per_row;
|
||||
optr[1] += elements_per_row;
|
||||
optr[2] += elements_per_row;
|
||||
}
|
||||
|
||||
optr[0] = output + (rows - 1) * elements_per_row + gid + out_offset / 4;
|
||||
optr[1] = output + (rows - 1) * elements_per_row + gid + out_offset / 4 + rows * elements_per_row;
|
||||
optr[2] = output + (rows - 1) * elements_per_row + gid + out_offset / 4 + 2 * rows * elements_per_row;
|
||||
result[0] = result[1] = result[2] = (float2)0;
|
||||
|
||||
for(int i = 0; i < rows; ++i)
|
||||
{
|
||||
input[0] = vload2(0, optr[0]);
|
||||
input[1] = vload2(0, optr[1]);
|
||||
input[2] = vload2(0, optr[2]);
|
||||
|
||||
result[0] = input[0] + _a * result[0];
|
||||
result[1] = input[1] + _a * result[1];
|
||||
result[2] = input[2] + _a * result[2];
|
||||
|
||||
vstore2(_gain * result[0], 0, optr[0]);
|
||||
vstore2(_gain * result[1], 0, optr[1]);
|
||||
vstore2(_gain * result[2], 0, optr[2]);
|
||||
|
||||
optr[0] -= elements_per_row;
|
||||
optr[1] -= elements_per_row;
|
||||
optr[2] -= elements_per_row;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// end of _spatiotemporalLPfilter
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////// verticalCausalFilter_Irregular ////////////////
|
||||
//////////////// verticalCausalFilter_Irregular ////////////////
|
||||
kernel void verticalCausalFilter_Irregular(
|
||||
global float * output,
|
||||
global float * buffer,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row,
|
||||
const int out_offset,
|
||||
const int buffer_offset,
|
||||
const float gain
|
||||
)
|
||||
{
|
||||
int gid = get_global_id(0) * 2;
|
||||
if(gid >= cols)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
global float * optr[3];
|
||||
global float * bptr = buffer + gid + buffer_offset / 4;
|
||||
float2 result[3] = { (float2)0, (float2)0, (float2)0 };
|
||||
float2 grad, input[3];
|
||||
optr[0] = output + gid + out_offset / 4;
|
||||
optr[1] = output + gid + out_offset / 4 + rows * elements_per_row;
|
||||
optr[2] = output + gid + out_offset / 4 + 2 * rows * elements_per_row;
|
||||
for(int i = 0; i < rows; ++i, bptr += elements_per_row)
|
||||
{
|
||||
input[0] = vload2(0, optr[0]);
|
||||
input[1] = vload2(0, optr[1]);
|
||||
input[2] = vload2(0, optr[2]);
|
||||
grad = vload2(0, bptr);
|
||||
result[0] = input[0] + grad * result[0];
|
||||
result[1] = input[1] + grad * result[1];
|
||||
result[2] = input[2] + grad * result[2];
|
||||
vstore2(result[0], 0, optr[0]);
|
||||
vstore2(result[1], 0, optr[1]);
|
||||
vstore2(result[2], 0, optr[2]);
|
||||
optr[0] += elements_per_row;
|
||||
optr[1] += elements_per_row;
|
||||
optr[2] += elements_per_row;
|
||||
}
|
||||
|
||||
int start_idx = mad24(rows - 1, elements_per_row, gid);
|
||||
optr[0] = output + start_idx + out_offset / 4;
|
||||
optr[1] = output + start_idx + out_offset / 4 + rows * elements_per_row;
|
||||
optr[2] = output + start_idx + out_offset / 4 + 2 * rows * elements_per_row;
|
||||
bptr = buffer + start_idx + buffer_offset / 4;
|
||||
result[0] = result[1] = result[2] = (float2)0;
|
||||
for(int i = 0; i < rows; ++i, bptr -= elements_per_row)
|
||||
{
|
||||
input[0] = vload2(0, optr[0]);
|
||||
input[1] = vload2(0, optr[1]);
|
||||
input[2] = vload2(0, optr[2]);
|
||||
grad = vload2(0, bptr);
|
||||
result[0] = input[0] + grad * result[0];
|
||||
result[1] = input[1] + grad * result[1];
|
||||
result[2] = input[2] + grad * result[2];
|
||||
vstore2(gain * result[0], 0, optr[0]);
|
||||
vstore2(gain * result[1], 0, optr[1]);
|
||||
vstore2(gain * result[2], 0, optr[2]);
|
||||
optr[0] -= elements_per_row;
|
||||
optr[1] -= elements_per_row;
|
||||
optr[2] -= elements_per_row;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////// _adaptiveHorizontalCausalFilter_addInput ////////////////
|
||||
kernel void adaptiveHorizontalCausalFilter_addInput(
|
||||
global const float * input,
|
||||
global const float * gradient,
|
||||
global float * output,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row,
|
||||
const int in_offset,
|
||||
const int grad_offset,
|
||||
const int out_offset
|
||||
)
|
||||
{
|
||||
int gid = get_global_id(0);
|
||||
if(gid >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
global const float * iptr =
|
||||
input + mad24(gid, elements_per_row, in_offset / 4);
|
||||
global const float * gptr =
|
||||
gradient + mad24(gid, elements_per_row, grad_offset / 4);
|
||||
global float * optr =
|
||||
output + mad24(gid, elements_per_row, out_offset / 4);
|
||||
|
||||
float4 in_v4, grad_v4, out_v4, res_v4 = (float4)(0);
|
||||
for(int i = 0; i < cols / 4; ++i, iptr += 4, gptr += 4, optr += 4)
|
||||
{
|
||||
in_v4 = vload4(0, iptr);
|
||||
grad_v4 = vload4(0, gptr);
|
||||
|
||||
res_v4.x = in_v4.x + grad_v4.x * res_v4.w;
|
||||
res_v4.y = in_v4.y + grad_v4.y * res_v4.x;
|
||||
res_v4.z = in_v4.z + grad_v4.z * res_v4.y;
|
||||
res_v4.w = in_v4.w + grad_v4.w * res_v4.z;
|
||||
|
||||
vstore4(res_v4, 0, optr);
|
||||
}
|
||||
|
||||
optr = output + mad24(gid + 1, elements_per_row, -4 + out_offset / 4);
|
||||
gptr = gradient + mad24(gid + 1, elements_per_row, -4 + grad_offset / 4);
|
||||
res_v4 = (float4)(0);
|
||||
|
||||
for(int i = 0; i < cols / 4; ++i, gptr -= 4, optr -= 4)
|
||||
{
|
||||
grad_v4 = vload4(0, gptr);
|
||||
out_v4 = vload4(0, optr);
|
||||
|
||||
res_v4.w = out_v4.w + grad_v4.w * res_v4.x;
|
||||
res_v4.z = out_v4.z + grad_v4.z * res_v4.w;
|
||||
res_v4.y = out_v4.y + grad_v4.y * res_v4.z;
|
||||
res_v4.x = out_v4.x + grad_v4.x * res_v4.y;
|
||||
|
||||
vstore4(res_v4, 0, optr);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////// _localLuminanceAdaptation ////////////////
|
||||
// FIXME:
|
||||
// This kernel seems to have precision problem on GPU
|
||||
kernel void localLuminanceAdaptation(
|
||||
global const float * luma,
|
||||
global const float * input,
|
||||
global float * output,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row,
|
||||
const float _localLuminanceAddon,
|
||||
const float _localLuminanceFactor,
|
||||
const float _maxInputValue
|
||||
)
|
||||
{
|
||||
int gidx = get_global_id(0) * 4, gidy = get_global_id(1);
|
||||
if(gidx >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int offset = mad24(gidy, elements_per_row, gidx);
|
||||
float4 luma_vec = vload4(0, luma + offset);
|
||||
float4 X0 = luma_vec * _localLuminanceFactor + _localLuminanceAddon;
|
||||
float4 input_val = vload4(0, input + offset);
|
||||
// output of the following line may be different between GPU and CPU
|
||||
float4 out_vec = (_maxInputValue + X0) * input_val / (input_val + X0 + 0.00000000001f);
|
||||
vstore4(out_vec, 0, output + offset);
|
||||
}
|
||||
// end of basicretinafilter
|
||||
//------------------------------------------------------
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
//------------------------------------------------------
|
||||
// magno
|
||||
// TODO: this kernel has too many buffer accesses, better to make it
|
||||
// vector read/write for fetch efficiency
|
||||
kernel void amacrineCellsComputing(
|
||||
global const float * opl_on,
|
||||
global const float * opl_off,
|
||||
global float * prev_in_on,
|
||||
global float * prev_in_off,
|
||||
global float * out_on,
|
||||
global float * out_off,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row,
|
||||
const float coeff
|
||||
)
|
||||
{
|
||||
int gidx = get_global_id(0) * 4, gidy = get_global_id(1);
|
||||
if(gidx >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int offset = mad24(gidy, elements_per_row, gidx);
|
||||
opl_on += offset;
|
||||
opl_off += offset;
|
||||
prev_in_on += offset;
|
||||
prev_in_off += offset;
|
||||
out_on += offset;
|
||||
out_off += offset;
|
||||
|
||||
float4 val_opl_on = vload4(0, opl_on);
|
||||
float4 val_opl_off = vload4(0, opl_off);
|
||||
|
||||
float4 magnoXonPixelResult = coeff * (vload4(0, out_on) + val_opl_on - vload4(0, prev_in_on));
|
||||
vstore4(fmax(magnoXonPixelResult, 0.f), 0, out_on);
|
||||
float4 magnoXoffPixelResult = coeff * (vload4(0, out_off) + val_opl_off - vload4(0, prev_in_off));
|
||||
vstore4(fmax(magnoXoffPixelResult, 0.f), 0, out_off);
|
||||
|
||||
vstore4(val_opl_on, 0, prev_in_on);
|
||||
vstore4(val_opl_off, 0, prev_in_off);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
//------------------------------------------------------
|
||||
// parvo
|
||||
// TODO: this kernel has too many buffer accesses, needs optimization
|
||||
kernel void OPL_OnOffWaysComputing(
|
||||
global float4 * photo_out,
|
||||
global float4 * horiz_out,
|
||||
global float4 * bipol_on,
|
||||
global float4 * bipol_off,
|
||||
global float4 * parvo_on,
|
||||
global float4 * parvo_off,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row
|
||||
)
|
||||
{
|
||||
int gidx = get_global_id(0), gidy = get_global_id(1);
|
||||
if(gidx * 4 >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// we assume elements_per_row must be multiples of 4
|
||||
int offset = mad24(gidy, elements_per_row >> 2, gidx);
|
||||
photo_out += offset;
|
||||
horiz_out += offset;
|
||||
bipol_on += offset;
|
||||
bipol_off += offset;
|
||||
parvo_on += offset;
|
||||
parvo_off += offset;
|
||||
|
||||
float4 diff = *photo_out - *horiz_out;
|
||||
float4 isPositive = convert_float4(abs(diff > (float4)0.0f));
|
||||
float4 res_on = isPositive * diff;
|
||||
float4 res_off = (isPositive - (float4)(1.0f)) * diff;
|
||||
|
||||
*bipol_on = res_on;
|
||||
*parvo_on = res_on;
|
||||
|
||||
*bipol_off = res_off;
|
||||
*parvo_off = res_off;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
//------------------------------------------------------
|
||||
// retinacolor
|
||||
inline int bayerSampleOffset(int step, int rows, int x, int y)
|
||||
{
|
||||
return mad24(y, step, x) +
|
||||
((y % 2) + (x % 2)) * rows * step;
|
||||
}
|
||||
|
||||
|
||||
/////// colorMultiplexing //////
|
||||
kernel void runColorMultiplexingBayer(
|
||||
global const float * input,
|
||||
global float * output,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row
|
||||
)
|
||||
{
|
||||
int gidx = get_global_id(0) * 4, gidy = get_global_id(1);
|
||||
if(gidx >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int offset = mad24(gidy, elements_per_row, gidx);
|
||||
float4 val;
|
||||
val.x = input[bayerSampleOffset(elements_per_row, rows, gidx + 0, gidy)];
|
||||
val.y = input[bayerSampleOffset(elements_per_row, rows, gidx + 1, gidy)];
|
||||
val.z = input[bayerSampleOffset(elements_per_row, rows, gidx + 2, gidy)];
|
||||
val.w = input[bayerSampleOffset(elements_per_row, rows, gidx + 3, gidy)];
|
||||
vstore4(val, 0, output + offset);
|
||||
}
|
||||
|
||||
kernel void runColorDemultiplexingBayer(
|
||||
global const float * input,
|
||||
global float * output,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row
|
||||
)
|
||||
{
|
||||
int gidx = get_global_id(0) * 4, gidy = get_global_id(1);
|
||||
if(gidx >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int offset = mad24(gidy, elements_per_row, gidx);
|
||||
float4 val = vload4(0, input + offset);
|
||||
output[bayerSampleOffset(elements_per_row, rows, gidx + 0, gidy)] = val.x;
|
||||
output[bayerSampleOffset(elements_per_row, rows, gidx + 1, gidy)] = val.y;
|
||||
output[bayerSampleOffset(elements_per_row, rows, gidx + 2, gidy)] = val.z;
|
||||
output[bayerSampleOffset(elements_per_row, rows, gidx + 3, gidy)] = val.w;
|
||||
}
|
||||
|
||||
kernel void demultiplexAssign(
|
||||
global const float * input,
|
||||
global float * output,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row
|
||||
)
|
||||
{
|
||||
int gidx = get_global_id(0), gidy = get_global_id(1);
|
||||
if(gidx >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int offset = bayerSampleOffset(elements_per_row, rows, gidx, gidy);
|
||||
output[offset] = input[offset];
|
||||
}
|
||||
|
||||
|
||||
//// normalizeGrayOutputCentredSigmoide
|
||||
kernel void normalizeGrayOutputCentredSigmoide(
|
||||
global const float * input,
|
||||
global float * output,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row,
|
||||
const float meanval,
|
||||
const float X0
|
||||
)
|
||||
|
||||
{
|
||||
int gidx = get_global_id(0) * 4, gidy = get_global_id(1);
|
||||
if(gidx >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int offset = mad24(gidy, elements_per_row, gidx);
|
||||
|
||||
float4 input_val = vload4(0, input + offset);
|
||||
input_val = meanval + (meanval + X0) * (input_val - meanval) / (fabs(input_val - meanval) + X0);
|
||||
vstore4(input_val, 0, output + offset);
|
||||
}
|
||||
|
||||
//// normalize by photoreceptors density
|
||||
kernel void normalizePhotoDensity(
|
||||
global const float * chroma,
|
||||
global const float * colorDensity,
|
||||
global const float * multiplex,
|
||||
global float * luma,
|
||||
global float * demultiplex,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row,
|
||||
const float pG
|
||||
)
|
||||
{
|
||||
const int gidx = get_global_id(0) * 4, gidy = get_global_id(1);
|
||||
if(gidx >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const int offset = mad24(gidy, elements_per_row, gidx);
|
||||
int index = offset;
|
||||
|
||||
float4 Cr = vload4(0, chroma + index) * vload4(0, colorDensity + index);
|
||||
index += elements_per_row * rows;
|
||||
float4 Cg = vload4(0, chroma + index) * vload4(0, colorDensity + index);
|
||||
index += elements_per_row * rows;
|
||||
float4 Cb = vload4(0, chroma + index) * vload4(0, colorDensity + index);
|
||||
|
||||
const float4 luma_res = (Cr + Cg + Cb) * pG;
|
||||
vstore4(luma_res, 0, luma + offset);
|
||||
float4 res_v4 = vload4(0, multiplex + offset) - luma_res;
|
||||
demultiplex[bayerSampleOffset(elements_per_row, rows, gidx + 0, gidy)] = res_v4.x;
|
||||
demultiplex[bayerSampleOffset(elements_per_row, rows, gidx + 1, gidy)] = res_v4.y;
|
||||
demultiplex[bayerSampleOffset(elements_per_row, rows, gidx + 2, gidy)] = res_v4.z;
|
||||
demultiplex[bayerSampleOffset(elements_per_row, rows, gidx + 3, gidy)] = res_v4.w;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////// computeGradient ///////
|
||||
// TODO:
|
||||
// this function maybe accelerated by image2d_t or lds
|
||||
kernel void computeGradient(
|
||||
global const float * luma,
|
||||
global float * gradient,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row
|
||||
)
|
||||
{
|
||||
int gidx = get_global_id(0) + 2, gidy = get_global_id(1) + 2;
|
||||
if(gidx >= cols - 2 || gidy >= rows - 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int offset = mad24(gidy, elements_per_row, gidx);
|
||||
luma += offset;
|
||||
|
||||
// horizontal and vertical local gradients
|
||||
const float v_grad = fabs(luma[elements_per_row] - luma[- elements_per_row]);
|
||||
const float h_grad = fabs(luma[1] - luma[-1]);
|
||||
|
||||
// neighborhood horizontal and vertical gradients
|
||||
const float cur_val = luma[0];
|
||||
const float v_grad_p = fabs(cur_val - luma[- 2 * elements_per_row]);
|
||||
const float h_grad_p = fabs(cur_val - luma[- 2]);
|
||||
const float v_grad_n = fabs(cur_val - luma[2 * elements_per_row]);
|
||||
const float h_grad_n = fabs(cur_val - luma[2]);
|
||||
|
||||
const float horiz_grad = 0.5f * h_grad + 0.25f * (h_grad_p + h_grad_n);
|
||||
const float verti_grad = 0.5f * v_grad + 0.25f * (v_grad_p + v_grad_n);
|
||||
const bool is_vertical_greater = (horiz_grad < verti_grad) &&
|
||||
((verti_grad - horiz_grad) > 1e-5);
|
||||
|
||||
gradient[offset + elements_per_row * rows] = is_vertical_greater ? 0.06f : 0.57f;
|
||||
gradient[offset ] = is_vertical_greater ? 0.57f : 0.06f;
|
||||
}
|
||||
|
||||
|
||||
/////// substractResidual ///////
|
||||
kernel void substractResidual(
|
||||
global float * input,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row,
|
||||
const float pR,
|
||||
const float pG,
|
||||
const float pB
|
||||
)
|
||||
{
|
||||
const int gidx = get_global_id(0) * 4, gidy = get_global_id(1);
|
||||
if(gidx >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int indices [3] =
|
||||
{
|
||||
mad24(gidy, elements_per_row, gidx),
|
||||
mad24(gidy + rows, elements_per_row, gidx),
|
||||
mad24(gidy + 2 * rows, elements_per_row, gidx)
|
||||
};
|
||||
float4 vals[3];
|
||||
vals[0] = vload4(0, input + indices[0]);
|
||||
vals[1] = vload4(0, input + indices[1]);
|
||||
vals[2] = vload4(0, input + indices[2]);
|
||||
|
||||
float4 residu = pR * vals[0] + pG * vals[1] + pB * vals[2];
|
||||
vstore4(vals[0] - residu, 0, input + indices[0]);
|
||||
vstore4(vals[1] - residu, 0, input + indices[1]);
|
||||
vstore4(vals[2] - residu, 0, input + indices[2]);
|
||||
}
|
||||
|
||||
///// clipRGBOutput_0_maxInputValue /////
|
||||
kernel void clipRGBOutput_0_maxInputValue(
|
||||
global float * input,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row,
|
||||
const float maxVal
|
||||
)
|
||||
{
|
||||
const int gidx = get_global_id(0) * 4, gidy = get_global_id(1);
|
||||
if(gidx >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const int offset = mad24(gidy, elements_per_row, gidx);
|
||||
float4 val = vload4(0, input + offset);
|
||||
val = clamp(val, 0.0f, maxVal);
|
||||
vstore4(val, 0, input + offset);
|
||||
}
|
||||
|
||||
//// normalizeGrayOutputNearZeroCentreredSigmoide ////
|
||||
kernel void normalizeGrayOutputNearZeroCentreredSigmoide(
|
||||
global float * input,
|
||||
global float * output,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row,
|
||||
const float maxVal,
|
||||
const float X0cube
|
||||
)
|
||||
{
|
||||
const int gidx = get_global_id(0) * 4, gidy = get_global_id(1);
|
||||
if(gidx >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const int offset = mad24(gidy, elements_per_row, gidx);
|
||||
float4 currentCubeLuminance = vload4(0, input + offset);
|
||||
currentCubeLuminance = currentCubeLuminance * currentCubeLuminance * currentCubeLuminance;
|
||||
float4 val = currentCubeLuminance * X0cube / (X0cube + currentCubeLuminance);
|
||||
vstore4(val, 0, output + offset);
|
||||
}
|
||||
|
||||
//// centerReductImageLuminance ////
|
||||
kernel void centerReductImageLuminance(
|
||||
global float * input,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row,
|
||||
const float mean,
|
||||
const float std_dev
|
||||
)
|
||||
{
|
||||
const int gidx = get_global_id(0) * 4, gidy = get_global_id(1);
|
||||
if(gidx >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const int offset = mad24(gidy, elements_per_row, gidx);
|
||||
|
||||
float4 val = vload4(0, input + offset);
|
||||
val = (val - mean) / std_dev;
|
||||
vstore4(val, 0, input + offset);
|
||||
}
|
||||
|
||||
//// inverseValue ////
|
||||
kernel void inverseValue(
|
||||
global float * input,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int elements_per_row
|
||||
)
|
||||
{
|
||||
const int gidx = get_global_id(0) * 4, gidy = get_global_id(1);
|
||||
if(gidx >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const int offset = mad24(gidy, elements_per_row, gidx);
|
||||
float4 val = vload4(0, input + offset);
|
||||
val = 1.f / val;
|
||||
vstore4(val, 0, input + offset);
|
||||
}
|
||||
|
||||
#define CV_PI 3.1415926535897932384626433832795
|
||||
|
||||
//// _processRetinaParvoMagnoMapping ////
|
||||
kernel void processRetinaParvoMagnoMapping(
|
||||
global float * parvo,
|
||||
global float * magno,
|
||||
global float * output,
|
||||
const int cols,
|
||||
const int rows,
|
||||
const int halfCols,
|
||||
const int halfRows,
|
||||
const int elements_per_row,
|
||||
const float minDistance
|
||||
)
|
||||
{
|
||||
const int gidx = get_global_id(0), gidy = get_global_id(1);
|
||||
if(gidx >= cols || gidy >= rows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const int offset = mad24(gidy, elements_per_row, gidx);
|
||||
|
||||
float distanceToCenter =
|
||||
sqrt(((float)(gidy - halfRows) * (gidy - halfRows) + (gidx - halfCols) * (gidx - halfCols)));
|
||||
|
||||
float a = distanceToCenter < minDistance ?
|
||||
(0.5f + 0.5f * (float)cos(CV_PI * distanceToCenter / minDistance)) : 0;
|
||||
float b = 1.f - a;
|
||||
|
||||
output[offset] = parvo[offset] * a + magno[offset] * b;
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2011
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "parvoretinafilter.hpp"
|
||||
|
||||
// @author Alexandre BENOIT, benoit.alexandre.vision@gmail.com, LISTIC : www.listic.univ-savoie.fr, Gipsa-Lab, France: www.gipsa-lab.inpg.fr/
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
//////////////////////////////////////////////////////////
|
||||
// OPL RETINA FILTER
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
// Constructor and Desctructor of the OPL retina filter
|
||||
|
||||
ParvoRetinaFilter::ParvoRetinaFilter(const unsigned int NBrows, const unsigned int NBcolumns)
|
||||
:BasicRetinaFilter(NBrows, NBcolumns, 3),
|
||||
_photoreceptorsOutput(NBrows*NBcolumns),
|
||||
_horizontalCellsOutput(NBrows*NBcolumns),
|
||||
_parvocellularOutputON(NBrows*NBcolumns),
|
||||
_parvocellularOutputOFF(NBrows*NBcolumns),
|
||||
_bipolarCellsOutputON(NBrows*NBcolumns),
|
||||
_bipolarCellsOutputOFF(NBrows*NBcolumns),
|
||||
_localAdaptationOFF(NBrows*NBcolumns)
|
||||
{
|
||||
// link to the required local parent adaptation buffers
|
||||
_localAdaptationON=&_localBuffer;
|
||||
_parvocellularOutputONminusOFF=&_filterOutput;
|
||||
// (*_localAdaptationON)=&_localBuffer;
|
||||
// (*_parvocellularOutputONminusOFF)=&(BasicRetinaFilter::TemplateBuffer);
|
||||
|
||||
// init: set all the values to 0
|
||||
clearAllBuffers();
|
||||
|
||||
|
||||
#ifdef OPL_RETINA_ELEMENT_DEBUG
|
||||
std::cout<<"ParvoRetinaFilter::Init OPL retina filter at specified frame size OK\n"<<std::endl;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
ParvoRetinaFilter::~ParvoRetinaFilter()
|
||||
{
|
||||
|
||||
#ifdef OPL_RETINA_ELEMENT_DEBUG
|
||||
std::cout<<"ParvoRetinaFilter::Delete OPL retina filter OK"<<std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////
|
||||
// functions of the PARVO filter
|
||||
////////////////////////////////////
|
||||
|
||||
// function that clears all buffers of the object
|
||||
void ParvoRetinaFilter::clearAllBuffers()
|
||||
{
|
||||
BasicRetinaFilter::clearAllBuffers();
|
||||
_photoreceptorsOutput=0;
|
||||
_horizontalCellsOutput=0;
|
||||
_parvocellularOutputON=0;
|
||||
_parvocellularOutputOFF=0;
|
||||
_bipolarCellsOutputON=0;
|
||||
_bipolarCellsOutputOFF=0;
|
||||
_localAdaptationOFF=0;
|
||||
}
|
||||
|
||||
/**
|
||||
* resize parvo retina filter object (resize all allocated buffers
|
||||
* @param NBrows: the new height size
|
||||
* @param NBcolumns: the new width size
|
||||
*/
|
||||
void ParvoRetinaFilter::resize(const unsigned int NBrows, const unsigned int NBcolumns)
|
||||
{
|
||||
BasicRetinaFilter::resize(NBrows, NBcolumns);
|
||||
_photoreceptorsOutput.resize(NBrows*NBcolumns);
|
||||
_horizontalCellsOutput.resize(NBrows*NBcolumns);
|
||||
_parvocellularOutputON.resize(NBrows*NBcolumns);
|
||||
_parvocellularOutputOFF.resize(NBrows*NBcolumns);
|
||||
_bipolarCellsOutputON.resize(NBrows*NBcolumns);
|
||||
_bipolarCellsOutputOFF.resize(NBrows*NBcolumns);
|
||||
_localAdaptationOFF.resize(NBrows*NBcolumns);
|
||||
|
||||
// link to the required local parent adaptation buffers
|
||||
_localAdaptationON=&_localBuffer;
|
||||
_parvocellularOutputONminusOFF=&_filterOutput;
|
||||
|
||||
// clean buffers
|
||||
clearAllBuffers();
|
||||
}
|
||||
|
||||
// change the parameters of the filter
|
||||
void ParvoRetinaFilter::setOPLandParvoFiltersParameters(const float beta1, const float tau1, const float k1, const float beta2, const float tau2, const float k2)
|
||||
{
|
||||
// init photoreceptors low pass filter
|
||||
setLPfilterParameters(beta1, tau1, k1);
|
||||
// init horizontal cells low pass filter
|
||||
setLPfilterParameters(beta2, tau2, k2, 1);
|
||||
// init parasol ganglion cells low pass filter (default parameters)
|
||||
setLPfilterParameters(0, tau1, k1, 2);
|
||||
|
||||
}
|
||||
|
||||
// update/set size of the frames
|
||||
|
||||
// run filter for a new frame input
|
||||
// output return is (*_parvocellularOutputONminusOFF)
|
||||
const std::valarray<float> &ParvoRetinaFilter::runFilter(const std::valarray<float> &inputFrame, const bool useParvoOutput)
|
||||
{
|
||||
_spatiotemporalLPfilter(get_data(inputFrame), &_photoreceptorsOutput[0]);
|
||||
_spatiotemporalLPfilter(&_photoreceptorsOutput[0], &_horizontalCellsOutput[0], 1);
|
||||
_OPL_OnOffWaysComputing();
|
||||
|
||||
if (useParvoOutput)
|
||||
{
|
||||
// local adaptation processes on ON and OFF ways
|
||||
_spatiotemporalLPfilter(&_bipolarCellsOutputON[0], &(*_localAdaptationON)[0], 2);
|
||||
_localLuminanceAdaptation(&_parvocellularOutputON[0], &(*_localAdaptationON)[0]);
|
||||
|
||||
_spatiotemporalLPfilter(&_bipolarCellsOutputOFF[0], &_localAdaptationOFF[0], 2);
|
||||
_localLuminanceAdaptation(&_parvocellularOutputOFF[0], &_localAdaptationOFF[0]);
|
||||
|
||||
//// Final loop that computes the main output of this filter
|
||||
//
|
||||
//// loop that makes the difference between photoreceptor cells output and horizontal cells
|
||||
//// positive part goes on the ON way, negative pat goes on the OFF way
|
||||
float *parvocellularOutputONminusOFF_PTR=&(*_parvocellularOutputONminusOFF)[0];
|
||||
float *parvocellularOutputON_PTR=&_parvocellularOutputON[0];
|
||||
float *parvocellularOutputOFF_PTR=&_parvocellularOutputOFF[0];
|
||||
|
||||
for (unsigned int IDpixel=0 ; IDpixel<_filterOutput.getNBpixels() ; ++IDpixel)
|
||||
*(parvocellularOutputONminusOFF_PTR++)= (*(parvocellularOutputON_PTR++)-*(parvocellularOutputOFF_PTR++));
|
||||
}
|
||||
return (*_parvocellularOutputONminusOFF);
|
||||
}
|
||||
|
||||
void ParvoRetinaFilter::_OPL_OnOffWaysComputing() // WARNING : this method requires many buffer accesses, parallelizing can increase bandwith & core efficacy
|
||||
{
|
||||
// loop that makes the difference between photoreceptor cells output and horizontal cells
|
||||
// positive part goes on the ON way, negative pat goes on the OFF way
|
||||
|
||||
#ifdef MAKE_PARALLEL
|
||||
cv::parallel_for_(cv::Range(0,_filterOutput.getNBpixels()), Parallel_OPL_OnOffWaysComputing(&_photoreceptorsOutput[0], &_horizontalCellsOutput[0], &_bipolarCellsOutputON[0], &_bipolarCellsOutputOFF[0], &_parvocellularOutputON[0], &_parvocellularOutputOFF[0]));
|
||||
#else
|
||||
float *photoreceptorsOutput_PTR= &_photoreceptorsOutput[0];
|
||||
float *horizontalCellsOutput_PTR= &_horizontalCellsOutput[0];
|
||||
float *bipolarCellsON_PTR = &_bipolarCellsOutputON[0];
|
||||
float *bipolarCellsOFF_PTR = &_bipolarCellsOutputOFF[0];
|
||||
float *parvocellularOutputON_PTR= &_parvocellularOutputON[0];
|
||||
float *parvocellularOutputOFF_PTR= &_parvocellularOutputOFF[0];
|
||||
// compute bipolar cells response equal to photoreceptors minus horizontal cells response
|
||||
// and copy the result on parvo cellular outputs... keeping time before their local contrast adaptation for final result
|
||||
for (unsigned int IDpixel=0 ; IDpixel<_filterOutput.getNBpixels() ; ++IDpixel)
|
||||
{
|
||||
float pixelDifference = *(photoreceptorsOutput_PTR++) -*(horizontalCellsOutput_PTR++);
|
||||
// test condition to allow write pixelDifference in ON or OFF buffer and 0 in the over
|
||||
float isPositive=(float) (pixelDifference>0.0f);
|
||||
|
||||
// ON and OFF channels writing step
|
||||
*(parvocellularOutputON_PTR++)=*(bipolarCellsON_PTR++) = isPositive*pixelDifference;
|
||||
*(parvocellularOutputOFF_PTR++)=*(bipolarCellsOFF_PTR++)= (isPositive-1.0f)*pixelDifference;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
@@ -0,0 +1,264 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2011
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef ParvoRetinaFilter_H_
|
||||
#define ParvoRetinaFilter_H_
|
||||
|
||||
/**
|
||||
* @class ParvoRetinaFilter
|
||||
* @brief class which describes the OPL retina model and the Inner Plexiform Layer parvocellular channel of the retina:
|
||||
* -> performs a contours extraction with powerfull local data enhancement as at the retina level
|
||||
* -> spectrum whitening occurs at the OPL (Outer Plexiform Layer) of the retina: corrects the 1/f spectrum tendancy of natural images
|
||||
* ---> enhances details with mid spatial frequencies, attenuates low spatial frequencies (luminance), attenuates high temporal frequencies and high spatial frequencies, etc.
|
||||
*
|
||||
* TYPICAL USE:
|
||||
*
|
||||
* // create object at a specified picture size
|
||||
* ParvoRetinaFilter *contoursExtractor;
|
||||
* contoursExtractor =new ParvoRetinaFilter(frameSizeRows, frameSizeColumns);
|
||||
*
|
||||
* // init gain, spatial and temporal parameters:
|
||||
* contoursExtractor->setCoefficientsTable(0, 0.7, 1, 0, 7, 1);
|
||||
*
|
||||
* // during program execution, call the filter for contours extraction for an input picture called "FrameBuffer":
|
||||
* contoursExtractor->runfilter(FrameBuffer);
|
||||
*
|
||||
* // get the output frame, check in the class description below for more outputs:
|
||||
* const float *contours=contoursExtractor->getParvoONminusOFF();
|
||||
*
|
||||
* // at the end of the program, destroy object:
|
||||
* delete contoursExtractor;
|
||||
|
||||
* @author Alexandre BENOIT, benoit.alexandre.vision@gmail.com, LISTIC : www.listic.univ-savoie.fr, Gipsa-Lab, France: www.gipsa-lab.inpg.fr/
|
||||
* Creation date 2007
|
||||
* Based on Alexandre BENOIT thesis: "Le système visuel humain au secours de la vision par ordinateur"
|
||||
*
|
||||
*/
|
||||
|
||||
#include "basicretinafilter.hpp"
|
||||
|
||||
|
||||
//#define _OPL_RETINA_ELEMENT_DEBUG
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
//retina classes that derivate from the Basic Retrina class
|
||||
class ParvoRetinaFilter: public BasicRetinaFilter
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* constructor parameters are only linked to image input size
|
||||
* @param NBrows: number of rows of the input image
|
||||
* @param NBcolumns: number of columns of the input image
|
||||
*/
|
||||
ParvoRetinaFilter(const unsigned int NBrows=480, const unsigned int NBcolumns=640);
|
||||
|
||||
/**
|
||||
* standard desctructor
|
||||
*/
|
||||
virtual ~ParvoRetinaFilter();
|
||||
|
||||
/**
|
||||
* resize method, keeps initial parameters, all buffers are flushed
|
||||
* @param NBrows: number of rows of the input image
|
||||
* @param NBcolumns: number of columns of the input image
|
||||
*/
|
||||
void resize(const unsigned int NBrows, const unsigned int NBcolumns);
|
||||
|
||||
/**
|
||||
* function that clears all buffers of the object
|
||||
*/
|
||||
void clearAllBuffers();
|
||||
|
||||
/**
|
||||
* setup the OPL and IPL parvo channels
|
||||
* @param beta1: gain of the horizontal cells network, if 0, then the mean value of the output is zero, if the parameter is near 1, the amplitude is boosted but it should only be used for values rescaling... if needed
|
||||
* @param tau1: the time constant of the first order low pass filter of the photoreceptors, use it to cut high temporal frequencies (noise or fast motion), unit is frames, typical value is 1 frame
|
||||
* @param k1: the spatial constant of the first order low pass filter of the photoreceptors, use it to cut high spatial frequencies (noise or thick contours), unit is pixels, typical value is 1 pixel
|
||||
* @param beta2: gain of the horizontal cells network, if 0, then the mean value of the output is zero, if the parameter is near 1, then, the luminance is not filtered and is still reachable at the output, typicall value is 0
|
||||
* @param tau2: the time constant of the first order low pass filter of the horizontal cells, use it to cut low temporal frequencies (local luminance variations), unit is frames, typical value is 1 frame, as the photoreceptors
|
||||
* @param k2: the spatial constant of the first order low pass filter of the horizontal cells, use it to cut low spatial frequencies (local luminance), unit is pixels, typical value is 5 pixel, this value is also used for local contrast computing when computing the local contrast adaptation at the ganglion cells level (Inner Plexiform Layer parvocellular channel model)
|
||||
*/
|
||||
void setOPLandParvoFiltersParameters(const float beta1, const float tau1, const float k1, const float beta2, const float tau2, const float k2);
|
||||
|
||||
/**
|
||||
* setup more precisely the low pass filter used for the ganglion cells low pass filtering (used for local luminance adaptation)
|
||||
* @param tau: time constant of the filter (unit is frame for video processing)
|
||||
* @param k: spatial constant of the filter (unit is pixels)
|
||||
*/
|
||||
void setGanglionCellsLocalAdaptationLPfilterParameters(const float tau, const float k)
|
||||
{ BasicRetinaFilter::setLPfilterParameters(0, tau, k, 2); } // change the parameters of the filter
|
||||
|
||||
|
||||
/**
|
||||
* launch filter that runs the OPL spatiotemporal filtering and optionally finalizes IPL Pagno filter (model of the Parvocellular channel of the Inner Plexiform Layer of the retina)
|
||||
* @param inputFrame: the input image to be processed, this can be the direct gray level input frame, but a better efficacy is expected if the input is preliminary processed by the photoreceptors local adaptation possible to acheive with the help of a BasicRetinaFilter object
|
||||
* @param useParvoOutput: set true if the final IPL filtering step has to be computed (local contrast enhancement)
|
||||
* @return the processed Parvocellular channel output (updated only if useParvoOutput is true)
|
||||
* @details: in any case, after this function call, photoreceptors and horizontal cells output are updated, use getPhotoreceptorsLPfilteringOutput() and getHorizontalCellsOutput() to get them
|
||||
* also, bipolar cells output are accessible (difference between photoreceptors and horizontal cells, ON output has positive values, OFF ouput has negative values), use the following access methods: getBipolarCellsON() and getBipolarCellsOFF()if useParvoOutput is true,
|
||||
* if useParvoOutput is true, the complete Parvocellular channel is computed, more outputs are updated and can be accessed threw: getParvoON(), getParvoOFF() and their difference with getOutput()
|
||||
*/
|
||||
const std::valarray<float> &runFilter(const std::valarray<float> &inputFrame, const bool useParvoOutput=true); // output return is _parvocellularOutputONminusOFF
|
||||
|
||||
/**
|
||||
* @return the output of the photoreceptors filtering step (high cut frequency spatio-temporal low pass filter)
|
||||
*/
|
||||
inline const std::valarray<float> &getPhotoreceptorsLPfilteringOutput() const { return _photoreceptorsOutput; }
|
||||
|
||||
/**
|
||||
* @return the output of the photoreceptors filtering step (low cut frequency spatio-temporal low pass filter)
|
||||
*/
|
||||
inline const std::valarray<float> &getHorizontalCellsOutput() const { return _horizontalCellsOutput; }
|
||||
|
||||
/**
|
||||
* @return the output Parvocellular ON channel of the retina model
|
||||
*/
|
||||
inline const std::valarray<float> &getParvoON() const { return _parvocellularOutputON; }
|
||||
|
||||
/**
|
||||
* @return the output Parvocellular OFF channel of the retina model
|
||||
*/
|
||||
inline const std::valarray<float> &getParvoOFF() const { return _parvocellularOutputOFF; }
|
||||
|
||||
/**
|
||||
* @return the output of the Bipolar cells of the ON channel of the retina model same as function getParvoON() but without luminance local adaptation
|
||||
*/
|
||||
inline const std::valarray<float> &getBipolarCellsON() const { return _bipolarCellsOutputON; }
|
||||
|
||||
/**
|
||||
* @return the output of the Bipolar cells of the OFF channel of the retina model same as function getParvoON() but without luminance local adaptation
|
||||
*/
|
||||
inline const std::valarray<float> &getBipolarCellsOFF() const { return _bipolarCellsOutputOFF; }
|
||||
|
||||
/**
|
||||
* @return the photoreceptors's temporal constant
|
||||
*/
|
||||
inline float getPhotoreceptorsTemporalConstant() { return _filteringCoeficientsTable[2]; }
|
||||
|
||||
/**
|
||||
* @return the horizontal cells' temporal constant
|
||||
*/
|
||||
inline float getHcellsTemporalConstant(){return _filteringCoeficientsTable[5]; }
|
||||
|
||||
private:
|
||||
// template buffers
|
||||
std::valarray <float>_photoreceptorsOutput;
|
||||
std::valarray <float>_horizontalCellsOutput;
|
||||
std::valarray <float>_parvocellularOutputON;
|
||||
std::valarray <float>_parvocellularOutputOFF;
|
||||
std::valarray <float>_bipolarCellsOutputON;
|
||||
std::valarray <float>_bipolarCellsOutputOFF;
|
||||
std::valarray <float>_localAdaptationOFF;
|
||||
std::valarray <float> *_localAdaptationON;
|
||||
TemplateBuffer<float> *_parvocellularOutputONminusOFF;
|
||||
// private functions
|
||||
void _OPL_OnOffWaysComputing();
|
||||
|
||||
#ifdef MAKE_PARALLEL
|
||||
/******************************************************
|
||||
** IF some parallelizing thread methods are available, then, main loops are parallelized using these functors
|
||||
** ==> main idea paralellise main filters loops, then, only the most used methods are parallelized... TODO : increase the number of parallelised methods as necessary
|
||||
** ==> functors names = Parallel_$$$ where $$$= the name of the serial method that is parallelised
|
||||
** ==> functors constructors can differ from the parameters used with their related serial functions
|
||||
*/
|
||||
class Parallel_OPL_OnOffWaysComputing: public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
float *photoreceptorsOutput, *horizontalCellsOutput, *bipolarCellsON, *bipolarCellsOFF, *parvocellularOutputON, *parvocellularOutputOFF;
|
||||
public:
|
||||
Parallel_OPL_OnOffWaysComputing(float *photoreceptorsOutput_PTR, float *horizontalCellsOutput_PTR, float *bipolarCellsON_PTR, float *bipolarCellsOFF_PTR, float *parvocellularOutputON_PTR, float *parvocellularOutputOFF_PTR)
|
||||
:photoreceptorsOutput(photoreceptorsOutput_PTR), horizontalCellsOutput(horizontalCellsOutput_PTR), bipolarCellsON(bipolarCellsON_PTR), bipolarCellsOFF(bipolarCellsOFF_PTR), parvocellularOutputON(parvocellularOutputON_PTR), parvocellularOutputOFF(parvocellularOutputOFF_PTR) {}
|
||||
|
||||
virtual void operator()( const Range& r ) const CV_OVERRIDE {
|
||||
// compute bipolar cells response equal to photoreceptors minus horizontal cells response
|
||||
// and copy the result on parvo cellular outputs... keeping time before their local contrast adaptation for final result
|
||||
float *photoreceptorsOutput_PTR= photoreceptorsOutput+r.start;
|
||||
float *horizontalCellsOutput_PTR= horizontalCellsOutput+r.start;
|
||||
float *bipolarCellsON_PTR = bipolarCellsON+r.start;
|
||||
float *bipolarCellsOFF_PTR = bipolarCellsOFF+r.start;
|
||||
float *parvocellularOutputON_PTR= parvocellularOutputON+r.start;
|
||||
float *parvocellularOutputOFF_PTR= parvocellularOutputOFF+r.start;
|
||||
|
||||
for (int IDpixel=r.start ; IDpixel!=r.end ; ++IDpixel)
|
||||
{
|
||||
float pixelDifference = *(photoreceptorsOutput_PTR++) -*(horizontalCellsOutput_PTR++);
|
||||
// test condition to allow write pixelDifference in ON or OFF buffer and 0 in the over
|
||||
float isPositive=(float) (pixelDifference>0.0f);
|
||||
|
||||
// ON and OFF channels writing step
|
||||
*(parvocellularOutputON_PTR++)=*(bipolarCellsON_PTR++) = isPositive*pixelDifference;
|
||||
*(parvocellularOutputOFF_PTR++)=*(bipolarCellsOFF_PTR++)= (isPositive-1.0f)*pixelDifference;
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
};
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OPENCV_PRECOMP_H__
|
||||
#define __OPENCV_PRECOMP_H__
|
||||
|
||||
#include "opencv2/opencv_modules.hpp"
|
||||
#include "opencv2/bioinspired.hpp"
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/core/private.hpp"
|
||||
#include "opencv2/core/ocl.hpp"
|
||||
#include "opencv2/core/opencl/ocl_defs.hpp"
|
||||
|
||||
#include <valarray>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
// special function to get pointer to constant valarray elements, since
|
||||
// simple &arr[0] does not compile on VS2005/VS2008.
|
||||
template<typename T> inline const T* get_data(const std::valarray<T>& arr)
|
||||
{ return &((std::valarray<T>&)arr)[0]; }
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,845 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2015
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2015, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
* Retina.cpp
|
||||
*
|
||||
* Created on: Jul 19, 2011
|
||||
* Author: Alexandre Benoit
|
||||
*/
|
||||
#include "precomp.hpp"
|
||||
#include "retinafilter.hpp"
|
||||
#include "retina_ocl.hpp"
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <valarray>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
|
||||
class RetinaImpl CV_FINAL : public Retina
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Main constructor with most commun use setup : create an instance of color ready retina model
|
||||
* @param inputSize : the input frame size
|
||||
*/
|
||||
RetinaImpl(const Size inputSize);
|
||||
|
||||
/**
|
||||
* Complete Retina filter constructor which allows all basic structural parameters definition
|
||||
* @param inputSize : the input frame size
|
||||
* @param colorMode : the chosen processing mode : with or without color processing
|
||||
* @param colorSamplingMethod: specifies which kind of color sampling will be used
|
||||
* @param useRetinaLogSampling: activate retina log sampling, if true, the 2 following parameters can be used
|
||||
* @param reductionFactor: only usefull if param useRetinaLogSampling=true, specifies the reduction factor of the output frame (as the center (fovea) is high resolution and corners can be underscaled, then a reduction of the output is allowed without precision leak
|
||||
* @param samplingStrength: only usefull if param useRetinaLogSampling=true, specifies the strength of the log scale that is applied
|
||||
*/
|
||||
RetinaImpl(const Size inputSize, const bool colorMode, int colorSamplingMethod=RETINA_COLOR_BAYER, const bool useRetinaLogSampling=false, const float reductionFactor=1.0f, const float samplingStrength=10.0f);
|
||||
|
||||
virtual ~RetinaImpl() CV_OVERRIDE;
|
||||
/**
|
||||
* retreive retina input buffer size
|
||||
*/
|
||||
Size getInputSize() CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* retreive retina output buffer size
|
||||
*/
|
||||
Size getOutputSize() CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* try to open an XML retina parameters file to adjust current retina instance setup
|
||||
* => if the xml file does not exist, then default setup is applied
|
||||
* => warning, Exceptions are thrown if read XML file is not valid
|
||||
* @param retinaParameterFile : the parameters filename
|
||||
* @param applyDefaultSetupOnFailure : set to true if an error must be thrown on error
|
||||
*/
|
||||
void setup(String retinaParameterFile="", const bool applyDefaultSetupOnFailure=true) CV_OVERRIDE;
|
||||
|
||||
|
||||
/**
|
||||
* try to open an XML retina parameters file to adjust current retina instance setup
|
||||
* => if the xml file does not exist, then default setup is applied
|
||||
* => warning, Exceptions are thrown if read XML file is not valid
|
||||
* @param fs : the open Filestorage which contains retina parameters
|
||||
* @param applyDefaultSetupOnFailure : set to true if an error must be thrown on error
|
||||
*/
|
||||
void setup(cv::FileStorage &fs, const bool applyDefaultSetupOnFailure=true) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* try to open an XML retina parameters file to adjust current retina instance setup
|
||||
* => if the xml file does not exist, then default setup is applied
|
||||
* => warning, Exceptions are thrown if read XML file is not valid
|
||||
* @param newParameters : a parameters structures updated with the new target configuration
|
||||
* @param applyDefaultSetupOnFailure : set to true if an error must be thrown on error
|
||||
*/
|
||||
void setup(RetinaParameters newParameters) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* @return the current parameters setup
|
||||
*/
|
||||
struct RetinaParameters getParameters() CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* parameters setup display method
|
||||
* @return a string which contains formatted parameters information
|
||||
*/
|
||||
String printSetup() CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* write xml/yml formated parameters information
|
||||
* @rparam fs : the filename of the xml file that will be open and writen with formatted parameters information
|
||||
*/
|
||||
virtual void write( String fs ) const CV_OVERRIDE;
|
||||
|
||||
|
||||
/**
|
||||
* write xml/yml formated parameters information
|
||||
* @param fs : a cv::Filestorage object ready to be filled
|
||||
*/
|
||||
virtual void write( FileStorage& fs ) const CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* setup the OPL and IPL parvo channels (see biologocal model)
|
||||
* OPL is referred as Outer Plexiform Layer of the retina, it allows the spatio-temporal filtering which withens the spectrum and reduces spatio-temporal noise while attenuating global luminance (low frequency energy)
|
||||
* IPL parvo is the OPL next processing stage, it refers to Inner Plexiform layer of the retina, it allows high contours sensitivity in foveal vision.
|
||||
* for more informations, please have a look at the paper Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
* @param colorMode : specifies if (true) color is processed of not (false) to then processing gray level image
|
||||
* @param normaliseOutput : specifies if (true) output is rescaled between 0 and 255 of not (false)
|
||||
* @param photoreceptorsLocalAdaptationSensitivity: the photoreceptors sensitivity renage is 0-1 (more log compression effect when value increases)
|
||||
* @param photoreceptorsTemporalConstant: the time constant of the first order low pass filter of the photoreceptors, use it to cut high temporal frequencies (noise or fast motion), unit is frames, typical value is 1 frame
|
||||
* @param photoreceptorsSpatialConstant: the spatial constant of the first order low pass filter of the photoreceptors, use it to cut high spatial frequencies (noise or thick contours), unit is pixels, typical value is 1 pixel
|
||||
* @param horizontalCellsGain: gain of the horizontal cells network, if 0, then the mean value of the output is zero, if the parameter is near 1, then, the luminance is not filtered and is still reachable at the output, typicall value is 0
|
||||
* @param HcellsTemporalConstant: the time constant of the first order low pass filter of the horizontal cells, use it to cut low temporal frequencies (local luminance variations), unit is frames, typical value is 1 frame, as the photoreceptors
|
||||
* @param HcellsSpatialConstant: the spatial constant of the first order low pass filter of the horizontal cells, use it to cut low spatial frequencies (local luminance), unit is pixels, typical value is 5 pixel, this value is also used for local contrast computing when computing the local contrast adaptation at the ganglion cells level (Inner Plexiform Layer parvocellular channel model)
|
||||
* @param ganglionCellsSensitivity: the compression strengh of the ganglion cells local adaptation output, set a value between 160 and 250 for best results, a high value increases more the low value sensitivity... and the output saturates faster, recommended value: 230
|
||||
*/
|
||||
void setupOPLandIPLParvoChannel(const bool colorMode=true, const bool normaliseOutput = true, const float photoreceptorsLocalAdaptationSensitivity=0.7f, const float photoreceptorsTemporalConstant=0.5f, const float photoreceptorsSpatialConstant=0.53f, const float horizontalCellsGain=0.f, const float HcellsTemporalConstant=1.f, const float HcellsSpatialConstant=7.f, const float ganglionCellsSensitivity=0.7f) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* set parameters values for the Inner Plexiform Layer (IPL) magnocellular channel
|
||||
* this channel processes signals outpint from OPL processing stage in peripheral vision, it allows motion information enhancement. It is decorrelated from the details channel. See reference paper for more details.
|
||||
* @param normaliseOutput : specifies if (true) output is rescaled between 0 and 255 of not (false)
|
||||
* @param parasolCells_beta: the low pass filter gain used for local contrast adaptation at the IPL level of the retina (for ganglion cells local adaptation), typical value is 0
|
||||
* @param parasolCells_tau: the low pass filter time constant used for local contrast adaptation at the IPL level of the retina (for ganglion cells local adaptation), unit is frame, typical value is 0 (immediate response)
|
||||
* @param parasolCells_k: the low pass filter spatial constant used for local contrast adaptation at the IPL level of the retina (for ganglion cells local adaptation), unit is pixels, typical value is 5
|
||||
* @param amacrinCellsTemporalCutFrequency: the time constant of the first order high pass fiter of the magnocellular way (motion information channel), unit is frames, tipicall value is 5
|
||||
* @param V0CompressionParameter: the compression strengh of the ganglion cells local adaptation output, set a value between 160 and 250 for best results, a high value increases more the low value sensitivity... and the output saturates faster, recommended value: 200
|
||||
* @param localAdaptintegration_tau: specifies the temporal constant of the low pas filter involved in the computation of the local "motion mean" for the local adaptation computation
|
||||
* @param localAdaptintegration_k: specifies the spatial constant of the low pas filter involved in the computation of the local "motion mean" for the local adaptation computation
|
||||
*/
|
||||
void setupIPLMagnoChannel(const bool normaliseOutput = true, const float parasolCells_beta=0.f, const float parasolCells_tau=0.f, const float parasolCells_k=7.f, const float amacrinCellsTemporalCutFrequency=1.2f, const float V0CompressionParameter=0.95f, const float localAdaptintegration_tau=0.f, const float localAdaptintegration_k=7.f) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* method which allows retina to be applied on an input image, after run, encapsulated retina module is ready to deliver its outputs using dedicated acccessors, see getParvo and getMagno methods
|
||||
* @param inputImage : the input cv::Mat image to be processed, can be gray level or BGR coded in any format (from 8bit to 16bits)
|
||||
*/
|
||||
void run(InputArray inputImage) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* method that applies a luminance correction (initially High Dynamic Range (HDR) tone mapping) using only the 2 local adaptation stages of the retina parvo channel : photoreceptors level and ganlion cells level. Spatio temporal filtering is applied but limited to temporal smoothing and eventually high frequencies attenuation. This is a lighter method than the one available using the regular run method. It is then faster but it does not include complete temporal filtering nor retina spectral whitening. This is an adptation of the original still image HDR tone mapping algorithm of David Alleyson, Sabine Susstruck and Laurence Meylan's work, please cite:
|
||||
* -> Meylan L., Alleysson D., and Susstrunk S., A Model of Retinal Local Adaptation for the Tone Mapping of Color Filter Array Images, Journal of Optical Society of America, A, Vol. 24, N 9, September, 1st, 2007, pp. 2807-2816
|
||||
@param inputImage the input image to process RGB or gray levels
|
||||
@param outputToneMappedImage the output tone mapped image
|
||||
*/
|
||||
void applyFastToneMapping(InputArray inputImage, OutputArray outputToneMappedImage) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* accessor of the details channel of the retina (models foveal vision)
|
||||
* @param retinaOutput_parvo : the output buffer (reallocated if necessary), this output is rescaled for standard 8bits image processing use in OpenCV
|
||||
*/
|
||||
void getParvo(OutputArray retinaOutput_parvo) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* accessor of the details channel of the retina (models foveal vision)
|
||||
* @param retinaOutput_parvo : a cv::Mat header filled with the internal parvo buffer of the retina module. This output is the original retina filter model output, without any quantification or rescaling
|
||||
*/
|
||||
void getParvoRAW(OutputArray retinaOutput_parvo) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* accessor of the motion channel of the retina (models peripheral vision)
|
||||
* @param retinaOutput_magno : the output buffer (reallocated if necessary), this output is rescaled for standard 8bits image processing use in OpenCV
|
||||
*/
|
||||
void getMagno(OutputArray retinaOutput_magno) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* accessor of the motion channel of the retina (models peripheral vision)
|
||||
* @param retinaOutput_magno : a cv::Mat header filled with the internal retina magno buffer of the retina module. This output is the original retina filter model output, without any quantification or rescaling
|
||||
*/
|
||||
void getMagnoRAW(OutputArray retinaOutput_magno) CV_OVERRIDE;
|
||||
|
||||
// original API level data accessors : get buffers addresses from a Mat header, similar to getParvoRAW and getMagnoRAW...
|
||||
Mat getMagnoRAW() const CV_OVERRIDE;
|
||||
Mat getParvoRAW() const CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* activate color saturation as the final step of the color demultiplexing process
|
||||
* -> this saturation is a sigmoide function applied to each channel of the demultiplexed image.
|
||||
* @param saturateColors: boolean that activates color saturation (if true) or desactivate (if false)
|
||||
* @param colorSaturationValue: the saturation factor
|
||||
*/
|
||||
void setColorSaturation(const bool saturateColors=true, const float colorSaturationValue=4.0f) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* clear all retina buffers (equivalent to opening the eyes after a long period of eye close ;o)
|
||||
*/
|
||||
void clearBuffers() CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Activate/desactivate the Magnocellular pathway processing (motion information extraction), by default, it is activated
|
||||
* @param activate: true if Magnocellular output should be activated, false if not
|
||||
*/
|
||||
void activateMovingContoursProcessing(const bool activate) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Activate/desactivate the Parvocellular pathway processing (contours information extraction), by default, it is activated
|
||||
* @param activate: true if Parvocellular (contours information extraction) output should be activated, false if not
|
||||
*/
|
||||
void activateContoursProcessing(const bool activate) CV_OVERRIDE;
|
||||
private:
|
||||
|
||||
// Parameteres setup members
|
||||
RetinaParameters _retinaParameters; // structure of parameters
|
||||
|
||||
// Retina model related modules
|
||||
std::valarray<float> _inputBuffer; //!< buffer used to convert input cv::Mat to internal retina buffers format (valarrays)
|
||||
|
||||
// pointer to retina model
|
||||
cv::Ptr<RetinaFilter> _retinaFilter; //!< the pointer to the retina module, allocated with instance construction
|
||||
|
||||
//! private method called by constructors, gathers their parameters and use them in a unified way
|
||||
void _init(const Size inputSize, const bool colorMode, int colorSamplingMethod=RETINA_COLOR_BAYER, const bool useRetinaLogSampling=false, const float reductionFactor=1.0f, const float samplingStrength=10.0f);
|
||||
|
||||
/**
|
||||
* exports a valarray buffer outing from bioinspired objects to a cv::Mat in CV_8UC1 (gray level picture) or CV_8UC3 (color) format
|
||||
* @param grayMatrixToConvert the valarray to export to OpenCV
|
||||
* @param nbRows : the number of rows of the valarray flatten matrix
|
||||
* @param nbColumns : the number of rows of the valarray flatten matrix
|
||||
* @param colorMode : a flag which mentions if matrix is color (true) or graylevel (false)
|
||||
* @param outBuffer : the output matrix which is reallocated to satisfy Retina output buffer dimensions
|
||||
*/
|
||||
void _convertValarrayBuffer2cvMat(const std::valarray<float> &grayMatrixToConvert, const unsigned int nbRows, const unsigned int nbColumns, const bool colorMode, OutputArray outBuffer);
|
||||
|
||||
/**
|
||||
* convert a cv::Mat to a valarray buffer in float format
|
||||
* @param inputMatToConvert : the OpenCV cv::Mat that has to be converted to gray or RGB valarray buffer that will be processed by the retina model
|
||||
* @param outputValarrayMatrix : the output valarray
|
||||
* @return the input image color mode (color=true, gray levels=false)
|
||||
*/
|
||||
bool _convertCvMat2ValarrayBuffer(InputArray inputMatToConvert, std::valarray<float> &outputValarrayMatrix);
|
||||
|
||||
|
||||
bool _wasOCLRunCalled;
|
||||
#ifdef HAVE_OPENCL
|
||||
cv::Ptr<ocl::RetinaOCLImpl> _ocl_retina;
|
||||
|
||||
bool ocl_run(InputArray inputImage);
|
||||
bool ocl_getParvo(OutputArray retinaOutput_parvo);
|
||||
bool ocl_getMagno(OutputArray retinaOutput_magno);
|
||||
bool ocl_getParvoRAW(OutputArray retinaOutput_parvo);
|
||||
bool ocl_getMagnoRAW(OutputArray retinaOutput_magno);
|
||||
#endif
|
||||
};
|
||||
|
||||
// smart pointers allocation :
|
||||
Ptr<Retina> Retina::create(Size inputSize)
|
||||
{
|
||||
return makePtr<RetinaImpl>(inputSize);
|
||||
}
|
||||
|
||||
Ptr<Retina> Retina::create(Size inputSize, const bool colorMode, int colorSamplingMethod, const bool useRetinaLogSampling, const float reductionFactor, const float samplingStrength)
|
||||
{
|
||||
return makePtr<RetinaImpl>(inputSize, colorMode, colorSamplingMethod, useRetinaLogSampling, reductionFactor, samplingStrength);
|
||||
}
|
||||
|
||||
|
||||
// RetinaImpl code
|
||||
RetinaImpl::RetinaImpl(const cv::Size inputSz)
|
||||
{
|
||||
_init(inputSz, true, RETINA_COLOR_BAYER, false);
|
||||
#ifdef HAVE_OPENCL
|
||||
if (inputSz.width % 4 == 0 && cv::ocl::useOpenCL())
|
||||
_ocl_retina.reset(new ocl::RetinaOCLImpl(inputSz));
|
||||
#endif
|
||||
}
|
||||
|
||||
RetinaImpl::RetinaImpl(const cv::Size inputSz, const bool colorMode, int colorSamplingMethod, const bool useRetinaLogSampling, const float reductionFactor, const float samplingStrength)
|
||||
{
|
||||
_init(inputSz, colorMode, colorSamplingMethod, useRetinaLogSampling, reductionFactor, samplingStrength);
|
||||
#ifdef HAVE_OPENCL
|
||||
if (inputSz.width % 4 == 0 && !useRetinaLogSampling && cv::ocl::useOpenCL())
|
||||
_ocl_retina.reset(new ocl::RetinaOCLImpl(inputSz, colorMode, colorSamplingMethod,
|
||||
useRetinaLogSampling, reductionFactor, samplingStrength));
|
||||
#endif
|
||||
}
|
||||
|
||||
RetinaImpl::~RetinaImpl()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* retreive retina input buffer size
|
||||
*/
|
||||
Size RetinaImpl::getInputSize(){return cv::Size(_retinaFilter->getInputNBcolumns(), _retinaFilter->getInputNBrows());}
|
||||
|
||||
/**
|
||||
* retreive retina output buffer size
|
||||
*/
|
||||
Size RetinaImpl::getOutputSize(){return cv::Size(_retinaFilter->getOutputNBcolumns(), _retinaFilter->getOutputNBrows());}
|
||||
|
||||
|
||||
void RetinaImpl::setColorSaturation(const bool saturateColors, const float colorSaturationValue)
|
||||
{
|
||||
_retinaFilter->setColorSaturation(saturateColors, colorSaturationValue);
|
||||
}
|
||||
|
||||
struct RetinaParameters RetinaImpl::getParameters(){return _retinaParameters;}
|
||||
|
||||
void RetinaImpl::setup(String retinaParameterFile, const bool applyDefaultSetupOnFailure)
|
||||
{
|
||||
try
|
||||
{
|
||||
// opening retinaParameterFile in read mode
|
||||
cv::FileStorage fs(retinaParameterFile, cv::FileStorage::READ);
|
||||
setup(fs, applyDefaultSetupOnFailure);
|
||||
}
|
||||
catch(const Exception &e)
|
||||
{
|
||||
printf("Retina::setup: wrong/unappropriate xml parameter file : error report :`n=>%s\n", e.what());
|
||||
if (applyDefaultSetupOnFailure)
|
||||
{
|
||||
printf("Retina::setup: resetting retina with default parameters\n");
|
||||
setupOPLandIPLParvoChannel();
|
||||
setupIPLMagnoChannel();
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("=> keeping current parameters\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RetinaImpl::setup(cv::FileStorage &fs, const bool applyDefaultSetupOnFailure)
|
||||
{
|
||||
try
|
||||
{
|
||||
// read parameters file if it exists or apply default setup if asked for
|
||||
if (!fs.isOpened())
|
||||
{
|
||||
printf("Retina::setup: provided parameters file could not be open... skeeping configuration\n");
|
||||
return;
|
||||
// implicit else case : retinaParameterFile could be open (it exists at least)
|
||||
}
|
||||
// OPL and Parvo init first... update at the same time the parameters structure and the retina core
|
||||
cv::FileNode rootFn = fs.root(), currFn=rootFn["OPLandIPLparvo"];
|
||||
currFn["colorMode"]>>_retinaParameters.OPLandIplParvo.colorMode;
|
||||
currFn["normaliseOutput"]>>_retinaParameters.OPLandIplParvo.normaliseOutput;
|
||||
currFn["photoreceptorsLocalAdaptationSensitivity"]>>_retinaParameters.OPLandIplParvo.photoreceptorsLocalAdaptationSensitivity;
|
||||
currFn["photoreceptorsTemporalConstant"]>>_retinaParameters.OPLandIplParvo.photoreceptorsTemporalConstant;
|
||||
currFn["photoreceptorsSpatialConstant"]>>_retinaParameters.OPLandIplParvo.photoreceptorsSpatialConstant;
|
||||
currFn["horizontalCellsGain"]>>_retinaParameters.OPLandIplParvo.horizontalCellsGain;
|
||||
currFn["hcellsTemporalConstant"]>>_retinaParameters.OPLandIplParvo.hcellsTemporalConstant;
|
||||
currFn["hcellsSpatialConstant"]>>_retinaParameters.OPLandIplParvo.hcellsSpatialConstant;
|
||||
currFn["ganglionCellsSensitivity"]>>_retinaParameters.OPLandIplParvo.ganglionCellsSensitivity;
|
||||
setupOPLandIPLParvoChannel(_retinaParameters.OPLandIplParvo.colorMode, _retinaParameters.OPLandIplParvo.normaliseOutput, _retinaParameters.OPLandIplParvo.photoreceptorsLocalAdaptationSensitivity, _retinaParameters.OPLandIplParvo.photoreceptorsTemporalConstant, _retinaParameters.OPLandIplParvo.photoreceptorsSpatialConstant, _retinaParameters.OPLandIplParvo.horizontalCellsGain, _retinaParameters.OPLandIplParvo.hcellsTemporalConstant, _retinaParameters.OPLandIplParvo.hcellsSpatialConstant, _retinaParameters.OPLandIplParvo.ganglionCellsSensitivity);
|
||||
|
||||
// init retina IPL magno setup... update at the same time the parameters structure and the retina core
|
||||
currFn=rootFn["IPLmagno"];
|
||||
currFn["normaliseOutput"]>>_retinaParameters.IplMagno.normaliseOutput;
|
||||
currFn["parasolCells_beta"]>>_retinaParameters.IplMagno.parasolCells_beta;
|
||||
currFn["parasolCells_tau"]>>_retinaParameters.IplMagno.parasolCells_tau;
|
||||
currFn["parasolCells_k"]>>_retinaParameters.IplMagno.parasolCells_k;
|
||||
currFn["amacrinCellsTemporalCutFrequency"]>>_retinaParameters.IplMagno.amacrinCellsTemporalCutFrequency;
|
||||
currFn["V0CompressionParameter"]>>_retinaParameters.IplMagno.V0CompressionParameter;
|
||||
currFn["localAdaptintegration_tau"]>>_retinaParameters.IplMagno.localAdaptintegration_tau;
|
||||
currFn["localAdaptintegration_k"]>>_retinaParameters.IplMagno.localAdaptintegration_k;
|
||||
|
||||
setupIPLMagnoChannel(_retinaParameters.IplMagno.normaliseOutput, _retinaParameters.IplMagno.parasolCells_beta, _retinaParameters.IplMagno.parasolCells_tau, _retinaParameters.IplMagno.parasolCells_k, _retinaParameters.IplMagno.amacrinCellsTemporalCutFrequency,_retinaParameters.IplMagno.V0CompressionParameter, _retinaParameters.IplMagno.localAdaptintegration_tau, _retinaParameters.IplMagno.localAdaptintegration_k);
|
||||
|
||||
}
|
||||
catch(const Exception &e)
|
||||
{
|
||||
printf("RetinaImpl::setup: resetting retina with default parameters\n");
|
||||
if (applyDefaultSetupOnFailure)
|
||||
{
|
||||
setupOPLandIPLParvoChannel();
|
||||
setupIPLMagnoChannel();
|
||||
}
|
||||
printf("Retina::setup: wrong/unappropriate xml parameter file : error report :`n=>%s\n", e.what());
|
||||
printf("=> keeping current parameters\n");
|
||||
}
|
||||
}
|
||||
|
||||
void RetinaImpl::setup(RetinaParameters newConfiguration)
|
||||
{
|
||||
// simply copy structures
|
||||
memcpy(&_retinaParameters, &newConfiguration, sizeof(RetinaParameters));
|
||||
// apply setup
|
||||
setupOPLandIPLParvoChannel(_retinaParameters.OPLandIplParvo.colorMode, _retinaParameters.OPLandIplParvo.normaliseOutput, _retinaParameters.OPLandIplParvo.photoreceptorsLocalAdaptationSensitivity, _retinaParameters.OPLandIplParvo.photoreceptorsTemporalConstant, _retinaParameters.OPLandIplParvo.photoreceptorsSpatialConstant, _retinaParameters.OPLandIplParvo.horizontalCellsGain, _retinaParameters.OPLandIplParvo.hcellsTemporalConstant, _retinaParameters.OPLandIplParvo.hcellsSpatialConstant, _retinaParameters.OPLandIplParvo.ganglionCellsSensitivity);
|
||||
setupIPLMagnoChannel(_retinaParameters.IplMagno.normaliseOutput, _retinaParameters.IplMagno.parasolCells_beta, _retinaParameters.IplMagno.parasolCells_tau, _retinaParameters.IplMagno.parasolCells_k, _retinaParameters.IplMagno.amacrinCellsTemporalCutFrequency,_retinaParameters.IplMagno.V0CompressionParameter, _retinaParameters.IplMagno.localAdaptintegration_tau, _retinaParameters.IplMagno.localAdaptintegration_k);
|
||||
|
||||
}
|
||||
|
||||
String RetinaImpl::printSetup()
|
||||
{
|
||||
std::stringstream outmessage;
|
||||
|
||||
// displaying OPL and IPL parvo setup
|
||||
outmessage<<"Current Retina instance setup :"
|
||||
<<"\nOPLandIPLparvo"<<"{"
|
||||
<< "\n\t colorMode : " << _retinaParameters.OPLandIplParvo.colorMode
|
||||
<< "\n\t normalizeParvoOutput :" << _retinaParameters.OPLandIplParvo.normaliseOutput
|
||||
<< "\n\t photoreceptorsLocalAdaptationSensitivity : " << _retinaParameters.OPLandIplParvo.photoreceptorsLocalAdaptationSensitivity
|
||||
<< "\n\t photoreceptorsTemporalConstant : " << _retinaParameters.OPLandIplParvo.photoreceptorsTemporalConstant
|
||||
<< "\n\t photoreceptorsSpatialConstant : " << _retinaParameters.OPLandIplParvo.photoreceptorsSpatialConstant
|
||||
<< "\n\t horizontalCellsGain : " << _retinaParameters.OPLandIplParvo.horizontalCellsGain
|
||||
<< "\n\t hcellsTemporalConstant : " << _retinaParameters.OPLandIplParvo.hcellsTemporalConstant
|
||||
<< "\n\t hcellsSpatialConstant : " << _retinaParameters.OPLandIplParvo.hcellsSpatialConstant
|
||||
<< "\n\t parvoGanglionCellsSensitivity : " << _retinaParameters.OPLandIplParvo.ganglionCellsSensitivity
|
||||
<<"}\n";
|
||||
|
||||
// displaying IPL magno setup
|
||||
outmessage<<"Current Retina instance setup :"
|
||||
<<"\nIPLmagno"<<"{"
|
||||
<< "\n\t normaliseOutput : " << _retinaParameters.IplMagno.normaliseOutput
|
||||
<< "\n\t parasolCells_beta : " << _retinaParameters.IplMagno.parasolCells_beta
|
||||
<< "\n\t parasolCells_tau : " << _retinaParameters.IplMagno.parasolCells_tau
|
||||
<< "\n\t parasolCells_k : " << _retinaParameters.IplMagno.parasolCells_k
|
||||
<< "\n\t amacrinCellsTemporalCutFrequency : " << _retinaParameters.IplMagno.amacrinCellsTemporalCutFrequency
|
||||
<< "\n\t V0CompressionParameter : " << _retinaParameters.IplMagno.V0CompressionParameter
|
||||
<< "\n\t localAdaptintegration_tau : " << _retinaParameters.IplMagno.localAdaptintegration_tau
|
||||
<< "\n\t localAdaptintegration_k : " << _retinaParameters.IplMagno.localAdaptintegration_k
|
||||
<<"}";
|
||||
return outmessage.str().c_str();
|
||||
}
|
||||
|
||||
void RetinaImpl::write( String fs ) const
|
||||
{
|
||||
FileStorage parametersSaveFile(fs, cv::FileStorage::WRITE );
|
||||
write(parametersSaveFile);
|
||||
}
|
||||
|
||||
void RetinaImpl::write( FileStorage& fs ) const
|
||||
{
|
||||
if (!fs.isOpened())
|
||||
return; // basic error case
|
||||
fs<<"OPLandIPLparvo"<<"{";
|
||||
fs << "colorMode" << _retinaParameters.OPLandIplParvo.colorMode;
|
||||
fs << "normaliseOutput" << _retinaParameters.OPLandIplParvo.normaliseOutput;
|
||||
fs << "photoreceptorsLocalAdaptationSensitivity" << _retinaParameters.OPLandIplParvo.photoreceptorsLocalAdaptationSensitivity;
|
||||
fs << "photoreceptorsTemporalConstant" << _retinaParameters.OPLandIplParvo.photoreceptorsTemporalConstant;
|
||||
fs << "photoreceptorsSpatialConstant" << _retinaParameters.OPLandIplParvo.photoreceptorsSpatialConstant;
|
||||
fs << "horizontalCellsGain" << _retinaParameters.OPLandIplParvo.horizontalCellsGain;
|
||||
fs << "hcellsTemporalConstant" << _retinaParameters.OPLandIplParvo.hcellsTemporalConstant;
|
||||
fs << "hcellsSpatialConstant" << _retinaParameters.OPLandIplParvo.hcellsSpatialConstant;
|
||||
fs << "ganglionCellsSensitivity" << _retinaParameters.OPLandIplParvo.ganglionCellsSensitivity;
|
||||
fs << "}";
|
||||
fs<<"IPLmagno"<<"{";
|
||||
fs << "normaliseOutput" << _retinaParameters.IplMagno.normaliseOutput;
|
||||
fs << "parasolCells_beta" << _retinaParameters.IplMagno.parasolCells_beta;
|
||||
fs << "parasolCells_tau" << _retinaParameters.IplMagno.parasolCells_tau;
|
||||
fs << "parasolCells_k" << _retinaParameters.IplMagno.parasolCells_k;
|
||||
fs << "amacrinCellsTemporalCutFrequency" << _retinaParameters.IplMagno.amacrinCellsTemporalCutFrequency;
|
||||
fs << "V0CompressionParameter" << _retinaParameters.IplMagno.V0CompressionParameter;
|
||||
fs << "localAdaptintegration_tau" << _retinaParameters.IplMagno.localAdaptintegration_tau;
|
||||
fs << "localAdaptintegration_k" << _retinaParameters.IplMagno.localAdaptintegration_k;
|
||||
fs<<"}";
|
||||
}
|
||||
|
||||
void RetinaImpl::setupOPLandIPLParvoChannel(const bool colorMode, const bool normaliseOutput, const float photoreceptorsLocalAdaptationSensitivity, const float photoreceptorsTemporalConstant, const float photoreceptorsSpatialConstant, const float horizontalCellsGain, const float HcellsTemporalConstant, const float HcellsSpatialConstant, const float ganglionCellsSensitivity)
|
||||
{
|
||||
// retina core parameters setup
|
||||
_retinaFilter->setColorMode(colorMode);
|
||||
_retinaFilter->setPhotoreceptorsLocalAdaptationSensitivity(photoreceptorsLocalAdaptationSensitivity);
|
||||
_retinaFilter->setOPLandParvoParameters(0, photoreceptorsTemporalConstant, photoreceptorsSpatialConstant, horizontalCellsGain, HcellsTemporalConstant, HcellsSpatialConstant, ganglionCellsSensitivity);
|
||||
_retinaFilter->setParvoGanglionCellsLocalAdaptationSensitivity(ganglionCellsSensitivity);
|
||||
_retinaFilter->activateNormalizeParvoOutput_0_maxOutputValue(normaliseOutput);
|
||||
|
||||
// update parameters struture
|
||||
|
||||
_retinaParameters.OPLandIplParvo.colorMode = colorMode;
|
||||
_retinaParameters.OPLandIplParvo.normaliseOutput = normaliseOutput;
|
||||
_retinaParameters.OPLandIplParvo.photoreceptorsLocalAdaptationSensitivity = photoreceptorsLocalAdaptationSensitivity;
|
||||
_retinaParameters.OPLandIplParvo.photoreceptorsTemporalConstant = photoreceptorsTemporalConstant;
|
||||
_retinaParameters.OPLandIplParvo.photoreceptorsSpatialConstant = photoreceptorsSpatialConstant;
|
||||
_retinaParameters.OPLandIplParvo.horizontalCellsGain = horizontalCellsGain;
|
||||
_retinaParameters.OPLandIplParvo.hcellsTemporalConstant = HcellsTemporalConstant;
|
||||
_retinaParameters.OPLandIplParvo.hcellsSpatialConstant = HcellsSpatialConstant;
|
||||
_retinaParameters.OPLandIplParvo.ganglionCellsSensitivity = ganglionCellsSensitivity;
|
||||
|
||||
}
|
||||
|
||||
void RetinaImpl::setupIPLMagnoChannel(const bool normaliseOutput, const float parasolCells_beta, const float parasolCells_tau, const float parasolCells_k, const float amacrinCellsTemporalCutFrequency, const float V0CompressionParameter, const float localAdaptintegration_tau, const float localAdaptintegration_k)
|
||||
{
|
||||
|
||||
_retinaFilter->setMagnoCoefficientsTable(parasolCells_beta, parasolCells_tau, parasolCells_k, amacrinCellsTemporalCutFrequency, V0CompressionParameter, localAdaptintegration_tau, localAdaptintegration_k);
|
||||
_retinaFilter->activateNormalizeMagnoOutput_0_maxOutputValue(normaliseOutput);
|
||||
|
||||
// update parameters struture
|
||||
_retinaParameters.IplMagno.normaliseOutput = normaliseOutput;
|
||||
_retinaParameters.IplMagno.parasolCells_beta = parasolCells_beta;
|
||||
_retinaParameters.IplMagno.parasolCells_tau = parasolCells_tau;
|
||||
_retinaParameters.IplMagno.parasolCells_k = parasolCells_k;
|
||||
_retinaParameters.IplMagno.amacrinCellsTemporalCutFrequency = amacrinCellsTemporalCutFrequency;
|
||||
_retinaParameters.IplMagno.V0CompressionParameter = V0CompressionParameter;
|
||||
_retinaParameters.IplMagno.localAdaptintegration_tau = localAdaptintegration_tau;
|
||||
_retinaParameters.IplMagno.localAdaptintegration_k = localAdaptintegration_k;
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
bool RetinaImpl::ocl_run(InputArray inputMatToConvert)
|
||||
{
|
||||
_ocl_retina->run(inputMatToConvert);
|
||||
_wasOCLRunCalled = true;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void RetinaImpl::run(InputArray inputMatToConvert)
|
||||
{
|
||||
CV_OCL_RUN((_ocl_retina && inputMatToConvert.isUMat()), ocl_run(inputMatToConvert));
|
||||
|
||||
_wasOCLRunCalled = false;
|
||||
// first convert input image to the compatible format : std::valarray<float>
|
||||
const bool colorMode = _convertCvMat2ValarrayBuffer(inputMatToConvert.getMat(), _inputBuffer);
|
||||
// process the retina
|
||||
if (!_retinaFilter->runFilter(_inputBuffer, colorMode, false, _retinaParameters.OPLandIplParvo.colorMode && colorMode, false))
|
||||
CV_Error(Error::StsBadArg, "RetinaImpl cannot be applied, wrong input buffer size");
|
||||
}
|
||||
|
||||
void RetinaImpl::applyFastToneMapping(InputArray inputImage, OutputArray outputToneMappedImage)
|
||||
{
|
||||
// first convert input image to the compatible format :
|
||||
const bool colorMode = _convertCvMat2ValarrayBuffer(inputImage.getMat(), _inputBuffer);
|
||||
const unsigned int nbPixels=_retinaFilter->getOutputNBrows()*_retinaFilter->getOutputNBcolumns();
|
||||
|
||||
// process tone mapping
|
||||
if (colorMode)
|
||||
{
|
||||
std::valarray<float> imageOutput(nbPixels*3);
|
||||
_retinaFilter->runRGBToneMapping(_inputBuffer, imageOutput, true, _retinaParameters.OPLandIplParvo.photoreceptorsLocalAdaptationSensitivity, _retinaParameters.OPLandIplParvo.ganglionCellsSensitivity);
|
||||
_convertValarrayBuffer2cvMat(imageOutput, _retinaFilter->getOutputNBrows(), _retinaFilter->getOutputNBcolumns(), true, outputToneMappedImage);
|
||||
}else
|
||||
{
|
||||
std::valarray<float> imageOutput(nbPixels);
|
||||
_retinaFilter->runGrayToneMapping(_inputBuffer, imageOutput, _retinaParameters.OPLandIplParvo.photoreceptorsLocalAdaptationSensitivity, _retinaParameters.OPLandIplParvo.ganglionCellsSensitivity);
|
||||
_convertValarrayBuffer2cvMat(imageOutput, _retinaFilter->getOutputNBrows(), _retinaFilter->getOutputNBcolumns(), false, outputToneMappedImage);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
bool RetinaImpl::ocl_getParvo(OutputArray retinaOutput_parvo)
|
||||
{
|
||||
CV_Assert(_wasOCLRunCalled);
|
||||
_ocl_retina->getParvo(retinaOutput_parvo);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void RetinaImpl::getParvo(OutputArray retinaOutput_parvo)
|
||||
{
|
||||
if (_wasOCLRunCalled)
|
||||
{
|
||||
CV_OCL_RUN(true, ocl_getParvo(retinaOutput_parvo));
|
||||
CV_Error(Error::StsInternal, "");
|
||||
}
|
||||
|
||||
if (_retinaFilter->getColorMode())
|
||||
{
|
||||
// reallocate output buffer (if necessary)
|
||||
_convertValarrayBuffer2cvMat(_retinaFilter->getColorOutput(), _retinaFilter->getOutputNBrows(), _retinaFilter->getOutputNBcolumns(), true, retinaOutput_parvo);
|
||||
}else
|
||||
{
|
||||
// reallocate output buffer (if necessary)
|
||||
_convertValarrayBuffer2cvMat(_retinaFilter->getContours(), _retinaFilter->getOutputNBrows(), _retinaFilter->getOutputNBcolumns(), false, retinaOutput_parvo);
|
||||
}
|
||||
//retinaOutput_parvo/=255.0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
bool RetinaImpl::ocl_getMagno(OutputArray retinaOutput_magno)
|
||||
{
|
||||
CV_Assert(_wasOCLRunCalled);
|
||||
_ocl_retina->getMagno(retinaOutput_magno);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void RetinaImpl::getMagno(OutputArray retinaOutput_magno)
|
||||
{
|
||||
if (_wasOCLRunCalled)
|
||||
{
|
||||
CV_OCL_RUN(true, ocl_getMagno(retinaOutput_magno));
|
||||
CV_Error(Error::StsInternal, "");
|
||||
}
|
||||
|
||||
// reallocate output buffer (if necessary)
|
||||
_convertValarrayBuffer2cvMat(_retinaFilter->getMovingContours(), _retinaFilter->getOutputNBrows(), _retinaFilter->getOutputNBcolumns(), false, retinaOutput_magno);
|
||||
//retinaOutput_magno/=255.0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
bool RetinaImpl::ocl_getMagnoRAW(OutputArray magnoOutputBufferCopy)
|
||||
{
|
||||
CV_Assert(_wasOCLRunCalled);
|
||||
_ocl_retina->getMagnoRAW(magnoOutputBufferCopy);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// original API level data accessors : copy buffers if size matches, reallocate if required
|
||||
void RetinaImpl::getMagnoRAW(OutputArray magnoOutputBufferCopy)
|
||||
{
|
||||
if (_wasOCLRunCalled)
|
||||
{
|
||||
CV_OCL_RUN(true, ocl_getMagnoRAW(magnoOutputBufferCopy));
|
||||
CV_Error(Error::StsInternal, "");
|
||||
}
|
||||
|
||||
// get magno channel header
|
||||
const cv::Mat magnoChannel=cv::Mat(getMagnoRAW());
|
||||
// copy data
|
||||
magnoChannel.copyTo(magnoOutputBufferCopy);
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
bool RetinaImpl::ocl_getParvoRAW(OutputArray parvoOutputBufferCopy)
|
||||
{
|
||||
CV_Assert(_wasOCLRunCalled);
|
||||
_ocl_retina->getParvoRAW(parvoOutputBufferCopy);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void RetinaImpl::getParvoRAW(OutputArray parvoOutputBufferCopy)
|
||||
{
|
||||
if (_wasOCLRunCalled)
|
||||
{
|
||||
CV_OCL_RUN(true, ocl_getParvoRAW(parvoOutputBufferCopy));
|
||||
CV_Error(Error::StsInternal, "");
|
||||
}
|
||||
|
||||
// get parvo channel header
|
||||
const cv::Mat parvoChannel=cv::Mat(getParvoRAW());
|
||||
// copy data
|
||||
parvoChannel.copyTo(parvoOutputBufferCopy);
|
||||
}
|
||||
|
||||
// original API level data accessors : get buffers addresses...
|
||||
Mat RetinaImpl::getMagnoRAW() const {
|
||||
CV_Assert(!_wasOCLRunCalled);
|
||||
// create a cv::Mat header for the valarray
|
||||
return Mat((int)_retinaFilter->getMovingContours().size(),1, CV_32F, (void*)get_data(_retinaFilter->getMovingContours()));
|
||||
|
||||
}
|
||||
|
||||
Mat RetinaImpl::getParvoRAW() const {
|
||||
CV_Assert(!_wasOCLRunCalled);
|
||||
if (_retinaFilter->getColorMode()) // check if color mode is enabled
|
||||
{
|
||||
// create a cv::Mat table (for RGB planes as a single vector)
|
||||
return Mat((int)_retinaFilter->getColorOutput().size(), 1, CV_32F, (void*)get_data(_retinaFilter->getColorOutput()));
|
||||
}
|
||||
// otherwise, output is gray level
|
||||
// create a cv::Mat header for the valarray
|
||||
return Mat((int)_retinaFilter->getContours().size(), 1, CV_32F, (void*)get_data(_retinaFilter->getContours()));
|
||||
}
|
||||
|
||||
// private method called by constructors
|
||||
void RetinaImpl::_init(const cv::Size inputSz, const bool colorMode, int colorSamplingMethod, const bool useRetinaLogSampling, const float reductionFactor, const float samplingStrength)
|
||||
{
|
||||
_wasOCLRunCalled = false;
|
||||
// basic error check
|
||||
if (inputSz.height*inputSz.width <= 0)
|
||||
CV_Error(Error::StsBadArg, "Bad retina size setup : size height and with must be superior to zero");
|
||||
|
||||
unsigned int nbPixels=inputSz.height*inputSz.width;
|
||||
// resize buffers if size does not match
|
||||
_inputBuffer.resize(nbPixels*3); // buffer supports gray images but also 3 channels color buffers... (larger is better...)
|
||||
|
||||
// allocate the retina model
|
||||
_retinaFilter.reset(new RetinaFilter(inputSz.height, inputSz.width, colorMode, colorSamplingMethod, useRetinaLogSampling, reductionFactor, samplingStrength));
|
||||
|
||||
_retinaParameters.OPLandIplParvo.colorMode = colorMode;
|
||||
// prepare the default parameter XML file with default setup
|
||||
setup(_retinaParameters);
|
||||
|
||||
// init retina
|
||||
_retinaFilter->clearAllBuffers();
|
||||
}
|
||||
|
||||
void RetinaImpl::_convertValarrayBuffer2cvMat(const std::valarray<float> &grayMatrixToConvert, const unsigned int nbRows, const unsigned int nbColumns, const bool colorMode, OutputArray outBuffer)
|
||||
{
|
||||
// fill output buffer with the valarray buffer
|
||||
const float *valarrayPTR=get_data(grayMatrixToConvert);
|
||||
if (!colorMode)
|
||||
{
|
||||
outBuffer.create(cv::Size(nbColumns, nbRows), CV_8U);
|
||||
Mat outMat = outBuffer.getMat();
|
||||
for (unsigned int i=0;i<nbRows;++i)
|
||||
{
|
||||
for (unsigned int j=0;j<nbColumns;++j)
|
||||
{
|
||||
cv::Point2d pixel(j,i);
|
||||
outMat.at<unsigned char>(pixel)=(unsigned char)cvRound(*(valarrayPTR++));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned int nbPixels=nbColumns*nbRows;
|
||||
const unsigned int doubleNBpixels=nbColumns*nbRows*2;
|
||||
outBuffer.create(cv::Size(nbColumns, nbRows), CV_8UC3);
|
||||
Mat outMat = outBuffer.getMat();
|
||||
for (unsigned int i=0;i<nbRows;++i)
|
||||
{
|
||||
for (unsigned int j=0;j<nbColumns;++j,++valarrayPTR)
|
||||
{
|
||||
cv::Point2d pixel(j,i);
|
||||
cv::Vec3b pixelValues;
|
||||
pixelValues[2]=(unsigned char)cvRound(*(valarrayPTR));
|
||||
pixelValues[1]=(unsigned char)cvRound(*(valarrayPTR+nbPixels));
|
||||
pixelValues[0]=(unsigned char)cvRound(*(valarrayPTR+doubleNBpixels));
|
||||
|
||||
outMat.at<cv::Vec3b>(pixel)=pixelValues;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RetinaImpl::_convertCvMat2ValarrayBuffer(InputArray inputMat, std::valarray<float> &outputValarrayMatrix)
|
||||
{
|
||||
const Mat inputMatToConvert=inputMat.getMat();
|
||||
// first check input consistency
|
||||
if (inputMatToConvert.empty())
|
||||
CV_Error(Error::StsBadArg, "RetinaImpl cannot be applied, input buffer is empty");
|
||||
|
||||
// retreive color mode from image input
|
||||
int imageNumberOfChannels = inputMatToConvert.channels();
|
||||
|
||||
// convert to float AND fill the valarray buffer
|
||||
typedef float T; // define here the target pixel format, here, float
|
||||
const int dsttype = DataType<T>::depth; // output buffer is float format
|
||||
|
||||
const unsigned int nbPixels=inputMat.getMat().rows*inputMat.getMat().cols;
|
||||
const unsigned int doubleNBpixels=inputMat.getMat().rows*inputMat.getMat().cols*2;
|
||||
|
||||
if(imageNumberOfChannels==4)
|
||||
{
|
||||
// create a cv::Mat table (for RGBA planes)
|
||||
cv::Mat planes[4] =
|
||||
{
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[doubleNBpixels]),
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[nbPixels]),
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[0])
|
||||
};
|
||||
planes[3] = cv::Mat(inputMatToConvert.size(), dsttype); // last channel (alpha) does not point on the valarray (not usefull in our case)
|
||||
// split color cv::Mat in 4 planes... it fills valarray directely
|
||||
cv::split(Mat_<Vec<T, 4> >(inputMatToConvert), planes);
|
||||
}
|
||||
else if (imageNumberOfChannels==3)
|
||||
{
|
||||
// create a cv::Mat table (for RGB planes)
|
||||
cv::Mat planes[] =
|
||||
{
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[doubleNBpixels]),
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[nbPixels]),
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[0])
|
||||
};
|
||||
// split color cv::Mat in 3 planes... it fills valarray directely
|
||||
cv::split(cv::Mat_<Vec<T, 3> >(inputMatToConvert), planes);
|
||||
}
|
||||
else if(imageNumberOfChannels==1)
|
||||
{
|
||||
// create a cv::Mat header for the valarray
|
||||
cv::Mat dst(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[0]);
|
||||
inputMatToConvert.convertTo(dst, dsttype);
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsUnsupportedFormat, "input image must be single channel (gray levels), bgr format (color) or bgra (color with transparency which won't be considered");
|
||||
|
||||
return imageNumberOfChannels>1; // return bool : false for gray level image processing, true for color mode
|
||||
}
|
||||
|
||||
void RetinaImpl::clearBuffers()
|
||||
{
|
||||
#ifdef HAVE_OPENCL
|
||||
if (_ocl_retina)
|
||||
_ocl_retina->clearBuffers();
|
||||
#endif
|
||||
|
||||
_retinaFilter->clearAllBuffers();
|
||||
}
|
||||
|
||||
void RetinaImpl::activateMovingContoursProcessing(const bool activate) { _retinaFilter->activateMovingContoursProcessing(activate); }
|
||||
|
||||
void RetinaImpl::activateContoursProcessing(const bool activate) { _retinaFilter->activateContoursProcessing(activate); }
|
||||
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
@@ -0,0 +1,683 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2010-2013, Multicoreware, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Peng Xiao, pengxiao@multicorewareinc.com
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors as is and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OCL_RETINA_HPP__
|
||||
#define __OCL_RETINA_HPP__
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/bioinspired/retina.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
// please refer to c++ headers for API comments
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
namespace ocl
|
||||
{
|
||||
void normalizeGrayOutputCentredSigmoide(const float meanValue, const float sensitivity, UMat &in, UMat &out, const float maxValue = 255.f);
|
||||
void normalizeGrayOutput_0_maxOutputValue(UMat &inputOutputBuffer, const float maxOutputValue = 255.0);
|
||||
void normalizeGrayOutputNearZeroCentreredSigmoide(UMat &inputPicture, UMat &outputBuffer, const float sensitivity = 40, const float maxOutputValue = 255.0f);
|
||||
void centerReductImageLuminance(UMat &inputOutputBuffer);
|
||||
|
||||
class BasicRetinaFilter
|
||||
{
|
||||
public:
|
||||
BasicRetinaFilter(const unsigned int NBrows, const unsigned int NBcolumns, const unsigned int parametersListSize = 1, const bool useProgressiveFilter = false);
|
||||
~BasicRetinaFilter();
|
||||
inline void clearOutputBuffer()
|
||||
{
|
||||
_filterOutput = 0;
|
||||
}
|
||||
inline void clearSecondaryBuffer()
|
||||
{
|
||||
_localBuffer = 0;
|
||||
}
|
||||
inline void clearAllBuffers()
|
||||
{
|
||||
clearOutputBuffer();
|
||||
clearSecondaryBuffer();
|
||||
}
|
||||
void resize(const unsigned int NBrows, const unsigned int NBcolumns);
|
||||
const UMat &runFilter_LPfilter(const UMat &inputFrame, const unsigned int filterIndex = 0);
|
||||
void runFilter_LPfilter(const UMat &inputFrame, UMat &outputFrame, const unsigned int filterIndex = 0);
|
||||
void runFilter_LPfilter_Autonomous(UMat &inputOutputFrame, const unsigned int filterIndex = 0);
|
||||
const UMat &runFilter_LocalAdapdation(const UMat &inputOutputFrame, const UMat &localLuminance);
|
||||
void runFilter_LocalAdapdation(const UMat &inputFrame, const UMat &localLuminance, UMat &outputFrame);
|
||||
const UMat &runFilter_LocalAdapdation_autonomous(const UMat &inputFrame);
|
||||
void runFilter_LocalAdapdation_autonomous(const UMat &inputFrame, UMat &outputFrame);
|
||||
void setLPfilterParameters(const float beta, const float tau, const float k, const unsigned int filterIndex = 0);
|
||||
inline void setV0CompressionParameter(const float v0, const float maxInputValue, const float)
|
||||
{
|
||||
_v0 = v0 * maxInputValue;
|
||||
_localLuminanceFactor = v0;
|
||||
_localLuminanceAddon = maxInputValue * (1.0f - v0);
|
||||
_maxInputValue = maxInputValue;
|
||||
}
|
||||
inline void setV0CompressionParameter(const float v0, const float meanLuminance)
|
||||
{
|
||||
this->setV0CompressionParameter(v0, _maxInputValue, meanLuminance);
|
||||
}
|
||||
inline void setV0CompressionParameter(const float v0)
|
||||
{
|
||||
_v0 = v0 * _maxInputValue;
|
||||
_localLuminanceFactor = v0;
|
||||
_localLuminanceAddon = _maxInputValue * (1.0f - v0);
|
||||
}
|
||||
inline void setV0CompressionParameterToneMapping(const float v0, const float maxInputValue, const float meanLuminance = 128.0f)
|
||||
{
|
||||
_v0 = v0 * maxInputValue;
|
||||
_localLuminanceFactor = 1.0f;
|
||||
_localLuminanceAddon = meanLuminance * _v0;
|
||||
_maxInputValue = maxInputValue;
|
||||
}
|
||||
inline void updateCompressionParameter(const float meanLuminance)
|
||||
{
|
||||
_localLuminanceFactor = 1;
|
||||
_localLuminanceAddon = meanLuminance * _v0;
|
||||
}
|
||||
inline float getV0CompressionParameter()
|
||||
{
|
||||
return _v0 / _maxInputValue;
|
||||
}
|
||||
inline const UMat &getOutput() const
|
||||
{
|
||||
return _filterOutput;
|
||||
}
|
||||
inline unsigned int getNBrows()
|
||||
{
|
||||
return _filterOutput.rows;
|
||||
}
|
||||
inline unsigned int getNBcolumns()
|
||||
{
|
||||
return _filterOutput.cols;
|
||||
}
|
||||
inline unsigned int getNBpixels()
|
||||
{
|
||||
return _filterOutput.size().area();
|
||||
}
|
||||
inline void normalizeGrayOutput_0_maxOutputValue(const float maxValue)
|
||||
{
|
||||
ocl::normalizeGrayOutput_0_maxOutputValue(_filterOutput, maxValue);
|
||||
}
|
||||
inline void normalizeGrayOutputCentredSigmoide()
|
||||
{
|
||||
ocl::normalizeGrayOutputCentredSigmoide(0.0, 2.0, _filterOutput, _filterOutput);
|
||||
}
|
||||
inline void centerReductImageLuminance()
|
||||
{
|
||||
ocl::centerReductImageLuminance(_filterOutput);
|
||||
}
|
||||
inline float getMaxInputValue()
|
||||
{
|
||||
return this->_maxInputValue;
|
||||
}
|
||||
inline void setMaxInputValue(const float newMaxInputValue)
|
||||
{
|
||||
this->_maxInputValue = newMaxInputValue;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
int _NBrows;
|
||||
int _NBcols;
|
||||
unsigned int _halfNBrows;
|
||||
unsigned int _halfNBcolumns;
|
||||
|
||||
UMat _filterOutput;
|
||||
UMat _localBuffer;
|
||||
|
||||
std::valarray <float>_filteringCoeficientsTable;
|
||||
float _v0;
|
||||
float _maxInputValue;
|
||||
float _meanInputValue;
|
||||
float _localLuminanceFactor;
|
||||
float _localLuminanceAddon;
|
||||
|
||||
float _a;
|
||||
float _tau;
|
||||
float _gain;
|
||||
|
||||
void _spatiotemporalLPfilter(const UMat &inputFrame, UMat &LPfilterOutput, const unsigned int coefTableOffset = 0);
|
||||
void _spatiotemporalLPfilter_h(const UMat &inputFrame, UMat &LPfilterOutput, const unsigned int coefTableOffset = 0);
|
||||
void _spatiotemporalLPfilter_v(UMat &LPfilterOutput, const unsigned int multichannel = 0);
|
||||
float _squaringSpatiotemporalLPfilter(const UMat &inputFrame, UMat &outputFrame, const unsigned int filterIndex = 0);
|
||||
void _spatiotemporalLPfilter_Irregular(const UMat &inputFrame, UMat &outputFrame, const unsigned int filterIndex = 0);
|
||||
void _localSquaringSpatioTemporalLPfilter(const UMat &inputFrame, UMat &LPfilterOutput, const unsigned int *integrationAreas, const unsigned int filterIndex = 0);
|
||||
void _localLuminanceAdaptation(const UMat &inputFrame, const UMat &localLuminance, UMat &outputFrame, const bool updateLuminanceMean = true);
|
||||
void _localLuminanceAdaptation(UMat &inputOutputFrame, const UMat &localLuminance);
|
||||
void _localLuminanceAdaptationPosNegValues(const UMat &inputFrame, const UMat &localLuminance, float *outputFrame);
|
||||
void _horizontalCausalFilter_addInput(const UMat &inputFrame, UMat &outputFrame);
|
||||
void _verticalCausalFilter(UMat &outputFrame);
|
||||
void _verticalCausalFilter_multichannel(UMat &outputFrame);
|
||||
void _verticalCausalFilter_Irregular(UMat &outputFrame, const UMat &spatialConstantBuffer);
|
||||
};
|
||||
|
||||
class MagnoRetinaFilter: public BasicRetinaFilter
|
||||
{
|
||||
public:
|
||||
MagnoRetinaFilter(const unsigned int NBrows, const unsigned int NBcolumns);
|
||||
virtual ~MagnoRetinaFilter();
|
||||
void clearAllBuffers();
|
||||
void resize(const unsigned int NBrows, const unsigned int NBcolumns);
|
||||
void setCoefficientsTable(const float parasolCells_beta, const float parasolCells_tau, const float parasolCells_k, const float amacrinCellsTemporalCutFrequency, const float localAdaptIntegration_tau, const float localAdaptIntegration_k);
|
||||
|
||||
const UMat &runFilter(const UMat &OPL_ON, const UMat &OPL_OFF);
|
||||
|
||||
inline const UMat &getMagnoON() const
|
||||
{
|
||||
return _magnoXOutputON;
|
||||
}
|
||||
inline const UMat &getMagnoOFF() const
|
||||
{
|
||||
return _magnoXOutputOFF;
|
||||
}
|
||||
inline const UMat &getMagnoYsaturated() const
|
||||
{
|
||||
return _magnoYsaturated;
|
||||
}
|
||||
inline void normalizeGrayOutputNearZeroCentreredSigmoide()
|
||||
{
|
||||
ocl::normalizeGrayOutputNearZeroCentreredSigmoide(_magnoYOutput, _magnoYsaturated);
|
||||
}
|
||||
inline float getTemporalConstant()
|
||||
{
|
||||
return this->_filteringCoeficientsTable[2];
|
||||
}
|
||||
private:
|
||||
UMat _previousInput_ON;
|
||||
UMat _previousInput_OFF;
|
||||
UMat _amacrinCellsTempOutput_ON;
|
||||
UMat _amacrinCellsTempOutput_OFF;
|
||||
UMat _magnoXOutputON;
|
||||
UMat _magnoXOutputOFF;
|
||||
UMat _localProcessBufferON;
|
||||
UMat _localProcessBufferOFF;
|
||||
UMat _magnoYOutput;
|
||||
UMat _magnoYsaturated;
|
||||
|
||||
float _temporalCoefficient;
|
||||
void _amacrineCellsComputing(const UMat &OPL_ON, const UMat &OPL_OFF);
|
||||
};
|
||||
|
||||
class ParvoRetinaFilter: public BasicRetinaFilter
|
||||
{
|
||||
public:
|
||||
ParvoRetinaFilter(const unsigned int NBrows = 480, const unsigned int NBcolumns = 640);
|
||||
virtual ~ParvoRetinaFilter();
|
||||
void resize(const unsigned int NBrows, const unsigned int NBcolumns);
|
||||
void clearAllBuffers();
|
||||
void setOPLandParvoFiltersParameters(const float beta1, const float tau1, const float k1, const float beta2, const float tau2, const float k2);
|
||||
|
||||
inline void setGanglionCellsLocalAdaptationLPfilterParameters(const float tau, const float k)
|
||||
{
|
||||
BasicRetinaFilter::setLPfilterParameters(0, tau, k, 2);
|
||||
}
|
||||
const UMat &runFilter(const UMat &inputFrame, const bool useParvoOutput = true);
|
||||
|
||||
inline const UMat &getPhotoreceptorsLPfilteringOutput() const
|
||||
{
|
||||
return _photoreceptorsOutput;
|
||||
}
|
||||
|
||||
inline const UMat &getHorizontalCellsOutput() const
|
||||
{
|
||||
return _horizontalCellsOutput;
|
||||
}
|
||||
|
||||
inline const UMat &getParvoON() const
|
||||
{
|
||||
return _parvocellularOutputON;
|
||||
}
|
||||
|
||||
inline const UMat &getParvoOFF() const
|
||||
{
|
||||
return _parvocellularOutputOFF;
|
||||
}
|
||||
|
||||
inline const UMat &getBipolarCellsON() const
|
||||
{
|
||||
return _bipolarCellsOutputON;
|
||||
}
|
||||
|
||||
inline const UMat &getBipolarCellsOFF() const
|
||||
{
|
||||
return _bipolarCellsOutputOFF;
|
||||
}
|
||||
|
||||
inline float getPhotoreceptorsTemporalConstant()
|
||||
{
|
||||
return this->_filteringCoeficientsTable[2];
|
||||
}
|
||||
|
||||
inline float getHcellsTemporalConstant()
|
||||
{
|
||||
return this->_filteringCoeficientsTable[5];
|
||||
}
|
||||
private:
|
||||
UMat _photoreceptorsOutput;
|
||||
UMat _horizontalCellsOutput;
|
||||
UMat _parvocellularOutputON;
|
||||
UMat _parvocellularOutputOFF;
|
||||
UMat _bipolarCellsOutputON;
|
||||
UMat _bipolarCellsOutputOFF;
|
||||
UMat _localAdaptationOFF;
|
||||
UMat _localAdaptationON;
|
||||
UMat _parvocellularOutputONminusOFF;
|
||||
void _OPL_OnOffWaysComputing();
|
||||
};
|
||||
class RetinaColor: public BasicRetinaFilter
|
||||
{
|
||||
public:
|
||||
RetinaColor(const unsigned int NBrows, const unsigned int NBcolumns, const int samplingMethod = RETINA_COLOR_DIAGONAL);
|
||||
virtual ~RetinaColor();
|
||||
|
||||
void clearAllBuffers();
|
||||
void resize(const unsigned int NBrows, const unsigned int NBcolumns);
|
||||
inline void runColorMultiplexing(const UMat &inputRGBFrame)
|
||||
{
|
||||
runColorMultiplexing(inputRGBFrame, _multiplexedFrame);
|
||||
}
|
||||
void runColorMultiplexing(const UMat &demultiplexedInputFrame, UMat &multiplexedFrame);
|
||||
void runColorDemultiplexing(const UMat &multiplexedColorFrame, const bool adaptiveFiltering = false, const float maxInputValue = 255.0);
|
||||
|
||||
void setColorSaturation(const bool saturateColors = true, const float colorSaturationValue = 4.0)
|
||||
{
|
||||
_saturateColors = saturateColors;
|
||||
_colorSaturationValue = colorSaturationValue;
|
||||
}
|
||||
|
||||
void setChrominanceLPfilterParameters(const float beta, const float tau, const float k)
|
||||
{
|
||||
setLPfilterParameters(beta, tau, k);
|
||||
}
|
||||
|
||||
bool applyKrauskopfLMS2Acr1cr2Transform(UMat &result);
|
||||
bool applyLMS2LabTransform(UMat &result);
|
||||
inline const UMat &getMultiplexedFrame() const
|
||||
{
|
||||
return _multiplexedFrame;
|
||||
}
|
||||
|
||||
inline const UMat &getDemultiplexedColorFrame() const
|
||||
{
|
||||
return _demultiplexedColorFrame;
|
||||
}
|
||||
|
||||
inline const UMat &getLuminance() const
|
||||
{
|
||||
return _luminance;
|
||||
}
|
||||
inline const UMat &getChrominance() const
|
||||
{
|
||||
return _chrominance;
|
||||
}
|
||||
void clipRGBOutput_0_maxInputValue(UMat &inputOutputBuffer, const float maxOutputValue = 255.0);
|
||||
void normalizeRGBOutput_0_maxOutputValue(const float maxOutputValue = 255.0);
|
||||
inline void setDemultiplexedColorFrame(const UMat &demultiplexedImage)
|
||||
{
|
||||
_demultiplexedColorFrame = demultiplexedImage;
|
||||
}
|
||||
protected:
|
||||
inline unsigned int bayerSampleOffset(unsigned int index)
|
||||
{
|
||||
return index + ((index / getNBcolumns()) % 2) * getNBpixels() + ((index % getNBcolumns()) % 2) * getNBpixels();
|
||||
}
|
||||
inline Rect getROI(int idx)
|
||||
{
|
||||
return Rect(0, idx * _NBrows, _NBcols, _NBrows);
|
||||
}
|
||||
int _samplingMethod;
|
||||
bool _saturateColors;
|
||||
float _colorSaturationValue;
|
||||
UMat _luminance;
|
||||
UMat _multiplexedFrame;
|
||||
UMat _RGBmosaic;
|
||||
UMat _tempMultiplexedFrame;
|
||||
UMat _demultiplexedTempBuffer;
|
||||
UMat _demultiplexedColorFrame;
|
||||
UMat _chrominance;
|
||||
UMat _colorLocalDensity;
|
||||
UMat _imageGradient;
|
||||
|
||||
float _pR, _pG, _pB;
|
||||
bool _objectInit;
|
||||
|
||||
void _initColorSampling();
|
||||
void _adaptiveSpatialLPfilter_h(const UMat &inputFrame, const UMat &gradient, UMat &outputFrame);
|
||||
void _adaptiveSpatialLPfilter_v(const UMat &gradient, UMat &outputFrame);
|
||||
void _adaptiveHorizontalCausalFilter_addInput(const UMat &inputFrame, const UMat &gradient, UMat &outputFrame);
|
||||
void _computeGradient(const UMat &luminance, UMat &gradient);
|
||||
void _normalizeOutputs_0_maxOutputValue(void);
|
||||
void _applyImageColorSpaceConversion(const UMat &inputFrame, UMat &outputFrame, const float *transformTable);
|
||||
};
|
||||
class RetinaFilter
|
||||
{
|
||||
public:
|
||||
RetinaFilter(const unsigned int sizeRows, const unsigned int sizeColumns, const bool colorMode = false, const int samplingMethod = RETINA_COLOR_BAYER, const bool useRetinaLogSampling = false, const double reductionFactor = 1.0, const double samplingStrength = 10.0);
|
||||
~RetinaFilter();
|
||||
|
||||
void clearAllBuffers();
|
||||
void resize(const unsigned int NBrows, const unsigned int NBcolumns);
|
||||
bool checkInput(const UMat &input, const bool colorMode);
|
||||
bool runFilter(const UMat &imageInput, const bool useAdaptiveFiltering = true, const bool processRetinaParvoMagnoMapping = false, const bool useColorMode = false, const bool inputIsColorMultiplexed = false);
|
||||
|
||||
void setGlobalParameters(const float OPLspatialResponse1 = 0.7, const float OPLtemporalresponse1 = 1, const float OPLassymetryGain = 0, const float OPLspatialResponse2 = 5, const float OPLtemporalresponse2 = 1, const float LPfilterSpatialResponse = 5, const float LPfilterGain = 0, const float LPfilterTemporalresponse = 0, const float MovingContoursExtractorCoefficient = 5, const bool normalizeParvoOutput_0_maxOutputValue = false, const bool normalizeMagnoOutput_0_maxOutputValue = false, const float maxOutputValue = 255.0, const float maxInputValue = 255.0, const float meanValue = 128.0);
|
||||
|
||||
inline void setPhotoreceptorsLocalAdaptationSensitivity(const float V0CompressionParameter)
|
||||
{
|
||||
_photoreceptorsPrefilter.setV0CompressionParameter(1 - V0CompressionParameter);
|
||||
_setInitPeriodCount();
|
||||
}
|
||||
|
||||
inline void setParvoGanglionCellsLocalAdaptationSensitivity(const float V0CompressionParameter)
|
||||
{
|
||||
_ParvoRetinaFilter.setV0CompressionParameter(V0CompressionParameter);
|
||||
_setInitPeriodCount();
|
||||
}
|
||||
|
||||
inline void setGanglionCellsLocalAdaptationLPfilterParameters(const float spatialResponse, const float temporalResponse)
|
||||
{
|
||||
_ParvoRetinaFilter.setGanglionCellsLocalAdaptationLPfilterParameters(temporalResponse, spatialResponse);
|
||||
_setInitPeriodCount();
|
||||
};
|
||||
|
||||
inline void setMagnoGanglionCellsLocalAdaptationSensitivity(const float V0CompressionParameter)
|
||||
{
|
||||
_MagnoRetinaFilter.setV0CompressionParameter(V0CompressionParameter);
|
||||
_setInitPeriodCount();
|
||||
}
|
||||
|
||||
void setOPLandParvoParameters(const float beta1, const float tau1, const float k1, const float beta2, const float tau2, const float k2, const float V0CompressionParameter)
|
||||
{
|
||||
_ParvoRetinaFilter.setOPLandParvoFiltersParameters(beta1, tau1, k1, beta2, tau2, k2);
|
||||
_ParvoRetinaFilter.setV0CompressionParameter(V0CompressionParameter);
|
||||
_setInitPeriodCount();
|
||||
}
|
||||
|
||||
void setMagnoCoefficientsTable(const float parasolCells_beta, const float parasolCells_tau, const float parasolCells_k, const float amacrinCellsTemporalCutFrequency, const float V0CompressionParameter, const float localAdaptintegration_tau, const float localAdaptintegration_k)
|
||||
{
|
||||
_MagnoRetinaFilter.setCoefficientsTable(parasolCells_beta, parasolCells_tau, parasolCells_k, amacrinCellsTemporalCutFrequency, localAdaptintegration_tau, localAdaptintegration_k);
|
||||
_MagnoRetinaFilter.setV0CompressionParameter(V0CompressionParameter);
|
||||
_setInitPeriodCount();
|
||||
}
|
||||
|
||||
inline void activateNormalizeParvoOutput_0_maxOutputValue(const bool normalizeParvoOutput_0_maxOutputValue)
|
||||
{
|
||||
_normalizeParvoOutput_0_maxOutputValue = normalizeParvoOutput_0_maxOutputValue;
|
||||
}
|
||||
|
||||
inline void activateNormalizeMagnoOutput_0_maxOutputValue(const bool normalizeMagnoOutput_0_maxOutputValue)
|
||||
{
|
||||
_normalizeMagnoOutput_0_maxOutputValue = normalizeMagnoOutput_0_maxOutputValue;
|
||||
}
|
||||
|
||||
inline void setMaxOutputValue(const float maxOutputValue)
|
||||
{
|
||||
_maxOutputValue = maxOutputValue;
|
||||
}
|
||||
|
||||
void setColorMode(const bool desiredColorMode)
|
||||
{
|
||||
_useColorMode = desiredColorMode;
|
||||
}
|
||||
inline void setColorSaturation(const bool saturateColors = true, const float colorSaturationValue = 4.0)
|
||||
{
|
||||
_colorEngine.setColorSaturation(saturateColors, colorSaturationValue);
|
||||
}
|
||||
inline const UMat &getLocalAdaptation() const
|
||||
{
|
||||
return _photoreceptorsPrefilter.getOutput();
|
||||
}
|
||||
inline const UMat &getPhotoreceptors() const
|
||||
{
|
||||
return _ParvoRetinaFilter.getPhotoreceptorsLPfilteringOutput();
|
||||
}
|
||||
|
||||
inline const UMat &getHorizontalCells() const
|
||||
{
|
||||
return _ParvoRetinaFilter.getHorizontalCellsOutput();
|
||||
}
|
||||
inline bool areContoursProcessed()
|
||||
{
|
||||
return _useParvoOutput;
|
||||
}
|
||||
bool getParvoFoveaResponse(UMat &parvoFovealResponse);
|
||||
inline void activateContoursProcessing(const bool useParvoOutput)
|
||||
{
|
||||
_useParvoOutput = useParvoOutput;
|
||||
}
|
||||
|
||||
const UMat &getContours();
|
||||
|
||||
inline const UMat &getContoursON() const
|
||||
{
|
||||
return _ParvoRetinaFilter.getParvoON();
|
||||
}
|
||||
|
||||
inline const UMat &getContoursOFF() const
|
||||
{
|
||||
return _ParvoRetinaFilter.getParvoOFF();
|
||||
}
|
||||
|
||||
inline bool areMovingContoursProcessed()
|
||||
{
|
||||
return _useMagnoOutput;
|
||||
}
|
||||
|
||||
inline void activateMovingContoursProcessing(const bool useMagnoOutput)
|
||||
{
|
||||
_useMagnoOutput = useMagnoOutput;
|
||||
}
|
||||
|
||||
inline const UMat &getMovingContours() const
|
||||
{
|
||||
return _MagnoRetinaFilter.getOutput();
|
||||
}
|
||||
|
||||
inline const UMat &getMovingContoursSaturated() const
|
||||
{
|
||||
return _MagnoRetinaFilter.getMagnoYsaturated();
|
||||
}
|
||||
|
||||
inline const UMat &getMovingContoursON() const
|
||||
{
|
||||
return _MagnoRetinaFilter.getMagnoON();
|
||||
}
|
||||
|
||||
inline const UMat &getMovingContoursOFF() const
|
||||
{
|
||||
return _MagnoRetinaFilter.getMagnoOFF();
|
||||
}
|
||||
|
||||
inline const UMat &getRetinaParvoMagnoMappedOutput() const
|
||||
{
|
||||
return _retinaParvoMagnoMappedFrame;
|
||||
}
|
||||
|
||||
inline const UMat &getParvoContoursChannel() const
|
||||
{
|
||||
return _colorEngine.getLuminance();
|
||||
}
|
||||
|
||||
inline const UMat &getParvoChrominance() const
|
||||
{
|
||||
return _colorEngine.getChrominance();
|
||||
}
|
||||
inline const UMat &getColorOutput() const
|
||||
{
|
||||
return _colorEngine.getDemultiplexedColorFrame();
|
||||
}
|
||||
|
||||
inline bool isColorMode()
|
||||
{
|
||||
return _useColorMode;
|
||||
}
|
||||
bool getColorMode()
|
||||
{
|
||||
return _useColorMode;
|
||||
}
|
||||
|
||||
inline bool isInitTransitionDone()
|
||||
{
|
||||
if (_ellapsedFramesSinceLastReset < _globalTemporalConstant)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
inline float getRetinaSamplingBackProjection(const float projectedRadiusLength)
|
||||
{
|
||||
return projectedRadiusLength;
|
||||
}
|
||||
|
||||
inline unsigned int getInputNBrows()
|
||||
{
|
||||
return _photoreceptorsPrefilter.getNBrows();
|
||||
}
|
||||
|
||||
inline unsigned int getInputNBcolumns()
|
||||
{
|
||||
return _photoreceptorsPrefilter.getNBcolumns();
|
||||
}
|
||||
|
||||
inline unsigned int getInputNBpixels()
|
||||
{
|
||||
return _photoreceptorsPrefilter.getNBpixels();
|
||||
}
|
||||
|
||||
inline unsigned int getOutputNBrows()
|
||||
{
|
||||
return _photoreceptorsPrefilter.getNBrows();
|
||||
}
|
||||
|
||||
inline unsigned int getOutputNBcolumns()
|
||||
{
|
||||
return _photoreceptorsPrefilter.getNBcolumns();
|
||||
}
|
||||
|
||||
inline unsigned int getOutputNBpixels()
|
||||
{
|
||||
return _photoreceptorsPrefilter.getNBpixels();
|
||||
}
|
||||
private:
|
||||
bool _useParvoOutput;
|
||||
bool _useMagnoOutput;
|
||||
|
||||
unsigned int _ellapsedFramesSinceLastReset;
|
||||
unsigned int _globalTemporalConstant;
|
||||
|
||||
UMat _retinaParvoMagnoMappedFrame;
|
||||
BasicRetinaFilter _photoreceptorsPrefilter;
|
||||
ParvoRetinaFilter _ParvoRetinaFilter;
|
||||
MagnoRetinaFilter _MagnoRetinaFilter;
|
||||
RetinaColor _colorEngine;
|
||||
|
||||
bool _useMinimalMemoryForToneMappingONLY;
|
||||
bool _normalizeParvoOutput_0_maxOutputValue;
|
||||
bool _normalizeMagnoOutput_0_maxOutputValue;
|
||||
float _maxOutputValue;
|
||||
bool _useColorMode;
|
||||
|
||||
void _setInitPeriodCount();
|
||||
void _processRetinaParvoMagnoMapping();
|
||||
void _runGrayToneMapping(const UMat &grayImageInput, UMat &grayImageOutput , const float PhotoreceptorsCompression = 0.6, const float ganglionCellsCompression = 0.6);
|
||||
};
|
||||
|
||||
class RetinaOCLImpl CV_FINAL : public Retina
|
||||
{
|
||||
public:
|
||||
RetinaOCLImpl(Size getInputSize);
|
||||
RetinaOCLImpl(Size getInputSize, const bool colorMode, int colorSamplingMethod = RETINA_COLOR_BAYER, const bool useRetinaLogSampling = false, const double reductionFactor = 1.0, const double samplingStrength = 10.0);
|
||||
virtual ~RetinaOCLImpl() CV_OVERRIDE;
|
||||
|
||||
Size getInputSize() CV_OVERRIDE;
|
||||
Size getOutputSize() CV_OVERRIDE;
|
||||
|
||||
void setup(String retinaParameterFile = "", const bool applyDefaultSetupOnFailure = true) CV_OVERRIDE;
|
||||
void setup(cv::FileStorage &fs, const bool applyDefaultSetupOnFailure = true) CV_OVERRIDE;
|
||||
void setup(RetinaParameters newParameters) CV_OVERRIDE;
|
||||
|
||||
RetinaParameters getParameters() CV_OVERRIDE;
|
||||
|
||||
String printSetup() CV_OVERRIDE;
|
||||
virtual void write(String fs) const CV_OVERRIDE;
|
||||
virtual void write(FileStorage& fs) const CV_OVERRIDE;
|
||||
|
||||
void setupOPLandIPLParvoChannel(const bool colorMode = true, const bool normaliseOutput = true, const float photoreceptorsLocalAdaptationSensitivity = 0.7, const float photoreceptorsTemporalConstant = 0.5, const float photoreceptorsSpatialConstant = 0.53, const float horizontalCellsGain = 0, const float HcellsTemporalConstant = 1, const float HcellsSpatialConstant = 7, const float ganglionCellsSensitivity = 0.7) CV_OVERRIDE;
|
||||
void setupIPLMagnoChannel(const bool normaliseOutput = true, const float parasolCells_beta = 0, const float parasolCells_tau = 0, const float parasolCells_k = 7, const float amacrinCellsTemporalCutFrequency = 1.2, const float V0CompressionParameter = 0.95, const float localAdaptintegration_tau = 0, const float localAdaptintegration_k = 7) CV_OVERRIDE;
|
||||
|
||||
void run(InputArray inputImage) CV_OVERRIDE;
|
||||
void getParvo(OutputArray retinaOutput_parvo) CV_OVERRIDE;
|
||||
void getMagno(OutputArray retinaOutput_magno) CV_OVERRIDE;
|
||||
|
||||
void setColorSaturation(const bool saturateColors = true, const float colorSaturationValue = 4.0) CV_OVERRIDE;
|
||||
void clearBuffers() CV_OVERRIDE;
|
||||
void activateMovingContoursProcessing(const bool activate) CV_OVERRIDE;
|
||||
void activateContoursProcessing(const bool activate) CV_OVERRIDE;
|
||||
|
||||
// unimplemented interfaces:
|
||||
void applyFastToneMapping(InputArray /*inputImage*/, OutputArray /*outputToneMappedImage*/) CV_OVERRIDE;
|
||||
void getParvoRAW(OutputArray /*retinaOutput_parvo*/) CV_OVERRIDE;
|
||||
void getMagnoRAW(OutputArray /*retinaOutput_magno*/) CV_OVERRIDE;
|
||||
Mat getMagnoRAW() const CV_OVERRIDE;
|
||||
Mat getParvoRAW() const CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
RetinaParameters _retinaParameters;
|
||||
UMat _inputBuffer;
|
||||
cv::Ptr<RetinaFilter> _retinaFilter;
|
||||
bool convertToColorPlanes(const UMat& input, UMat &output);
|
||||
void convertToInterleaved(const UMat& input, bool colorMode, UMat &output);
|
||||
void _init(const Size getInputSize, const bool colorMode, int colorSamplingMethod = RETINA_COLOR_BAYER, const bool useRetinaLogSampling = false, const double reductionFactor = 1.0, const double samplingStrength = 10.0);
|
||||
};
|
||||
|
||||
} /* namespace ocl */
|
||||
} /* namespace bioinspired */
|
||||
} /* namespace cv */
|
||||
|
||||
#endif /* HAVE_OPENCL */
|
||||
#endif /* __OCL_RETINA_HPP__ */
|
||||
@@ -0,0 +1,725 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2011
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "retinacolor.hpp"
|
||||
|
||||
// @author Alexandre BENOIT, benoit.alexandre.vision@gmail.com, LISTIC : www.listic.univ-savoie.fr, Gipsa-Lab, France: www.gipsa-lab.inpg.fr/
|
||||
|
||||
#include <iostream>
|
||||
#include <ctime>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
// init static values
|
||||
static float _LMStoACr1Cr2[]={1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -0.5, -0.5, 1.0};
|
||||
//static double _ACr1Cr2toLMS[]={0.5, 0.5, 0.0, 0.5, -0.5, 0.0, 0.5, 0.0, 1.0};
|
||||
static float _LMStoLab[]={0.5774f, 0.5774f, 0.5774f, 0.4082f, 0.4082f, -0.8165f, 0.7071f, -0.7071f, 0.f};
|
||||
|
||||
// constructor/desctructor
|
||||
RetinaColor::RetinaColor(const unsigned int NBrows, const unsigned int NBcolumns, const int samplingMethod)
|
||||
:BasicRetinaFilter(NBrows, NBcolumns, 3),
|
||||
_colorSampling(NBrows*NBcolumns),
|
||||
_RGBmosaic(NBrows*NBcolumns*3),
|
||||
_tempMultiplexedFrame(NBrows*NBcolumns),
|
||||
_demultiplexedTempBuffer(NBrows*NBcolumns*3),
|
||||
_demultiplexedColorFrame(NBrows*NBcolumns*3),
|
||||
_chrominance(NBrows*NBcolumns*3),
|
||||
_colorLocalDensity(NBrows*NBcolumns*3),
|
||||
_imageGradient(NBrows*NBcolumns*2)
|
||||
{
|
||||
// link to parent buffers (let's recycle !)
|
||||
_luminance=&_filterOutput;
|
||||
_multiplexedFrame=&_localBuffer;
|
||||
|
||||
_objectInit=false;
|
||||
_samplingMethod=samplingMethod;
|
||||
_saturateColors=false;
|
||||
_colorSaturationValue=4.0;
|
||||
|
||||
// set default spatio-temporal filter parameters
|
||||
setLPfilterParameters(0.0, 0.0, 1.5);
|
||||
setLPfilterParameters(0.0, 0.0, 10.5, 1);// for the low pass filter dedicated to contours energy extraction (demultiplexing process)
|
||||
setLPfilterParameters(0.f, 0.f, 0.9f, 2);
|
||||
|
||||
// init default value on image Gradient
|
||||
_imageGradient=0.57f;
|
||||
|
||||
// init color sampling map
|
||||
_initColorSampling();
|
||||
|
||||
// flush all buffers
|
||||
clearAllBuffers();
|
||||
}
|
||||
|
||||
RetinaColor::~RetinaColor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* function that clears all buffers of the object
|
||||
*/
|
||||
void RetinaColor::clearAllBuffers()
|
||||
{
|
||||
BasicRetinaFilter::clearAllBuffers();
|
||||
_tempMultiplexedFrame=0.f;
|
||||
_demultiplexedTempBuffer=0.f;
|
||||
|
||||
_demultiplexedColorFrame=0.f;
|
||||
_chrominance=0.f;
|
||||
_imageGradient=0.57f;
|
||||
}
|
||||
|
||||
/**
|
||||
* resize retina color filter object (resize all allocated buffers)
|
||||
* @param NBrows: the new height size
|
||||
* @param NBcolumns: the new width size
|
||||
*/
|
||||
void RetinaColor::resize(const unsigned int NBrows, const unsigned int NBcolumns)
|
||||
{
|
||||
BasicRetinaFilter::clearAllBuffers();
|
||||
_colorSampling.resize(NBrows*NBcolumns);
|
||||
_RGBmosaic.resize(NBrows*NBcolumns*3);
|
||||
_tempMultiplexedFrame.resize(NBrows*NBcolumns);
|
||||
_demultiplexedTempBuffer.resize(NBrows*NBcolumns*3);
|
||||
_demultiplexedColorFrame.resize(NBrows*NBcolumns*3);
|
||||
_chrominance.resize(NBrows*NBcolumns*3);
|
||||
_colorLocalDensity.resize(NBrows*NBcolumns*3);
|
||||
_imageGradient.resize(NBrows*NBcolumns*2);
|
||||
|
||||
// link to parent buffers (let's recycle !)
|
||||
_luminance=&_filterOutput;
|
||||
_multiplexedFrame=&_localBuffer;
|
||||
|
||||
// init color sampling map
|
||||
_initColorSampling();
|
||||
|
||||
// clean buffers
|
||||
clearAllBuffers();
|
||||
}
|
||||
|
||||
|
||||
void RetinaColor::_initColorSampling()
|
||||
{
|
||||
|
||||
// filling the conversion table for multiplexed <=> demultiplexed frame
|
||||
srand((unsigned)time(NULL));
|
||||
|
||||
// preInit cones probabilities
|
||||
_pR=_pB=_pG=0;
|
||||
switch (_samplingMethod)
|
||||
{
|
||||
case RETINA_COLOR_RANDOM:
|
||||
for (unsigned int index=0 ; index<this->getNBpixels(); ++index)
|
||||
{
|
||||
|
||||
// random RGB sampling
|
||||
unsigned int colorIndex=rand()%24;
|
||||
|
||||
if (colorIndex<8){
|
||||
colorIndex=0;
|
||||
|
||||
++_pR;
|
||||
}else
|
||||
{
|
||||
if (colorIndex<21){
|
||||
colorIndex=1;
|
||||
++_pG;
|
||||
}else{
|
||||
colorIndex=2;
|
||||
++_pB;
|
||||
}
|
||||
}
|
||||
_colorSampling[index] = colorIndex*this->getNBpixels()+index;
|
||||
}
|
||||
_pR/=(float)this->getNBpixels();
|
||||
_pG/=(float)this->getNBpixels();
|
||||
_pB/=(float)this->getNBpixels();
|
||||
std::cout<<"Color channels proportions: pR, pG, pB= "<<_pR<<", "<<_pG<<", "<<_pB<<", "<<std::endl;
|
||||
break;
|
||||
case RETINA_COLOR_DIAGONAL:
|
||||
for (unsigned int index=0 ; index<this->getNBpixels(); ++index)
|
||||
{
|
||||
_colorSampling[index] = index+((index%3+(index%_filterOutput.getNBcolumns()))%3)*_filterOutput.getNBpixels();
|
||||
}
|
||||
_pR=_pB=_pG=1.f/3;
|
||||
break;
|
||||
case RETINA_COLOR_BAYER: // default sets bayer sampling
|
||||
for (unsigned int index=0 ; index<_filterOutput.getNBpixels(); ++index)
|
||||
{
|
||||
//First line: R G R G
|
||||
_colorSampling[index] = index+((index/_filterOutput.getNBcolumns())%2)*_filterOutput.getNBpixels()+((index%_filterOutput.getNBcolumns())%2)*_filterOutput.getNBpixels();
|
||||
//First line: G R G R
|
||||
//_colorSampling[index] = 3*index+((index/_filterOutput.getNBcolumns())%2)+((index%_filterOutput.getNBcolumns()+1)%2);
|
||||
}
|
||||
_pR=_pB=0.25;
|
||||
_pG=0.5;
|
||||
break;
|
||||
default:
|
||||
#ifdef RETINACOLORDEBUG
|
||||
std::cerr<<"RetinaColor::No or wrong color sampling method, skeeping"<<std::endl;
|
||||
#endif
|
||||
return;
|
||||
break;//.. not useful, yes
|
||||
|
||||
}
|
||||
// feeling the mosaic buffer:
|
||||
_RGBmosaic=0;
|
||||
for (unsigned int index=0 ; index<_filterOutput.getNBpixels(); ++index)
|
||||
// the RGB _RGBmosaic buffer contains 1 where the pixel corresponds to a sampled color
|
||||
_RGBmosaic[_colorSampling[index]]=1.0;
|
||||
|
||||
// computing photoreceptors local density
|
||||
_spatiotemporalLPfilter(&_RGBmosaic[0], &_colorLocalDensity[0]);
|
||||
_spatiotemporalLPfilter(&_RGBmosaic[0]+_filterOutput.getNBpixels(), &_colorLocalDensity[0]+_filterOutput.getNBpixels());
|
||||
_spatiotemporalLPfilter(&_RGBmosaic[0]+_filterOutput.getDoubleNBpixels(), &_colorLocalDensity[0]+_filterOutput.getDoubleNBpixels());
|
||||
unsigned int maxNBpixels=3*_filterOutput.getNBpixels();
|
||||
float *colorLocalDensityPTR=&_colorLocalDensity[0];
|
||||
for (unsigned int i=0;i<maxNBpixels;++i, ++colorLocalDensityPTR)
|
||||
*colorLocalDensityPTR=1.f/ *colorLocalDensityPTR;
|
||||
|
||||
#ifdef RETINACOLORDEBUG
|
||||
std::cout<<"INIT _colorLocalDensity max, min: "<<_colorLocalDensity.max()<<", "<<_colorLocalDensity.min()<<std::endl;
|
||||
#endif
|
||||
// end of the init step
|
||||
_objectInit=true;
|
||||
}
|
||||
|
||||
// public functions
|
||||
|
||||
void RetinaColor::runColorDemultiplexing(const std::valarray<float> &multiplexedColorFrame, const bool adaptiveFiltering, const float maxInputValue)
|
||||
{
|
||||
// demultiplex the grey frame to RGB frame
|
||||
// -> first set demultiplexed frame to 0
|
||||
_demultiplexedTempBuffer=0;
|
||||
// -> demultiplex process
|
||||
unsigned int *colorSamplingPRT=&_colorSampling[0];
|
||||
const float *multiplexedColorFramePtr=get_data(multiplexedColorFrame);
|
||||
for (unsigned int indexa=0; indexa<_filterOutput.getNBpixels() ; ++indexa)
|
||||
_demultiplexedTempBuffer[*(colorSamplingPRT++)]=*(multiplexedColorFramePtr++);
|
||||
|
||||
// interpolate the demultiplexed frame depending on the color sampling method
|
||||
if (!adaptiveFiltering)
|
||||
_interpolateImageDemultiplexedImage(&_demultiplexedTempBuffer[0]);
|
||||
|
||||
// low pass filtering the demultiplexed frame
|
||||
_spatiotemporalLPfilter(&_demultiplexedTempBuffer[0], &_chrominance[0]);
|
||||
_spatiotemporalLPfilter(&_demultiplexedTempBuffer[0]+_filterOutput.getNBpixels(), &_chrominance[0]+_filterOutput.getNBpixels());
|
||||
_spatiotemporalLPfilter(&_demultiplexedTempBuffer[0]+_filterOutput.getDoubleNBpixels(), &_chrominance[0]+_filterOutput.getDoubleNBpixels());
|
||||
|
||||
/*if (_samplingMethod=BAYER)
|
||||
{
|
||||
_applyRIFfilter(_chrominance, _chrominance);
|
||||
_applyRIFfilter(_chrominance+_filterOutput.getNBpixels(), _chrominance+_filterOutput.getNBpixels());
|
||||
_applyRIFfilter(_chrominance+_filterOutput.getDoubleNBpixels(), _chrominance+_filterOutput.getDoubleNBpixels());
|
||||
}*/
|
||||
|
||||
// normalize by the photoreceptors local density and retrieve the local luminance
|
||||
float *chrominancePTR= &_chrominance[0];
|
||||
float *colorLocalDensityPTR= &_colorLocalDensity[0];
|
||||
float *luminance= &(*_luminance)[0];
|
||||
if (!adaptiveFiltering)// compute the gradient on the luminance
|
||||
{
|
||||
if (_samplingMethod==RETINA_COLOR_RANDOM)
|
||||
for (unsigned int indexc=0; indexc<_filterOutput.getNBpixels() ; ++indexc, ++chrominancePTR, ++colorLocalDensityPTR, ++luminance)
|
||||
{
|
||||
// normalize by photoreceptors density
|
||||
float Cr=*(chrominancePTR)*_colorLocalDensity[indexc];
|
||||
float Cg=*(chrominancePTR+_filterOutput.getNBpixels())*_colorLocalDensity[indexc+_filterOutput.getNBpixels()];
|
||||
float Cb=*(chrominancePTR+_filterOutput.getDoubleNBpixels())*_colorLocalDensity[indexc+_filterOutput.getDoubleNBpixels()];
|
||||
*luminance=(Cr+Cg+Cb)*_pG;
|
||||
*(chrominancePTR)=Cr-*luminance;
|
||||
*(chrominancePTR+_filterOutput.getNBpixels())=Cg-*luminance;
|
||||
*(chrominancePTR+_filterOutput.getDoubleNBpixels())=Cb-*luminance;
|
||||
}
|
||||
else
|
||||
for (unsigned int indexc=0; indexc<_filterOutput.getNBpixels() ; ++indexc, ++chrominancePTR, ++colorLocalDensityPTR, ++luminance)
|
||||
{
|
||||
float Cr=*(chrominancePTR);
|
||||
float Cg=*(chrominancePTR+_filterOutput.getNBpixels());
|
||||
float Cb=*(chrominancePTR+_filterOutput.getDoubleNBpixels());
|
||||
*luminance=_pR*Cr+_pG*Cg+_pB*Cb;
|
||||
*(chrominancePTR)=Cr-*luminance;
|
||||
*(chrominancePTR+_filterOutput.getNBpixels())=Cg-*luminance;
|
||||
*(chrominancePTR+_filterOutput.getDoubleNBpixels())=Cb-*luminance;
|
||||
}
|
||||
|
||||
// in order to get the color image, each colored map needs to be added the luminance
|
||||
// -> to do so, compute: multiplexedColorFrame - remultiplexed chrominances
|
||||
runColorMultiplexing(_chrominance, _tempMultiplexedFrame);
|
||||
//lum = 1/3((f*(ImR))/(f*mR) + (f*(ImG))/(f*mG) + (f*(ImB))/(f*mB));
|
||||
float *luminancePTR= &(*_luminance)[0];
|
||||
chrominancePTR= &_chrominance[0];
|
||||
float *demultiplexedColorFramePTR= &_demultiplexedColorFrame[0];
|
||||
for (unsigned int indexp=0; indexp<_filterOutput.getNBpixels() ; ++indexp, ++luminancePTR, ++chrominancePTR, ++demultiplexedColorFramePTR)
|
||||
{
|
||||
*luminancePTR=(multiplexedColorFrame[indexp]-_tempMultiplexedFrame[indexp]);
|
||||
*(demultiplexedColorFramePTR)=*(chrominancePTR)+*luminancePTR;
|
||||
*(demultiplexedColorFramePTR+_filterOutput.getNBpixels())=*(chrominancePTR+_filterOutput.getNBpixels())+*luminancePTR;
|
||||
*(demultiplexedColorFramePTR+_filterOutput.getDoubleNBpixels())=*(chrominancePTR+_filterOutput.getDoubleNBpixels())+*luminancePTR;
|
||||
}
|
||||
|
||||
}else
|
||||
{
|
||||
const float *multiplexedColorFramePTR= get_data(multiplexedColorFrame);
|
||||
for (unsigned int indexc=0; indexc<_filterOutput.getNBpixels() ; ++indexc, ++chrominancePTR, ++colorLocalDensityPTR, ++luminance, ++multiplexedColorFramePTR)
|
||||
{
|
||||
// normalize by photoreceptors density
|
||||
float Cr=*(chrominancePTR)*_colorLocalDensity[indexc];
|
||||
float Cg=*(chrominancePTR+_filterOutput.getNBpixels())*_colorLocalDensity[indexc+_filterOutput.getNBpixels()];
|
||||
float Cb=*(chrominancePTR+_filterOutput.getDoubleNBpixels())*_colorLocalDensity[indexc+_filterOutput.getDoubleNBpixels()];
|
||||
*luminance=(Cr+Cg+Cb)*_pG;
|
||||
_demultiplexedTempBuffer[_colorSampling[indexc]] = *multiplexedColorFramePTR - *luminance;
|
||||
|
||||
}
|
||||
|
||||
// compute the gradient of the luminance
|
||||
#ifdef MAKE_PARALLEL // call the TemplateBuffer TBB clipping method
|
||||
cv::parallel_for_(cv::Range(2,_filterOutput.getNBrows()-2), Parallel_computeGradient(_filterOutput.getNBcolumns(), _filterOutput.getNBrows(), &(*_luminance)[0], &_imageGradient[0]));
|
||||
#else
|
||||
_computeGradient(&(*_luminance)[0]);
|
||||
#endif
|
||||
// adaptively filter the submosaics to get the adaptive densities, here the buffer _chrominance is used as a temp buffer
|
||||
_adaptiveSpatialLPfilter(&_RGBmosaic[0], &_chrominance[0]);
|
||||
_adaptiveSpatialLPfilter(&_RGBmosaic[0]+_filterOutput.getNBpixels(), &_chrominance[0]+_filterOutput.getNBpixels());
|
||||
_adaptiveSpatialLPfilter(&_RGBmosaic[0]+_filterOutput.getDoubleNBpixels(), &_chrominance[0]+_filterOutput.getDoubleNBpixels());
|
||||
|
||||
_adaptiveSpatialLPfilter(&_demultiplexedTempBuffer[0], &_demultiplexedColorFrame[0]);
|
||||
_adaptiveSpatialLPfilter(&_demultiplexedTempBuffer[0]+_filterOutput.getNBpixels(), &_demultiplexedColorFrame[0]+_filterOutput.getNBpixels());
|
||||
_adaptiveSpatialLPfilter(&_demultiplexedTempBuffer[0]+_filterOutput.getDoubleNBpixels(), &_demultiplexedColorFrame[0]+_filterOutput.getDoubleNBpixels());
|
||||
|
||||
/* for (unsigned int index=0; index<_filterOutput.getNBpixels()*3 ; ++index) // cette boucle pourrait �tre supprimee en passant la densit� � la fonction de filtrage
|
||||
_demultiplexedColorFrame[index] /= _chrominance[index];*/
|
||||
_demultiplexedColorFrame/=_chrominance; // more optimal ;o)
|
||||
|
||||
// compute and substract the residual luminance
|
||||
for (unsigned int index=0; index<_filterOutput.getNBpixels() ; ++index)
|
||||
{
|
||||
float residu = _pR*_demultiplexedColorFrame[index] + _pG*_demultiplexedColorFrame[index+_filterOutput.getNBpixels()] + _pB*_demultiplexedColorFrame[index+_filterOutput.getDoubleNBpixels()];
|
||||
_demultiplexedColorFrame[index] = _demultiplexedColorFrame[index] - residu;
|
||||
_demultiplexedColorFrame[index+_filterOutput.getNBpixels()] = _demultiplexedColorFrame[index+_filterOutput.getNBpixels()] - residu;
|
||||
_demultiplexedColorFrame[index+_filterOutput.getDoubleNBpixels()] = _demultiplexedColorFrame[index+_filterOutput.getDoubleNBpixels()] - residu;
|
||||
}
|
||||
|
||||
// multiplex the obtained chrominance
|
||||
runColorMultiplexing(_demultiplexedColorFrame, _tempMultiplexedFrame);
|
||||
_demultiplexedTempBuffer=0;
|
||||
|
||||
// get the luminance, et and add it to each chrominance
|
||||
for (unsigned int index=0; index<_filterOutput.getNBpixels() ; ++index)
|
||||
{
|
||||
(*_luminance)[index]=multiplexedColorFrame[index]-_tempMultiplexedFrame[index];
|
||||
_demultiplexedTempBuffer[_colorSampling[index]] = _demultiplexedColorFrame[_colorSampling[index]];//multiplexedColorFrame[index] - (*_luminance)[index];
|
||||
}
|
||||
|
||||
_spatiotemporalLPfilter(&_demultiplexedTempBuffer[0], &_demultiplexedTempBuffer[0]);
|
||||
_spatiotemporalLPfilter(&_demultiplexedTempBuffer[0]+_filterOutput.getNBpixels(), &_demultiplexedTempBuffer[0]+_filterOutput.getNBpixels());
|
||||
_spatiotemporalLPfilter(&_demultiplexedTempBuffer[0]+_filterOutput.getDoubleNBpixels(), &_demultiplexedTempBuffer[0]+_filterOutput.getDoubleNBpixels());
|
||||
|
||||
// get the luminance and add it to each chrominance
|
||||
for (unsigned int index=0; index<_filterOutput.getNBpixels() ; ++index)
|
||||
{
|
||||
_demultiplexedColorFrame[index] = _demultiplexedTempBuffer[index]*_colorLocalDensity[index]+ (*_luminance)[index];
|
||||
_demultiplexedColorFrame[index+_filterOutput.getNBpixels()] = _demultiplexedTempBuffer[index+_filterOutput.getNBpixels()]*_colorLocalDensity[index+_filterOutput.getNBpixels()]+ (*_luminance)[index];
|
||||
_demultiplexedColorFrame[index+_filterOutput.getDoubleNBpixels()] = _demultiplexedTempBuffer[index+_filterOutput.getDoubleNBpixels()]*_colorLocalDensity[index+_filterOutput.getDoubleNBpixels()]+ (*_luminance)[index];
|
||||
}
|
||||
}
|
||||
|
||||
// eliminate saturated colors by simple clipping values to the input range
|
||||
clipRGBOutput_0_maxInputValue(NULL, maxInputValue);
|
||||
|
||||
/* transfert image gradient in order to check validity
|
||||
memcpy((*_luminance), _imageGradient, sizeof(float)*_filterOutput.getNBpixels());
|
||||
memcpy(_demultiplexedColorFrame, _imageGradient+_filterOutput.getNBpixels(), sizeof(float)*_filterOutput.getNBpixels());
|
||||
memcpy(_demultiplexedColorFrame+_filterOutput.getNBpixels(), _imageGradient+_filterOutput.getNBpixels(), sizeof(float)*_filterOutput.getNBpixels());
|
||||
memcpy(_demultiplexedColorFrame+2*_filterOutput.getNBpixels(), _imageGradient+_filterOutput.getNBpixels(), sizeof(float)*_filterOutput.getNBpixels());
|
||||
*/
|
||||
|
||||
if (_saturateColors)
|
||||
{
|
||||
TemplateBuffer<float>::normalizeGrayOutputCentredSigmoide(128, _colorSaturationValue, maxInputValue, &_demultiplexedColorFrame[0], &_demultiplexedColorFrame[0], _filterOutput.getNBpixels());
|
||||
TemplateBuffer<float>::normalizeGrayOutputCentredSigmoide(128, _colorSaturationValue, maxInputValue, &_demultiplexedColorFrame[0]+_filterOutput.getNBpixels(), &_demultiplexedColorFrame[0]+_filterOutput.getNBpixels(), _filterOutput.getNBpixels());
|
||||
TemplateBuffer<float>::normalizeGrayOutputCentredSigmoide(128, _colorSaturationValue, maxInputValue, &_demultiplexedColorFrame[0]+_filterOutput.getNBpixels()*2, &_demultiplexedColorFrame[0]+_filterOutput.getNBpixels()*2, _filterOutput.getNBpixels());
|
||||
}
|
||||
}
|
||||
|
||||
// color multiplexing: input frame size=_NBrows*_filterOutput.getNBcolumns()*3, multiplexedFrame output size=_NBrows*_filterOutput.getNBcolumns()
|
||||
void RetinaColor::runColorMultiplexing(const std::valarray<float> &demultiplexedInputFrame, std::valarray<float> &multiplexedFrame)
|
||||
{
|
||||
// multiply each color layer by its bayer mask
|
||||
unsigned int *colorSamplingPTR= &_colorSampling[0];
|
||||
float *multiplexedFramePTR= &multiplexedFrame[0];
|
||||
for (unsigned int indexp=0; indexp<_filterOutput.getNBpixels(); ++indexp)
|
||||
*(multiplexedFramePTR++)=demultiplexedInputFrame[*(colorSamplingPTR++)];
|
||||
}
|
||||
|
||||
void RetinaColor::normalizeRGBOutput_0_maxOutputValue(const float maxOutputValue)
|
||||
{
|
||||
//normalizeGrayOutputCentredSigmoide(0.0, 2, _chrominance);
|
||||
TemplateBuffer<float>::normalizeGrayOutput_0_maxOutputValue(&_demultiplexedColorFrame[0], 3*_filterOutput.getNBpixels(), maxOutputValue);
|
||||
//normalizeGrayOutputCentredSigmoide(0.0, 2, _chrominance+_filterOutput.getNBpixels());
|
||||
//normalizeGrayOutput_0_maxOutputValue(_demultiplexedColorFrame+_filterOutput.getNBpixels(), _filterOutput.getNBpixels(), maxOutputValue);
|
||||
//normalizeGrayOutputCentredSigmoide(0.0, 2, _chrominance+2*_filterOutput.getNBpixels());
|
||||
//normalizeGrayOutput_0_maxOutputValue(_demultiplexedColorFrame+_filterOutput.getDoubleNBpixels(), _filterOutput.getNBpixels(), maxOutputValue);
|
||||
TemplateBuffer<float>::normalizeGrayOutput_0_maxOutputValue(&(*_luminance)[0], _filterOutput.getNBpixels(), maxOutputValue);
|
||||
}
|
||||
|
||||
/// normalize output between 0 and maxOutputValue;
|
||||
void RetinaColor::clipRGBOutput_0_maxInputValue(float *inputOutputBuffer, const float maxInputValue)
|
||||
{
|
||||
//std::cout<<"RetinaColor::normalizing RGB frame..."<<std::endl;
|
||||
// if outputBuffer unsassigned, the rewrite the buffer
|
||||
if (inputOutputBuffer==NULL)
|
||||
inputOutputBuffer= &_demultiplexedColorFrame[0];
|
||||
|
||||
#ifdef MAKE_PARALLEL // call the TemplateBuffer TBB clipping method
|
||||
cv::parallel_for_(cv::Range(0,_filterOutput.getNBpixels()*3), Parallel_clipBufferValues<float>(inputOutputBuffer, 0, maxInputValue));
|
||||
#else
|
||||
float *inputOutputBufferPTR=inputOutputBuffer;
|
||||
for (unsigned int jf = 0; jf < _filterOutput.getNBpixels()*3; ++jf, ++inputOutputBufferPTR)
|
||||
{
|
||||
if (*inputOutputBufferPTR>maxInputValue)
|
||||
*inputOutputBufferPTR=maxInputValue;
|
||||
else if (*inputOutputBufferPTR<0)
|
||||
*inputOutputBufferPTR=0;
|
||||
}
|
||||
#endif
|
||||
//std::cout<<"RetinaColor::...normalizing RGB frame OK"<<std::endl;
|
||||
}
|
||||
|
||||
void RetinaColor::_interpolateImageDemultiplexedImage(float *inputOutputBuffer)
|
||||
{
|
||||
|
||||
switch(_samplingMethod)
|
||||
{
|
||||
|
||||
case RETINA_COLOR_RANDOM:
|
||||
return; // no need to interpolate
|
||||
break;
|
||||
|
||||
case RETINA_COLOR_DIAGONAL:
|
||||
_interpolateSingleChannelImage111(inputOutputBuffer);
|
||||
break;
|
||||
|
||||
case RETINA_COLOR_BAYER: // default sets bayer sampling
|
||||
_interpolateBayerRGBchannels(inputOutputBuffer);
|
||||
break;
|
||||
default:
|
||||
std::cerr<<"RetinaColor::No or wrong color sampling method, skeeping"<<std::endl;
|
||||
return;
|
||||
break;//.. not useful, yes
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RetinaColor::_interpolateSingleChannelImage111(float *inputOutputBuffer)
|
||||
{
|
||||
for (unsigned int indexr=0 ; indexr<_filterOutput.getNBrows(); ++indexr)
|
||||
{
|
||||
for (unsigned int indexc=1 ; indexc<_filterOutput.getNBcolumns()-1; ++indexc)
|
||||
{
|
||||
unsigned int index=indexc+indexr*_filterOutput.getNBcolumns();
|
||||
inputOutputBuffer[index]=(inputOutputBuffer[index-1]+inputOutputBuffer[index]+inputOutputBuffer[index+1])/3.f;
|
||||
}
|
||||
}
|
||||
for (unsigned int indexc=0 ; indexc<_filterOutput.getNBcolumns(); ++indexc)
|
||||
{
|
||||
for (unsigned int indexr=1 ; indexr<_filterOutput.getNBrows()-1; ++indexr)
|
||||
{
|
||||
unsigned int index=indexc+indexr*_filterOutput.getNBcolumns();
|
||||
inputOutputBuffer[index]=(inputOutputBuffer[index-_filterOutput.getNBcolumns()]+inputOutputBuffer[index]+inputOutputBuffer[index+_filterOutput.getNBcolumns()])/3.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RetinaColor::_interpolateBayerRGBchannels(float *inputOutputBuffer)
|
||||
{
|
||||
for (unsigned int indexr=0 ; indexr<_filterOutput.getNBrows()-1; indexr+=2)
|
||||
{
|
||||
for (unsigned int indexc=1 ; indexc<_filterOutput.getNBcolumns()-1; indexc+=2)
|
||||
{
|
||||
unsigned int indexR=indexc+indexr*_filterOutput.getNBcolumns();
|
||||
unsigned int indexB=_filterOutput.getDoubleNBpixels()+indexc+1+(indexr+1)*_filterOutput.getNBcolumns();
|
||||
inputOutputBuffer[indexR]=(inputOutputBuffer[indexR-1]+inputOutputBuffer[indexR+1])/2.f;
|
||||
inputOutputBuffer[indexB]=(inputOutputBuffer[indexB-1]+inputOutputBuffer[indexB+1])/2.f;
|
||||
}
|
||||
}
|
||||
for (unsigned int indexr=1 ; indexr<_filterOutput.getNBrows()-1; indexr+=2)
|
||||
{
|
||||
for (unsigned int indexc=0 ; indexc<_filterOutput.getNBcolumns(); ++indexc)
|
||||
{
|
||||
unsigned int indexR=indexc+indexr*_filterOutput.getNBcolumns();
|
||||
unsigned int indexB=_filterOutput.getDoubleNBpixels()+indexc+1+(indexr+1)*_filterOutput.getNBcolumns();
|
||||
inputOutputBuffer[indexR]=(inputOutputBuffer[indexR-_filterOutput.getNBcolumns()]+inputOutputBuffer[indexR+_filterOutput.getNBcolumns()])/2.f;
|
||||
inputOutputBuffer[indexB]=(inputOutputBuffer[indexB-_filterOutput.getNBcolumns()]+inputOutputBuffer[indexB+_filterOutput.getNBcolumns()])/2.f;
|
||||
|
||||
}
|
||||
}
|
||||
for (unsigned int indexr=1 ; indexr<_filterOutput.getNBrows()-1; ++indexr)
|
||||
for (unsigned int indexc=0 ; indexc<_filterOutput.getNBcolumns(); indexc+=2)
|
||||
{
|
||||
unsigned int indexG=_filterOutput.getNBpixels()+indexc+(indexr)*_filterOutput.getNBcolumns()+indexr%2;
|
||||
inputOutputBuffer[indexG]=(inputOutputBuffer[indexG-1]+inputOutputBuffer[indexG+1]+inputOutputBuffer[indexG-_filterOutput.getNBcolumns()]+inputOutputBuffer[indexG+_filterOutput.getNBcolumns()])*0.25f;
|
||||
}
|
||||
}
|
||||
|
||||
void RetinaColor::_applyRIFfilter(const float *sourceBuffer, float *destinationBuffer)
|
||||
{
|
||||
for (unsigned int indexr=1 ; indexr<_filterOutput.getNBrows()-1; ++indexr)
|
||||
{
|
||||
for (unsigned int indexc=1 ; indexc<_filterOutput.getNBcolumns()-1; ++indexc)
|
||||
{
|
||||
unsigned int index=indexc+indexr*_filterOutput.getNBcolumns();
|
||||
_tempMultiplexedFrame[index]=(4.f*sourceBuffer[index]+sourceBuffer[index-1-_filterOutput.getNBcolumns()]+sourceBuffer[index-1+_filterOutput.getNBcolumns()]+sourceBuffer[index+1-_filterOutput.getNBcolumns()]+sourceBuffer[index+1+_filterOutput.getNBcolumns()])*0.125f;
|
||||
}
|
||||
}
|
||||
memcpy(destinationBuffer, &_tempMultiplexedFrame[0], sizeof(float)*_filterOutput.getNBpixels());
|
||||
}
|
||||
|
||||
void RetinaColor::_getNormalizedContoursImage(const float *inputFrame, float *outputFrame)
|
||||
{
|
||||
float maxValue=0.f;
|
||||
float normalisationFactor=1.f/3.f;
|
||||
for (unsigned int indexr=1 ; indexr<_filterOutput.getNBrows()-1; ++indexr)
|
||||
{
|
||||
for (unsigned int indexc=1 ; indexc<_filterOutput.getNBcolumns()-1; ++indexc)
|
||||
{
|
||||
unsigned int index=indexc+indexr*_filterOutput.getNBcolumns();
|
||||
outputFrame[index]=normalisationFactor*fabs(8.f*inputFrame[index]-inputFrame[index-1]-inputFrame[index+1]-inputFrame[index-_filterOutput.getNBcolumns()]-inputFrame[index+_filterOutput.getNBcolumns()]-inputFrame[index-1-_filterOutput.getNBcolumns()]-inputFrame[index-1+_filterOutput.getNBcolumns()]-inputFrame[index+1-_filterOutput.getNBcolumns()]-inputFrame[index+1+_filterOutput.getNBcolumns()]);
|
||||
if (outputFrame[index]>maxValue)
|
||||
maxValue=outputFrame[index];
|
||||
}
|
||||
}
|
||||
normalisationFactor=1.f/maxValue;
|
||||
// normalisation [0, 1]
|
||||
for (unsigned int indexp=1 ; indexp<_filterOutput.getNBrows()-1; ++indexp)
|
||||
outputFrame[indexp]=outputFrame[indexp]*normalisationFactor;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// ADAPTIVE BASIC RETINA FILTER
|
||||
//////////////////////////////////////////////////////////
|
||||
// run LP filter for a new frame input and save result at a specific output adress
|
||||
void RetinaColor::_adaptiveSpatialLPfilter(const float *inputFrame, float *outputFrame)
|
||||
{
|
||||
|
||||
/**********/
|
||||
_gain = (1-0.57f)*(1-0.57f)*(1-0.06f)*(1-0.06f);
|
||||
|
||||
// launch the serie of 1D directional filters in order to compute the 2D low pass filter
|
||||
// -> horizontal filters work with the first layer of imageGradient
|
||||
_adaptiveHorizontalCausalFilter_addInput(inputFrame, outputFrame, 0, _filterOutput.getNBrows());
|
||||
_horizontalAnticausalFilter_Irregular(outputFrame, 0, _filterOutput.getNBrows(), &_imageGradient[0]);
|
||||
// -> horizontal filters work with the second layer of imageGradient
|
||||
_verticalCausalFilter_Irregular(outputFrame, 0, _filterOutput.getNBcolumns(), &_imageGradient[0]+_filterOutput.getNBpixels());
|
||||
_adaptiveVerticalAnticausalFilter_multGain(outputFrame, 0, _filterOutput.getNBcolumns());
|
||||
}
|
||||
|
||||
// horizontal causal filter which adds the input inside... replaces the parent _horizontalCausalFilter_Irregular_addInput by avoiding a product for each pixel
|
||||
void RetinaColor::_adaptiveHorizontalCausalFilter_addInput(const float *inputFrame, float *outputFrame, unsigned int IDrowStart, unsigned int IDrowEnd)
|
||||
{
|
||||
#ifdef MAKE_PARALLEL
|
||||
cv::parallel_for_(cv::Range(IDrowStart,IDrowEnd), Parallel_adaptiveHorizontalCausalFilter_addInput(inputFrame, outputFrame, &_imageGradient[0], _filterOutput.getNBcolumns()));
|
||||
#else
|
||||
float* outputPTR=outputFrame+IDrowStart*_filterOutput.getNBcolumns();
|
||||
const float* inputPTR=inputFrame+IDrowStart*_filterOutput.getNBcolumns();
|
||||
const float *imageGradientPTR= &_imageGradient[0]+IDrowStart*_filterOutput.getNBcolumns();
|
||||
for (unsigned int IDrow=IDrowStart; IDrow<IDrowEnd; ++IDrow)
|
||||
{
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBcolumns(); ++index)
|
||||
{
|
||||
//std::cout<<(*imageGradientPTR)<<" ";
|
||||
result = *(inputPTR++) + (*imageGradientPTR)* result;
|
||||
*(outputPTR++) = result;
|
||||
++imageGradientPTR;
|
||||
}
|
||||
// std::cout<<" "<<std::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// vertical anticausal filter which multiplies the output by _gain... replaces the parent _verticalAnticausalFilter_multGain by avoiding a product for each pixel and taking into account the second layer of the _imageGradient buffer
|
||||
void RetinaColor::_adaptiveVerticalAnticausalFilter_multGain(float *outputFrame, unsigned int IDcolumnStart, unsigned int IDcolumnEnd)
|
||||
{
|
||||
#ifdef MAKE_PARALLEL
|
||||
cv::parallel_for_(cv::Range(IDcolumnStart,IDcolumnEnd), Parallel_adaptiveVerticalAnticausalFilter_multGain(outputFrame, &_imageGradient[0]+_filterOutput.getNBpixels(), _filterOutput.getNBrows(), _filterOutput.getNBcolumns(), _gain));
|
||||
#else
|
||||
float* outputOffset=outputFrame+_filterOutput.getNBpixels()-_filterOutput.getNBcolumns();
|
||||
float* gradOffset= &_imageGradient[0]+_filterOutput.getNBpixels()*2-_filterOutput.getNBcolumns();
|
||||
|
||||
for (unsigned int IDcolumn=IDcolumnStart; IDcolumn<IDcolumnEnd; ++IDcolumn)
|
||||
{
|
||||
float result=0;
|
||||
float *outputPTR=outputOffset+IDcolumn;
|
||||
float *imageGradientPTR=gradOffset+IDcolumn;
|
||||
for (unsigned int index=0; index<_filterOutput.getNBrows(); ++index)
|
||||
{
|
||||
result = *(outputPTR) + (*(imageGradientPTR)) * result;
|
||||
*(outputPTR) = _gain*result;
|
||||
outputPTR-=_filterOutput.getNBcolumns();
|
||||
imageGradientPTR-=_filterOutput.getNBcolumns();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
void RetinaColor::_computeGradient(const float *luminance)
|
||||
{
|
||||
for (unsigned int idLine=2;idLine<_filterOutput.getNBrows()-2;++idLine)
|
||||
{
|
||||
for (unsigned int idColumn=2;idColumn<_filterOutput.getNBcolumns()-2;++idColumn)
|
||||
{
|
||||
const unsigned int pixelIndex=idColumn+_filterOutput.getNBcolumns()*idLine;
|
||||
|
||||
// horizontal and vertical local gradients
|
||||
const float verticalGrad=fabs(luminance[pixelIndex+_filterOutput.getNBcolumns()]-luminance[pixelIndex-_filterOutput.getNBcolumns()]);
|
||||
const float horizontalGrad=fabs(luminance[pixelIndex+1]-luminance[pixelIndex-1]);
|
||||
|
||||
// neighborhood horizontal and vertical gradients
|
||||
const float verticalGrad_p=fabs(luminance[pixelIndex]-luminance[pixelIndex-2*_filterOutput.getNBcolumns()]);
|
||||
const float horizontalGrad_p=fabs(luminance[pixelIndex]-luminance[pixelIndex-2]);
|
||||
const float verticalGrad_n=fabs(luminance[pixelIndex+2*_filterOutput.getNBcolumns()]-luminance[pixelIndex]);
|
||||
const float horizontalGrad_n=fabs(luminance[pixelIndex+2]-luminance[pixelIndex]);
|
||||
|
||||
const float horizontalGradient=0.5f*horizontalGrad+0.25f*(horizontalGrad_p+horizontalGrad_n);
|
||||
const float verticalGradient=0.5f*verticalGrad+0.25f*(verticalGrad_p+verticalGrad_n);
|
||||
|
||||
// compare local gradient means and fill the appropriate filtering coefficient value that will be used in adaptative filters
|
||||
if (horizontalGradient<verticalGradient)
|
||||
{
|
||||
_imageGradient[pixelIndex+_filterOutput.getNBpixels()]=0.06f;
|
||||
_imageGradient[pixelIndex]=0.57f;
|
||||
}
|
||||
else
|
||||
{
|
||||
_imageGradient[pixelIndex+_filterOutput.getNBpixels()]=0.57f;
|
||||
_imageGradient[pixelIndex]=0.06f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RetinaColor::applyKrauskopfLMS2Acr1cr2Transform(std::valarray<float> &result)
|
||||
{
|
||||
bool processSuccess=true;
|
||||
// basic preliminary error check
|
||||
if (result.size()!=_demultiplexedColorFrame.size())
|
||||
{
|
||||
std::cerr<<"RetinaColor::applyKrauskopfLMS2Acr1cr2Transform: input buffer does not match retina buffer size, conversion aborted"<<std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// apply transformation
|
||||
_applyImageColorSpaceConversion(_demultiplexedColorFrame, result, _LMStoACr1Cr2);
|
||||
|
||||
return processSuccess;
|
||||
}
|
||||
|
||||
bool RetinaColor::applyLMS2LabTransform(std::valarray<float> &result)
|
||||
{
|
||||
bool processSuccess=true;
|
||||
// basic preliminary error check
|
||||
if (result.size()!=_demultiplexedColorFrame.size())
|
||||
{
|
||||
std::cerr<<"RetinaColor::applyKrauskopfLMS2Acr1cr2Transform: input buffer does not match retina buffer size, conversion aborted"<<std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// apply transformation
|
||||
_applyImageColorSpaceConversion(_demultiplexedColorFrame, result, _LMStoLab);
|
||||
|
||||
return processSuccess;
|
||||
}
|
||||
|
||||
// template function able to perform a custom color space transformation
|
||||
void RetinaColor::_applyImageColorSpaceConversion(const std::valarray<float> &inputFrameBuffer, std::valarray<float> &outputFrameBuffer, const float *transformTable)
|
||||
{
|
||||
// two step methods in order to allow inputFrame and outputFrame to be the same
|
||||
unsigned int nbPixels=(unsigned int)(inputFrameBuffer.size()/3), dbpixels=(unsigned int)(2*inputFrameBuffer.size()/3);
|
||||
|
||||
const float *inputFrame=get_data(inputFrameBuffer);
|
||||
float *outputFrame= &outputFrameBuffer[0];
|
||||
|
||||
for (unsigned int dataIndex=0; dataIndex<nbPixels;++dataIndex, ++outputFrame, ++inputFrame)
|
||||
{
|
||||
// first step, compute each new values
|
||||
float layer1 = *(inputFrame)**(transformTable+0) +*(inputFrame+nbPixels)**(transformTable+1) +*(inputFrame+dbpixels)**(transformTable+2);
|
||||
float layer2 = *(inputFrame)**(transformTable+3) +*(inputFrame+nbPixels)**(transformTable+4) +*(inputFrame+dbpixels)**(transformTable+5);
|
||||
float layer3 = *(inputFrame)**(transformTable+6) +*(inputFrame+nbPixels)**(transformTable+7) +*(inputFrame+dbpixels)**(transformTable+8);
|
||||
// second, affect the output
|
||||
*(outputFrame) = layer1;
|
||||
*(outputFrame+nbPixels) = layer2;
|
||||
*(outputFrame+dbpixels) = layer3;
|
||||
}
|
||||
}
|
||||
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
@@ -0,0 +1,390 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2011
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @class RetinaColor a color multilexing/demultiplexing (demosaicing) based on a human vision inspiration. Different mosaicing strategies can be used, included random sampling !
|
||||
* => please take a look at the nice and efficient demosaicing strategy introduced by B.Chaix de Lavarene, take a look at the cited paper for more mathematical details
|
||||
* @brief Retina color sampling model which allows classical bayer sampling, random and potentially several other method ! Low color errors on corners !
|
||||
* -> Based on the research of:
|
||||
* .Brice Chaix Lavarene (chaix@lis.inpg.fr)
|
||||
* .Jeanny Herault (herault@lis.inpg.fr)
|
||||
* .David Alleyson (david.alleyson@upmf-grenoble.fr)
|
||||
* .collaboration: alexandre benoit (benoit.alexandre.vision@gmail.com or benoit@lis.inpg.fr)
|
||||
* Please cite: B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
* @author Alexandre BENOIT, benoit.alexandre.vision@gmail.com, LISTIC / Gipsa-Lab, France: www.gipsa-lab.inpg.fr/
|
||||
* Creation date 2007
|
||||
*/
|
||||
|
||||
#ifndef RETINACOLOR_HPP_
|
||||
#define RETINACOLOR_HPP_
|
||||
|
||||
#include "basicretinafilter.hpp"
|
||||
|
||||
//#define __RETINACOLORDEBUG //define RETINACOLORDEBUG in order to display debug data
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
class RetinaColor: public BasicRetinaFilter
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @typedef which allows to select the type of photoreceptors color sampling
|
||||
*/
|
||||
|
||||
/**
|
||||
* constructor of the retina color processing model
|
||||
* @param NBrows: number of rows of the input image
|
||||
* @param NBcolumns: number of columns of the input image
|
||||
* @param samplingMethod: the chosen color sampling method
|
||||
*/
|
||||
RetinaColor(const unsigned int NBrows, const unsigned int NBcolumns, const int samplingMethod=RETINA_COLOR_BAYER);
|
||||
|
||||
/**
|
||||
* standard destructor
|
||||
*/
|
||||
virtual ~RetinaColor();
|
||||
|
||||
/**
|
||||
* function that clears all buffers of the object
|
||||
*/
|
||||
void clearAllBuffers();
|
||||
|
||||
/**
|
||||
* resize retina color filter object (resize all allocated buffers)
|
||||
* @param NBrows: the new height size
|
||||
* @param NBcolumns: the new width size
|
||||
*/
|
||||
void resize(const unsigned int NBrows, const unsigned int NBcolumns);
|
||||
|
||||
|
||||
/**
|
||||
* color multiplexing function: a demultiplexed RGB frame of size M*N*3 is transformed into a multiplexed M*N*1 pixels frame where each pixel is either Red, or Green or Blue
|
||||
* @param inputRGBFrame: the input RGB frame to be processed
|
||||
* @return, nothing but the multiplexed frame is available by the use of the getMultiplexedFrame() function
|
||||
*/
|
||||
inline void runColorMultiplexing(const std::valarray<float> &inputRGBFrame) { runColorMultiplexing(inputRGBFrame, *_multiplexedFrame); }
|
||||
|
||||
/**
|
||||
* color multiplexing function: a demultipleed RGB frame of size M*N*3 is transformed into a multiplexed M*N*1 pixels frame where each pixel is either Red, or Green or Blue if using RGB images
|
||||
* @param demultiplexedInputFrame: the demultiplexed input frame to be processed of size M*N*3
|
||||
* @param multiplexedFrame: the resulting multiplexed frame
|
||||
*/
|
||||
void runColorMultiplexing(const std::valarray<float> &demultiplexedInputFrame, std::valarray<float> &multiplexedFrame);
|
||||
|
||||
/**
|
||||
* color demultiplexing function: a multiplexed frame of size M*N*1 pixels is transformed into a RGB demultiplexed M*N*3 pixels frame
|
||||
* @param multiplexedColorFrame: the input multiplexed frame to be processed
|
||||
* @param adaptiveFiltering: specifies if an adaptive filtering has to be perform rather than standard filtering (adaptive filtering allows a better rendering)
|
||||
* @param maxInputValue: the maximum input data value (should be 255 for 8 bits images but it can change in the case of High Dynamic Range Images (HDRI)
|
||||
* @return, nothing but the output demultiplexed frame is available by the use of the getDemultiplexedColorFrame() function, also use getLuminance() and getChrominance() in order to retreive either luminance or chrominance
|
||||
*/
|
||||
void runColorDemultiplexing(const std::valarray<float> &multiplexedColorFrame, const bool adaptiveFiltering=false, const float maxInputValue=255.0);
|
||||
|
||||
/**
|
||||
* activate color saturation as the final step of the color demultiplexing process
|
||||
* -> this saturation is a sigmoide function applied to each channel of the demultiplexed image.
|
||||
* @param saturateColors: boolean that activates color saturation (if true) or desactivate (if false)
|
||||
* @param colorSaturationValue: the saturation factor
|
||||
* */
|
||||
void setColorSaturation(const bool saturateColors=true, const float colorSaturationValue=4.0) { _saturateColors=saturateColors; _colorSaturationValue=colorSaturationValue; }
|
||||
|
||||
/**
|
||||
* set parameters of the low pass spatio-temporal filter used to retreive the low chrominance
|
||||
* @param beta: gain of the filter (generally set to zero)
|
||||
* @param tau: time constant of the filter (unit is frame for video processing), typically 0 when considering static processing, 1 or more if a temporal smoothing effect is required
|
||||
* @param k: spatial constant of the filter (unit is pixels), typical value is 2.5
|
||||
*/
|
||||
void setChrominanceLPfilterParameters(const float beta, const float tau, const float k) { setLPfilterParameters(beta, tau, k); }
|
||||
|
||||
/**
|
||||
* apply to the retina color output the Krauskopf transformation which leads to an opponent color system: output colorspace if Acr1cr2 if input of the retina was LMS color space
|
||||
* @param result: the input buffer to fill with the transformed colorspace retina output
|
||||
* @return true if process ended successfully
|
||||
*/
|
||||
bool applyKrauskopfLMS2Acr1cr2Transform(std::valarray<float> &result);
|
||||
|
||||
/**
|
||||
* apply to the retina color output the CIE Lab color transformation
|
||||
* @param result: the input buffer to fill with the transformed colorspace retina output
|
||||
* @return true if process ended successfully
|
||||
*/
|
||||
bool applyLMS2LabTransform(std::valarray<float> &result);
|
||||
|
||||
/**
|
||||
* @return the multiplexed frame result (use this after function runColorMultiplexing)
|
||||
*/
|
||||
inline const std::valarray<float> &getMultiplexedFrame() const { return *_multiplexedFrame; }
|
||||
|
||||
/**
|
||||
* @return the demultiplexed frame result (use this after function runColorDemultiplexing)
|
||||
*/
|
||||
inline const std::valarray<float> &getDemultiplexedColorFrame() const { return _demultiplexedColorFrame; }
|
||||
|
||||
/**
|
||||
* @return the luminance of the processed frame (use this after function runColorDemultiplexing)
|
||||
*/
|
||||
inline const std::valarray<float> &getLuminance() const { return *_luminance; }
|
||||
|
||||
/**
|
||||
* @return the chrominance of the processed frame (use this after function runColorDemultiplexing)
|
||||
*/
|
||||
inline const std::valarray<float> &getChrominance() const { return _chrominance; }
|
||||
|
||||
/**
|
||||
* standard 0 to 255 image clipping function appled to RGB images (of size M*N*3 pixels)
|
||||
* @param inputOutputBuffer: the image to be normalized (rewrites the input), if no parameter, then, the built in buffer reachable by getOutput() function is normalized
|
||||
* @param maxOutputValue: the maximum value allowed at the output (values superior to it would be clipped
|
||||
*/
|
||||
void clipRGBOutput_0_maxInputValue(float *inputOutputBuffer, const float maxOutputValue=255.0);
|
||||
|
||||
/**
|
||||
* standard 0 to 255 image normalization function appled to RGB images (of size M*N*3 pixels)
|
||||
* @param maxOutputValue: the maximum value allowed at the output (values superior to it would be clipped
|
||||
*/
|
||||
void normalizeRGBOutput_0_maxOutputValue(const float maxOutputValue=255.0);
|
||||
|
||||
/**
|
||||
* return the color sampling map: a Nrows*Mcolumns image in which each pixel value is the ofsset adress which gives the adress of the sampled pixel on an Nrows*Mcolumns*3 color image ordered by layers: layer1, layer2, layer3
|
||||
*/
|
||||
inline const std::valarray<unsigned int> &getSamplingMap() const { return _colorSampling; }
|
||||
|
||||
/**
|
||||
* function used (to bypass processing) to manually set the color output
|
||||
* @param demultiplexedImage: the color image (luminance+chrominance) which has to be written in the object buffer
|
||||
*/
|
||||
inline void setDemultiplexedColorFrame(const std::valarray<float> &demultiplexedImage) { _demultiplexedColorFrame=demultiplexedImage; }
|
||||
|
||||
protected:
|
||||
|
||||
// private functions
|
||||
int _samplingMethod;
|
||||
bool _saturateColors;
|
||||
float _colorSaturationValue;
|
||||
// links to parent buffers (more convienient names
|
||||
TemplateBuffer<float> *_luminance;
|
||||
std::valarray<float> *_multiplexedFrame;
|
||||
// instance buffers
|
||||
std::valarray<unsigned int> _colorSampling; // table (size (_nbRows*_nbColumns) which specifies the color of each pixel
|
||||
std::valarray<float> _RGBmosaic;
|
||||
std::valarray<float> _tempMultiplexedFrame;
|
||||
std::valarray<float> _demultiplexedTempBuffer;
|
||||
std::valarray<float> _demultiplexedColorFrame;
|
||||
std::valarray<float> _chrominance;
|
||||
std::valarray<float> _colorLocalDensity;// buffer which contains the local density of the R, G and B photoreceptors for a normalization use
|
||||
std::valarray<float> _imageGradient;
|
||||
|
||||
// variables
|
||||
float _pR, _pG, _pB; // probabilities of color R, G and B
|
||||
bool _objectInit;
|
||||
|
||||
// protected functions
|
||||
void _initColorSampling();
|
||||
void _interpolateImageDemultiplexedImage(float *inputOutputBuffer);
|
||||
void _interpolateSingleChannelImage111(float *inputOutputBuffer);
|
||||
void _interpolateBayerRGBchannels(float *inputOutputBuffer);
|
||||
void _applyRIFfilter(const float *sourceBuffer, float *destinationBuffer);
|
||||
void _getNormalizedContoursImage(const float *inputFrame, float *outputFrame);
|
||||
// -> special adaptive filters dedicated to low pass filtering on the chrominance (skeeps filtering on the edges)
|
||||
void _adaptiveSpatialLPfilter(const float *inputFrame, float *outputFrame);
|
||||
void _adaptiveHorizontalCausalFilter_addInput(const float *inputFrame, float *outputFrame, const unsigned int IDrowStart, const unsigned int IDrowEnd); // TBB parallelized
|
||||
void _adaptiveVerticalAnticausalFilter_multGain(float *outputFrame, const unsigned int IDcolumnStart, const unsigned int IDcolumnEnd);
|
||||
void _computeGradient(const float *luminance);
|
||||
void _normalizeOutputs_0_maxOutputValue(void);
|
||||
|
||||
// color space transform
|
||||
void _applyImageColorSpaceConversion(const std::valarray<float> &inputFrame, std::valarray<float> &outputFrame, const float *transformTable);
|
||||
|
||||
#ifdef MAKE_PARALLEL
|
||||
/******************************************************
|
||||
** IF some parallelizing thread methods are available, then, main loops are parallelized using these functors
|
||||
** ==> main idea paralellise main filters loops, then, only the most used methods are parallelized... TODO : increase the number of parallelised methods as necessary
|
||||
** ==> functors names = Parallel_$$$ where $$$= the name of the serial method that is parallelised
|
||||
** ==> functors constructors can differ from the parameters used with their related serial functions
|
||||
*/
|
||||
|
||||
/* Template :
|
||||
class Parallel_ : public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
Parallel_()
|
||||
: {}
|
||||
|
||||
virtual void operator()( const cv::Range& r ) const {
|
||||
|
||||
}
|
||||
}:
|
||||
*/
|
||||
class Parallel_adaptiveHorizontalCausalFilter_addInput: public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
float *outputFrame;
|
||||
const float *inputFrame, *imageGradient;
|
||||
unsigned int nbColumns;
|
||||
public:
|
||||
Parallel_adaptiveHorizontalCausalFilter_addInput(const float *inputImg, float *bufferToProcess, const float *imageGrad, const unsigned int nbCols)
|
||||
:outputFrame(bufferToProcess), inputFrame(inputImg), imageGradient(imageGrad), nbColumns(nbCols) { }
|
||||
|
||||
virtual void operator()( const Range& r ) const CV_OVERRIDE
|
||||
{
|
||||
float* outputPTR=outputFrame+r.start*nbColumns;
|
||||
const float* inputPTR=inputFrame+r.start*nbColumns;
|
||||
const float *imageGradientPTR= imageGradient+r.start*nbColumns;
|
||||
for (int IDrow=r.start; IDrow!=r.end; ++IDrow)
|
||||
{
|
||||
float result=0;
|
||||
for (unsigned int index=0; index<nbColumns; ++index)
|
||||
{
|
||||
result = *(inputPTR++) + (*imageGradientPTR++)* result;
|
||||
*(outputPTR++) = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Parallel_adaptiveVerticalAnticausalFilter_multGain: public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
float *outputFrame;
|
||||
const float *imageGradient;
|
||||
unsigned int nbRows, nbColumns;
|
||||
float filterParam_gain;
|
||||
public:
|
||||
Parallel_adaptiveVerticalAnticausalFilter_multGain(float *bufferToProcess, const float *imageGrad, const unsigned int nbRws, const unsigned int nbCols, const float gain)
|
||||
:outputFrame(bufferToProcess), imageGradient(imageGrad), nbRows(nbRws), nbColumns(nbCols), filterParam_gain(gain) { }
|
||||
|
||||
virtual void operator()( const Range& r ) const CV_OVERRIDE {
|
||||
float* offset=outputFrame+nbColumns*nbRows-nbColumns;
|
||||
const float* gradOffset= imageGradient+nbColumns*nbRows-nbColumns;
|
||||
for (int IDcolumn=r.start; IDcolumn!=r.end; ++IDcolumn)
|
||||
{
|
||||
float result=0;
|
||||
float *outputPTR=offset+IDcolumn;
|
||||
const float *imageGradientPTR=gradOffset+IDcolumn;
|
||||
for (unsigned int index=0; index<nbRows; ++index)
|
||||
{
|
||||
result = *(outputPTR) + *(imageGradientPTR) * result;
|
||||
*(outputPTR) = filterParam_gain*result;
|
||||
outputPTR-=nbColumns;
|
||||
imageGradientPTR-=nbColumns;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Parallel_computeGradient: public cv::ParallelLoopBody
|
||||
{
|
||||
protected:
|
||||
float *imageGradient;
|
||||
const float *luminance;
|
||||
unsigned int nbColumns, doubleNbColumns, nbRows, nbPixels;
|
||||
public:
|
||||
Parallel_computeGradient(const unsigned int nbCols, const unsigned int nbRws, const float *lum, float *imageGrad)
|
||||
:imageGradient(imageGrad), luminance(lum), nbColumns(nbCols), doubleNbColumns(2*nbCols), nbRows(nbRws), nbPixels(nbRws*nbCols) { }
|
||||
|
||||
virtual void operator()( const Range& r ) const CV_OVERRIDE {
|
||||
for (int idLine=r.start;idLine!=r.end;++idLine)
|
||||
{
|
||||
for (unsigned int idColumn=2;idColumn<nbColumns-2;++idColumn)
|
||||
{
|
||||
const unsigned int pixelIndex=idColumn+nbColumns*idLine;
|
||||
|
||||
// horizontal and vertical local gradients
|
||||
const float verticalGrad=fabs(luminance[pixelIndex+nbColumns]-luminance[pixelIndex-nbColumns]);
|
||||
const float horizontalGrad=fabs(luminance[pixelIndex+1]-luminance[pixelIndex-1]);
|
||||
|
||||
// neighborhood horizontal and vertical gradients
|
||||
const float verticalGrad_p=fabs(luminance[pixelIndex]-luminance[pixelIndex-doubleNbColumns]);
|
||||
const float horizontalGrad_p=fabs(luminance[pixelIndex]-luminance[pixelIndex-2]);
|
||||
const float verticalGrad_n=fabs(luminance[pixelIndex+doubleNbColumns]-luminance[pixelIndex]);
|
||||
const float horizontalGrad_n=fabs(luminance[pixelIndex+2]-luminance[pixelIndex]);
|
||||
|
||||
const float horizontalGradient=0.5f*horizontalGrad+0.25f*(horizontalGrad_p+horizontalGrad_n);
|
||||
const float verticalGradient=0.5f*verticalGrad+0.25f*(verticalGrad_p+verticalGrad_n);
|
||||
|
||||
// compare local gradient means and fill the appropriate filtering coefficient value that will be used in adaptative filters
|
||||
if (horizontalGradient<verticalGradient)
|
||||
{
|
||||
imageGradient[pixelIndex+nbPixels]=0.06f;
|
||||
imageGradient[pixelIndex]=0.57f;
|
||||
}
|
||||
else
|
||||
{
|
||||
imageGradient[pixelIndex+nbPixels]=0.57f;
|
||||
imageGradient[pixelIndex]=0.06f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
};
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
|
||||
#endif /*RETINACOLOR_HPP_*/
|
||||
@@ -0,0 +1,318 @@
|
||||
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2013
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
**
|
||||
** This class is based on image processing tools of the author and already used within the Retina class (this is the same code as method retina::applyFastToneMapping, but in an independent class, it is light from a memory requirement point of view). It implements an adaptation of the efficient tone mapping algorithm propose by David Alleyson, Sabine Susstruck and Laurence Meylan's work, please cite:
|
||||
** -> Meylan L., Alleysson D., and Susstrunk S., A Model of Retinal Local Adaptation for the Tone Mapping of Color Filter Array Images, Journal of Optical Society of America, A, Vol. 24, N 9, September, 1st, 2007, pp. 2807-2816
|
||||
**
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
* retinafasttonemapping.cpp
|
||||
*
|
||||
* Created on: May 26, 2013
|
||||
* Author: Alexandre Benoit
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "basicretinafilter.hpp"
|
||||
#include "retinacolor.hpp"
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <valarray>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
/**
|
||||
* @class RetinaFastToneMappingImpl a wrapper class which allows the tone mapping algorithm of Meylan&al(2007) to be used with OpenCV.
|
||||
* This algorithm is already implemented in thre Retina class (retina::applyFastToneMapping) but used it does not require all the retina model to be allocated. This allows a light memory use for low memory devices (smartphones, etc.
|
||||
* As a summary, these are the model properties:
|
||||
* => 2 stages of local luminance adaptation with a different local neighborhood for each.
|
||||
* => first stage models the retina photorecetors local luminance adaptation
|
||||
* => second stage models th ganglion cells local information adaptation
|
||||
* => compared to the initial publication, this class uses spatio-temporal low pass filters instead of spatial only filters.
|
||||
* ====> this can help noise robustness and temporal stability for video sequence use cases.
|
||||
* for more information, read to the following papers :
|
||||
* Meylan L., Alleysson D., and Susstrunk S., A Model of Retinal Local Adaptation for the Tone Mapping of Color Filter Array Images, Journal of Optical Society of America, A, Vol. 24, N 9, September, 1st, 2007, pp. 2807-2816Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
* regarding spatio-temporal filter and the bigger retina model :
|
||||
* Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
*/
|
||||
|
||||
class RetinaFastToneMappingImpl : public RetinaFastToneMapping
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* constructor
|
||||
* @param imageInput: the size of the images to process
|
||||
*/
|
||||
RetinaFastToneMappingImpl(Size imageInput)
|
||||
{
|
||||
unsigned int nbPixels=imageInput.height*imageInput.width;
|
||||
|
||||
// basic error check
|
||||
if (nbPixels <= 0)
|
||||
CV_Error(cv::Error::StsError, "Bad retina size setup : size height and with must be superior to zero");
|
||||
|
||||
// resize buffers
|
||||
_inputBuffer.resize(nbPixels*3); // buffer supports gray images but also 3 channels color buffers... (larger is better...)
|
||||
_imageOutput.resize(nbPixels*3);
|
||||
_temp2.resize(nbPixels);
|
||||
// allocate the main filter with 2 setup sets properties (one for each low pass filter
|
||||
_multiuseFilter = makePtr<BasicRetinaFilter>(imageInput.height, imageInput.width, 2);
|
||||
// allocate the color manager (multiplexer/demultiplexer
|
||||
_colorEngine = makePtr<RetinaColor>(imageInput.height, imageInput.width);
|
||||
// setup filter behaviors with default values
|
||||
setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* basic destructor
|
||||
*/
|
||||
virtual ~RetinaFastToneMappingImpl() { }
|
||||
|
||||
/**
|
||||
* method that applies a luminance correction (initially High Dynamic Range (HDR) tone mapping) using only the 2 local adaptation stages of the retina parvocellular channel : photoreceptors level and ganlion cells level. Spatio temporal filtering is applied but limited to temporal smoothing and eventually high frequencies attenuation. This is a lighter method than the one available using the regular retina::run method. It is then faster but it does not include complete temporal filtering nor retina spectral whitening. Then, it can have a more limited effect on images with a very high dynamic range. This is an adptation of the original still image HDR tone mapping algorithm of David Alleyson, Sabine Susstruck and Laurence Meylan's work, please cite:
|
||||
* -> Meylan L., Alleysson D., and Susstrunk S., A Model of Retinal Local Adaptation for the Tone Mapping of Color Filter Array Images, Journal of Optical Society of America, A, Vol. 24, N 9, September, 1st, 2007, pp. 2807-2816
|
||||
@param inputImage the input image to process RGB or gray levels
|
||||
@param outputToneMappedImage the output tone mapped image
|
||||
*/
|
||||
virtual void applyFastToneMapping(InputArray inputImage, OutputArray outputToneMappedImage) CV_OVERRIDE
|
||||
{
|
||||
// first convert input image to the compatible format :
|
||||
const bool colorMode = _convertCvMat2ValarrayBuffer(inputImage.getMat(), _inputBuffer);
|
||||
|
||||
// process tone mapping
|
||||
if (colorMode)
|
||||
{
|
||||
_runRGBToneMapping(_inputBuffer, _imageOutput, true);
|
||||
_convertValarrayBuffer2cvMat(_imageOutput, _multiuseFilter->getNBrows(), _multiuseFilter->getNBcolumns(), true, outputToneMappedImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
_runGrayToneMapping(_inputBuffer, _imageOutput);
|
||||
_convertValarrayBuffer2cvMat(_imageOutput, _multiuseFilter->getNBrows(), _multiuseFilter->getNBcolumns(), false, outputToneMappedImage);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* setup method that updates tone mapping behaviors by adjusing the local luminance computation area
|
||||
* @param photoreceptorsNeighborhoodRadius the first stage local adaptation area
|
||||
* @param ganglioncellsNeighborhoodRadius the second stage local adaptation area
|
||||
* @param meanLuminanceModulatorK the factor applied to modulate the meanLuminance information (default is 1, see reference paper)
|
||||
*/
|
||||
virtual void setup(const float photoreceptorsNeighborhoodRadius=3.f, const float ganglioncellsNeighborhoodRadius=1.f, const float meanLuminanceModulatorK=1.f) CV_OVERRIDE
|
||||
{
|
||||
// setup the spatio-temporal properties of each filter
|
||||
_meanLuminanceModulatorK = meanLuminanceModulatorK;
|
||||
_multiuseFilter->setV0CompressionParameter(1.f, 255.f, 128.f);
|
||||
_multiuseFilter->setLPfilterParameters(0.f, 0.f, photoreceptorsNeighborhoodRadius, 1);
|
||||
_multiuseFilter->setLPfilterParameters(0.f, 0.f, ganglioncellsNeighborhoodRadius, 2);
|
||||
}
|
||||
|
||||
private:
|
||||
// a filter able to perform local adaptation and low pass spatio-temporal filtering
|
||||
cv::Ptr <BasicRetinaFilter> _multiuseFilter;
|
||||
cv::Ptr <RetinaColor> _colorEngine;
|
||||
|
||||
//!< buffer used to convert input cv::Mat to internal retina buffers format (valarrays)
|
||||
std::valarray<float> _inputBuffer;
|
||||
std::valarray<float> _imageOutput;
|
||||
std::valarray<float> _temp2;
|
||||
float _meanLuminanceModulatorK;
|
||||
|
||||
|
||||
void _convertValarrayBuffer2cvMat(const std::valarray<float> &grayMatrixToConvert, const unsigned int nbRows, const unsigned int nbColumns, const bool colorMode, OutputArray outBuffer)
|
||||
{
|
||||
// fill output buffer with the valarray buffer
|
||||
const float *valarrayPTR=get_data(grayMatrixToConvert);
|
||||
if (!colorMode)
|
||||
{
|
||||
outBuffer.create(cv::Size(nbColumns, nbRows), CV_8U);
|
||||
Mat outMat = outBuffer.getMat();
|
||||
for (unsigned int i=0;i<nbRows;++i)
|
||||
{
|
||||
for (unsigned int j=0;j<nbColumns;++j)
|
||||
{
|
||||
cv::Point2d pixel(j,i);
|
||||
outMat.at<unsigned char>(pixel)=(unsigned char)*(valarrayPTR++);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned int nbPixels=nbColumns*nbRows;
|
||||
const unsigned int doubleNBpixels=nbColumns*nbRows*2;
|
||||
outBuffer.create(cv::Size(nbColumns, nbRows), CV_8UC3);
|
||||
Mat outMat = outBuffer.getMat();
|
||||
for (unsigned int i=0;i<nbRows;++i)
|
||||
{
|
||||
for (unsigned int j=0;j<nbColumns;++j,++valarrayPTR)
|
||||
{
|
||||
cv::Point2d pixel(j,i);
|
||||
cv::Vec3b pixelValues;
|
||||
pixelValues[2]=(unsigned char)*(valarrayPTR);
|
||||
pixelValues[1]=(unsigned char)*(valarrayPTR+nbPixels);
|
||||
pixelValues[0]=(unsigned char)*(valarrayPTR+doubleNBpixels);
|
||||
|
||||
outMat.at<cv::Vec3b>(pixel)=pixelValues;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _convertCvMat2ValarrayBuffer(InputArray inputMat, std::valarray<float> &outputValarrayMatrix)
|
||||
{
|
||||
const Mat inputMatToConvert=inputMat.getMat();
|
||||
// first check input consistency
|
||||
if (inputMatToConvert.empty())
|
||||
CV_Error(cv::Error::StsError, "RetinaImpl cannot be applied, input buffer is empty");
|
||||
|
||||
// retreive color mode from image input
|
||||
int imageNumberOfChannels = inputMatToConvert.channels();
|
||||
|
||||
// convert to float AND fill the valarray buffer
|
||||
typedef float T; // define here the target pixel format, here, float
|
||||
const int dsttype = DataType<T>::depth; // output buffer is float format
|
||||
|
||||
const unsigned int nbPixels=inputMat.getMat().rows*inputMat.getMat().cols;
|
||||
const unsigned int doubleNBpixels=inputMat.getMat().rows*inputMat.getMat().cols*2;
|
||||
|
||||
if(imageNumberOfChannels==4)
|
||||
{
|
||||
// create a cv::Mat table (for RGBA planes)
|
||||
cv::Mat planes[4] =
|
||||
{
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[doubleNBpixels]),
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[nbPixels]),
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[0])
|
||||
};
|
||||
planes[3] = cv::Mat(inputMatToConvert.size(), dsttype); // last channel (alpha) does not point on the valarray (not usefull in our case)
|
||||
// split color cv::Mat in 4 planes... it fills valarray directely
|
||||
cv::split(Mat_<Vec<T, 4> >(inputMatToConvert), planes);
|
||||
}
|
||||
else if (imageNumberOfChannels==3)
|
||||
{
|
||||
// create a cv::Mat table (for RGB planes)
|
||||
cv::Mat planes[] =
|
||||
{
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[doubleNBpixels]),
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[nbPixels]),
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[0])
|
||||
};
|
||||
// split color cv::Mat in 3 planes... it fills valarray directely
|
||||
cv::split(cv::Mat_<Vec<T, 3> >(inputMatToConvert), planes);
|
||||
}
|
||||
else if(imageNumberOfChannels==1)
|
||||
{
|
||||
// create a cv::Mat header for the valarray
|
||||
cv::Mat dst(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[0]);
|
||||
inputMatToConvert.convertTo(dst, dsttype);
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsUnsupportedFormat, "input image must be single channel (gray levels), bgr format (color) or bgra (color with transparency which won't be considered");
|
||||
|
||||
return imageNumberOfChannels>1; // return bool : false for gray level image processing, true for color mode
|
||||
}
|
||||
|
||||
|
||||
// run the initilized retina filter in order to perform gray image tone mapping, after this call all retina outputs are updated
|
||||
void _runGrayToneMapping(const std::valarray<float> &grayImageInput, std::valarray<float> &grayImageOutput)
|
||||
{
|
||||
// apply tone mapping on the multiplexed image
|
||||
// -> photoreceptors local adaptation (large area adaptation)
|
||||
_multiuseFilter->runFilter_LPfilter(grayImageInput, grayImageOutput, 0); // compute low pass filtering modeling the horizontal cells filtering to acess local luminance
|
||||
_multiuseFilter->setV0CompressionParameterToneMapping(1.f, grayImageOutput.max(), _meanLuminanceModulatorK*grayImageOutput.sum()/(float)_multiuseFilter->getNBpixels());
|
||||
_multiuseFilter->runFilter_LocalAdapdation(grayImageInput, grayImageOutput, _temp2); // adapt contrast to local luminance
|
||||
|
||||
// -> ganglion cells local adaptation (short area adaptation)
|
||||
_multiuseFilter->runFilter_LPfilter(_temp2, grayImageOutput, 1); // compute low pass filtering (high cut frequency (remove spatio-temporal noise)
|
||||
_multiuseFilter->setV0CompressionParameterToneMapping(1.f, _temp2.max(), _meanLuminanceModulatorK*grayImageOutput.sum()/(float)_multiuseFilter->getNBpixels());
|
||||
_multiuseFilter->runFilter_LocalAdapdation(_temp2, grayImageOutput, grayImageOutput); // adapt contrast to local luminance
|
||||
|
||||
}
|
||||
|
||||
// run the initilized retina filter in order to perform color tone mapping, after this call all retina outputs are updated
|
||||
void _runRGBToneMapping(const std::valarray<float> &RGBimageInput, std::valarray<float> &RGBimageOutput, const bool useAdaptiveFiltering)
|
||||
{
|
||||
// multiplex the image with the color sampling method specified in the constructor
|
||||
_colorEngine->runColorMultiplexing(RGBimageInput);
|
||||
|
||||
// apply tone mapping on the multiplexed image
|
||||
_runGrayToneMapping(_colorEngine->getMultiplexedFrame(), RGBimageOutput);
|
||||
|
||||
// demultiplex tone maped image
|
||||
_colorEngine->runColorDemultiplexing(RGBimageOutput, useAdaptiveFiltering, _multiuseFilter->getMaxInputValue());//_ColorEngine->getMultiplexedFrame());//_ParvoRetinaFilter->getPhotoreceptorsLPfilteringOutput());
|
||||
|
||||
// rescaling result between 0 and 255
|
||||
_colorEngine->normalizeRGBOutput_0_maxOutputValue(255.0);
|
||||
|
||||
// return the result
|
||||
RGBimageOutput=_colorEngine->getDemultiplexedColorFrame();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Ptr<RetinaFastToneMapping> RetinaFastToneMapping::create(Size inputSize)
|
||||
{
|
||||
return makePtr<RetinaFastToneMappingImpl>(inputSize);
|
||||
}
|
||||
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
@@ -0,0 +1,526 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2011
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "retinafilter.hpp"
|
||||
|
||||
// @author Alexandre BENOIT, benoit.alexandre.vision@gmail.com, LISTIC : www.listic.univ-savoie.fr, Gipsa-Lab, France: www.gipsa-lab.inpg.fr/
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
// standard constructor without any log sampling of the input frame
|
||||
RetinaFilter::RetinaFilter(const unsigned int sizeRows, const unsigned int sizeColumns, const bool colorMode, const int samplingMethod, const bool useRetinaLogSampling, const double reductionFactor, const double samplingStrength)
|
||||
:
|
||||
_retinaParvoMagnoMappedFrame(0),
|
||||
_retinaParvoMagnoMapCoefTable(0),
|
||||
_photoreceptorsPrefilter((1-(int)useRetinaLogSampling)*sizeRows+useRetinaLogSampling*ImageLogPolProjection::predictOutputSize(sizeRows, reductionFactor), (1-(int)useRetinaLogSampling)*sizeColumns+useRetinaLogSampling*ImageLogPolProjection::predictOutputSize(sizeColumns, reductionFactor), 4),
|
||||
_ParvoRetinaFilter((1-(int)useRetinaLogSampling)*sizeRows+useRetinaLogSampling*ImageLogPolProjection::predictOutputSize(sizeRows, reductionFactor), (1-(int)useRetinaLogSampling)*sizeColumns+useRetinaLogSampling*ImageLogPolProjection::predictOutputSize(sizeColumns, reductionFactor)),
|
||||
_MagnoRetinaFilter((1-(int)useRetinaLogSampling)*sizeRows+useRetinaLogSampling*ImageLogPolProjection::predictOutputSize(sizeRows, reductionFactor), (1-(int)useRetinaLogSampling)*sizeColumns+useRetinaLogSampling*ImageLogPolProjection::predictOutputSize(sizeColumns, reductionFactor)),
|
||||
_colorEngine((1-(int)useRetinaLogSampling)*sizeRows+useRetinaLogSampling*ImageLogPolProjection::predictOutputSize(sizeRows, reductionFactor), (1-(int)useRetinaLogSampling)*sizeColumns+useRetinaLogSampling*ImageLogPolProjection::predictOutputSize(sizeColumns, reductionFactor), samplingMethod),
|
||||
// configure retina photoreceptors log sampling... if necessary
|
||||
_photoreceptorsLogSampling(NULL)
|
||||
{
|
||||
|
||||
#ifdef RETINADEBUG
|
||||
std::cout<<"RetinaFilter::size( "<<_photoreceptorsPrefilter.getNBrows()<<", "<<_photoreceptorsPrefilter.getNBcolumns()<<")"<<" =? "<<_photoreceptorsPrefilter.getNBpixels()<<std::endl;
|
||||
#endif
|
||||
if (useRetinaLogSampling)
|
||||
{
|
||||
_photoreceptorsLogSampling = new ImageLogPolProjection(sizeRows, sizeColumns, ImageLogPolProjection::RETINALOGPROJECTION, true);
|
||||
if (!_photoreceptorsLogSampling->initProjection(reductionFactor, samplingStrength))
|
||||
{
|
||||
std::cerr<<"RetinaFilter::Problem initializing photoreceptors log sampling, could not setup retina filter"<<std::endl;
|
||||
delete _photoreceptorsLogSampling;
|
||||
_photoreceptorsLogSampling=NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef RETINADEBUG
|
||||
std::cout<<"_photoreceptorsLogSampling::size( "<<_photoreceptorsLogSampling->getNBrows()<<", "<<_photoreceptorsLogSampling->getNBcolumns()<<")"<<" =? "<<_photoreceptorsLogSampling->getNBpixels()<<std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// set default processing activities
|
||||
_useParvoOutput=true;
|
||||
_useMagnoOutput=true;
|
||||
|
||||
_useColorMode=colorMode;
|
||||
|
||||
// create hybrid output and related coefficient table
|
||||
_createHybridTable();
|
||||
|
||||
// set default parameters
|
||||
setGlobalParameters();
|
||||
|
||||
// stability controls values init
|
||||
_setInitPeriodCount();
|
||||
_globalTemporalConstant=25;
|
||||
|
||||
// reset all buffers
|
||||
clearAllBuffers();
|
||||
|
||||
|
||||
// std::cout<<"RetinaFilter::size( "<<this->getNBrows()<<", "<<this->getNBcolumns()<<")"<<_filterOutput.size()<<" =? "<<_filterOutput.getNBpixels()<<std::endl;
|
||||
|
||||
}
|
||||
|
||||
// destructor
|
||||
RetinaFilter::~RetinaFilter()
|
||||
{
|
||||
if (_photoreceptorsLogSampling!=NULL)
|
||||
delete _photoreceptorsLogSampling;
|
||||
}
|
||||
|
||||
// function that clears all buffers of the object
|
||||
void RetinaFilter::clearAllBuffers()
|
||||
{
|
||||
_photoreceptorsPrefilter.clearAllBuffers();
|
||||
_ParvoRetinaFilter.clearAllBuffers();
|
||||
_MagnoRetinaFilter.clearAllBuffers();
|
||||
_colorEngine.clearAllBuffers();
|
||||
if (_photoreceptorsLogSampling!=NULL)
|
||||
_photoreceptorsLogSampling->clearAllBuffers();
|
||||
// stability controls value init
|
||||
_setInitPeriodCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* resize retina filter object (resize all allocated buffers
|
||||
* @param NBrows: the new height size
|
||||
* @param NBcolumns: the new width size
|
||||
*/
|
||||
void RetinaFilter::resize(const unsigned int NBrows, const unsigned int NBcolumns)
|
||||
{
|
||||
unsigned int rows=NBrows, cols=NBcolumns;
|
||||
|
||||
// resize optionnal member and adjust other modules size if required
|
||||
if (_photoreceptorsLogSampling)
|
||||
{
|
||||
_photoreceptorsLogSampling->resize(NBrows, NBcolumns);
|
||||
rows=_photoreceptorsLogSampling->getOutputNBrows();
|
||||
cols=_photoreceptorsLogSampling->getOutputNBcolumns();
|
||||
}
|
||||
|
||||
_photoreceptorsPrefilter.resize(rows, cols);
|
||||
_ParvoRetinaFilter.resize(rows, cols);
|
||||
_MagnoRetinaFilter.resize(rows, cols);
|
||||
_colorEngine.resize(rows, cols);
|
||||
|
||||
// reset parvo magno mapping
|
||||
_createHybridTable();
|
||||
|
||||
// clean buffers
|
||||
clearAllBuffers();
|
||||
|
||||
}
|
||||
|
||||
// stability controls value init
|
||||
void RetinaFilter::_setInitPeriodCount()
|
||||
{
|
||||
|
||||
// find out the maximum temporal constant value and apply a security factor
|
||||
// false value (obviously too long) but appropriate for simple use
|
||||
_globalTemporalConstant=(unsigned int)(_ParvoRetinaFilter.getPhotoreceptorsTemporalConstant()+_ParvoRetinaFilter.getHcellsTemporalConstant()+_MagnoRetinaFilter.getTemporalConstant());
|
||||
// reset frame counter
|
||||
_ellapsedFramesSinceLastReset=0;
|
||||
}
|
||||
|
||||
void RetinaFilter::_createHybridTable()
|
||||
{
|
||||
// create hybrid output and related coefficient table
|
||||
_retinaParvoMagnoMappedFrame.resize(_photoreceptorsPrefilter.getNBpixels());
|
||||
|
||||
_retinaParvoMagnoMapCoefTable.resize(_photoreceptorsPrefilter.getNBpixels()*2);
|
||||
|
||||
// fill _hybridParvoMagnoCoefTable
|
||||
int i, j, halfRows=_photoreceptorsPrefilter.getNBrows()/2, halfColumns=_photoreceptorsPrefilter.getNBcolumns()/2;
|
||||
float *hybridParvoMagnoCoefTablePTR= &_retinaParvoMagnoMapCoefTable[0];
|
||||
float minDistance=MIN(halfRows, halfColumns)*0.7f;
|
||||
for (i=0;i<(int)_photoreceptorsPrefilter.getNBrows();++i)
|
||||
{
|
||||
for (j=0;j<(int)_photoreceptorsPrefilter.getNBcolumns();++j)
|
||||
{
|
||||
float distanceToCenter=std::sqrt(((float)(i-halfRows)*(i-halfRows)+(j-halfColumns)*(j-halfColumns)));
|
||||
if (distanceToCenter<minDistance)
|
||||
{
|
||||
float a=*(hybridParvoMagnoCoefTablePTR++)=0.5f+0.5f*(float)cos(CV_PI*distanceToCenter/minDistance);
|
||||
*(hybridParvoMagnoCoefTablePTR++)=1.f-a;
|
||||
}else
|
||||
{
|
||||
*(hybridParvoMagnoCoefTablePTR++)=0.f;
|
||||
*(hybridParvoMagnoCoefTablePTR++)=1.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setup parameters function and global data filling
|
||||
void RetinaFilter::setGlobalParameters(const float OPLspatialResponse1, const float OPLtemporalresponse1, const float OPLassymetryGain, const float OPLspatialResponse2, const float OPLtemporalresponse2, const float LPfilterSpatialResponse, const float LPfilterGain, const float LPfilterTemporalresponse, const float MovingContoursExtractorCoefficient, const bool normalizeParvoOutput_0_maxOutputValue, const bool normalizeMagnoOutput_0_maxOutputValue, const float maxOutputValue, const float maxInputValue, const float meanValue)
|
||||
{
|
||||
_normalizeParvoOutput_0_maxOutputValue=normalizeParvoOutput_0_maxOutputValue;
|
||||
_normalizeMagnoOutput_0_maxOutputValue=normalizeMagnoOutput_0_maxOutputValue;
|
||||
_maxOutputValue=maxOutputValue;
|
||||
_photoreceptorsPrefilter.setV0CompressionParameter(0.9f, maxInputValue, meanValue);
|
||||
_photoreceptorsPrefilter.setLPfilterParameters(10, 0, 1.5, 1); // keeps low pass filter with high cut frequency in memory (usefull for the tone mapping function)
|
||||
_photoreceptorsPrefilter.setLPfilterParameters(10, 0, 3.0, 2); // keeps low pass filter with low cut frequency in memory (usefull for the tone mapping function)
|
||||
_photoreceptorsPrefilter.setLPfilterParameters(0, 0, 10, 3); // keeps low pass filter with low cut frequency in memory (usefull for the tone mapping function)
|
||||
//this->setV0CompressionParameter(0.6, maxInputValue, meanValue); // keeps log compression sensitivity parameter (usefull for the tone mapping function)
|
||||
_ParvoRetinaFilter.setOPLandParvoFiltersParameters(0,OPLtemporalresponse1, OPLspatialResponse1, OPLassymetryGain, OPLtemporalresponse2, OPLspatialResponse2);
|
||||
_ParvoRetinaFilter.setV0CompressionParameter(0.9f, maxInputValue, meanValue);
|
||||
_MagnoRetinaFilter.setCoefficientsTable(LPfilterGain, LPfilterTemporalresponse, LPfilterSpatialResponse, MovingContoursExtractorCoefficient, 0, 2.0f*LPfilterSpatialResponse);
|
||||
_MagnoRetinaFilter.setV0CompressionParameter(0.7f, maxInputValue, meanValue);
|
||||
|
||||
// stability controls value init
|
||||
_setInitPeriodCount();
|
||||
}
|
||||
|
||||
bool RetinaFilter::checkInput(const std::valarray<float> &input, const bool)
|
||||
{
|
||||
|
||||
BasicRetinaFilter *inputTarget=&_photoreceptorsPrefilter;
|
||||
if (_photoreceptorsLogSampling)
|
||||
inputTarget=_photoreceptorsLogSampling;
|
||||
|
||||
bool test=input.size()==inputTarget->getNBpixels() || input.size()==(inputTarget->getNBpixels()*3) ;
|
||||
if (!test)
|
||||
{
|
||||
std::cerr<<"RetinaFilter::checkInput: input buffer does not match retina buffer size, conversion aborted"<<std::endl;
|
||||
std::cout<<"RetinaFilter::checkInput: input size="<<input.size()<<" / "<<"retina size="<<inputTarget->getNBpixels()<<std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// main function that runs the filter for a given input frame
|
||||
bool RetinaFilter::runFilter(const std::valarray<float> &imageInput, const bool useAdaptiveFiltering, const bool processRetinaParvoMagnoMapping, const bool useColorMode, const bool inputIsColorMultiplexed)
|
||||
{
|
||||
// preliminary check
|
||||
bool processSuccess=true;
|
||||
if (!checkInput(imageInput, useColorMode))
|
||||
return false;
|
||||
|
||||
// run the color multiplexing if needed and compute each suub filter of the retina:
|
||||
// -> local adaptation
|
||||
// -> contours OPL extraction
|
||||
// -> moving contours extraction
|
||||
|
||||
// stability controls value update
|
||||
++_ellapsedFramesSinceLastReset;
|
||||
|
||||
_useColorMode=useColorMode;
|
||||
|
||||
/* pointer to the appropriate input data after,
|
||||
* by default, if graylevel mode, the input is processed,
|
||||
* if color or something else must be considered, specific preprocessing are applied
|
||||
*/
|
||||
|
||||
const std::valarray<float> *selectedPhotoreceptorsLocalAdaptationInput= &imageInput;
|
||||
const std::valarray<float> *selectedPhotoreceptorsColorInput=&imageInput;
|
||||
|
||||
//********** Following is input data specific photoreceptors processing
|
||||
if (_photoreceptorsLogSampling)
|
||||
{
|
||||
_photoreceptorsLogSampling->runProjection(imageInput, useColorMode);
|
||||
selectedPhotoreceptorsColorInput=selectedPhotoreceptorsLocalAdaptationInput=&(_photoreceptorsLogSampling->getSampledFrame());
|
||||
}
|
||||
|
||||
if (useColorMode&& (!inputIsColorMultiplexed)) // not multiplexed color input case
|
||||
{
|
||||
_colorEngine.runColorMultiplexing(*selectedPhotoreceptorsColorInput);
|
||||
selectedPhotoreceptorsLocalAdaptationInput=&(_colorEngine.getMultiplexedFrame());
|
||||
}
|
||||
|
||||
//********** Following is generic Retina processing
|
||||
|
||||
// photoreceptors local adaptation
|
||||
_photoreceptorsPrefilter.runFilter_LocalAdapdation(*selectedPhotoreceptorsLocalAdaptationInput, _ParvoRetinaFilter.getHorizontalCellsOutput());
|
||||
// safety pixel values checks
|
||||
//_photoreceptorsPrefilter.normalizeGrayOutput_0_maxOutputValue(_maxOutputValue);
|
||||
|
||||
// run parvo filter
|
||||
_ParvoRetinaFilter.runFilter(_photoreceptorsPrefilter.getOutput(), _useParvoOutput);
|
||||
|
||||
if (_useParvoOutput)
|
||||
{
|
||||
_ParvoRetinaFilter.normalizeGrayOutputCentredSigmoide(); // models the saturation of the cells, usefull for visualisation of the ON-OFF Parvo Output, Bipolar cells outputs do not change !!!
|
||||
_ParvoRetinaFilter.centerReductImageLuminance(); // best for further spectrum analysis
|
||||
|
||||
if (_normalizeParvoOutput_0_maxOutputValue)
|
||||
_ParvoRetinaFilter.normalizeGrayOutput_0_maxOutputValue(_maxOutputValue);
|
||||
}
|
||||
|
||||
if (_useParvoOutput&&_useMagnoOutput)
|
||||
{
|
||||
_MagnoRetinaFilter.runFilter(_ParvoRetinaFilter.getBipolarCellsON(), _ParvoRetinaFilter.getBipolarCellsOFF());
|
||||
if (_normalizeMagnoOutput_0_maxOutputValue)
|
||||
{
|
||||
_MagnoRetinaFilter.normalizeGrayOutput_0_maxOutputValue(_maxOutputValue);
|
||||
}
|
||||
_MagnoRetinaFilter.normalizeGrayOutputNearZeroCentreredSigmoide();
|
||||
}
|
||||
|
||||
if (_useParvoOutput&&_useMagnoOutput&&processRetinaParvoMagnoMapping)
|
||||
{
|
||||
_processRetinaParvoMagnoMapping();
|
||||
if (_useColorMode)
|
||||
_colorEngine.runColorDemultiplexing(_retinaParvoMagnoMappedFrame, useAdaptiveFiltering, _maxOutputValue);//_ColorEngine->getMultiplexedFrame());//_ParvoRetinaFilter->getPhotoreceptorsLPfilteringOutput());
|
||||
|
||||
return processSuccess;
|
||||
}
|
||||
|
||||
if (_useParvoOutput&&_useColorMode)
|
||||
{
|
||||
_colorEngine.runColorDemultiplexing(_ParvoRetinaFilter.getOutput(), useAdaptiveFiltering, _maxOutputValue);//_ColorEngine->getMultiplexedFrame());//_ParvoRetinaFilter->getPhotoreceptorsLPfilteringOutput());
|
||||
// compute A Cr1 Cr2 to LMS color space conversion
|
||||
//if (true)
|
||||
// _applyImageColorSpaceConversion(_ColorEngine->getChrominance(), lmsTempBuffer.Buffer(), _LMStoACr1Cr2);
|
||||
}
|
||||
|
||||
return processSuccess;
|
||||
}
|
||||
|
||||
const std::valarray<float> &RetinaFilter::getContours()
|
||||
{
|
||||
if (_useColorMode)
|
||||
return _colorEngine.getLuminance();
|
||||
else
|
||||
return _ParvoRetinaFilter.getOutput();
|
||||
}
|
||||
|
||||
// run the initilized retina filter in order to perform gray image tone mapping, after this call all retina outputs are updated
|
||||
void RetinaFilter::runGrayToneMapping(const std::valarray<float> &grayImageInput, std::valarray<float> &grayImageOutput, const float PhotoreceptorsCompression, const float ganglionCellsCompression)
|
||||
{
|
||||
// preliminary check
|
||||
if (!checkInput(grayImageInput, false))
|
||||
return;
|
||||
|
||||
this->_runGrayToneMapping(grayImageInput, grayImageOutput, PhotoreceptorsCompression, ganglionCellsCompression);
|
||||
}
|
||||
|
||||
// run the initilized retina filter in order to perform gray image tone mapping, after this call all retina outputs are updated
|
||||
void RetinaFilter::_runGrayToneMapping(const std::valarray<float> &grayImageInput, std::valarray<float> &grayImageOutput, const float PhotoreceptorsCompression, const float ganglionCellsCompression)
|
||||
{
|
||||
// stability controls value update
|
||||
++_ellapsedFramesSinceLastReset;
|
||||
|
||||
std::valarray<float> temp2(grayImageInput.size());
|
||||
|
||||
// apply tone mapping on the multiplexed image
|
||||
// -> photoreceptors local adaptation (large area adaptation)
|
||||
_photoreceptorsPrefilter.runFilter_LPfilter(grayImageInput, grayImageOutput, 2); // compute low pass filtering modeling the horizontal cells filtering to acess local luminance
|
||||
_photoreceptorsPrefilter.setV0CompressionParameterToneMapping(1.f-PhotoreceptorsCompression, grayImageOutput.max(), 1.f*grayImageOutput.sum()/(float)_photoreceptorsPrefilter.getNBpixels());
|
||||
_photoreceptorsPrefilter.runFilter_LocalAdapdation(grayImageInput, grayImageOutput, temp2); // adapt contrast to local luminance
|
||||
|
||||
// -> ganglion cells local adaptation (short area adaptation)
|
||||
_photoreceptorsPrefilter.runFilter_LPfilter(temp2, grayImageOutput, 1); // compute low pass filtering (high cut frequency (remove spatio-temporal noise)
|
||||
_photoreceptorsPrefilter.setV0CompressionParameterToneMapping(1.f-ganglionCellsCompression, temp2.max(), 1.f*temp2.sum()/(float)_photoreceptorsPrefilter.getNBpixels());
|
||||
_photoreceptorsPrefilter.runFilter_LocalAdapdation(temp2, grayImageOutput, grayImageOutput); // adapt contrast to local luminance
|
||||
}
|
||||
|
||||
// run the initilized retina filter in order to perform color tone mapping, after this call all retina outputs are updated
|
||||
void RetinaFilter::runRGBToneMapping(const std::valarray<float> &RGBimageInput, std::valarray<float> &RGBimageOutput, const bool useAdaptiveFiltering, const float PhotoreceptorsCompression, const float ganglionCellsCompression)
|
||||
{
|
||||
// preliminary check
|
||||
if (!checkInput(RGBimageInput, true))
|
||||
return;
|
||||
|
||||
// multiplex the image with the color sampling method specified in the constructor
|
||||
_colorEngine.runColorMultiplexing(RGBimageInput);
|
||||
|
||||
// apply tone mapping on the multiplexed image
|
||||
_runGrayToneMapping(_colorEngine.getMultiplexedFrame(), RGBimageOutput, PhotoreceptorsCompression, ganglionCellsCompression);
|
||||
|
||||
// demultiplex tone maped image
|
||||
_colorEngine.runColorDemultiplexing(RGBimageOutput, useAdaptiveFiltering, _photoreceptorsPrefilter.getMaxInputValue());//_ColorEngine->getMultiplexedFrame());//_ParvoRetinaFilter->getPhotoreceptorsLPfilteringOutput());
|
||||
|
||||
// rescaling result between 0 and 255
|
||||
_colorEngine.normalizeRGBOutput_0_maxOutputValue(255.0);
|
||||
|
||||
// return the result
|
||||
RGBimageOutput=_colorEngine.getDemultiplexedColorFrame();
|
||||
}
|
||||
|
||||
void RetinaFilter::runLMSToneMapping(const std::valarray<float> &, std::valarray<float> &, const bool, const float, const float)
|
||||
{
|
||||
std::cerr<<"not working, sorry"<<std::endl;
|
||||
|
||||
/* // preliminary check
|
||||
const std::valarray<float> &bufferInput=checkInput(LMSimageInput, true);
|
||||
if (!bufferInput)
|
||||
return NULL;
|
||||
|
||||
if (!_useColorMode)
|
||||
std::cerr<<"RetinaFilter::Can not call tone mapping oeration if the retina filter was created for gray scale images"<<std::endl;
|
||||
|
||||
// create a temporary buffer of size nrows, Mcolumns, 3 layers
|
||||
std::valarray<float> lmsTempBuffer(LMSimageInput);
|
||||
std::cout<<"RetinaFilter::--->min LMS value="<<lmsTempBuffer.min()<<std::endl;
|
||||
|
||||
// setup local adaptation parameter at the photoreceptors level
|
||||
setV0CompressionParameter(PhotoreceptorsCompression, _maxInputValue);
|
||||
// get the local energy of each color channel
|
||||
// ->L
|
||||
_spatiotemporalLPfilter(LMSimageInput, _filterOutput, 1);
|
||||
setV0CompressionParameterToneMapping(PhotoreceptorsCompression, _maxInputValue, this->sum()/_NBpixels);
|
||||
_localLuminanceAdaptation(LMSimageInput, _filterOutput, lmsTempBuffer.Buffer());
|
||||
// ->M
|
||||
_spatiotemporalLPfilter(LMSimageInput+_NBpixels, _filterOutput, 1);
|
||||
setV0CompressionParameterToneMapping(PhotoreceptorsCompression, _maxInputValue, this->sum()/_NBpixels);
|
||||
_localLuminanceAdaptation(LMSimageInput+_NBpixels, _filterOutput, lmsTempBuffer.Buffer()+_NBpixels);
|
||||
// ->S
|
||||
_spatiotemporalLPfilter(LMSimageInput+_NBpixels*2, _filterOutput, 1);
|
||||
setV0CompressionParameterToneMapping(PhotoreceptorsCompression, _maxInputValue, this->sum()/_NBpixels);
|
||||
_localLuminanceAdaptation(LMSimageInput+_NBpixels*2, _filterOutput, lmsTempBuffer.Buffer()+_NBpixels*2);
|
||||
|
||||
// eliminate negative values
|
||||
for (unsigned int i=0;i<lmsTempBuffer.size();++i)
|
||||
if (lmsTempBuffer.Buffer()[i]<0)
|
||||
lmsTempBuffer.Buffer()[i]=0;
|
||||
std::cout<<"RetinaFilter::->min LMS value="<<lmsTempBuffer.min()<<std::endl;
|
||||
|
||||
// compute LMS to A Cr1 Cr2 color space conversion
|
||||
_applyImageColorSpaceConversion(lmsTempBuffer.Buffer(), lmsTempBuffer.Buffer(), _LMStoACr1Cr2);
|
||||
|
||||
TemplateBuffer <float> acr1cr2TempBuffer(_NBrows, _NBcolumns, 3);
|
||||
memcpy(acr1cr2TempBuffer.Buffer(), lmsTempBuffer.Buffer(), sizeof(float)*_NBpixels*3);
|
||||
|
||||
// compute A Cr1 Cr2 to LMS color space conversion
|
||||
_applyImageColorSpaceConversion(acr1cr2TempBuffer.Buffer(), lmsTempBuffer.Buffer(), _ACr1Cr2toLMS);
|
||||
|
||||
// eliminate negative values
|
||||
for (unsigned int i=0;i<lmsTempBuffer.size();++i)
|
||||
if (lmsTempBuffer.Buffer()[i]<0)
|
||||
lmsTempBuffer.Buffer()[i]=0;
|
||||
|
||||
// rewrite output to the appropriate buffer
|
||||
_colorEngine->setDemultiplexedColorFrame(lmsTempBuffer.Buffer());
|
||||
*/
|
||||
}
|
||||
|
||||
// return image with center Parvo and peripheral Magno channels
|
||||
void RetinaFilter::_processRetinaParvoMagnoMapping()
|
||||
{
|
||||
float *hybridParvoMagnoPTR= &_retinaParvoMagnoMappedFrame[0];
|
||||
const float *parvoOutputPTR= get_data(_ParvoRetinaFilter.getOutput());
|
||||
const float *magnoXOutputPTR= get_data(_MagnoRetinaFilter.getOutput());
|
||||
float *hybridParvoMagnoCoefTablePTR= &_retinaParvoMagnoMapCoefTable[0];
|
||||
|
||||
for (unsigned int i=0 ; i<_photoreceptorsPrefilter.getNBpixels() ; ++i, hybridParvoMagnoCoefTablePTR+=2)
|
||||
{
|
||||
float hybridValue=*(parvoOutputPTR++)**(hybridParvoMagnoCoefTablePTR)+*(magnoXOutputPTR++)**(hybridParvoMagnoCoefTablePTR+1);
|
||||
*(hybridParvoMagnoPTR++)=hybridValue;
|
||||
}
|
||||
|
||||
TemplateBuffer<float>::normalizeGrayOutput_0_maxOutputValue(&_retinaParvoMagnoMappedFrame[0], _photoreceptorsPrefilter.getNBpixels());
|
||||
|
||||
}
|
||||
|
||||
bool RetinaFilter::getParvoFoveaResponse(std::valarray<float> &parvoFovealResponse)
|
||||
{
|
||||
if (!_useParvoOutput)
|
||||
return false;
|
||||
if (parvoFovealResponse.size() != _ParvoRetinaFilter.getNBpixels())
|
||||
return false;
|
||||
|
||||
const float *parvoOutputPTR= get_data(_ParvoRetinaFilter.getOutput());
|
||||
float *fovealParvoResponsePTR= &parvoFovealResponse[0];
|
||||
float *hybridParvoMagnoCoefTablePTR= &_retinaParvoMagnoMapCoefTable[0];
|
||||
|
||||
for (unsigned int i=0 ; i<_photoreceptorsPrefilter.getNBpixels() ; ++i, hybridParvoMagnoCoefTablePTR+=2)
|
||||
{
|
||||
*(fovealParvoResponsePTR++)=*(parvoOutputPTR++)**(hybridParvoMagnoCoefTablePTR);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// method to retrieve the parafoveal magnocellular pathway response (no energy motion in fovea)
|
||||
bool RetinaFilter::getMagnoParaFoveaResponse(std::valarray<float> &magnoParafovealResponse)
|
||||
{
|
||||
if (!_useMagnoOutput)
|
||||
return false;
|
||||
if (magnoParafovealResponse.size() != _MagnoRetinaFilter.getNBpixels())
|
||||
return false;
|
||||
|
||||
const float *magnoXOutputPTR= get_data(_MagnoRetinaFilter.getOutput());
|
||||
float *parafovealMagnoResponsePTR=&magnoParafovealResponse[0];
|
||||
float *hybridParvoMagnoCoefTablePTR=&_retinaParvoMagnoMapCoefTable[0]+1;
|
||||
|
||||
for (unsigned int i=0 ; i<_photoreceptorsPrefilter.getNBpixels() ; ++i, hybridParvoMagnoCoefTablePTR+=2)
|
||||
{
|
||||
*(parafovealMagnoResponsePTR++)=*(magnoXOutputPTR++)**(hybridParvoMagnoCoefTablePTR);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
@@ -0,0 +1,548 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2011
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @class RetinaFilter
|
||||
* @brief class which describes the retina model developped at the LIS/GIPSA-LAB www.gipsa-lab.inpg.fr:
|
||||
* -> performs a contours and moving contours extraction with powerfull local data enhancement as at the retina level
|
||||
* Based on Alexandre BENOIT thesis: "Le systeme visuel humain au secours de la vision par ordinateur"
|
||||
*
|
||||
* => various optimisations and enhancements added after 2007 such as tone mapping capabilities, see reference paper cited in the licence and :
|
||||
* Benoit A.,Alleysson D., Herault J., Le Callet P. (2009), "Spatio-Temporal Tone Mapping Operator based on a Retina model", Computational Color Imaging Workshop (CCIW09),pp 12-22, Saint Etienne, France
|
||||
*
|
||||
* TYPICAL USE:
|
||||
*
|
||||
* // create object at a specified picture size
|
||||
* Retina *retina;
|
||||
* retina =new Retina(frameSizeRows, frameSizeColumns, RGBmode);
|
||||
*
|
||||
* // init gain, spatial and temporal parameters:
|
||||
* retina->setParameters(0.7, 1, 0, 7, 1, 5, 0, 0, 3 , true);
|
||||
*
|
||||
* // during program execution, call the filter for local luminance correction, contours extraction, moving contours extraction from an input picture called "FrameBuffer":
|
||||
* retina->runfilter(FrameBuffer);
|
||||
*
|
||||
* // get the different output frames, check in the class description below for more outputs:
|
||||
* const std::valarray<float> correctedLuminance=retina->getLocalAdaptation();
|
||||
* const std::valarray<float> contours=retina->getContours();
|
||||
* const std::valarray<float> movingContours=retina->getMovingContours();
|
||||
*
|
||||
* // at the end of the program, destroy object:
|
||||
* delete retina;
|
||||
*
|
||||
* @author Alexandre BENOIT, benoit.alexandre.vision@gmail.com, LISTIC / Gipsa-Lab, France: www.gipsa-lab.inpg.fr/
|
||||
* Creation date 2007
|
||||
*/
|
||||
|
||||
#ifndef RETINACLASSES_H_
|
||||
#define RETINACLASSES_H_
|
||||
|
||||
#include "basicretinafilter.hpp"
|
||||
#include "parvoretinafilter.hpp"
|
||||
#include "magnoretinafilter.hpp"
|
||||
|
||||
// optional includes (depending on the related publications)
|
||||
#include "imagelogpolprojection.hpp"
|
||||
|
||||
#include "retinacolor.hpp"
|
||||
|
||||
//#define __RETINADEBUG // define RETINADEBUG to display debug data
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
// retina class that process the 3 outputs of the retina filtering stages
|
||||
class RetinaFilter//: public BasicRetinaFilter
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* constructor of the retina filter model with log sampling of the input frame (models the photoreceptors log sampling (central high resolution fovea and lower precision borders))
|
||||
* @param sizeRows: number of rows of the input image
|
||||
* @param sizeColumns: number of columns of the input image
|
||||
* @param colorMode: specifies if the retina works with color (true) of stays in grayscale processing (false), can be adjusted online by the use of setColorMode method
|
||||
* @param samplingMethod: specifies which kind of color sampling will be used
|
||||
* @param useRetinaLogSampling: activate retina log sampling, if true, the 2 following parameters can be used
|
||||
* @param reductionFactor: only usefull if param useRetinaLogSampling=true, specifies the reduction factor of the output frame (as the center (fovea) is high resolution and corners can be underscaled, then a reduction of the output is allowed without precision leak
|
||||
* @param samplingStrength: only usefull if param useRetinaLogSampling=true, specifies the strength of the log scale that is applied
|
||||
*/
|
||||
RetinaFilter(const unsigned int sizeRows, const unsigned int sizeColumns, const bool colorMode=false, const int samplingMethod=RETINA_COLOR_BAYER, const bool useRetinaLogSampling=false, const double reductionFactor=1.0, const double samplingStrength=10.0);
|
||||
|
||||
/**
|
||||
* standard destructor
|
||||
*/
|
||||
~RetinaFilter();
|
||||
|
||||
/**
|
||||
* function that clears all buffers of the object
|
||||
*/
|
||||
void clearAllBuffers();
|
||||
|
||||
/**
|
||||
* resize retina parvo filter object (resize all allocated buffers)
|
||||
* @param NBrows: the new height size
|
||||
* @param NBcolumns: the new width size
|
||||
*/
|
||||
void resize(const unsigned int NBrows, const unsigned int NBcolumns);
|
||||
|
||||
/**
|
||||
* Input buffer checker: allows to check if the passed image buffer corresponds to retina filter expectations
|
||||
* @param input: the input image buffer
|
||||
* @param colorMode: specifiy if the input should be considered by the retina as colored of not
|
||||
* @return false if not compatible or it returns true if OK
|
||||
*/
|
||||
bool checkInput(const std::valarray<float> &input, const bool colorMode);
|
||||
|
||||
/**
|
||||
* run the initilized retina filter, after this call all retina outputs are updated
|
||||
* @param imageInput: image input buffer, can be grayscale or RGB image respecting the size specified at the constructor level
|
||||
* @param useAdaptiveFiltering: set true if you want to use adaptive color demultilexing (solve some color artefact problems), see RetinaColor for citation references
|
||||
* @param processRetinaParvoMagnoMapping: tels if the main outputs takes into account the mapping of the Parvo and Magno channels on the retina (centred parvo (fovea) and magno outside (parafovea))
|
||||
* @param useColorMode: color information is used if true, warning, if input is only gray level, a buffer overflow error will occur
|
||||
-> note that if color mode is activated and processRetinaParvoMagnoMapping==true, then the demultiplexed color frame (accessible throw getColorOutput() will be a color contours frame in the fovea and gray level moving contours outside
|
||||
@param inputIsColorMultiplexed: set trus if the input data is a multiplexed color image (using Bayer sampling for example), the color sampling method must correspond to the RETINA_COLORSAMPLINGMETHOD passed at constructor!
|
||||
* @return true if process ran well, false in case of failure
|
||||
*/
|
||||
bool runFilter(const std::valarray<float> &imageInput, const bool useAdaptiveFiltering=true, const bool processRetinaParvoMagnoMapping=false, const bool useColorMode=false, const bool inputIsColorMultiplexed=false);
|
||||
|
||||
/**
|
||||
* run the initilized retina filter in order to perform color tone mapping applied on an RGB image, after this call the color output of the retina is updated (use function getColorOutput() to grab it)
|
||||
* the algorithm is based on David Alleyson, Sabine Susstruck and Laurence Meylan's work, please cite:
|
||||
* -> Meylan L., Alleysson D., and S�sstrunk S., A Model of Retinal Local Adaptation for the Tone Mapping of Color Filter Array Images, Journal of Optical Society of America, A, Vol. 24, N� 9, September, 1st, 2007, pp. 2807-2816
|
||||
* get the resulting gray frame by calling function getParvoColor()
|
||||
* @param grayImageInput: RGB image input buffer respecting the size specified at the constructor level
|
||||
* @param PhotoreceptorsCompression: sets the log compression parameters applied at the photoreceptors level (enhance luminance in dark areas)
|
||||
* @param ganglionCellsCompression: sets the log compression applied at the gnaglion cells output (enhance contrast)
|
||||
*/
|
||||
void runGrayToneMapping(const std::valarray<float> &grayImageInput, std::valarray<float> &grayImageOutput, const float PhotoreceptorsCompression=0.6, const float ganglionCellsCompression=0.6);
|
||||
|
||||
/**
|
||||
* run the initilized retina filter in order to perform color tone mapping applied on an RGB image, after this call the color output of the retina is updated (use function getColorOutput() to grab it)
|
||||
* the algorithm is based on David Alleyson, Sabine Susstruck and Laurence Meylan's work, please cite:
|
||||
* -> Meylan L., Alleysson D., and S�sstrunk S., A Model of Retinal Local Adaptation for the Tone Mapping of Color Filter Array Images, Journal of Optical Society of America, A, Vol. 24, N� 9, September, 1st, 2007, pp. 2807-2816
|
||||
* get the resulting RGB frame by calling function getParvoColor()
|
||||
* @param RGBimageInput: RGB image input buffer respecting the size specified at the constructor level
|
||||
* @param useAdaptiveFiltering: set true if you want to use adaptive color demultilexing (solve some color artefact problems), see RetinaColor for citation references
|
||||
* @param PhotoreceptorsCompression: sets the log compression parameters applied at the photoreceptors level (enhance luminance in dark areas)
|
||||
* @param ganglionCellsCompression: sets the log compression applied at the ganglion cells output (enhance contrast)
|
||||
*/
|
||||
void runRGBToneMapping(const std::valarray<float> &RGBimageInput, std::valarray<float> &imageOutput, const bool useAdaptiveFiltering, const float PhotoreceptorsCompression=0.6, const float ganglionCellsCompression=0.6);
|
||||
|
||||
/**
|
||||
* run the initilized retina filter in order to perform color tone mapping applied on an RGB image, after this call the color output of the retina is updated (use function getColorOutput() to grab it)
|
||||
* get the resulting RGB frame by calling function getParvoColor()
|
||||
* @param LMSimageInput: RGB image input buffer respecting the size specified at the constructor level
|
||||
* @param useAdaptiveFiltering: set true if you want to use adaptive color demultilexing (solve some color artefact problems), see RetinaColor for citation references
|
||||
* @param PhotoreceptorsCompression: sets the log compression parameters applied at the photoreceptors level (enhance luminance in dark areas)
|
||||
* @param ganglionCellsCompression: sets the log compression applied at the gnaglion cells output (enhance contrast)
|
||||
*/
|
||||
void runLMSToneMapping(const std::valarray<float> &LMSimageInput, std::valarray<float> &imageOutput, const bool useAdaptiveFiltering, const float PhotoreceptorsCompression=0.6, const float ganglionCellsCompression=0.6);
|
||||
|
||||
/**
|
||||
* set up function of the retina filter: all the retina is initialized at this step, some specific parameters are set by default, use setOPLandParvoCoefficientsTable() and setMagnoCoefficientsTable in order to setup the retina with more options
|
||||
* @param OPLspatialResponse1: (equal to k1 in setOPLandParvoCoefficientsTable() function) the spatial constant of the first order low pass filter of the photoreceptors, use it to cut high spatial frequencies (noise or thick contours), unit is pixels, typical value is 1 pixel
|
||||
* @param OPLtemporalresponse1: (equal to tau1 in setOPLandParvoCoefficientsTable() function) the time constant of the first order low pass filter of the photoreceptors, use it to cut high temporal frequencies (noise or fast motion), unit is frames, typical value is 1 frame
|
||||
* @param OPLassymetryGain: (equal to beta2 in setOPLandParvoCoefficientsTable() function) gain of the horizontal cells network, if 0, then the mean value of the output is zero, if the parameter is near 1, then, the luminance is not filtered and is still reachable at the output, typicall value is 0
|
||||
* @param OPLspatialResponse2: (equal to k2 in setOPLandParvoCoefficientsTable() function) the spatial constant of the first order low pass filter of the horizontal cells, use it to cut low spatial frequencies (local luminance), unit is pixels, typical value is 5 pixel
|
||||
* @param OPLtemporalresponse2: (equal to tau2 in setOPLandParvoCoefficientsTable() function) the time constant of the first order low pass filter of the horizontal cells, use it to cut low temporal frequencies (local luminance variations), unit is frames, typical value is 1 frame, as the photoreceptors
|
||||
* @param LPfilterSpatialResponse: (equal to parasolCells_k in setMagnoCoefficientsTable() function) the low pass filter spatial constant used for local contrast adaptation at the IPL level of the retina (for ganglion cells local adaptation), unit is pixels, typical value is 5
|
||||
* @param LPfilterGain: (equal to parasolCells_beta in setMagnoCoefficientsTable() function) the low pass filter gain used for local contrast adaptation at the IPL level of the retina (for ganglion cells local adaptation), typical value is 0
|
||||
* @param LPfilterTemporalresponse: (equal to parasolCells_tau in setMagnoCoefficientsTable() function) the low pass filter time constant used for local contrast adaptation at the IPL level of the retina (for ganglion cells local adaptation), unit is frame, typical value is 0 (immediate response)
|
||||
* @param MovingContoursExtractorCoefficient: (equal to amacrinCellsTemporalCutFrequency in setMagnoCoefficientsTable() function)the time constant of the first order high pass fiter of the magnocellular way (motion information channel), unit is frames, tipicall value is 5
|
||||
* @param normalizeParvoOutput_0_maxOutputValue: specifies if the Parvo cellular output should be normalized between 0 and maxOutputValue (true) or not (false) in order to remain at a null mean value, true value is recommended for visualisation
|
||||
* @param normalizeMagnoOutput_0_maxOutputValue: specifies if the Magno cellular output should be normalized between 0 and maxOutputValue (true) or not (false), setting true may be hazardous because it can enhace the noise response when nothing is moving
|
||||
* @param maxOutputValue: the maximum amplitude value of the normalized outputs (generally 255 for 8bit per channel pictures)
|
||||
* @param maxInputValue: the maximum pixel value of the input picture (generally 255 for 8bit per channel pictures), specify it in other case (for example High Dynamic Range Images)
|
||||
* @param meanValue: the global mean value of the input data usefull for local adaptation setup
|
||||
*/
|
||||
void setGlobalParameters(const float OPLspatialResponse1=0.7, const float OPLtemporalresponse1=1, const float OPLassymetryGain=0, const float OPLspatialResponse2=5, const float OPLtemporalresponse2=1, const float LPfilterSpatialResponse=5, const float LPfilterGain=0, const float LPfilterTemporalresponse=0, const float MovingContoursExtractorCoefficient=5, const bool normalizeParvoOutput_0_maxOutputValue=false, const bool normalizeMagnoOutput_0_maxOutputValue=false, const float maxOutputValue=255.0, const float maxInputValue=255.0, const float meanValue=128.0);
|
||||
|
||||
/**
|
||||
* setup the local luminance adaptation capability
|
||||
* @param V0CompressionParameter: the compression strengh of the photoreceptors local adaptation output, set a value between 160 and 250 for best results, a high value increases more the low value sensitivity... and the output saturates faster, recommended value: 160
|
||||
*/
|
||||
inline void setPhotoreceptorsLocalAdaptationSensitivity(const float V0CompressionParameter) { _photoreceptorsPrefilter.setV0CompressionParameter(1-V0CompressionParameter);_setInitPeriodCount(); }
|
||||
|
||||
/**
|
||||
* setup the local luminance adaptation capability
|
||||
* @param V0CompressionParameter: the compression strengh of the parvocellular pathway (details) local adaptation output, set a value between 160 and 250 for best results, a high value increases more the low value sensitivity... and the output saturates faster, recommended value: 160
|
||||
*/
|
||||
inline void setParvoGanglionCellsLocalAdaptationSensitivity(const float V0CompressionParameter) { _ParvoRetinaFilter.setV0CompressionParameter(V0CompressionParameter);_setInitPeriodCount(); }
|
||||
|
||||
/**
|
||||
* setup the local luminance adaptation area of integration
|
||||
* @param spatialResponse: the spatial constant of the low pass filter applied on the bipolar cells output in order to compute local contrast mean values
|
||||
* @param temporalResponse: the spatial constant of the low pass filter applied on the bipolar cells output in order to compute local contrast mean values (generally set to zero: immediate response)
|
||||
*/
|
||||
inline void setGanglionCellsLocalAdaptationLPfilterParameters(const float spatialResponse, const float temporalResponse) { _ParvoRetinaFilter.setGanglionCellsLocalAdaptationLPfilterParameters(temporalResponse, spatialResponse);_setInitPeriodCount(); }
|
||||
|
||||
/**
|
||||
* setup the local luminance adaptation capability
|
||||
* @param V0CompressionParameter: the compression strengh of the magnocellular pathway (motion) local adaptation output, set a value between 160 and 250 for best results, a high value increases more the low value sensitivity... and the output saturates faster, recommended value: 160
|
||||
*/
|
||||
inline void setMagnoGanglionCellsLocalAdaptationSensitivity(const float V0CompressionParameter) { _MagnoRetinaFilter.setV0CompressionParameter(V0CompressionParameter);_setInitPeriodCount(); }
|
||||
|
||||
/**
|
||||
* setup the OPL and IPL parvo channels
|
||||
* @param beta1: gain of the horizontal cells network, if 0, then the mean value of the output is zero (default value), if the parameter is near 1, the amplitude is boosted but it should only be used for values rescaling... if needed
|
||||
* @param tau1: the time constant of the first order low pass filter of the photoreceptors, use it to cut high temporal frequencies (noise or fast motion), unit is frames, typical value is 1 frame
|
||||
* @param k1: the spatial constant of the first order low pass filter of the photoreceptors, use it to cut high spatial frequencies (noise or thick contours), unit is pixels, typical value is 1 pixel
|
||||
* @param beta2: gain of the horizontal cells network, if 0, then the mean value of the output is zero, if the parameter is near 1, then, the luminance is not filtered and is still reachable at the output, typicall value is 0
|
||||
* @param tau2: the time constant of the first order low pass filter of the horizontal cells, use it to cut low temporal frequencies (local luminance variations), unit is frames, typical value is 1 frame, as the photoreceptors
|
||||
* @param k2: the spatial constant of the first order low pass filter of the horizontal cells, use it to cut low spatial frequencies (local luminance), unit is pixels, typical value is 5 pixel, this value is also used for local contrast computing when computing the local contrast adaptation at the ganglion cells level (Inner Plexiform Layer parvocellular channel model)
|
||||
* @param V0CompressionParameter: the compression strengh of the ganglion cells local adaptation output, set a value between 160 and 250 for best results, a high value increases more the low value sensitivity... and the output saturates faster, recommended value: 230
|
||||
*/
|
||||
void setOPLandParvoParameters(const float beta1, const float tau1, const float k1, const float beta2, const float tau2, const float k2, const float V0CompressionParameter) { _ParvoRetinaFilter.setOPLandParvoFiltersParameters(beta1, tau1, k1, beta2, tau2, k2);_ParvoRetinaFilter.setV0CompressionParameter(V0CompressionParameter);_setInitPeriodCount(); }
|
||||
|
||||
/**
|
||||
* set parameters values for the Inner Plexiform Layer (IPL) magnocellular channel
|
||||
* @param parasolCells_beta: the low pass filter gain used for local contrast adaptation at the IPL level of the retina (for ganglion cells local adaptation), typical value is 0
|
||||
* @param parasolCells_tau: the low pass filter time constant used for local contrast adaptation at the IPL level of the retina (for ganglion cells local adaptation), unit is frame, typical value is 0 (immediate response)
|
||||
* @param parasolCells_k: the low pass filter spatial constant used for local contrast adaptation at the IPL level of the retina (for ganglion cells local adaptation), unit is pixels, typical value is 5
|
||||
* @param amacrinCellsTemporalCutFrequency: the time constant of the first order high pass fiter of the magnocellular way (motion information channel), unit is frames, tipicall value is 5
|
||||
* @param V0CompressionParameter: the compression strengh of the ganglion cells local adaptation output, set a value between 160 and 250 for best results, a high value increases more the low value sensitivity... and the output saturates faster, recommended value: 200
|
||||
* @param localAdaptintegration_tau: specifies the temporal constant of the low pas filter involved in the computation of the local "motion mean" for the local adaptation computation
|
||||
* @param localAdaptintegration_k: specifies the spatial constant of the low pas filter involved in the computation of the local "motion mean" for the local adaptation computation
|
||||
*/
|
||||
void setMagnoCoefficientsTable(const float parasolCells_beta, const float parasolCells_tau, const float parasolCells_k, const float amacrinCellsTemporalCutFrequency, const float V0CompressionParameter, const float localAdaptintegration_tau, const float localAdaptintegration_k) { _MagnoRetinaFilter.setCoefficientsTable(parasolCells_beta, parasolCells_tau, parasolCells_k, amacrinCellsTemporalCutFrequency, localAdaptintegration_tau, localAdaptintegration_k);_MagnoRetinaFilter.setV0CompressionParameter(V0CompressionParameter);_setInitPeriodCount(); }
|
||||
|
||||
/**
|
||||
* set if the parvo output should be or not normalized between 0 and 255 (for display purpose generally)
|
||||
* @param normalizeParvoOutput_0_maxOutputValue: true if normalization should be done
|
||||
*/
|
||||
inline void activateNormalizeParvoOutput_0_maxOutputValue(const bool normalizeParvoOutput_0_maxOutputValue) { _normalizeParvoOutput_0_maxOutputValue=normalizeParvoOutput_0_maxOutputValue; }
|
||||
|
||||
/**
|
||||
* set if the magno output should be or not normalized between 0 and 255 (for display purpose generally), take care, if nothing is moving, then, the noise will be enanced !!!
|
||||
* @param normalizeMagnoOutput_0_maxOutputValue: true if normalization should be done
|
||||
*/
|
||||
inline void activateNormalizeMagnoOutput_0_maxOutputValue(const bool normalizeMagnoOutput_0_maxOutputValue) { _normalizeMagnoOutput_0_maxOutputValue=normalizeMagnoOutput_0_maxOutputValue; }
|
||||
|
||||
/**
|
||||
* setup the maximum amplitude value of the normalized outputs (generally 255 for 8bit per channel pictures)
|
||||
* @param maxOutputValue: maximum amplitude value of the normalized outputs (generally 255 for 8bit per channel pictures)
|
||||
*/
|
||||
inline void setMaxOutputValue(const float maxOutputValue) { _maxOutputValue=maxOutputValue; }
|
||||
|
||||
/**
|
||||
* sets the color mode of the frame grabber
|
||||
* @param desiredColorMode: true if the user needs color information, false for graylevels
|
||||
*/
|
||||
void setColorMode(const bool desiredColorMode) { _useColorMode=desiredColorMode; }
|
||||
|
||||
/**
|
||||
* activate color saturation as the final step of the color demultiplexing process
|
||||
* -> this saturation is a sigmoide function applied to each channel of the demultiplexed image.
|
||||
* @param saturateColors: boolean that activates color saturation (if true) or desactivate (if false)
|
||||
* @param colorSaturationValue: the saturation factor
|
||||
* */
|
||||
inline void setColorSaturation(const bool saturateColors=true, const float colorSaturationValue=4.0) { _colorEngine.setColorSaturation(saturateColors, colorSaturationValue); }
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// function that retrieve the main retina outputs, one by one, or all in a structure
|
||||
|
||||
/**
|
||||
* @return the input image sampled by the photoreceptors spatial sampling
|
||||
*/
|
||||
inline const std::valarray<float> &getPhotoreceptorsSampledFrame() const
|
||||
{
|
||||
CV_Assert(_photoreceptorsLogSampling);
|
||||
return _photoreceptorsLogSampling->getSampledFrame();
|
||||
};
|
||||
|
||||
/**
|
||||
* @return photoreceptors output, locally adapted luminance only, no high frequency spatio-temporal noise reduction at the next retina processing stages, use getPhotoreceptors method to get complete photoreceptors output
|
||||
*/
|
||||
inline const std::valarray<float> &getLocalAdaptation() const {return _photoreceptorsPrefilter.getOutput(); }
|
||||
|
||||
/**
|
||||
* @return photoreceptors output: locally adapted luminance and high frequency spatio-temporal noise reduction, high luminance is a little saturated at this stage, but this is corrected naturally at the next retina processing stages
|
||||
*/
|
||||
inline const std::valarray<float> &getPhotoreceptors() const {return _ParvoRetinaFilter.getPhotoreceptorsLPfilteringOutput(); }
|
||||
|
||||
/**
|
||||
* @return the local luminance of the processed frame (it is the horizontal cells output)
|
||||
*/
|
||||
inline const std::valarray<float> &getHorizontalCells() const {return _ParvoRetinaFilter.getHorizontalCellsOutput(); }
|
||||
|
||||
///////// CONTOURS part, PARVOCELLULAR RETINA PATHWAY
|
||||
/**
|
||||
* @return true if Parvocellular output is activated, false if not
|
||||
*/
|
||||
inline bool areContoursProcessed() { return _useParvoOutput; }
|
||||
|
||||
/**
|
||||
* method to retrieve the foveal parvocellular pathway response (no details energy in parafovea)
|
||||
* @param parvoParafovealResponse: buffer that will be filled with the response of the magnocellular pathway in the parafoveal area
|
||||
* @return true if process succeeded (if buffer exists, is its size matches retina size, if magno channel is activated and if mapping is initialized
|
||||
*/
|
||||
bool getParvoFoveaResponse(std::valarray<float> &parvoFovealResponse);
|
||||
|
||||
/**
|
||||
* @param useParvoOutput: true if Parvocellular output should be activated, false if not
|
||||
*/
|
||||
inline void activateContoursProcessing(const bool useParvoOutput) { _useParvoOutput=useParvoOutput; }
|
||||
|
||||
/**
|
||||
* @return the parvocellular contours information (details), should be used at the fovea level
|
||||
*/
|
||||
const std::valarray<float> &getContours(); // Parvocellular output
|
||||
|
||||
/**
|
||||
* @return the parvocellular contours ON information (details), should be used at the fovea level
|
||||
*/
|
||||
inline const std::valarray<float> &getContoursON() const {return _ParvoRetinaFilter.getParvoON(); } // Parvocellular ON output
|
||||
|
||||
/**
|
||||
* @return the parvocellular contours OFF information (details), should be used at the fovea level
|
||||
*/
|
||||
inline const std::valarray<float> &getContoursOFF() const {return _ParvoRetinaFilter.getParvoOFF(); } // Parvocellular OFF output
|
||||
|
||||
///////// MOVING CONTOURS part, MAGNOCELLULAR RETINA PATHWAY
|
||||
/**
|
||||
* @return true if Magnocellular output is activated, false if not
|
||||
*/
|
||||
inline bool areMovingContoursProcessed() { return _useMagnoOutput; }
|
||||
|
||||
/**
|
||||
* method to retrieve the parafoveal magnocellular pathway response (no motion energy in fovea)
|
||||
* @param magnoParafovealResponse: buffer that will be filled with the response of the magnocellular pathway in the parafoveal area
|
||||
* @return true if process succeeded (if buffer exists, is its size matches retina size, if magno channel is activated and if mapping is initialized
|
||||
*/
|
||||
bool getMagnoParaFoveaResponse(std::valarray<float> &magnoParafovealResponse);
|
||||
|
||||
/**
|
||||
* @param useMagnoOutput: true if Magnoocellular output should be activated, false if not
|
||||
*/
|
||||
inline void activateMovingContoursProcessing(const bool useMagnoOutput) { _useMagnoOutput=useMagnoOutput; }
|
||||
|
||||
/**
|
||||
* @return the magnocellular moving contours information (motion), should be used at the parafovea level without post-processing
|
||||
*/
|
||||
inline const std::valarray<float> &getMovingContours() const {return _MagnoRetinaFilter.getOutput(); } // Magnocellular output
|
||||
|
||||
/**
|
||||
* @return the magnocellular moving contours information (motion), should be used at the parafovea level with assymetric sigmoide post-processing which saturates motion information
|
||||
*/
|
||||
inline const std::valarray<float> &getMovingContoursSaturated() const {return _MagnoRetinaFilter.getMagnoYsaturated(); } // Saturated Magnocellular output
|
||||
|
||||
/**
|
||||
* @return the magnocellular moving contours ON information (motion), should be used at the parafovea level without post-processing
|
||||
*/
|
||||
inline const std::valarray<float> &getMovingContoursON() const {return _MagnoRetinaFilter.getMagnoON(); } // Magnocellular ON output
|
||||
|
||||
/**
|
||||
* @return the magnocellular moving contours OFF information (motion), should be used at the parafovea level without post-processing
|
||||
*/
|
||||
inline const std::valarray<float> &getMovingContoursOFF() const {return _MagnoRetinaFilter.getMagnoOFF(); } // Magnocellular OFF output
|
||||
|
||||
/**
|
||||
* @return a gray level image with center Parvo and peripheral Magno X channels, WARNING, the result will be ok if you called previously fucntion runFilter(imageInput, processRetinaParvoMagnoMapping=true);
|
||||
* -> will be accessible even if color mode is activated (but the image is color sampled so quality is poor), but get the same thing but in color by the use of function getParvoColor()
|
||||
*/
|
||||
inline const std::valarray<float> &getRetinaParvoMagnoMappedOutput() const {return _retinaParvoMagnoMappedFrame; } // return image with center Parvo and peripheral Magno channels
|
||||
|
||||
/**
|
||||
* color processing dedicated functions
|
||||
* @return the parvo channel (contours, details) of the processed frame, grayscale output
|
||||
*/
|
||||
inline const std::valarray<float> &getParvoContoursChannel() const {return _colorEngine.getLuminance(); }
|
||||
|
||||
/**
|
||||
* color processing dedicated functions
|
||||
* @return the chrominance of the processed frame (same colorspace as the input output, usually RGB)
|
||||
*/
|
||||
inline const std::valarray<float> &getParvoChrominance() const {return _colorEngine.getChrominance(); } // only retreive chrominance
|
||||
|
||||
/**
|
||||
* color processing dedicated functions
|
||||
* @return the parvo + chrominance channels of the processed frame (same colorspace as the input output, usually RGB)
|
||||
*/
|
||||
inline const std::valarray<float> &getColorOutput() const {return _colorEngine.getDemultiplexedColorFrame(); } // retrieve luminance+chrominance
|
||||
|
||||
/**
|
||||
* apply to the retina color output the Krauskopf transformation which leads to an opponent color system: output colorspace if Acr1cr2 if input of the retina was LMS color space
|
||||
* @param result: the input buffer to fill with the transformed colorspace retina output
|
||||
* @return true if process ended successfully
|
||||
*/
|
||||
inline bool applyKrauskopfLMS2Acr1cr2Transform(std::valarray<float> &result) { return _colorEngine.applyKrauskopfLMS2Acr1cr2Transform(result); }
|
||||
|
||||
/**
|
||||
* apply to the retina color output the Krauskopf transformation which leads to an opponent color system: output colorspace if Acr1cr2 if input of the retina was LMS color space
|
||||
* @param result: the input buffer to fill with the transformed colorspace retina output
|
||||
* @return true if process ended successfully
|
||||
*/
|
||||
inline bool applyLMS2LabTransform(std::valarray<float> &result) { return _colorEngine.applyLMS2LabTransform(result); }
|
||||
|
||||
/**
|
||||
* color processing dedicated functions
|
||||
* @return the retina initialized mode, true if color mode (RGB), false if grayscale
|
||||
*/
|
||||
inline bool isColorMode() { return _useColorMode; } // return true if RGB mode, false if gray level mode
|
||||
|
||||
/**
|
||||
* @return the irregular low pass filter ouput at the photoreceptors level
|
||||
*/
|
||||
inline const std::valarray<float> &getIrregularLPfilteredInputFrame() const {return _photoreceptorsLogSampling->getIrregularLPfilteredInputFrame(); }
|
||||
|
||||
/**
|
||||
* @return true if color mode is activated, false if gray levels processing
|
||||
*/
|
||||
bool getColorMode() { return _useColorMode; }
|
||||
|
||||
/**
|
||||
*
|
||||
* @return true if a sufficient number of processed frames has been done since the last parameters update in order to get the stable state (r�gime permanent)
|
||||
*/
|
||||
inline bool isInitTransitionDone() { if (_ellapsedFramesSinceLastReset<_globalTemporalConstant)return false; return true; }
|
||||
|
||||
/**
|
||||
* find a distance in the image input space when the distance is known in the retina log sampled space...read again if it is not clear enough....sorry, i should sleep
|
||||
* @param projectedRadiusLength: the distance to image center in the retina log sampled space
|
||||
* @return the distance to image center in the input image space
|
||||
*/
|
||||
inline float getRetinaSamplingBackProjection(const float projectedRadiusLength)
|
||||
{
|
||||
if (_photoreceptorsLogSampling)
|
||||
return (float)_photoreceptorsLogSampling->getOriginalRadiusLength(projectedRadiusLength);
|
||||
return projectedRadiusLength;
|
||||
}
|
||||
|
||||
/////////////////:
|
||||
// retina dimensions getters
|
||||
|
||||
/**
|
||||
* @return number of rows of the filter
|
||||
*/
|
||||
inline unsigned int getInputNBrows() { if (_photoreceptorsLogSampling) return _photoreceptorsLogSampling->getNBrows();else return _photoreceptorsPrefilter.getNBrows(); }
|
||||
|
||||
/**
|
||||
* @return number of columns of the filter
|
||||
*/
|
||||
inline unsigned int getInputNBcolumns() { if (_photoreceptorsLogSampling) return _photoreceptorsLogSampling->getNBcolumns();else return _photoreceptorsPrefilter.getNBcolumns(); }
|
||||
|
||||
/**
|
||||
* @return number of pixels of the filter
|
||||
*/
|
||||
inline unsigned int getInputNBpixels() { if (_photoreceptorsLogSampling) return _photoreceptorsLogSampling->getNBpixels();else return _photoreceptorsPrefilter.getNBpixels(); }
|
||||
|
||||
/**
|
||||
* @return the height of the frame output
|
||||
*/
|
||||
inline unsigned int getOutputNBrows() { return _photoreceptorsPrefilter.getNBrows(); }
|
||||
|
||||
/**
|
||||
* @return the width of the frame output
|
||||
*/
|
||||
inline unsigned int getOutputNBcolumns() { return _photoreceptorsPrefilter.getNBcolumns(); }
|
||||
|
||||
/**
|
||||
* @return the numbers of output pixels (width*height) of the images used by the object
|
||||
*/
|
||||
inline unsigned int getOutputNBpixels() { return _photoreceptorsPrefilter.getNBpixels(); }
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// processing activation flags
|
||||
bool _useParvoOutput;
|
||||
bool _useMagnoOutput;
|
||||
|
||||
|
||||
// filter stability controls
|
||||
unsigned int _ellapsedFramesSinceLastReset;
|
||||
unsigned int _globalTemporalConstant;
|
||||
|
||||
// private template buffers and related access pointers
|
||||
std::valarray<float> _retinaParvoMagnoMappedFrame;
|
||||
std::valarray<float> _retinaParvoMagnoMapCoefTable;
|
||||
// private objects of the class
|
||||
BasicRetinaFilter _photoreceptorsPrefilter;
|
||||
ParvoRetinaFilter _ParvoRetinaFilter;
|
||||
MagnoRetinaFilter _MagnoRetinaFilter;
|
||||
RetinaColor _colorEngine;
|
||||
ImageLogPolProjection *_photoreceptorsLogSampling;
|
||||
|
||||
bool _useMinimalMemoryForToneMappingONLY;
|
||||
|
||||
bool _normalizeParvoOutput_0_maxOutputValue;
|
||||
bool _normalizeMagnoOutput_0_maxOutputValue;
|
||||
float _maxOutputValue;
|
||||
bool _useColorMode;
|
||||
|
||||
|
||||
|
||||
// private functions
|
||||
void _setInitPeriodCount();
|
||||
void _createHybridTable();
|
||||
void _processRetinaParvoMagnoMapping();
|
||||
void _runGrayToneMapping(const std::valarray<float> &grayImageInput, std::valarray<float> &grayImageOutput ,const float PhotoreceptorsCompression=0.6, const float ganglionCellsCompression=0.6);
|
||||
|
||||
|
||||
};
|
||||
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
|
||||
#endif /*RETINACLASSES_H_*/
|
||||
@@ -0,0 +1,555 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models. Presented models originate from Jeanny Herault's original research and have been reused and adapted by the author&collaborators for computed vision applications since his thesis with Alice Caplier at Gipsa-Lab.
|
||||
** Use: extract still images & image sequences features, from contours details to motion spatio-temporal features, etc. for high level visual scene analysis. Also contribute to image enhancement/compression such as tone mapping.
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications) and Gipsa Lab (original research origins & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2011
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
** The retina filter includes the research contributions of phd/research collegues from which code has been redrawn by the author :
|
||||
** _take a look at the retinacolor.hpp module to discover Brice Chaix de Lavarene color mosaicing/demosaicing and the reference paper:
|
||||
** ====> B. Chaix de Lavarene, D. Alleysson, B. Durette, J. Herault (2007). "Efficient demosaicing through recursive filtering", IEEE International Conference on Image Processing ICIP 2007
|
||||
** _take a look at imagelogpolprojection.hpp to discover retina spatial log sampling which originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is also proposed and originates from Jeanny's discussions.
|
||||
** ====> more informations in the above cited Jeanny Heraults's book.
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2011, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __TEMPLATEBUFFER_HPP__
|
||||
#define __TEMPLATEBUFFER_HPP__
|
||||
|
||||
#include <valarray>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
//#define __TEMPLATEBUFFERDEBUG //define TEMPLATEBUFFERDEBUG in order to display debug information
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
//// If a parallelization method is available then, you should define MAKE_PARALLEL, in the other case, the classical serial code will be used
|
||||
#define MAKE_PARALLEL
|
||||
// ==> then include required includes
|
||||
#ifdef MAKE_PARALLEL
|
||||
|
||||
// ==> declare usefull generic tools
|
||||
template <class type>
|
||||
class Parallel_clipBufferValues: public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
type *bufferToClip;
|
||||
type minValue, maxValue;
|
||||
|
||||
public:
|
||||
Parallel_clipBufferValues(type* bufferToProcess, const type min, const type max)
|
||||
: bufferToClip(bufferToProcess), minValue(min), maxValue(max) { }
|
||||
|
||||
virtual void operator()( const cv::Range &r ) const CV_OVERRIDE {
|
||||
type *inputOutputBufferPTR=bufferToClip+r.start;
|
||||
for (int jf = r.start; jf != r.end; ++jf, ++inputOutputBufferPTR)
|
||||
{
|
||||
if (*inputOutputBufferPTR>maxValue)
|
||||
*inputOutputBufferPTR=maxValue;
|
||||
else if (*inputOutputBufferPTR<minValue)
|
||||
*inputOutputBufferPTR=minValue;
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @class TemplateBuffer
|
||||
* @brief this class is a simple template memory buffer which contains basic functions to get information on or normalize the buffer content
|
||||
* note that thanks to the parent STL template class "valarray", it is possible to perform easily operations on the full array such as addition, product etc.
|
||||
* @author Alexandre BENOIT (benoit.alexandre.vision@gmail.com), helped by Gelu IONESCU (gelu.ionescu@lis.inpg.fr)
|
||||
* creation date: september 2007
|
||||
*/
|
||||
template <class type> class TemplateBuffer : public std::valarray<type>
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* constructor for monodimensional array
|
||||
* @param dim: the size of the vector
|
||||
*/
|
||||
TemplateBuffer(const size_t dim=0)
|
||||
: std::valarray<type>((type)0, dim)
|
||||
{
|
||||
_NBrows=1;
|
||||
_NBcolumns=dim;
|
||||
_NBdepths=1;
|
||||
_NBpixels=dim;
|
||||
_doubleNBpixels=2*dim;
|
||||
}
|
||||
|
||||
/**
|
||||
* constructor by copy for monodimensional array
|
||||
* @param pVal: the pointer to a buffer to copy
|
||||
* @param dim: the size of the vector
|
||||
*/
|
||||
TemplateBuffer(const type* pVal, const size_t dim)
|
||||
: std::valarray<type>(pVal, dim)
|
||||
{
|
||||
_NBrows=1;
|
||||
_NBcolumns=dim;
|
||||
_NBdepths=1;
|
||||
_NBpixels=dim;
|
||||
_doubleNBpixels=2*dim;
|
||||
}
|
||||
|
||||
/**
|
||||
* constructor for bidimensional array
|
||||
* @param dimRows: the size of the vector
|
||||
* @param dimColumns: the size of the vector
|
||||
* @param depth: the number of layers of the buffer in its third dimension (3 of color images, 1 for gray images.
|
||||
*/
|
||||
TemplateBuffer(const size_t dimRows, const size_t dimColumns, const size_t depth=1)
|
||||
: std::valarray<type>((type)0, dimRows*dimColumns*depth)
|
||||
{
|
||||
#ifdef TEMPLATEBUFFERDEBUG
|
||||
std::cout<<"TemplateBuffer::TemplateBuffer: new buffer, size="<<dimRows<<", "<<dimColumns<<", "<<depth<<"valarraySize="<<this->size()<<std::endl;
|
||||
#endif
|
||||
_NBrows=dimRows;
|
||||
_NBcolumns=dimColumns;
|
||||
_NBdepths=depth;
|
||||
_NBpixels=dimRows*dimColumns;
|
||||
_doubleNBpixels=2*dimRows*dimColumns;
|
||||
//_createTableIndex();
|
||||
#ifdef TEMPLATEBUFFERDEBUG
|
||||
std::cout<<"TemplateBuffer::TemplateBuffer: construction successful"<<std::endl;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* copy constructor
|
||||
* @param toCopy
|
||||
* @return thenconstructed instance
|
||||
*emplateBuffer(const TemplateBuffer &toCopy)
|
||||
:_NBrows(toCopy.getNBrows()),_NBcolumns(toCopy.getNBcolumns()),_NBdepths(toCopy.getNBdephs()), _NBpixels(toCopy.getNBpixels()), _doubleNBpixels(toCopy.getNBpixels()*2)
|
||||
//std::valarray<type>(toCopy)
|
||||
{
|
||||
memcpy(Buffer(), toCopy.Buffer(), this->size());
|
||||
}*/
|
||||
/**
|
||||
* destructor
|
||||
*/
|
||||
virtual ~TemplateBuffer()
|
||||
{
|
||||
#ifdef TEMPLATEBUFFERDEBUG
|
||||
std::cout<<"~TemplateBuffer"<<std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* delete the buffer content (set zeros)
|
||||
*/
|
||||
inline void setZero() { std::valarray<type>::operator=(0); } //memset(Buffer(), 0, sizeof(type)*_NBpixels); }
|
||||
|
||||
/**
|
||||
* @return the numbers of rows (height) of the images used by the object
|
||||
*/
|
||||
inline unsigned int getNBrows() { return (unsigned int)_NBrows; }
|
||||
|
||||
/**
|
||||
* @return the numbers of columns (width) of the images used by the object
|
||||
*/
|
||||
inline unsigned int getNBcolumns() { return (unsigned int)_NBcolumns; }
|
||||
|
||||
/**
|
||||
* @return the numbers of pixels (width*height) of the images used by the object
|
||||
*/
|
||||
inline unsigned int getNBpixels() { return (unsigned int)_NBpixels; }
|
||||
|
||||
/**
|
||||
* @return the numbers of pixels (width*height) of the images used by the object
|
||||
*/
|
||||
inline unsigned int getDoubleNBpixels() { return (unsigned int)_doubleNBpixels; }
|
||||
|
||||
/**
|
||||
* @return the numbers of depths (3rd dimension: 1 for gray images, 3 for rgb images) of the images used by the object
|
||||
*/
|
||||
inline unsigned int getDepthSize() { return (unsigned int)_NBdepths; }
|
||||
|
||||
/**
|
||||
* resize the buffer and recompute table index etc.
|
||||
*/
|
||||
void resizeBuffer(const size_t dimRows, const size_t dimColumns, const size_t depth=1)
|
||||
{
|
||||
this->resize(dimRows*dimColumns*depth);
|
||||
_NBrows=dimRows;
|
||||
_NBcolumns=dimColumns;
|
||||
_NBdepths=depth;
|
||||
_NBpixels=dimRows*dimColumns;
|
||||
_doubleNBpixels=2*dimRows*dimColumns;
|
||||
}
|
||||
|
||||
inline TemplateBuffer<type> & operator=(const std::valarray<type> &b)
|
||||
{
|
||||
//std::cout<<"TemplateBuffer<type> & operator= affect vector: "<<std::endl;
|
||||
std::valarray<type>::operator=(b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline TemplateBuffer<type> & operator=(const type &b)
|
||||
{
|
||||
//std::cout<<"TemplateBuffer<type> & operator= affect value: "<<b<<std::endl;
|
||||
std::valarray<type>::operator=(b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* inline const type &operator[](const unsigned int &b)
|
||||
{
|
||||
return (*this)[b];
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* @return the buffer adress in non const mode
|
||||
*/
|
||||
inline type* Buffer() { return &(*this)[0]; }
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Standard Image manipulation functions
|
||||
|
||||
/**
|
||||
* standard 0 to 255 image normalization function
|
||||
* @param inputOutputBuffer: the image to be normalized (rewrites the input), if no parameter, then, the built in buffer reachable by getOutput() function is normalized
|
||||
* @param nbPixels: specifies the number of pixel on which the normalization should be performed, if 0, then all pixels specified in the constructor are processed
|
||||
* @param maxOutputValue: the maximum output value
|
||||
*/
|
||||
static void normalizeGrayOutput_0_maxOutputValue(type *inputOutputBuffer, const size_t nbPixels, const type maxOutputValue=(type)255.0);
|
||||
|
||||
/**
|
||||
* standard 0 to 255 image normalization function
|
||||
* @param inputOutputBuffer: the image to be normalized (rewrites the input), if no parameter, then, the built in buffer reachable by getOutput() function is normalized
|
||||
* @param nbPixels: specifies the number of pixel on which the normalization should be performed, if 0, then all pixels specified in the constructor are processed
|
||||
* @param maxOutputValue: the maximum output value
|
||||
*/
|
||||
void normalizeGrayOutput_0_maxOutputValue(const type maxOutputValue=(type)255.0) { normalizeGrayOutput_0_maxOutputValue(this->Buffer(), this->size(), maxOutputValue); }
|
||||
|
||||
/**
|
||||
* sigmoide image normalization function (saturates min and max values)
|
||||
* @param meanValue: specifies the mean value of th pixels to be processed
|
||||
* @param sensitivity: strength of the sigmoide
|
||||
* @param inputPicture: the image to be normalized if no parameter, then, the built in buffer reachable by getOutput() function is normalized
|
||||
* @param outputBuffer: the ouput buffer on which the result is writed, if no parameter, then, the built in buffer reachable by getOutput() function is normalized
|
||||
* @param maxOutputValue: the maximum output value
|
||||
*/
|
||||
static void normalizeGrayOutputCentredSigmoide(const type meanValue, const type sensitivity, const type maxOutputValue, type *inputPicture, type *outputBuffer, const unsigned int nbPixels);
|
||||
|
||||
/**
|
||||
* sigmoide image normalization function on the current buffer (saturates min and max values)
|
||||
* @param meanValue: specifies the mean value of th pixels to be processed
|
||||
* @param sensitivity: strength of the sigmoide
|
||||
* @param maxOutputValue: the maximum output value
|
||||
*/
|
||||
inline void normalizeGrayOutputCentredSigmoide(const type meanValue=(type)0.0, const type sensitivity=(type)2.0, const type maxOutputValue=(type)255.0) { CV_UNUSED(maxOutputValue); normalizeGrayOutputCentredSigmoide(meanValue, sensitivity, 255.0, this->Buffer(), this->Buffer(), this->getNBpixels()); }
|
||||
|
||||
/**
|
||||
* sigmoide image normalization function (saturates min and max values), in this function, the sigmoide is centered on low values (high saturation of the medium and high values
|
||||
* @param inputPicture: the image to be normalized if no parameter, then, the built in buffer reachable by getOutput() function is normalized
|
||||
* @param outputBuffer: the ouput buffer on which the result is writed, if no parameter, then, the built in buffer reachable by getOutput() function is normalized
|
||||
* @param sensitivity: strength of the sigmoide
|
||||
* @param maxOutputValue: the maximum output value
|
||||
*/
|
||||
void normalizeGrayOutputNearZeroCentreredSigmoide(type *inputPicture=(type*)NULL, type *outputBuffer=(type*)NULL, const type sensitivity=(type)40, const type maxOutputValue=(type)255.0);
|
||||
|
||||
/**
|
||||
* center and reduct the image (image-mean)/std
|
||||
* @param inputOutputBuffer: the image to be normalized if no parameter, the result is rewrited on it
|
||||
*/
|
||||
void centerReductImageLuminance(type *inputOutputBuffer=(type*)NULL);
|
||||
|
||||
/**
|
||||
* @return standard deviation of the buffer
|
||||
*/
|
||||
double getStandardDeviation()
|
||||
{
|
||||
double standardDeviation=0;
|
||||
double meanValue=getMean();
|
||||
|
||||
type *bufferPTR=Buffer();
|
||||
for (unsigned int i=0;i<this->size();++i)
|
||||
{
|
||||
double diff=(*(bufferPTR++)-meanValue);
|
||||
standardDeviation+=diff*diff;
|
||||
}
|
||||
return std::sqrt(standardDeviation/this->size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clip buffer histogram
|
||||
* @param minRatio: the minimum ratio of the lower pixel values, range=[0,1] and lower than maxRatio
|
||||
* @param maxRatio: the aximum ratio of the higher pixel values, range=[0,1] and higher than minRatio
|
||||
*/
|
||||
void clipHistogram(double minRatio, double maxRatio, double maxOutputValue)
|
||||
{
|
||||
|
||||
if (minRatio>=maxRatio)
|
||||
{
|
||||
std::cerr<<"TemplateBuffer::clipHistogram: minRatio must be inferior to maxRatio, buffer unchanged"<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
/* minRatio=min(max(minRatio, 1.0),0.0);
|
||||
maxRatio=max(max(maxRatio, 0.0),1.0);
|
||||
*/
|
||||
|
||||
// find the pixel value just above the threshold
|
||||
const double maxThreshold=this->max()*maxRatio;
|
||||
const double minThreshold=(this->max()-this->min())*minRatio+this->min();
|
||||
|
||||
type *bufferPTR=this->Buffer();
|
||||
|
||||
double deltaH=maxThreshold;
|
||||
double deltaL=maxThreshold;
|
||||
|
||||
double updatedHighValue=maxThreshold;
|
||||
double updatedLowValue=maxThreshold;
|
||||
|
||||
for (unsigned int i=0;i<this->size();++i)
|
||||
{
|
||||
double curentValue=(double)*(bufferPTR++);
|
||||
|
||||
// updating "closest to the high threshold" pixel value
|
||||
double highValueTest=maxThreshold-curentValue;
|
||||
if (highValueTest>0)
|
||||
{
|
||||
if (deltaH>highValueTest)
|
||||
{
|
||||
deltaH=highValueTest;
|
||||
updatedHighValue=curentValue;
|
||||
}
|
||||
}
|
||||
|
||||
// updating "closest to the low threshold" pixel value
|
||||
double lowValueTest=curentValue-minThreshold;
|
||||
if (lowValueTest>0)
|
||||
{
|
||||
if (deltaL>lowValueTest)
|
||||
{
|
||||
deltaL=lowValueTest;
|
||||
updatedLowValue=curentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout<<"Tdebug"<<std::endl;
|
||||
std::cout<<"deltaL="<<deltaL<<", deltaH="<<deltaH<<std::endl;
|
||||
std::cout<<"this->max()"<<this->max()<<"maxThreshold="<<maxThreshold<<"updatedHighValue="<<updatedHighValue<<std::endl;
|
||||
std::cout<<"this->min()"<<this->min()<<"minThreshold="<<minThreshold<<"updatedLowValue="<<updatedLowValue<<std::endl;
|
||||
// clipping values outside than the updated thresholds
|
||||
bufferPTR=this->Buffer();
|
||||
#ifdef MAKE_PARALLEL // call the TemplateBuffer multitreaded clipping method
|
||||
parallel_for_(cv::Range(0,this->size()), Parallel_clipBufferValues<type>(bufferPTR, updatedLowValue, updatedHighValue));
|
||||
#else
|
||||
|
||||
for (unsigned int i=0;i<this->size();++i, ++bufferPTR)
|
||||
{
|
||||
if (*bufferPTR<updatedLowValue)
|
||||
*bufferPTR=updatedLowValue;
|
||||
else if (*bufferPTR>updatedHighValue)
|
||||
*bufferPTR=updatedHighValue;
|
||||
}
|
||||
#endif
|
||||
normalizeGrayOutput_0_maxOutputValue(this->Buffer(), this->size(), maxOutputValue);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the mean value of the vector
|
||||
*/
|
||||
inline double getMean() { return this->sum()/this->size(); }
|
||||
|
||||
protected:
|
||||
size_t _NBrows;
|
||||
size_t _NBcolumns;
|
||||
size_t _NBdepths;
|
||||
size_t _NBpixels;
|
||||
size_t _doubleNBpixels;
|
||||
// utilities
|
||||
static type _abs(const type x);
|
||||
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
/// normalize output between 0 and 255, can be applied on images of different size that the declared size if nbPixels parameters is setted up;
|
||||
template <class type>
|
||||
void TemplateBuffer<type>::normalizeGrayOutput_0_maxOutputValue(type *inputOutputBuffer, const size_t processedPixels, const type maxOutputValue)
|
||||
{
|
||||
type maxValue=inputOutputBuffer[0], minValue=inputOutputBuffer[0];
|
||||
|
||||
// get the min and max value
|
||||
type *inputOutputBufferPTR=inputOutputBuffer;
|
||||
for (size_t j = 0; j<processedPixels; ++j)
|
||||
{
|
||||
type pixValue = *(inputOutputBufferPTR++);
|
||||
if (maxValue < pixValue)
|
||||
maxValue = pixValue;
|
||||
else if (minValue > pixValue)
|
||||
minValue = pixValue;
|
||||
}
|
||||
// change the range of the data to 0->255
|
||||
|
||||
type factor = maxOutputValue/(maxValue-minValue);
|
||||
type offset = (type)(-minValue*factor);
|
||||
|
||||
inputOutputBufferPTR=inputOutputBuffer;
|
||||
for (size_t j = 0; j < processedPixels; ++j, ++inputOutputBufferPTR)
|
||||
*inputOutputBufferPTR=*(inputOutputBufferPTR)*factor+offset;
|
||||
|
||||
}
|
||||
// normalize data with a sigmoide close to 0 (saturates values for those superior to 0)
|
||||
template <class type>
|
||||
void TemplateBuffer<type>::normalizeGrayOutputNearZeroCentreredSigmoide(type *inputBuffer, type *outputBuffer, const type sensitivity, const type maxOutputValue)
|
||||
{
|
||||
if (inputBuffer==NULL)
|
||||
inputBuffer=Buffer();
|
||||
if (outputBuffer==NULL)
|
||||
outputBuffer=Buffer();
|
||||
|
||||
type X0cube=sensitivity*sensitivity*sensitivity;
|
||||
|
||||
type *inputBufferPTR=inputBuffer;
|
||||
type *outputBufferPTR=outputBuffer;
|
||||
|
||||
for (size_t j = 0; j < _NBpixels; ++j, ++inputBufferPTR)
|
||||
{
|
||||
|
||||
type currentCubeLuminance=*inputBufferPTR**inputBufferPTR**inputBufferPTR;
|
||||
*(outputBufferPTR++)=maxOutputValue*currentCubeLuminance/(currentCubeLuminance+X0cube);
|
||||
}
|
||||
}
|
||||
|
||||
// normalize and adjust luminance with a centered to 128 sigmode
|
||||
template <class type>
|
||||
void TemplateBuffer<type>::normalizeGrayOutputCentredSigmoide(const type meanValue, const type sensitivity, const type maxOutputValue, type *inputBuffer, type *outputBuffer, const unsigned int nbPixels)
|
||||
{
|
||||
|
||||
if (sensitivity==1.0)
|
||||
{
|
||||
std::cerr<<"TemplateBuffer::TemplateBuffer<type>::normalizeGrayOutputCentredSigmoide error: 2nd parameter (sensitivity) must not equal 0, copying original data..."<<std::endl;
|
||||
memcpy(outputBuffer, inputBuffer, sizeof(type)*nbPixels);
|
||||
return;
|
||||
}
|
||||
|
||||
type X0=maxOutputValue/(sensitivity-(type)1.0);
|
||||
|
||||
type *inputBufferPTR=inputBuffer;
|
||||
type *outputBufferPTR=outputBuffer;
|
||||
|
||||
for (size_t j = 0; j < nbPixels; ++j, ++inputBufferPTR)
|
||||
*(outputBufferPTR++)=(meanValue+(meanValue+X0)*(*(inputBufferPTR)-meanValue)/(_abs(*(inputBufferPTR)-meanValue)+X0));
|
||||
|
||||
}
|
||||
|
||||
// center and reduct the image (image-mean)/std
|
||||
template <class type>
|
||||
void TemplateBuffer<type>::centerReductImageLuminance(type *inputOutputBuffer)
|
||||
{
|
||||
// if outputBuffer unsassigned, the rewrite the buffer
|
||||
if (inputOutputBuffer==NULL)
|
||||
inputOutputBuffer=Buffer();
|
||||
type meanValue=0, stdValue=0;
|
||||
|
||||
// compute mean value
|
||||
for (size_t j = 0; j < _NBpixels; ++j)
|
||||
meanValue+=inputOutputBuffer[j];
|
||||
meanValue/=((type)_NBpixels);
|
||||
|
||||
// compute std value
|
||||
type *inputOutputBufferPTR=inputOutputBuffer;
|
||||
for (size_t index=0;index<_NBpixels;++index)
|
||||
{
|
||||
type inputMinusMean=*(inputOutputBufferPTR++)-meanValue;
|
||||
stdValue+=inputMinusMean*inputMinusMean;
|
||||
}
|
||||
|
||||
stdValue=std::sqrt(stdValue/((type)_NBpixels));
|
||||
// adjust luminance in regard of mean and std value;
|
||||
inputOutputBufferPTR=inputOutputBuffer;
|
||||
for (size_t index=0;index<_NBpixels;++index, ++inputOutputBufferPTR)
|
||||
*inputOutputBufferPTR=(*(inputOutputBufferPTR)-meanValue)/stdValue;
|
||||
}
|
||||
|
||||
|
||||
template <class type>
|
||||
type TemplateBuffer<type>::_abs(const type x)
|
||||
{
|
||||
|
||||
if (x>0)
|
||||
return x;
|
||||
else
|
||||
return -x;
|
||||
}
|
||||
|
||||
template < >
|
||||
inline int TemplateBuffer<int>::_abs(const int x)
|
||||
{
|
||||
return std::abs(x);
|
||||
}
|
||||
template < >
|
||||
inline double TemplateBuffer<double>::_abs(const double x)
|
||||
{
|
||||
return std::fabs(x);
|
||||
}
|
||||
|
||||
template < >
|
||||
inline float TemplateBuffer<float>::_abs(const float x)
|
||||
{
|
||||
return std::fabs(x);
|
||||
}
|
||||
|
||||
}// end of namespace bioinspired
|
||||
}// end of namespace cv
|
||||
#endif
|
||||
@@ -0,0 +1,590 @@
|
||||
/*#******************************************************************************
|
||||
** IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
**
|
||||
** By downloading, copying, installing or using the software you agree to this license.
|
||||
** If you do not agree to this license, do not download, install,
|
||||
** copy or use the software.
|
||||
**
|
||||
**
|
||||
** bioinspired : interfaces allowing OpenCV users to integrate Human Vision System models.
|
||||
** TransientAreasSegmentationModule Use: extract areas that present spatio-temporal changes.
|
||||
** => It should be used at the output of the cv::bioinspired::Retina::getMagnoRAW() output that enhances spatio-temporal changes
|
||||
**
|
||||
** Maintainers : Listic lab (code author current affiliation & applications)
|
||||
**
|
||||
** Creation - enhancement process 2007-2015
|
||||
** Author: Alexandre Benoit (benoit.alexandre.vision@gmail.com), LISTIC lab, Annecy le vieux, France
|
||||
**
|
||||
** Theses algorithm have been developped by Alexandre BENOIT since his thesis with Alice Caplier at Gipsa-Lab (www.gipsa-lab.inpg.fr) and the research he pursues at LISTIC Lab (www.listic.univ-savoie.fr).
|
||||
** Refer to the following research paper for more information:
|
||||
** Strat, S.T.; Benoit, A.; Lambert, P., "Retina enhanced bag of words descriptors for video classification," Signal Processing Conference (EUSIPCO), 2014 Proceedings of the 22nd European , vol., no., pp.1307,1311, 1-5 Sept. 2014 (http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=6952461&isnumber=6951911)
|
||||
** Benoit A., Caplier A., Durette B., Herault, J., "USING HUMAN VISUAL SYSTEM MODELING FOR BIO-INSPIRED LOW LEVEL IMAGE PROCESSING", Elsevier, Computer Vision and Image Understanding 114 (2010), pp. 758-773, DOI: http://dx.doi.org/10.1016/j.cviu.2010.01.011
|
||||
** This work have been carried out thanks to Jeanny Herault who's research and great discussions are the basis of all this work, please take a look at his book:
|
||||
** Vision: Images, Signals and Neural Networks: Models of Neural Processing in Visual Perception (Progress in Neural Processing),By: Jeanny Herault, ISBN: 9814273686. WAPI (Tower ID): 113266891.
|
||||
**
|
||||
**
|
||||
** License Agreement
|
||||
** For Open Source Computer Vision Library
|
||||
**
|
||||
** Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
** Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
**
|
||||
** For Human Visual System tools (bioinspired)
|
||||
** Copyright (C) 2007-2015, LISTIC Lab, Annecy le Vieux and GIPSA Lab, Grenoble, France, all rights reserved.
|
||||
**
|
||||
** Third party copyrights are property of their respective owners.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
**
|
||||
** * Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
**
|
||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
**
|
||||
** * The name of the copyright holders may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** This software is provided by the copyright holders and contributors "as is" and
|
||||
** any express or implied warranties, including, but not limited to, the implied
|
||||
** warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
** In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
** indirect, incidental, special, exemplary, or consequential damages
|
||||
** (including, but not limited to, procurement of substitute goods or services;
|
||||
** loss of use, data, or profits; or business interruption) however caused
|
||||
** and on any theory of liability, whether in contract, strict liability,
|
||||
** or tort (including negligence or otherwise) arising in any way out of
|
||||
** the use of this software, even if advised of the possibility of such damage.
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* @class TransientAreasSegmentationModule
|
||||
* @brief class which provides a transient/moving areas segmentation module
|
||||
* -> perform a locally adapted segmentation by using the retina magno output data
|
||||
* @author Alexandre BENOIT, benoit.alexandre.vision@gmail.com
|
||||
* Release date 2007-2014
|
||||
* Based on Alexandre BENOIT thesis: "Le système visuel humain au secours de la vision par ordinateur"
|
||||
* -> 3 spatio temporal filters are used:
|
||||
* a first one which filters the noise and local variations of the input motion energy
|
||||
* a second (more powerfull low pass spatial filter) which gives the neighborhood motion energy
|
||||
* a third that measures the global motion context on a wide area
|
||||
* -> the segmentation consists in the comparison of these filter outputs, if the local motion energy is higher to the neighborhood motion energy, then the area is considered as moving and is segmented
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "basicretinafilter.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#define _SEGMENTATIONDEBUG //define SEGMENTATIONDEBUG to access more data/methods
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace bioinspired
|
||||
{
|
||||
|
||||
class TransientAreasSegmentationModuleImpl : protected BasicRetinaFilter
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* constructor
|
||||
* @param Size : size of the images input to segment (output will be the same size)
|
||||
*/
|
||||
TransientAreasSegmentationModuleImpl(const Size inputSize);
|
||||
|
||||
/**
|
||||
* standard destructor
|
||||
*/
|
||||
virtual ~TransientAreasSegmentationModuleImpl();
|
||||
|
||||
/**
|
||||
* @return the size of the manage input and output images
|
||||
*/
|
||||
Size getSize(){return cv::Size(getNBcolumns(), getNBrows());}
|
||||
|
||||
/**
|
||||
* try to open an XML segmentation parameters file to adjust current segmentation instance setup
|
||||
* => if the xml file does not exist, then default setup is applied
|
||||
* => warning, Exceptions are thrown if read XML file is not valid
|
||||
* @param retinaParameterFile : the parameters filename
|
||||
* @param applyDefaultSetupOnFailure : set to true if an error must be thrown on error
|
||||
*/
|
||||
void setup(String segmentationParameterFile="", const bool applyDefaultSetupOnFailure=true);
|
||||
|
||||
/**
|
||||
* try to open an XML segmentation parameters file to adjust current segmentation instance setup
|
||||
* => if the xml file does not exist, then default setup is applied
|
||||
* => warning, Exceptions are thrown if read XML file is not valid
|
||||
* @param fs : the open Filestorage which contains segmentation parameters
|
||||
* @param applyDefaultSetupOnFailure : set to true if an error must be thrown on error
|
||||
*/
|
||||
void setup(cv::FileStorage &fs, const bool applyDefaultSetupOnFailure=true);
|
||||
|
||||
/**
|
||||
* try to open an XML segmentation parameters file to adjust current segmentation instance setup
|
||||
* => if the xml file does not exist, then default setup is applied
|
||||
* => warning, Exceptions are thrown if read XML file is not valid
|
||||
* @param newParameters : a parameters structures updated with the new target configuration
|
||||
* @param applyDefaultSetupOnFailure : set to true if an error must be thrown on error
|
||||
*/
|
||||
void setup(SegmentationParameters newParameters);
|
||||
|
||||
/**
|
||||
* @return the current parameters setup
|
||||
*/
|
||||
SegmentationParameters getParameters();
|
||||
|
||||
/**
|
||||
* parameters setup display method
|
||||
* @return a string which contains formatted parameters information
|
||||
*/
|
||||
String printSetup();
|
||||
|
||||
/**
|
||||
* write xml/yml formated parameters information
|
||||
* @rparam fs : the filename of the xml file that will be open and writen with formatted parameters information
|
||||
*/
|
||||
virtual void write( String fs ) const;
|
||||
|
||||
/**
|
||||
* write xml/yml formated parameters information
|
||||
* @param fs : a cv::Filestorage object ready to be filled
|
||||
*/
|
||||
virtual void write( cv::FileStorage& fs ) const;
|
||||
|
||||
|
||||
/**
|
||||
* main processing method, get result using methods getSegmentationPicture()
|
||||
* @param inputToSegment : the image to process, it must match the instance buffer size !
|
||||
* @param channelIndex : the channel to process in case of multichannel images
|
||||
*/
|
||||
void run(InputArray inputToSegment, const int channelIndex=0);
|
||||
|
||||
/**
|
||||
* access function
|
||||
* return the last segmentation result: a boolean picture which is resampled between 0 and 255 for a display purpose
|
||||
*/
|
||||
void getSegmentationPicture(OutputArray transientAreas);
|
||||
|
||||
/**
|
||||
* cleans all the buffers of the instance
|
||||
*/
|
||||
void clearAllBuffers();
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* main processing method
|
||||
* @param inputToSegment : the image to process as a valarray buffer it must match the instance buffer size !
|
||||
* @param channelIndex : the channel to process in case of multichannel images
|
||||
*/
|
||||
void _run(const std::valarray<float> &inputToSegment, const int channelIndex=0);
|
||||
|
||||
/**
|
||||
* access function
|
||||
* @return the local motion energy level picture (experimental, not usefull)
|
||||
*/
|
||||
inline const std::valarray<float> &getLocalMotionPicture() const {return _localMotion;}
|
||||
|
||||
/**
|
||||
* access function
|
||||
* @return the neighborhood motion energy level picture (experimental, not usefull)
|
||||
*/
|
||||
inline const std::valarray<float> &getNeighborhoodMotionPicture() const {return _neighborhoodMotion;}
|
||||
|
||||
/**
|
||||
* access function
|
||||
* @return the motion energy context level picture (experimental, not usefull)
|
||||
*/
|
||||
inline const std::valarray<float> &getMotionContextPicture() const {return _contextMotionEnergy;}
|
||||
|
||||
cv::bioinspired::SegmentationParameters _segmentationParameters;
|
||||
// template buffers and related acess pointers
|
||||
std::valarray<float> _inputToSegment;
|
||||
std::valarray<float> _contextMotionEnergy;
|
||||
std::valarray<bool> _segmentedAreas;
|
||||
|
||||
// pointers to base class buffers
|
||||
std::valarray<float> &_localMotion;
|
||||
std::valarray<float> &_neighborhoodMotion;
|
||||
unsigned int _numberOfSegmentedObjects;
|
||||
|
||||
cv::Mat _conversionBuffer;
|
||||
cv::Mat _segmentedPicture;
|
||||
|
||||
// Buffer conversion utilities
|
||||
void _convertValarrayBuffer2cvMat(const std::valarray<bool> &grayMatrixToConvert, const unsigned int nbRows, const unsigned int nbColumns, OutputArray outBuffer);
|
||||
bool _convertCvMat2ValarrayBuffer(InputArray inputMat, std::valarray<float> &outputValarrayMatrix);
|
||||
|
||||
const TransientAreasSegmentationModuleImpl & operator = (const TransientAreasSegmentationModuleImpl &);
|
||||
};
|
||||
|
||||
class TransientAreasSegmentationModuleImpl_: public TransientAreasSegmentationModule
|
||||
{
|
||||
public:
|
||||
TransientAreasSegmentationModuleImpl_(const Size size):_segmTool(size){}
|
||||
inline virtual Size getSize() CV_OVERRIDE { return _segmTool.getSize(); }
|
||||
inline virtual void write( cv::FileStorage& fs ) const CV_OVERRIDE { _segmTool.write(fs); }
|
||||
inline virtual void setup(String segmentationParameterFile, const bool applyDefaultSetupOnFailure) CV_OVERRIDE { _segmTool.setup(segmentationParameterFile, applyDefaultSetupOnFailure); }
|
||||
inline virtual void setup(cv::FileStorage &fs, const bool applyDefaultSetupOnFailure) CV_OVERRIDE { _segmTool.setup(fs, applyDefaultSetupOnFailure); }
|
||||
inline virtual void setup(SegmentationParameters newParameters) CV_OVERRIDE { _segmTool.setup(newParameters); }
|
||||
inline virtual String printSetup() CV_OVERRIDE { return _segmTool.printSetup(); }
|
||||
inline virtual SegmentationParameters getParameters() CV_OVERRIDE { return _segmTool.getParameters(); }
|
||||
inline virtual void write( String fs ) const CV_OVERRIDE { _segmTool.write(fs); }
|
||||
inline virtual void run(InputArray inputToSegment, const int channelIndex) CV_OVERRIDE { _segmTool.run(inputToSegment, channelIndex); }
|
||||
inline virtual void getSegmentationPicture(OutputArray transientAreas) CV_OVERRIDE { return _segmTool.getSegmentationPicture(transientAreas); }
|
||||
inline virtual void clearAllBuffers() CV_OVERRIDE { _segmTool.clearAllBuffers(); }
|
||||
|
||||
private:
|
||||
TransientAreasSegmentationModuleImpl _segmTool;
|
||||
};
|
||||
|
||||
/**
|
||||
* allocator
|
||||
* @param Size : size of the images input to segment (output will be the same size)
|
||||
*/
|
||||
Ptr<TransientAreasSegmentationModule> TransientAreasSegmentationModule::create(Size inputSize){
|
||||
return makePtr<TransientAreasSegmentationModuleImpl_>(inputSize);
|
||||
}
|
||||
|
||||
// Constructor and destructors
|
||||
TransientAreasSegmentationModuleImpl::TransientAreasSegmentationModuleImpl(const Size size)
|
||||
:BasicRetinaFilter(size.height, size.width, 3),
|
||||
// allocate the output of the class
|
||||
_inputToSegment(size.height*size.width),
|
||||
_contextMotionEnergy(size.height*size.width),
|
||||
_segmentedAreas(size.height*size.width),
|
||||
// set the pointer to the 2 frame buffer to the correct adress:
|
||||
// -> the first low pass filter buffer will be _localBuffer
|
||||
// -> the second will be _filterOutput;
|
||||
_localMotion(_localBuffer),
|
||||
_neighborhoodMotion(_filterOutput)
|
||||
{
|
||||
|
||||
// default parameters setup
|
||||
setup(_segmentationParameters);
|
||||
//clean before running
|
||||
clearAllBuffers();
|
||||
}
|
||||
|
||||
TransientAreasSegmentationModuleImpl::~TransientAreasSegmentationModuleImpl()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void TransientAreasSegmentationModuleImpl::clearAllBuffers()
|
||||
{
|
||||
// flush parent buffers
|
||||
bioinspired::BasicRetinaFilter::clearAllBuffers();
|
||||
// flush instance buffers
|
||||
_contextMotionEnergy=0;
|
||||
_segmentedAreas=0;
|
||||
}
|
||||
|
||||
SegmentationParameters TransientAreasSegmentationModuleImpl::getParameters()
|
||||
{
|
||||
return _segmentationParameters;
|
||||
}
|
||||
|
||||
// setup from XML file
|
||||
void TransientAreasSegmentationModuleImpl::setup(String segmentationParameterFile, const bool applyDefaultSetupOnFailure)
|
||||
{
|
||||
try
|
||||
{
|
||||
// opening retinaParameterFile in read mode
|
||||
cv::FileStorage fs(segmentationParameterFile, cv::FileStorage::READ);
|
||||
setup(fs, applyDefaultSetupOnFailure);
|
||||
}catch(const cv::Exception &e)
|
||||
{
|
||||
printf("Retina::setup: wrong/unappropriate xml parameter file : error report :`n=>%s\n", e.what());
|
||||
if (applyDefaultSetupOnFailure)
|
||||
{
|
||||
printf("Retina::setup: resetting retina with default parameters\n");
|
||||
cv::bioinspired::SegmentationParameters defaults;
|
||||
setup(defaults);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("=> keeping current parameters");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setup from cv::Filestorage object
|
||||
void TransientAreasSegmentationModuleImpl::setup(cv::FileStorage &fs, const bool applyDefaultSetupOnFailure)
|
||||
{
|
||||
try
|
||||
{
|
||||
// read parameters file if it exists or apply default setup if asked for
|
||||
if (!fs.isOpened())
|
||||
{
|
||||
std::cout<<"Retina::setup: provided parameters file could not be open... skeeping configuration"<<std::endl;
|
||||
return;
|
||||
// implicit else case : retinaParameterFile could be open (it exists at least)
|
||||
}
|
||||
// OPL and Parvo init first... update at the same time the parameters structure and the retina core
|
||||
cv::FileNode rootFn = fs.root(), currFn=rootFn["SegmentationModuleSetup"];
|
||||
currFn["thresholdON"]>>_segmentationParameters.thresholdON;
|
||||
currFn["thresholdOFF"]>>_segmentationParameters.thresholdOFF;
|
||||
currFn["localEnergy_temporalConstant"]>>_segmentationParameters.localEnergy_temporalConstant;
|
||||
currFn["localEnergy_spatialConstant"]>>_segmentationParameters.localEnergy_spatialConstant;
|
||||
currFn["neighborhoodEnergy_temporalConstant"]>>_segmentationParameters.neighborhoodEnergy_temporalConstant;
|
||||
currFn["neighborhoodEnergy_spatialConstant"]>>_segmentationParameters.neighborhoodEnergy_spatialConstant;
|
||||
currFn["contextEnergy_temporalConstant"]>>_segmentationParameters.contextEnergy_temporalConstant;
|
||||
currFn["contextEnergy_spatialConstant"]>>_segmentationParameters.contextEnergy_spatialConstant;
|
||||
setup(_segmentationParameters);
|
||||
|
||||
}catch(const cv::Exception &e)
|
||||
{
|
||||
std::cout<<"Retina::setup: resetting retina with default parameters"<<std::endl;
|
||||
if (applyDefaultSetupOnFailure)
|
||||
{
|
||||
cv::bioinspired::SegmentationParameters defaults;
|
||||
setup(defaults);
|
||||
}
|
||||
std::cout<<"SegmentationModule::setup: wrong/unappropriate xml parameter file : error report :`n=>"<<e.what()<<std::endl;
|
||||
std::cout<<"=> keeping current parameters"<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// setup parameters for the 2 filters that allow the segmentation
|
||||
void TransientAreasSegmentationModuleImpl::setup(cv::bioinspired::SegmentationParameters newParameters)
|
||||
{
|
||||
|
||||
// copy structure contents
|
||||
_segmentationParameters = newParameters;
|
||||
// apply setup
|
||||
// init local motion energy extraction low pass filter
|
||||
BasicRetinaFilter::setLPfilterParameters(0, newParameters.localEnergy_temporalConstant, newParameters.localEnergy_spatialConstant);
|
||||
// init neighbohood motion energy extraction low pass filter
|
||||
BasicRetinaFilter::setLPfilterParameters(0, newParameters.neighborhoodEnergy_temporalConstant, newParameters.neighborhoodEnergy_spatialConstant, 1);
|
||||
|
||||
// init large area low pass filter
|
||||
BasicRetinaFilter::setLPfilterParameters(0, newParameters.contextEnergy_temporalConstant, newParameters.contextEnergy_spatialConstant, 2);
|
||||
|
||||
}
|
||||
|
||||
String TransientAreasSegmentationModuleImpl::printSetup()
|
||||
{
|
||||
std::stringstream outmessage;
|
||||
|
||||
outmessage<<"Current segmentation instance setup :"
|
||||
<<"\n\t thresholdON : " << _segmentationParameters.thresholdON
|
||||
<<"\n\t thresholdOFF : " << _segmentationParameters.thresholdOFF
|
||||
<<"\n\t localEnergy_temporalConstant : " << _segmentationParameters.localEnergy_temporalConstant
|
||||
<<"\n\t localEnergy_spatialConstant : " << _segmentationParameters.localEnergy_spatialConstant
|
||||
<<"\n\t neighborhoodEnergy_temporalConstant : " << _segmentationParameters.neighborhoodEnergy_temporalConstant
|
||||
<<"\n\t neighborhoodEnergy_spatialConstant : " << _segmentationParameters.neighborhoodEnergy_spatialConstant
|
||||
<<"\n\t contextEnergy_temporalConstant : " << _segmentationParameters.contextEnergy_temporalConstant
|
||||
<<"\n\t contextEnergy_spatialConstant : " << _segmentationParameters.contextEnergy_spatialConstant;
|
||||
|
||||
return outmessage.str().c_str();
|
||||
}
|
||||
|
||||
void TransientAreasSegmentationModuleImpl::write( String fs ) const
|
||||
{
|
||||
cv::FileStorage parametersSaveFile(fs, cv::FileStorage::WRITE );
|
||||
write(parametersSaveFile);
|
||||
}
|
||||
|
||||
void TransientAreasSegmentationModuleImpl::write( cv::FileStorage& fs ) const
|
||||
{
|
||||
if (!fs.isOpened())
|
||||
return; // basic error case
|
||||
fs <<"SegmentationModuleSetup"<<"{";
|
||||
fs <<"thresholdON" << _segmentationParameters.thresholdON;
|
||||
fs <<"thresholdOFF" << _segmentationParameters.thresholdOFF;
|
||||
fs <<"localEnergy_temporalConstant" << _segmentationParameters.localEnergy_temporalConstant;
|
||||
fs <<"localEnergy_spatialConstant" << _segmentationParameters.localEnergy_spatialConstant;
|
||||
fs <<"neighborhoodEnergy_temporalConstant" << _segmentationParameters.neighborhoodEnergy_temporalConstant;
|
||||
fs <<"neighborhoodEnergy_spatialConstant" << _segmentationParameters.neighborhoodEnergy_spatialConstant;
|
||||
fs <<"contextEnergy_temporalConstant" << _segmentationParameters.contextEnergy_temporalConstant;
|
||||
fs <<"contextEnergy_spatialConstant" << _segmentationParameters.contextEnergy_spatialConstant;
|
||||
fs <<"}";
|
||||
}
|
||||
|
||||
void TransientAreasSegmentationModuleImpl::run(InputArray inputToProcess, const int channelIndex)
|
||||
{
|
||||
cv::Mat inputToSegment=inputToProcess.getMat();
|
||||
// preliminary basic error check
|
||||
if ( (inputToSegment.rows*inputToSegment.cols) != (int)_inputToSegment.size())
|
||||
{
|
||||
std::stringstream errorMsg;
|
||||
errorMsg<<"Input matrix size does not match instance buffers setup !"
|
||||
<<"\n\t Input size is : "<<inputToSegment.rows*inputToSegment.cols
|
||||
<<"\n\t v.s. internalBuffer size is : "<< _inputToSegment.size();
|
||||
CV_Error(cv::Error::StsError, errorMsg.str().c_str());
|
||||
}
|
||||
if (channelIndex >= inputToSegment.channels())
|
||||
{
|
||||
std::stringstream errorMsg;
|
||||
errorMsg<<"Cannot access channel index "<<channelIndex<<" on the input matrix with channels quantity = "<<inputToSegment.channels();
|
||||
CV_Error(cv::Error::StsError, errorMsg.str().c_str());
|
||||
}
|
||||
|
||||
// create a cv::Mat header for the input valarray
|
||||
// convert to float AND fill the valarray buffer
|
||||
typedef float T; // define here the target pixel format, here, float
|
||||
const int dsttype = cv::DataType<T>::depth; // output buffer is float format
|
||||
cv::Mat dst(inputToSegment.size(), dsttype, &_inputToSegment[0]);
|
||||
inputToSegment.convertTo(dst, dsttype);
|
||||
//cv::imshow("Mask",dst);
|
||||
//cv::waitKey();
|
||||
// call the low level method
|
||||
_run(_inputToSegment, channelIndex);
|
||||
}
|
||||
|
||||
void TransientAreasSegmentationModuleImpl::_run(const std::valarray<float> &inputToSegment, const int channelIndex)
|
||||
{
|
||||
#ifdef SEGMENTATIONDEBUG
|
||||
std::cout<<"Input length vs internal buffers length = "<<inputToSegment.size()<<", "<<_localMotion.size()<<std::endl;
|
||||
#endif
|
||||
// preliminary basic error check
|
||||
// FIXME validate basic tests
|
||||
//if (inputToSegment.size() != _localMotion.size())
|
||||
// throw cv::Exception(-1, "Input matrix size does not match instance buffers setup !", "SegmentationModule::run", "SegmentationModule.cpp", 0);
|
||||
|
||||
// first square the input in order to increase the signal to noise ratio
|
||||
// get motion local energy
|
||||
_squaringSpatiotemporalLPfilter(&const_cast<std::valarray<float>&>(inputToSegment)[channelIndex*getNBpixels()], &_localMotion[0]);
|
||||
|
||||
// second low pass filter: access to the neighborhood motion energy
|
||||
_spatiotemporalLPfilter(&_localMotion[0], &_neighborhoodMotion[0], 1);
|
||||
|
||||
// third low pass filter: access to the background motion energy
|
||||
_spatiotemporalLPfilter(&_localMotion[0], &_contextMotionEnergy[0], 2);
|
||||
|
||||
// compute the ON and OFF ways (positive and negative values of the difference of the two filterings)
|
||||
float*localMotionPTR=&_localMotion[0], *neighborhoodMotionPTR=&_neighborhoodMotion[0], *contextMotionPTR=&_contextMotionEnergy[0];
|
||||
|
||||
// float meanEnergy=LPfilter2.sum()/(float)_LPfilter2.size();
|
||||
bool *segmentationPicturePTR= &_segmentedAreas[0];
|
||||
for (unsigned int index=0; index<_filterOutput.getNBpixels() ; ++index, ++segmentationPicturePTR, ++localMotionPTR, ++neighborhoodMotionPTR, contextMotionPTR++)
|
||||
{
|
||||
float generalMotionContextDecision=*neighborhoodMotionPTR-*contextMotionPTR;
|
||||
|
||||
if (generalMotionContextDecision>0) // local maximum should be detected in this case
|
||||
{
|
||||
/* apply segmentation on local motion superior to its neighborhood
|
||||
* => to segment objects moving faster than their neighborhood
|
||||
*/
|
||||
if (generalMotionContextDecision>_segmentationParameters.thresholdON)// && meanEnergy*1.1<*neighborhoodMotionPTR)
|
||||
{
|
||||
*segmentationPicturePTR=((*localMotionPTR-*neighborhoodMotionPTR)>_segmentationParameters.thresholdON);
|
||||
}
|
||||
else
|
||||
*segmentationPicturePTR=false;
|
||||
}
|
||||
#ifdef USE_LOCALMINIMUMS
|
||||
else // local minimum should be detected in this case
|
||||
{
|
||||
/* apply segmentation for non moving objects
|
||||
* only if the wide area around motion energy is high
|
||||
* => to segment object moving slower than the neighborhood
|
||||
*/
|
||||
if (-1.0*generalMotionContextDecision>_segmentationParameters.thresholdOFF && meanEnergy*0.9>*neighborhoodMotionPTR)
|
||||
{
|
||||
/* in order to segment non moving objects in a camera motion case
|
||||
* we focus on local energy which is much lower than the wide neighborhood
|
||||
*/
|
||||
*segmentationPicturePTR+=(*neighborhoodMotionPTR-*localMotionPTR>_segmentationParameters.thresholdOFF)*127;
|
||||
}
|
||||
}
|
||||
#else
|
||||
else
|
||||
*segmentationPicturePTR=false;
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
#ifdef SEGMENTATIONDEBUG
|
||||
std::cout<<"ON: max, min="<<_localMotionON.min()<<", "<<_localMotionON.max();
|
||||
std::cout<<"/// \\\ OFF: max, min="<<_localMotionOFF.min()<<", "<<_localMotionOFF.max()<<std::endl;
|
||||
std::cout<<"/// \\\ motion: max, min="<<_globalMotionEnergy.min()<<", "<<_globalMotionEnergy.max()<<std::endl;
|
||||
std::cout<<"/// \\\ thresholds: ON, OFF="<<_thresholdON<<", "<<_thresholdOFF<<", meanEnergy= "<<meanEnergy<<std::endl;
|
||||
#endif
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
void TransientAreasSegmentationModuleImpl::getSegmentationPicture(OutputArray transientAreas)
|
||||
{
|
||||
_convertValarrayBuffer2cvMat(_segmentedAreas, getNBrows(), getNBcolumns(), transientAreas);
|
||||
}
|
||||
|
||||
|
||||
void TransientAreasSegmentationModuleImpl::_convertValarrayBuffer2cvMat(const std::valarray<bool> &grayMatrixToConvert, const unsigned int nbRows, const unsigned int nbColumns, OutputArray outBuffer)
|
||||
{
|
||||
// fill output buffer with the valarray buffer
|
||||
const bool *valarrayPTR=get_data(grayMatrixToConvert);
|
||||
|
||||
outBuffer.create(cv::Size(nbColumns, nbRows), CV_8U);
|
||||
Mat outMat = outBuffer.getMat();
|
||||
for (unsigned int i=0;i<nbRows;++i)
|
||||
{
|
||||
for (unsigned int j=0;j<nbColumns;++j)
|
||||
{
|
||||
cv::Point2d pixel(j,i);
|
||||
outMat.at<unsigned char>(pixel)=(unsigned char)*(valarrayPTR++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TransientAreasSegmentationModuleImpl::_convertCvMat2ValarrayBuffer(InputArray inputMat, std::valarray<float> &outputValarrayMatrix)
|
||||
{
|
||||
const Mat inputMatToConvert=inputMat.getMat();
|
||||
// first check input consistency
|
||||
if (inputMatToConvert.empty())
|
||||
CV_Error(cv::Error::StsError, "RetinaImpl cannot be applied, input buffer is empty");
|
||||
|
||||
// retreive color mode from image input
|
||||
int imageNumberOfChannels = inputMatToConvert.channels();
|
||||
|
||||
// convert to float AND fill the valarray buffer
|
||||
typedef float T; // define here the target pixel format, here, float
|
||||
const int dsttype = DataType<T>::depth; // output buffer is float format
|
||||
|
||||
const unsigned int nbPixels=inputMat.getMat().rows*inputMat.getMat().cols;
|
||||
const unsigned int doubleNBpixels=inputMat.getMat().rows*inputMat.getMat().cols*2;
|
||||
|
||||
if(imageNumberOfChannels==4)
|
||||
{
|
||||
// create a cv::Mat table (for RGBA planes)
|
||||
cv::Mat planes[4] =
|
||||
{
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[doubleNBpixels]),
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[nbPixels]),
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[0])
|
||||
};
|
||||
planes[3] = cv::Mat(inputMatToConvert.size(), dsttype); // last channel (alpha) does not point on the valarray (not usefull in our case)
|
||||
// split color cv::Mat in 4 planes... it fills valarray directely
|
||||
cv::split(Mat_<Vec<T, 4> >(inputMatToConvert), planes);
|
||||
}
|
||||
else if (imageNumberOfChannels==3)
|
||||
{
|
||||
// create a cv::Mat table (for RGB planes)
|
||||
cv::Mat planes[] =
|
||||
{
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[doubleNBpixels]),
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[nbPixels]),
|
||||
cv::Mat(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[0])
|
||||
};
|
||||
// split color cv::Mat in 3 planes... it fills valarray directely
|
||||
cv::split(cv::Mat_<Vec<T, 3> >(inputMatToConvert), planes);
|
||||
}
|
||||
else if(imageNumberOfChannels==1)
|
||||
{
|
||||
// create a cv::Mat header for the valarray
|
||||
cv::Mat dst(inputMatToConvert.size(), dsttype, &outputValarrayMatrix[0]);
|
||||
inputMatToConvert.convertTo(dst, dsttype);
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsUnsupportedFormat, "input image must be single channel (gray levels), bgr format (color) or bgra (color with transparency which won't be considered");
|
||||
|
||||
return imageNumberOfChannels>1; // return bool : false for gray level image processing, true for color mode
|
||||
}
|
||||
|
||||
}} //namespaces end : cv and bioinspired
|
||||
@@ -0,0 +1,6 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
CV_TEST_MAIN("cv")
|
||||
@@ -0,0 +1,10 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#ifndef __OPENCV_TEST_PRECOMP_HPP__
|
||||
#define __OPENCV_TEST_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/bioinspired.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,115 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2010-2013, Multicoreware, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Peng Xiao, pengxiao@multicorewareinc.com
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors as is and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_test.hpp"
|
||||
|
||||
#define RETINA_ITERATIONS 5
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
PARAM_TEST_CASE(Retina_OCL, bool, int, bool, double, double)
|
||||
{
|
||||
bool colorMode;
|
||||
int colorSamplingMethod;
|
||||
bool useLogSampling;
|
||||
float reductionFactor;
|
||||
float samplingStrength;
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
colorMode = GET_PARAM(0);
|
||||
colorSamplingMethod = GET_PARAM(1);
|
||||
useLogSampling = GET_PARAM(2);
|
||||
reductionFactor = static_cast<float>(GET_PARAM(3));
|
||||
samplingStrength = static_cast<float>(GET_PARAM(4));
|
||||
}
|
||||
};
|
||||
|
||||
OCL_TEST_P(Retina_OCL, Accuracy)
|
||||
{
|
||||
Mat input = imread(cvtest::TS::ptr()->get_data_path() + "shared/lena.png", colorMode);
|
||||
CV_Assert(!input.empty());
|
||||
|
||||
Ptr<bioinspired::Retina> retina = bioinspired::Retina::create(
|
||||
input.size(),
|
||||
colorMode,
|
||||
colorSamplingMethod,
|
||||
useLogSampling,
|
||||
reductionFactor,
|
||||
samplingStrength);
|
||||
|
||||
Mat gold_parvo;
|
||||
Mat gold_magno;
|
||||
UMat ocl_parvo;
|
||||
UMat ocl_magno;
|
||||
|
||||
for(int i = 0; i < RETINA_ITERATIONS; i ++)
|
||||
{
|
||||
OCL_OFF(retina->run(input));
|
||||
OCL_OFF(retina->getParvo(gold_parvo));
|
||||
OCL_OFF(retina->getMagno(gold_magno));
|
||||
OCL_OFF(retina->clearBuffers());
|
||||
|
||||
OCL_ON(retina->run(input));
|
||||
OCL_ON(retina->getParvo(ocl_parvo));
|
||||
OCL_ON(retina->getMagno(ocl_magno));
|
||||
OCL_ON(retina->clearBuffers());
|
||||
|
||||
int eps = 1;
|
||||
|
||||
EXPECT_MAT_NEAR(gold_parvo, ocl_parvo, eps);
|
||||
EXPECT_MAT_NEAR(gold_magno, ocl_magno, eps);
|
||||
}
|
||||
}
|
||||
|
||||
OCL_INSTANTIATE_TEST_CASE_P(Contrib, Retina_OCL, testing::Combine(
|
||||
testing::Bool(),
|
||||
testing::Values((int)cv::bioinspired::RETINA_COLOR_BAYER),
|
||||
testing::Values(false/*,true*/),
|
||||
testing::Values(1.0, 0.5),
|
||||
testing::Values(10.0, 5.0)));
|
||||
|
||||
}} // namespace
|
||||
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 212 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,187 @@
|
||||
Processing images causing optical illusions {#tutorial_bioinspired_retina_illusion}
|
||||
=============================================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
I will show here how the bioinspired module can reproduce a well-known optical illusion that
|
||||
our eyes perceive in certain light condition: The Adelson checkerboard.
|
||||
|
||||
The Adelson checkerboard
|
||||
------------------------
|
||||
|
||||
Looking at the checkerboard image below, human eyes perceive the "B" square lighter than the
|
||||
"A" square, although they are pictured in the very same RGB color.
|
||||
Of course in the physical world, checkerboard has a "B" square which is lighter than "A", but in this image the
|
||||
shadow of the green cylinder casting over the "B" square ends up in making the "A" and "B"
|
||||
squares actually having the same luminance.
|
||||
|
||||

|
||||
|
||||
Our visual system does "compensate" for the shadow, making us perceive the "B" square lighter,
|
||||
as the shadow wouldn't be there. This is due to local adaptation process that is performed in the
|
||||
foveal area.
|
||||
|
||||
You may find the original Adelson's explanation [here](http://web.mit.edu/persci/people/adelson/checkershadow_description.html).
|
||||
|
||||
Proof: You can convince yourself by using an image manipulation program, cutting out a portion
|
||||
of the two squares, and looking at them without any background. You can also measure the RGB
|
||||
values of the two squares with the picker tool.
|
||||
|
||||
In this image I've cropped a little piece of the A and B squares and I've put them side-by-side.
|
||||
It should be quite evident they have the same luminance.
|
||||

|
||||
|
||||
It's worth to know that this illusion works because the checkerboard image, as you may see it
|
||||
on your laptop, casts on your retina with dimensions that cause the retina local adaptation to take
|
||||
into account both the two squares at the same time.
|
||||
|
||||
The foveal vision area is something like one inch at one meter (and because your eye moves
|
||||
continuously, with the so called "saccades", your brain is able to reconstruct the entire
|
||||
color scene in real time). This means that one single letter, either A or B, can hit
|
||||
your fovea at any time.
|
||||
|
||||
The point is that, even if you can't see both letters at the same time in a single eye fixation,
|
||||
when looking at one letter your fovea also takes into account light information from what is around it.
|
||||
This means that the fovea actually perceives also the neighboring cells.
|
||||
|
||||
The net effect is that when looking at one area, your eye locally adapts to luminance, filters noise,
|
||||
enforces contours, etc. considering what *surrounds* this area, and this makes the illusion work. We
|
||||
say that *the retina works in a "center surround" manner*.
|
||||
|
||||
So, the "A" cell being surrounded by lighter cells can be perceived darker. As a comparison, cell "B" 's
|
||||
neighborhood is darker and the cell "B" is then perceived lighter.
|
||||
|
||||
Finally, since shadow edges are soft, retina eliminates this information. Then shadows do not disrupt the overall chessboard observation making possible to "confidently being fooled" by the perceived cells luminance.
|
||||
|
||||
Reproducing the illusion
|
||||
------------------------
|
||||
The bioinspired module does mimic (also) the parvocellular retina process, that is our foveal
|
||||
vision, and it does reproduce our eyes' local adaptation.
|
||||
|
||||
This means we can expect the parvo channel output to really contain luminance values
|
||||
similar to those we perceive with our eyes. Specifically, in this case we expect the "B" square
|
||||
RGB values to be actually lighter than the "A" ones.
|
||||
|
||||
To correctly mimic what our eye does we need opencv to do the local adaptation on the right
|
||||
image portion. This means we have to ensure that the opencv's notion of "local" does match with our
|
||||
image's dimensions, otherwise the local adaptation wouldn't work as expected.
|
||||
|
||||
For this reason we may have to adjust the **hcellsSpatialConstant** parameter (that technically
|
||||
specifies the low spatial cut frequency, or slow luminance changes sensitivity) depending by
|
||||
the image resolution.
|
||||
|
||||
For the image in this tutorial, the default retina parameters should be fine.
|
||||
|
||||
In order to feed the image to the bioinspired module, you can use either your own code or
|
||||
the *example_bioinspired_retinaDemo* example that comes with the bioinspired module.
|
||||
|
||||
Running
|
||||
@code{.sh}
|
||||
example_bioinspired_retinaDemo -image checkershadow_illusion4med.jpg
|
||||
@endcode
|
||||
|
||||
will cause our image to be processed in both parvocellular and magnocellular channels (we are interested
|
||||
just in the first one).
|
||||
|
||||
If you choose to use your own code, please note that the parvocellular (and magnocellular)
|
||||
channel does require some iterations (frames to be processed) before actually getting steady.
|
||||
|
||||
Actually parvo (and magno) channel do cares about temporal information. That is, when you start
|
||||
feeding frames, it is similar to you with closed eyes; then you open them and you see the chessboard.
|
||||
|
||||
This is a static image but your retina just starts moving to a new context (eyes opening) and
|
||||
has to adapt.
|
||||
|
||||
While in this transient state the luminance information do matters, and you see more or less
|
||||
the absolute luminance values. Absolute luminance is exactly what you need **not** to look at in
|
||||
order to reproduce the illusion..
|
||||
|
||||
As soon as steady state is reached, you receive more contextual luminance information. Your eyes work
|
||||
in a center-surround manner and take into account the neighborhood luminance to evaluate the
|
||||
region of interest luminance level. And that's when our illusion comes out !
|
||||
|
||||
This is something that you don't need to worry about when you process videos, because you are
|
||||
naturally feeding the virtual retina with several frames, but you have to take care of it in
|
||||
order to process a single frame.
|
||||
|
||||
What you will actually need to do when processing a single frame, and you only need steady state response,
|
||||
is to repeatedly feed the retina with the same frame (this is what the example code does), as you
|
||||
would do with a still video. Alternatively you can set retina temporal parameters to 0 to get steady state immediately
|
||||
(**photoreceptorsTemporalConstant** and **hcellsTemporalConstant** parameters of the xml file); however
|
||||
in this case you should be aware that you are now making experiments with something that is
|
||||
deliberately less accurate in reproducing the behaviour of a real retina!
|
||||
|
||||
Here there is a small fragment of python code we used to process the image. It does 20
|
||||
iterations. This is an arbitrary number that we found experimentally to be (more than)
|
||||
enough
|
||||
|
||||
@code{.py}
|
||||
import cv2 as cv
|
||||
|
||||
inputImage = cv.imread('checkershadow_illusion4med.jpg', 1)
|
||||
retina = cv.bioinspired.createRetina((inputImage.shape[1], inputImage.shape[0]))
|
||||
|
||||
# the retina object is created with default parameters. If you want to read
|
||||
# the parameters from an external XML file, uncomment the next line
|
||||
#retina.setup('MyRetinaParameters.xml')
|
||||
|
||||
# feed the retina with several frames, in order to reach 'steady' state
|
||||
for i in range(20):
|
||||
retina.run(inputImage)
|
||||
|
||||
# get our processed image :)
|
||||
retinaOut_parvo = retina.getParvo()
|
||||
|
||||
# show both the original image and the processed one
|
||||
cv.imshow('image', inputImage)
|
||||
cv.imshow('retina parvo out', retinaOut_parvo)
|
||||
|
||||
# wait for a key to be pressed and exit
|
||||
cv.waitKey(0)
|
||||
cv.destroyAllWindows()
|
||||
|
||||
# write the output image on a file
|
||||
cv.imwrite('checkershadow_parvo.png', retinaOut_parvo)
|
||||
@endcode
|
||||
|
||||
Whatever method you used to process the image, you should end up
|
||||
with something like this:
|
||||
|
||||

|
||||
|
||||
Analyzing the results
|
||||
----------------------
|
||||
|
||||
We expected that the "B" pixels in the parvo channel output are lighter than "A" ones.
|
||||
|
||||
.. And in fact that is!
|
||||
|
||||
Looking at the resulting image might not tell us so much at a first glance: the "B" square looks
|
||||
lighter than "A" to our eyes, as it did in the input image. The difference is that, contrarily to
|
||||
the input image, now the RGB values of the pixels are actually lighter; note that when looking at
|
||||
the output image, we are actually applying the parvocellular process
|
||||
two times: first in the bioinspired module, then in our eyes.
|
||||
We can convince ourselves that the illusion appeared
|
||||
in the computed image by measuring the squares' luminance with the image manipulation program
|
||||
and the picker tool, or by cropping pieces of the squares and putting them side-by-side.
|
||||
|
||||
In the following image I cropped a portion of square "A" and a portion of square "B", and I placed
|
||||
them side-by-side, as I did for the original Adelson image.
|
||||
|
||||

|
||||
|
||||
It should be quite evident that the "B" square is really lighter than the "A" square! Congratulations: you have
|
||||
just reproduced the Adelson illusion with the Bioinspired module!
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
I want to thank:
|
||||
|
||||
**Alexandre Benoit** - for being so kind of explaining me how this whole thing works, for giving me the
|
||||
opportunity of writing this tutorial, and for reviewing it.
|
||||
|
||||
**Edward Adelson** - for allowing me to freely use his checkerboard image.
|
||||
|
||||
**Antonio Cuni** - for reviewing this tutorial and for writing the Python code.
|
||||
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 68 KiB |
@@ -0,0 +1,479 @@
|
||||
Retina and real-world vision {#tutorial_bioinspired_retina_model}
|
||||
=============================================================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
I present here a model of human retina that shows some interesting properties for image
|
||||
preprocessing and enhancement. In this tutorial you will learn how to:
|
||||
|
||||
- discover the main two channels outing from your retina
|
||||
- see the basics to use the retina model
|
||||
- discover some parameters tweaks
|
||||
|
||||
General overview
|
||||
----------------
|
||||
|
||||
The proposed model originates from Jeanny Herault's research @cite Herault2010 at
|
||||
[Gipsa](http://www.gipsa-lab.inpg.fr). It is involved in image processing applications with
|
||||
[Listic](http://www.listic.univ-savoie.fr) (code maintainer and user) lab. This is not a complete
|
||||
model but it already present interesting properties that can be involved for enhanced image
|
||||
processing experience. The model allows the following human retina properties to be used :
|
||||
|
||||
- spectral whitening that has 3 important effects: high spatio-temporal frequency signals
|
||||
canceling (noise), mid-frequencies details enhancement and low frequencies luminance energy
|
||||
reduction. This *all in one* property directly allows visual signals cleaning of classical
|
||||
undesired distortions introduced by image sensors and input luminance range.
|
||||
- local logarithmic luminance compression allows details to be enhanced even in low light
|
||||
conditions.
|
||||
- decorrelation of the details information (Parvocellular output channel) and transient
|
||||
information (events, motion made available at the Magnocellular output channel).
|
||||
|
||||
The first two points are illustrated below :
|
||||
|
||||
In the figure below, the OpenEXR image sample *CrissyField.exr*, a High Dynamic Range image is
|
||||
shown. In order to make it visible on this web-page, the original input image is linearly rescaled
|
||||
to the classical image luminance range [0-255] and is converted to 8bit/channel format. Such strong
|
||||
conversion hides many details because of too strong local contrasts. Furthermore, noise energy is
|
||||
also strong and pollutes visual information.
|
||||
|
||||

|
||||
|
||||
In the following image, applying the ideas proposed in @cite Benoit2010, as your retina does, local
|
||||
luminance adaptation, spatial noise removal and spectral whitening work together and transmit
|
||||
accurate information on lower range 8bit data channels. On this picture, noise in significantly
|
||||
removed, local details hidden by strong luminance contrasts are enhanced. Output image keeps its
|
||||
naturalness and visual content is enhanced. Color processing is based on the color
|
||||
multiplexing/demultiplexing method proposed in @cite Chaix2007 .
|
||||
|
||||

|
||||
|
||||
*Note :* image sample can be downloaded from the [OpenEXR website](http://www.openexr.com).
|
||||
Regarding this demonstration, before retina processing, input image has been linearly rescaled
|
||||
within 0-255 keeping its channels float format. 5% of its histogram ends has been cut (mostly
|
||||
removes wrong HDR pixels). Check out the sample
|
||||
*opencv/samples/cpp/OpenEXRimages_HighDynamicRange_Retina_toneMapping.cpp* for similar
|
||||
processing. The following demonstration will only consider classical 8bit/channel images.
|
||||
|
||||
The retina model output channels
|
||||
--------------------------------
|
||||
|
||||
The retina model presents two outputs that benefit from the above cited behaviors.
|
||||
|
||||
- The first one is called the Parvocellular channel. It is mainly active in the foveal retina area
|
||||
(high resolution central vision with color sensitive photo-receptors), its aim is to provide
|
||||
accurate color vision for visual details remaining static on the retina. On the other hand
|
||||
objects moving on the retina projection are blurred.
|
||||
- The second well known channel is the Magnocellular channel. It is mainly active in the retina
|
||||
peripheral vision and send signals related to change events (motion, transient events, etc.).
|
||||
These outing signals also help visual system to focus/center retina on 'transient'/moving areas
|
||||
for more detailed analysis thus improving visual scene context and object classification.
|
||||
|
||||
**NOTE :** regarding the proposed model, contrary to the real retina, we apply these two channels on
|
||||
the entire input images using the same resolution. This allows enhanced visual details and motion
|
||||
information to be extracted on all the considered images... but remember, that these two channels
|
||||
are complementary. For example, if Magnocellular channel gives strong energy in an area, then, the
|
||||
Parvocellular channel is certainly blurred there since there is a transient event.
|
||||
|
||||
As an illustration, we apply in the following the retina model on a webcam video stream of a dark
|
||||
visual scene. In this visual scene, captured in an amphitheater of the university, some students are
|
||||
moving while talking to the teacher.
|
||||
|
||||
In this video sequence, because of the dark ambiance, signal to noise ratio is low and color
|
||||
artifacts are present on visual features edges because of the low quality image capture tool-chain.
|
||||
|
||||

|
||||
|
||||
Below is shown the retina foveal vision applied on the entire image. In the used retina
|
||||
configuration, global luminance is preserved and local contrasts are enhanced. Also, signal to noise
|
||||
ratio is improved : since high frequency spatio-temporal noise is reduced, enhanced details are not
|
||||
corrupted by any enhanced noise.
|
||||
|
||||

|
||||
|
||||
Below is the output of the Magnocellular output of the retina model. Its signals are strong where
|
||||
transient events occur. Here, a student is moving at the bottom of the image thus generating high
|
||||
energy. The remaining of the image is static however, it is corrupted by a strong noise. Here, the
|
||||
retina filters out most of the noise thus generating low false motion area 'alarms'. This channel
|
||||
can be used as a transient/moving areas detector : it would provide relevant information for a low
|
||||
cost segmentation tool that would highlight areas in which an event is occurring.
|
||||
|
||||

|
||||
|
||||
Retina use case
|
||||
---------------
|
||||
|
||||
This model can be used basically for spatio-temporal video effects but also in the aim of :
|
||||
|
||||
- performing texture analysis with enhanced signal to noise ratio and enhanced details robust
|
||||
against input images luminance ranges (check out the Parvocellular retina channel output)
|
||||
- performing motion analysis also taking benefit of the previously cited properties.
|
||||
|
||||
Literature
|
||||
----------
|
||||
|
||||
For more information, refer to the following papers : @cite Benoit2010
|
||||
|
||||
- Please have a look at the reference work of Jeanny Herault that you can read in his book @cite Herault2010
|
||||
|
||||
This retina filter code includes the research contributions of phd/research colleagues from which
|
||||
code has been redrawn by the author :
|
||||
|
||||
- take a look at the *retinacolor.hpp* module to discover Brice Chaix de Lavarene phD color
|
||||
mosaicing/demosaicing and his reference paper @cite Chaix2007
|
||||
|
||||
- take a look at *imagelogpolprojection.hpp* to discover retina spatial log sampling which
|
||||
originates from Barthelemy Durette phd with Jeanny Herault. A Retina / V1 cortex projection is
|
||||
also proposed and originates from Jeanny's discussions. More informations in the above cited
|
||||
Jeanny Heraults's book.
|
||||
|
||||
Code tutorial
|
||||
-------------
|
||||
|
||||
Please refer to the original tutorial source code in file
|
||||
*opencv_folder/samples/cpp/tutorial_code/bioinspired/retina_tutorial.cpp*.
|
||||
|
||||
@note do not forget that the retina model is included in the following namespace: cv::bioinspired
|
||||
|
||||
To compile it, assuming OpenCV is correctly installed, use the following command. It requires the
|
||||
opencv_core *(cv::Mat and friends objects management)*, opencv_highgui *(display and image/video
|
||||
read)* and opencv_bioinspired *(Retina description)* libraries to compile.
|
||||
|
||||
@code{.sh}
|
||||
// compile
|
||||
gcc retina_tutorial.cpp -o Retina_tuto -lopencv_core -lopencv_highgui -lopencv_bioinspired -lopencv_videoio -lopencv_imgcodecs
|
||||
|
||||
// Run commands : add 'log' as a last parameter to apply a spatial log sampling (simulates retina sampling)
|
||||
// run on webcam
|
||||
./Retina_tuto -video
|
||||
// run on video file
|
||||
./Retina_tuto -video myVideo.avi
|
||||
// run on an image
|
||||
./Retina_tuto -image myPicture.jpg
|
||||
// run on an image with log sampling
|
||||
./Retina_tuto -image myPicture.jpg log
|
||||
@endcode
|
||||
|
||||
Here is a code explanation :
|
||||
|
||||
Retina definition is present in the bioinspired package and a simple include allows to use it. You
|
||||
can rather use the specific header : *opencv2/bioinspired.hpp* if you prefer but then include the
|
||||
other required openv modules : *opencv2/core.hpp* and *opencv2/highgui.hpp*
|
||||
@code{.cpp}
|
||||
#include "opencv2/opencv.hpp"
|
||||
@endcode
|
||||
Provide user some hints to run the program with a help function
|
||||
@code{.cpp}
|
||||
// the help procedure
|
||||
static void help(std::string errorMessage)
|
||||
{
|
||||
std::cout<<"Program init error : "<<errorMessage<<std::endl;
|
||||
std::cout<<"\nProgram call procedure : retinaDemo [processing mode] [Optional : media target] [Optional LAST parameter: \"log\" to activate retina log sampling]"<<std::endl;
|
||||
std::cout<<"\t[processing mode] :"<<std::endl;
|
||||
std::cout<<"\t -image : for still image processing"<<std::endl;
|
||||
std::cout<<"\t -video : for video stream processing"<<std::endl;
|
||||
std::cout<<"\t[Optional : media target] :"<<std::endl;
|
||||
std::cout<<"\t if processing an image or video file, then, specify the path and filename of the target to process"<<std::endl;
|
||||
std::cout<<"\t leave empty if processing video stream coming from a connected video device"<<std::endl;
|
||||
std::cout<<"\t[Optional : activate retina log sampling] : an optional last parameter can be specified for retina spatial log sampling"<<std::endl;
|
||||
std::cout<<"\t set \"log\" without quotes to activate this sampling, output frame size will be divided by 4"<<std::endl;
|
||||
std::cout<<"\nExamples:"<<std::endl;
|
||||
std::cout<<"\t-Image processing : ./retinaDemo -image lena.jpg"<<std::endl;
|
||||
std::cout<<"\t-Image processing with log sampling : ./retinaDemo -image lena.jpg log"<<std::endl;
|
||||
std::cout<<"\t-Video processing : ./retinaDemo -video myMovie.mp4"<<std::endl;
|
||||
std::cout<<"\t-Live video processing : ./retinaDemo -video"<<std::endl;
|
||||
std::cout<<"\nPlease start again with new parameters"<<std::endl;
|
||||
std::cout<<"****************************************************"<<std::endl;
|
||||
std::cout<<" NOTE : this program generates the default retina parameters file 'RetinaDefaultParameters.xml'"<<std::endl;
|
||||
std::cout<<" => you can use this to fine tune parameters and load them if you save to file 'RetinaSpecificParameters.xml'"<<std::endl;
|
||||
}
|
||||
@endcode
|
||||
Then, start the main program and first declare a *cv::Mat* matrix in which input images will be
|
||||
loaded. Also allocate a *cv::VideoCapture* object ready to load video streams (if necessary)
|
||||
@code{.cpp}
|
||||
int main(int argc, char* argv[]) {
|
||||
// declare the retina input buffer... that will be fed differently in regard of the input media
|
||||
cv::Mat inputFrame;
|
||||
cv::VideoCapture videoCapture; // in case a video media is used, its manager is declared here
|
||||
@endcode
|
||||
In the main program, before processing, first check input command parameters. Here it loads a first
|
||||
input image coming from a single loaded image (if user chose command *-image*) or from a video
|
||||
stream (if user chose command *-video*). Also, if the user added *log* command at the end of its
|
||||
program call, the spatial logarithmic image sampling performed by the retina is taken into account
|
||||
by the Boolean flag *useLogSampling*.
|
||||
@code{.cpp}
|
||||
// welcome message
|
||||
std::cout<<"****************************************************"<<std::endl;
|
||||
std::cout<<"* Retina demonstration : demonstrates the use of is a wrapper class of the Gipsa/Listic Labs retina model."<<std::endl;
|
||||
std::cout<<"* This demo will try to load the file 'RetinaSpecificParameters.xml' (if exists).\nTo create it, copy the autogenerated template 'RetinaDefaultParameters.xml'.\nThen tweak it with your own retina parameters."<<std::endl;
|
||||
// basic input arguments checking
|
||||
if (argc<2)
|
||||
{
|
||||
help("bad number of parameter");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool useLogSampling = !strcmp(argv[argc-1], "log"); // check if user wants retina log sampling processing
|
||||
|
||||
std::string inputMediaType=argv[1];
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// checking input media type (still image, video file, live video acquisition)
|
||||
if (!strcmp(inputMediaType.c_str(), "-image") && argc >= 3)
|
||||
{
|
||||
std::cout<<"RetinaDemo: processing image "<<argv[2]<<std::endl;
|
||||
// image processing case
|
||||
inputFrame = cv::imread(std::string(argv[2]), 1); // load image in RGB mode
|
||||
}else
|
||||
if (!strcmp(inputMediaType.c_str(), "-video"))
|
||||
{
|
||||
if (argc == 2 || (argc == 3 && useLogSampling)) // attempt to grab images from a video capture device
|
||||
{
|
||||
videoCapture.open(0);
|
||||
}else// attempt to grab images from a video filestream
|
||||
{
|
||||
std::cout<<"RetinaDemo: processing video stream "<<argv[2]<<std::endl;
|
||||
videoCapture.open(argv[2]);
|
||||
}
|
||||
|
||||
// grab a first frame to check if everything is ok
|
||||
videoCapture>>inputFrame;
|
||||
}else
|
||||
{
|
||||
// bad command parameter
|
||||
help("bad command parameter");
|
||||
return -1;
|
||||
}
|
||||
@endcode
|
||||
Once all input parameters are processed, a first image should have been loaded, if not, display
|
||||
error and stop program :
|
||||
@code{.cpp}
|
||||
if (inputFrame.empty())
|
||||
{
|
||||
help("Input media could not be loaded, aborting");
|
||||
return -1;
|
||||
}
|
||||
@endcode
|
||||
Now, everything is ready to run the retina model. I propose here to allocate a retina instance and
|
||||
to manage the eventual log sampling option. The Retina constructor expects at least a cv::Size
|
||||
object that shows the input data size that will have to be managed. One can activate other options
|
||||
such as color and its related color multiplexing strategy (here Bayer multiplexing is chosen using
|
||||
*enum cv::bioinspired::RETINA_COLOR_BAYER*). If using log sampling, the image reduction factor
|
||||
(smaller output images) and log sampling strength can be adjusted.
|
||||
@code{.cpp}
|
||||
// pointer to a retina object
|
||||
cv::Ptr<cv::bioinspired::Retina> myRetina;
|
||||
|
||||
// if the last parameter is 'log', then activate log sampling (favour foveal vision and subsamples peripheral vision)
|
||||
if (useLogSampling)
|
||||
{
|
||||
myRetina = cv::bioinspired::createRetina(inputFrame.size(), true, cv::bioinspired::RETINA_COLOR_BAYER, true, 2.0, 10.0);
|
||||
}
|
||||
else// -> else allocate "classical" retina :
|
||||
myRetina = cv::bioinspired::createRetina(inputFrame.size());
|
||||
@endcode
|
||||
Once done, the proposed code writes a default xml file that contains the default parameters of the
|
||||
retina. This is useful to make your own config using this template. Here generated template xml file
|
||||
is called *RetinaDefaultParameters.xml*.
|
||||
@code{.cpp}
|
||||
// save default retina parameters file in order to let you see this and maybe modify it and reload using method "setup"
|
||||
myRetina->write("RetinaDefaultParameters.xml");
|
||||
@endcode
|
||||
In the following line, the retina attempts to load another xml file called
|
||||
*RetinaSpecificParameters.xml*. If you created it and introduced your own setup, it will be loaded,
|
||||
in the other case, default retina parameters are used.
|
||||
@code{.cpp}
|
||||
// load parameters if file exists
|
||||
myRetina->setup("RetinaSpecificParameters.xml");
|
||||
@endcode
|
||||
It is not required here but just to show it is possible, you can reset the retina buffers to zero to
|
||||
force it to forget past events.
|
||||
@code{.cpp}
|
||||
// reset all retina buffers (imagine you close your eyes for a long time)
|
||||
myRetina->clearBuffers();
|
||||
@endcode
|
||||
Now, it is time to run the retina ! First create some output buffers ready to receive the two retina
|
||||
channels outputs
|
||||
@code{.cpp}
|
||||
// declare retina output buffers
|
||||
cv::Mat retinaOutput_parvo;
|
||||
cv::Mat retinaOutput_magno;
|
||||
@endcode
|
||||
Then, run retina in a loop, load new frames from video sequence if necessary and get retina outputs
|
||||
back to dedicated buffers.
|
||||
@code{.cpp}
|
||||
// processing loop with no stop condition
|
||||
while(true)
|
||||
{
|
||||
// if using video stream, then, grabbing a new frame, else, input remains the same
|
||||
if (videoCapture.isOpened())
|
||||
videoCapture>>inputFrame;
|
||||
|
||||
// run retina filter on the loaded input frame
|
||||
myRetina->run(inputFrame);
|
||||
// Retrieve and display retina output
|
||||
myRetina->getParvo(retinaOutput_parvo);
|
||||
myRetina->getMagno(retinaOutput_magno);
|
||||
cv::imshow("retina input", inputFrame);
|
||||
cv::imshow("Retina Parvo", retinaOutput_parvo);
|
||||
cv::imshow("Retina Magno", retinaOutput_magno);
|
||||
cv::waitKey(10);
|
||||
}
|
||||
@endcode
|
||||
That's done ! But if you want to secure the system, take care and manage Exceptions. The retina can
|
||||
throw some when it sees irrelevant data (no input frame, wrong setup, etc.). Then, i recommend to
|
||||
surround all the retina code by a try/catch system like this :
|
||||
@code{.cpp}
|
||||
try{
|
||||
// pointer to a retina object
|
||||
cv::Ptr<cv::Retina> myRetina;
|
||||
[---]
|
||||
// processing loop with no stop condition
|
||||
while(true)
|
||||
{
|
||||
[---]
|
||||
}
|
||||
|
||||
}catch(cv::Exception e)
|
||||
{
|
||||
std::cerr<<"Error using Retina : "<<e.what()<<std::endl;
|
||||
}
|
||||
@endcode
|
||||
|
||||
Retina parameters, what to do ?
|
||||
-------------------------------
|
||||
|
||||
First, it is recommended to read the reference paper @cite Benoit2010
|
||||
|
||||
Once done open the configuration file *RetinaDefaultParameters.xml* generated by the demo and let's
|
||||
have a look at it.
|
||||
@code{.cpp}
|
||||
<?xml version="1.0"?>
|
||||
<opencv_storage>
|
||||
<OPLandIPLparvo>
|
||||
<colorMode>1</colorMode>
|
||||
<normaliseOutput>1</normaliseOutput>
|
||||
<photoreceptorsLocalAdaptationSensitivity>7.5e-01</photoreceptorsLocalAdaptationSensitivity>
|
||||
<photoreceptorsTemporalConstant>9.0e-01</photoreceptorsTemporalConstant>
|
||||
<photoreceptorsSpatialConstant>5.7e-01</photoreceptorsSpatialConstant>
|
||||
<horizontalCellsGain>0.01</horizontalCellsGain>
|
||||
<hcellsTemporalConstant>0.5</hcellsTemporalConstant>
|
||||
<hcellsSpatialConstant>7.</hcellsSpatialConstant>
|
||||
<ganglionCellsSensitivity>7.5e-01</ganglionCellsSensitivity></OPLandIPLparvo>
|
||||
<IPLmagno>
|
||||
<normaliseOutput>1</normaliseOutput>
|
||||
<parasolCells_beta>0.</parasolCells_beta>
|
||||
<parasolCells_tau>0.</parasolCells_tau>
|
||||
<parasolCells_k>7.</parasolCells_k>
|
||||
<amacrinCellsTemporalCutFrequency>2.0e+00</amacrinCellsTemporalCutFrequency>
|
||||
<V0CompressionParameter>9.5e-01</V0CompressionParameter>
|
||||
<localAdaptintegration_tau>0.</localAdaptintegration_tau>
|
||||
<localAdaptintegration_k>7.</localAdaptintegration_k></IPLmagno>
|
||||
</opencv_storage>
|
||||
@endcode
|
||||
Here are some hints but actually, the best parameter setup depends more on what you want to do with
|
||||
the retina rather than the images input that you give to retina. Apart from the more specific case
|
||||
of High Dynamic Range images (HDR) that require more specific setup for specific luminance
|
||||
compression objective, the retina behaviors should be rather stable from content to content. Note
|
||||
that OpenCV is able to manage such HDR format thanks to the OpenEXR images compatibility.
|
||||
|
||||
Then, if the application target requires details enhancement prior to specific image processing, you
|
||||
need to know if mean luminance information is required or not. If not, the the retina can cancel or
|
||||
significantly reduce its energy thus giving more visibility to higher spatial frequency details.
|
||||
|
||||
|
||||
## Basic parameters
|
||||
|
||||
The simplest parameters are as follows :
|
||||
|
||||
- **colorMode** : let the retina process color information (if 1) or gray scale images (if 0). In
|
||||
that last case, only the first channels of the input will be processed.
|
||||
- **normaliseOutput** : each channel has such parameter: if the value is set to 1, then the considered
|
||||
channel's output is rescaled between 0 and 255. Be aware at this case of the Magnocellular output
|
||||
level (motion/transient channel detection). Residual noise will also be rescaled !
|
||||
|
||||
**Note :** using color requires color channels multiplexing/demultipexing which also demands more
|
||||
processing. You can expect much faster processing using gray levels : it would require around 30
|
||||
product per pixel for all of the retina processes and it has recently been parallelized for multicore
|
||||
architectures.
|
||||
|
||||
## Photo-receptors parameters
|
||||
|
||||
The following parameters act on the entry point of the retina - photo-receptors - and has impact on all
|
||||
of the following processes. These sensors are low pass spatio-temporal filters that smooth temporal and
|
||||
spatial data and also adjust their sensitivity to local luminance,thus, leads to improving details extraction
|
||||
and high frequency noise canceling.
|
||||
|
||||
- **photoreceptorsLocalAdaptationSensitivity** between 0 and 1. Values close to 1 allow high
|
||||
luminance log compression's effect at the photo-receptors level. Values closer to 0 provide a more
|
||||
linear sensitivity. Increased alone, it can burn the *Parvo (details channel)* output image. If
|
||||
adjusted in collaboration with **ganglionCellsSensitivity**,images can be very contrasted
|
||||
whatever the local luminance there is... at the cost of a naturalness decrease.
|
||||
- **photoreceptorsTemporalConstant** this setups the temporal constant of the low pass filter
|
||||
effect at the entry of the retina. High value leads to strong temporal smoothing effect : moving
|
||||
objects are blurred and can disappear while static object are favored. But when starting the
|
||||
retina processing, stable state is reached later.
|
||||
- **photoreceptorsSpatialConstant** specifies the spatial constant related to photo-receptors' low
|
||||
pass filter's effect. Those parameters specify the minimum value of the spatial signal period allowed
|
||||
in what follows. Typically, this filter should cut high frequency noise. On the other hand, a 0 value
|
||||
cuts none of the noise while higher values start to cut high spatial frequencies, and progressively
|
||||
lower frequencies... Be aware to not go to high levels if you want to see some details of the input images !
|
||||
A good compromise for color images is a 0.53 value since such choice won't affect too much the color spectrum.
|
||||
Higher values would lead to gray and blurred output images.
|
||||
|
||||
## Horizontal cells parameters
|
||||
|
||||
This parameter set tunes the neural network connected to the photo-receptors, the horizontal cells.
|
||||
It modulates photo-receptors sensitivity and completes the processing for final spectral whitening
|
||||
(part of the spatial band pass effect thus favoring visual details enhancement).
|
||||
|
||||
- **horizontalCellsGain** here is a critical parameter ! If you are not interested with the mean
|
||||
luminance and want just to focus on details enhancement, then, set this parameterto zero. However, if
|
||||
you want to keep some environment luminance's data, let some low spatial frequencies pass into the system and set a
|
||||
higher value (\<1).
|
||||
- **hcellsTemporalConstant** similar to photo-receptors, this parameter acts on the temporal constant of a
|
||||
low pass temporal filter that smoothes input data. Here, a high value generates a high retina
|
||||
after effect while a lower value makes the retina more reactive. This value should be lower than
|
||||
**photoreceptorsTemporalConstant** to limit strong retina after effects.
|
||||
- **hcellsSpatialConstant** is the spatial constant of these cells filter's low pass one.
|
||||
It specifies the lowest spatial frequency allowed in what follows. Visually, a high value leads
|
||||
to very low spatial frequencies processing and leads to salient halo effects. Lower values
|
||||
reduce this effect but has the limit of not go lower than the value of
|
||||
**photoreceptorsSpatialConstant**. Those 2 parameters actually specify the spatial band-pass of
|
||||
the retina.
|
||||
|
||||
**NOTE** Once the processing managed by the previous parameters is done, input data is cleaned from noise
|
||||
and luminance is already partly enhanced. The following parameters act on the last processing stages
|
||||
of the two outing retina signals.
|
||||
|
||||
## Parvo (details channel) dedicated parameter
|
||||
|
||||
- **ganglionCellsSensitivity** specifies the strength of the final local adaptation occurring at
|
||||
the output of this details' dedicated channel. Parameter values remain between 0 and 1. Low value
|
||||
tend to give a linear response while higher values enforce the remaining low contrasted areas.
|
||||
|
||||
**Note :** this parameter can correct eventual burned images by favoring low energetic details of
|
||||
the visual scene, even in bright areas.
|
||||
|
||||
## IPL Magno (motion/transient channel) parameters
|
||||
|
||||
Once image's information are cleaned, this channel acts as a high pass temporal filter that
|
||||
selects only the signals related to transient signals (events, motion, etc.). A low pass spatial filter
|
||||
smoothes extracted transient data while a final logarithmic compression enhances low transient events
|
||||
thus enhancing event sensitivity.
|
||||
|
||||
- **parasolCells_beta** generally set to zero, can be considered as an amplifier gain at the
|
||||
entry point of this processing stage. Generally set to 0.
|
||||
- **parasolCells_tau** the temporal smoothing effect that can be added
|
||||
- **parasolCells_k** the spatial constant of the spatial filtering effect, set it at a high value
|
||||
to favor low spatial frequency signals that are lower subject for residual noise.
|
||||
- **amacrinCellsTemporalCutFrequency** specifies the temporal constant of the high pass filter.
|
||||
High values let slow transient events to be selected.
|
||||
- **V0CompressionParameter** specifies the strength of the log compression. Similar behaviors to
|
||||
previous description but here enforces sensitivity of transient events.
|
||||
- **localAdaptintegration_tau** generally set to 0, has no real use actually in here.
|
||||
- **localAdaptintegration_k** specifies the size of the area on which local adaptation is
|
||||
performed. Low values lead to short range local adaptation (higher sensitivity to noise), high
|
||||
values secure log compression.
|
||||
@@ -0,0 +1,14 @@
|
||||
Discovering the human retina and its use for image processing {#tutorial_table_of_content_retina}
|
||||
============================
|
||||
|
||||
- @subpage tutorial_bioinspired_retina_model
|
||||
|
||||
*Author:* Alexandre Benoit
|
||||
|
||||
Processing regular images
|
||||
|
||||
- @subpage tutorial_bioinspired_retina_illusion
|
||||
|
||||
*Author:* Andrea Merello
|
||||
|
||||
See how to reproduce human eye optical illusions
|
||||