Merge pull request #1474 from f-morozov:for_pr

This commit is contained in:
Roman Donchenko
2013-10-02 11:49:51 +04:00
committed by OpenCV Buildbot
32 changed files with 3582 additions and 425 deletions

View File

@@ -0,0 +1,377 @@
HDR imaging
=============
.. highlight:: cpp
This section describes high dynamic range imaging algorithms namely tonemapping, exposure alignment, camera calibration with multiple exposures and exposure fusion.
Tonemap
---------------------------
.. ocv:class:: Tonemap : public Algorithm
Base class for tonemapping algorithms - tools that are used to map HDR image to 8-bit range.
Tonemap::process
---------------------------
Tonemaps image
.. ocv:function:: void Tonemap::process(InputArray src, OutputArray dst)
:param src: source image - 32-bit 3-channel Mat
:param dst: destination image - 32-bit 3-channel Mat with values in [0, 1] range
createTonemap
---------------------------
Creates simple linear mapper with gamma correction
.. ocv:function:: Ptr<Tonemap> createTonemap(float gamma = 1.0f)
:param gamma: positive value for gamma correction. Gamma value of 1.0 implies no correction, gamma equal to 2.2f is suitable for most displays.
Generally gamma > 1 brightens the image and gamma < 1 darkens it.
TonemapDrago
---------------------------
.. ocv:class:: TonemapDrago : public Tonemap
Adaptive logarithmic mapping is a fast global tonemapping algorithm that scales the image in logarithmic domain.
Since it's a global operator the same function is applied to all the pixels, it is controlled by the bias parameter.
Optional saturation enhancement is possible as described in [FL02]_.
For more information see [DM03]_.
createTonemapDrago
---------------------------
Creates TonemapDrago object
.. ocv:function:: Ptr<TonemapDrago> createTonemapDrago(float gamma = 1.0f, float saturation = 1.0f, float bias = 0.85f)
:param gamma: gamma value for gamma correction. See :ocv:func:`createTonemap`
:param saturation: positive saturation enhancement value. 1.0 preserves saturation, values greater than 1 increase saturation and values less than 1 decrease it.
:param bias: value for bias function in [0, 1] range. Values from 0.7 to 0.9 usually give best results, default value is 0.85.
TonemapDurand
---------------------------
.. ocv:class:: TonemapDurand : public Tonemap
This algorithm decomposes image into two layers: base layer and detail layer using bilateral filter and compresses contrast of the base layer thus preserving all the details.
This implementation uses regular bilateral filter from opencv.
Saturation enhancement is possible as in ocv:class:`TonemapDrago`.
For more information see [DD02]_.
createTonemapDurand
---------------------------
Creates TonemapDurand object
.. ocv:function:: Ptr<TonemapDurand> createTonemapDurand(float gamma = 1.0f, float contrast = 4.0f, float saturation = 1.0f, float sigma_space = 2.0f, float sigma_color = 2.0f)
:param gamma: gamma value for gamma correction. See :ocv:func:`createTonemap`
:param contrast: resulting contrast on logarithmic scale, i. e. log(max / min), where max and min are maximum and minimum luminance values of the resulting image.
:param saturation: saturation enhancement value. See :ocv:func:`createTonemapDrago`
:param sigma_space: bilateral filter sigma in color space
:param sigma_color: bilateral filter sigma in coordinate space
TonemapReinhard
---------------------------
.. ocv:class:: TonemapReinhard : public Tonemap
This is a global tonemapping operator that models human visual system.
Mapping function is controlled by adaptation parameter, that is computed using light adaptation and color adaptation.
For more information see [RD05]_.
createTonemapReinhard
---------------------------
Creates TonemapReinhard object
.. ocv:function:: Ptr<TonemapReinhard> createTonemapReinhard(float gamma = 1.0f, float intensity = 0.0f, float light_adapt = 1.0f, float color_adapt = 0.0f)
:param gamma: gamma value for gamma correction. See :ocv:func:`createTonemap`
:param intensity: result intensity in [-8, 8] range. Greater intensity produces brighter results.
:param light_adapt: light adaptation in [0, 1] range. If 1 adaptation is based only on pixel value, if 0 it's global, otherwise it's a weighted mean of this two cases.
:param color_adapt: chromatic adaptation in [0, 1] range. If 1 channels are treated independently, if 0 adaptation level is the same for each channel.
TonemapMantiuk
---------------------------
.. ocv:class:: TonemapMantiuk : public Tonemap
This algorithm transforms image to contrast using gradients on all levels of gaussian pyramid, transforms contrast values to HVS response and scales the response.
After this the image is reconstructed from new contrast values.
For more information see [MM06]_.
createTonemapMantiuk
---------------------------
Creates TonemapMantiuk object
.. ocv:function:: Ptr<TonemapMantiuk> createTonemapMantiuk(float gamma = 1.0f, float scale = 0.7f, float saturation = 1.0f)
:param gamma: gamma value for gamma correction. See :ocv:func:`createTonemap`
:param scale: contrast scale factor. HVS response is multiplied by this parameter, thus compressing dynamic range. Values from 0.6 to 0.9 produce best results.
:param saturation: saturation enhancement value. See :ocv:func:`createTonemapDrago`
AlignExposures
---------------------------
.. ocv:class:: AlignExposures : public Algorithm
The base class for algorithms that align images of the same scene with different exposures
AlignExposures::process
---------------------------
Aligns images
.. ocv:function:: void AlignExposures::process(InputArrayOfArrays src, std::vector<Mat>& dst, InputArray times, InputArray response)
:param src: vector of input images
:param dst: vector of aligned images
:param times: vector of exposure time values for each image
:param response: 256x1 matrix with inverse camera response function for each pixel value, it should have the same number of channels as images.
AlignMTB
---------------------------
.. ocv:class:: AlignMTB : public AlignExposures
This algorithm converts images to median threshold bitmaps (1 for pixels brighter than median luminance and 0 otherwise) and than aligns the resulting bitmaps using bit operations.
It is invariant to exposure, so exposure values and camera response are not necessary.
In this implementation new image regions are filled with zeros.
For more information see [GW03]_.
AlignMTB::process
---------------------------
Short version of process, that doesn't take extra arguments.
.. ocv:function:: void AlignMTB::process(InputArrayOfArrays src, std::vector<Mat>& dst)
:param src: vector of input images
:param dst: vector of aligned images
AlignMTB::calculateShift
---------------------------
Calculates shift between two images, i. e. how to shift the second image to correspond it with the first.
.. ocv:function:: Point AlignMTB::calculateShift(InputArray img0, InputArray img1)
:param img0: first image
:param img1: second image
AlignMTB::shiftMat
---------------------------
Helper function, that shift Mat filling new regions with zeros.
.. ocv:function:: void AlignMTB::shiftMat(InputArray src, OutputArray dst, const Point shift)
:param src: input image
:param dst: result image
:param shift: shift value
AlignMTB::computeBitmaps
---------------------------
Computes median threshold and exclude bitmaps of given image.
.. ocv:function:: void AlignMTB::computeBitmaps(InputArray img, OutputArray tb, OutputArray eb)
:param img: input image
:param tb: median threshold bitmap
:param eb: exclude bitmap
createAlignMTB
---------------------------
Creates AlignMTB object
.. ocv:function:: Ptr<AlignMTB> createAlignMTB(int max_bits = 6, int exclude_range = 4, bool cut = true)
:param max_bits: logarithm to the base 2 of maximal shift in each dimension. Values of 5 and 6 are usually good enough (31 and 63 pixels shift respectively).
:param exclude_range: range for exclusion bitmap that is constructed to suppress noise around the median value.
:param cut: if true cuts images, otherwise fills the new regions with zeros.
CalibrateCRF
---------------------------
.. ocv:class:: CalibrateCRF : public Algorithm
The base class for camera response calibration algorithms.
CalibrateCRF::process
---------------------------
Recovers inverse camera response.
.. ocv:function:: void CalibrateCRF::process(InputArrayOfArrays src, OutputArray dst, InputArray times)
:param src: vector of input images
:param dst: 256x1 matrix with inverse camera response function
:param times: vector of exposure time values for each image
CalibrateDebevec
---------------------------
.. ocv:class:: CalibrateDebevec : public CalibrateCRF
Inverse camera response function is extracted for each brightness value by minimizing an objective function as linear system.
Objective function is constructed using pixel values on the same position in all images, extra term is added to make the result smoother.
For more information see [DM97]_.
createCalibrateDebevec
---------------------------
Creates CalibrateDebevec object
.. ocv:function:: createCalibrateDebevec(int samples = 70, float lambda = 10.0f, bool random = false)
:param samples: number of pixel locations to use
:param lambda: smoothness term weight. Greater values produce smoother results, but can alter the response.
:param random: if true sample pixel locations are chosen at random, otherwise the form a rectangular grid.
CalibrateRobertson
---------------------------
.. ocv:class:: CalibrateRobertson : public CalibrateCRF
Inverse camera response function is extracted for each brightness value by minimizing an objective function as linear system.
This algorithm uses all image pixels.
For more information see [RB99]_.
createCalibrateRobertson
---------------------------
Creates CalibrateRobertson object
.. ocv:function:: createCalibrateRobertson(int max_iter = 30, float threshold = 0.01f)
:param max_iter: maximal number of Gauss-Seidel solver iterations.
:param threshold: target difference between results of two successive steps of the minimization.
MergeExposures
---------------------------
.. ocv:class:: MergeExposures : public Algorithm
The base class algorithms that can merge exposure sequence to a single image.
MergeExposures::process
---------------------------
Merges images.
.. ocv:function:: void MergeExposures::process(InputArrayOfArrays src, OutputArray dst, InputArray times, InputArray response)
:param src: vector of input images
:param dst: result image
:param times: vector of exposure time values for each image
:param response: 256x1 matrix with inverse camera response function for each pixel value, it should have the same number of channels as images.
MergeDebevec
---------------------------
.. ocv:class:: MergeDebevec : public MergeExposures
The resulting HDR image is calculated as weighted average of the exposures considering exposure values and camera response.
For more information see [DM97]_.
createMergeDebevec
---------------------------
Creates MergeDebevec object
.. ocv:function:: Ptr<MergeDebevec> createMergeDebevec()
MergeMertens
---------------------------
.. ocv:class:: MergeMertens : public MergeExposures
Pixels are weighted using contrast, saturation and well-exposedness measures, than images are combined using laplacian pyramids.
The resulting image weight is constructed as weighted average of contrast, saturation and well-exposedness measures.
The resulting image doesn't require tonemapping and can be converted to 8-bit image by multiplying by 255, but it's recommended to apply gamma correction and/or linear tonemapping.
For more information see [MK07]_.
MergeMertens::process
---------------------------
Short version of process, that doesn't take extra arguments.
.. ocv:function:: void MergeMertens::process(InputArrayOfArrays src, OutputArray dst)
:param src: vector of input images
:param dst: result image
createMergeMertens
---------------------------
Creates MergeMertens object
.. ocv:function:: Ptr<MergeMertens> createMergeMertens(float contrast_weight = 1.0f, float saturation_weight = 1.0f, float exposure_weight = 0.0f)
:param contrast_weight: contrast measure weight. See :ocv:class:`MergeMertens`.
:param saturation_weight: saturation measure weight
:param exposure_weight: well-exposedness measure weight
MergeRobertson
---------------------------
.. ocv:class:: MergeRobertson : public MergeExposures
The resulting HDR image is calculated as weighted average of the exposures considering exposure values and camera response.
For more information see [RB99]_.
createMergeRobertson
---------------------------
Creates MergeRobertson object
.. ocv:function:: Ptr<MergeRobertson> createMergeRobertson()
References
==========
.. [DM03] F. Drago, K. Myszkowski, T. Annen, N. Chiba, "Adaptive Logarithmic Mapping For Displaying High Contrast Scenes", Computer Graphics Forum, 2003, 22, 419 - 426.
.. [FL02] R. Fattal, D. Lischinski, M. Werman, "Gradient Domain High Dynamic Range Compression", Proceedings OF ACM SIGGRAPH, 2002, 249 - 256.
.. [DD02] F. Durand and Julie Dorsey, "Fast Bilateral Filtering for the Display of High-Dynamic-Range Images", ACM Transactions on Graphics, 2002, 21, 3, 257 - 266.
.. [RD05] E. Reinhard, K. Devlin, "Dynamic Range Reduction Inspired by Photoreceptor Physiology", IEEE Transactions on Visualization and Computer Graphics, 2005, 11, 13 - 24.
.. [MM06] R. Mantiuk, K. Myszkowski, H.-P. Seidel, "Perceptual Framework for Contrast Processing of High Dynamic Range Images", ACM Transactions on Applied Perception, 2006, 3, 3, 286 - 308.
.. [GW03] G. Ward, "Fast, Robust Image Registration for Compositing High Dynamic Range Photographs from Handheld Exposures", Journal of Graphics Tools, 2003, 8, 17 - 30.
.. [DM97] P. Debevec, J. Malik, "Recovering High Dynamic Range Radiance Maps from Photographs", Proceedings OF ACM SIGGRAPH, 1997, 369 - 378.
.. [MK07] T. Mertens, J. Kautz, F. Van Reeth, "Exposure Fusion", Proceedings of the 15th Pacific Conference on Computer Graphics and Applications, 2007, 382 - 390.
.. [RB99] M. Robertson , S. Borman , R. Stevenson , "Dynamic range improvement through multiple exposures ", Proceedings of the Int. Conf. on Image Processing , 1999, 159 - 163.

