cpp
inline void log_(const Mat& src, Mat& dst)
{
max(src, Scalar::all(1e-4), dst);
log(dst, dst);
}
class TonemapImpl CV_FINAL : public Tonemap
{
public:
TonemapImpl(float _gamma) : name("Tonemap"), gamma(_gamma)
{
}
void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
Mat src = _src.getMat();
CV_Assert(!src.empty());
CV_Assert(_src.dims() == 2 && _src.type() == CV_32FC3);
_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 CV_OVERRIDE { return gamma; }
void setGamma(float val) CV_OVERRIDE { gamma = val; }
void write(FileStorage& fs) const CV_OVERRIDE
{
writeFormat(fs);
fs << "name" << name
<< "gamma" << gamma;
}
void read(const FileNode& fn) CV_OVERRIDE
{
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name);
gamma = fn["gamma"];
}
protected:
String name;
float gamma;
};
Ptr<Tonemap> createTonemap(float gamma)
{
return makePtr<TonemapImpl>(gamma);
}
class TonemapDragoImpl CV_FINAL : public TonemapDrago
{
public:
TonemapDragoImpl(float _gamma, float _saturation, float _bias) :
name("TonemapDrago"),
gamma(_gamma),
saturation(_saturation),
bias(_bias)
{
}
void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
Mat src = _src.getMat();
CV_Assert(!src.empty());
_dst.create(src.size(), CV_32FC3);
Mat img = _dst.getMat();
//不同点1 这里的process优于gamma给的是1.0,所以实际就是做了一次最大最小归一化,而Luminance HDR 中并没有这一步
//其次cvtColor(img, gray_img, COLOR_RGB2GRAY)
//opencv 输入的的hdr图_src 是bgr的
//而Luminance HDR中
//pfs::Channel *X, *Y, *Z;
//frame.getXYZChannels(X, Y, Z);
//...
//pfs::Array2Df &Yr = *Y;
//...
//也就是推测应该是XYZ空间,直接娶了Y亮度分量,由于我是在windows下没有编译成功,
//没有调试,所以不确定推测是否正确,但是看它后面色调映射后直接转成Qimage rgb 显示
//所以彩色Yr 实际是rgb中的绿色通道
//而且从 Luminance HDR 中xyz.cpp中
//const float rgb2xyzD65Mat[3][3] = {{0.4124564f, 0.3575761f, 0.1804375f},
// {0.2126729f, 0.7151522f, 0.0721750f},
// {0.0193339f, 0.1191920f, 0.9503041f}};
//
//void ConvertRGB2XYZ::operator()(float i1, float i2, float i3, float &o1,
// float &o2, float &o3) const {
// o1 = rgb2xyzD65Mat[0][0] * i1 + rgb2xyzD65Mat[0][1] * i2 +
// rgb2xyzD65Mat[0][2] * i3;
// o2 = rgb2xyzD65Mat[1][0] * i1 + rgb2xyzD65Mat[1][1] * i2 +
// rgb2xyzD65Mat[1][2] * i3;
// o3 = rgb2xyzD65Mat[2][0] * i1 + rgb2xyzD65Mat[2][1] * i2 +
// rgb2xyzD65Mat[2][2] * i3;
//}
//可知rgb 转 xyz 的 y 转换公式是0.2126729fxR+0.7151522fxG+0.0721750fxB
//这个标准跟opencv的cvtClor 是不一样的,这里的0.7151522f更高
//从Luminance HDR中将这个算法抽取测试发现直接使用Yr(G 绿色分量),还是使用cvtColor opencv 的结果和Luminance HDR 差别也不大, 可能是因为0.7151522f 接近1的原因
//calculateLuminance(w, h, Yr.data(), avLum, maxLum);
//
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);
//不同点2 Luminance HDR avLum=mean
//但是opencv中
//inline void log_(const Mat& src, Mat& dst)
//{
// max(src, Scalar::all(1e-4), dst);
// log(dst, dst);
//}
//需要改成
//inline void log_(const Mat& src, Mat& dst)
//{
// log(src+1e-4, dst);
//}
//Luminance HDR avLum 才 等于 mean
//并且sum(log_img)[0]calculateLuminance
//中的 avLumThr 累计会有float 和double 导致的累加结果的差异
//测试发现这里会导致最终色调映射的结果有肉眼可见的差异
//
//float maxLum;
//float avLum;
//calculateLuminance(w, h, Yr.data(), avLum, maxLum);
//几何均值
float mean = expf(static_cast<float>(sum(log_img)[0]) / log_img.total());
//除以几何均值的好处,我的理解,同一张hdr图将其每个像素都乘以k 得到的新图,两张图经过后面的色调映射得到的ldr图的结果是相同的,消除乘性因子
gray_img /= mean;
log_img.release();
//这里的max就是maxLum
//因为 normalize maximum luminance by average luminance
//maxLum /= avLum;
double max;
minMaxLoc(gray_img, NULL, &max);
CV_Assert(max > 0);
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);
//不同点3 map 其实就是论文公式Ld的乘号右边大项了
//也就是Luminance HDR中方法 tmo_drago03中的
//L(x, y) = xlogf(Yw + 1.f) / (interpol);
//也就是论文公式的左边项 1.f/divider opencv是直接给忽略掉了,
//Luminance HDR是完全按照论文公式来的
div.release();
//不同点4 按照论文公式 Ld 得到的map的取值范围现在已经是[0~1]了
//mapLuminance 的逻辑如果saturation=1.f
//那么和Luminance HDR的实现的不同就是opencv 用的是最大最小归一化的并且还
//进行 gray_img /= mean;这一步的值gray_img
//而Luminance HDR 中的是无最大最小归一化并进行进行 gray_img /= mean后的gray_img
//区别还是不小,我认为完全按照论文公式更合理些,比如divider 这里就能保证map的值映射到[0~1]
//Luminance HDR 方法pfstmo_drago03中是没有saturation参数
//但是在它的后处理逻辑中有饱和度调节的逻辑,逻辑不一样
//opencv中在hdr_common.cpp
//每通道pow(channels[i], saturation, channels[i]);来调节饱和度
//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);
//}
//而Luminance HDR 中是
//void TMWorker::postprocessFrame(pfs::Frame *working_frame, TonemappingOptions //*tm_options) {
// // auto-level?
// // black-point?
// // white-point?
// if (tm_options->postsaturation != 1.0) {
// pfs::applySaturation(working_frame, tm_options->postsaturation);
// }
// if (tm_options->postgamma != 1.0) {
// pfs::applyGamma(working_frame, tm_options->postgamma);
// }
//
//}
//namespace pfs {
//namespace colorspace {
//
//inline void ChangeSaturation::operator()(float i1, float i2, float i3, float &o1,
// float &o2, float &o3) const {
// float h, s, l;
// rgb2hsl(i1, i2, i3, h, s, l);
// hsl2rgb(h, multiplier*s, l, o1, o2, o3);
//}
//}
//}
//先将rgb转到hsl空间 然后对hsl中对 s通道乘以aturation 然后再转回rgb的
mapLuminance(img, img, gray_img, map, saturation);
//不同点4 process中应用gamma的逻辑和applyGamma 的逻辑不同点就是process 是最大最小归一化然后gamma
linear->setGamma(gamma);
linear->process(img, img);
//要改成和Luminance HDR 逻辑一致,那就是在这一步只进行gamma,然后clamp限制img的值的范围在[0~1]
// postprocessFrame(working_frame, tm_options);
// emit tonemapSuccess(working_frame, tm_options);
//connect(m_TMWorker, &TMWorker::tonemapSuccess, this,
// &MainWindow::addLdrFrame);
//...
//QImage *fromLDRPFStoQImage(pfs::Frame *in_frame, float min_luminance,
// float max_luminance, RGBMappingType mapping_method) {
//...
// pfs::Channel *Xc, *Yc, *Zc;
// in_frame->getXYZChannels(Xc, Yc, Zc);
// ...
// QImage *temp_qimage = new QImage(
// in_frame->getWidth(), in_frame->getHeight(), QImage::Format_RGB32);
////直接clamp到[0,1],乘以255显示
// QRgbRemapper remapper(min_luminance, max_luminance, mapping_method);
// utils::transform(Xc->begin(), Xc->end(), Yc->begin(), Zc->begin(),
// reinterpret_cast<QRgb *>(temp_qimage->bits()), remapper);
//...
// return temp_qimage;
//}
}
float getGamma() const CV_OVERRIDE { return gamma; }
void setGamma(float val) CV_OVERRIDE { gamma = val; }
float getSaturation() const CV_OVERRIDE { return saturation; }
void setSaturation(float val) CV_OVERRIDE { saturation = val; }
float getBias() const CV_OVERRIDE { return bias; }
void setBias(float val) CV_OVERRIDE { bias = val; }
void write(FileStorage& fs) const CV_OVERRIDE
{
writeFormat(fs);
fs << "name" << name
<< "gamma" << gamma
<< "bias" << bias
<< "saturation" << saturation;
}
void read(const FileNode& fn) CV_OVERRIDE
{
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name);
gamma = fn["gamma"];
bias = fn["bias"];
saturation = fn["saturation"];
}
protected:
String name;
float gamma, saturation, bias;
};
Ptr<TonemapDrago> createTonemapDrago(float gamma, float saturation, float bias)
{
return makePtr<TonemapDragoImpl>(gamma, saturation, bias);
}
LuminanceHDR-master\src\TonemappingOperators\drago03\pfstmo_drago03.cpp
cpp
/**
* @brief Adaptive logarithmic tone mapping
*
* Adaptive logarithmic mapping for displaying high contrast
* scenes.
* F. Drago, K. Myszkowski, T. Annen, and N. Chiba. In Eurographics 2003.
*
* This file is a part of LuminanceHDR package, based on pfstmo.
* ----------------------------------------------------------------------
* Copyright (C) 2003,2004 Grzegorz Krawczyk
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* ----------------------------------------------------------------------
*
* @author Grzegorz Krawczyk, <krawczyk@mpi-inf.mpg.de>
*
* $Id: pfstmo_drago03.cpp,v 1.3 2008/09/04 12:46:48 julians37 Exp $
*/
#include <cmath>
#include <sstream>
#include <iostream>
#include "Libpfs/exception.h"
#include "Libpfs/frame.h"
#include "Libpfs/progress.h"
#include "tmo_drago03.h"
#include "../../opthelper.h"
void pfstmo_drago03(pfs::Frame &frame, float opt_biasValue, pfs::Progress &ph) {
#ifndef NDEBUG
std::stringstream ss;
ss << "pfstmo_drago03 (";
ss << "bias: " << opt_biasValue;
ss << ")";
std::cout << ss.str() << std::endl;
#endif
ph.setValue(0);
pfs::Channel *X, *Y, *Z;
frame.getXYZChannels(X, Y, Z);
if (!X || !Y || !Z) {
throw pfs::Exception("Missing X, Y, Z channels in the PFS stream");
}
frame.getTags().setTag("LUMINANCE", "RELATIVE");
pfs::Array2Df &Xr = *X;
pfs::Array2Df &Yr = *Y;
pfs::Array2Df &Zr = *Z;
int w = Yr.getCols();
int h = Yr.getRows();
float maxLum;
float avLum;
calculateLuminance(w, h, Yr.data(), avLum, maxLum);
pfs::Array2Df L(w, h);
try {
tmo_drago03(Yr, L, maxLum, avLum, opt_biasValue, ph);
} catch (...) {
throw pfs::Exception("Tonemapping Failed!");
}
if (!ph.canceled()) {
#pragma omp parallel for
for (int y = 0; y < h; y++) {
int x = 0;
#ifdef __SSE2__
for (; x < w - 3; x+=4) {
vfloat yrv = LVFU(Yr(x, y));
vfloat scalev = vselfnotzero(vmaskf_eq(yrv, ZEROV), LVFU(L(x,y)) / yrv);
STVFU(Yr(x, y), yrv * scalev);
STVFU(Xr(x, y), LVFU(Xr(x, y)) * scalev);
STVFU(Zr(x, y), LVFU(Zr(x, y)) * scalev);
}
#endif
for (; x < w; x++) {
float yr = Yr(x, y);
float scale = yr != 0.f ? L(x, y) / yr : 0.f;
assert(!isnan(scale));
Yr(x, y) = Yr(x, y) * scale;
Xr(x, y) = Xr(x, y) * scale;
Zr(x, y) = Zr(x, y) * scale;
}
}
ph.setValue(100);
}
}
LuminanceHDR-master\src\TonemappingOperators\drago03\tmo_drago03.cpp
cpp
/**
* @brief Frederic Drago logmapping operator
*
* Adaptive logarithmic mapping for displaying high contrast
* scenes.
* F. Drago, K. Myszkowski, T. Annen, and N. Chiba. In Eurographics 2003.
*
* This file is a part of LuminanceHDR package, based on pfstmo.
* ----------------------------------------------------------------------
* Copyright (C) 2003,2004 Grzegorz Krawczyk
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* ----------------------------------------------------------------------
*
* @author Grzegorz Krawczyk, <krawczyk@mpi-sb.mpg.de>
*
* $Id: tmo_drago03.cpp,v 1.4 2008/11/04 23:43:08 rafm Exp $
*/
#include "tmo_drago03.h"
#include <cassert>
#include <cmath>
#include "Libpfs/utils/msec_timer.h"
#include "Libpfs/frame.h"
#include "Libpfs/progress.h"
#include "TonemappingOperators/pfstmo.h"
#include "../../opthelper.h"
#include "../../sleef.c"
namespace {
/*
inline float biasFunc(float b, float x) {
return b == 0.f ? xexpf( x * xlogf(b)) : 1.f;
}
*/
const float LOG05 = -0.693147f; // log(0.5)
}
void calculateLuminance(unsigned int width, unsigned int height, const float *Y,
float &avLum, float &maxLum) {
avLum = 0.0f;
maxLum = 0.0f;
int size = width * height;
#ifdef __SSE2__
vfloat maxLumv = ZEROV;
vfloat avLumv = ZEROV;
#endif // __SSE2__
#pragma omp parallel
{
float eps = 1e-4f;
float avLumThr = 0.f;
float maxLumThr = 0.f;
#ifdef __SSE2__
vfloat epsv = F2V(eps);
vfloat maxLumThrv = ZEROV;
vfloat avLumThrv = ZEROV;
#endif // __SSE2__
#pragma omp for nowait
for(int i = 0; i < height; ++i) {
size_t j = 0;
#ifdef __SSE2__
for (; j < width - 3; j+=4) {
vfloat Ywv = LVFU(Y[i * width + j]);
avLumThrv += xlogf(Ywv + epsv);
maxLumThrv = vmaxf(Ywv, maxLumThrv);
}
#endif
for (; j < width; j++) {
float Yw = Y[i * width + j];
avLumThr += xlogf(Yw + eps);
maxLumThr = std::max(Yw, maxLumThr);
}
}
#pragma omp critical
{
#ifdef __SSE2__
avLumv += avLumThrv;
maxLumv = vmaxf(maxLumv, maxLumThrv);
#endif
avLum += avLumThr;
maxLum = std::max(maxLum, maxLumThr);
}
}
#ifdef __SSE2__
avLum += vhadd(avLumv);
maxLum = std::max(maxLum, vhmax(maxLumv));
#endif
avLum = exp(avLum / size);
}
void tmo_drago03(const pfs::Array2Df &Y, pfs::Array2Df &L, float maxLum,
float avLum, float bias, pfs::Progress &ph) {
assert(Y.getRows() == L.getRows());
assert(Y.getCols() == L.getCols());
#ifdef TIMER_PROFILING
msec_timer stop_watch;
stop_watch.start();
#endif
// normalize maximum luminance by average luminance
maxLum /= avLum;
float divider = std::log10(maxLum + 1.0f);
float biasP = (log(bias) / LOG05);
float logmaxLum = log(maxLum);
// Normal tone mapping of every pixel
#pragma omp parallel
{
int yEnd = Y.getRows();
int xEnd = Y.getCols();
#ifdef __SSE2__
vfloat avLumv = F2V(avLum);
vfloat onev = F2V(1.f);
vfloat twov = F2V(2.f);
vfloat eightv = F2V(8.f);
vfloat biasPv = F2V(biasP);
vfloat logmaxLumv = F2V(logmaxLum);
vfloat dividerv = F2V(divider);
#endif
#pragma omp for
for (int y = 0; y < yEnd; y++) {
int x = 0;
#ifdef __SSE2__
for (; x < xEnd - 3; x+=4) {
vfloat Ywv = LVFU(Y(x, y)) / avLumv;
vfloat interpolv = xlogf( twov + eightv * xexpf(biasPv * (xlogf(Ywv) - logmaxLumv)));
STVFU(L(x, y), xlogf(Ywv + onev) / (interpolv * dividerv)); // avoid loss of precision
}
#endif
for (; x < xEnd; x++) {
float Yw = Y(x, y) / avLum;
float interpol = xlogf( 2.f + 8.f * xexpf(biasP * (xlogf(Yw) - logmaxLum)));
L(x, y) = xlogf(Yw + 1.f) / (interpol * divider); // avoid loss of precision
assert(!isnan(L(x, y)));
}
}
}
#ifdef TIMER_PROFILING
stop_watch.stop_and_update();
cout << endl;
cout << "tmo_drago03 = " << stop_watch.get_time() << " msec" << endl;
#endif
}
LuminanceHDR-master\src\Libpfs\manip\saturation.cpp
cpp
/*
* This file is a part of Luminance HDR package.
* ----------------------------------------------------------------------
* Copyright (C) 2003,2004 Rafal Mantiuk and Grzegorz Krawczyk
* Copyright (C) 2012 Davide Anastasia
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* ----------------------------------------------------------------------
*
*/
//! \brief Apply saturation correction the the pfs stream
//! \author Rafal Mantiuk, <mantiuk@mpi-sb.mpg.de>
#include "saturation.h"
#include <cassert>
#include <cmath>
#include <iostream>
#include "Libpfs/array2d.h"
#include "Libpfs/colorspace/colorspace.h"
#include "Libpfs/colorspace/saturation.h"
#include "Libpfs/utils/transform.h"
#include "Libpfs/frame.h"
#include "Libpfs/utils/msec_timer.h"
using namespace pfs;
using namespace colorspace;
using namespace utils;
namespace pfs {
void applySaturation(pfs::Frame *frame, float multiplier) {
if (multiplier == 1.0f) return;
pfs::Channel *X, *Y, *Z;
frame->getXYZChannels(X, Y, Z);
applySaturation(X, Y, Z, multiplier);
}
void applySaturation(pfs::Array2Df *R, pfs::Array2Df *G, pfs::Array2Df *B,
const float multiplier) {
#ifdef TIMER_PROFILING
msec_timer f_timer;
f_timer.start();
#endif
utils::transform(R->begin(), R->end(), G->begin(), B->begin(), R->begin(), G->begin(), B->begin(), ChangeSaturation(multiplier));
#ifdef TIMER_PROFILING
f_timer.stop_and_update();
std::cout << "applySaturation() = " << f_timer.get_time() << " msec"
<< std::endl;
#endif
}
}
LuminanceHDR-master\src\Libpfs\colorspace\saturation.hxx
cpp
/*
* This file is a part of Luminance HDR package.
* ----------------------------------------------------------------------
* Copyright (C) 2013 Davide Anastasia
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* ----------------------------------------------------------------------
*/
//! \brief Saturation enhancement function
//! \author Franco Comida <francocomida@users.sourceforge.net>
#ifndef PFS_COLORSPACE_SATURATION_HXX
#define PFS_COLORSPACE_SATURATION_HXX
#include <Common/CommonFunctions.h>
#include <Libpfs/colorspace/saturation.h>
namespace pfs {
namespace colorspace {
inline void ChangeSaturation::operator()(float i1, float i2, float i3, float &o1,
float &o2, float &o3) const {
float h, s, l;
rgb2hsl(i1, i2, i3, h, s, l);
hsl2rgb(h, multiplier*s, l, o1, o2, o3);
}
}
}
#endif // PFS_COLORSPACE_SATURATION_HXX
Adaptive Logarithmic Mapping For Displaying High Contrast Scenes
公式

参考