tonemap.cpp 16.3 KB
Newer Older
Fedor Morozov's avatar
Fedor Morozov committed
1 2 3 4 5 6 7 8 9 10 11 12
/*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
//
13
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
Fedor Morozov's avatar
Fedor Morozov committed
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
// 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*/

Alexander Shishkov's avatar
Alexander Shishkov committed
42
#include "precomp.hpp"
Fedor Morozov's avatar
Fedor Morozov committed
43 44
#include "opencv2/photo.hpp"
#include "opencv2/imgproc.hpp"
Fedor Morozov's avatar
Fedor Morozov committed
45
#include "hdr_common.hpp"
Fedor Morozov's avatar
Fedor Morozov committed
46 47 48

namespace cv
{
49

50 51 52 53 54 55
inline void log_(const Mat& src, Mat& dst)
{
    max(src, Scalar::all(1e-4), dst);
    log(dst, dst);
}

Fedor Morozov's avatar
Fedor Morozov committed
56
class TonemapImpl : public Tonemap
57
{
Fedor Morozov's avatar
Fedor Morozov committed
58
public:
59
    TonemapImpl(float _gamma) : name("Tonemap"), gamma(_gamma)
Fedor Morozov's avatar
Fedor Morozov committed
60 61 62
    {
    }

63
    void process(InputArray _src, OutputArray _dst)
Fedor Morozov's avatar
Fedor Morozov committed
64
    {
65 66
        CV_INSTRUMENT_REGION()

Fedor Morozov's avatar
Fedor Morozov committed
67 68 69 70
        Mat src = _src.getMat();
        CV_Assert(!src.empty());
        _dst.create(src.size(), CV_32FC3);
        Mat dst = _dst.getMat();
71

Fedor Morozov's avatar
Fedor Morozov committed
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
        double min, max;
        minMaxLoc(src, &min, &max);
        if(max - min > DBL_EPSILON) {
            dst = (src - min) / (max - min);
        } else {
            src.copyTo(dst);
        }

        pow(dst, 1.0f / gamma, dst);
    }

    float getGamma() const { return gamma; }
    void setGamma(float val) { gamma = val; }

    void write(FileStorage& fs) const
Fedor Morozov's avatar
Fedor Morozov committed
87
    {
88
        writeFormat(fs);
Fedor Morozov's avatar
Fedor Morozov committed
89 90
        fs << "name" << name
           << "gamma" << gamma;
Fedor Morozov's avatar
Fedor Morozov committed
91 92
    }

Fedor Morozov's avatar
Fedor Morozov committed
93 94 95 96 97 98
    void read(const FileNode& fn)
    {
        FileNode n = fn["name"];
        CV_Assert(n.isString() && String(n) == name);
        gamma = fn["gamma"];
    }
Fedor Morozov's avatar
Fedor Morozov committed
99

Fedor Morozov's avatar
Fedor Morozov committed
100
protected:
Fedor Morozov's avatar
Fedor Morozov committed
101 102
    String name;
    float gamma;
Fedor Morozov's avatar
Fedor Morozov committed
103
};
Fedor Morozov's avatar
Fedor Morozov committed
104

Fedor Morozov's avatar
Fedor Morozov committed
105
Ptr<Tonemap> createTonemap(float gamma)
Fedor Morozov's avatar
Fedor Morozov committed
106
{
107
    return makePtr<TonemapImpl>(gamma);
Fedor Morozov's avatar
Fedor Morozov committed
108 109
}

Fedor Morozov's avatar
Fedor Morozov committed
110
class TonemapDragoImpl : public TonemapDrago
Fedor Morozov's avatar
Fedor Morozov committed
111
{
Fedor Morozov's avatar
Fedor Morozov committed
112
public:
113 114 115 116 117
    TonemapDragoImpl(float _gamma, float _saturation, float _bias) :
        name("TonemapDrago"),
        gamma(_gamma),
        saturation(_saturation),
        bias(_bias)
Fedor Morozov's avatar
Fedor Morozov committed
118 119 120
    {
    }

121
    void process(InputArray _src, OutputArray _dst)
Fedor Morozov's avatar
Fedor Morozov committed
122
    {
123 124
        CV_INSTRUMENT_REGION()

Fedor Morozov's avatar
Fedor Morozov committed
125 126 127 128
        Mat src = _src.getMat();
        CV_Assert(!src.empty());
        _dst.create(src.size(), CV_32FC3);
        Mat img = _dst.getMat();
129

Fedor Morozov's avatar
Fedor Morozov committed
130
        Ptr<Tonemap> linear = createTonemap(1.0f);
Fedor Morozov's avatar
Fedor Morozov committed
131 132 133 134 135
        linear->process(src, img);

        Mat gray_img;
        cvtColor(img, gray_img, COLOR_RGB2GRAY);
        Mat log_img;
136
        log_(gray_img, log_img);
Fedor Morozov's avatar
Fedor Morozov committed
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
        float mean = expf(static_cast<float>(sum(log_img)[0]) / log_img.total());
        gray_img /= mean;
        log_img.release();

        double max;
        minMaxLoc(gray_img, NULL, &max);

        Mat map;
        log(gray_img + 1.0f, map);
        Mat div;
        pow(gray_img / static_cast<float>(max), logf(bias) / logf(0.5f), div);
        log(2.0f + 8.0f * div, div);
        map = map.mul(1.0f / div);
        div.release();

        mapLuminance(img, img, gray_img, map, saturation);
153

Fedor Morozov's avatar
Fedor Morozov committed
154 155 156 157 158 159 160 161 162 163 164 165 166 167
        linear->setGamma(gamma);
        linear->process(img, img);
    }

    float getGamma() const { return gamma; }
    void setGamma(float val) { gamma = val; }

    float getSaturation() const { return saturation; }
    void setSaturation(float val) { saturation = val; }

    float getBias() const { return bias; }
    void setBias(float val) { bias = val; }

    void write(FileStorage& fs) const
Fedor Morozov's avatar
Fedor Morozov committed
168
    {
169
        writeFormat(fs);
Fedor Morozov's avatar
Fedor Morozov committed
170 171
        fs << "name" << name
           << "gamma" << gamma
Fedor Morozov's avatar
Fedor Morozov committed
172 173
           << "bias" << bias
           << "saturation" << saturation;
Fedor Morozov's avatar
Fedor Morozov committed
174 175 176 177 178 179 180
    }

    void read(const FileNode& fn)
    {
        FileNode n = fn["name"];
        CV_Assert(n.isString() && String(n) == name);
        gamma = fn["gamma"];
Fedor Morozov's avatar
Fedor Morozov committed
181 182
        bias = fn["bias"];
        saturation = fn["saturation"];
183 184
    }

Fedor Morozov's avatar
Fedor Morozov committed
185
protected:
Fedor Morozov's avatar
Fedor Morozov committed
186 187
    String name;
    float gamma, saturation, bias;
Fedor Morozov's avatar
Fedor Morozov committed
188 189
};

Fedor Morozov's avatar
Fedor Morozov committed
190
Ptr<TonemapDrago> createTonemapDrago(float gamma, float saturation, float bias)
191
{
192
    return makePtr<TonemapDragoImpl>(gamma, saturation, bias);
Fedor Morozov's avatar
Fedor Morozov committed
193
}
194

Fedor Morozov's avatar
Fedor Morozov committed
195
class TonemapDurandImpl : public TonemapDurand
Fedor Morozov's avatar
Fedor Morozov committed
196
{
Fedor Morozov's avatar
Fedor Morozov committed
197
public:
198 199 200 201 202 203 204
    TonemapDurandImpl(float _gamma, float _contrast, float _saturation, float _sigma_color, float _sigma_space) :
        name("TonemapDurand"),
        gamma(_gamma),
        contrast(_contrast),
        saturation(_saturation),
        sigma_color(_sigma_color),
        sigma_space(_sigma_space)
Fedor Morozov's avatar
Fedor Morozov committed
205 206
    {
    }
Fedor Morozov's avatar
Fedor Morozov committed
207

208
    void process(InputArray _src, OutputArray _dst)
Fedor Morozov's avatar
Fedor Morozov committed
209
    {
210 211
        CV_INSTRUMENT_REGION()

Fedor Morozov's avatar
Fedor Morozov committed
212 213 214 215
        Mat src = _src.getMat();
        CV_Assert(!src.empty());
        _dst.create(src.size(), CV_32FC3);
        Mat img = _dst.getMat();
Fedor Morozov's avatar
Fedor Morozov committed
216
        Ptr<Tonemap> linear = createTonemap(1.0f);
Fedor Morozov's avatar
Fedor Morozov committed
217 218 219 220 221
        linear->process(src, img);

        Mat gray_img;
        cvtColor(img, gray_img, COLOR_RGB2GRAY);
        Mat log_img;
222
        log_(gray_img, log_img);
Fedor Morozov's avatar
Fedor Morozov committed
223 224
        Mat map_img;
        bilateralFilter(log_img, map_img, -1, sigma_color, sigma_space);
225

Fedor Morozov's avatar
Fedor Morozov committed
226 227 228 229 230 231 232 233 234
        double min, max;
        minMaxLoc(map_img, &min, &max);
        float scale = contrast / static_cast<float>(max - min);
        exp(map_img * (scale - 1.0f) + log_img, map_img);
        log_img.release();

        mapLuminance(img, img, gray_img, map_img, saturation);
        pow(img, 1.0f / gamma, img);
    }
Fedor Morozov's avatar
Fedor Morozov committed
235

Fedor Morozov's avatar
Fedor Morozov committed
236 237
    float getGamma() const { return gamma; }
    void setGamma(float val) { gamma = val; }
Fedor Morozov's avatar
Fedor Morozov committed
238

Fedor Morozov's avatar
Fedor Morozov committed
239 240
    float getSaturation() const { return saturation; }
    void setSaturation(float val) { saturation = val; }
Fedor Morozov's avatar
Fedor Morozov committed
241

Fedor Morozov's avatar
Fedor Morozov committed
242 243
    float getContrast() const { return contrast; }
    void setContrast(float val) { contrast = val; }
Fedor Morozov's avatar
Fedor Morozov committed
244

Fedor Morozov's avatar
Fedor Morozov committed
245 246
    float getSigmaColor() const { return sigma_color; }
    void setSigmaColor(float val) { sigma_color = val; }
Fedor Morozov's avatar
Fedor Morozov committed
247

Fedor Morozov's avatar
Fedor Morozov committed
248 249
    float getSigmaSpace() const { return sigma_space; }
    void setSigmaSpace(float val) { sigma_space = val; }
Fedor Morozov's avatar
Fedor Morozov committed
250

Fedor Morozov's avatar
Fedor Morozov committed
251
    void write(FileStorage& fs) const
Fedor Morozov's avatar
Fedor Morozov committed
252
    {
253
        writeFormat(fs);
Fedor Morozov's avatar
Fedor Morozov committed
254 255
        fs << "name" << name
           << "gamma" << gamma
256 257
           << "contrast" << contrast
           << "sigma_color" << sigma_color
Fedor Morozov's avatar
Fedor Morozov committed
258 259
           << "sigma_space" << sigma_space
           << "saturation" << saturation;
Fedor Morozov's avatar
Fedor Morozov committed
260 261
    }

Fedor Morozov's avatar
Fedor Morozov committed
262 263 264 265 266
    void read(const FileNode& fn)
    {
        FileNode n = fn["name"];
        CV_Assert(n.isString() && String(n) == name);
        gamma = fn["gamma"];
Fedor Morozov's avatar
Fedor Morozov committed
267 268 269 270
        contrast = fn["contrast"];
        sigma_color = fn["sigma_color"];
        sigma_space = fn["sigma_space"];
        saturation = fn["saturation"];
Fedor Morozov's avatar
Fedor Morozov committed
271
    }
Fedor Morozov's avatar
Fedor Morozov committed
272

Fedor Morozov's avatar
Fedor Morozov committed
273
protected:
Fedor Morozov's avatar
Fedor Morozov committed
274
    String name;
275
    float gamma, contrast, saturation, sigma_color, sigma_space;
Fedor Morozov's avatar
Fedor Morozov committed
276 277
};

278
Ptr<TonemapDurand> createTonemapDurand(float gamma, float contrast, float saturation, float sigma_color, float sigma_space)
Fedor Morozov's avatar
Fedor Morozov committed
279
{
280
    return makePtr<TonemapDurandImpl>(gamma, contrast, saturation, sigma_color, sigma_space);
281 282
}

283
class TonemapReinhardImpl : public TonemapReinhard
284
{
Fedor Morozov's avatar
Fedor Morozov committed
285
public:
286 287 288 289 290 291
    TonemapReinhardImpl(float _gamma, float _intensity, float _light_adapt, float _color_adapt) :
        name("TonemapReinhard"),
        gamma(_gamma),
        intensity(_intensity),
        light_adapt(_light_adapt),
        color_adapt(_color_adapt)
Fedor Morozov's avatar
Fedor Morozov committed
292 293 294 295 296
    {
    }

    void process(InputArray _src, OutputArray _dst)
    {
297 298
        CV_INSTRUMENT_REGION()

Fedor Morozov's avatar
Fedor Morozov committed
299 300 301 302
        Mat src = _src.getMat();
        CV_Assert(!src.empty());
        _dst.create(src.size(), CV_32FC3);
        Mat img = _dst.getMat();
Fedor Morozov's avatar
Fedor Morozov committed
303
        Ptr<Tonemap> linear = createTonemap(1.0f);
Fedor Morozov's avatar
Fedor Morozov committed
304
        linear->process(src, img);
305

Fedor Morozov's avatar
Fedor Morozov committed
306 307 308
        Mat gray_img;
        cvtColor(img, gray_img, COLOR_RGB2GRAY);
        Mat log_img;
309
        log_(gray_img, log_img);
Fedor Morozov's avatar
Fedor Morozov committed
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329

        float log_mean = static_cast<float>(sum(log_img)[0] / log_img.total());
        double log_min, log_max;
        minMaxLoc(log_img, &log_min, &log_max);
        log_img.release();

        double key = static_cast<float>((log_max - log_mean) / (log_max - log_min));
        float map_key = 0.3f + 0.7f * pow(static_cast<float>(key), 1.4f);
        intensity = exp(-intensity);
        Scalar chan_mean = mean(img);
        float gray_mean = static_cast<float>(mean(gray_img)[0]);

        std::vector<Mat> channels(3);
        split(img, channels);

        for(int i = 0; i < 3; i++) {
            float global = color_adapt * static_cast<float>(chan_mean[i]) + (1.0f - color_adapt) * gray_mean;
            Mat adapt = color_adapt * channels[i] + (1.0f - color_adapt) * gray_img;
            adapt = light_adapt * adapt + (1.0f - light_adapt) * global;
            pow(intensity * adapt, map_key, adapt);
330
            channels[i] = channels[i].mul(1.0f / (adapt + channels[i]));
Fedor Morozov's avatar
Fedor Morozov committed
331 332 333
        }
        gray_img.release();
        merge(channels, img);
334

Fedor Morozov's avatar
Fedor Morozov committed
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
        linear->setGamma(gamma);
        linear->process(img, img);
    }

    float getGamma() const { return gamma; }
    void setGamma(float val) { gamma = val; }

    float getIntensity() const { return intensity; }
    void setIntensity(float val) { intensity = val; }

    float getLightAdaptation() const { return light_adapt; }
    void setLightAdaptation(float val) { light_adapt = val; }

    float getColorAdaptation() const { return color_adapt; }
    void setColorAdaptation(float val) { color_adapt = val; }

    void write(FileStorage& fs) const
Fedor Morozov's avatar
Fedor Morozov committed
352
    {
353
        writeFormat(fs);
Fedor Morozov's avatar
Fedor Morozov committed
354 355
        fs << "name" << name
           << "gamma" << gamma
356 357
           << "intensity" << intensity
           << "light_adapt" << light_adapt
Fedor Morozov's avatar
Fedor Morozov committed
358
           << "color_adapt" << color_adapt;
Fedor Morozov's avatar
Fedor Morozov committed
359 360 361 362 363 364 365
    }

    void read(const FileNode& fn)
    {
        FileNode n = fn["name"];
        CV_Assert(n.isString() && String(n) == name);
        gamma = fn["gamma"];
Fedor Morozov's avatar
Fedor Morozov committed
366 367 368
        intensity = fn["intensity"];
        light_adapt = fn["light_adapt"];
        color_adapt = fn["color_adapt"];
Fedor Morozov's avatar
Fedor Morozov committed
369 370 371
    }

protected:
Fedor Morozov's avatar
Fedor Morozov committed
372 373
    String name;
    float gamma, intensity, light_adapt, color_adapt;
Fedor Morozov's avatar
Fedor Morozov committed
374 375
};

376
Ptr<TonemapReinhard> createTonemapReinhard(float gamma, float contrast, float sigma_color, float sigma_space)
Fedor Morozov's avatar
Fedor Morozov committed
377
{
378
    return makePtr<TonemapReinhardImpl>(gamma, contrast, sigma_color, sigma_space);
Fedor Morozov's avatar
Fedor Morozov committed
379 380 381 382 383
}

class TonemapMantiukImpl : public TonemapMantiuk
{
public:
384 385 386 387 388
    TonemapMantiukImpl(float _gamma, float _scale, float _saturation) :
        name("TonemapMantiuk"),
        gamma(_gamma),
        scale(_scale),
        saturation(_saturation)
Fedor Morozov's avatar
Fedor Morozov committed
389 390 391
    {
    }

392
    void process(InputArray _src, OutputArray _dst)
Fedor Morozov's avatar
Fedor Morozov committed
393
    {
394 395
        CV_INSTRUMENT_REGION()

Fedor Morozov's avatar
Fedor Morozov committed
396 397 398 399
        Mat src = _src.getMat();
        CV_Assert(!src.empty());
        _dst.create(src.size(), CV_32FC3);
        Mat img = _dst.getMat();
Fedor Morozov's avatar
Fedor Morozov committed
400
        Ptr<Tonemap> linear = createTonemap(1.0f);
Fedor Morozov's avatar
Fedor Morozov committed
401 402 403 404 405
        linear->process(src, img);

        Mat gray_img;
        cvtColor(img, gray_img, COLOR_RGB2GRAY);
        Mat log_img;
406
        log_(gray_img, log_img);
Fedor Morozov's avatar
Fedor Morozov committed
407 408 409 410 411

        std::vector<Mat> x_contrast, y_contrast;
        getContrast(log_img, x_contrast, y_contrast);

        for(size_t i = 0; i < x_contrast.size(); i++) {
412 413
            mapContrast(x_contrast[i]);
            mapContrast(y_contrast[i]);
Fedor Morozov's avatar
Fedor Morozov committed
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
        }

        Mat right(src.size(), CV_32F);
        calculateSum(x_contrast, y_contrast, right);

        Mat p, r, product, x = log_img;
        calculateProduct(x, r);
        r = right - r;
        r.copyTo(p);

        const float target_error = 1e-3f;
        float target_norm = static_cast<float>(right.dot(right)) * powf(target_error, 2.0f);
        int max_iterations = 100;
        float rr = static_cast<float>(r.dot(r));

        for(int i = 0; i < max_iterations; i++)
        {
            calculateProduct(p, product);
            float alpha = rr / static_cast<float>(p.dot(product));

            r -= alpha * product;
            x += alpha * p;

            float new_rr = static_cast<float>(r.dot(r));
            p = r + (new_rr / rr) * p;
            rr = new_rr;

            if(rr < target_norm) {
                break;
            }
        }
        exp(x, x);
        mapLuminance(img, img, gray_img, x, saturation);

Fedor Morozov's avatar
Fedor Morozov committed
448
        linear = createTonemap(gamma);
Fedor Morozov's avatar
Fedor Morozov committed
449 450 451 452 453 454 455 456 457 458 459 460 461 462
        linear->process(img, img);
    }

