87 #include <visp3/core/vpImageFilter.h>
88 #include <visp3/core/vpMath.h>
89 #include <visp3/imgproc/vpImgproc.h>
91 #define MAX_RETINEX_SCALES 8
93 std::vector<double> retinexScalesDistribution(
int scaleDiv,
int level,
int scale)
95 std::vector<double> scales(MAX_RETINEX_SCALES);
98 scales[0] = scale / 2.0;
100 else if (scaleDiv == 2) {
101 scales[0] = scale / 2.0;
105 double size_step = scale /
static_cast<double>(scaleDiv);
110 for (i = 0; i < scaleDiv; ++i) {
111 scales[
static_cast<size_t>(i)] = 2.0 + (i * size_step);
116 size_step = std::log(scale - 2.0) /
static_cast<double>(scaleDiv);
117 for (i = 0; i < scaleDiv; ++i) {
118 scales[
static_cast<size_t>(i)] = 2.0 + std::pow(10.0, (i * size_step) / std::log(10.0));
123 size_step = std::log(scale - 2.0) /
static_cast<double>(scaleDiv);
124 for (i = 0; i < scaleDiv; ++i) {
125 scales[
static_cast<size_t>(i)] = scale - std::pow(10.0, (i * size_step) / std::log(10.0));
139 void MSRCR(
vpImage<vpRGBa> &I,
int v_scale,
int scaleDiv,
int level,
double dynamic,
int v_kernelSize)
143 std::vector<double> retinexScales = retinexScalesDistribution(scaleDiv, level, v_scale);
148 double weight = 1.0 /
static_cast<double>(scaleDiv);
150 std::vector<vpImage<double> > doubleRGB(3);
151 std::vector<vpImage<double> > doubleResRGB(3);
152 unsigned int size = I.
getSize();
154 int kernelSize = v_kernelSize;
155 if (kernelSize == -1) {
157 kernelSize =
static_cast<int>(std::min<unsigned int>(I.
getWidth(), I.
getHeight()) / 2.0);
158 kernelSize = (kernelSize - (kernelSize % 2)) + 1;
161 for (
int channel = 0; channel < 3; ++channel) {
165 for (
unsigned int cpt = 0; cpt < size; ++cpt) {
169 doubleRGB[
static_cast<size_t>(channel)].bitmap[cpt] = I.
bitmap[cpt].R + 1.0;
173 doubleRGB[
static_cast<size_t>(channel)].bitmap[cpt] = I.
bitmap[cpt].G + 1.0;
177 doubleRGB[
static_cast<size_t>(channel)].bitmap[cpt] = I.
bitmap[cpt].B + 1.0;
185 for (
int sc = 0; sc < scaleDiv; ++sc) {
187 double sigma = retinexScales[
static_cast<size_t>(sc)];
190 for (
unsigned int cpt = 0; cpt < size; ++cpt) {
194 doubleResRGB[
static_cast<size_t>(channel)].bitmap[cpt] +=
195 weight * (std::log(doubleRGB[
static_cast<size_t>(channel)].bitmap[cpt]) - std::log(blurImage.
bitmap[cpt]));
200 std::vector<double> dest(size * 3);
201 const double gain = 1.0, alpha = 128.0, offset = 0.0;
203 for (
unsigned int cpt = 0; cpt < size; ++cpt) {
204 double logl = std::log(
static_cast<double>(I.
bitmap[cpt].R + I.
bitmap[cpt].G + I.
bitmap[cpt].B + 3.0));
206 dest[cpt * 3] = (gain * (std::log(alpha * doubleRGB[0].bitmap[cpt]) - logl) * doubleResRGB[0].bitmap[cpt]) + offset;
207 dest[(cpt * 3) + 1] =
208 (gain * (std::log(alpha * doubleRGB[1].bitmap[cpt]) - logl) * doubleResRGB[1].bitmap[cpt]) + offset;
209 dest[(cpt * 3) + 2] =
210 (gain * (std::log(alpha * doubleRGB[2].bitmap[cpt]) - logl) * doubleResRGB[2].bitmap[cpt]) + offset;
213 double sum = std::accumulate(dest.begin(), dest.end(), 0.0);
214 double mean = sum / dest.size();
216 std::vector<double> diff(dest.size());
218 #if VISP_CXX_STANDARD > VISP_CXX_STANDARD_98
219 std::transform(dest.begin(), dest.end(), diff.begin(), std::bind(std::minus<double>(), std::placeholders::_1, mean));
221 std::transform(dest.begin(), dest.end(), diff.begin(), std::bind2nd(std::minus<double>(), mean));
224 double sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0);
225 double stdev = std::sqrt(sq_sum / dest.size());
227 double mini = mean - (dynamic * stdev);
228 double maxi = mean + (dynamic * stdev);
229 double range = maxi - mini;
235 for (
unsigned int cpt = 0; cpt < size; ++cpt) {
236 I.
bitmap[cpt].R = vpMath::saturate<unsigned char>((255.0 * (dest[(cpt * 3) + 0] - mini)) / range);
237 I.
bitmap[cpt].G = vpMath::saturate<unsigned char>((255.0 * (dest[(cpt * 3) + 1] - mini)) / range);
238 I.
bitmap[cpt].B = vpMath::saturate<unsigned char>((255.0 * (dest[(cpt * 3) + 2] - mini)) / range);
247 if ((scale < 16) || (scale > 250)) {
248 std::cerr <<
"Scale must be between the interval [16 - 250]" << std::endl;
253 if ((scaleDiv < 1) || (scaleDiv > 8)) {
254 std::cerr <<
"Scale division must be between the interval [1 - 8]" << std::endl;
262 MSRCR(I, scale, scaleDiv, level, dynamic, kernelSize);
269 vp::retinex(I2, scale, scaleDiv, level, dynamic, kernelSize);
static void gaussianBlur(const vpImage< ImageType > &I, vpImage< FilterType > &GI, unsigned int size=7, FilterType sigma=0., bool normalize=true, const vpImage< bool > *p_mask=nullptr)
unsigned int getWidth() const
unsigned int getSize() const
Type * bitmap
points toward the bitmap
unsigned int getHeight() const
static bool nul(double x, double threshold=0.001)
VISP_EXPORT void retinex(vpImage< vpRGBa > &I, int scale=240, int scaleDiv=3, int level=RETINEX_UNIFORM, double dynamic=1.2, int kernelSize=-1)
@ RETINEX_HIGH
Enhances the bright regions of the image.
@ RETINEX_LOW
Enhances dark regions of the image.
@ RETINEX_UNIFORM
Tends to treat all image intensities similarly.