View File

@@ -9,3 +9,4 @@ photo. Computational Photography
inpainting
denoising
hdr_imaging

View File

@@ -80,6 +80,214 @@ CV_EXPORTS_W void fastNlMeansDenoisingColoredMulti( InputArrayOfArrays srcImgs,
float h = 3, float hColor = 3,
int templateWindowSize = 7, int searchWindowSize = 21);
enum { LDR_SIZE = 256 };
class CV_EXPORTS_W Tonemap : public Algorithm
{
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_EXPORTS_W Ptr<Tonemap> createTonemap(float gamma = 1.0f);
// "Adaptive Logarithmic Mapping For Displaying HighContrast Scenes", Drago et al., 2003
class CV_EXPORTS_W TonemapDrago : public Tonemap
{
public:
CV_WRAP virtual float getSaturation() const = 0;
CV_WRAP virtual void setSaturation(float saturation) = 0;
CV_WRAP virtual float getBias() const = 0;
CV_WRAP virtual void setBias(float bias) = 0;
};
CV_EXPORTS_W Ptr<TonemapDrago> createTonemapDrago(float gamma = 1.0f, float saturation = 1.0f, float bias = 0.85f);
// "Fast Bilateral Filtering for the Display of High-Dynamic-Range Images", Durand, Dorsey, 2002
class CV_EXPORTS_W TonemapDurand : public Tonemap
{
public:
CV_WRAP virtual float getSaturation() const = 0;
CV_WRAP virtual void setSaturation(float saturation) = 0;
CV_WRAP virtual float getContrast() const = 0;
CV_WRAP virtual void setContrast(float contrast) = 0;
CV_WRAP virtual float getSigmaSpace() const = 0;
CV_WRAP virtual void setSigmaSpace(float sigma_space) = 0;
CV_WRAP virtual float getSigmaColor() const = 0;
CV_WRAP virtual void setSigmaColor(float sigma_color) = 0;
};
CV_EXPORTS_W Ptr<TonemapDurand>
createTonemapDurand(float gamma = 1.0f, float contrast = 4.0f, float saturation = 1.0f, float sigma_space = 2.0f, float sigma_color = 2.0f);
// "Dynamic Range Reduction Inspired by Photoreceptor Physiology", Reinhard, Devlin, 2005
class CV_EXPORTS_W TonemapReinhard : public Tonemap
{
public:
CV_WRAP virtual float getIntensity() const = 0;
CV_WRAP virtual void setIntensity(float intensity) = 0;
CV_WRAP virtual float getLightAdaptation() const = 0;
CV_WRAP virtual void setLightAdaptation(float light_adapt) = 0;
CV_WRAP virtual float getColorAdaptation() const = 0;
CV_WRAP virtual void setColorAdaptation(float color_adapt) = 0;
};
CV_EXPORTS_W Ptr<TonemapReinhard>
createTonemapReinhard(float gamma = 1.0f, float intensity = 0.0f, float light_adapt = 1.0f, float color_adapt = 0.0f);
// "Perceptual Framework for Contrast Processing of High Dynamic Range Images", Mantiuk et al., 2006
class CV_EXPORTS_W TonemapMantiuk : public Tonemap
{
public:
CV_WRAP virtual float getScale() const = 0;
CV_WRAP virtual void setScale(float scale) = 0;
CV_WRAP virtual float getSaturation() const = 0;
CV_WRAP virtual void setSaturation(float saturation) = 0;
};
CV_EXPORTS_W Ptr<TonemapMantiuk>
createTonemapMantiuk(float gamma = 1.0f, float scale = 0.7f, float saturation = 1.0f);
class CV_EXPORTS_W AlignExposures : public Algorithm
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, std::vector<Mat>& dst,
InputArray times, InputArray response) = 0;
};
// "Fast, Robust Image Registration for Compositing High Dynamic Range Photographs from Handheld Exposures", Ward, 2003
class CV_EXPORTS_W AlignMTB : public AlignExposures
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, std::vector<Mat>& dst,
InputArray times, InputArray response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, std::vector<Mat>& dst) = 0;
CV_WRAP virtual Point calculateShift(InputArray img0, InputArray img1) = 0;
CV_WRAP virtual void shiftMat(InputArray src, OutputArray dst, const Point shift) = 0;
CV_WRAP virtual void computeBitmaps(InputArray img, OutputArray tb, OutputArray eb) = 0;
CV_WRAP virtual int getMaxBits() const = 0;
CV_WRAP virtual void setMaxBits(int max_bits) = 0;
CV_WRAP virtual int getExcludeRange() const = 0;
CV_WRAP virtual void setExcludeRange(int exclude_range) = 0;
CV_WRAP virtual bool getCut() const = 0;
CV_WRAP virtual void setCut(bool value) = 0;
};
CV_EXPORTS_W Ptr<AlignMTB> createAlignMTB(int max_bits = 6, int exclude_range = 4, bool cut = true);
class CV_EXPORTS_W CalibrateCRF : public Algorithm
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, InputArray times) = 0;
};
// "Recovering High Dynamic Range Radiance Maps from Photographs", Debevec, Malik, 1997
class CV_EXPORTS_W CalibrateDebevec : public CalibrateCRF
{
public:
CV_WRAP virtual float getLambda() const = 0;
CV_WRAP virtual void setLambda(float lambda) = 0;
CV_WRAP virtual int getSamples() const = 0;
CV_WRAP virtual void setSamples(int samples) = 0;
CV_WRAP virtual bool getRandom() const = 0;
CV_WRAP virtual void setRandom(bool random) = 0;
};
CV_EXPORTS_W Ptr<CalibrateDebevec> createCalibrateDebevec(int samples = 70, float lambda = 10.0f, bool random = false);
// "Dynamic range improvement through multiple exposures", Robertson et al., 1999
class CV_EXPORTS_W CalibrateRobertson : public CalibrateCRF
{
public:
CV_WRAP virtual int getMaxIter() const = 0;
CV_WRAP virtual void setMaxIter(int max_iter) = 0;
CV_WRAP virtual float getThreshold() const = 0;
CV_WRAP virtual void setThreshold(float threshold) = 0;
CV_WRAP virtual Mat getRadiance() const = 0;
};
CV_EXPORTS_W Ptr<CalibrateRobertson> createCalibrateRobertson(int max_iter = 30, float threshold = 0.01f);
class CV_EXPORTS_W MergeExposures : public Algorithm
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
InputArray times, InputArray response) = 0;
};
// "Recovering High Dynamic Range Radiance Maps from Photographs", Debevec, Malik, 1997
class CV_EXPORTS_W MergeDebevec : public MergeExposures
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
InputArray times, InputArray response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, InputArray times) = 0;
};
CV_EXPORTS_W Ptr<MergeDebevec> createMergeDebevec();
// "Exposure Fusion", Mertens et al., 2007
class CV_EXPORTS_W MergeMertens : public MergeExposures
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
InputArray times, InputArray response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst) = 0;
CV_WRAP virtual float getContrastWeight() const = 0;
CV_WRAP virtual void setContrastWeight(float contrast_weiht) = 0;
CV_WRAP virtual float getSaturationWeight() const = 0;
CV_WRAP virtual void setSaturationWeight(float saturation_weight) = 0;
CV_WRAP virtual float getExposureWeight() const = 0;
CV_WRAP virtual void setExposureWeight(float exposure_weight) = 0;
};
CV_EXPORTS_W Ptr<MergeMertens>
createMergeMertens(float contrast_weight = 1.0f, float saturation_weight = 1.0f, float exposure_weight = 0.0f);
// "Dynamic range improvement through multiple exposures", Robertson et al., 1999
class CV_EXPORTS_W MergeRobertson : public MergeExposures
{
public:
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
InputArray times, InputArray response) = 0;
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, InputArray times) = 0;
};
CV_EXPORTS_W Ptr<MergeRobertson> createMergeRobertson();
} // cv
#endif