    float getGamma() const { return gamma; }
    void setGamma(float val) { gamma = val; }

    float getScale() const { return scale; }
    void setScale(float val) { scale = val; }

    float getSaturation() const { return saturation; }
    void setSaturation(float val) { saturation = val; }

    void write(FileStorage& fs) const
    {
463
        writeFormat(fs);
Fedor Morozov's avatar
Fedor Morozov committed
464 465
        fs << "name" << name
           << "gamma" << gamma
466
           << "scale" << scale
Fedor Morozov's avatar
Fedor Morozov committed
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
           << "saturation" << saturation;
    }

    void read(const FileNode& fn)
    {
        FileNode n = fn["name"];
        CV_Assert(n.isString() && String(n) == name);
        gamma = fn["gamma"];
        scale = fn["scale"];
        saturation = fn["saturation"];
    }

protected:
    String name;
    float gamma, scale, saturation;

    void signedPow(Mat src, float power, Mat& dst)
    {
        Mat sign = (src > 0);
486 487
        sign.convertTo(sign, CV_32F, 1.0f/255.0f);
        sign = sign * 2.0f - 1.0f;
Fedor Morozov's avatar
Fedor Morozov committed
488 489 490 491
        pow(abs(src), power, dst);
        dst = dst.mul(sign);
    }

492
    void mapContrast(Mat& contrast)
Fedor Morozov's avatar
Fedor Morozov committed
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
    {
        const float response_power = 0.4185f;
        signedPow(contrast, response_power, contrast);
        contrast *= scale;
        signedPow(contrast, 1.0f / response_power, contrast);
    }

