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