270
modules/photo/src/align.cpp Normal file
View File

@@ -0,0 +1,270 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
#include "opencv2/photo.hpp"
#include "opencv2/imgproc.hpp"
#include "hdr_common.hpp"
namespace cv
{
class AlignMTBImpl : public AlignMTB
{
public:
AlignMTBImpl(int _max_bits, int _exclude_range, bool _cut) :
name("AlignMTB"),
max_bits(_max_bits),
exclude_range(_exclude_range),
cut(_cut)
{
}
void process(InputArrayOfArrays src, std::vector<Mat>& dst,
InputArray, InputArray)
{
process(src, dst);
}
void process(InputArrayOfArrays _src, std::vector<Mat>& dst)
{
std::vector<Mat> src;
_src.getMatVector(src);
checkImageDimensions(src);
dst.resize(src.size());
size_t pivot = src.size() / 2;
dst[pivot] = src[pivot];
Mat gray_base;
cvtColor(src[pivot], gray_base, COLOR_RGB2GRAY);
std::vector<Point> shifts;
for(size_t i = 0; i < src.size(); i++) {
if(i == pivot) {
shifts.push_back(Point(0, 0));
continue;
}
Mat gray;
cvtColor(src[i], gray, COLOR_RGB2GRAY);
Point shift = calculateShift(gray_base, gray);
shifts.push_back(shift);
shiftMat(src[i], dst[i], shift);
}
if(cut) {
Point max(0, 0), min(0, 0);
for(size_t i = 0; i < shifts.size(); i++) {
if(shifts[i].x > max.x) {
max.x = shifts[i].x;
}
if(shifts[i].y > max.y) {
max.y = shifts[i].y;
}
if(shifts[i].x < min.x) {
min.x = shifts[i].x;
}
if(shifts[i].y < min.y) {
min.y = shifts[i].y;
}
}
Point size = dst[0].size();
for(size_t i = 0; i < dst.size(); i++) {
dst[i] = dst[i](Rect(max, min + size));
}
}
}
Point calculateShift(InputArray _img0, InputArray _img1)
{
Mat img0 = _img0.getMat();
Mat img1 = _img1.getMat();
CV_Assert(img0.channels() == 1 && img0.type() == img1.type());
CV_Assert(img0.size() == img0.size());
int maxlevel = static_cast<int>(log((double)max(img0.rows, img0.cols)) / log(2.0)) - 1;
maxlevel = min(maxlevel, max_bits - 1);
std::vector<Mat> pyr0;
std::vector<Mat> pyr1;
buildPyr(img0, pyr0, maxlevel);
buildPyr(img1, pyr1, maxlevel);
Point shift(0, 0);
for(int level = maxlevel; level >= 0; level--) {
shift *= 2;
Mat tb1, tb2, eb1, eb2;
computeBitmaps(pyr0[level], tb1, eb1);
computeBitmaps(pyr1[level], tb2, eb2);
int min_err = pyr0[level].total();
Point new_shift(shift);
for(int i = -1; i <= 1; i++) {
for(int j = -1; j <= 1; j++) {
Point test_shift = shift + Point(i, j);
Mat shifted_tb2, shifted_eb2, diff;
shiftMat(tb2, shifted_tb2, test_shift);
shiftMat(eb2, shifted_eb2, test_shift);
bitwise_xor(tb1, shifted_tb2, diff);
bitwise_and(diff, eb1, diff);
bitwise_and(diff, shifted_eb2, diff);
int err = countNonZero(diff);
if(err < min_err) {
new_shift = test_shift;
min_err = err;
}
}
}
shift = new_shift;
}
return shift;
}
void shiftMat(InputArray _src, OutputArray _dst, const Point shift)
{
Mat src = _src.getMat();
_dst.create(src.size(), src.type());
Mat dst = _dst.getMat();
Mat res = Mat::zeros(src.size(), src.type());
int width = src.cols - abs(shift.x);
int height = src.rows - abs(shift.y);
Rect dst_rect(max(shift.x, 0), max(shift.y, 0), width, height);
Rect src_rect(max(-shift.x, 0), max(-shift.y, 0), width, height);
src(src_rect).copyTo(res(dst_rect));
res.copyTo(dst);
}
int getMaxBits() const { return max_bits; }
void setMaxBits(int val) { max_bits = val; }
int getExcludeRange() const { return exclude_range; }
void setExcludeRange(int val) { exclude_range = val; }
bool getCut() const { return cut; }
void setCut(bool val) { cut = val; }
void write(FileStorage& fs) const
{
fs << "name" << name
<< "max_bits" << max_bits
<< "exclude_range" << exclude_range
<< "cut" << static_cast<int>(cut);
}
void read(const FileNode& fn)
{
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name);
max_bits = fn["max_bits"];
exclude_range = fn["exclude_range"];
int cut_val = fn["cut"];
cut = (cut_val != 0);
}
void computeBitmaps(InputArray _img, OutputArray _tb, OutputArray _eb)
{
Mat img = _img.getMat();
_tb.create(img.size(), CV_8U);
_eb.create(img.size(), CV_8U);
Mat tb = _tb.getMat(), eb = _eb.getMat();
int median = getMedian(img);
compare(img, median, tb, CMP_GT);
compare(abs(img - median), exclude_range, eb, CMP_GT);
}
protected:
String name;
int max_bits, exclude_range;
bool cut;
void downsample(Mat& src, Mat& dst)
{
dst = Mat(src.rows / 2, src.cols / 2, CV_8UC1);
int offset = src.cols * 2;
uchar *src_ptr = src.ptr();
uchar *dst_ptr = dst.ptr();
for(int y = 0; y < dst.rows; y ++) {
uchar *ptr = src_ptr;
for(int x = 0; x < dst.cols; x++) {
dst_ptr[0] = ptr[0];
dst_ptr++;
ptr += 2;
}
src_ptr += offset;
}
}
void buildPyr(Mat& img, std::vector<Mat>& pyr, int maxlevel)
{
pyr.resize(maxlevel + 1);
pyr[0] = img.clone();
for(int level = 0; level < maxlevel; level++) {
downsample(pyr[level], pyr[level + 1]);
}
}
int getMedian(Mat& img)
{
int channels = 0;
Mat hist;
int hist_size = LDR_SIZE;
float range[] = {0, LDR_SIZE} ;
const float* ranges[] = {range};
calcHist(&img, 1, &channels, Mat(), hist, 1, &hist_size, ranges);
float *ptr = hist.ptr<float>();
int median = 0, sum = 0;
int thresh = img.total() / 2;
while(sum < thresh && median < LDR_SIZE) {
sum += static_cast<int>(ptr[median]);
median++;
}
return median;
}
};
Ptr<AlignMTB> createAlignMTB(int max_bits, int exclude_range, bool cut)
{
return makePtr<AlignMTBImpl>(max_bits, exclude_range, cut);
}
}

