Mantiuk's tonemapping
authorFedor Morozov <f-morozov@ya.ru>
Mon, 5 Aug 2013 15:31:10 +0000 (19:31 +0400)
committerFedor Morozov <f-morozov@ya.ru>
Mon, 5 Aug 2013 15:31:10 +0000 (19:31 +0400)
1  2 
modules/photo/include/opencv2/photo.hpp
modules/photo/src/align.cpp
modules/photo/src/hdr_common.cpp
modules/photo/src/hdr_common.hpp
modules/photo/src/merge.cpp
modules/photo/src/tonemap.cpp
modules/photo/test/test_hdr.cpp

@@@ -87,15 -85,11 +85,11 @@@ class CV_EXPORTS_W Tonemap : public Alg
  public:
      CV_WRAP virtual void process(InputArray src, OutputArray dst) = 0;
  
 -      CV_WRAP virtual float getGamma() const = 0;
 -      CV_WRAP virtual void setGamma(float gamma) = 0;
 +    CV_WRAP virtual float getGamma() const = 0;
 +    CV_WRAP virtual void setGamma(float gamma) = 0;
  };
  
- class CV_EXPORTS_W TonemapLinear : public Tonemap
- {
- };
- CV_EXPORTS_W Ptr<TonemapLinear> createTonemapLinear(float gamma = 1.0f);
 -CV_EXPORTS_W Ptr<Tonemap> createTonemapLinear(float gamma = 1.0f);
++CV_EXPORTS_W Ptr<Tonemap> createTonemap(float gamma = 1.0f);
  
  // "Adaptive Logarithmic Mapping For Displaying HighContrast Scenes", Drago et al., 2003
  
@@@ -229,7 -228,7 +228,7 @@@ protected
  
  CV_EXPORTS_W Ptr<AlignMTB> createAlignMTB(int max_bits, int exclude_range)
  {
 -      return new AlignMTBImpl(max_bits, exclude_range);
 +    return new AlignMTBImpl(max_bits, exclude_range);
  }
  
- }
+ }
@@@ -62,25 -61,13 +61,25 @@@ void checkImageDimensions(const std::ve
  
  Mat tringleWeights()
  {
 -      Mat w(256, 3, CV_32F);
 -      for(int i = 0; i < 256; i++) {
 -              for(int j = 0; j < 3; j++) {
 -                      w.at<float>(i, j) = i < 128 ? i + 1.0f : 256.0f - i;
 -              }
 -      }
 -      return w;
 +    Mat w(256, 3, CV_32F);
 +    for(int i = 0; i < 256; i++) {
 +        for(int j = 0; j < 3; j++) {
 +            w.at<float>(i, j) = i < 128 ? i + 1.0f : 256.0f - i;
 +        }
 +    }
 +    return w;
 +}
 +
 +void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation)
 +{
 +    std::vector<Mat> channels(3);
 +    split(src, channels);
 +    for(int i = 0; i < 3; i++) {
 +        channels[i] = channels[i].mul(1.0f / lum);
 +        pow(channels[i], saturation, channels[i]);
 +        channels[i] = channels[i].mul(new_lum);
 +    }
 +    merge(channels, dst);
  }
  
- };
+ };
@@@ -53,8 -52,6 +52,8 @@@ void checkImageDimensions(const std::ve
  
  Mat tringleWeights();
  
 +void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation);
 +
  };
  
- #endif
+ #endif
@@@ -257,7 -256,7 +256,7 @@@ protected
  
  Ptr<MergeMertens> createMergeMertens(float wcon, float wsat, float wexp)
  {
 -      return new MergeMertensImpl(wcon, wsat, wexp);
 +    return new MergeMertensImpl(wcon, wsat, wexp);
  }
  
