比较阅读理解opencv 和 LuminanceHDR中 色调映射Drago算法

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

公式

参考

https://zhuanlan.zhihu.com/p/696636187

从原理到代码:深入理解OpenCV中cv::TonemapDrago色调映射的底层逻辑

相关推荐
自我意识的多元宇宙1 小时前
【数据结构】图----图的应用(拓扑排序)
数据结构·算法
itzixiao2 小时前
L1-055 谁是赢家(10 分)[java][python]
java·python·算法
hoiii1872 小时前
基于协方差矩阵的车辆检测(Matlab实现)
计算机视觉·matlab·矩阵
ghie90902 小时前
运用强跟踪无迹卡尔曼滤波来实现捷联惯导的初始对准
算法
菜菜的顾清寒2 小时前
力扣HOT100(21)相交链表
算法·leetcode·链表
七颗糖很甜2 小时前
开源雷达NEXRAD Level 3 数据完整获取与 Python 处理教程
大数据·python·算法
六bring个六2 小时前
opencv读取图片和视频
opencv·计算机视觉
JXNL@2 小时前
TDK DPX105950DT 射频双工器全解析:从原理、参数到应用设计
算法
池塘的蜗牛2 小时前
FMCW(2)-速度
算法