View File

@@ -0,0 +1,276 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
#include "opencv2/photo.hpp"
#include "opencv2/imgproc.hpp"
//#include "opencv2/highgui.hpp"
#include "hdr_common.hpp"
namespace cv
{
class CalibrateDebevecImpl : public CalibrateDebevec
{
public:
CalibrateDebevecImpl(int _samples, float _lambda, bool _random) :
name("CalibrateDebevec"),
samples(_samples),
lambda(_lambda),
random(_random),
w(tringleWeights())
{
}
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times)
{
std::vector<Mat> images;
src.getMatVector(images);
Mat times = _times.getMat();
CV_Assert(images.size() == times.total());
checkImageDimensions(images);
CV_Assert(images[0].depth() == CV_8U);
int channels = images[0].channels();
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
dst.create(LDR_SIZE, 1, CV_32FCC);
Mat result = dst.getMat();
std::vector<Point> sample_points;
if(random) {
for(int i = 0; i < samples; i++) {
sample_points.push_back(Point(rand() % images[0].cols, rand() % images[0].rows));
}
} else {
int x_points = static_cast<int>(sqrt(static_cast<double>(samples) * images[0].cols / images[0].rows));
int y_points = samples / x_points;
int step_x = images[0].cols / x_points;
int step_y = images[0].rows / y_points;
for(int i = 0, x = step_x / 2; i < x_points; i++, x += step_x) {
for(int j = 0, y = step_y; j < y_points; j++, y += step_y) {
sample_points.push_back(Point(x, y));
}
}
}
std::vector<Mat> result_split(channels);
for(int channel = 0; channel < channels; channel++) {
Mat A = Mat::zeros(sample_points.size() * images.size() + LDR_SIZE + 1, LDR_SIZE + sample_points.size(), CV_32F);
Mat B = Mat::zeros(A.rows, 1, CV_32F);
int eq = 0;
for(size_t i = 0; i < sample_points.size(); i++) {
for(size_t j = 0; j < images.size(); j++) {
int val = images[j].ptr()[3*(sample_points[i].y * images[j].cols + sample_points[j].x) + channel];
A.at<float>(eq, val) = w.at<float>(val);
A.at<float>(eq, LDR_SIZE + i) = -w.at<float>(val);
B.at<float>(eq, 0) = w.at<float>(val) * log(times.at<float>(j));
eq++;
}
}
A.at<float>(eq, LDR_SIZE / 2) = 1;
eq++;
for(int i = 0; i < 254; i++) {
A.at<float>(eq, i) = lambda * w.at<float>(i + 1);
A.at<float>(eq, i + 1) = -2 * lambda * w.at<float>(i + 1);
A.at<float>(eq, i + 2) = lambda * w.at<float>(i + 1);
eq++;
}
Mat solution;
solve(A, B, solution, DECOMP_SVD);
solution.rowRange(0, LDR_SIZE).copyTo(result_split[channel]);
}
merge(result_split, result);
exp(result, result);
}
int getSamples() const { return samples; }
void setSamples(int val) { samples = val; }
float getLambda() const { return lambda; }
void setLambda(float val) { lambda = val; }
bool getRandom() const { return random; }
void setRandom(bool val) { random = val; }
void write(FileStorage& fs) const
{
fs << "name" << name
<< "samples" << samples
<< "lambda" << lambda
<< "random" << static_cast<int>(random);
}
void read(const FileNode& fn)
{
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name);
samples = fn["samples"];
lambda = fn["lambda"];
int random_val = fn["random"];
random = (random_val != 0);
}
protected:
String name;
int samples;
float lambda;
bool random;
Mat w;
};
Ptr<CalibrateDebevec> createCalibrateDebevec(int samples, float lambda, bool random)
{
return makePtr<CalibrateDebevecImpl>(samples, lambda, random);
}
class CalibrateRobertsonImpl : public CalibrateRobertson
{
public:
CalibrateRobertsonImpl(int _max_iter, float _threshold) :
name("CalibrateRobertson"),
max_iter(_max_iter),
threshold(_threshold),
weight(RobertsonWeights())
{
}
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times)
{
std::vector<Mat> images;
src.getMatVector(images);
Mat times = _times.getMat();
CV_Assert(images.size() == times.total());
checkImageDimensions(images);
CV_Assert(images[0].depth() == CV_8U);
int channels = images[0].channels();
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
dst.create(LDR_SIZE, 1, CV_32FCC);
Mat response = dst.getMat();
response = linearResponse(3) / (LDR_SIZE / 2.0f);
Mat card = Mat::zeros(LDR_SIZE, 1, CV_32FCC);
for(size_t i = 0; i < images.size(); i++) {
uchar *ptr = images[i].ptr();
for(size_t pos = 0; pos < images[i].total(); pos++) {
for(int c = 0; c < channels; c++, ptr++) {
card.at<Vec3f>(*ptr)[c] += 1;
}
}
}
card = 1.0 / card;
Ptr<MergeRobertson> merge = createMergeRobertson();
for(int iter = 0; iter < max_iter; iter++) {
radiance = Mat::zeros(images[0].size(), CV_32FCC);
merge->process(images, radiance, times, response);
Mat new_response = Mat::zeros(LDR_SIZE, 1, CV_32FC3);
for(size_t i = 0; i < images.size(); i++) {
uchar *ptr = images[i].ptr();
float* rad_ptr = radiance.ptr<float>();
for(size_t pos = 0; pos < images[i].total(); pos++) {
for(int c = 0; c < channels; c++, ptr++, rad_ptr++) {
new_response.at<Vec3f>(*ptr)[c] += times.at<float>(i) * *rad_ptr;
}
}
}
new_response = new_response.mul(card);
for(int c = 0; c < 3; c++) {
float middle = new_response.at<Vec3f>(LDR_SIZE / 2)[c];
for(int i = 0; i < LDR_SIZE; i++) {
new_response.at<Vec3f>(i)[c] /= middle;
}
}
float diff = static_cast<float>(sum(sum(abs(new_response - response)))[0] / channels);
new_response.copyTo(response);
if(diff < threshold) {
break;
}
}
}
int getMaxIter() const { return max_iter; }
void setMaxIter(int val) { max_iter = val; }
float getThreshold() const { return threshold; }
void setThreshold(float val) { threshold = val; }
Mat getRadiance() const { return radiance; }
void write(FileStorage& fs) const
{
fs << "name" << name
<< "max_iter" << max_iter
<< "threshold" << threshold;
}
void read(const FileNode& fn)
{
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name);
max_iter = fn["max_iter"];
threshold = fn["threshold"];
}
protected:
String name;
int max_iter;
float threshold;
Mat weight, radiance;
};
Ptr<CalibrateRobertson> createCalibrateRobertson(int max_iter, float threshold)
{
return makePtr<CalibrateRobertsonImpl>(max_iter, threshold);
}
}