- }
+ }
  namespace cv
  {
  
- class TonemapLinearImpl : public TonemapLinear
 -class TonemapLinearImpl : public Tonemap
++class TonemapImpl : public Tonemap
  {
  public:
-     TonemapLinearImpl(float gamma) : gamma(gamma), name("TonemapLinear")
 -      TonemapLinearImpl(float gamma) : gamma(gamma), name("TonemapLinear")
 -      {
 -      }
 -
 -      void process(InputArray _src, OutputArray _dst) 
 -      {
 -              Mat src = _src.getMat();
 -              CV_Assert(!src.empty());
 -              _dst.create(src.size(), CV_32FC3);
 -              Mat dst = _dst.getMat();
 -              
 -              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
++    TonemapImpl(float gamma) : gamma(gamma), name("Tonemap")
 +    {
 +    }
 +
 +    void process(InputArray _src, OutputArray _dst) 
 +    {
 +        Mat src = _src.getMat();
 +        CV_Assert(!src.empty());
 +        _dst.create(src.size(), CV_32FC3);
 +        Mat dst = _dst.getMat();
 +        
 +        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
      {
          fs << "name" << name
             << "gamma" << gamma;
      }
  
  protected:
 -      String name;
 -      float gamma;
 +    String name;
 +    float gamma;
  };
  
- Ptr<TonemapLinear> createTonemapLinear(float gamma)
 -Ptr<Tonemap> createTonemapLinear(float gamma)
++Ptr<Tonemap> createTonemap(float gamma)
  {
-     return new TonemapLinearImpl(gamma);
 -      return new TonemapLinearImpl(gamma);
++    return new TonemapImpl(gamma);
  }
  
  class TonemapDragoImpl : public TonemapDrago
  {
  public:
 -      TonemapDragoImpl(float gamma, float bias) : 
 -          gamma(gamma), 
 +    TonemapDragoImpl(float gamma, float saturation, float bias) : 
 +        gamma(gamma), 
 +        saturation(saturation),
          bias(bias),
 -              name("TonemapLinear")
 -      {
 -      }
 -
 -      void process(InputArray _src, OutputArray _dst) 
 -      {
 -              Mat src = _src.getMat();
 -              CV_Assert(!src.empty());
 -              _dst.create(src.size(), CV_32FC3);
 -              Mat img = _dst.getMat();
 -              
 -        Ptr<Tonemap> linear = createTonemapLinear(1.0f);
 -              linear->process(src, img);
 -
 -              Mat gray_img;
 -              cvtColor(img, gray_img, COLOR_RGB2GRAY);
 -              Mat log_img;
 -              log(gray_img, log_img);
 -              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);
 -              map = map.mul(1.0f / gray_img);
 -              div.release();
 -              gray_img.release();
 -
 -              std::vector<Mat> channels(3);
 -              split(img, channels);
 -              for(int i = 0; i < 3; i++) {
 -                      channels[i] = channels[i].mul(map);
 -              }
 -              map.release();
 -              merge(channels, img);
 -              
 -              linear->setGamma(gamma);
 -              linear->process(img, img);
 -      }
 -
 -      float getGamma() const { return gamma; }
 -      void setGamma(float val) { gamma = val; }
 -
 -      float getBias() const { return bias; }
 -      void setBias(float val) { bias = val; }
 -
 -      void write(FileStorage& fs) const
 +        name("TonemapDrago")
 +    {
 +    }
 +
 +    void process(InputArray _src, OutputArray _dst) 
 +    {
 +        Mat src = _src.getMat();
 +        CV_Assert(!src.empty());
 +        _dst.create(src.size(), CV_32FC3);
 +        Mat img = _dst.getMat();
 +        
-         Ptr<TonemapLinear> linear = createTonemapLinear(1.0f);
++        Ptr<Tonemap> linear = createTonemap(1.0f);
 +        linear->process(src, img);
 +
 +        Mat gray_img;
 +        cvtColor(img, gray_img, COLOR_RGB2GRAY);
 +        Mat log_img;
 +        log(gray_img, log_img);
 +        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);
 +        
 +        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
      {
          fs << "name" << name
             << "gamma" << gamma
@@@ -184,58 -184,60 +183,58 @@@ Ptr<TonemapDrago> createTonemapDrago(fl
  class TonemapDurandImpl : public TonemapDurand
  {
  public:
 -      TonemapDurandImpl(float gamma, float contrast, float sigma_color, float sigma_space) : 
 -          gamma(gamma), 
 +    TonemapDurandImpl(float gamma, float saturation, float contrast, float sigma_color, float sigma_space) : 
 +        gamma(gamma), 
 +        saturation(saturation),
          contrast(contrast),
 -              sigma_color(sigma_color),
 -              sigma_space(sigma_space),
 -              name("TonemapDurand")
 -      {
 -      }
 -
 -      void process(InputArray _src, OutputArray _dst) 
 -      {
 -              Mat src = _src.getMat();
 -              CV_Assert(!src.empty());
 -              _dst.create(src.size(), CV_32FC3);
 -              Mat dst = _dst.getMat();
 -              
 -              Mat gray_img;
 -              cvtColor(src, gray_img, COLOR_RGB2GRAY);
 -              Mat log_img;
 -              log(gray_img, log_img);
 -              Mat map_img;
 -              bilateralFilter(log_img, map_img, -1, sigma_color, sigma_space);
 -        
 -              double min, max;
 -              minMaxLoc(map_img, &min, &max);
 -              float scale = contrast / static_cast<float>(max - min);
 +        sigma_color(sigma_color),
 +        sigma_space(sigma_space),
 +        name("TonemapDurand")
 +    {
 +    }
  
 -              exp(map_img * (scale - 1.0f) + log_img, map_img);
 -              log_img.release();
 -              map_img = map_img.mul(1.0f / gray_img);
 -              gray_img.release();
 +    void process(InputArray _src, OutputArray _dst) 
 +    {
 +        Mat src = _src.getMat();
 +        CV_Assert(!src.empty());
 +        _dst.create(src.size(), CV_32FC3);
 +        Mat img = _dst.getMat();
-         Ptr<TonemapLinear> linear = createTonemapLinear(1.0f);
++        Ptr<Tonemap> linear = createTonemap(1.0f);
 +        linear->process(src, img);
 +
 +        Mat gray_img;
 +        cvtColor(img, gray_img, COLOR_RGB2GRAY);
 +        Mat log_img;
 +        log(gray_img, log_img);
 +        Mat map_img;
 +        bilateralFilter(log_img, map_img, -1, sigma_color, sigma_space);
 +        
 +        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);
 +    }
  
 -              std::vector<Mat> channels(3);
 -              split(src, channels);
 -              for(int i = 0; i < 3; i++) {
 -                      channels[i] = channels[i].mul(map_img);
 -              }
 -              merge(channels, dst);
 -              pow(dst, 1.0f / gamma, dst);
 -      }
 +    float getGamma() const { return gamma; }
 +    void setGamma(float val) { gamma = val; }
  
 -      float getGamma() const { return gamma; }
 -      void setGamma(float val) { gamma = val; }
 +    float getSaturation() const { return saturation; }
 +    void setSaturation(float val) { saturation = val; }
  
 -      float getContrast() const { return contrast; }
 -      void setContrast(float val) { contrast = val; }
 +    float getContrast() const { return contrast; }
 +    void setContrast(float val) { contrast = val; }
  
 -      float getSigmaColor() const { return sigma_color; }
 -      void setSigmaColor(float val) { sigma_color = val; }
 +    float getSigmaColor() const { return sigma_color; }
 +    void setSigmaColor(float val) { sigma_color = val; }
  
 -      float getSigmaSpace() const { return sigma_space; }
 -      void setSigmaSpace(float val) { sigma_space = val; }
 +    float getSigmaSpace() const { return sigma_space; }
 +    void setSigmaSpace(float val) { sigma_space = val; }
  
 -      void write(FileStorage& fs) const
 +    void write(FileStorage& fs) const
      {
          fs << "name" << name
             << "gamma" << gamma
@@@ -269,70 -269,71 +268,70 @@@ Ptr<TonemapDurand> createTonemapDurand(
  class TonemapReinhardDevlinImpl : public TonemapReinhardDevlin
  {
  public:
 -      TonemapReinhardDevlinImpl(float gamma, float intensity, float light_adapt, float color_adapt) : 
 -          gamma(gamma), 
 +    TonemapReinhardDevlinImpl(float gamma, float intensity, float light_adapt, float color_adapt) : 
 +        gamma(gamma), 
          intensity(intensity),
 -              light_adapt(light_adapt),
 -              color_adapt(color_adapt),
 -              name("TonemapReinhardDevlin")
 -      {
 -      }
 -
 -      void process(InputArray _src, OutputArray _dst)
 -      {
 -              Mat src = _src.getMat();
 -              CV_Assert(!src.empty());
 -              _dst.create(src.size(), CV_32FC3);
 -              Mat img = _dst.getMat();
 -              
 -        Ptr<Tonemap> linear = createTonemapLinear(1.0f);
 -              linear->process(src, img);
 -              
 -              Mat gray_img;
 -              cvtColor(img, gray_img, COLOR_RGB2GRAY);
 -              Mat log_img;
 -              log(gray_img, log_img);
 -
 -              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);
 -                      channels[i] = channels[i].mul(1.0f / (adapt + channels[i]));            
 -              }
 -              gray_img.release();
 -              merge(channels, img);
 -              
 -              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
 +        light_adapt(light_adapt),
 +        color_adapt(color_adapt),
 +        name("TonemapReinhardDevlin")
 +    {
 +    }
 +
 +    void process(InputArray _src, OutputArray _dst)
 +    {
 +        Mat src = _src.getMat();
 +        CV_Assert(!src.empty());
 +        _dst.create(src.size(), CV_32FC3);
 +        Mat img = _dst.getMat();
-         Ptr<TonemapLinear> linear = createTonemapLinear(1.0f);
++        Ptr<Tonemap> linear = createTonemap(1.0f);
 +        linear->process(src, img);
 +        
 +        Mat gray_img;
 +        cvtColor(img, gray_img, COLOR_RGB2GRAY);
 +        Mat log_img;
 +        log(gray_img, log_img);
 +
 +        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);
 +            channels[i] = channels[i].mul(1.0f / (adapt + channels[i]));        
 +        }
 +        gray_img.release();
 +        merge(channels, img);
 +        
 +        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
      {
          fs << "name" << name
             << "gamma" << gamma
@@@ -358,175 -359,7 +357,175 @@@ protected
  
  Ptr<TonemapReinhardDevlin> createTonemapReinhardDevlin(float gamma, float contrast, float sigma_color, float sigma_space)
  {
 -      return new TonemapReinhardDevlinImpl(gamma, contrast, sigma_color, sigma_space);
 +    return new TonemapReinhardDevlinImpl(gamma, contrast, sigma_color, sigma_space);
 +}
 +
 +class TonemapMantiukImpl : public TonemapMantiuk
 +{
 +public:
 +    TonemapMantiukImpl(float gamma, float scale, float saturation) : 
 +        gamma(gamma), 
 +        scale(scale),
 +        saturation(saturation),
 +        name("TonemapMantiuk")
 +    {
 +    }
 +
 +    void process(InputArray _src, OutputArray _dst) 
 +    {
 +        Mat src = _src.getMat();
 +        CV_Assert(!src.empty());
 +        _dst.create(src.size(), CV_32FC3);
 +        Mat img = _dst.getMat();
-         Ptr<TonemapLinear> linear = createTonemapLinear(1.0f);
++        Ptr<Tonemap> linear = createTonemap(1.0f);
 +        linear->process(src, img);
 +
 +        Mat gray_img;
 +        cvtColor(img, gray_img, COLOR_RGB2GRAY);
 +        Mat log_img;
 +        log(gray_img, log_img);
 +
 +        std::vector<Mat> x_contrast, y_contrast;
 +        getContrast(log_img, x_contrast, y_contrast);
 +
 +        for(size_t i = 0; i < x_contrast.size(); i++) {
 +            mapContrast(x_contrast[i], scale);
 +            mapContrast(y_contrast[i], scale);
 +        }
 +
 +        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);
 +
-         linear = createTonemapLinear(gamma);
++        linear = createTonemap(gamma);
 +        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
 +    {
 +        fs << "name" << name
 +           << "gamma" << gamma
 +           << "scale" << scale 
 +           << "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);
 +        sign.convertTo(sign, CV_32F, 1/255.0f);
 +        sign = sign * 2 - 1;
 +        pow(abs(src), power, dst);
 +        dst = dst.mul(sign);
 +    }
 +
 +    void mapContrast(Mat& contrast, float scale)
 +    {
 +        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);
 +            resize(layer, layer, Size(layer.cols / 2, layer.rows / 2));
 +        }
 +    }
 +
 +    void calculateSum(std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast, Mat& sum)
 +    {
 +        sum = Mat::zeros(x_contrast[x_contrast.size() - 1].size(), CV_32F);
 +        for(int i = x_contrast.size() - 1; i >= 0; i--)
 +        {
 +            Mat grad_x, grad_y;
 +            getGradient(x_contrast[i], grad_x, 1);
 +            getGradient(y_contrast[i], grad_y, 1);
 +            resize(sum, sum, x_contrast[i].size());
 +            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)
 +{
 +    return new TonemapMantiukImpl(gamma, scale, saturation);
  }
  
- }
+ }
@@@ -91,13 -91,14 +91,13 @@@ void loadResponseCSV(String path, Mat& 
  
  TEST(Photo_Tonemap, regression)
  {
 -      string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
 +      string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/tonemap/";
        
        Mat img, expected, result;
 -      loadImage(test_path + "rle.hdr", img);
 +      loadImage(test_path + "image.hdr", img);
        float gamma = 2.2f;
 -      test_path += "tonemap/";
        
-       Ptr<TonemapLinear> linear = createTonemapLinear(gamma);
+     Ptr<Tonemap> linear = createTonemapLinear(gamma);
        linear->process(img, result);
        loadImage(test_path + "linear.png", expected);
        result.convertTo(result, CV_8UC3, 255);