Merge pull request #1474 from f-morozov:for_pr
This commit is contained in:
377
modules/photo/doc/hdr_imaging.rst
Normal file
377
modules/photo/doc/hdr_imaging.rst
Normal 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.
|
||||
@@ -9,3 +9,4 @@ photo. Computational Photography
|
||||
|
||||
inpainting
|
||||
denoising
|
||||
hdr_imaging
|
||||
@@ -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
270
modules/photo/src/align.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
276
modules/photo/src/calibrate.cpp
Normal file
276
modules/photo/src/calibrate.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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!");
|
||||
|
||||
105
modules/photo/src/hdr_common.cpp
Normal file
105
modules/photo/src/hdr_common.cpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
||||
62
modules/photo/src/hdr_common.hpp
Normal file
62
modules/photo/src/hdr_common.hpp
Normal 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
351
modules/photo/src/merge.cpp
Normal 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>();
|
||||
}
|
||||
|
||||
}
|
||||
531
modules/photo/src/tonemap.cpp
Normal file
531
modules/photo/src/tonemap.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
249
modules/photo/test/test_hdr.cpp
Normal file
249
modules/photo/test/test_hdr.cpp
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user