View File

@@ -116,7 +116,7 @@ static void fastNlMeansDenoisingMultiCheckPreconditions(
int imgToDenoiseIndex, int temporalWindowSize,
int templateWindowSize, int searchWindowSize)
{
int src_imgs_size = (int)srcImgs.size();
int src_imgs_size = static_cast<int>(srcImgs.size());
if (src_imgs_size == 0) {
CV_Error(Error::StsBadArg, "Input images vector should not be empty!");
}
@@ -198,7 +198,7 @@ void cv::fastNlMeansDenoisingColoredMulti( InputArrayOfArrays _srcImgs, OutputAr
_dst.create(srcImgs[0].size(), srcImgs[0].type());
Mat dst = _dst.getMat();
int src_imgs_size = (int)srcImgs.size();
int src_imgs_size = static_cast<int>(srcImgs.size());
if (srcImgs[0].type() != CV_8UC3) {
CV_Error(Error::StsBadArg, "Type of input images should be CV_8UC3!");

View File

@@ -0,0 +1,105 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
#include "opencv2/photo.hpp"
#include "hdr_common.hpp"
namespace cv
{
void checkImageDimensions(const std::vector<Mat>& images)
{
CV_Assert(!images.empty());
int width = images[0].cols;
int height = images[0].rows;
int type = images[0].type();
for(size_t i = 0; i < images.size(); i++) {
CV_Assert(images[i].cols == width && images[i].rows == height);
CV_Assert(images[i].type() == type);
}
}
Mat tringleWeights()
{
Mat w(LDR_SIZE, 1, CV_32F);
int half = LDR_SIZE / 2;
for(int i = 0; i < LDR_SIZE; i++) {
w.at<float>(i) = i < half ? i + 1.0f : LDR_SIZE - i;
}
return w;
}
Mat RobertsonWeights()
{
Mat weight(LDR_SIZE, 1, CV_32FC3);
float q = (LDR_SIZE - 1) / 4.0f;
for(int i = 0; i < LDR_SIZE; i++) {
float value = i / q - 2.0f;
value = exp(-value * value);
weight.at<Vec3f>(i) = Vec3f::all(value);
}
return weight;
}
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);
}
Mat linearResponse(int channels)
{
Mat response = Mat(LDR_SIZE, 1, CV_MAKETYPE(CV_32F, channels));
for(int i = 0; i < LDR_SIZE; i++) {
response.at<Vec3f>(i) = Vec3f::all(static_cast<float>(i));
}
return response;
}
};

View File

@@ -0,0 +1,62 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifndef __OPENCV_HDR_COMMON_HPP__
#define __OPENCV_HDR_COMMON_HPP__
#include "precomp.hpp"
#include "opencv2/photo.hpp"
namespace cv
{
void checkImageDimensions(const std::vector<Mat>& images);
Mat tringleWeights();
void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation);
Mat RobertsonWeights();
Mat linearResponse(int channels);
};
#endif

