Home | History | Annotate | Download | only in src
      1 /*M///////////////////////////////////////////////////////////////////////////////////////
      2 //
      3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
      4 //
      5 //  By downloading, copying, installing or using the software you agree to this license.
      6 //  If you do not agree to this license, do not download, install,
      7 //  copy or use the software.
      8 //
      9 //
     10 //                           License Agreement
     11 //                For Open Source Computer Vision Library
     12 //
     13 // Copyright (C) 2013, OpenCV Foundation, all rights reserved.
     14 // Third party copyrights are property of their respective owners.
     15 //
     16 // Redistribution and use in source and binary forms, with or without modification,
     17 // are permitted provided that the following conditions are met:
     18 //
     19 //   * Redistribution's of source code must retain the above copyright notice,
     20 //     this list of conditions and the following disclaimer.
     21 //
     22 //   * Redistribution's in binary form must reproduce the above copyright notice,
     23 //     this list of conditions and the following disclaimer in the documentation
     24 //     and/or other materials provided with the distribution.
     25 //
     26 //   * The name of the copyright holders may not be used to endorse or promote products
     27 //     derived from this software without specific prior written permission.
     28 //
     29 // This software is provided by the copyright holders and contributors "as is" and
     30 // any express or implied warranties, including, but not limited to, the implied
     31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
     32 // In no event shall the Intel Corporation or contributors be liable for any direct,
     33 // indirect, incidental, special, exemplary, or consequential damages
     34 // (including, but not limited to, procurement of substitute goods or services;
     35 // loss of use, data, or profits; or business interruption) however caused
     36 // and on any theory of liability, whether in contract, strict liability,
     37 // or tort (including negligence or otherwise) arising in any way out of
     38 // the use of this software, even if advised of the possibility of such damage.
     39 //
     40 //M*/
     41 
     42 #include "precomp.hpp"
     43 #include "opencv2/photo.hpp"
     44 #include "opencv2/imgproc.hpp"
     45 #include "hdr_common.hpp"
     46 
     47 namespace cv
     48 {
     49 
     50 inline void log_(const Mat& src, Mat& dst)
     51 {
     52     max(src, Scalar::all(1e-4), dst);
     53     log(dst, dst);
     54 }
     55 
     56 class TonemapImpl : public Tonemap
     57 {
     58 public:
     59     TonemapImpl(float _gamma) : name("Tonemap"), gamma(_gamma)
     60     {
     61     }
     62 
     63     void process(InputArray _src, OutputArray _dst)
     64     {
     65         Mat src = _src.getMat();
     66         CV_Assert(!src.empty());
     67         _dst.create(src.size(), CV_32FC3);
     68         Mat dst = _dst.getMat();
     69 
     70         double min, max;
     71         minMaxLoc(src, &min, &max);
     72         if(max - min > DBL_EPSILON) {
     73             dst = (src - min) / (max - min);
     74         } else {
     75             src.copyTo(dst);
     76         }
     77 
     78         pow(dst, 1.0f / gamma, dst);
     79     }
     80 
     81     float getGamma() const { return gamma; }
     82     void setGamma(float val) { gamma = val; }
     83 
     84     void write(FileStorage& fs) const
     85     {
     86         fs << "name" << name
     87            << "gamma" << gamma;
     88     }
     89 
     90     void read(const FileNode& fn)
     91     {
     92         FileNode n = fn["name"];
     93         CV_Assert(n.isString() && String(n) == name);
     94         gamma = fn["gamma"];
     95     }
     96 
     97 protected:
     98     String name;
     99     float gamma;
    100 };
    101 
    102 Ptr<Tonemap> createTonemap(float gamma)
    103 {
    104     return makePtr<TonemapImpl>(gamma);
    105 }
    106 
    107 class TonemapDragoImpl : public TonemapDrago
    108 {
    109 public:
    110     TonemapDragoImpl(float _gamma, float _saturation, float _bias) :
    111         name("TonemapDrago"),
    112         gamma(_gamma),
    113         saturation(_saturation),
    114         bias(_bias)
    115     {
    116     }
    117 
    118     void process(InputArray _src, OutputArray _dst)
    119     {
    120         Mat src = _src.getMat();
    121         CV_Assert(!src.empty());
    122         _dst.create(src.size(), CV_32FC3);
    123         Mat img = _dst.getMat();
    124 
    125         Ptr<Tonemap> linear = createTonemap(1.0f);
    126         linear->process(src, img);
    127 
    128         Mat gray_img;
    129         cvtColor(img, gray_img, COLOR_RGB2GRAY);
    130         Mat log_img;
    131         log_(gray_img, log_img);
    132         float mean = expf(static_cast<float>(sum(log_img)[0]) / log_img.total());
    133         gray_img /= mean;
    134         log_img.release();
    135 
    136         double max;
    137         minMaxLoc(gray_img, NULL, &max);
    138 
    139         Mat map;
    140         log(gray_img + 1.0f, map);
    141         Mat div;
    142         pow(gray_img / static_cast<float>(max), logf(bias) / logf(0.5f), div);
    143         log(2.0f + 8.0f * div, div);
    144         map = map.mul(1.0f / div);
    145         div.release();
    146 
    147         mapLuminance(img, img, gray_img, map, saturation);
    148 
    149         linear->setGamma(gamma);
    150         linear->process(img, img);
    151     }
    152 
    153     float getGamma() const { return gamma; }
    154     void setGamma(float val) { gamma = val; }
    155 
    156     float getSaturation() const { return saturation; }
    157     void setSaturation(float val) { saturation = val; }
    158 
    159     float getBias() const { return bias; }
    160     void setBias(float val) { bias = val; }
    161 
    162     void write(FileStorage& fs) const
    163     {
    164         fs << "name" << name
    165            << "gamma" << gamma
    166            << "bias" << bias
    167            << "saturation" << saturation;
    168     }
    169 
    170     void read(const FileNode& fn)
    171     {
    172         FileNode n = fn["name"];
    173         CV_Assert(n.isString() && String(n) == name);
    174         gamma = fn["gamma"];
    175         bias = fn["bias"];
    176         saturation = fn["saturation"];
    177     }
    178 
    179 protected:
    180     String name;
    181     float gamma, saturation, bias;
    182 };
    183 
    184 Ptr<TonemapDrago> createTonemapDrago(float gamma, float saturation, float bias)
    185 {
    186     return makePtr<TonemapDragoImpl>(gamma, saturation, bias);
    187 }
    188 
    189 class TonemapDurandImpl : public TonemapDurand
    190 {
    191 public:
    192     TonemapDurandImpl(float _gamma, float _contrast, float _saturation, float _sigma_color, float _sigma_space) :
    193         name("TonemapDurand"),
    194         gamma(_gamma),
    195         contrast(_contrast),
    196         saturation(_saturation),
    197         sigma_color(_sigma_color),
    198         sigma_space(_sigma_space)
    199     {
    200     }
    201 
    202     void process(InputArray _src, OutputArray _dst)
    203     {
    204         Mat src = _src.getMat();
    205         CV_Assert(!src.empty());
    206         _dst.create(src.size(), CV_32FC3);
    207         Mat img = _dst.getMat();
    208         Ptr<Tonemap> linear = createTonemap(1.0f);
    209         linear->process(src, img);
    210 
    211         Mat gray_img;
    212         cvtColor(img, gray_img, COLOR_RGB2GRAY);
    213         Mat log_img;
    214         log_(gray_img, log_img);
    215         Mat map_img;
    216         bilateralFilter(log_img, map_img, -1, sigma_color, sigma_space);
    217 
    218         double min, max;
    219         minMaxLoc(map_img, &min, &max);
    220         float scale = contrast / static_cast<float>(max - min);
    221         exp(map_img * (scale - 1.0f) + log_img, map_img);
    222         log_img.release();
    223 
    224         mapLuminance(img, img, gray_img, map_img, saturation);
    225         pow(img, 1.0f / gamma, img);
    226     }
    227 
    228     float getGamma() const { return gamma; }
    229     void setGamma(float val) { gamma = val; }
    230 
    231     float getSaturation() const { return saturation; }
    232     void setSaturation(float val) { saturation = val; }
    233 
    234     float getContrast() const { return contrast; }
    235     void setContrast(float val) { contrast = val; }
    236 
    237     float getSigmaColor() const { return sigma_color; }
    238     void setSigmaColor(float val) { sigma_color = val; }
    239 
    240     float getSigmaSpace() const { return sigma_space; }
    241     void setSigmaSpace(float val) { sigma_space = val; }
    242 
    243     void write(FileStorage& fs) const
    244     {
    245         fs << "name" << name
    246            << "gamma" << gamma
    247            << "contrast" << contrast
    248            << "sigma_color" << sigma_color
    249            << "sigma_space" << sigma_space
    250            << "saturation" << saturation;
    251     }
    252 
    253     void read(const FileNode& fn)
    254     {
    255         FileNode n = fn["name"];
    256         CV_Assert(n.isString() && String(n) == name);
    257         gamma = fn["gamma"];
    258         contrast = fn["contrast"];
    259         sigma_color = fn["sigma_color"];
    260         sigma_space = fn["sigma_space"];
    261         saturation = fn["saturation"];
    262     }
    263 
    264 protected:
    265     String name;
    266     float gamma, contrast, saturation, sigma_color, sigma_space;
    267 };
    268 
    269 Ptr<TonemapDurand> createTonemapDurand(float gamma, float contrast, float saturation, float sigma_color, float sigma_space)
    270 {
    271     return makePtr<TonemapDurandImpl>(gamma, contrast, saturation, sigma_color, sigma_space);
    272 }
    273 
    274 class TonemapReinhardImpl : public TonemapReinhard
    275 {
    276 public:
    277     TonemapReinhardImpl(float _gamma, float _intensity, float _light_adapt, float _color_adapt) :
    278         name("TonemapReinhard"),
    279         gamma(_gamma),
    280         intensity(_intensity),
    281         light_adapt(_light_adapt),
    282         color_adapt(_color_adapt)
    283     {
    284     }
    285 
    286     void process(InputArray _src, OutputArray _dst)
    287     {
    288         Mat src = _src.getMat();
    289         CV_Assert(!src.empty());
    290         _dst.create(src.size(), CV_32FC3);
    291         Mat img = _dst.getMat();
    292         Ptr<Tonemap> linear = createTonemap(1.0f);
    293         linear->process(src, img);
    294 
    295         Mat gray_img;
    296         cvtColor(img, gray_img, COLOR_RGB2GRAY);
    297         Mat log_img;
    298         log_(gray_img, log_img);
    299 
    300         float log_mean = static_cast<float>(sum(log_img)[0] / log_img.total());
    301         double log_min, log_max;
    302         minMaxLoc(log_img, &log_min, &log_max);
    303         log_img.release();
    304 
    305         double key = static_cast<float>((log_max - log_mean) / (log_max - log_min));
    306         float map_key = 0.3f + 0.7f * pow(static_cast<float>(key), 1.4f);
    307         intensity = exp(-intensity);
    308         Scalar chan_mean = mean(img);
    309         float gray_mean = static_cast<float>(mean(gray_img)[0]);
    310 
    311         std::vector<Mat> channels(3);
    312         split(img, channels);
    313 
    314         for(int i = 0; i < 3; i++) {
    315             float global = color_adapt * static_cast<float>(chan_mean[i]) + (1.0f - color_adapt) * gray_mean;
    316             Mat adapt = color_adapt * channels[i] + (1.0f - color_adapt) * gray_img;
    317             adapt = light_adapt * adapt + (1.0f - light_adapt) * global;
    318             pow(intensity * adapt, map_key, adapt);
    319             channels[i] = channels[i].mul(1.0f / (adapt + channels[i]));
    320         }
    321         gray_img.release();
    322         merge(channels, img);
    323 
    324         linear->setGamma(gamma);
    325         linear->process(img, img);
    326     }
    327 
    328     float getGamma() const { return gamma; }
    329     void setGamma(float val) { gamma = val; }
    330 
    331     float getIntensity() const { return intensity; }
    332     void setIntensity(float val) { intensity = val; }
    333 
    334     float getLightAdaptation() const { return light_adapt; }
    335     void setLightAdaptation(float val) { light_adapt = val; }
    336 
    337     float getColorAdaptation() const { return color_adapt; }
    338     void setColorAdaptation(float val) { color_adapt = val; }
    339 
    340     void write(FileStorage& fs) const
    341     {
    342         fs << "name" << name
    343            << "gamma" << gamma
    344            << "intensity" << intensity
    345            << "light_adapt" << light_adapt
    346            << "color_adapt" << color_adapt;
    347     }
    348 
    349     void read(const FileNode& fn)
    350     {
    351         FileNode n = fn["name"];
    352         CV_Assert(n.isString() && String(n) == name);
    353         gamma = fn["gamma"];
    354         intensity = fn["intensity"];
    355         light_adapt = fn["light_adapt"];
    356         color_adapt = fn["color_adapt"];
    357     }
    358 
    359 protected:
    360     String name;
    361     float gamma, intensity, light_adapt, color_adapt;
    362 };
    363 
    364 Ptr<TonemapReinhard> createTonemapReinhard(float gamma, float contrast, float sigma_color, float sigma_space)
    365 {
    366     return makePtr<TonemapReinhardImpl>(gamma, contrast, sigma_color, sigma_space);
    367 }
    368 
    369 class TonemapMantiukImpl : public TonemapMantiuk
    370 {
    371 public:
    372     TonemapMantiukImpl(float _gamma, float _scale, float _saturation) :
    373         name("TonemapMantiuk"),
    374         gamma(_gamma),
    375         scale(_scale),
    376         saturation(_saturation)
    377     {
    378     }
    379 
    380     void process(InputArray _src, OutputArray _dst)
    381     {
    382         Mat src = _src.getMat();
    383         CV_Assert(!src.empty());
    384         _dst.create(src.size(), CV_32FC3);
    385         Mat img = _dst.getMat();
    386         Ptr<Tonemap> linear = createTonemap(1.0f);
    387         linear->process(src, img);
    388 
    389         Mat gray_img;
    390         cvtColor(img, gray_img, COLOR_RGB2GRAY);
    391         Mat log_img;
    392         log_(gray_img, log_img);
    393 
    394         std::vector<Mat> x_contrast, y_contrast;
    395         getContrast(log_img, x_contrast, y_contrast);
    396 
    397         for(size_t i = 0; i < x_contrast.size(); i++) {
    398             mapContrast(x_contrast[i]);
    399             mapContrast(y_contrast[i]);
    400         }
    401 
    402         Mat right(src.size(), CV_32F);
    403         calculateSum(x_contrast, y_contrast, right);
    404 
    405         Mat p, r, product, x = log_img;
    406         calculateProduct(x, r);
    407         r = right - r;
    408         r.copyTo(p);
    409 
    410         const float target_error = 1e-3f;
    411         float target_norm = static_cast<float>(right.dot(right)) * powf(target_error, 2.0f);
    412         int max_iterations = 100;
    413         float rr = static_cast<float>(r.dot(r));
    414 
    415         for(int i = 0; i < max_iterations; i++)
    416         {
    417             calculateProduct(p, product);
    418             float alpha = rr / static_cast<float>(p.dot(product));
    419 
    420             r -= alpha * product;
    421             x += alpha * p;
    422 
    423             float new_rr = static_cast<float>(r.dot(r));
    424             p = r + (new_rr / rr) * p;
    425             rr = new_rr;
    426 
    427             if(rr < target_norm) {
    428                 break;
    429             }
    430         }
    431         exp(x, x);
    432         mapLuminance(img, img, gray_img, x, saturation);
    433 
    434         linear = createTonemap(gamma);
    435         linear->process(img, img);
    436     }
    437 
    438     float getGamma() const { return gamma; }
    439     void setGamma(float val) { gamma = val; }
    440 
    441     float getScale() const { return scale; }
    442     void setScale(float val) { scale = val; }
    443 
    444     float getSaturation() const { return saturation; }
    445     void setSaturation(float val) { saturation = val; }
    446 
    447     void write(FileStorage& fs) const
    448     {
    449         fs << "name" << name
    450            << "gamma" << gamma
    451            << "scale" << scale
    452            << "saturation" << saturation;
    453     }
    454 
    455     void read(const FileNode& fn)
    456     {
    457         FileNode n = fn["name"];
    458         CV_Assert(n.isString() && String(n) == name);
    459         gamma = fn["gamma"];
    460         scale = fn["scale"];
    461         saturation = fn["saturation"];
    462     }
    463 
    464 protected:
    465     String name;
    466     float gamma, scale, saturation;
    467 
    468     void signedPow(Mat src, float power, Mat& dst)
    469     {
    470         Mat sign = (src > 0);
    471         sign.convertTo(sign, CV_32F, 1.0f/255.0f);
    472         sign = sign * 2.0f - 1.0f;
    473         pow(abs(src), power, dst);
    474         dst = dst.mul(sign);
    475     }
    476 
    477     void mapContrast(Mat& contrast)
    478     {
    479         const float response_power = 0.4185f;
    480         signedPow(contrast, response_power, contrast);
    481         contrast *= scale;
    482         signedPow(contrast, 1.0f / response_power, contrast);
    483     }
    484 
    485     void getGradient(Mat src, Mat& dst, int pos)
    486     {
    487         dst = Mat::zeros(src.size(), CV_32F);
    488         Mat a, b;
    489         Mat grad = src.colRange(1, src.cols) - src.colRange(0, src.cols - 1);
    490         grad.copyTo(dst.colRange(pos, src.cols + pos - 1));
    491         if(pos == 1) {
    492             src.col(0).copyTo(dst.col(0));
    493         }
    494     }
    495 
    496     void getContrast(Mat src, std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast)
    497     {
    498         int levels = static_cast<int>(logf(static_cast<float>(min(src.rows, src.cols))) / logf(2.0f));
    499         x_contrast.resize(levels);
    500         y_contrast.resize(levels);
    501 
    502         Mat layer;
    503         src.copyTo(layer);
    504         for(int i = 0; i < levels; i++) {
    505             getGradient(layer, x_contrast[i], 0);
    506             getGradient(layer.t(), y_contrast[i], 0);
    507             resize(layer, layer, Size(layer.cols / 2, layer.rows / 2));
    508         }
    509     }
    510 
    511     void calculateSum(std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast, Mat& sum)
    512     {
    513         sum = Mat::zeros(x_contrast[x_contrast.size() - 1].size(), CV_32F);
    514         for(int i = (int)x_contrast.size() - 1; i >= 0; i--)
    515         {
    516             Mat grad_x, grad_y;
    517             getGradient(x_contrast[i], grad_x, 1);
    518             getGradient(y_contrast[i], grad_y, 1);
    519             resize(sum, sum, x_contrast[i].size());
    520             sum += grad_x + grad_y.t();
    521         }
    522     }
    523 
    524     void calculateProduct(Mat src, Mat& dst)
    525     {
    526         std::vector<Mat> x_contrast, y_contrast;
    527         getContrast(src, x_contrast, y_contrast);
    528         calculateSum(x_contrast, y_contrast, dst);
    529     }
    530 };
    531 
    532 Ptr<TonemapMantiuk> createTonemapMantiuk(float gamma, float scale, float saturation)
    533 {
    534     return makePtr<TonemapMantiukImpl>(gamma, scale, saturation);
    535 }
    536 
    537 }
    538