    void getGradient(Mat src, Mat& dst, int pos)
    {
        dst = Mat::zeros(src.size(), CV_32F);
        Mat a, b;
        Mat grad = src.colRange(1, src.cols) - src.colRange(0, src.cols - 1);
        grad.copyTo(dst.colRange(pos, src.cols + pos - 1));
        if(pos == 1) {
            src.col(0).copyTo(dst.col(0));
        }
    }

    void getContrast(Mat src, std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast)
    {
        int levels = static_cast<int>(logf(static_cast<float>(min(src.rows, src.cols))) / logf(2.0f));
        x_contrast.resize(levels);
        y_contrast.resize(levels);

        Mat layer;
        src.copyTo(layer);
        for(int i = 0; i < levels; i++) {
            getGradient(layer, x_contrast[i], 0);
            getGradient(layer.t(), y_contrast[i], 0);
522
            resize(layer, layer, Size(layer.cols / 2, layer.rows / 2), 0, 0, INTER_LINEAR);
Fedor Morozov's avatar
Fedor Morozov committed
523 524 525 526 527
        }
    }

    void calculateSum(std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast, Mat& sum)
    {
528 529 530 531 532
        if (x_contrast.empty())
            return;
        const int last = (int)x_contrast.size() - 1;
        sum = Mat::zeros(x_contrast[last].size(), CV_32F);
        for(int i = last; i >= 0; i--)
Fedor Morozov's avatar
Fedor Morozov committed
533 534 535 536
        {
            Mat grad_x, grad_y;
            getGradient(x_contrast[i], grad_x, 1);
            getGradient(y_contrast[i], grad_y, 1);
537
            resize(sum, sum, x_contrast[i].size(), 0, 0, INTER_LINEAR);
Fedor Morozov's avatar
Fedor Morozov committed
538 539 540 541 542 543 544 545 546 547 548 549 550 551
            sum += grad_x + grad_y.t();
        }
    }

    void calculateProduct(Mat src, Mat& dst)
    {
        std::vector<Mat> x_contrast, y_contrast;
        getContrast(src, x_contrast, y_contrast);
        calculateSum(x_contrast, y_contrast, dst);
    }
};

Ptr<TonemapMantiuk> createTonemapMantiuk(float gamma, float scale, float saturation)
{
552
    return makePtr<TonemapMantiukImpl>(gamma, scale, saturation);
553
}
Fedor Morozov's avatar
Fedor Morozov committed
554

555
}