351
modules/photo/src/merge.cpp Normal file
View File

@@ -0,0 +1,351 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
#include "opencv2/photo.hpp"
#include "opencv2/imgproc.hpp"
#include "hdr_common.hpp"
namespace cv
{
class MergeDebevecImpl : public MergeDebevec
{
public:
MergeDebevecImpl() :
name("MergeDebevec"),
weights(tringleWeights())
{
}
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times, InputArray input_response)
{
std::vector<Mat> images;
src.getMatVector(images);
Mat times = _times.getMat();
CV_Assert(images.size() == times.total());
checkImageDimensions(images);
CV_Assert(images[0].depth() == CV_8U);
int channels = images[0].channels();
Size size = images[0].size();
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
dst.create(images[0].size(), CV_32FCC);
Mat result = dst.getMat();
Mat response = input_response.getMat();
if(response.empty()) {
response = linearResponse(channels);
response.at<Vec3f>(0) = response.at<Vec3f>(1);
}
log(response, response);
CV_Assert(response.rows == LDR_SIZE && response.cols == 1 &&
response.channels() == channels);
Mat exp_values(times);
log(exp_values, exp_values);
result = Mat::zeros(size, CV_32FCC);
std::vector<Mat> result_split;
split(result, result_split);
Mat weight_sum = Mat::zeros(size, CV_32F);
for(size_t i = 0; i < images.size(); i++) {
std::vector<Mat> splitted;
split(images[i], splitted);
Mat w = Mat::zeros(size, CV_32F);
for(int c = 0; c < channels; c++) {
LUT(splitted[c], weights, splitted[c]);
w += splitted[c];
}
w /= channels;
Mat response_img;
LUT(images[i], response, response_img);
split(response_img, splitted);
for(int c = 0; c < channels; c++) {
result_split[c] += w.mul(splitted[c] - exp_values.at<float>(i));
}
weight_sum += w;
}
weight_sum = 1.0f / weight_sum;
for(int c = 0; c < channels; c++) {
result_split[c] = result_split[c].mul(weight_sum);
}
merge(result_split, result);
exp(result, result);
}
void process(InputArrayOfArrays src, OutputArray dst, InputArray times)
{
process(src, dst, times, Mat());
}
protected:
String name;
Mat weights;
};
Ptr<MergeDebevec> createMergeDebevec()
{
return makePtr<MergeDebevecImpl>();
}
class MergeMertensImpl : public MergeMertens
{
public:
MergeMertensImpl(float _wcon, float _wsat, float _wexp) :
name("MergeMertens"),
wcon(_wcon),
wsat(_wsat),
wexp(_wexp)
{
}
void process(InputArrayOfArrays src, OutputArrayOfArrays dst, InputArray, InputArray)
{
process(src, dst);
}
void process(InputArrayOfArrays src, OutputArray dst)
{
std::vector<Mat> images;
src.getMatVector(images);
checkImageDimensions(images);
int channels = images[0].channels();
CV_Assert(channels == 1 || channels == 3);
Size size = images[0].size();
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
std::vector<Mat> weights(images.size());
Mat weight_sum = Mat::zeros(size, CV_32F);
for(size_t i = 0; i < images.size(); i++) {
Mat img, gray, contrast, saturation, wellexp;
std::vector<Mat> splitted(channels);
images[i].convertTo(img, CV_32F, 1.0f/255.0f);
if(channels == 3) {
cvtColor(img, gray, COLOR_RGB2GRAY);
} else {
img.copyTo(gray);
}
split(img, splitted);
Laplacian(gray, contrast, CV_32F);
contrast = abs(contrast);
Mat mean = Mat::zeros(size, CV_32F);
for(int c = 0; c < channels; c++) {
mean += splitted[c];
}
mean /= channels;
saturation = Mat::zeros(size, CV_32F);
for(int c = 0; c < channels; c++) {
Mat deviation = splitted[c] - mean;
pow(deviation, 2.0f, deviation);
saturation += deviation;
}
sqrt(saturation, saturation);
wellexp = Mat::ones(size, CV_32F);
for(int c = 0; c < channels; c++) {
Mat exp = splitted[c] - 0.5f;
pow(exp, 2.0f, exp);
exp = -exp / 0.08f;
wellexp = wellexp.mul(exp);
}
pow(contrast, wcon, contrast);
pow(saturation, wsat, saturation);
pow(wellexp, wexp, wellexp);
weights[i] = contrast;
if(channels == 3) {
weights[i] = weights[i].mul(saturation);
}
weights[i] = weights[i].mul(wellexp);
weight_sum += weights[i];
}
int maxlevel = static_cast<int>(logf(static_cast<float>(min(size.width, size.height))) / logf(2.0f));
std::vector<Mat> res_pyr(maxlevel + 1);
for(size_t i = 0; i < images.size(); i++) {
weights[i] /= weight_sum;
Mat img;
images[i].convertTo(img, CV_32F, 1.0f/255.0f);
std::vector<Mat> img_pyr, weight_pyr;
buildPyramid(img, img_pyr, maxlevel);
buildPyramid(weights[i], weight_pyr, maxlevel);
for(int lvl = 0; lvl < maxlevel; lvl++) {
Mat up;
pyrUp(img_pyr[lvl + 1], up, img_pyr[lvl].size());
img_pyr[lvl] -= up;
}
for(int lvl = 0; lvl <= maxlevel; lvl++) {
std::vector<Mat> splitted(channels);
split(img_pyr[lvl], splitted);
for(int c = 0; c < channels; c++) {
splitted[c] = splitted[c].mul(weight_pyr[lvl]);
}
merge(splitted, img_pyr[lvl]);
if(res_pyr[lvl].empty()) {
res_pyr[lvl] = img_pyr[lvl];
} else {
res_pyr[lvl] += img_pyr[lvl];
}
}
}
for(int lvl = maxlevel; lvl > 0; lvl--) {
Mat up;
pyrUp(res_pyr[lvl], up, res_pyr[lvl - 1].size());
res_pyr[lvl - 1] += up;
}
dst.create(size, CV_32FCC);
res_pyr[0].copyTo(dst.getMat());
}
float getContrastWeight() const { return wcon; }
void setContrastWeight(float val) { wcon = val; }
float getSaturationWeight() const { return wsat; }
void setSaturationWeight(float val) { wsat = val; }
float getExposureWeight() const { return wexp; }
void setExposureWeight(float val) { wexp = val; }
void write(FileStorage& fs) const
{
fs << "name" << name
<< "contrast_weight" << wcon
<< "saturation_weight" << wsat
<< "exposure_weight" << wexp;
}
void read(const FileNode& fn)
{
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name);
wcon = fn["contrast_weight"];
wsat = fn["saturation_weight"];
wexp = fn["exposure_weight"];
}
protected:
String name;
float wcon, wsat, wexp;
};
Ptr<MergeMertens> createMergeMertens(float wcon, float wsat, float wexp)
{
return makePtr<MergeMertensImpl>(wcon, wsat, wexp);
}
class MergeRobertsonImpl : public MergeRobertson
{
public:
MergeRobertsonImpl() :
name("MergeRobertson"),
weight(RobertsonWeights())
{
}
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times, InputArray input_response)
{
std::vector<Mat> images;
src.getMatVector(images);
Mat times = _times.getMat();
CV_Assert(images.size() == times.total());
checkImageDimensions(images);
CV_Assert(images[0].depth() == CV_8U);
int channels = images[0].channels();
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
dst.create(images[0].size(), CV_32FCC);
Mat result = dst.getMat();
Mat response = input_response.getMat();
if(response.empty()) {
float middle = LDR_SIZE / 2.0f;
response = linearResponse(channels) / middle;
}
CV_Assert(response.rows == LDR_SIZE && response.cols == 1 &&
response.channels() == channels);
result = Mat::zeros(images[0].size(), CV_32FCC);
Mat wsum = Mat::zeros(images[0].size(), CV_32FCC);
for(size_t i = 0; i < images.size(); i++) {
Mat im, w;
LUT(images[i], weight, w);
LUT(images[i], response, im);
result += times.at<float>(i) * w.mul(im);
wsum += times.at<float>(i) * times.at<float>(i) * w;
}
result = result.mul(1 / wsum);
}
void process(InputArrayOfArrays src, OutputArray dst, InputArray times)
{
process(src, dst, times, Mat());
}
protected:
String name;
Mat weight;
};
Ptr<MergeRobertson> createMergeRobertson()
{
return makePtr<MergeRobertsonImpl>();
}
}

View File

@@ -0,0 +1,531 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
#include "opencv2/photo.hpp"
#include "opencv2/imgproc.hpp"
#include "hdr_common.hpp"
namespace cv
{
class TonemapImpl : public Tonemap
{
public:
TonemapImpl(float _gamma) : name("Tonemap"), gamma(_gamma)
{
}
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;
}
void read(const FileNode& fn)
{
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 : public TonemapDrago
{
public:
TonemapDragoImpl(float _gamma, float _saturation, float _bias) :
name("TonemapDrago"),
gamma(_gamma),
saturation(_saturation),
bias(_bias)
{
}
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 = 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
<< "bias" << bias
<< "saturation" << saturation;
}
void read(const FileNode& fn)
{
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);
}
class TonemapDurandImpl : public TonemapDurand
{
public:
TonemapDurandImpl(float _gamma, float _contrast, float _saturation, float _sigma_color, float _sigma_space) :
name("TonemapDurand"),
gamma(_gamma),
contrast(_contrast),
saturation(_saturation),
sigma_color(_sigma_color),
sigma_space(_sigma_space)
{
}
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 = 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);
}
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 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; }
void write(FileStorage& fs) const
{
fs << "name" << name
<< "gamma" << gamma
<< "contrast" << contrast
<< "sigma_color" << sigma_color
<< "sigma_space" << sigma_space
<< "saturation" << saturation;
}
void read(const FileNode& fn)
{
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name);
gamma = fn["gamma"];
contrast = fn["contrast"];
sigma_color = fn["sigma_color"];
sigma_space = fn["sigma_space"];
saturation = fn["saturation"];
}
protected:
String name;
float gamma, contrast, saturation, sigma_color, sigma_space;
};
Ptr<TonemapDurand> createTonemapDurand(float gamma, float contrast, float saturation, float sigma_color, float sigma_space)
{
return makePtr<TonemapDurandImpl>(gamma, contrast, saturation, sigma_color, sigma_space);
}
class TonemapReinhardImpl : public TonemapReinhard
{
public:
TonemapReinhardImpl(float _gamma, float _intensity, float _light_adapt, float _color_adapt) :
name("TonemapReinhard"),
gamma(_gamma),
intensity(_intensity),
light_adapt(_light_adapt),
color_adapt(_color_adapt)
{
}
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 = 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
<< "intensity" << intensity
<< "light_adapt" << light_adapt
<< "color_adapt" << color_adapt;
}
void read(const FileNode& fn)
{
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name);
gamma = fn["gamma"];
intensity = fn["intensity"];
light_adapt = fn["light_adapt"];
color_adapt = fn["color_adapt"];
}
protected:
String name;
float gamma, intensity, light_adapt, color_adapt;
};
Ptr<TonemapReinhard> createTonemapReinhard(float gamma, float contrast, float sigma_color, float sigma_space)
{
return makePtr<TonemapReinhardImpl>(gamma, contrast, sigma_color, sigma_space);
}
class TonemapMantiukImpl : public TonemapMantiuk
{
public:
TonemapMantiukImpl(float _gamma, float _scale, float _saturation) :
name("TonemapMantiuk"),
gamma(_gamma),
scale(_scale),
saturation(_saturation)
{
}
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 = 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]);
mapContrast(y_contrast[i]);
}
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 = 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.0f/255.0f);
sign = sign * 2.0f - 1.0f;
pow(abs(src), power, dst);
dst = dst.mul(sign);
}
void mapContrast(Mat& contrast)
{
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 makePtr<TonemapMantiukImpl>(gamma, scale, saturation);
}
}

View File

@@ -0,0 +1,249 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "test_precomp.hpp"
#include <string>
#include <algorithm>
#include <fstream>
using namespace cv;
using namespace std;
void loadImage(string path, Mat &img)
{
img = imread(path, -1);
ASSERT_FALSE(img.empty()) << "Could not load input image " << path;
}
void checkEqual(Mat img0, Mat img1, double threshold)
{
double max = 1.0;
minMaxLoc(abs(img0 - img1), NULL, &max);
ASSERT_FALSE(max > threshold) << max;
}
static vector<float> DEFAULT_VECTOR;
void loadExposureSeq(String path, vector<Mat>& images, vector<float>& times = DEFAULT_VECTOR)
{
ifstream list_file((path + "list.txt").c_str());
ASSERT_TRUE(list_file.is_open());
string name;
float val;
while(list_file >> name >> val) {
Mat img = imread(path + name);
ASSERT_FALSE(img.empty()) << "Could not load input image " << path + name;
images.push_back(img);
times.push_back(1 / val);
}
list_file.close();
}
void loadResponseCSV(String path, Mat& response)
{
response = Mat(256, 1, CV_32FC3);
ifstream resp_file(path.c_str());
for(int i = 0; i < 256; i++) {
for(int c = 0; c < 3; c++) {
resp_file >> response.at<Vec3f>(i)[c];
resp_file.ignore(1);
}
}
resp_file.close();
}
TEST(Photo_Tonemap, regression)
{
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/tonemap/";
Mat img, expected, result;
loadImage(test_path + "image.hdr", img);
float gamma = 2.2f;
Ptr<Tonemap> linear = createTonemap(gamma);
linear->process(img, result);
loadImage(test_path + "linear.png", expected);
result.convertTo(result, CV_8UC3, 255);
checkEqual(result, expected, 3);
Ptr<TonemapDrago> drago = createTonemapDrago(gamma);
drago->process(img, result);
loadImage(test_path + "drago.png", expected);
result.convertTo(result, CV_8UC3, 255);
checkEqual(result, expected, 3);
Ptr<TonemapDurand> durand = createTonemapDurand(gamma);
durand->process(img, result);
loadImage(test_path + "durand.png", expected);
result.convertTo(result, CV_8UC3, 255);
checkEqual(result, expected, 3);
Ptr<TonemapReinhard> reinhard = createTonemapReinhard(gamma);
reinhard->process(img, result);
loadImage(test_path + "reinhard.png", expected);
result.convertTo(result, CV_8UC3, 255);
checkEqual(result, expected, 3);
Ptr<TonemapMantiuk> mantiuk = createTonemapMantiuk(gamma);
mantiuk->process(img, result);
loadImage(test_path + "mantiuk.png", expected);
result.convertTo(result, CV_8UC3, 255);
checkEqual(result, expected, 3);
}
TEST(Photo_AlignMTB, regression)
{
const int TESTS_COUNT = 100;
string folder = string(cvtest::TS::ptr()->get_data_path()) + "shared/";
string file_name = folder + "lena.png";
Mat img;
loadImage(file_name, img);
cvtColor(img, img, COLOR_RGB2GRAY);
int max_bits = 5;
int max_shift = 32;
srand(static_cast<unsigned>(time(0)));
int errors = 0;
Ptr<AlignMTB> align = createAlignMTB(max_bits);
for(int i = 0; i < TESTS_COUNT; i++) {
Point shift(rand() % max_shift, rand() % max_shift);
Mat res;
align->shiftMat(img, res, shift);
Point calc = align->calculateShift(img, res);
errors += (calc != -shift);
}
ASSERT_TRUE(errors < 5) << errors << " errors";
}
TEST(Photo_MergeMertens, regression)
{
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
vector<Mat> images;
loadExposureSeq((test_path + "exposures/").c_str() , images);
Ptr<MergeMertens> merge = createMergeMertens();
Mat result, expected;
loadImage(test_path + "merge/mertens.png", expected);
merge->process(images, result);
result.convertTo(result, CV_8UC3, 255);
checkEqual(expected, result, 3);
}
TEST(Photo_MergeDebevec, regression)
{
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
vector<Mat> images;
vector<float> times;
Mat response;
loadExposureSeq(test_path + "exposures/", images, times);
loadResponseCSV(test_path + "exposures/response.csv", response);
Ptr<MergeDebevec> merge = createMergeDebevec();
Mat result, expected;
loadImage(test_path + "merge/debevec.hdr", expected);
merge->process(images, result, times, response);
Ptr<Tonemap> map = createTonemap();
map->process(result, result);
map->process(expected, expected);
checkEqual(expected, result, 1e-2f);
}
TEST(Photo_MergeRobertson, regression)
{
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
vector<Mat> images;
vector<float> times;
loadExposureSeq(test_path + "exposures/", images, times);
Ptr<MergeRobertson> merge = createMergeRobertson();
Mat result, expected;
loadImage(test_path + "merge/robertson.hdr", expected);
merge->process(images, result, times);
Ptr<Tonemap> map = createTonemap();
map->process(result, result);
map->process(expected, expected);
checkEqual(expected, result, 1e-2f);
}
TEST(Photo_CalibrateDebevec, regression)
{
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
vector<Mat> images;
vector<float> times;
Mat response, expected;
loadExposureSeq(test_path + "exposures/", images, times);
loadResponseCSV(test_path + "calibrate/debevec.csv", expected);
Ptr<CalibrateDebevec> calibrate = createCalibrateDebevec();
calibrate->process(images, response, times);
Mat diff = abs(response - expected);
diff = diff.mul(1.0f / response);
double max;
minMaxLoc(diff, NULL, &max);
ASSERT_FALSE(max > 0.1);
}
TEST(Photo_CalibrateRobertson, regression)
{
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
vector<Mat> images;
vector<float> times;
Mat response, expected;
loadExposureSeq(test_path + "exposures/", images, times);
loadResponseCSV(test_path + "calibrate/robertson.csv", expected);
Ptr<CalibrateRobertson> calibrate = createCalibrateRobertson();
calibrate->process(images, response, times);
checkEqual(expected, response, 1e-3f);
}