vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
set(the_description "Computational Photography")
|
||||
|
||||
if(HAVE_CUDA)
|
||||
ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef -Wmissing-declarations -Wshadow)
|
||||
endif()
|
||||
|
||||
ocv_define_module(photo opencv_imgproc opencv_geometry OPTIONAL opencv_cudaarithm opencv_cudaimgproc WRAP java objc python js)
|
||||
|
||||
if(HAVE_CUDA AND ENABLE_CUDA_FIRST_CLASS_LANGUAGE AND HAVE_opencv_cudaarithm AND HAVE_opencv_cudaimgproc)
|
||||
ocv_target_link_libraries(${the_module} PRIVATE CUDA::cudart${CUDA_LIB_EXT})
|
||||
endif()
|
||||
@@ -0,0 +1,967 @@
|
||||
/*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) 2008-2012, 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*/
|
||||
|
||||
#ifndef OPENCV_PHOTO_HPP
|
||||
#define OPENCV_PHOTO_HPP
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/photo/ccm.hpp"
|
||||
|
||||
/**
|
||||
@defgroup photo Computational Photography
|
||||
|
||||
This module includes photo processing algorithms
|
||||
@{
|
||||
@defgroup photo_inpaint Inpainting
|
||||
@defgroup photo_denoise Denoising
|
||||
@defgroup photo_hdr HDR imaging
|
||||
|
||||
This section describes high dynamic range imaging algorithms namely tonemapping, exposure alignment,
|
||||
camera calibration with multiple exposures and exposure fusion.
|
||||
|
||||
@defgroup photo_decolor Contrast Preserving Decolorization
|
||||
|
||||
Useful links:
|
||||
|
||||
http://www.cse.cuhk.edu.hk/leojia/projects/color2gray/index.html
|
||||
|
||||
@defgroup photo_clone Seamless Cloning
|
||||
|
||||
Useful links:
|
||||
|
||||
https://www.learnopencv.com/seamless-cloning-using-opencv-python-cpp
|
||||
|
||||
@defgroup photo_render Non-Photorealistic Rendering
|
||||
|
||||
Useful links:
|
||||
|
||||
http://www.inf.ufrgs.br/~eslgastal/DomainTransform
|
||||
|
||||
https://www.learnopencv.com/non-photorealistic-rendering-using-opencv-python-c/
|
||||
|
||||
@}
|
||||
*/
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
//! @addtogroup photo
|
||||
//! @{
|
||||
|
||||
//! @addtogroup photo_inpaint
|
||||
//! @{
|
||||
//! the inpainting algorithm
|
||||
enum
|
||||
{
|
||||
INPAINT_NS = 0, //!< Use Navier-Stokes based method
|
||||
INPAINT_TELEA = 1 //!< Use the algorithm proposed by Alexandru Telea @cite Telea04
|
||||
};
|
||||
|
||||
/** @brief Restores the selected region in an image using the region neighborhood.
|
||||
|
||||
@param src Input 8-bit, 16-bit unsigned or 32-bit float 1-channel or 8-bit 3-channel image.
|
||||
@param inpaintMask Inpainting mask, 8-bit 1-channel image. Non-zero pixels indicate the area that
|
||||
needs to be inpainted.
|
||||
@param dst Output image with the same size and type as src .
|
||||
@param inpaintRadius Radius of a circular neighborhood of each point inpainted that is considered
|
||||
by the algorithm.
|
||||
@param flags Inpainting method that could be cv::INPAINT_NS or cv::INPAINT_TELEA
|
||||
|
||||
The function reconstructs the selected image area from the pixel near the area boundary. The
|
||||
function may be used to remove dust and scratches from a scanned photo, or to remove undesirable
|
||||
objects from still images or video. See <http://en.wikipedia.org/wiki/Inpainting> for more details.
|
||||
|
||||
@note
|
||||
- An example using the inpainting technique can be found at
|
||||
opencv_source_code/samples/cpp/inpaint.cpp
|
||||
- (Python) An example using the inpainting technique can be found at
|
||||
opencv_source_code/samples/python/inpaint.py
|
||||
*/
|
||||
CV_EXPORTS_W void inpaint( InputArray src, InputArray inpaintMask,
|
||||
OutputArray dst, double inpaintRadius, int flags );
|
||||
|
||||
//! @} photo_inpaint
|
||||
|
||||
//! @addtogroup photo_denoise
|
||||
//! @{
|
||||
|
||||
/** @brief Perform image denoising using Non-local Means Denoising algorithm
|
||||
<http://www.ipol.im/pub/algo/bcm_non_local_means_denoising/> with several computational
|
||||
optimizations. Noise expected to be a gaussian white noise
|
||||
|
||||
@param src Input 8-bit 1-channel, 2-channel, 3-channel or 4-channel image.
|
||||
@param dst Output image with the same size and type as src .
|
||||
@param templateWindowSize Size in pixels of the template patch that is used to compute weights.
|
||||
Should be odd. Recommended value 7 pixels
|
||||
@param searchWindowSize Size in pixels of the window that is used to compute weighted average for
|
||||
given pixel. Should be odd. Affect performance linearly: greater searchWindowsSize - greater
|
||||
denoising time. Recommended value 21 pixels
|
||||
@param h Parameter regulating filter strength. Big h value perfectly removes noise but also
|
||||
removes image details, smaller h value preserves details but also preserves some noise
|
||||
|
||||
This function expected to be applied to grayscale images. For colored images look at
|
||||
fastNlMeansDenoisingColored. Advanced usage of this functions can be manual denoising of colored
|
||||
image in different colorspaces. Such approach is used in fastNlMeansDenoisingColored by converting
|
||||
image to CIELAB colorspace and then separately denoise L and AB components with different h
|
||||
parameter.
|
||||
*/
|
||||
CV_EXPORTS_W void fastNlMeansDenoising( InputArray src, OutputArray dst, float h = 3,
|
||||
int templateWindowSize = 7, int searchWindowSize = 21);
|
||||
|
||||
/** @brief Perform image denoising using Non-local Means Denoising algorithm
|
||||
<http://www.ipol.im/pub/algo/bcm_non_local_means_denoising/> with several computational
|
||||
optimizations. Noise expected to be a gaussian white noise
|
||||
|
||||
@param src Input 8-bit or 16-bit (only with NORM_L1) 1-channel,
|
||||
2-channel, 3-channel or 4-channel image.
|
||||
@param dst Output image with the same size and type as src .
|
||||
@param templateWindowSize Size in pixels of the template patch that is used to compute weights.
|
||||
Should be odd. Recommended value 7 pixels
|
||||
@param searchWindowSize Size in pixels of the window that is used to compute weighted average for
|
||||
given pixel. Should be odd. Affect performance linearly: greater searchWindowsSize - greater
|
||||
denoising time. Recommended value 21 pixels
|
||||
@param h Array of parameters regulating filter strength, either one
|
||||
parameter applied to all channels or one per channel in dst. Big h value
|
||||
perfectly removes noise but also removes image details, smaller h
|
||||
value preserves details but also preserves some noise
|
||||
@param normType Type of norm used for weight calculation. Can be either NORM_L2 or NORM_L1
|
||||
|
||||
This function expected to be applied to grayscale images. For colored images look at
|
||||
fastNlMeansDenoisingColored. Advanced usage of this functions can be manual denoising of colored
|
||||
image in different colorspaces. Such approach is used in fastNlMeansDenoisingColored by converting
|
||||
image to CIELAB colorspace and then separately denoise L and AB components with different h
|
||||
parameter.
|
||||
*/
|
||||
CV_EXPORTS_W void fastNlMeansDenoising( InputArray src, OutputArray dst,
|
||||
const std::vector<float>& h,
|
||||
int templateWindowSize = 7, int searchWindowSize = 21,
|
||||
int normType = NORM_L2);
|
||||
|
||||
/** @brief Modification of fastNlMeansDenoising function for colored images
|
||||
|
||||
@param src Input 8-bit 3-channel image.
|
||||
@param dst Output image with the same size and type as src .
|
||||
@param templateWindowSize Size in pixels of the template patch that is used to compute weights.
|
||||
Should be odd. Recommended value 7 pixels
|
||||
@param searchWindowSize Size in pixels of the window that is used to compute weighted average for
|
||||
given pixel. Should be odd. Affect performance linearly: greater searchWindowsSize - greater
|
||||
denoising time. Recommended value 21 pixels
|
||||
@param h Parameter regulating filter strength for luminance component. Bigger h value perfectly
|
||||
removes noise but also removes image details, smaller h value preserves details but also preserves
|
||||
some noise
|
||||
@param hColor The same as h but for color components. For most images value equals 10
|
||||
will be enough to remove colored noise and do not distort colors
|
||||
|
||||
The function converts image to CIELAB colorspace and then separately denoise L and AB components
|
||||
with given h parameters using fastNlMeansDenoising function.
|
||||
*/
|
||||
CV_EXPORTS_W void fastNlMeansDenoisingColored( InputArray src, OutputArray dst,
|
||||
float h = 3, float hColor = 3,
|
||||
int templateWindowSize = 7, int searchWindowSize = 21);
|
||||
|
||||
/** @brief Modification of fastNlMeansDenoising function for images sequence where consecutive images have been
|
||||
captured in small period of time. For example video. This version of the function is for grayscale
|
||||
images or for manual manipulation with colorspaces. See @cite Buades2005DenoisingIS for more details
|
||||
(open access [here](https://static.aminer.org/pdf/PDF/000/317/196/spatio_temporal_wiener_filtering_of_image_sequences_using_a_parametric.pdf)).
|
||||
|
||||
@param srcImgs Input 8-bit 1-channel, 2-channel, 3-channel or
|
||||
4-channel images sequence. All images should have the same type and
|
||||
size.
|
||||
@param imgToDenoiseIndex Target image to denoise index in srcImgs sequence
|
||||
@param temporalWindowSize Number of surrounding images to use for target image denoising. Should
|
||||
be odd. Images from imgToDenoiseIndex - temporalWindowSize / 2 to
|
||||
imgToDenoiseIndex + temporalWindowSize / 2 from srcImgs will be used to denoise
|
||||
srcImgs[imgToDenoiseIndex] image.
|
||||
@param dst Output image with the same size and type as srcImgs images.
|
||||
@param templateWindowSize Size in pixels of the template patch that is used to compute weights.
|
||||
Should be odd. Recommended value 7 pixels
|
||||
@param searchWindowSize Size in pixels of the window that is used to compute weighted average for
|
||||
given pixel. Should be odd. Affect performance linearly: greater searchWindowsSize - greater
|
||||
denoising time. Recommended value 21 pixels
|
||||
@param h Parameter regulating filter strength. Bigger h value
|
||||
perfectly removes noise but also removes image details, smaller h
|
||||
value preserves details but also preserves some noise
|
||||
*/
|
||||
CV_EXPORTS_W void fastNlMeansDenoisingMulti( InputArrayOfArrays srcImgs, OutputArray dst,
|
||||
int imgToDenoiseIndex, int temporalWindowSize,
|
||||
float h = 3, int templateWindowSize = 7, int searchWindowSize = 21);
|
||||
|
||||
/** @brief Modification of fastNlMeansDenoising function for images sequence where consecutive images have been
|
||||
captured in small period of time. For example video. This version of the function is for grayscale
|
||||
images or for manual manipulation with colorspaces. See @cite Buades2005DenoisingIS for more details
|
||||
(open access [here](https://static.aminer.org/pdf/PDF/000/317/196/spatio_temporal_wiener_filtering_of_image_sequences_using_a_parametric.pdf)).
|
||||
|
||||
@param srcImgs Input 8-bit or 16-bit (only with NORM_L1) 1-channel,
|
||||
2-channel, 3-channel or 4-channel images sequence. All images should
|
||||
have the same type and size.
|
||||
@param imgToDenoiseIndex Target image to denoise index in srcImgs sequence
|
||||
@param temporalWindowSize Number of surrounding images to use for target image denoising. Should
|
||||
be odd. Images from imgToDenoiseIndex - temporalWindowSize / 2 to
|
||||
imgToDenoiseIndex + temporalWindowSize / 2 from srcImgs will be used to denoise
|
||||
srcImgs[imgToDenoiseIndex] image.
|
||||
@param dst Output image with the same size and type as srcImgs images.
|
||||
@param templateWindowSize Size in pixels of the template patch that is used to compute weights.
|
||||
Should be odd. Recommended value 7 pixels
|
||||
@param searchWindowSize Size in pixels of the window that is used to compute weighted average for
|
||||
given pixel. Should be odd. Affect performance linearly: greater searchWindowsSize - greater
|
||||
denoising time. Recommended value 21 pixels
|
||||
@param h Array of parameters regulating filter strength, either one
|
||||
parameter applied to all channels or one per channel in dst. Big h value
|
||||
perfectly removes noise but also removes image details, smaller h
|
||||
value preserves details but also preserves some noise
|
||||
@param normType Type of norm used for weight calculation. Can be either NORM_L2 or NORM_L1
|
||||
*/
|
||||
CV_EXPORTS_W void fastNlMeansDenoisingMulti( InputArrayOfArrays srcImgs, OutputArray dst,
|
||||
int imgToDenoiseIndex, int temporalWindowSize,
|
||||
const std::vector<float>& h,
|
||||
int templateWindowSize = 7, int searchWindowSize = 21,
|
||||
int normType = NORM_L2);
|
||||
|
||||
/** @brief Modification of fastNlMeansDenoisingMulti function for colored images sequences
|
||||
|
||||
@param srcImgs Input 8-bit 3-channel images sequence. All images should have the same type and
|
||||
size.
|
||||
@param imgToDenoiseIndex Target image to denoise index in srcImgs sequence
|
||||
@param temporalWindowSize Number of surrounding images to use for target image denoising. Should
|
||||
be odd. Images from imgToDenoiseIndex - temporalWindowSize / 2 to
|
||||
imgToDenoiseIndex + temporalWindowSize / 2 from srcImgs will be used to denoise
|
||||
srcImgs[imgToDenoiseIndex] image.
|
||||
@param dst Output image with the same size and type as srcImgs images.
|
||||
@param templateWindowSize Size in pixels of the template patch that is used to compute weights.
|
||||
Should be odd. Recommended value 7 pixels
|
||||
@param searchWindowSize Size in pixels of the window that is used to compute weighted average for
|
||||
given pixel. Should be odd. Affect performance linearly: greater searchWindowsSize - greater
|
||||
denoising time. Recommended value 21 pixels
|
||||
@param h Parameter regulating filter strength for luminance component. Bigger h value perfectly
|
||||
removes noise but also removes image details, smaller h value preserves details but also preserves
|
||||
some noise.
|
||||
@param hColor The same as h but for color components.
|
||||
|
||||
The function converts images to CIELAB colorspace and then separately denoise L and AB components
|
||||
with given h parameters using fastNlMeansDenoisingMulti function.
|
||||
*/
|
||||
CV_EXPORTS_W void fastNlMeansDenoisingColoredMulti( InputArrayOfArrays srcImgs, OutputArray dst,
|
||||
int imgToDenoiseIndex, int temporalWindowSize,
|
||||
float h = 3, float hColor = 3,
|
||||
int templateWindowSize = 7, int searchWindowSize = 21);
|
||||
|
||||
/** @brief Primal-dual algorithm is an algorithm for solving special types of variational problems (that is,
|
||||
finding a function to minimize some functional). As the image denoising, in particular, may be seen
|
||||
as the variational problem, primal-dual algorithm then can be used to perform denoising and this is
|
||||
exactly what is implemented.
|
||||
|
||||
It should be noted, that this implementation was taken from the July 2013 blog entry
|
||||
@cite MA13 , which also contained (slightly more general) ready-to-use source code on Python.
|
||||
Subsequently, that code was rewritten on C++ with the usage of openCV by Vadim Pisarevsky at the end
|
||||
of July 2013 and finally it was slightly adapted by later authors.
|
||||
|
||||
Although the thorough discussion and justification of the algorithm involved may be found in
|
||||
@cite ChambolleEtAl, it might make sense to skim over it here, following @cite MA13 . To begin
|
||||
with, we consider the 1-byte gray-level images as the functions from the rectangular domain of
|
||||
pixels (it may be seen as set
|
||||
\f$\left\{(x,y)\in\mathbb{N}\times\mathbb{N}\mid 1\leq x\leq n,\;1\leq y\leq m\right\}\f$ for some
|
||||
\f$m,\;n\in\mathbb{N}\f$) into \f$\{0,1,\dots,255\}\f$. We shall denote the noised images as \f$f_i\f$ and with
|
||||
this view, given some image \f$x\f$ of the same size, we may measure how bad it is by the formula
|
||||
|
||||
\f[\left\|\left\|\nabla x\right\|\right\| + \lambda\sum_i\left\|\left\|x-f_i\right\|\right\|\f]
|
||||
|
||||
\f$\|\|\cdot\|\|\f$ here denotes \f$L_2\f$-norm and as you see, the first addend states that we want our
|
||||
image to be smooth (ideally, having zero gradient, thus being constant) and the second states that
|
||||
we want our result to be close to the observations we've got. If we treat \f$x\f$ as a function, this is
|
||||
exactly the functional what we seek to minimize and here the Primal-Dual algorithm comes into play.
|
||||
|
||||
@param observations This array should contain one or more noised versions of the image that is to
|
||||
be restored.
|
||||
@param result Here the denoised image will be stored. There is no need to do pre-allocation of
|
||||
storage space, as it will be automatically allocated, if necessary.
|
||||
@param lambda Corresponds to \f$\lambda\f$ in the formulas above. As it is enlarged, the smooth
|
||||
(blurred) images are treated more favorably than detailed (but maybe more noised) ones. Roughly
|
||||
speaking, as it becomes smaller, the result will be more blur but more sever outliers will be
|
||||
removed.
|
||||
@param niters Number of iterations that the algorithm will run. Of course, as more iterations as
|
||||
better, but it is hard to quantitatively refine this statement, so just use the default and
|
||||
increase it if the results are poor.
|
||||
*/
|
||||
CV_EXPORTS_W void denoise_TVL1(const std::vector<Mat>& observations,Mat& result, double lambda=1.0, int niters=30);
|
||||
|
||||
//! @} photo_denoise
|
||||
|
||||
//! @addtogroup photo_hdr
|
||||
//! @{
|
||||
|
||||
enum { LDR_SIZE = 256 };
|
||||
|
||||
/** @brief Base class for tonemapping algorithms - tools that are used to map HDR image to 8-bit range.
|
||||
*/
|
||||
class CV_EXPORTS_W Tonemap : public Algorithm
|
||||
{
|
||||
public:
|
||||
/** @brief Tonemaps image
|
||||
|
||||
@param src source image - CV_32FC3 Mat (float 32 bits 3 channels)
|
||||
@param dst destination image - CV_32FC3 Mat with values in [0, 1] range
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/** @brief Creates simple linear mapper with gamma correction
|
||||
|
||||
@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.
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<Tonemap> createTonemap(float gamma = 1.0f);
|
||||
|
||||
/** @brief 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 @cite FL02 .
|
||||
|
||||
For more information see @cite DM03 .
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/** @brief Creates TonemapDrago object
|
||||
|
||||
@param gamma gamma value for gamma correction. See 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.
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<TonemapDrago> createTonemapDrago(float gamma = 1.0f, float saturation = 1.0f, float bias = 0.85f);
|
||||
|
||||
|
||||
/** @brief 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 @cite RD05 .
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/** @brief Creates TonemapReinhard object
|
||||
|
||||
@param gamma gamma value for gamma correction. See 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.
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<TonemapReinhard>
|
||||
createTonemapReinhard(float gamma = 1.0f, float intensity = 0.0f, float light_adapt = 1.0f, float color_adapt = 0.0f);
|
||||
|
||||
/** @brief 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 @cite MM06 .
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/** @brief Creates TonemapMantiuk object
|
||||
|
||||
@param gamma gamma value for gamma correction. See 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 createTonemapDrago
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<TonemapMantiuk>
|
||||
createTonemapMantiuk(float gamma = 1.0f, float scale = 0.7f, float saturation = 1.0f);
|
||||
|
||||
/** @brief The base class for algorithms that align images of the same scene with different exposures
|
||||
*/
|
||||
class CV_EXPORTS_W AlignExposures : public Algorithm
|
||||
{
|
||||
public:
|
||||
/** @brief Aligns images
|
||||
|
||||
@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.
|
||||
*/
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, std::vector<Mat>& dst,
|
||||
InputArray times, InputArray response) = 0;
|
||||
};
|
||||
|
||||
/** @brief 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 @cite GW03 .
|
||||
*/
|
||||
class CV_EXPORTS_W AlignMTB : public AlignExposures
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, std::vector<Mat>& dst,
|
||||
InputArray times, InputArray response) CV_OVERRIDE = 0;
|
||||
|
||||
/** @brief Short version of process, that doesn't take extra arguments.
|
||||
|
||||
@param src vector of input images
|
||||
@param dst vector of aligned images
|
||||
*/
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, std::vector<Mat>& dst) = 0;
|
||||
|
||||
/** @brief Calculates shift between two images, i. e. how to shift the second image to correspond it with the
|
||||
first.
|
||||
|
||||
@param img0 first image
|
||||
@param img1 second image
|
||||
*/
|
||||
CV_WRAP virtual Point calculateShift(InputArray img0, InputArray img1) = 0;
|
||||
/** @brief Helper function, that shift Mat filling new regions with zeros.
|
||||
|
||||
@param src input image
|
||||
@param dst result image
|
||||
@param shift shift value
|
||||
*/
|
||||
CV_WRAP virtual void shiftMat(InputArray src, OutputArray dst, const Point shift) = 0;
|
||||
/** @brief Computes median threshold and exclude bitmaps of given image.
|
||||
|
||||
@param img input image
|
||||
@param tb median threshold bitmap
|
||||
@param eb exclude bitmap
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/** @brief Creates AlignMTB object
|
||||
|
||||
@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.
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<AlignMTB> createAlignMTB(int max_bits = 6, int exclude_range = 4, bool cut = true);
|
||||
|
||||
/** @brief The base class for camera response calibration algorithms.
|
||||
*/
|
||||
class CV_EXPORTS_W CalibrateCRF : public Algorithm
|
||||
{
|
||||
public:
|
||||
/** @brief Recovers inverse camera response.
|
||||
|
||||
@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
|
||||
*/
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, InputArray times) = 0;
|
||||
};
|
||||
|
||||
/** @brief 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 @cite DM97 .
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/** @brief Creates CalibrateDebevec object
|
||||
|
||||
@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 they form a
|
||||
rectangular grid.
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<CalibrateDebevec> createCalibrateDebevec(int samples = 70, float lambda = 10.0f, bool random = false);
|
||||
|
||||
/** @brief 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 @cite RB99 .
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/** @brief Creates CalibrateRobertson object
|
||||
|
||||
@param max_iter maximal number of Gauss-Seidel solver iterations.
|
||||
@param threshold target difference between results of two successive steps of the minimization.
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<CalibrateRobertson> createCalibrateRobertson(int max_iter = 30, float threshold = 0.01f);
|
||||
|
||||
/** @brief The base class algorithms that can merge exposure sequence to a single image.
|
||||
*/
|
||||
class CV_EXPORTS_W MergeExposures : public Algorithm
|
||||
{
|
||||
public:
|
||||
/** @brief Merges images.
|
||||
|
||||
@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.
|
||||
*/
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
|
||||
InputArray times, InputArray response) = 0;
|
||||
};
|
||||
|
||||
/** @brief The resulting HDR image is calculated as weighted average of the exposures considering exposure
|
||||
values and camera response.
|
||||
|
||||
For more information see @cite DM97 .
|
||||
*/
|
||||
class CV_EXPORTS_W MergeDebevec : public MergeExposures
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
|
||||
InputArray times, InputArray response) CV_OVERRIDE = 0;
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, InputArray times) = 0;
|
||||
};
|
||||
|
||||
/** @brief Creates MergeDebevec object
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<MergeDebevec> createMergeDebevec();
|
||||
|
||||
/** @brief 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 @cite MK07 .
|
||||
*/
|
||||
class CV_EXPORTS_W MergeMertens : public MergeExposures
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
|
||||
InputArray times, InputArray response) CV_OVERRIDE = 0;
|
||||
/** @brief Short version of process, that doesn't take extra arguments.
|
||||
|
||||
@param src vector of input images
|
||||
@param dst result image
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/** @brief Creates MergeMertens object
|
||||
|
||||
@param contrast_weight contrast measure weight. See MergeMertens.
|
||||
@param saturation_weight saturation measure weight
|
||||
@param exposure_weight well-exposedness measure weight
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<MergeMertens>
|
||||
createMergeMertens(float contrast_weight = 1.0f, float saturation_weight = 1.0f, float exposure_weight = 0.0f);
|
||||
|
||||
/** @brief The resulting HDR image is calculated as weighted average of the exposures considering exposure
|
||||
values and camera response.
|
||||
|
||||
For more information see @cite RB99 .
|
||||
*/
|
||||
class CV_EXPORTS_W MergeRobertson : public MergeExposures
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
|
||||
InputArray times, InputArray response) CV_OVERRIDE = 0;
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, InputArray times) = 0;
|
||||
};
|
||||
|
||||
/** @brief Creates MergeRobertson object
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<MergeRobertson> createMergeRobertson();
|
||||
|
||||
//! @} photo_hdr
|
||||
|
||||
//! @addtogroup photo_decolor
|
||||
//! @{
|
||||
|
||||
/** @brief Transforms a color image to a grayscale image. It is a basic tool in digital printing, stylized
|
||||
black-and-white photograph rendering, and in many single channel image processing applications
|
||||
@cite CL12 .
|
||||
|
||||
@param src Input 8-bit 3-channel image.
|
||||
@param grayscale Output 8-bit 1-channel image.
|
||||
@param color_boost Output 8-bit 3-channel image.
|
||||
|
||||
This function is to be applied on color images.
|
||||
*/
|
||||
CV_EXPORTS_W void decolor( InputArray src, OutputArray grayscale, OutputArray color_boost);
|
||||
|
||||
//! @} photo_decolor
|
||||
|
||||
//! @addtogroup photo_clone
|
||||
//! @{
|
||||
|
||||
|
||||
//! Flags for the seamlessClone algorithm
|
||||
enum SeamlessCloneFlags
|
||||
{
|
||||
/**
|
||||
@brief Normal seamless cloning.
|
||||
This method is ideal for inserting objects with complex outlines into a new background.
|
||||
It preserves the original appearance and lighting of the inserted object, ensuring a natural blend.
|
||||
*/
|
||||
NORMAL_CLONE = 1,
|
||||
|
||||
/**
|
||||
@brief Mixed seamless cloning.
|
||||
This method addresses cases where simple color-based selection or alpha masking is time-consuming
|
||||
and may result in undesirable halos. By combining structure from the source and texture from the
|
||||
destination, mixed seamless cloning is highly effective, even with loosely defined selections.
|
||||
*/
|
||||
MIXED_CLONE = 2,
|
||||
|
||||
/**
|
||||
@brief Monochrome transfer cloning.
|
||||
This method allows users to replace specific features of an object, such as grayscale textures
|
||||
or patterns, with alternative features. It is particularly useful for artistic effects or
|
||||
targeted object modifications.
|
||||
*/
|
||||
MONOCHROME_TRANSFER = 3,
|
||||
|
||||
/**
|
||||
@brief Enhanced normal seamless cloning.
|
||||
Similar to `NORMAL_CLONE`, but with an advanced approach to ROI (Region of Interest) calculation.
|
||||
This mode processes a larger source region by considering the entire mask area instead of only
|
||||
the bounding rectangle of non-zero pixels.
|
||||
*/
|
||||
NORMAL_CLONE_WIDE = 9,
|
||||
|
||||
/**
|
||||
@brief Enhanced mixed seamless cloning.
|
||||
Similar to `MIXED_CLONE`, but with an advanced approach to ROI (Region of Interest) calculation.
|
||||
This mode processes a larger source region by considering the entire mask area instead of only
|
||||
the bounding rectangle of non-zero pixels.
|
||||
*/
|
||||
MIXED_CLONE_WIDE = 10,
|
||||
|
||||
/**
|
||||
@brief Enhanced monochrome transfer cloning.
|
||||
Similar to `MONOCHROME_TRANSFER`, but with an advanced approach to ROI (Region of Interest) calculation.
|
||||
This mode processes a larger source region by considering the entire mask area instead of only
|
||||
the bounding rectangle of non-zero pixels.
|
||||
*/
|
||||
MONOCHROME_TRANSFER_WIDE = 11
|
||||
};
|
||||
|
||||
|
||||
/** @example samples/cpp/tutorial_code/photo/seamless_cloning/cloning_demo.cpp
|
||||
An example using seamlessClone function
|
||||
*/
|
||||
/** @example samples/cpp/snippets/cloning_demo.cpp
|
||||
An example using illuminationChange, colorChange, seamlessClone, textureFlattening functions
|
||||
*/
|
||||
/** @brief Performs seamless cloning to blend a region from a source image into a destination image.
|
||||
This function is designed for local image editing, allowing changes restricted to a region
|
||||
(manually selected as the ROI) to be applied effortlessly and seamlessly. These changes can
|
||||
range from slight distortions to complete replacement by novel content @cite PM03.
|
||||
|
||||
@param src The source image (8-bit 3-channel), from which a region will be blended into the destination.
|
||||
@param dst The destination image (8-bit 3-channel), where the src image will be blended.
|
||||
@param mask A binary mask (8-bit, 1, 3, or 4-channel) specifying the region in the source image to blend.
|
||||
Non-zero pixels indicate the region to be blended. If an empty Mat is provided, a mask with
|
||||
all non-zero pixels is created internally.
|
||||
@param p The point where the center of the src image is placed in the dst image.
|
||||
@param blend The output image that stores the result of the seamless cloning. It has the same size and type as `dst`.
|
||||
@param flags Flags that control the type of cloning method, can take values of `cv::SeamlessCloneFlags`.
|
||||
*/
|
||||
CV_EXPORTS_W void seamlessClone( InputArray src, InputArray dst, InputArray mask, Point p,
|
||||
OutputArray blend, int flags);
|
||||
|
||||
/** @brief Given an original color image, two differently colored versions of this image can be mixed
|
||||
seamlessly.
|
||||
|
||||
@param src Input 8-bit 3-channel image.
|
||||
@param mask Input 8-bit 1 or 3-channel image.
|
||||
@param dst Output image with the same size and type as src .
|
||||
@param red_mul R-channel multiply factor.
|
||||
@param green_mul G-channel multiply factor.
|
||||
@param blue_mul B-channel multiply factor.
|
||||
|
||||
Multiplication factor is between .5 to 2.5.
|
||||
*/
|
||||
CV_EXPORTS_W void colorChange(InputArray src, InputArray mask, OutputArray dst, float red_mul = 1.0f,
|
||||
float green_mul = 1.0f, float blue_mul = 1.0f);
|
||||
|
||||
/** @brief Applying an appropriate non-linear transformation to the gradient field inside the selection and
|
||||
then integrating back with a Poisson solver, modifies locally the apparent illumination of an image.
|
||||
|
||||
@param src Input 8-bit 3-channel image.
|
||||
@param mask Input 8-bit 1 or 3-channel image.
|
||||
@param dst Output image with the same size and type as src.
|
||||
@param alpha Value ranges between 0-2.
|
||||
@param beta Value ranges between 0-2.
|
||||
|
||||
This is useful to highlight under-exposed foreground objects or to reduce specular reflections.
|
||||
*/
|
||||
CV_EXPORTS_W void illuminationChange(InputArray src, InputArray mask, OutputArray dst,
|
||||
float alpha = 0.2f, float beta = 0.4f);
|
||||
|
||||
/** @brief By retaining only the gradients at edge locations, before integrating with the Poisson solver, one
|
||||
washes out the texture of the selected region, giving its contents a flat aspect. Here Canny Edge %Detector is used.
|
||||
|
||||
@param src Input 8-bit 3-channel image.
|
||||
@param mask Input 8-bit 1 or 3-channel image.
|
||||
@param dst Output image with the same size and type as src.
|
||||
@param low_threshold %Range from 0 to 100.
|
||||
@param high_threshold Value \> 100.
|
||||
@param kernel_size The size of the Sobel kernel to be used.
|
||||
|
||||
@note
|
||||
The algorithm assumes that the color of the source image is close to that of the destination. This
|
||||
assumption means that when the colors don't match, the source image color gets tinted toward the
|
||||
color of the destination image.
|
||||
*/
|
||||
CV_EXPORTS_W void textureFlattening(InputArray src, InputArray mask, OutputArray dst,
|
||||
float low_threshold = 30, float high_threshold = 45,
|
||||
int kernel_size = 3);
|
||||
|
||||
//! @} photo_clone
|
||||
|
||||
//! @addtogroup photo_render
|
||||
//! @{
|
||||
|
||||
//! Edge preserving filters
|
||||
enum
|
||||
{
|
||||
RECURS_FILTER = 1, //!< Recursive Filtering
|
||||
NORMCONV_FILTER = 2 //!< Normalized Convolution Filtering
|
||||
};
|
||||
|
||||
/** @brief Filtering is the fundamental operation in image and video processing. Edge-preserving smoothing
|
||||
filters are used in many different applications @cite EM11 .
|
||||
|
||||
@param src Input 8-bit 3-channel image.
|
||||
@param dst Output 8-bit 3-channel image.
|
||||
@param flags Edge preserving filters: cv::RECURS_FILTER or cv::NORMCONV_FILTER
|
||||
@param sigma_s %Range between 0 to 200.
|
||||
@param sigma_r %Range between 0 to 1.
|
||||
*/
|
||||
CV_EXPORTS_W void edgePreservingFilter(InputArray src, OutputArray dst, int flags = 1,
|
||||
float sigma_s = 60, float sigma_r = 0.4f);
|
||||
|
||||
/** @brief This filter enhances the details of a particular image.
|
||||
|
||||
@param src Input 8-bit 3-channel image.
|
||||
@param dst Output image with the same size and type as src.
|
||||
@param sigma_s %Range between 0 to 200.
|
||||
@param sigma_r %Range between 0 to 1.
|
||||
*/
|
||||
CV_EXPORTS_W void detailEnhance(InputArray src, OutputArray dst, float sigma_s = 10,
|
||||
float sigma_r = 0.15f);
|
||||
|
||||
/** @example samples/cpp/tutorial_code/photo/non_photorealistic_rendering/npr_demo.cpp
|
||||
An example using non-photorealistic line drawing functions
|
||||
*/
|
||||
/** @brief Pencil-like non-photorealistic line drawing
|
||||
|
||||
@param src Input 8-bit 3-channel image.
|
||||
@param dst1 Output 8-bit 1-channel image.
|
||||
@param dst2 Output image with the same size and type as src.
|
||||
@param sigma_s %Range between 0 to 200.
|
||||
@param sigma_r %Range between 0 to 1.
|
||||
@param shade_factor %Range between 0 to 0.1.
|
||||
*/
|
||||
CV_EXPORTS_W void pencilSketch(InputArray src, OutputArray dst1, OutputArray dst2,
|
||||
float sigma_s = 60, float sigma_r = 0.07f, float shade_factor = 0.02f);
|
||||
|
||||
/** @brief Stylization aims to produce digital imagery with a wide variety of effects not focused on
|
||||
photorealism. Edge-aware filters are ideal for stylization, as they can abstract regions of low
|
||||
contrast while preserving, or enhancing, high-contrast features.
|
||||
|
||||
@param src Input 8-bit 3-channel image.
|
||||
@param dst Output image with the same size and type as src.
|
||||
@param sigma_s %Range between 0 to 200.
|
||||
@param sigma_r %Range between 0 to 1.
|
||||
*/
|
||||
CV_EXPORTS_W void stylization(InputArray src, OutputArray dst, float sigma_s = 60,
|
||||
float sigma_r = 0.45f);
|
||||
|
||||
//! @} photo_render
|
||||
|
||||
//! @addtogroup photo_ca_correction Chromatic Aberration Correction
|
||||
//! @{
|
||||
|
||||
/** @example samples/cpp/snippets/chromatic_aberration_correction.cpp
|
||||
An example correcting chromatic aberration with C++
|
||||
*/
|
||||
/** @example samples/python/snippets/chromatic_aberration_correction.py
|
||||
* An example correcting chromatic aberration with Python
|
||||
*/
|
||||
/** @brief Corrects lateral chromatic aberration in an image using polynomial distortion model.
|
||||
|
||||
This function loads polynomial calibration data from the specified file and applies
|
||||
a channel‐specific warp to remove chromatic aberration.
|
||||
If @p input_image has one channel, it is assumed to be a raw Bayer image and is
|
||||
first demosaiced using @p bayer_pattern. If it has three channels, it is treated
|
||||
as a BGR image and @p bayer_pattern is ignored.
|
||||
|
||||
Firstly, calibration needs to be done using apps/chromatic-aberration-calibration/ca_calibration.py on a photo of
|
||||
a pattern of black discs on white background, included in opencv_extra/testdata/cv/cameracalibration/chromatic_aberration/chromatic_aberration_pattern_a3.png
|
||||
|
||||
Calibration and correction are based on the algorithm described in @cite rudakova2013precise.
|
||||
The chromatic aberration is modeled as a polynomial of some degree in red and blue channels compared to green.
|
||||
In calibration, a photo of many black discs on white background is used, and the displacements
|
||||
between the centres of discs in red and blue channels compared to green are minimized. The coefficients
|
||||
are then saved in a yaml file which can be used with this function to correct lateral chromatic aberration.
|
||||
|
||||
@param input_image Input BGR image to correct
|
||||
@param coefficients Coefficient model
|
||||
@param output_image Corrected BGR image
|
||||
@param image_size Size of images for the calibration coefficient model
|
||||
@param calib_degree Degree of the calibration coefficient model
|
||||
@param bayer_pattern Bayer pattern code (e.g. cv::COLOR_BayerBG2BGR) used for
|
||||
demosaicing when @p input_image has one channel; ignored otherwise.
|
||||
|
||||
@sa loadChromaticAberrationParams, demosaicing
|
||||
*/
|
||||
CV_EXPORTS_W void correctChromaticAberration(InputArray input_image, InputArray coefficients, OutputArray output_image,
|
||||
const Size& image_size, int calib_degree, int bayer_pattern = -1);
|
||||
|
||||
/** @brief Load chromatic-aberration calibration parameters from opened FileStorage.
|
||||
*
|
||||
R e*ads the red and blue polynomial coefficients from the specified file and
|
||||
packs them into a 4×N CV_32F matrix:
|
||||
row 0 = blue dx coefficients
|
||||
row 1 = blue dy coefficients
|
||||
row 2 = red dx coefficients
|
||||
row 3 = red dy coefficients
|
||||
|
||||
@param node Node of opened cv::FileStorage object.
|
||||
@param coeffMat Output 4xN coefficient matrix (CV_32F).
|
||||
@param degree Polynomial degree inferred from N.
|
||||
@param calib_size Calibration image size read from file.
|
||||
|
||||
@sa correctChromaticAberration
|
||||
*/
|
||||
CV_EXPORTS_W void loadChromaticAberrationParams(
|
||||
const FileNode& node,
|
||||
OutputArray coeffMat,
|
||||
CV_OUT Size& calib_size,
|
||||
CV_OUT int& degree);
|
||||
|
||||
//! @} photo_ca_correction
|
||||
|
||||
//! @} photo
|
||||
|
||||
} // cv
|
||||
|
||||
#include "./photo/segmentation.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,322 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef OPENCV_PHOTO_CCM_HPP
|
||||
#define OPENCV_PHOTO_CCM_HPP
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace ccm
|
||||
{
|
||||
|
||||
/** @defgroup ccm Color Correction module
|
||||
@{
|
||||
*/
|
||||
|
||||
/** @brief Enum of the possible types of ccm.
|
||||
*/
|
||||
enum CcmType
|
||||
{
|
||||
CCM_LINEAR, ///< Uses a \f$3\times3\f$ matrix to linearly transform RGB values without offsets.
|
||||
CCM_AFFINE, ///< Uses a \f$4\times3\f$ matrix to affine transform RGB values with both scaling and offset terms.
|
||||
};
|
||||
|
||||
/** @brief Enum of the possible types of initial method.
|
||||
*/
|
||||
enum InitialMethodType
|
||||
{
|
||||
INITIAL_METHOD_WHITE_BALANCE, ///< The white balance method. The initial value is:\n
|
||||
/// \f$
|
||||
/// M_{CCM}=
|
||||
/// \begin{bmatrix}
|
||||
/// k_R & 0 & 0\\
|
||||
/// 0 & k_G & 0\\
|
||||
/// 0 & 0 & k_B\\
|
||||
/// \end{bmatrix}
|
||||
/// \f$\n
|
||||
/// where\n
|
||||
/// \f$
|
||||
/// k_R=mean(R_{li}')/mean(R_{li})\\
|
||||
/// k_G=mean(G_{li}')/mean(G_{li})\\
|
||||
/// k_B=mean(B_{li}')/mean(B_{li})
|
||||
/// \f$
|
||||
INITIAL_METHOD_LEAST_SQUARE, ///< The least square method is an optimal solution under the linear RGB distance function
|
||||
};
|
||||
/** @brief Macbeth and Vinyl ColorChecker with 2deg D50
|
||||
*/
|
||||
enum ColorCheckerType {
|
||||
COLORCHECKER_MACBETH, ///< Macbeth ColorChecker
|
||||
COLORCHECKER_VINYL, ///< DKK ColorChecker
|
||||
COLORCHECKER_DIGITAL_SG, ///< DigitalSG ColorChecker with 140 squares
|
||||
};
|
||||
enum ColorSpace {
|
||||
COLOR_SPACE_SRGB, ///< https://en.wikipedia.org/wiki/SRGB , RGB color space
|
||||
COLOR_SPACE_SRGBL, ///< https://en.wikipedia.org/wiki/SRGB , linear RGB color space
|
||||
COLOR_SPACE_ADOBE_RGB, ///< https://en.wikipedia.org/wiki/Adobe_RGB_color_space , RGB color space
|
||||
COLOR_SPACE_ADOBE_RGBL, ///< https://en.wikipedia.org/wiki/Adobe_RGB_color_space , linear RGB color space
|
||||
COLOR_SPACE_WIDE_GAMUT_RGB, ///< https://en.wikipedia.org/wiki/Wide-gamut_RGB_color_space , RGB color space
|
||||
COLOR_SPACE_WIDE_GAMUT_RGBL, ///< https://en.wikipedia.org/wiki/Wide-gamut_RGB_color_space , linear RGB color space
|
||||
COLOR_SPACE_PRO_PHOTO_RGB, ///< https://en.wikipedia.org/wiki/ProPhoto_RGB_color_space , RGB color space
|
||||
COLOR_SPACE_PRO_PHOTO_RGBL, ///< https://en.wikipedia.org/wiki/ProPhoto_RGB_color_space , linear RGB color space
|
||||
COLOR_SPACE_DCI_P3_RGB, ///< https://en.wikipedia.org/wiki/DCI-P3 , RGB color space
|
||||
COLOR_SPACE_DCI_P3_RGBL, ///< https://en.wikipedia.org/wiki/DCI-P3 , linear RGB color space
|
||||
COLOR_SPACE_APPLE_RGB, ///< http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html , RGB color space
|
||||
COLOR_SPACE_APPLE_RGBL, ///< http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html , linear RGB color space
|
||||
COLOR_SPACE_REC_709_RGB, ///< https://en.wikipedia.org/wiki/Rec._709 , RGB color space
|
||||
COLOR_SPACE_REC_709_RGBL, ///< https://en.wikipedia.org/wiki/Rec._709 , linear RGB color space
|
||||
COLOR_SPACE_REC_2020_RGB, ///< https://en.wikipedia.org/wiki/Rec._2020 , RGB color space
|
||||
COLOR_SPACE_REC_2020_RGBL, ///< https://en.wikipedia.org/wiki/Rec._2020 , linear RGB color space
|
||||
COLOR_SPACE_XYZ_D65_2, ///< https://en.wikipedia.org/wiki/CIE_1931_color_space , XYZ color space, D65 illuminant, 2 degree
|
||||
COLOR_SPACE_XYZ_D50_2, ///< https://en.wikipedia.org/wiki/CIE_1931_color_space , XYZ color space, D50 illuminant, 2 degree
|
||||
COLOR_SPACE_XYZ_D65_10, ///< https://en.wikipedia.org/wiki/CIE_1931_color_space , XYZ color space, D65 illuminant, 10 degree
|
||||
COLOR_SPACE_XYZ_D50_10, ///< https://en.wikipedia.org/wiki/CIE_1931_color_space , XYZ color space, D50 illuminant, 10 degree
|
||||
COLOR_SPACE_XYZ_A_2, ///< https://en.wikipedia.org/wiki/CIE_1931_color_space , XYZ color space, A illuminant, 2 degree
|
||||
COLOR_SPACE_XYZ_A_10, ///< https://en.wikipedia.org/wiki/CIE_1931_color_space , XYZ color space, A illuminant, 10 degree
|
||||
COLOR_SPACE_XYZ_D55_2, ///< https://en.wikipedia.org/wiki/CIE_1931_color_space , XYZ color space, D55 illuminant, 2 degree
|
||||
COLOR_SPACE_XYZ_D55_10, ///< https://en.wikipedia.org/wiki/CIE_1931_color_space , XYZ color space, D55 illuminant, 10 degree
|
||||
COLOR_SPACE_XYZ_D75_2, ///< https://en.wikipedia.org/wiki/CIE_1931_color_space , XYZ color space, D75 illuminant, 2 degree
|
||||
COLOR_SPACE_XYZ_D75_10, ///< https://en.wikipedia.org/wiki/CIE_1931_color_space , XYZ color space, D75 illuminant, 10 degree
|
||||
COLOR_SPACE_XYZ_E_2, ///< https://en.wikipedia.org/wiki/CIE_1931_color_space , XYZ color space, E illuminant, 2 degree
|
||||
COLOR_SPACE_XYZ_E_10, ///< https://en.wikipedia.org/wiki/CIE_1931_color_space , XYZ color space, E illuminant, 10 degree
|
||||
COLOR_SPACE_LAB_D65_2, ///< https://en.wikipedia.org/wiki/CIELAB_color_space , Lab color space, D65 illuminant, 2 degree
|
||||
COLOR_SPACE_LAB_D50_2, ///< https://en.wikipedia.org/wiki/CIELAB_color_space , Lab color space, D50 illuminant, 2 degree
|
||||
COLOR_SPACE_LAB_D65_10, ///< https://en.wikipedia.org/wiki/CIELAB_color_space , Lab color space, D65 illuminant, 10 degree
|
||||
COLOR_SPACE_LAB_D50_10, ///< https://en.wikipedia.org/wiki/CIELAB_color_space , Lab color space, D50 illuminant, 10 degree
|
||||
COLOR_SPACE_LAB_A_2, ///< https://en.wikipedia.org/wiki/CIELAB_color_space , Lab color space, A illuminant, 2 degree
|
||||
COLOR_SPACE_LAB_A_10, ///< https://en.wikipedia.org/wiki/CIELAB_color_space , Lab color space, A illuminant, 10 degree
|
||||
COLOR_SPACE_LAB_D55_2, ///< https://en.wikipedia.org/wiki/CIELAB_color_space , Lab color space, D55 illuminant, 2 degree
|
||||
COLOR_SPACE_LAB_D55_10, ///< https://en.wikipedia.org/wiki/CIELAB_color_space , Lab color space, D55 illuminant, 10 degree
|
||||
COLOR_SPACE_LAB_D75_2, ///< https://en.wikipedia.org/wiki/CIELAB_color_space , Lab color space, D75 illuminant, 2 degree
|
||||
COLOR_SPACE_LAB_D75_10, ///< https://en.wikipedia.org/wiki/CIELAB_color_space , Lab color space, D75 illuminant, 10 degree
|
||||
COLOR_SPACE_LAB_E_2, ///< https://en.wikipedia.org/wiki/CIELAB_color_space , Lab color space, E illuminant, 2 degree
|
||||
COLOR_SPACE_LAB_E_10 ///< https://en.wikipedia.org/wiki/CIELAB_color_space , Lab color space, E illuminant, 10 degree
|
||||
};
|
||||
|
||||
/** @brief Linearization transformation type
|
||||
*/
|
||||
enum LinearizationType
|
||||
{
|
||||
|
||||
LINEARIZATION_IDENTITY, ///<no change is made
|
||||
LINEARIZATION_GAMMA, ///<gamma correction; Need assign a value to gamma simultaneously
|
||||
LINEARIZATION_COLORPOLYFIT, ///<polynomial fitting channels respectively; Need assign a value to deg simultaneously
|
||||
LINEARIZATION_COLORLOGPOLYFIT, ///<logarithmic polynomial fitting channels respectively; Need assign a value to deg simultaneously
|
||||
LINEARIZATION_GRAYPOLYFIT, ///<grayscale polynomial fitting; Need assign a value to deg and dst_whites simultaneously
|
||||
LINEARIZATION_GRAYLOGPOLYFIT ///<grayscale Logarithmic polynomial fitting; Need assign a value to deg and dst_whites simultaneously
|
||||
};
|
||||
|
||||
/** @brief Enum of possible functions to calculate the distance between colors.
|
||||
|
||||
See https://en.wikipedia.org/wiki/Color_difference for details
|
||||
*/
|
||||
enum DistanceType
|
||||
{
|
||||
DISTANCE_CIE76, ///<The 1976 formula is the first formula that related a measured color difference to a known set of CIELAB coordinates.
|
||||
DISTANCE_CIE94_GRAPHIC_ARTS, ///<The 1976 definition was extended to address perceptual non-uniformities.
|
||||
DISTANCE_CIE94_TEXTILES,
|
||||
DISTANCE_CIE2000,
|
||||
DISTANCE_CMC_1TO1, ///<In 1984, the Colour Measurement Committee of the Society of Dyers and Colourists defined a difference measure, also based on the L*C*h color model.
|
||||
DISTANCE_CMC_2TO1,
|
||||
DISTANCE_RGB, ///<Euclidean distance of rgb color space
|
||||
DISTANCE_RGBL ///<Euclidean distance of rgbl color space
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Applies gamma correction to the input image.
|
||||
* @param src Input image.
|
||||
* @param dst Output image.
|
||||
* @param gamma Gamma correction greater than zero.
|
||||
*/
|
||||
CV_EXPORTS_W void gammaCorrection(InputArray src, OutputArray dst, double gamma);
|
||||
|
||||
/** @brief Core class of ccm model
|
||||
|
||||
Produce a ColorCorrectionModel instance for inference
|
||||
*/
|
||||
class CV_EXPORTS_W ColorCorrectionModel
|
||||
{
|
||||
public:
|
||||
CV_WRAP ColorCorrectionModel();
|
||||
|
||||
/** @brief Color Correction Model
|
||||
|
||||
Supported list of color cards:
|
||||
- @ref COLORCHECKER_MACBETH, the Macbeth ColorChecker
|
||||
- @ref COLORCHECKER_VINYL, the DKK ColorChecker
|
||||
- @ref COLORCHECKER_DIGITAL_SG, the DigitalSG ColorChecker with 140 squares
|
||||
|
||||
@param src detected colors of ColorChecker patches;
|
||||
the color type is RGB not BGR, and the color values are in [0, 1];
|
||||
@param constColor the Built-in color card
|
||||
*/
|
||||
CV_WRAP ColorCorrectionModel(InputArray src, int constColor);
|
||||
|
||||
/** @brief Color Correction Model
|
||||
@param src detected colors of ColorChecker patches;
|
||||
the color type is RGB not BGR, and the color values are in [0, 1];
|
||||
@param colors the reference color values, the color values are in [0, 1].
|
||||
@param refColorSpace the corresponding color space
|
||||
If the color type is some RGB, the format is RGB not BGR;
|
||||
*/
|
||||
CV_WRAP ColorCorrectionModel(InputArray src, InputArray colors, ColorSpace refColorSpace);
|
||||
|
||||
/** @brief Color Correction Model
|
||||
@param src detected colors of ColorChecker patches;
|
||||
the color type is RGB not BGR, and the color values are in [0, 1];
|
||||
@param colors the reference color values, the color values are in [0, 1].
|
||||
@param refColorSpace the corresponding color space
|
||||
If the color type is some RGB, the format is RGB not BGR;
|
||||
@param coloredPatchesMask binary mask indicating which patches are colored (non-gray) patches
|
||||
*/
|
||||
CV_WRAP ColorCorrectionModel(InputArray src, InputArray colors, ColorSpace refColorSpace, InputArray coloredPatchesMask);
|
||||
|
||||
/** @brief set ColorSpace
|
||||
@note It should be some RGB color space;
|
||||
Supported list of color cards:
|
||||
- @ref COLOR_SPACE_SRGB
|
||||
- @ref COLOR_SPACE_ADOBE_RGB
|
||||
- @ref COLOR_SPACE_WIDE_GAMUT_RGB
|
||||
- @ref COLOR_SPACE_PRO_PHOTO_RGB
|
||||
- @ref COLOR_SPACE_DCI_P3_RGB
|
||||
- @ref COLOR_SPACE_APPLE_RGB
|
||||
- @ref COLOR_SPACE_REC_709_RGB
|
||||
- @ref COLOR_SPACE_REC_2020_RGB
|
||||
@param cs the absolute color space that detected colors convert to;
|
||||
default: @ref COLOR_SPACE_SRGB
|
||||
*/
|
||||
CV_WRAP void setColorSpace(ColorSpace cs);
|
||||
|
||||
/** @brief set ccmType
|
||||
@param ccmType the shape of color correction matrix(CCM);
|
||||
default: @ref CCM_LINEAR
|
||||
*/
|
||||
CV_WRAP void setCcmType(CcmType ccmType);
|
||||
|
||||
/** @brief set Distance
|
||||
@param distance the type of color distance;
|
||||
default: @ref DISTANCE_CIE2000
|
||||
*/
|
||||
CV_WRAP void setDistance(DistanceType distance);
|
||||
|
||||
/** @brief set Linear
|
||||
@param linearizationType the method of linearization;
|
||||
default: @ref LINEARIZATION_GAMMA
|
||||
*/
|
||||
CV_WRAP void setLinearization(LinearizationType linearizationType);
|
||||
|
||||
/** @brief set Gamma
|
||||
|
||||
@note only valid when linear is set to "gamma";
|
||||
|
||||
@param gamma the gamma value of gamma correction;
|
||||
default: 2.2;
|
||||
*/
|
||||
CV_WRAP void setLinearizationGamma(double gamma);
|
||||
|
||||
/** @brief set degree
|
||||
@note only valid when linear is set to
|
||||
- @ref LINEARIZATION_COLORPOLYFIT
|
||||
- @ref LINEARIZATION_GRAYPOLYFIT
|
||||
- @ref LINEARIZATION_COLORLOGPOLYFIT
|
||||
- @ref LINEARIZATION_GRAYLOGPOLYFIT
|
||||
|
||||
@param deg the degree of linearization polynomial
|
||||
default: 3
|
||||
|
||||
*/
|
||||
CV_WRAP void setLinearizationDegree(int deg);
|
||||
|
||||
/** @brief set SaturatedThreshold.
|
||||
The colors in the closed interval [lower, upper] are reserved to participate
|
||||
in the calculation of the loss function and initialization parameters
|
||||
@param lower the lower threshold to determine saturation;
|
||||
default: 0;
|
||||
@param upper the upper threshold to determine saturation;
|
||||
default: 0
|
||||
*/
|
||||
CV_WRAP void setSaturatedThreshold(double lower, double upper);
|
||||
|
||||
/** @brief set WeightsList
|
||||
@param weightsList the list of weight of each color;
|
||||
default: empty array
|
||||
*/
|
||||
CV_WRAP void setWeightsList(const Mat& weightsList);
|
||||
|
||||
/** @brief set WeightCoeff
|
||||
@param weightsCoeff the exponent number of L* component of the reference color in CIE Lab color space;
|
||||
default: 0
|
||||
*/
|
||||
CV_WRAP void setWeightCoeff(double weightsCoeff);
|
||||
|
||||
/** @brief set InitialMethod
|
||||
@param initialMethodType the method of calculating CCM initial value;
|
||||
default: INITIAL_METHOD_LEAST_SQUARE
|
||||
*/
|
||||
CV_WRAP void setInitialMethod(InitialMethodType initialMethodType);
|
||||
|
||||
/** @brief set MaxCount
|
||||
@param maxCount used in MinProblemSolver-DownhillSolver;
|
||||
Terminal criteria to the algorithm;
|
||||
default: 5000;
|
||||
*/
|
||||
CV_WRAP void setMaxCount(int maxCount);
|
||||
|
||||
/** @brief set Epsilon
|
||||
@param epsilon used in MinProblemSolver-DownhillSolver;
|
||||
Terminal criteria to the algorithm;
|
||||
default: 1e-4;
|
||||
*/
|
||||
CV_WRAP void setEpsilon(double epsilon);
|
||||
|
||||
/** @brief Set whether the input image is in RGB color space
|
||||
@param rgb If true, the model expects input images in RGB format.
|
||||
If false, input is assumed to be in BGR (default).
|
||||
*/
|
||||
CV_WRAP void setRGB(bool rgb);
|
||||
|
||||
/** @brief make color correction */
|
||||
CV_WRAP Mat compute();
|
||||
|
||||
CV_WRAP Mat getColorCorrectionMatrix() const;
|
||||
CV_WRAP double getLoss() const;
|
||||
CV_WRAP Mat getSrcLinearRGB() const;
|
||||
CV_WRAP Mat getRefLinearRGB() const;
|
||||
CV_WRAP Mat getMask() const;
|
||||
CV_WRAP Mat getWeights() const;
|
||||
|
||||
/** @brief Applies color correction to the input image using a fitted color correction matrix.
|
||||
*
|
||||
* The conventional ranges for R, G, and B channel values are:
|
||||
- 0 to 255 for CV_8U images
|
||||
- 0 to 65535 for CV_16U images
|
||||
- 0 to 1 for CV_32F images
|
||||
@param src Input 8-bit, 16-bit unsigned or 32-bit float 3-channel image..
|
||||
@param dst Output image of the same size and datatype as src.
|
||||
@param islinear default false.
|
||||
*/
|
||||
CV_WRAP void correctImage(InputArray src, OutputArray dst, bool islinear = false);
|
||||
|
||||
CV_WRAP void write(cv::FileStorage& fs) const;
|
||||
CV_WRAP void read(const cv::FileNode& node);
|
||||
|
||||
class Impl;
|
||||
private:
|
||||
std::shared_ptr<Impl> p;
|
||||
};
|
||||
|
||||
CV_EXPORTS void write(cv::FileStorage& fs, const std::string&, const ColorCorrectionModel& ccm);
|
||||
CV_EXPORTS void read(const cv::FileNode& node, ColorCorrectionModel& ccm, const ColorCorrectionModel& defaultValue = ColorCorrectionModel());
|
||||
|
||||
//! @} ccm
|
||||
} // namespace ccm
|
||||
} // namespace cv
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,157 @@
|
||||
/*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) 2008-2012, 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*/
|
||||
|
||||
#ifndef OPENCV_PHOTO_CUDA_HPP
|
||||
#define OPENCV_PHOTO_CUDA_HPP
|
||||
|
||||
#include "opencv2/core/cuda.hpp"
|
||||
|
||||
namespace cv { namespace cuda {
|
||||
|
||||
//! @addtogroup photo_denoise
|
||||
//! @{
|
||||
|
||||
/** @brief Performs pure non local means denoising without any simplification, and thus it is not fast.
|
||||
|
||||
@param src Source image. Supports only CV_8UC1, CV_8UC2 and CV_8UC3.
|
||||
@param dst Destination image.
|
||||
@param h Filter sigma regulating filter strength for color.
|
||||
@param search_window Size of search window.
|
||||
@param block_size Size of block used for computing weights.
|
||||
@param borderMode Border type. See borderInterpolate for details. BORDER_REFLECT101 ,
|
||||
BORDER_REPLICATE , BORDER_CONSTANT , BORDER_REFLECT and BORDER_WRAP are supported for now.
|
||||
@param stream Stream for the asynchronous version.
|
||||
|
||||
@sa
|
||||
fastNlMeansDenoising
|
||||
*/
|
||||
CV_EXPORTS void nonLocalMeans(InputArray src, OutputArray dst,
|
||||
float h,
|
||||
int search_window = 21,
|
||||
int block_size = 7,
|
||||
int borderMode = BORDER_DEFAULT,
|
||||
Stream& stream = Stream::Null());
|
||||
CV_WRAP inline void nonLocalMeans(const GpuMat& src, CV_OUT GpuMat& dst,
|
||||
float h,
|
||||
int search_window = 21,
|
||||
int block_size = 7,
|
||||
int borderMode = BORDER_DEFAULT,
|
||||
Stream& stream = Stream::Null())
|
||||
{
|
||||
nonLocalMeans(InputArray(src), OutputArray(dst), h, search_window, block_size, borderMode, stream);
|
||||
}
|
||||
|
||||
/** @brief Perform image denoising using Non-local Means Denoising algorithm
|
||||
<http://www.ipol.im/pub/algo/bcm_non_local_means_denoising> with several computational
|
||||
optimizations. Noise expected to be a gaussian white noise
|
||||
|
||||
@param src Input 8-bit 1-channel, 2-channel or 3-channel image.
|
||||
@param dst Output image with the same size and type as src .
|
||||
@param h Parameter regulating filter strength. Big h value perfectly removes noise but also
|
||||
removes image details, smaller h value preserves details but also preserves some noise
|
||||
@param search_window Size in pixels of the window that is used to compute weighted average for
|
||||
given pixel. Should be odd. Affect performance linearly: greater search_window - greater
|
||||
denoising time. Recommended value 21 pixels
|
||||
@param block_size Size in pixels of the template patch that is used to compute weights. Should be
|
||||
odd. Recommended value 7 pixels
|
||||
@param stream Stream for the asynchronous invocations.
|
||||
|
||||
This function expected to be applied to grayscale images. For colored images look at
|
||||
FastNonLocalMeansDenoising::labMethod.
|
||||
|
||||
@sa
|
||||
fastNlMeansDenoising
|
||||
*/
|
||||
CV_EXPORTS void fastNlMeansDenoising(InputArray src, OutputArray dst,
|
||||
float h,
|
||||
int search_window = 21,
|
||||
int block_size = 7,
|
||||
Stream& stream = Stream::Null());
|
||||
CV_WRAP inline void fastNlMeansDenoising(const GpuMat& src, CV_OUT GpuMat& dst,
|
||||
float h,
|
||||
int search_window = 21,
|
||||
int block_size = 7,
|
||||
Stream& stream = Stream::Null())
|
||||
{
|
||||
fastNlMeansDenoising(InputArray(src), OutputArray(dst), h, search_window, block_size, stream);
|
||||
}
|
||||
|
||||
/** @brief Modification of fastNlMeansDenoising function for colored images
|
||||
|
||||
@param src Input 8-bit 3-channel image.
|
||||
@param dst Output image with the same size and type as src .
|
||||
@param h_luminance Parameter regulating filter strength. Big h value perfectly removes noise but
|
||||
also removes image details, smaller h value preserves details but also preserves some noise
|
||||
@param photo_render float The same as h but for color components. For most images value equals 10 will be
|
||||
enough to remove colored noise and do not distort colors
|
||||
@param search_window Size in pixels of the window that is used to compute weighted average for
|
||||
given pixel. Should be odd. Affect performance linearly: greater search_window - greater
|
||||
denoising time. Recommended value 21 pixels
|
||||
@param block_size Size in pixels of the template patch that is used to compute weights. Should be
|
||||
odd. Recommended value 7 pixels
|
||||
@param stream Stream for the asynchronous invocations.
|
||||
|
||||
The function converts image to CIELAB colorspace and then separately denoise L and AB components
|
||||
with given h parameters using FastNonLocalMeansDenoising::simpleMethod function.
|
||||
|
||||
@sa
|
||||
fastNlMeansDenoisingColored
|
||||
*/
|
||||
CV_EXPORTS void fastNlMeansDenoisingColored(InputArray src, OutputArray dst,
|
||||
float h_luminance, float photo_render,
|
||||
int search_window = 21,
|
||||
int block_size = 7,
|
||||
Stream& stream = Stream::Null());
|
||||
CV_WRAP inline void fastNlMeansDenoisingColored(const GpuMat& src, CV_OUT GpuMat& dst,
|
||||
float h_luminance, float photo_render,
|
||||
int search_window = 21,
|
||||
int block_size = 7,
|
||||
Stream& stream = Stream::Null())
|
||||
{
|
||||
fastNlMeansDenoisingColored(InputArray(src), OutputArray(dst), h_luminance, photo_render, search_window, block_size, stream);
|
||||
}
|
||||
|
||||
//! @} photo
|
||||
|
||||
}} // namespace cv { namespace cuda {
|
||||
|
||||
#endif /* OPENCV_PHOTO_CUDA_HPP */
|
||||
@@ -0,0 +1,14 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_PHOTO_LEGACY_CONSTANTS_H
|
||||
#define OPENCV_PHOTO_LEGACY_CONSTANTS_H
|
||||
|
||||
enum InpaintingModes
|
||||
{
|
||||
CV_INPAINT_NS =0,
|
||||
CV_INPAINT_TELEA =1
|
||||
};
|
||||
|
||||
#endif // OPENCV_PHOTO_LEGACY_CONSTANTS_H
|
||||
@@ -0,0 +1,48 @@
|
||||
/*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.
|
||||
// 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*/
|
||||
|
||||
#ifdef __OPENCV_BUILD
|
||||
#error this is a compatibility header which should not be used inside the OpenCV library
|
||||
#endif
|
||||
|
||||
#include "opencv2/photo.hpp"
|
||||
@@ -0,0 +1,141 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_PHOTO_SEGMENTATION_HPP
|
||||
#define OPENCV_PHOTO_SEGMENTATION_HPP
|
||||
|
||||
#include "opencv2/photo.hpp"
|
||||
|
||||
namespace cv {
|
||||
|
||||
namespace segmentation {
|
||||
|
||||
//! @addtogroup photo_segmentation
|
||||
//! @{
|
||||
|
||||
|
||||
/** @brief Intelligent Scissors image segmentation
|
||||
*
|
||||
* This class is used to find the path (contour) between two points
|
||||
* which can be used for image segmentation.
|
||||
*
|
||||
* Usage example:
|
||||
* @snippet snippets/photo_segmentation.cpp usage_example_intelligent_scissors
|
||||
*
|
||||
* Reference: <a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.138.3811&rep=rep1&type=pdf">"Intelligent Scissors for Image Composition"</a>
|
||||
* algorithm designed by Eric N. Mortensen and William A. Barrett, Brigham Young University
|
||||
* @cite Mortensen95intelligentscissors
|
||||
*/
|
||||
class CV_EXPORTS_W_SIMPLE IntelligentScissorsMB
|
||||
{
|
||||
public:
|
||||
CV_WRAP
|
||||
IntelligentScissorsMB();
|
||||
|
||||
/** @brief Specify weights of feature functions
|
||||
*
|
||||
* Consider keeping weights normalized (sum of weights equals to 1.0)
|
||||
* Discrete dynamic programming (DP) goal is minimization of costs between pixels.
|
||||
*
|
||||
* @param weight_non_edge Specify cost of non-edge pixels (default: 0.43f)
|
||||
* @param weight_gradient_direction Specify cost of gradient direction function (default: 0.43f)
|
||||
* @param weight_gradient_magnitude Specify cost of gradient magnitude function (default: 0.14f)
|
||||
*/
|
||||
CV_WRAP
|
||||
IntelligentScissorsMB& setWeights(float weight_non_edge, float weight_gradient_direction, float weight_gradient_magnitude);
|
||||
|
||||
/** @brief Specify gradient magnitude max value threshold
|
||||
*
|
||||
* Zero limit value is used to disable gradient magnitude thresholding (default behavior, as described in original article).
|
||||
* Otherwize pixels with `gradient magnitude >= threshold` have zero cost.
|
||||
*
|
||||
* @note Thresholding should be used for images with irregular regions (to avoid stuck on parameters from high-contract areas, like embedded logos).
|
||||
*
|
||||
* @param gradient_magnitude_threshold_max Specify gradient magnitude max value threshold (default: 0, disabled)
|
||||
*/
|
||||
CV_WRAP
|
||||
IntelligentScissorsMB& setGradientMagnitudeMaxLimit(float gradient_magnitude_threshold_max = 0.0f);
|
||||
|
||||
/** @brief Switch to "Laplacian Zero-Crossing" edge feature extractor and specify its parameters
|
||||
*
|
||||
* This feature extractor is used by default according to article.
|
||||
*
|
||||
* Implementation has additional filtering for regions with low-amplitude noise.
|
||||
* This filtering is enabled through parameter of minimal gradient amplitude (use some small value 4, 8, 16).
|
||||
*
|
||||
* @note Current implementation of this feature extractor is based on processing of grayscale images (color image is converted to grayscale image first).
|
||||
*
|
||||
* @note Canny edge detector is a bit slower, but provides better results (especially on color images): use setEdgeFeatureCannyParameters().
|
||||
*
|
||||
* @param gradient_magnitude_min_value Minimal gradient magnitude value for edge pixels (default: 0, check is disabled)
|
||||
*/
|
||||
CV_WRAP
|
||||
IntelligentScissorsMB& setEdgeFeatureZeroCrossingParameters(float gradient_magnitude_min_value = 0.0f);
|
||||
|
||||
/** @brief Switch edge feature extractor to use Canny edge detector
|
||||
*
|
||||
* @note "Laplacian Zero-Crossing" feature extractor is used by default (following to original article)
|
||||
*
|
||||
* @sa Canny
|
||||
*/
|
||||
CV_WRAP
|
||||
IntelligentScissorsMB& setEdgeFeatureCannyParameters(
|
||||
double threshold1, double threshold2,
|
||||
int apertureSize = 3, bool L2gradient = false
|
||||
);
|
||||
|
||||
/** @brief Specify input image and extract image features
|
||||
*
|
||||
* @param image input image. Type is #CV_8UC1 / #CV_8UC3
|
||||
*/
|
||||
CV_WRAP
|
||||
IntelligentScissorsMB& applyImage(InputArray image);
|
||||
|
||||
/** @brief Specify custom features of input image
|
||||
*
|
||||
* Customized advanced variant of applyImage() call.
|
||||
*
|
||||
* @param non_edge Specify cost of non-edge pixels. Type is CV_8UC1. Expected values are `{0, 1}`.
|
||||
* @param gradient_direction Specify gradient direction feature. Type is CV_32FC2. Values are expected to be normalized: `x^2 + y^2 == 1`
|
||||
* @param gradient_magnitude Specify cost of gradient magnitude function: Type is CV_32FC1. Values should be in range `[0, 1]`.
|
||||
* @param image **Optional parameter**. Must be specified if subset of features is specified (non-specified features are calculated internally)
|
||||
*/
|
||||
CV_WRAP
|
||||
IntelligentScissorsMB& applyImageFeatures(
|
||||
InputArray non_edge, InputArray gradient_direction, InputArray gradient_magnitude,
|
||||
InputArray image = noArray()
|
||||
);
|
||||
|
||||
/** @brief Prepares a map of optimal paths for the given source point on the image
|
||||
*
|
||||
* @note applyImage() / applyImageFeatures() must be called before this call
|
||||
*
|
||||
* @param sourcePt The source point used to find the paths
|
||||
*/
|
||||
CV_WRAP void buildMap(const Point& sourcePt);
|
||||
|
||||
/** @brief Extracts optimal contour for the given target point on the image
|
||||
*
|
||||
* @note buildMap() must be called before this call
|
||||
*
|
||||
* @param targetPt The target point
|
||||
* @param[out] contour The list of pixels which contains optimal path between the source and the target points of the image. Type is CV_32SC2 (compatible with `std::vector<Point>`)
|
||||
* @param backward Flag to indicate reverse order of retrieved pixels (use "true" value to fetch points from the target to the source point)
|
||||
*/
|
||||
CV_WRAP void getContour(const Point& targetPt, OutputArray contour, bool backward = false) const;
|
||||
|
||||
#ifndef CV_DOXYGEN
|
||||
struct Impl;
|
||||
inline Impl* getImpl() const { return impl.get(); }
|
||||
protected:
|
||||
std::shared_ptr<Impl> impl;
|
||||
#endif
|
||||
};
|
||||
|
||||
//! @}
|
||||
|
||||
} // namespace segmentation
|
||||
} // namespace cv
|
||||
|
||||
#endif // OPENCV_PHOTO_SEGMENTATION_HPP
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.opencv.test.photo;
|
||||
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Point;
|
||||
import org.opencv.photo.Photo;
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
public class PhotoTest extends OpenCVTestCase {
|
||||
|
||||
public void testInpaint() {
|
||||
Point p = new Point(matSize / 2, matSize / 2);
|
||||
Imgproc.circle(gray255, p, 2, colorBlack, Imgproc.FILLED);
|
||||
Imgproc.circle(gray0, p, 2, colorWhite, Imgproc.FILLED);
|
||||
|
||||
Photo.inpaint(gray255, gray0, dst, 3, Photo.INPAINT_TELEA);
|
||||
|
||||
assertMatEqual(getMat(CvType.CV_8U, 255), dst);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"whitelist":
|
||||
{
|
||||
"": [
|
||||
"createAlignMTB", "createCalibrateDebevec", "createCalibrateRobertson",
|
||||
"createMergeDebevec", "createMergeMertens", "createMergeRobertson",
|
||||
"createTonemapDrago", "createTonemapMantiuk", "createTonemapReinhard", "inpaint"],
|
||||
"CalibrateCRF": ["process"],
|
||||
"AlignExposures": ["process"],
|
||||
"AlignMTB" : ["calculateShift", "shiftMat", "computeBitmaps", "getMaxBits", "setMaxBits",
|
||||
"getExcludeRange", "setExcludeRange", "getCut", "setCut"],
|
||||
"CalibrateDebevec" : ["getLambda", "setLambda", "getSamples", "setSamples", "getRandom", "setRandom"],
|
||||
"CalibrateRobertson" : ["getMaxIter", "setMaxIter", "getThreshold", "setThreshold", "getRadiance"],
|
||||
"MergeExposures" : ["process"],
|
||||
"MergeDebevec" : ["process"],
|
||||
"MergeMertens" : ["process", "getContrastWeight", "setContrastWeight", "getSaturationWeight",
|
||||
"setSaturationWeight", "getExposureWeight", "setExposureWeight"],
|
||||
"MergeRobertson" : ["process"],
|
||||
"Tonemap" : ["process" , "getGamma", "setGamma"],
|
||||
"TonemapDrago" : ["getSaturation", "setSaturation", "getBias", "setBias",
|
||||
"getSigmaColor", "setSigmaColor", "getSigmaSpace","setSigmaSpace"],
|
||||
"TonemapMantiuk" : ["getScale", "setScale", "getSaturation", "setSaturation"],
|
||||
"TonemapReinhard" : ["getIntensity", "setIntensity", "getLightAdaptation", "setLightAdaptation",
|
||||
"getColorAdaptation", "setColorAdaptation"],
|
||||
"segmentation_IntelligentScissorsMB": [
|
||||
"IntelligentScissorsMB",
|
||||
"setWeights",
|
||||
"setGradientMagnitudeMaxLimit",
|
||||
"setEdgeFeatureZeroCrossingParameters",
|
||||
"setEdgeFeatureCannyParameters",
|
||||
"applyImage",
|
||||
"applyImageFeatures",
|
||||
"buildMap",
|
||||
"getContour"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"func_arg_fix" : {
|
||||
"Photo": {
|
||||
"(void)fastNlMeansDenoising:(Mat*)src dst:(Mat*)dst h:(FloatVector*)h templateWindowSize:(int)templateWindowSize searchWindowSize:(int)searchWindowSize normType:(int)normType" : { "h" : { "name" : "hVector" } },
|
||||
"(void)fastNlMeansDenoisingMulti:(NSArray<Mat*>*)srcImgs dst:(Mat*)dst imgToDenoiseIndex:(int)imgToDenoiseIndex temporalWindowSize:(int)temporalWindowSize h:(FloatVector*)h templateWindowSize:(int)templateWindowSize searchWindowSize:(int)searchWindowSize normType:(int)normType" : { "h" : { "name" : "hVector" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2014, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
#include "../perf_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_perf.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
|
||||
OCL_PERF_TEST(Photo, DenoisingGrayscale)
|
||||
{
|
||||
Mat _original = imread(getDataPath("cv/denoising/lena_noised_gaussian_sigma=10.png"), IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(_original.empty()) << "Could not load input image";
|
||||
|
||||
UMat result(_original.size(), _original.type()), original;
|
||||
_original.copyTo(original);
|
||||
|
||||
declare.in(original).out(result).iterations(10);
|
||||
|
||||
OCL_TEST_CYCLE()
|
||||
cv::fastNlMeansDenoising(original, result, 10);
|
||||
|
||||
SANITY_CHECK(result, 1);
|
||||
}
|
||||
|
||||
OCL_PERF_TEST(Photo, DenoisingColored)
|
||||
{
|
||||
Mat _original = imread(getDataPath("cv/denoising/lena_noised_gaussian_sigma=10.png"));
|
||||
ASSERT_FALSE(_original.empty()) << "Could not load input image";
|
||||
|
||||
UMat result(_original.size(), _original.type()), original;
|
||||
_original.copyTo(original);
|
||||
|
||||
declare.in(original).out(result).iterations(10);
|
||||
|
||||
OCL_TEST_CYCLE()
|
||||
cv::fastNlMeansDenoisingColored(original, result, 10, 10);
|
||||
|
||||
SANITY_CHECK(result, 2);
|
||||
}
|
||||
|
||||
OCL_PERF_TEST(Photo, DISABLED_DenoisingGrayscaleMulti)
|
||||
{
|
||||
const int imgs_count = 3;
|
||||
|
||||
vector<UMat> original(imgs_count);
|
||||
Mat tmp;
|
||||
for (int i = 0; i < imgs_count; i++)
|
||||
{
|
||||
string original_path = format("cv/denoising/lena_noised_gaussian_sigma=20_multi_%d.png", i);
|
||||
tmp = imread(getDataPath(original_path), IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(tmp.empty()) << "Could not load input image " << original_path;
|
||||
tmp.copyTo(original[i]);
|
||||
declare.in(original[i]);
|
||||
}
|
||||
UMat result(tmp.size(), tmp.type());
|
||||
declare.out(result).iterations(10);
|
||||
|
||||
OCL_TEST_CYCLE()
|
||||
cv::fastNlMeansDenoisingMulti(original, result, imgs_count / 2, imgs_count, 15);
|
||||
|
||||
SANITY_CHECK(result);
|
||||
}
|
||||
|
||||
OCL_PERF_TEST(Photo, DISABLED_DenoisingColoredMulti)
|
||||
{
|
||||
const int imgs_count = 3;
|
||||
|
||||
vector<UMat> original(imgs_count);
|
||||
Mat tmp;
|
||||
for (int i = 0; i < imgs_count; i++)
|
||||
{
|
||||
string original_path = format("cv/denoising/lena_noised_gaussian_sigma=20_multi_%d.png", i);
|
||||
tmp = imread(getDataPath(original_path), IMREAD_COLOR);
|
||||
ASSERT_FALSE(tmp.empty()) << "Could not load input image " << original_path;
|
||||
|
||||
tmp.copyTo(original[i]);
|
||||
declare.in(original[i]);
|
||||
}
|
||||
UMat result(tmp.size(), tmp.type());
|
||||
declare.out(result).iterations(10);
|
||||
|
||||
OCL_TEST_CYCLE()
|
||||
cv::fastNlMeansDenoisingColoredMulti(original, result, imgs_count / 2, imgs_count, 10, 15);
|
||||
|
||||
SANITY_CHECK(result);
|
||||
}
|
||||
|
||||
} } // namespace opencv_test::ocl
|
||||
|
||||
#endif // HAVE_OPENCL
|
||||
@@ -0,0 +1,103 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
|
||||
namespace opencv_test {
|
||||
namespace {
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
PERF_TEST(CV_ccm_perf_480_640, correctImage)
|
||||
{
|
||||
string path = cvtest::findDataFile("cv/mcc/mcc_ccm_test.yml");
|
||||
FileStorage fs(path, FileStorage::READ);
|
||||
Mat chartsRGB;
|
||||
fs["chartsRGB"] >> chartsRGB;
|
||||
fs.release();
|
||||
ASSERT_FALSE(chartsRGB.empty()) << "chartsRGB is empty after loading from: " << path;
|
||||
|
||||
cv::ccm::ColorCorrectionModel model(
|
||||
chartsRGB.col(1).clone().reshape(3, chartsRGB.rows/3) / 255.0,
|
||||
cv::ccm::COLORCHECKER_MACBETH
|
||||
);
|
||||
model.compute();
|
||||
Mat img(480, 640, CV_8UC3);
|
||||
randu(img, 0, 255);
|
||||
|
||||
Mat correctedImage;
|
||||
TEST_CYCLE() { model.correctImage(img, correctedImage); }
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
PERF_TEST(CV_ccm_perf_720_1280, correctImage)
|
||||
{
|
||||
string path = cvtest::findDataFile("cv/mcc/mcc_ccm_test.yml");
|
||||
FileStorage fs(path, FileStorage::READ);
|
||||
Mat chartsRGB;
|
||||
fs["chartsRGB"] >> chartsRGB;
|
||||
fs.release();
|
||||
ASSERT_FALSE(chartsRGB.empty()) << "chartsRGB is empty after loading from: " << path;
|
||||
|
||||
cv::ccm::ColorCorrectionModel model(
|
||||
chartsRGB.col(1).clone().reshape(3, chartsRGB.rows/3) / 255.0,
|
||||
cv::ccm::COLORCHECKER_MACBETH
|
||||
);
|
||||
model.compute();
|
||||
Mat img(720, 1280, CV_8UC3);
|
||||
randu(img, 0, 255);
|
||||
|
||||
Mat correctedImage;
|
||||
TEST_CYCLE() { model.correctImage(img, correctedImage); }
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
PERF_TEST(CV_ccm_perf_1080_1920, correctImage)
|
||||
{
|
||||
string path = cvtest::findDataFile("cv/mcc/mcc_ccm_test.yml");
|
||||
FileStorage fs(path, FileStorage::READ);
|
||||
Mat chartsRGB;
|
||||
fs["chartsRGB"] >> chartsRGB;
|
||||
fs.release();
|
||||
ASSERT_FALSE(chartsRGB.empty()) << "chartsRGB is empty after loading from: " << path;
|
||||
|
||||
cv::ccm::ColorCorrectionModel model(
|
||||
chartsRGB.col(1).clone().reshape(3, chartsRGB.rows/3) / 255.0,
|
||||
cv::ccm::COLORCHECKER_MACBETH
|
||||
);
|
||||
model.compute();
|
||||
Mat img(1080, 1920, CV_8UC3);
|
||||
randu(img, 0, 255);
|
||||
|
||||
Mat correctedImage;
|
||||
TEST_CYCLE() { model.correctImage(img, correctedImage); }
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
PERF_TEST(CV_ccm_perf_2160_3840, correctImage)
|
||||
{
|
||||
string path = cvtest::findDataFile("cv/mcc/mcc_ccm_test.yml");
|
||||
FileStorage fs(path, FileStorage::READ);
|
||||
Mat chartsRGB;
|
||||
fs["chartsRGB"] >> chartsRGB;
|
||||
fs.release();
|
||||
ASSERT_FALSE(chartsRGB.empty()) << "chartsRGB is empty after loading from: " << path;
|
||||
|
||||
cv::ccm::ColorCorrectionModel model(
|
||||
chartsRGB.col(1).clone().reshape(3, chartsRGB.rows/3) / 255.0,
|
||||
cv::ccm::COLORCHECKER_MACBETH
|
||||
);
|
||||
model.compute();
|
||||
Mat img(2160, 3840, CV_8UC3);
|
||||
randu(img, 0, 255);
|
||||
|
||||
Mat correctedImage;
|
||||
TEST_CYCLE() { model.correctImage(img, correctedImage); }
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opencv_test
|
||||
@@ -0,0 +1,39 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
PERF_TEST(ChromaticAberration, CorrectChromaticAberration)
|
||||
{
|
||||
std::string calib_file = getDataPath("cv/cameracalibration/chromatic_aberration/ca_photo_calib.yaml");
|
||||
std::string image_file = getDataPath("cv/cameracalibration/chromatic_aberration/ca_photo.png");
|
||||
|
||||
cv::Mat src = cv::imread(image_file);
|
||||
ASSERT_FALSE(src.empty()) << "Could not load input image";
|
||||
ASSERT_EQ(src.type(), CV_8UC3);
|
||||
|
||||
cv::Mat coeffMat;
|
||||
int degree = -1;
|
||||
Size calib_size = {-1,-1};
|
||||
|
||||
FileStorage fs(calib_file, FileStorage::READ);
|
||||
ASSERT_TRUE(fs.isOpened());
|
||||
ASSERT_NO_THROW(cv::loadChromaticAberrationParams(fs.root(), coeffMat, calib_size, degree));
|
||||
|
||||
cv::Mat dst;
|
||||
|
||||
TEST_CYCLE()
|
||||
{
|
||||
cv::correctChromaticAberration(src, coeffMat, dst, calib_size, degree);
|
||||
}
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opencv_test
|
||||
@@ -0,0 +1,189 @@
|
||||
/*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 "perf_precomp.hpp"
|
||||
|
||||
#include "opencv2/photo/cuda.hpp"
|
||||
#include "opencv2/ts/cuda_perf.hpp"
|
||||
|
||||
#include "opencv2/opencv_modules.hpp"
|
||||
|
||||
#if defined (HAVE_CUDA) && defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAIMGPROC)
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
using namespace perf;
|
||||
|
||||
#define CUDA_DENOISING_IMAGE_SIZES testing::Values(perf::szVGA, perf::sz720p)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// nonLocalMeans
|
||||
|
||||
DEF_PARAM_TEST(Sz_Depth_Cn_WinSz_BlockSz, cv::Size, MatDepth, MatCn, int, int);
|
||||
|
||||
PERF_TEST_P(Sz_Depth_Cn_WinSz_BlockSz, CUDA_NonLocalMeans,
|
||||
Combine(CUDA_DENOISING_IMAGE_SIZES,
|
||||
Values<MatDepth>(CV_8U),
|
||||
CUDA_CHANNELS_1_3,
|
||||
Values(21),
|
||||
Values(5)))
|
||||
{
|
||||
declare.time(600.0);
|
||||
|
||||
const cv::Size size = GET_PARAM(0);
|
||||
const int depth = GET_PARAM(1);
|
||||
const int channels = GET_PARAM(2);
|
||||
const int search_window_size = GET_PARAM(3);
|
||||
const int block_size = GET_PARAM(4);
|
||||
|
||||
const float h = 10;
|
||||
const int borderMode = cv::BORDER_REFLECT101;
|
||||
|
||||
const int type = CV_MAKE_TYPE(depth, channels);
|
||||
|
||||
cv::Mat src(size, type);
|
||||
declare.in(src, WARMUP_RNG);
|
||||
|
||||
if (PERF_RUN_CUDA())
|
||||
{
|
||||
const cv::cuda::GpuMat d_src(src);
|
||||
cv::cuda::GpuMat dst;
|
||||
|
||||
TEST_CYCLE() cv::cuda::nonLocalMeans(d_src, dst, h, search_window_size, block_size, borderMode);
|
||||
|
||||
CUDA_SANITY_CHECK(dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
FAIL_NO_CPU();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// fastNonLocalMeans
|
||||
|
||||
DEF_PARAM_TEST(Sz_Depth_Cn_WinSz_BlockSz, cv::Size, MatDepth, MatCn, int, int);
|
||||
|
||||
PERF_TEST_P(Sz_Depth_Cn_WinSz_BlockSz, CUDA_FastNonLocalMeans,
|
||||
Combine(CUDA_DENOISING_IMAGE_SIZES,
|
||||
Values<MatDepth>(CV_8U),
|
||||
CUDA_CHANNELS_1_3,
|
||||
Values(21),
|
||||
Values(7)))
|
||||
{
|
||||
declare.time(60.0);
|
||||
|
||||
const cv::Size size = GET_PARAM(0);
|
||||
const int depth = GET_PARAM(1);
|
||||
const int search_window_size = GET_PARAM(2);
|
||||
const int block_size = GET_PARAM(3);
|
||||
|
||||
const float h = 10;
|
||||
const int type = CV_MAKE_TYPE(depth, 1);
|
||||
|
||||
cv::Mat src(size, type);
|
||||
declare.in(src, WARMUP_RNG);
|
||||
|
||||
if (PERF_RUN_CUDA())
|
||||
{
|
||||
const cv::cuda::GpuMat d_src(src);
|
||||
cv::cuda::GpuMat dst;
|
||||
|
||||
TEST_CYCLE() cv::cuda::fastNlMeansDenoising(d_src, dst, h, search_window_size, block_size);
|
||||
|
||||
CUDA_SANITY_CHECK(dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::Mat dst;
|
||||
|
||||
TEST_CYCLE() cv::fastNlMeansDenoising(src, dst, h, block_size, search_window_size);
|
||||
|
||||
CPU_SANITY_CHECK(dst);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// fastNonLocalMeans (colored)
|
||||
|
||||
DEF_PARAM_TEST(Sz_Depth_WinSz_BlockSz, cv::Size, MatDepth, int, int);
|
||||
|
||||
PERF_TEST_P(Sz_Depth_WinSz_BlockSz, CUDA_FastNonLocalMeansColored,
|
||||
Combine(CUDA_DENOISING_IMAGE_SIZES,
|
||||
Values<MatDepth>(CV_8U),
|
||||
Values(21),
|
||||
Values(7)))
|
||||
{
|
||||
declare.time(60.0);
|
||||
|
||||
const cv::Size size = GET_PARAM(0);
|
||||
const int depth = GET_PARAM(1);
|
||||
const int search_window_size = GET_PARAM(2);
|
||||
const int block_size = GET_PARAM(3);
|
||||
|
||||
const float h = 10;
|
||||
const int type = CV_MAKE_TYPE(depth, 3);
|
||||
|
||||
cv::Mat src(size, type);
|
||||
declare.in(src, WARMUP_RNG);
|
||||
|
||||
if (PERF_RUN_CUDA())
|
||||
{
|
||||
const cv::cuda::GpuMat d_src(src);
|
||||
cv::cuda::GpuMat dst;
|
||||
|
||||
TEST_CYCLE() cv::cuda::fastNlMeansDenoisingColored(d_src, dst, h, h, search_window_size, block_size);
|
||||
|
||||
CUDA_SANITY_CHECK(dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::Mat dst;
|
||||
|
||||
TEST_CYCLE() cv::fastNlMeansDenoisingColored(src, dst, h, h, block_size, search_window_size);
|
||||
|
||||
CPU_SANITY_CHECK(dst);
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct ExposureSeq
|
||||
{
|
||||
std::vector<Mat> images;
|
||||
std::vector<float> times;
|
||||
};
|
||||
|
||||
ExposureSeq loadExposureSeq(const std::string& list_filename)
|
||||
{
|
||||
std::ifstream list_file(list_filename);
|
||||
EXPECT_TRUE(list_file.is_open());
|
||||
string name;
|
||||
float val;
|
||||
const String path(list_filename.substr(0, list_filename.find_last_of("\\/") + 1));
|
||||
ExposureSeq seq;
|
||||
while (list_file >> name >> val)
|
||||
{
|
||||
Mat img = imread(path + name);
|
||||
EXPECT_FALSE(img.empty()) << "Could not load input image " << path + name;
|
||||
seq.images.push_back(img);
|
||||
seq.times.push_back(1 / val);
|
||||
}
|
||||
list_file.close();
|
||||
return seq;
|
||||
}
|
||||
|
||||
PERF_TEST(HDR, Mertens)
|
||||
{
|
||||
const ExposureSeq seq = loadExposureSeq(getDataPath("cv/hdr/exposures/list.txt"));
|
||||
Ptr<MergeMertens> merge = createMergeMertens();
|
||||
Mat result(seq.images.front().size(), seq.images.front().type());
|
||||
TEST_CYCLE() merge->process(seq.images, result);
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
PERF_TEST(HDR, Debevec)
|
||||
{
|
||||
const ExposureSeq seq = loadExposureSeq(getDataPath("cv/hdr/exposures/list.txt"));
|
||||
Ptr<MergeDebevec> merge = createMergeDebevec();
|
||||
Mat result(seq.images.front().size(), seq.images.front().type());
|
||||
TEST_CYCLE() merge->process(seq.images, result, seq.times);
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
PERF_TEST(HDR, Robertson)
|
||||
{
|
||||
const ExposureSeq seq = loadExposureSeq(getDataPath("cv/hdr/exposures/list.txt"));
|
||||
Ptr<MergeRobertson> merge = createMergeRobertson();
|
||||
Mat result(seq.images.front().size(), seq.images.front().type());
|
||||
TEST_CYCLE() merge->process(seq.images, result, seq.times);
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opencv_test
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
|
||||
CV_ENUM(InpaintingMethod, INPAINT_NS, INPAINT_TELEA)
|
||||
typedef tuple<Size, InpaintingMethod> InpaintArea_InpaintingMethod_t;
|
||||
typedef perf::TestBaseWithParam<InpaintArea_InpaintingMethod_t> InpaintArea_InpaintingMethod;
|
||||
typedef perf::TestBaseWithParam<InpaintingMethod> Perf_InpaintingMethod;
|
||||
|
||||
|
||||
PERF_TEST_P(InpaintArea_InpaintingMethod, inpaint,
|
||||
testing::Combine(
|
||||
testing::Values(::perf::szSmall24, ::perf::szSmall32, ::perf::szSmall64),
|
||||
InpaintingMethod::all()
|
||||
)
|
||||
)
|
||||
{
|
||||
Mat src = imread(getDataPath("gpu/hog/road.png"));
|
||||
|
||||
Size sz = get<0>(GetParam());
|
||||
int inpaintingMethod = get<1>(GetParam());
|
||||
|
||||
Mat mask(src.size(), CV_8UC1, Scalar(0));
|
||||
Mat result(src.size(), src.type());
|
||||
|
||||
Rect inpaintArea(src.cols/3, src.rows/3, sz.width, sz.height);
|
||||
mask(inpaintArea).setTo(255);
|
||||
|
||||
declare.in(src, mask).out(result).time(120);
|
||||
|
||||
TEST_CYCLE() inpaint(src, mask, result, 10.0, inpaintingMethod);
|
||||
|
||||
Mat inpaintedArea = result(inpaintArea);
|
||||
SANITY_CHECK(inpaintedArea);
|
||||
}
|
||||
|
||||
PERF_TEST_P(Perf_InpaintingMethod, inpaintDots, InpaintingMethod::all())
|
||||
{
|
||||
Mat src = imread(getDataPath("gpu/hog/road.png"));
|
||||
|
||||
int inpaintingMethod = GetParam();
|
||||
|
||||
Mat mask(src.size(), CV_8UC1, Scalar(0));
|
||||
Mat result(src.size(), src.type());
|
||||
|
||||
for (int i = 0; i < src.size().height; i += 16) {
|
||||
for (int j = 0; j < src.size().width; j += 16) {
|
||||
mask.at<unsigned char>(i, j) = 255;
|
||||
}
|
||||
}
|
||||
|
||||
declare.in(src, mask).out(result).time(120);
|
||||
|
||||
TEST_CYCLE() inpaint(src, mask, result, 10.0, inpaintingMethod);
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,34 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
|
||||
typedef perf::TestBaseWithParam<int> TestIntelligentScissorsMB;
|
||||
|
||||
PERF_TEST_P(TestIntelligentScissorsMB, buildMap, testing::Values( IMREAD_GRAYSCALE, IMREAD_COLOR ))
|
||||
{
|
||||
const int flags = GetParam();
|
||||
|
||||
const Mat image = imread(findDataFile("cv/shared/lena.png"), flags);
|
||||
ASSERT_TRUE(!image.empty());
|
||||
|
||||
const Point source_point(275, 63);
|
||||
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
tool.applyImage(image);
|
||||
|
||||
PERF_SAMPLE_BEGIN()
|
||||
for (size_t idx = 1; idx <= 100; ++idx)
|
||||
{
|
||||
tool.buildMap(source_point);
|
||||
}
|
||||
PERF_SAMPLE_END()
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "perf_precomp.hpp"
|
||||
#include "opencv2/ts/cuda_perf.hpp"
|
||||
|
||||
static const char * impls[] = {
|
||||
#ifdef HAVE_CUDA
|
||||
"cuda",
|
||||
#endif
|
||||
"plain"
|
||||
};
|
||||
|
||||
#if defined(HAVE_HPX)
|
||||
#include <hpx/hpx_main.hpp>
|
||||
#endif
|
||||
|
||||
CV_PERF_TEST_MAIN_WITH_IMPLS(photo, impls, perf::printCudaInfo())
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef __OPENCV_PERF_PRECOMP_HPP__
|
||||
#define __OPENCV_PERF_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,281 @@
|
||||
/*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 CV_FINAL : 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) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
process(src, dst);
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays _src, std::vector<Mat>& dst) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
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) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat img0 = _img0.getMat();
|
||||
Mat img1 = _img1.getMat();
|
||||
CV_Assert(img0.channels() == 1 && img0.type() == img1.type());
|
||||
CV_Assert(img0.size() == img1.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 = (int)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) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
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 CV_OVERRIDE { return max_bits; }
|
||||
void setMaxBits(int val) CV_OVERRIDE { max_bits = val; }
|
||||
|
||||
int getExcludeRange() const CV_OVERRIDE { return exclude_range; }
|
||||
void setExcludeRange(int val) CV_OVERRIDE { exclude_range = val; }
|
||||
|
||||
bool getCut() const CV_OVERRIDE { return cut; }
|
||||
void setCut(bool val) CV_OVERRIDE { cut = val; }
|
||||
|
||||
void write(FileStorage& fs) const CV_OVERRIDE
|
||||
{
|
||||
writeFormat(fs);
|
||||
fs << "name" << name
|
||||
<< "max_bits" << max_bits
|
||||
<< "exclude_range" << exclude_range
|
||||
<< "cut" << static_cast<int>(cut);
|
||||
}
|
||||
|
||||
void read(const FileNode& fn) CV_OVERRIDE
|
||||
{
|
||||
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) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
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 = (int)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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*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.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective icvers.
|
||||
//
|
||||
// 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 Intel Corporation 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 "opencv2/core/base.hpp"
|
||||
|
||||
#ifndef __OPENCV_DENOISING_ARRAYS_HPP__
|
||||
#define __OPENCV_DENOISING_ARRAYS_HPP__
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
template <class T>
|
||||
struct Array2d
|
||||
{
|
||||
T* a;
|
||||
int n1,n2;
|
||||
bool needToDeallocArray;
|
||||
|
||||
Array2d(const Array2d& array2d):
|
||||
a(array2d.a), n1(array2d.n1), n2(array2d.n2), needToDeallocArray(false)
|
||||
{
|
||||
if (array2d.needToDeallocArray)
|
||||
{
|
||||
CV_Error(Error::BadDataPtr, "Copy constructor for self allocating arrays not supported");
|
||||
}
|
||||
}
|
||||
|
||||
Array2d(T* _a, int _n1, int _n2):
|
||||
a(_a), n1(_n1), n2(_n2), needToDeallocArray(false)
|
||||
{
|
||||
}
|
||||
|
||||
Array2d(int _n1, int _n2):
|
||||
n1(_n1), n2(_n2), needToDeallocArray(true)
|
||||
{
|
||||
a = new T[n1*n2];
|
||||
}
|
||||
|
||||
~Array2d()
|
||||
{
|
||||
if (needToDeallocArray)
|
||||
delete[] a;
|
||||
}
|
||||
|
||||
T* operator [] (int i)
|
||||
{
|
||||
return a + i*n2;
|
||||
}
|
||||
|
||||
inline T* row_ptr(int i)
|
||||
{
|
||||
return (*this)[i];
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct Array3d
|
||||
{
|
||||
T* a;
|
||||
int n1,n2,n3;
|
||||
bool needToDeallocArray;
|
||||
|
||||
Array3d(T* _a, int _n1, int _n2, int _n3):
|
||||
a(_a), n1(_n1), n2(_n2), n3(_n3), needToDeallocArray(false)
|
||||
{
|
||||
}
|
||||
|
||||
Array3d(int _n1, int _n2, int _n3):
|
||||
n1(_n1), n2(_n2), n3(_n3), needToDeallocArray(true)
|
||||
{
|
||||
a = new T[n1*n2*n3];
|
||||
}
|
||||
|
||||
~Array3d()
|
||||
{
|
||||
if (needToDeallocArray)
|
||||
delete[] a;
|
||||
}
|
||||
|
||||
Array2d<T> operator [] (int i)
|
||||
{
|
||||
Array2d<T> array2d(a + i*n2*n3, n2, n3);
|
||||
return array2d;
|
||||
}
|
||||
|
||||
inline T* row_ptr(int i1, int i2)
|
||||
{
|
||||
return a + i1*n2*n3 + i2*n3;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct Array4d
|
||||
{
|
||||
T* a;
|
||||
int n1,n2,n3,n4;
|
||||
bool needToDeallocArray;
|
||||
int steps[4];
|
||||
|
||||
void init_steps()
|
||||
{
|
||||
steps[0] = n2*n3*n4;
|
||||
steps[1] = n3*n4;
|
||||
steps[2] = n4;
|
||||
steps[3] = 1;
|
||||
}
|
||||
|
||||
Array4d(T* _a, int _n1, int _n2, int _n3, int _n4) :
|
||||
a(_a), n1(_n1), n2(_n2), n3(_n3), n4(_n4), needToDeallocArray(false)
|
||||
{
|
||||
init_steps();
|
||||
}
|
||||
|
||||
Array4d(int _n1, int _n2, int _n3, int _n4) :
|
||||
n1(_n1), n2(_n2), n3(_n3), n4(_n4), needToDeallocArray(true)
|
||||
{
|
||||
a = new T[n1*n2*n3*n4];
|
||||
init_steps();
|
||||
}
|
||||
|
||||
~Array4d()
|
||||
{
|
||||
if (needToDeallocArray)
|
||||
delete[] a;
|
||||
}
|
||||
|
||||
Array3d<T> operator [] (int i)
|
||||
{
|
||||
Array3d<T> array3d(a + i*n2*n3*n4, n2, n3, n4);
|
||||
return array3d;
|
||||
}
|
||||
|
||||
inline T* row_ptr(int i1, int i2, int i3)
|
||||
{
|
||||
return a + i1*n2*n3*n4 + i2*n3*n4 + i3*n4;
|
||||
}
|
||||
|
||||
inline int step_size(int dimension)
|
||||
{
|
||||
return steps[dimension];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,311 @@
|
||||
/*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 "hdr_common.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
class CalibrateDebevecImpl CV_FINAL : public CalibrateDebevec
|
||||
{
|
||||
public:
|
||||
CalibrateDebevecImpl(int _samples, float _lambda, bool _random) :
|
||||
name("CalibrateDebevec"),
|
||||
samples(_samples),
|
||||
lambda(_lambda),
|
||||
random(_random),
|
||||
w(triangleWeights())
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
// check inputs
|
||||
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);
|
||||
CV_Assert(times.type() == CV_32FC1);
|
||||
|
||||
// create output
|
||||
int channels = images[0].channels();
|
||||
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
|
||||
int rows = images[0].rows;
|
||||
int cols = images[0].cols;
|
||||
|
||||
dst.create(LDR_SIZE, 1, CV_32FCC);
|
||||
Mat result = dst.getMat();
|
||||
|
||||
// pick pixel locations (either random or in a rectangular grid)
|
||||
std::vector<Point> points;
|
||||
points.reserve(samples);
|
||||
if(random) {
|
||||
for(int i = 0; i < samples; i++) {
|
||||
points.push_back(Point(rand() % cols, rand() % rows));
|
||||
}
|
||||
} else {
|
||||
int x_points = static_cast<int>(sqrt(static_cast<double>(samples) * cols / rows));
|
||||
CV_Assert(0 < x_points && x_points <= cols);
|
||||
int y_points = samples / x_points;
|
||||
CV_Assert(0 < y_points && y_points <= rows);
|
||||
int step_x = cols / x_points;
|
||||
int step_y = 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 / 2; j < y_points; j++, y += step_y) {
|
||||
if( 0 <= x && x < cols && 0 <= y && y < rows ) {
|
||||
points.push_back(Point(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
// we can have slightly less grid points than specified
|
||||
//samples = static_cast<int>(points.size());
|
||||
}
|
||||
|
||||
// we need enough equations to ensure a sufficiently overdetermined system
|
||||
// (maybe only as a warning)
|
||||
//CV_Assert(points.size() * (images.size() - 1) >= LDR_SIZE);
|
||||
|
||||
// solve for imaging system response function, over each channel separately
|
||||
std::vector<Mat> result_split(channels);
|
||||
for(int ch = 0; ch < channels; ch++) {
|
||||
// initialize system of linear equations
|
||||
Mat A = Mat::zeros((int)points.size() * (int)images.size() + LDR_SIZE + 1,
|
||||
LDR_SIZE + (int)points.size(), CV_32F);
|
||||
Mat B = Mat::zeros(A.rows, 1, CV_32F);
|
||||
|
||||
// include the data-fitting equations
|
||||
int k = 0;
|
||||
for(size_t i = 0; i < points.size(); i++) {
|
||||
for(size_t j = 0; j < images.size(); j++) {
|
||||
// val = images[j].at<Vec3b>(points[i].y, points[i].x)[ch]
|
||||
int val = images[j].ptr()[channels*(points[i].y * cols + points[i].x) + ch];
|
||||
float wij = w.at<float>(val);
|
||||
A.at<float>(k, val) = wij;
|
||||
A.at<float>(k, LDR_SIZE + (int)i) = -wij;
|
||||
B.at<float>(k, 0) = wij * log(times.at<float>((int)j));
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
// fix the curve by setting its middle value to 0
|
||||
A.at<float>(k, LDR_SIZE / 2) = 1;
|
||||
k++;
|
||||
|
||||
// include the smoothness equations
|
||||
for(int i = 0; i < (LDR_SIZE - 2); i++) {
|
||||
float wi = w.at<float>(i + 1);
|
||||
A.at<float>(k, i) = lambda * wi;
|
||||
A.at<float>(k, i + 1) = -2 * lambda * wi;
|
||||
A.at<float>(k, i + 2) = lambda * wi;
|
||||
k++;
|
||||
}
|
||||
|
||||
// solve the overdetermined system using SVD (least-squares problem)
|
||||
Mat solution;
|
||||
solve(A, B, solution, DECOMP_SVD);
|
||||
solution.rowRange(0, LDR_SIZE).copyTo(result_split[ch]);
|
||||
}
|
||||
|
||||
// combine log-exposures and take its exponent
|
||||
merge(result_split, result);
|
||||
exp(result, result);
|
||||
}
|
||||
|
||||
int getSamples() const CV_OVERRIDE { return samples; }
|
||||
void setSamples(int val) CV_OVERRIDE { samples = val; }
|
||||
|
||||
float getLambda() const CV_OVERRIDE { return lambda; }
|
||||
void setLambda(float val) CV_OVERRIDE { lambda = val; }
|
||||
|
||||
bool getRandom() const CV_OVERRIDE { return random; }
|
||||
void setRandom(bool val) CV_OVERRIDE { random = val; }
|
||||
|
||||
void write(FileStorage& fs) const CV_OVERRIDE
|
||||
{
|
||||
writeFormat(fs);
|
||||
fs << "name" << name
|
||||
<< "samples" << samples
|
||||
<< "lambda" << lambda
|
||||
<< "random" << static_cast<int>(random);
|
||||
}
|
||||
|
||||
void read(const FileNode& fn) CV_OVERRIDE
|
||||
{
|
||||
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; // calibration algorithm identifier
|
||||
int samples; // number of pixel locations to sample
|
||||
float lambda; // constant that determines the amount of smoothness
|
||||
bool random; // whether to sample locations randomly or in a grid shape
|
||||
Mat w; // weighting function for corresponding pixel values
|
||||
};
|
||||
|
||||
Ptr<CalibrateDebevec> createCalibrateDebevec(int samples, float lambda, bool random)
|
||||
{
|
||||
return makePtr<CalibrateDebevecImpl>(samples, lambda, random);
|
||||
}
|
||||
|
||||
class CalibrateRobertsonImpl CV_FINAL : 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) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
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);
|
||||
CV_Assert(channels >= 1 && channels <= 3);
|
||||
|
||||
dst.create(LDR_SIZE, 1, CV_32FCC);
|
||||
Mat response = dst.getMat();
|
||||
response = linearResponse(3) / (static_cast<float>(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>((int)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 CV_OVERRIDE { return max_iter; }
|
||||
void setMaxIter(int val) CV_OVERRIDE { max_iter = val; }
|
||||
|
||||
float getThreshold() const CV_OVERRIDE { return threshold; }
|
||||
void setThreshold(float val) CV_OVERRIDE { threshold = val; }
|
||||
|
||||
Mat getRadiance() const CV_OVERRIDE { return radiance; }
|
||||
|
||||
void write(FileStorage& fs) const CV_OVERRIDE
|
||||
{
|
||||
writeFormat(fs);
|
||||
fs << "name" << name
|
||||
<< "max_iter" << max_iter
|
||||
<< "threshold" << threshold;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn) CV_OVERRIDE
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,557 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "opencv2/photo.hpp"
|
||||
#include "linearize.hpp"
|
||||
#include <cmath>
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
class ColorCorrectionModel::Impl
|
||||
{
|
||||
public:
|
||||
Mat src;
|
||||
|
||||
Color ref = Color();
|
||||
Mat dist;
|
||||
RGBBase_& cs;
|
||||
// Track initialization parameters for serialization
|
||||
ColorSpace csEnum;
|
||||
Mat mask;
|
||||
|
||||
// RGBl of detected data and the reference
|
||||
Mat srcRgbl;
|
||||
Mat dstRgbl;
|
||||
|
||||
// ccm type and shape
|
||||
CcmType ccmType;
|
||||
int shape;
|
||||
|
||||
// linear method and distance
|
||||
std::shared_ptr<Linear> linear = std::make_shared<Linear>();
|
||||
DistanceType distance;
|
||||
LinearizationType linearizationType;
|
||||
|
||||
Mat weights;
|
||||
Mat weightsList;
|
||||
Mat ccm;
|
||||
Mat ccm0;
|
||||
double gamma;
|
||||
int deg;
|
||||
std::vector<double> saturatedThreshold;
|
||||
InitialMethodType initialMethodType;
|
||||
double weightsCoeff;
|
||||
int maskedLen;
|
||||
double loss;
|
||||
int maxCount;
|
||||
double epsilon;
|
||||
bool rgb;
|
||||
Impl();
|
||||
|
||||
/** @brief Make no change for CCM_LINEAR.
|
||||
convert cv::Mat A to [A, 1] in CCM_AFFINE.
|
||||
@param inp the input array, type of cv::Mat.
|
||||
@return the output array, type of cv::Mat
|
||||
*/
|
||||
Mat prepare(const Mat& inp);
|
||||
|
||||
/** @brief Calculate weights and mask.
|
||||
@param weightsList the input array, type of cv::Mat.
|
||||
@param weightsCoeff type of double.
|
||||
@param saturateMask the input array, type of cv::Mat.
|
||||
*/
|
||||
void calWeightsMasks(const Mat& weightsList, double weightsCoeff, Mat saturateMask);
|
||||
|
||||
/** @brief Fitting nonlinear - optimization initial value by white balance.
|
||||
@return the output array, type of Mat
|
||||
*/
|
||||
void initialWhiteBalance(void);
|
||||
|
||||
/** @brief Fitting nonlinear-optimization initial value by least square.
|
||||
@param fit if fit is True, return optimalization for rgbl distance function.
|
||||
*/
|
||||
void initialLeastSquare(bool fit = false);
|
||||
|
||||
double calcLoss_(Color color);
|
||||
double calcLoss(const Mat ccm_);
|
||||
|
||||
/** @brief Fitting ccm if distance function is associated with CIE Lab color space.
|
||||
see details in https://github.com/opencv/opencv/blob/master/modules/core/include/opencv2/core/optim.hpp
|
||||
Set terminal criteria for solver is possible.
|
||||
*/
|
||||
void fitting(void);
|
||||
|
||||
void getColor(Mat& img_, bool islinear = false);
|
||||
void getColor(ColorCheckerType constColor);
|
||||
void getColor(Mat colors_, ColorSpace cs_, Mat colored_);
|
||||
void getColor(Mat colors_, ColorSpace refColorSpace_);
|
||||
|
||||
/** @brief Loss function base on cv::MinProblemSolver::Function.
|
||||
see details in https://github.com/opencv/opencv/blob/master/modules/core/include/opencv2/core/optim.hpp
|
||||
*/
|
||||
class LossFunction : public MinProblemSolver::Function
|
||||
{
|
||||
public:
|
||||
ColorCorrectionModel::Impl* ccmLoss;
|
||||
LossFunction(ColorCorrectionModel::Impl* ccm)
|
||||
: ccmLoss(ccm) {};
|
||||
|
||||
/** @brief Reset dims to ccm->shape.
|
||||
*/
|
||||
int getDims() const CV_OVERRIDE
|
||||
{
|
||||
return ccmLoss->shape;
|
||||
}
|
||||
|
||||
/** @brief Reset calculation.
|
||||
*/
|
||||
double calc(const double* x) const CV_OVERRIDE
|
||||
{
|
||||
Mat ccm_(ccmLoss->shape, 1, CV_64F);
|
||||
for (int i = 0; i < ccmLoss->shape; i++)
|
||||
{
|
||||
ccm_.at<double>(i, 0) = x[i];
|
||||
}
|
||||
ccm_ = ccm_.reshape(0, ccmLoss->shape / 3);
|
||||
return ccmLoss->calcLoss(ccm_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
ColorCorrectionModel::Impl::Impl()
|
||||
: cs(*GetCS::getInstance().getRgb(COLOR_SPACE_SRGB))
|
||||
, csEnum(COLOR_SPACE_SRGB)
|
||||
, ccmType(CCM_LINEAR)
|
||||
, distance(DISTANCE_CIE2000)
|
||||
, linearizationType(LINEARIZATION_GAMMA)
|
||||
, weights(Mat())
|
||||
, gamma(2.2)
|
||||
, deg(3)
|
||||
, saturatedThreshold({ 0, 0.98 })
|
||||
, initialMethodType(INITIAL_METHOD_LEAST_SQUARE)
|
||||
, weightsCoeff(0)
|
||||
, maxCount(5000)
|
||||
, epsilon(1.e-4)
|
||||
, rgb(true)
|
||||
{}
|
||||
|
||||
Mat ColorCorrectionModel::Impl::prepare(const Mat& inp)
|
||||
{
|
||||
switch (ccmType)
|
||||
{
|
||||
case cv::ccm::CCM_LINEAR:
|
||||
shape = 9;
|
||||
return inp;
|
||||
case cv::ccm::CCM_AFFINE:
|
||||
{
|
||||
shape = 12;
|
||||
Mat ones(inp.size(), CV_64F, Scalar(1));
|
||||
Mat out(inp.size(), CV_64FC4);
|
||||
const Mat srcs[] = { inp, ones };
|
||||
const int fromTo[] = { 0,0, 1,1, 2,2, 3,3 }; // inp[ch] → out[ch]
|
||||
mixChannels(srcs, 2, &out, 1, fromTo, 4);
|
||||
return out;
|
||||
}
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Wrong ccmType!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::Impl::calWeightsMasks(const Mat& weightsList_, double weightsCoeff_, Mat saturateMask)
|
||||
{
|
||||
// weights
|
||||
if (!weightsList_.empty())
|
||||
{
|
||||
weights = weightsList_;
|
||||
}
|
||||
else if (weightsCoeff_ != 0)
|
||||
{
|
||||
pow(ref.toLuminant(cs.illumobserver), weightsCoeff_, weights);
|
||||
}
|
||||
|
||||
// masks
|
||||
Mat weight_mask = Mat::ones(src.rows, 1, CV_8U);
|
||||
if (!weights.empty())
|
||||
{
|
||||
weight_mask = weights > 0;
|
||||
}
|
||||
this->mask = (weight_mask) & (saturateMask);
|
||||
|
||||
// weights' mask
|
||||
if (!weights.empty())
|
||||
{
|
||||
Mat weights_masked = maskCopyTo(this->weights, this->mask);
|
||||
weights = weights_masked / mean(weights_masked)[0];
|
||||
}
|
||||
maskedLen = (int)sum(mask)[0];
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::Impl::initialWhiteBalance()
|
||||
{
|
||||
// sum over all pixels – Scalar holds per-channel sums
|
||||
const cv::Scalar srcSum = cv::sum(srcRgbl);
|
||||
const cv::Scalar dstSum = cv::sum(dstRgbl);
|
||||
|
||||
// channel-wise gain factors
|
||||
const double gR = dstSum[0] / srcSum[0];
|
||||
const double gG = dstSum[1] / srcSum[1];
|
||||
const double gB = dstSum[2] / srcSum[2];
|
||||
|
||||
// shape == 9 for a 3×3 linear CCM, or 12 for a 3×4 affine CCM
|
||||
if (shape == 9) {
|
||||
// 3×3 diagonal matrix
|
||||
ccm0 = cv::Mat::zeros(3, 3, CV_64F);
|
||||
ccm0.at<double>(0, 0) = gR;
|
||||
ccm0.at<double>(1, 1) = gG;
|
||||
ccm0.at<double>(2, 2) = gB;
|
||||
}
|
||||
else {
|
||||
// 3×4 affine matrix (last column = zeros)
|
||||
ccm0 = cv::Mat::zeros(3, 4, CV_64F);
|
||||
ccm0.at<double>(0, 0) = gR;
|
||||
ccm0.at<double>(1, 1) = gG;
|
||||
ccm0.at<double>(2, 2) = gB;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ColorCorrectionModel::Impl::initialLeastSquare(bool fit)
|
||||
{
|
||||
Mat A, B, w;
|
||||
if (weights.empty())
|
||||
{
|
||||
A = srcRgbl;
|
||||
B = dstRgbl;
|
||||
}
|
||||
else
|
||||
{
|
||||
pow(weights, 0.5, w);
|
||||
Mat w_;
|
||||
merge(std::vector<Mat> { w, w, w }, w_);
|
||||
A = w_.mul(srcRgbl);
|
||||
B = w_.mul(dstRgbl);
|
||||
}
|
||||
solve(A.reshape(1, A.rows), B.reshape(1, B.rows), ccm0, DECOMP_SVD);
|
||||
|
||||
// if fit is True, return optimalization for rgbl distance function.
|
||||
if (fit)
|
||||
{
|
||||
ccm = ccm0;
|
||||
Mat residual = A.reshape(1, A.rows) * ccm.reshape(0, shape / 3) - B.reshape(1, B.rows);
|
||||
Scalar s = residual.dot(residual);
|
||||
double sum = s[0];
|
||||
loss = sqrt(sum / maskedLen);
|
||||
}
|
||||
}
|
||||
|
||||
double ColorCorrectionModel::Impl::calcLoss_(Color color)
|
||||
{
|
||||
Mat distlist = color.diff(ref, distance);
|
||||
Color lab = color.to(COLOR_SPACE_LAB_D50_2);
|
||||
Mat dist_;
|
||||
pow(distlist, 2, dist_);
|
||||
if (!weights.empty())
|
||||
{
|
||||
dist_ = weights.mul(dist_);
|
||||
}
|
||||
Scalar ss = sum(dist_);
|
||||
return ss[0];
|
||||
}
|
||||
|
||||
double ColorCorrectionModel::Impl::calcLoss(const Mat ccm_)
|
||||
{
|
||||
Mat converted = srcRgbl.reshape(1, 0) * ccm_;
|
||||
Color color(converted.reshape(3, 0), *(cs.l));
|
||||
return calcLoss_(color);
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::Impl::fitting(void)
|
||||
{
|
||||
cv::Ptr<DownhillSolver> solver = cv::DownhillSolver::create();
|
||||
cv::Ptr<LossFunction> ptr_F(new LossFunction(this));
|
||||
solver->setFunction(ptr_F);
|
||||
Mat reshapeCcm = ccm0.clone().reshape(0, 1);
|
||||
Mat step = Mat::ones(reshapeCcm.size(), CV_64F);
|
||||
solver->setInitStep(step);
|
||||
TermCriteria termcrit = TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, maxCount, epsilon);
|
||||
solver->setTermCriteria(termcrit);
|
||||
double res = solver->minimize(reshapeCcm);
|
||||
ccm = reshapeCcm.reshape(0, shape / 3);
|
||||
loss = sqrt(res / maskedLen);
|
||||
}
|
||||
|
||||
ColorCorrectionModel::ColorCorrectionModel()
|
||||
: p(std::make_shared<Impl>())
|
||||
{}
|
||||
|
||||
void ColorCorrectionModel::correctImage(InputArray src, OutputArray ref, bool islinear)
|
||||
{
|
||||
if (!p->ccm.data)
|
||||
{
|
||||
CV_Error(Error::StsBadArg, "No CCM values!" );
|
||||
}
|
||||
Mat img, normImg;
|
||||
if (p->rgb){
|
||||
cvtColor(src.getMat(), img, COLOR_BGR2RGB);
|
||||
} else {
|
||||
img = src.getMat();
|
||||
}
|
||||
|
||||
double scale;
|
||||
int type = img.type();
|
||||
switch (type) {
|
||||
case CV_8UC3:
|
||||
scale = 1.0 / 255.0;
|
||||
break;
|
||||
case CV_16UC3:
|
||||
scale = 1.0 / 65535.0;
|
||||
break;
|
||||
case CV_32FC3:
|
||||
scale = 1.0; // Already in [0,1] range
|
||||
break;
|
||||
default:
|
||||
CV_Error( cv::Error::StsUnsupportedFormat, "8-bit, 16-bit unsigned or 32-bit float 3-channel input images are supported");
|
||||
}
|
||||
|
||||
img.convertTo(normImg, CV_64F, scale);
|
||||
Mat linearImg = (p->linear)->linearize(normImg);
|
||||
Mat ccm = p->ccm.reshape(0, p->shape / 3);
|
||||
Mat imgCcm = multiple(p->prepare(linearImg), ccm);
|
||||
if (islinear == true)
|
||||
{
|
||||
imgCcm.copyTo(ref);
|
||||
}
|
||||
Mat imgCorrected = p->cs.fromLFunc(imgCcm, linearImg);
|
||||
|
||||
imgCorrected *= 1.0/scale;
|
||||
imgCorrected.convertTo(imgCorrected, type);
|
||||
|
||||
if (p->rgb)
|
||||
cvtColor(imgCorrected, imgCorrected, COLOR_RGB2BGR);
|
||||
imgCorrected.copyTo(ref);
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::Impl::getColor(ColorCheckerType constColor)
|
||||
{
|
||||
ref = GetColor().getColor(constColor);
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::Impl::getColor(Mat colors_, ColorSpace refColorSpace_)
|
||||
{
|
||||
ref = Color(colors_, *GetCS::getInstance().getCS(refColorSpace_));
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::Impl::getColor(Mat colors_, ColorSpace cs_, Mat colored_)
|
||||
{
|
||||
ref = Color(colors_, *GetCS::getInstance().getCS(cs_), colored_);
|
||||
}
|
||||
|
||||
ColorCorrectionModel::ColorCorrectionModel(InputArray src_, int constColor): p(std::make_shared<Impl>())
|
||||
{
|
||||
p->src = src_.getMat();
|
||||
p->getColor(static_cast<ColorCheckerType>(constColor));
|
||||
}
|
||||
|
||||
ColorCorrectionModel::ColorCorrectionModel(InputArray src_, InputArray colors_, ColorSpace refColorSpace_): p(std::make_shared<Impl>())
|
||||
{
|
||||
p->src = src_.getMat();
|
||||
p->getColor(colors_.getMat(), refColorSpace_);
|
||||
}
|
||||
|
||||
ColorCorrectionModel::ColorCorrectionModel(InputArray src_, InputArray colors_, ColorSpace cs_, InputArray coloredPatchesMask_): p(std::make_shared<Impl>())
|
||||
{
|
||||
p->src = src_.getMat();
|
||||
p->getColor(colors_.getMat(), cs_, coloredPatchesMask_.getMat());
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::setColorSpace(ColorSpace cs_)
|
||||
{
|
||||
p->cs = *GetCS::getInstance().getRgb(cs_);
|
||||
}
|
||||
void ColorCorrectionModel::setCcmType(CcmType ccmType_)
|
||||
{
|
||||
p->ccmType = ccmType_;
|
||||
}
|
||||
void ColorCorrectionModel::setDistance(DistanceType distance_)
|
||||
{
|
||||
p->distance = distance_;
|
||||
}
|
||||
void ColorCorrectionModel::setLinearization(LinearizationType linearizationType)
|
||||
{
|
||||
p->linearizationType = linearizationType;
|
||||
}
|
||||
void ColorCorrectionModel::setLinearizationGamma(double gamma)
|
||||
{
|
||||
p->gamma = gamma;
|
||||
}
|
||||
void ColorCorrectionModel::setLinearizationDegree(int deg)
|
||||
{
|
||||
p->deg = deg;
|
||||
}
|
||||
void ColorCorrectionModel::setSaturatedThreshold(double lower, double upper)
|
||||
{ //std::vector<double> saturatedThreshold
|
||||
p->saturatedThreshold = { lower, upper };
|
||||
}
|
||||
void ColorCorrectionModel::setWeightsList(const Mat& weightsList)
|
||||
{
|
||||
p->weightsList = weightsList;
|
||||
}
|
||||
void ColorCorrectionModel::setWeightCoeff(double weightsCoeff)
|
||||
{
|
||||
p->weightsCoeff = weightsCoeff;
|
||||
}
|
||||
void ColorCorrectionModel::setInitialMethod(InitialMethodType initialMethodType)
|
||||
{
|
||||
p->initialMethodType = initialMethodType;
|
||||
}
|
||||
void ColorCorrectionModel::setMaxCount(int maxCount_)
|
||||
{
|
||||
p->maxCount = maxCount_;
|
||||
}
|
||||
void ColorCorrectionModel::setEpsilon(double epsilon_)
|
||||
{
|
||||
p->epsilon = epsilon_;
|
||||
}
|
||||
void ColorCorrectionModel::setRGB(bool rgb_)
|
||||
{
|
||||
p->rgb = rgb_;
|
||||
}
|
||||
Mat ColorCorrectionModel::compute()
|
||||
{
|
||||
|
||||
Mat saturateMask = saturate(p->src, p->saturatedThreshold[0], p->saturatedThreshold[1]);
|
||||
p->linear = getLinear(p->gamma, p->deg, p->src, p->ref, saturateMask, (p->cs), p->linearizationType);
|
||||
p->calWeightsMasks(p->weightsList, p->weightsCoeff, saturateMask);
|
||||
p->srcRgbl = p->linear->linearize(maskCopyTo(p->src, p->mask));
|
||||
p->ref.colors = maskCopyTo(p->ref.colors, p->mask);
|
||||
p->dstRgbl = p->ref.to(*(p->cs.l)).colors;
|
||||
|
||||
// make no change for CCM_LINEAR, make change for CCM_AFFINE.
|
||||
p->srcRgbl = p->prepare(p->srcRgbl);
|
||||
|
||||
// distance function may affect the loss function and the fitting function
|
||||
switch (p->distance)
|
||||
{
|
||||
case cv::ccm::DISTANCE_RGBL:
|
||||
p->initialLeastSquare(true);
|
||||
break;
|
||||
default:
|
||||
switch (p->initialMethodType)
|
||||
{
|
||||
case cv::ccm::INITIAL_METHOD_WHITE_BALANCE:
|
||||
p->initialWhiteBalance();
|
||||
break;
|
||||
case cv::ccm::INITIAL_METHOD_LEAST_SQUARE:
|
||||
p->initialLeastSquare();
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Wrong initial_methoddistance_type!" );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
p->fitting();
|
||||
|
||||
return p->ccm;
|
||||
}
|
||||
Mat ColorCorrectionModel::getColorCorrectionMatrix() const
|
||||
{
|
||||
return p->ccm;
|
||||
}
|
||||
double ColorCorrectionModel::getLoss() const
|
||||
{
|
||||
return p->loss;
|
||||
}
|
||||
Mat ColorCorrectionModel::getSrcLinearRGB() const{
|
||||
return p->srcRgbl;
|
||||
}
|
||||
Mat ColorCorrectionModel::getRefLinearRGB() const{
|
||||
return p->dstRgbl;
|
||||
}
|
||||
Mat ColorCorrectionModel::getMask() const{
|
||||
return p->mask;
|
||||
}
|
||||
Mat ColorCorrectionModel::getWeights() const{
|
||||
return p->weights;
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::write(FileStorage& fs) const
|
||||
{
|
||||
fs << "ColorCorrectionModel" << "{"
|
||||
<< "ccm" << p->ccm
|
||||
<< "loss" << p->loss
|
||||
<< "csEnum" << p->csEnum
|
||||
<< "ccm_type" << p->ccmType
|
||||
<< "shape" << p->shape
|
||||
<< "linear" << *p->linear
|
||||
<< "distance" << p->distance
|
||||
<< "linear_type" << p->linearizationType
|
||||
<< "gamma" << p->gamma
|
||||
<< "deg" << p->deg
|
||||
<< "saturated_threshold" << p->saturatedThreshold
|
||||
<< "}";
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::read(const FileNode& node)
|
||||
{
|
||||
node["ccm"] >> p->ccm;
|
||||
node["loss"] >> p->loss;
|
||||
node["ccm_type"] >> p->ccmType;
|
||||
node["shape"] >> p->shape;
|
||||
node["distance"] >> p->distance;
|
||||
node["gamma"] >> p->gamma;
|
||||
node["deg"] >> p->deg;
|
||||
node["saturated_threshold"] >> p->saturatedThreshold;
|
||||
|
||||
ColorSpace csEnum;
|
||||
node["csEnum"] >> csEnum;
|
||||
setColorSpace(csEnum);
|
||||
|
||||
node["linear_type"] >> p->linearizationType;
|
||||
switch (p->linearizationType) {
|
||||
case cv::ccm::LINEARIZATION_GAMMA:
|
||||
p->linear = std::shared_ptr<Linear>(new LinearGamma());
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_COLORPOLYFIT:
|
||||
p->linear = std::shared_ptr<Linear>(new LinearColor<Polyfit>());
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_IDENTITY:
|
||||
p->linear = std::shared_ptr<Linear>(new LinearIdentity());
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_COLORLOGPOLYFIT:
|
||||
p->linear = std::shared_ptr<Linear>(new LinearColor<LogPolyfit>());
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_GRAYPOLYFIT:
|
||||
p->linear = std::shared_ptr<Linear>(new LinearGray<Polyfit>());
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_GRAYLOGPOLYFIT:
|
||||
p->linear = std::shared_ptr<Linear>(new LinearGray<LogPolyfit>());
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Wrong linear_type!");
|
||||
break;
|
||||
}
|
||||
node["linear"] >> *p->linear;
|
||||
}
|
||||
|
||||
void write(FileStorage& fs, const std::string&, const cv::ccm::ColorCorrectionModel& ccm)
|
||||
{
|
||||
ccm.write(fs);
|
||||
}
|
||||
|
||||
void read(const cv::FileNode& node, cv::ccm::ColorCorrectionModel& ccm, const cv::ccm::ColorCorrectionModel& defaultValue)
|
||||
{
|
||||
if (node.empty())
|
||||
ccm = defaultValue;
|
||||
else
|
||||
ccm.read(node);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
@@ -0,0 +1,391 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "color.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
Color::Color()
|
||||
: colors(Mat())
|
||||
, cs(std::make_shared<ColorSpaceBase>())
|
||||
{}
|
||||
Color::Color(Mat colors_, enum ColorSpace cs_)
|
||||
: colors(colors_)
|
||||
, cs(GetCS::getInstance().getCS(cs_))
|
||||
{}
|
||||
|
||||
Color::Color(Mat colors_, enum ColorSpace cs_, Mat colored_)
|
||||
: colors(colors_)
|
||||
, cs(GetCS::getInstance().getCS(cs_))
|
||||
, colored(colored_)
|
||||
{
|
||||
grays = ~colored;
|
||||
}
|
||||
Color::Color(Mat colors_, const ColorSpaceBase& cs_, Mat colored_)
|
||||
: colors(colors_)
|
||||
, cs(std::make_shared<ColorSpaceBase>(cs_))
|
||||
, colored(colored_)
|
||||
{
|
||||
grays = ~colored;
|
||||
}
|
||||
|
||||
Color::Color(Mat colors_, const ColorSpaceBase& cs_)
|
||||
: colors(colors_)
|
||||
, cs(std::make_shared<ColorSpaceBase>(cs_))
|
||||
{}
|
||||
|
||||
Color::Color(Mat colors_, std::shared_ptr<ColorSpaceBase> cs_)
|
||||
: colors(colors_)
|
||||
, cs(cs_)
|
||||
{}
|
||||
|
||||
Color Color::to(const ColorSpaceBase& other, ChromaticAdaptationType method, bool save)
|
||||
{
|
||||
auto it = history.find(other);
|
||||
if ( it != history.end() )
|
||||
{
|
||||
return *(it->second);
|
||||
}
|
||||
if (cs->relate(other))
|
||||
{
|
||||
return Color(cs->relation(other).run(colors), other);
|
||||
}
|
||||
Operations ops;
|
||||
ops.add(cs->to).add(XYZ(cs->illumobserver).cam(other.illumobserver, method)).add(other.from);
|
||||
Mat converted = ops.run(colors);
|
||||
if (save)
|
||||
{
|
||||
auto ptr = std::make_shared<Color>(converted, other);
|
||||
history[other] = ptr;
|
||||
return *ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Color(converted, other);
|
||||
}
|
||||
}
|
||||
|
||||
Color Color::to(ColorSpace other, ChromaticAdaptationType method, bool save)
|
||||
{
|
||||
return to(*GetCS::getInstance().getCS(other), method, save);
|
||||
}
|
||||
|
||||
Mat Color::channel(Mat m, int i)
|
||||
{
|
||||
Mat dchannels[3];
|
||||
split(m, dchannels);
|
||||
return dchannels[i];
|
||||
}
|
||||
|
||||
Mat Color::toGray(const IllumObserver& illumobserver, ChromaticAdaptationType method, bool save)
|
||||
{
|
||||
XYZ xyz = *XYZ::get(illumobserver);
|
||||
return channel(this->to(xyz, method, save).colors, 1);
|
||||
}
|
||||
|
||||
Mat Color::toLuminant(const IllumObserver& illumobserver, ChromaticAdaptationType method, bool save)
|
||||
{
|
||||
Lab lab = *Lab::get(illumobserver);
|
||||
return channel(this->to(lab, method, save).colors, 0);
|
||||
}
|
||||
|
||||
Mat Color::diff(Color& other, DistanceType method)
|
||||
{
|
||||
return diff(other, cs->illumobserver, method);
|
||||
}
|
||||
|
||||
Mat Color::diff(Color& other, const IllumObserver& illumobserver, DistanceType method)
|
||||
{
|
||||
Lab lab = *Lab::get(illumobserver);
|
||||
switch (method)
|
||||
{
|
||||
case cv::ccm::DISTANCE_CIE76:
|
||||
case cv::ccm::DISTANCE_CIE94_GRAPHIC_ARTS:
|
||||
case cv::ccm::DISTANCE_CIE94_TEXTILES:
|
||||
case cv::ccm::DISTANCE_CIE2000:
|
||||
case cv::ccm::DISTANCE_CMC_1TO1:
|
||||
case cv::ccm::DISTANCE_CMC_2TO1:
|
||||
return distance(to(lab).colors, other.to(lab).colors, method);
|
||||
case cv::ccm::DISTANCE_RGB:
|
||||
return distance(to(*cs->nl).colors, other.to(*cs->nl).colors, method);
|
||||
case cv::ccm::DISTANCE_RGBL:
|
||||
return distance(to(*cs->l).colors, other.to(*cs->l).colors, method);
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Wrong method!" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Color::getGray(double JDN)
|
||||
{
|
||||
if (!grays.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Mat lab = to(COLOR_SPACE_LAB_D65_2).colors;
|
||||
Mat gray(colors.size(), colors.type());
|
||||
int fromto[] = { 0, 0, -1, 1, -1, 2 };
|
||||
mixChannels(&lab, 1, &gray, 1, fromto, 3);
|
||||
Mat d = distance(lab, gray, DISTANCE_CIE2000);
|
||||
this->grays = d < JDN;
|
||||
this->colored = ~grays;
|
||||
}
|
||||
|
||||
Color Color::operator[](Mat mask)
|
||||
{
|
||||
return Color(maskCopyTo(colors, mask), cs);
|
||||
}
|
||||
|
||||
Mat GetColor::getColorChecker(const double* checker, int row)
|
||||
{
|
||||
Mat res(row, 1, CV_64FC3);
|
||||
for (int i = 0; i < row; ++i)
|
||||
{
|
||||
res.at<Vec3d>(i, 0) = Vec3d(checker[3 * i], checker[3 * i + 1], checker[3 * i + 2]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Mat GetColor::getColorCheckerMask(const uchar* checker, int row)
|
||||
{
|
||||
Mat res(row, 1, CV_8U);
|
||||
for (int i = 0; i < row; ++i)
|
||||
{
|
||||
res.at<uchar>(i, 0) = checker[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Color GetColor::getColor(ColorCheckerType const_color)
|
||||
{
|
||||
|
||||
/** @brief Data is from https://www.imatest.com/wp-content/uploads/2011/11/Lab-data-Iluminate-D65-D50-spectro.xls
|
||||
see Miscellaneous.md for details.
|
||||
*/
|
||||
static const double ColorChecker2005_LAB_D50_2[24][3] = { { 37.986, 13.555, 14.059 },
|
||||
{ 65.711, 18.13, 17.81 },
|
||||
{ 49.927, -4.88, -21.925 },
|
||||
{ 43.139, -13.095, 21.905 },
|
||||
{ 55.112, 8.844, -25.399 },
|
||||
{ 70.719, -33.397, -0.199 },
|
||||
{ 62.661, 36.067, 57.096 },
|
||||
{ 40.02, 10.41, -45.964 },
|
||||
{ 51.124, 48.239, 16.248 },
|
||||
{ 30.325, 22.976, -21.587 },
|
||||
{ 72.532, -23.709, 57.255 },
|
||||
{ 71.941, 19.363, 67.857 },
|
||||
{ 28.778, 14.179, -50.297 },
|
||||
{ 55.261, -38.342, 31.37 },
|
||||
{ 42.101, 53.378, 28.19 },
|
||||
{ 81.733, 4.039, 79.819 },
|
||||
{ 51.935, 49.986, -14.574 },
|
||||
{ 51.038, -28.631, -28.638 },
|
||||
{ 96.539, -0.425, 1.186 },
|
||||
{ 81.257, -0.638, -0.335 },
|
||||
{ 66.766, -0.734, -0.504 },
|
||||
{ 50.867, -0.153, -0.27 },
|
||||
{ 35.656, -0.421, -1.231 },
|
||||
{ 20.461, -0.079, -0.973 } };
|
||||
|
||||
static const uchar ColorChecker2005_COLORED_MASK[24] = { 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0 };
|
||||
static const double Vinyl_LAB_D50_2[18][3] = { { 100, 0.00520000001, -0.0104 },
|
||||
{ 73.0833969, -0.819999993, -2.02099991 },
|
||||
{ 62.493, 0.425999999, -2.23099995 },
|
||||
{ 50.4640007, 0.446999997, -2.32399988 },
|
||||
{ 37.7970009, 0.0359999985, -1.29700005 },
|
||||
{ 0, 0, 0 },
|
||||
{ 51.5880013, 73.5179977, 51.5690002 },
|
||||
{ 93.6989975, -15.7340002, 91.9420013 },
|
||||
{ 69.4079971, -46.5940018, 50.4869995 },
|
||||
{ 66.61000060000001, -13.6789999, -43.1720009 },
|
||||
{ 11.7110004, 16.9799995, -37.1759987 },
|
||||
{ 51.973999, 81.9440002, -8.40699959 },
|
||||
{ 40.5489998, 50.4399986, 24.8490009 },
|
||||
{ 60.8160019, 26.0690002, 49.4420013 },
|
||||
{ 52.2529984, -19.9500008, -23.9960003 },
|
||||
{ 51.2859993, 48.4700012, -15.0579996 },
|
||||
{ 68.70700069999999, 12.2959995, 16.2129993 },
|
||||
{ 63.6839981, 10.2930002, 16.7639999 } };
|
||||
static const uchar Vinyl_COLORED_MASK[18] = { 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1 };
|
||||
static const double DigitalSG_LAB_D50_2[140][3] = { { 96.55, -0.91, 0.57 },
|
||||
{ 6.43, -0.06, -0.41 },
|
||||
{ 49.7, -0.18, 0.03 },
|
||||
{ 96.5, -0.89, 0.59 },
|
||||
{ 6.5, -0.06, -0.44 },
|
||||
{ 49.66, -0.2, 0.01 },
|
||||
{ 96.52, -0.91, 0.58 },
|
||||
{ 6.49, -0.02, -0.28 },
|
||||
{ 49.72, -0.2, 0.04 },
|
||||
{ 96.43, -0.91, 0.67 },
|
||||
{ 49.72, -0.19, 0 },
|
||||
{ 32.6, 51.58, -10.85 },
|
||||
{ 60.75, 26.22, -18.6 },
|
||||
{ 28.69, 48.28, -39 },
|
||||
{ 49.38, -15.43, -48.48 },
|
||||
{ 60.63, -30.77, -26.23 },
|
||||
{ 19.29, -26.37, -6.15 },
|
||||
{ 60.15, -41.77, -12.6 },
|
||||
{ 21.42, 1.67, 8.79 },
|
||||
{ 49.69, -0.2, 0.01 },
|
||||
{ 6.5, -0.03, -0.67 },
|
||||
{ 21.82, 17.33, -18.35 },
|
||||
{ 41.53, 18.48, -37.26 },
|
||||
{ 19.99, -0.16, -36.29 },
|
||||
{ 60.16, -18.45, -31.42 },
|
||||
{ 19.94, -17.92, -20.96 },
|
||||
{ 60.68, -6.05, -32.81 },
|
||||
{ 50.81, -49.8, -9.63 },
|
||||
{ 60.65, -39.77, 20.76 },
|
||||
{ 6.53, -0.03, -0.43 },
|
||||
{ 96.56, -0.91, 0.59 },
|
||||
{ 84.19, -1.95, -8.23 },
|
||||
{ 84.75, 14.55, 0.23 },
|
||||
{ 84.87, -19.07, -0.82 },
|
||||
{ 85.15, 13.48, 6.82 },
|
||||
{ 84.17, -10.45, 26.78 },
|
||||
{ 61.74, 31.06, 36.42 },
|
||||
{ 64.37, 20.82, 18.92 },
|
||||
{ 50.4, -53.22, 14.62 },
|
||||
{ 96.51, -0.89, 0.65 },
|
||||
{ 49.74, -0.19, 0.03 },
|
||||
{ 31.91, 18.62, 21.99 },
|
||||
{ 60.74, 38.66, 70.97 },
|
||||
{ 19.35, 22.23, -58.86 },
|
||||
{ 96.52, -0.91, 0.62 },
|
||||
{ 6.66, 0, -0.3 },
|
||||
{ 76.51, 20.81, 22.72 },
|
||||
{ 72.79, 29.15, 24.18 },
|
||||
{ 22.33, -20.7, 5.75 },
|
||||
{ 49.7, -0.19, 0.01 },
|
||||
{ 6.53, -0.05, -0.61 },
|
||||
{ 63.42, 20.19, 19.22 },
|
||||
{ 34.94, 11.64, -50.7 },
|
||||
{ 52.03, -44.15, 39.04 },
|
||||
{ 79.43, 0.29, -0.17 },
|
||||
{ 30.67, -0.14, -0.53 },
|
||||
{ 63.6, 14.44, 26.07 },
|
||||
{ 64.37, 14.5, 17.05 },
|
||||
{ 60.01, -44.33, 8.49 },
|
||||
{ 6.63, -0.01, -0.47 },
|
||||
{ 96.56, -0.93, 0.59 },
|
||||
{ 46.37, -5.09, -24.46 },
|
||||
{ 47.08, 52.97, 20.49 },
|
||||
{ 36.04, 64.92, 38.51 },
|
||||
{ 65.05, 0, -0.32 },
|
||||
{ 40.14, -0.19, -0.38 },
|
||||
{ 43.77, 16.46, 27.12 },
|
||||
{ 64.39, 17, 16.59 },
|
||||
{ 60.79, -29.74, 41.5 },
|
||||
{ 96.48, -0.89, 0.64 },
|
||||
{ 49.75, -0.21, 0.01 },
|
||||
{ 38.18, -16.99, 30.87 },
|
||||
{ 21.31, 29.14, -27.51 },
|
||||
{ 80.57, 3.85, 89.61 },
|
||||
{ 49.71, -0.2, 0.03 },
|
||||
{ 60.27, 0.08, -0.41 },
|
||||
{ 67.34, 14.45, 16.9 },
|
||||
{ 64.69, 16.95, 18.57 },
|
||||
{ 51.12, -49.31, 44.41 },
|
||||
{ 49.7, -0.2, 0.02 },
|
||||
{ 6.67, -0.05, -0.64 },
|
||||
{ 51.56, 9.16, -26.88 },
|
||||
{ 70.83, -24.26, 64.77 },
|
||||
{ 48.06, 55.33, -15.61 },
|
||||
{ 35.26, -0.09, -0.24 },
|
||||
{ 75.16, 0.25, -0.2 },
|
||||
{ 44.54, 26.27, 38.93 },
|
||||
{ 35.91, 16.59, 26.46 },
|
||||
{ 61.49, -52.73, 47.3 },
|
||||
{ 6.59, -0.05, -0.5 },
|
||||
{ 96.58, -0.9, 0.61 },
|
||||
{ 68.93, -34.58, -0.34 },
|
||||
{ 69.65, 20.09, 78.57 },
|
||||
{ 47.79, -33.18, -30.21 },
|
||||
{ 15.94, -0.42, -1.2 },
|
||||
{ 89.02, -0.36, -0.48 },
|
||||
{ 63.43, 25.44, 26.25 },
|
||||
{ 65.75, 22.06, 27.82 },
|
||||
{ 61.47, 17.1, 50.72 },
|
||||
{ 96.53, -0.89, 0.66 },
|
||||
{ 49.79, -0.2, 0.03 },
|
||||
{ 85.17, 10.89, 17.26 },
|
||||
{ 89.74, -16.52, 6.19 },
|
||||
{ 84.55, 5.07, -6.12 },
|
||||
{ 84.02, -13.87, -8.72 },
|
||||
{ 70.76, 0.07, -0.35 },
|
||||
{ 45.59, -0.05, 0.23 },
|
||||
{ 20.3, 0.07, -0.32 },
|
||||
{ 61.79, -13.41, 55.42 },
|
||||
{ 49.72, -0.19, 0.02 },
|
||||
{ 6.77, -0.05, -0.44 },
|
||||
{ 21.85, 34.37, 7.83 },
|
||||
{ 42.66, 67.43, 48.42 },
|
||||
{ 60.33, 36.56, 3.56 },
|
||||
{ 61.22, 36.61, 17.32 },
|
||||
{ 62.07, 52.8, 77.14 },
|
||||
{ 72.42, -9.82, 89.66 },
|
||||
{ 62.03, 3.53, 57.01 },
|
||||
{ 71.95, -27.34, 73.69 },
|
||||
{ 6.59, -0.04, -0.45 },
|
||||
{ 49.77, -0.19, 0.04 },
|
||||
{ 41.84, 62.05, 10.01 },
|
||||
{ 19.78, 29.16, -7.85 },
|
||||
{ 39.56, 65.98, 33.71 },
|
||||
{ 52.39, 68.33, 47.84 },
|
||||
{ 81.23, 24.12, 87.51 },
|
||||
{ 81.8, 6.78, 95.75 },
|
||||
{ 71.72, -16.23, 76.28 },
|
||||
{ 20.31, 14.45, 16.74 },
|
||||
{ 49.68, -0.19, 0.05 },
|
||||
{ 96.48, -0.88, 0.68 },
|
||||
{ 49.69, -0.18, 0.03 },
|
||||
{ 6.39, -0.04, -0.33 },
|
||||
{ 96.54, -0.9, 0.67 },
|
||||
{ 49.72, -0.18, 0.05 },
|
||||
{ 6.49, -0.03, -0.41 },
|
||||
{ 96.51, -0.9, 0.69 },
|
||||
{ 49.7, -0.19, 0.07 },
|
||||
{ 6.47, 0, -0.38 },
|
||||
{ 96.46, -0.89, 0.7 } };
|
||||
|
||||
switch (const_color)
|
||||
{
|
||||
|
||||
case cv::ccm::COLORCHECKER_MACBETH:
|
||||
{
|
||||
Mat ColorChecker2005_LAB_D50_2_ = GetColor::getColorChecker(*ColorChecker2005_LAB_D50_2, 24);
|
||||
Mat ColorChecker2005_COLORED_MASK_ = GetColor::getColorCheckerMask(ColorChecker2005_COLORED_MASK, 24);
|
||||
Color Macbeth_D50_2 = Color(ColorChecker2005_LAB_D50_2_, COLOR_SPACE_LAB_D50_2, ColorChecker2005_COLORED_MASK_);
|
||||
return Macbeth_D50_2;
|
||||
}
|
||||
|
||||
case cv::ccm::COLORCHECKER_VINYL:
|
||||
{
|
||||
Mat Vinyl_LAB_D50_2__ = GetColor::getColorChecker(*Vinyl_LAB_D50_2, 18);
|
||||
Mat Vinyl_COLORED_MASK__ = GetColor::getColorCheckerMask(Vinyl_COLORED_MASK, 18);
|
||||
Color Vinyl_D50_2 = Color(Vinyl_LAB_D50_2__, COLOR_SPACE_LAB_D50_2, Vinyl_COLORED_MASK__);
|
||||
return Vinyl_D50_2;
|
||||
}
|
||||
|
||||
case cv::ccm::COLORCHECKER_DIGITAL_SG:
|
||||
{
|
||||
Mat DigitalSG_LAB_D50_2__ = GetColor::getColorChecker(*DigitalSG_LAB_D50_2, 140);
|
||||
Color DigitalSG_D50_2 = Color(DigitalSG_LAB_D50_2__, COLOR_SPACE_LAB_D50_2);
|
||||
return DigitalSG_D50_2;
|
||||
}
|
||||
}
|
||||
CV_Error(Error::StsNotImplemented, "");
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
@@ -0,0 +1,108 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_COLOR_HPP__
|
||||
#define __OPENCV_CCM_COLOR_HPP__
|
||||
|
||||
#include "distance.hpp"
|
||||
#include "colorspace.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
/** @brief Color defined by color_values and color space
|
||||
*/
|
||||
|
||||
class Color
|
||||
{
|
||||
public:
|
||||
/** @param grays mask of grayscale color
|
||||
@param colored mask of colored color
|
||||
@param history storage of historical conversion
|
||||
*/
|
||||
Mat colors;
|
||||
std::shared_ptr<ColorSpaceBase> cs;
|
||||
Mat grays;
|
||||
Mat colored;
|
||||
std::map<ColorSpaceBase, std::shared_ptr<Color>> history;
|
||||
|
||||
Color();
|
||||
Color(Mat colors_, enum ColorSpace cs_);
|
||||
Color(Mat colors_, enum ColorSpace cs_, Mat colored);
|
||||
Color(Mat colors_, const ColorSpaceBase& cs, Mat colored);
|
||||
Color(Mat colors_, const ColorSpaceBase& cs);
|
||||
Color(Mat colors_, std::shared_ptr<ColorSpaceBase> cs_);
|
||||
virtual ~Color() {};
|
||||
|
||||
/** @brief Change to other color space.
|
||||
The conversion process incorporates linear transformations to speed up.
|
||||
@param other type of ColorSpaceBase.
|
||||
@param method the chromatic adapation method.
|
||||
@param save when save if True, get data from history first.
|
||||
@return Color.
|
||||
*/
|
||||
Color to(const ColorSpaceBase& other, ChromaticAdaptationType method = BRADFORD, bool save = true);
|
||||
|
||||
/** @brief Convert color to another color space using ColorSpace enum.
|
||||
@param other type of ColorSpace.
|
||||
@param method the method of chromatic adaptation.
|
||||
@param save whether to save the conversion history.
|
||||
@return the output array, type of Color.
|
||||
*/
|
||||
Color to(ColorSpace other, ChromaticAdaptationType method = BRADFORD, bool save = true);
|
||||
|
||||
/** @brief Channels split.
|
||||
@return each channel.
|
||||
*/
|
||||
Mat channel(Mat m, int i);
|
||||
|
||||
/** @brief To Gray.
|
||||
*/
|
||||
Mat toGray(const IllumObserver& illumobserver, ChromaticAdaptationType method = BRADFORD, bool save = true);
|
||||
|
||||
/** @brief To Luminant.
|
||||
*/
|
||||
Mat toLuminant(const IllumObserver& illumobserver, ChromaticAdaptationType method = BRADFORD, bool save = true);
|
||||
|
||||
/** @brief Diff without IllumObserver.
|
||||
@param other type of Color.
|
||||
@param method type of distance.
|
||||
@return distance between self and other
|
||||
*/
|
||||
Mat diff(Color& other, DistanceType method = DISTANCE_CIE2000);
|
||||
|
||||
/** @brief Diff with IllumObserver.
|
||||
@param other type of Color.
|
||||
@param illumobserver type of IllumObserver.
|
||||
@param method type of distance.
|
||||
@return distance between self and other
|
||||
*/
|
||||
Mat diff(Color& other, const IllumObserver& illumobserver, DistanceType method = DISTANCE_CIE2000);
|
||||
|
||||
/** @brief Calculate gray mask.
|
||||
*/
|
||||
void getGray(double JDN = 2.0);
|
||||
|
||||
/** @brief Operator for mask copy.
|
||||
*/
|
||||
Color operator[](Mat mask);
|
||||
};
|
||||
|
||||
class GetColor
|
||||
{
|
||||
public:
|
||||
Color getColor(ColorCheckerType const_color);
|
||||
static Mat getColorChecker(const double* checker, int row);
|
||||
static Mat getColorCheckerMask(const uchar* checker, int row);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,769 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "colorspace.hpp"
|
||||
#include "operations.hpp"
|
||||
#include "illumobserver.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
static const std::vector<double>& getIlluminants(const IllumObserver& illumobserver)
|
||||
{
|
||||
static const std::map<IllumObserver, std::vector<double>> illuminants = {
|
||||
{ IllumObserver::getIllumObservers(A_2), { 1.098466069456375, 1, 0.3558228003436005 } },
|
||||
{ IllumObserver::getIllumObservers(A_10), { 1.111420406956693, 1, 0.3519978321919493 } },
|
||||
{ IllumObserver::getIllumObservers(D50_2), { 0.9642119944211994, 1, 0.8251882845188288 } },
|
||||
{ IllumObserver::getIllumObservers(D50_10), { 0.9672062750333777, 1, 0.8142801513128616 } },
|
||||
{ IllumObserver::getIllumObservers(D55_2), { 0.956797052643698, 1, 0.9214805860173273 } },
|
||||
{ IllumObserver::getIllumObservers(D55_10), { 0.9579665682254781, 1, 0.9092525159847462 } },
|
||||
{ IllumObserver::getIllumObservers(D65_2), { 0.95047, 1., 1.08883 } },
|
||||
{ IllumObserver::getIllumObservers(D65_10), { 0.94811, 1., 1.07304 } },
|
||||
{ IllumObserver::getIllumObservers(D75_2), { 0.9497220898840717, 1, 1.226393520724154 } },
|
||||
{ IllumObserver::getIllumObservers(D75_10), { 0.9441713925645873, 1, 1.2064272211720228 } },
|
||||
{ IllumObserver::getIllumObservers(E_2), { 1., 1., 1. } },
|
||||
{ IllumObserver::getIllumObservers(E_10), { 1., 1., 1. } },
|
||||
};
|
||||
auto it = illuminants.find(illumobserver);
|
||||
CV_Assert(it != illuminants.end());
|
||||
return it->second;
|
||||
};
|
||||
|
||||
/* @brief Basic class for ColorSpaceBase.
|
||||
*/
|
||||
bool ColorSpaceBase::relate(const ColorSpaceBase& other) const
|
||||
{
|
||||
return (type == other.type) && (illumobserver == other.illumobserver);
|
||||
};
|
||||
|
||||
Operations ColorSpaceBase::relation(const ColorSpaceBase& /*other*/) const
|
||||
{
|
||||
return Operations::getIdentityOps();
|
||||
}
|
||||
|
||||
bool ColorSpaceBase::operator<(const ColorSpaceBase& other) const
|
||||
{
|
||||
return (illumobserver < other.illumobserver || (illumobserver == other.illumobserver && type < other.type) || (illumobserver == other.illumobserver && type == other.type && linear < other.linear));
|
||||
}
|
||||
|
||||
/* @brief Base of RGB color space;
|
||||
* the argument values are from AdobeRGB;
|
||||
* Data from https://en.wikipedia.org/wiki/Adobe_RGB_color_space
|
||||
*/
|
||||
Operations RGBBase_::relation(const ColorSpaceBase& other) const
|
||||
{
|
||||
if (linear == other.linear)
|
||||
{
|
||||
return Operations::getIdentityOps();
|
||||
}
|
||||
if (linear)
|
||||
{
|
||||
return Operations({ Operation([this](Mat rgbl) -> Mat { return fromLFunc(rgbl); }) });
|
||||
}
|
||||
return Operations({ Operation([this](Mat rgb) -> Mat { return toLFunc(rgb); })});
|
||||
}
|
||||
|
||||
/* @brief Initial operations.
|
||||
*/
|
||||
void RGBBase_::init()
|
||||
{
|
||||
setParameter();
|
||||
calLinear();
|
||||
calM();
|
||||
calOperations();
|
||||
}
|
||||
|
||||
/* @brief Produce color space instance with linear and non-linear versions.
|
||||
* @param rgbl type of RGBBase_.
|
||||
*/
|
||||
void RGBBase_::bind(RGBBase_& rgbl)
|
||||
{
|
||||
init();
|
||||
rgbl.init();
|
||||
l = &rgbl;
|
||||
rgbl.l = &rgbl;
|
||||
nl = this;
|
||||
rgbl.nl = this;
|
||||
}
|
||||
|
||||
/* @brief Calculation of M_RGBL2XYZ_base.
|
||||
*/
|
||||
void RGBBase_::calM()
|
||||
{
|
||||
Mat XYZr, XYZg, XYZb, XYZ_rgbl, Srgb;
|
||||
XYZr = Mat(xyY2XYZ({ xr, yr }), true);
|
||||
XYZg = Mat(xyY2XYZ({ xg, yg }), true);
|
||||
XYZb = Mat(xyY2XYZ({ xb, yb }), true);
|
||||
merge(std::vector<Mat> { XYZr, XYZg, XYZb }, XYZ_rgbl);
|
||||
XYZ_rgbl = XYZ_rgbl.reshape(1, (int)XYZ_rgbl.total());
|
||||
Mat XYZw = Mat(getIlluminants(illumobserver), true);
|
||||
XYZw = XYZw.reshape(1, (int)XYZw.total());
|
||||
solve(XYZ_rgbl, XYZw, Srgb);
|
||||
merge(std::vector<Mat> { Srgb.at<double>(0) * XYZr, Srgb.at<double>(1) * XYZg,
|
||||
Srgb.at<double>(2) * XYZb },
|
||||
M_to);
|
||||
M_to = M_to.reshape(1, (int)M_to.total());
|
||||
M_from = M_to.inv();
|
||||
};
|
||||
|
||||
/* @brief operations to or from XYZ.
|
||||
*/
|
||||
void RGBBase_::calOperations()
|
||||
{
|
||||
if (linear)
|
||||
{
|
||||
to = Operations({ Operation(M_to.t()) });
|
||||
from = Operations({ Operation(M_from.t()) });
|
||||
}
|
||||
else
|
||||
{
|
||||
// rgb -> rgbl
|
||||
to = Operations({ Operation([this](Mat rgb) -> Mat { return toLFunc(rgb); }), Operation(M_to.t()) });
|
||||
// rgbl -> rgb
|
||||
from = Operations({ Operation(M_from.t()), Operation([this](Mat rgbl) -> Mat { return fromLFunc(rgbl); }) });
|
||||
}
|
||||
}
|
||||
|
||||
Mat RGBBase_::toLFunc(Mat& /*rgb*/) const { return Mat(); }
|
||||
|
||||
Mat RGBBase_::fromLFunc(Mat& /*rgbl*/, Mat dst) const { return dst; }
|
||||
|
||||
/* @brief Base of Adobe RGB color space;
|
||||
*/
|
||||
|
||||
Mat AdobeRGBBase_::toLFunc(Mat& rgb) const
|
||||
{
|
||||
Mat out;
|
||||
gammaCorrection(rgb, out, gamma);
|
||||
return out;
|
||||
}
|
||||
|
||||
Mat AdobeRGBBase_::fromLFunc(Mat& rgbl, Mat dst) const
|
||||
{
|
||||
gammaCorrection(rgbl, dst, 1. / gamma);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* @brief Base of sRGB color space;
|
||||
*/
|
||||
|
||||
void sRGBBase_::calLinear()
|
||||
{
|
||||
alpha = a + 1;
|
||||
K0 = a / (gamma - 1);
|
||||
phi = (pow(alpha, gamma) * pow(gamma - 1, gamma - 1)) / (pow(a, gamma - 1) * pow(gamma, gamma));
|
||||
beta = K0 / phi;
|
||||
}
|
||||
|
||||
/* @brief Used by toLFunc.
|
||||
*/
|
||||
double sRGBBase_::toLFuncEW(double x) const
|
||||
{
|
||||
if (x > K0)
|
||||
{
|
||||
return pow(((x + alpha - 1) / alpha), gamma);
|
||||
}
|
||||
else if (x >= -K0)
|
||||
{
|
||||
return x / phi;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -(pow(((-x + alpha - 1) / alpha), gamma));
|
||||
}
|
||||
}
|
||||
|
||||
/* @brief Linearization.
|
||||
* @param rgb the input array, type of cv::Mat.
|
||||
* @return the output array, type of cv::Mat.
|
||||
*/
|
||||
Mat sRGBBase_::toLFunc(Mat& rgb) const
|
||||
{
|
||||
return elementWise(rgb,
|
||||
[this](double a_) -> double { return toLFuncEW(a_); });
|
||||
}
|
||||
|
||||
/* @brief Used by fromLFunc.
|
||||
*/
|
||||
double sRGBBase_::fromLFuncEW(double x) const
|
||||
{
|
||||
if (x > beta)
|
||||
{
|
||||
return alpha * pow(x, 1 / gamma) - (alpha - 1);
|
||||
}
|
||||
else if (x >= -beta)
|
||||
{
|
||||
return x * phi;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -(alpha * pow(-x, 1 / gamma) - (alpha - 1));
|
||||
}
|
||||
}
|
||||
|
||||
/* @brief Delinearization.
|
||||
* @param rgbl the input array, type of cv::Mat.
|
||||
* @return the output array, type of cv::Mat.
|
||||
*/
|
||||
Mat sRGBBase_::fromLFunc(Mat& rgbl, Mat dst) const
|
||||
{
|
||||
return elementWise(rgbl, [this](double a_) -> double { return fromLFuncEW(a_); }, dst);
|
||||
}
|
||||
|
||||
/* @brief sRGB color space.
|
||||
* data from https://en.wikipedia.org/wiki/SRGB.
|
||||
*/
|
||||
void sRGB_::setParameter()
|
||||
{
|
||||
xr = 0.64;
|
||||
yr = 0.33;
|
||||
xg = 0.3;
|
||||
yg = 0.6;
|
||||
xb = 0.15;
|
||||
yb = 0.06;
|
||||
a = 0.055;
|
||||
gamma = 2.4;
|
||||
}
|
||||
|
||||
/* @brief Adobe RGB color space.
|
||||
*/
|
||||
void AdobeRGB_::setParameter()
|
||||
{
|
||||
xr = 0.64;
|
||||
yr = 0.33;
|
||||
xg = 0.21;
|
||||
yg = 0.71;
|
||||
xb = 0.15;
|
||||
yb = 0.06;
|
||||
gamma = 2.2;
|
||||
}
|
||||
|
||||
/* @brief Wide-gamut RGB color space.
|
||||
* data from https://en.wikipedia.org/wiki/Wide-gamut_RGB_color_space.
|
||||
*/
|
||||
void WideGamutRGB_::setParameter()
|
||||
{
|
||||
xr = 0.7347;
|
||||
yr = 0.2653;
|
||||
xg = 0.1152;
|
||||
yg = 0.8264;
|
||||
xb = 0.1566;
|
||||
yb = 0.0177;
|
||||
gamma = 2.2;
|
||||
}
|
||||
|
||||
/* @brief ProPhoto RGB color space.
|
||||
* data from https://en.wikipedia.org/wiki/ProPhoto_RGB_color_space.
|
||||
*/
|
||||
void ProPhotoRGB_::setParameter()
|
||||
{
|
||||
xr = 0.734699;
|
||||
yr = 0.265301;
|
||||
xg = 0.159597;
|
||||
yg = 0.840403;
|
||||
xb = 0.036598;
|
||||
yb = 0.000105;
|
||||
gamma = 1.8;
|
||||
}
|
||||
|
||||
/* @brief DCI-P3 RGB color space.
|
||||
* data from https://en.wikipedia.org/wiki/DCI-P3.
|
||||
*/
|
||||
|
||||
void DCI_P3_RGB_::setParameter()
|
||||
{
|
||||
xr = 0.68;
|
||||
yr = 0.32;
|
||||
xg = 0.265;
|
||||
yg = 0.69;
|
||||
xb = 0.15;
|
||||
yb = 0.06;
|
||||
gamma = 2.2;
|
||||
}
|
||||
|
||||
/* @brief Apple RGB color space.
|
||||
* data from
|
||||
* http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html.
|
||||
*/
|
||||
void AppleRGB_::setParameter()
|
||||
{
|
||||
xr = 0.625;
|
||||
yr = 0.34;
|
||||
xg = 0.28;
|
||||
yg = 0.595;
|
||||
xb = 0.155;
|
||||
yb = 0.07;
|
||||
gamma = 1.8;
|
||||
}
|
||||
|
||||
/* @brief REC_709 RGB color space.
|
||||
* data from https://en.wikipedia.org/wiki/Rec._709.
|
||||
*/
|
||||
void REC_709_RGB_::setParameter()
|
||||
{
|
||||
xr = 0.64;
|
||||
yr = 0.33;
|
||||
xg = 0.3;
|
||||
yg = 0.6;
|
||||
xb = 0.15;
|
||||
yb = 0.06;
|
||||
a = 0.099;
|
||||
gamma = 1 / 0.45;
|
||||
}
|
||||
|
||||
/* @brief REC_2020 RGB color space.
|
||||
* data from https://en.wikipedia.org/wiki/Rec._2020.
|
||||
*/
|
||||
|
||||
void REC_2020_RGB_::setParameter()
|
||||
{
|
||||
xr = 0.708;
|
||||
yr = 0.292;
|
||||
xg = 0.17;
|
||||
yg = 0.797;
|
||||
xb = 0.131;
|
||||
yb = 0.046;
|
||||
a = 0.09929682680944;
|
||||
gamma = 1 / 0.45;
|
||||
}
|
||||
|
||||
Operations XYZ::cam(IllumObserver dio, ChromaticAdaptationType method)
|
||||
{
|
||||
return (illumobserver == dio) ? Operations()
|
||||
: Operations({ Operation(cam_(illumobserver, dio, method).t()) });
|
||||
}
|
||||
Mat XYZ::cam_(IllumObserver sio, IllumObserver dio, ChromaticAdaptationType method) const
|
||||
{
|
||||
static std::map<std::tuple<IllumObserver, IllumObserver, ChromaticAdaptationType>, Mat> cams;
|
||||
|
||||
if (sio == dio)
|
||||
{
|
||||
return Mat::eye(cv::Size(3, 3), CV_64FC1);
|
||||
}
|
||||
if (cams.count(std::make_tuple(dio, sio, method)) == 1)
|
||||
{
|
||||
return cams[std::make_tuple(dio, sio, method)];
|
||||
}
|
||||
/* @brief XYZ color space.
|
||||
* Chromatic adaption matrices.
|
||||
*/
|
||||
|
||||
static const Mat Von_Kries = (Mat_<double>(3, 3) << 0.40024, 0.7076, -0.08081, -0.2263, 1.16532, 0.0457, 0., 0., 0.91822);
|
||||
static const Mat Bradford = (Mat_<double>(3, 3) << 0.8951, 0.2664, -0.1614, -0.7502, 1.7135, 0.0367, 0.0389, -0.0685, 1.0296);
|
||||
static const std::map<ChromaticAdaptationType, std::vector<Mat>> MAs = {
|
||||
{ IDENTITY, { Mat::eye(Size(3, 3), CV_64FC1), Mat::eye(Size(3, 3), CV_64FC1) } },
|
||||
{ VON_KRIES, { Von_Kries, Von_Kries.inv() } },
|
||||
{ BRADFORD, { Bradford, Bradford.inv() } }
|
||||
};
|
||||
|
||||
// Function from http://www.brucelindbloom.com/index.html?ColorCheckerRGB.html.
|
||||
Mat XYZws = Mat(getIlluminants(dio));
|
||||
Mat XYZWd = Mat(getIlluminants(sio));
|
||||
XYZws = XYZws.reshape(1, (int)XYZws.total());
|
||||
XYZWd = XYZWd.reshape(1, (int)XYZWd.total());
|
||||
Mat MA = MAs.at(method)[0];
|
||||
Mat MA_inv = MAs.at(method)[1];
|
||||
Mat M = MA_inv * Mat::diag((MA * XYZws) / (MA * XYZWd)) * MA;
|
||||
cams[std::make_tuple(dio, sio, method)] = M;
|
||||
cams[std::make_tuple(sio, dio, method)] = M.inv();
|
||||
return M;
|
||||
}
|
||||
|
||||
std::shared_ptr<XYZ> XYZ::get(IllumObserver illumobserver)
|
||||
{
|
||||
static std::map<IllumObserver, std::shared_ptr<XYZ>> xyz_cs;
|
||||
|
||||
if (xyz_cs.count(illumobserver) == 1)
|
||||
{
|
||||
return xyz_cs[illumobserver];
|
||||
}
|
||||
std::shared_ptr<XYZ> XYZ_CS = std::make_shared<XYZ>(illumobserver);
|
||||
xyz_cs[illumobserver] = XYZ_CS;
|
||||
return xyz_cs[illumobserver];
|
||||
}
|
||||
|
||||
/* @brief Lab color space.
|
||||
*/
|
||||
Lab::Lab(IllumObserver illumobserver_)
|
||||
: ColorSpaceBase(illumobserver_, "Lab", true)
|
||||
{
|
||||
to = { Operation([this](Mat src) -> Mat { return tosrc(src); }) };
|
||||
from = { Operation([this](Mat src) -> Mat { return fromsrc(src); }) };
|
||||
}
|
||||
|
||||
Vec3d Lab::fromxyz(const Vec3d& xyz)
|
||||
{
|
||||
auto& il = getIlluminants(illumobserver);
|
||||
double x = xyz[0] / il[0],
|
||||
y = xyz[1] / il[1],
|
||||
z = xyz[2] / il[2];
|
||||
auto f = [](double t) -> double {
|
||||
return t > T0 ? std::cbrt(t) : (M * t + C);
|
||||
};
|
||||
double fx = f(x), fy = f(y), fz = f(z);
|
||||
return { 116. * fy - 16., 500 * (fx - fy), 200 * (fy - fz) };
|
||||
}
|
||||
|
||||
/* @brief Calculate From.
|
||||
* @param src the input array, type of cv::Mat.
|
||||
* @return the output array, type of cv::Mat
|
||||
*/
|
||||
Mat Lab::fromsrc(Mat& src)
|
||||
{
|
||||
return channelWise(src,
|
||||
[this](cv::Vec3d a) -> cv::Vec3d { return fromxyz(a); });
|
||||
}
|
||||
|
||||
Vec3d Lab::tolab(const Vec3d& lab)
|
||||
{
|
||||
auto f_inv = [](double t) -> double {
|
||||
return t > DELTA ? pow(t, 3.0) : (t - C) / M;
|
||||
};
|
||||
double L = (lab[0] + 16.) / 116., a = lab[1] / 500., b = lab[2] / 200.;
|
||||
auto& il = getIlluminants(illumobserver);
|
||||
return { il[0] * f_inv(L + a),
|
||||
il[1] * f_inv(L),
|
||||
il[2] * f_inv(L - b) };
|
||||
}
|
||||
|
||||
/* @brief Calculate To.
|
||||
* @param src the input array, type of cv::Mat.
|
||||
* @return the output array, type of cv::Mat
|
||||
*/
|
||||
Mat Lab::tosrc(Mat& src)
|
||||
{
|
||||
return channelWise(src,
|
||||
[this](cv::Vec3d a) -> cv::Vec3d { return tolab(a); });
|
||||
}
|
||||
|
||||
std::shared_ptr<Lab> Lab::get(IllumObserver illumobserver)
|
||||
{
|
||||
static std::map<IllumObserver, std::shared_ptr<Lab>> lab_cs;
|
||||
|
||||
if (lab_cs.count(illumobserver) == 1)
|
||||
{
|
||||
return lab_cs[illumobserver];
|
||||
}
|
||||
std::shared_ptr<Lab> Lab_CS(new Lab(illumobserver));
|
||||
lab_cs[illumobserver] = Lab_CS;
|
||||
return lab_cs[illumobserver];
|
||||
}
|
||||
|
||||
GetCS::GetCS()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
GetCS& GetCS::getInstance()
|
||||
{
|
||||
static GetCS instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::shared_ptr<RGBBase_> GetCS::getRgb(enum ColorSpace cs_name)
|
||||
{
|
||||
switch (cs_name)
|
||||
{
|
||||
case cv::ccm::COLOR_SPACE_SRGB:
|
||||
if (map_cs.find(COLOR_SPACE_SRGB) == map_cs.end())
|
||||
{
|
||||
std::shared_ptr<sRGB_> sRGB_CS(new sRGB_(false));
|
||||
std::shared_ptr<sRGB_> sRGBL_CS(new sRGB_(true));
|
||||
(*sRGB_CS).bind(*sRGBL_CS);
|
||||
map_cs[COLOR_SPACE_SRGB] = sRGB_CS;
|
||||
map_cs[COLOR_SPACE_SRGBL] = sRGBL_CS;
|
||||
}
|
||||
return std::dynamic_pointer_cast<RGBBase_>(map_cs[COLOR_SPACE_SRGB]);
|
||||
|
||||
case cv::ccm::COLOR_SPACE_ADOBE_RGB:
|
||||
if (map_cs.find(COLOR_SPACE_ADOBE_RGB) == map_cs.end())
|
||||
{
|
||||
std::shared_ptr<AdobeRGB_> AdobeRGB_CS(new AdobeRGB_(false));
|
||||
std::shared_ptr<AdobeRGB_> AdobeRGBL_CS(new AdobeRGB_(true));
|
||||
(*AdobeRGB_CS).bind(*AdobeRGBL_CS);
|
||||
map_cs[COLOR_SPACE_ADOBE_RGB] = AdobeRGB_CS;
|
||||
map_cs[COLOR_SPACE_ADOBE_RGBL] = AdobeRGBL_CS;
|
||||
}
|
||||
return std::dynamic_pointer_cast<RGBBase_>(map_cs[COLOR_SPACE_ADOBE_RGB]);
|
||||
|
||||
case cv::ccm::COLOR_SPACE_WIDE_GAMUT_RGB:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<WideGamutRGB_> WideGamutRGB_CS(new WideGamutRGB_(false));
|
||||
std::shared_ptr<WideGamutRGB_> WideGamutRGBL_CS(new WideGamutRGB_(true));
|
||||
(*WideGamutRGB_CS).bind(*WideGamutRGBL_CS);
|
||||
map_cs[COLOR_SPACE_WIDE_GAMUT_RGB] = WideGamutRGB_CS;
|
||||
map_cs[COLOR_SPACE_WIDE_GAMUT_RGBL] = WideGamutRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_PRO_PHOTO_RGB:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<ProPhotoRGB_> ProPhotoRGB_CS(new ProPhotoRGB_(false));
|
||||
std::shared_ptr<ProPhotoRGB_> ProPhotoRGBL_CS(new ProPhotoRGB_(true));
|
||||
(*ProPhotoRGB_CS).bind(*ProPhotoRGBL_CS);
|
||||
map_cs[COLOR_SPACE_PRO_PHOTO_RGB] = ProPhotoRGB_CS;
|
||||
map_cs[COLOR_SPACE_PRO_PHOTO_RGBL] = ProPhotoRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_DCI_P3_RGB:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<DCI_P3_RGB_> DCI_P3_RGB_CS(new DCI_P3_RGB_(false));
|
||||
std::shared_ptr<DCI_P3_RGB_> DCI_P3_RGBL_CS(new DCI_P3_RGB_(true));
|
||||
(*DCI_P3_RGB_CS).bind(*DCI_P3_RGBL_CS);
|
||||
map_cs[COLOR_SPACE_DCI_P3_RGB] = DCI_P3_RGB_CS;
|
||||
map_cs[COLOR_SPACE_DCI_P3_RGBL] = DCI_P3_RGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_APPLE_RGB:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<AppleRGB_> AppleRGB_CS(new AppleRGB_(false));
|
||||
std::shared_ptr<AppleRGB_> AppleRGBL_CS(new AppleRGB_(true));
|
||||
(*AppleRGB_CS).bind(*AppleRGBL_CS);
|
||||
map_cs[COLOR_SPACE_APPLE_RGB] = AppleRGB_CS;
|
||||
map_cs[COLOR_SPACE_APPLE_RGBL] = AppleRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_REC_709_RGB:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<REC_709_RGB_> REC_709_RGB_CS(new REC_709_RGB_(false));
|
||||
std::shared_ptr<REC_709_RGB_> REC_709_RGBL_CS(new REC_709_RGB_(true));
|
||||
(*REC_709_RGB_CS).bind(*REC_709_RGBL_CS);
|
||||
map_cs[COLOR_SPACE_REC_709_RGB] = REC_709_RGB_CS;
|
||||
map_cs[COLOR_SPACE_REC_709_RGBL] = REC_709_RGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_REC_2020_RGB:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<REC_2020_RGB_> REC_2020_RGB_CS(new REC_2020_RGB_(false));
|
||||
std::shared_ptr<REC_2020_RGB_> REC_2020_RGBL_CS(new REC_2020_RGB_(true));
|
||||
(*REC_2020_RGB_CS).bind(*REC_2020_RGBL_CS);
|
||||
map_cs[COLOR_SPACE_REC_2020_RGB] = REC_2020_RGB_CS;
|
||||
map_cs[COLOR_SPACE_REC_2020_RGBL] = REC_2020_RGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_SRGBL:
|
||||
case cv::ccm::COLOR_SPACE_ADOBE_RGBL:
|
||||
case cv::ccm::COLOR_SPACE_WIDE_GAMUT_RGBL:
|
||||
case cv::ccm::COLOR_SPACE_PRO_PHOTO_RGBL:
|
||||
case cv::ccm::COLOR_SPACE_DCI_P3_RGBL:
|
||||
case cv::ccm::COLOR_SPACE_APPLE_RGBL:
|
||||
case cv::ccm::COLOR_SPACE_REC_709_RGBL:
|
||||
case cv::ccm::COLOR_SPACE_REC_2020_RGBL:
|
||||
CV_Error(Error::StsBadArg, "linear RGB colorspaces are not supported, you should assigned as normal RGB color space");
|
||||
break;
|
||||
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Only RGB color spaces are supported");
|
||||
}
|
||||
return (std::dynamic_pointer_cast<RGBBase_>)(map_cs[cs_name]);
|
||||
}
|
||||
|
||||
std::shared_ptr<ColorSpaceBase> GetCS::getCS(enum ColorSpace cs_name)
|
||||
{
|
||||
switch (cs_name)
|
||||
{
|
||||
case cv::ccm::COLOR_SPACE_SRGB:
|
||||
case cv::ccm::COLOR_SPACE_SRGBL:
|
||||
if (map_cs.find(COLOR_SPACE_SRGB) == map_cs.end())
|
||||
{
|
||||
std::shared_ptr<sRGB_> sRGB_CS(new sRGB_(false));
|
||||
std::shared_ptr<sRGB_> sRGBL_CS(new sRGB_(true));
|
||||
(*sRGB_CS).bind(*sRGBL_CS);
|
||||
map_cs[COLOR_SPACE_SRGB] = sRGB_CS;
|
||||
map_cs[COLOR_SPACE_SRGBL] = sRGBL_CS;
|
||||
}
|
||||
return map_cs[cs_name];
|
||||
|
||||
case cv::ccm::COLOR_SPACE_ADOBE_RGB:
|
||||
case cv::ccm::COLOR_SPACE_ADOBE_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<AdobeRGB_> AdobeRGB_CS(new AdobeRGB_(false));
|
||||
std::shared_ptr<AdobeRGB_> AdobeRGBL_CS(new AdobeRGB_(true));
|
||||
(*AdobeRGB_CS).bind(*AdobeRGBL_CS);
|
||||
map_cs[COLOR_SPACE_ADOBE_RGB] = AdobeRGB_CS;
|
||||
map_cs[COLOR_SPACE_ADOBE_RGBL] = AdobeRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_WIDE_GAMUT_RGB:
|
||||
case cv::ccm::COLOR_SPACE_WIDE_GAMUT_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<WideGamutRGB_> WideGamutRGB_CS(new WideGamutRGB_(false));
|
||||
std::shared_ptr<WideGamutRGB_> WideGamutRGBL_CS(new WideGamutRGB_(true));
|
||||
(*WideGamutRGB_CS).bind(*WideGamutRGBL_CS);
|
||||
map_cs[COLOR_SPACE_WIDE_GAMUT_RGB] = WideGamutRGB_CS;
|
||||
map_cs[COLOR_SPACE_WIDE_GAMUT_RGBL] = WideGamutRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_PRO_PHOTO_RGB:
|
||||
case cv::ccm::COLOR_SPACE_PRO_PHOTO_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<ProPhotoRGB_> ProPhotoRGB_CS(new ProPhotoRGB_(false));
|
||||
std::shared_ptr<ProPhotoRGB_> ProPhotoRGBL_CS(new ProPhotoRGB_(true));
|
||||
(*ProPhotoRGB_CS).bind(*ProPhotoRGBL_CS);
|
||||
map_cs[COLOR_SPACE_PRO_PHOTO_RGB] = ProPhotoRGB_CS;
|
||||
map_cs[COLOR_SPACE_PRO_PHOTO_RGBL] = ProPhotoRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_DCI_P3_RGB:
|
||||
case cv::ccm::COLOR_SPACE_DCI_P3_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<DCI_P3_RGB_> DCI_P3_RGB_CS(new DCI_P3_RGB_(false));
|
||||
std::shared_ptr<DCI_P3_RGB_> DCI_P3_RGBL_CS(new DCI_P3_RGB_(true));
|
||||
(*DCI_P3_RGB_CS).bind(*DCI_P3_RGBL_CS);
|
||||
map_cs[COLOR_SPACE_DCI_P3_RGB] = DCI_P3_RGB_CS;
|
||||
map_cs[COLOR_SPACE_DCI_P3_RGBL] = DCI_P3_RGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_APPLE_RGB:
|
||||
case cv::ccm::COLOR_SPACE_APPLE_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<AppleRGB_> AppleRGB_CS(new AppleRGB_(false));
|
||||
std::shared_ptr<AppleRGB_> AppleRGBL_CS(new AppleRGB_(true));
|
||||
(*AppleRGB_CS).bind(*AppleRGBL_CS);
|
||||
map_cs[COLOR_SPACE_APPLE_RGB] = AppleRGB_CS;
|
||||
map_cs[COLOR_SPACE_APPLE_RGBL] = AppleRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_REC_709_RGB:
|
||||
case cv::ccm::COLOR_SPACE_REC_709_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<REC_709_RGB_> REC_709_RGB_CS(new REC_709_RGB_(false));
|
||||
std::shared_ptr<REC_709_RGB_> REC_709_RGBL_CS(new REC_709_RGB_(true));
|
||||
(*REC_709_RGB_CS).bind(*REC_709_RGBL_CS);
|
||||
map_cs[COLOR_SPACE_REC_709_RGB] = REC_709_RGB_CS;
|
||||
map_cs[COLOR_SPACE_REC_709_RGBL] = REC_709_RGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_REC_2020_RGB:
|
||||
case cv::ccm::COLOR_SPACE_REC_2020_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<REC_2020_RGB_> REC_2020_RGB_CS(new REC_2020_RGB_(false));
|
||||
std::shared_ptr<REC_2020_RGB_> REC_2020_RGBL_CS(new REC_2020_RGB_(true));
|
||||
(*REC_2020_RGB_CS).bind(*REC_2020_RGBL_CS);
|
||||
map_cs[COLOR_SPACE_REC_2020_RGB] = REC_2020_RGB_CS;
|
||||
map_cs[COLOR_SPACE_REC_2020_RGBL] = REC_2020_RGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D65_2:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D65_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D50_2:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D50_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D65_10:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D65_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D50_10:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D50_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_A_2:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(A_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_A_10:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(A_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D55_2:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D55_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D55_10:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D55_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D75_2:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D75_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D75_10:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D75_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_E_2:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(E_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_E_10:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(E_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D65_2:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D65_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D50_2:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D50_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D65_10:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D65_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D50_10:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D50_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_A_2:
|
||||
return Lab::get(IllumObserver::getIllumObservers(A_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_A_10:
|
||||
return Lab::get(IllumObserver::getIllumObservers(A_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D55_2:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D55_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D55_10:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D55_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D75_2:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D75_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D75_10:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D75_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_E_2:
|
||||
return Lab::get(IllumObserver::getIllumObservers(E_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_E_10:
|
||||
return Lab::get(IllumObserver::getIllumObservers(E_10));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return map_cs[cs_name];
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
@@ -0,0 +1,343 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_COLORSPACE_HPP__
|
||||
#define __OPENCV_CCM_COLORSPACE_HPP__
|
||||
|
||||
#include "operations.hpp"
|
||||
#include "illumobserver.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
/** @brief Basic class for ColorSpace.
|
||||
*/
|
||||
class ColorSpaceBase
|
||||
{
|
||||
public:
|
||||
typedef std::function<Mat(Mat)> MatFunc;
|
||||
IllumObserver illumobserver;
|
||||
std::string type;
|
||||
bool linear;
|
||||
Operations to;
|
||||
Operations from;
|
||||
ColorSpaceBase* l;
|
||||
ColorSpaceBase* nl;
|
||||
|
||||
ColorSpaceBase() {};
|
||||
|
||||
ColorSpaceBase(IllumObserver illumobserver_, std::string type_, bool linear_)
|
||||
: illumobserver(illumobserver_)
|
||||
, type(type_)
|
||||
, linear(linear_) {};
|
||||
|
||||
virtual ~ColorSpaceBase()
|
||||
{
|
||||
l = 0;
|
||||
nl = 0;
|
||||
};
|
||||
virtual bool relate(const ColorSpaceBase& other) const;
|
||||
|
||||
virtual Operations relation(const ColorSpaceBase& /*other*/) const;
|
||||
|
||||
bool operator<(const ColorSpaceBase& other) const;
|
||||
};
|
||||
|
||||
/** @brief Base of RGB color space;
|
||||
the argument values are from AdobeRGB;
|
||||
Data from https://en.wikipedia.org/wiki/Adobe_RGB_color_space
|
||||
*/
|
||||
|
||||
class RGBBase_ : public ColorSpaceBase
|
||||
{
|
||||
public:
|
||||
// primaries
|
||||
double xr;
|
||||
double yr;
|
||||
double xg;
|
||||
double yg;
|
||||
double xb;
|
||||
double yb;
|
||||
Mat M_to;
|
||||
Mat M_from;
|
||||
|
||||
using ColorSpaceBase::ColorSpaceBase;
|
||||
|
||||
/** @brief There are 3 kinds of relationships for RGB:
|
||||
1. Different types; - no operation
|
||||
1. Same type, same linear; - copy
|
||||
2. Same type, different linear, self is nonlinear; - 2 toL
|
||||
3. Same type, different linear, self is linear - 3 fromL
|
||||
@param other type of ColorSpaceBase.
|
||||
@return Operations.
|
||||
*/
|
||||
Operations relation(const ColorSpaceBase& other) const CV_OVERRIDE;
|
||||
|
||||
/** @brief Initial operations.
|
||||
*/
|
||||
void init();
|
||||
/** @brief Produce color space instance with linear and non-linear versions.
|
||||
@param rgbl type of RGBBase_.
|
||||
*/
|
||||
void bind(RGBBase_& rgbl);
|
||||
|
||||
virtual Mat toLFunc(Mat& /*rgb*/) const;
|
||||
|
||||
virtual Mat fromLFunc(Mat& /*rgbl*/, Mat dst=Mat()) const;
|
||||
private:
|
||||
virtual void setParameter() {};
|
||||
|
||||
/** @brief Calculation of M_RGBL2XYZ_base.
|
||||
*/
|
||||
virtual void calM();
|
||||
|
||||
/** @brief operations to or from XYZ.
|
||||
*/
|
||||
virtual void calOperations();
|
||||
|
||||
virtual void calLinear() {};
|
||||
};
|
||||
|
||||
/** @brief Base of Adobe RGB color space;
|
||||
*/
|
||||
class AdobeRGBBase_ : public RGBBase_
|
||||
|
||||
{
|
||||
public:
|
||||
using RGBBase_::RGBBase_;
|
||||
double gamma;
|
||||
|
||||
private:
|
||||
Mat toLFunc(Mat& rgb) const CV_OVERRIDE;
|
||||
Mat fromLFunc(Mat& rgbl, Mat dst=Mat()) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief Base of sRGB color space;
|
||||
*/
|
||||
class sRGBBase_ : public RGBBase_
|
||||
|
||||
{
|
||||
public:
|
||||
using RGBBase_::RGBBase_;
|
||||
double a;
|
||||
double gamma;
|
||||
double alpha;
|
||||
double beta;
|
||||
double phi;
|
||||
double K0;
|
||||
|
||||
private:
|
||||
/** @brief linearization parameters
|
||||
*/
|
||||
virtual void calLinear() CV_OVERRIDE;
|
||||
/** @brief Used by toLFunc.
|
||||
*/
|
||||
double toLFuncEW(double x) const;
|
||||
|
||||
/** @brief Linearization.
|
||||
@param rgb the input array, type of cv::Mat.
|
||||
@return the output array, type of cv::Mat.
|
||||
*/
|
||||
Mat toLFunc(Mat& rgb) const CV_OVERRIDE;
|
||||
|
||||
/** @brief Used by fromLFunc.
|
||||
*/
|
||||
double fromLFuncEW(double x) const;
|
||||
|
||||
/** @brief Delinearization.
|
||||
@param rgbl the input array, type of cv::Mat.
|
||||
@return the output array, type of cv::Mat.
|
||||
*/
|
||||
Mat fromLFunc(Mat& rgbl, Mat dst=Mat()) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief sRGB color space.
|
||||
data from https://en.wikipedia.org/wiki/SRGB.
|
||||
*/
|
||||
class sRGB_ : public sRGBBase_
|
||||
|
||||
{
|
||||
public:
|
||||
sRGB_(bool linear_)
|
||||
: sRGBBase_(IllumObserver::getIllumObservers(D65_2), "sRGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief Adobe RGB color space.
|
||||
*/
|
||||
class AdobeRGB_ : public AdobeRGBBase_
|
||||
{
|
||||
public:
|
||||
AdobeRGB_(bool linear_ = false)
|
||||
: AdobeRGBBase_(IllumObserver::getIllumObservers(D65_2), "AdobeRGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief Wide-gamut RGB color space.
|
||||
data from https://en.wikipedia.org/wiki/Wide-gamut_RGB_color_space.
|
||||
*/
|
||||
class WideGamutRGB_ : public AdobeRGBBase_
|
||||
{
|
||||
public:
|
||||
WideGamutRGB_(bool linear_ = false)
|
||||
: AdobeRGBBase_(IllumObserver::getIllumObservers(D50_2), "WideGamutRGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief ProPhoto RGB color space.
|
||||
data from https://en.wikipedia.org/wiki/ProPhoto_RGB_color_space.
|
||||
*/
|
||||
|
||||
class ProPhotoRGB_ : public AdobeRGBBase_
|
||||
{
|
||||
public:
|
||||
ProPhotoRGB_(bool linear_ = false)
|
||||
: AdobeRGBBase_(IllumObserver::getIllumObservers(D50_2), "ProPhotoRGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief DCI-P3 RGB color space.
|
||||
data from https://en.wikipedia.org/wiki/DCI-P3.
|
||||
*/
|
||||
class DCI_P3_RGB_ : public AdobeRGBBase_
|
||||
{
|
||||
public:
|
||||
DCI_P3_RGB_(bool linear_ = false)
|
||||
: AdobeRGBBase_(IllumObserver::getIllumObservers(D65_2), "DCI_P3_RGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief Apple RGB color space.
|
||||
data from http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html.
|
||||
*/
|
||||
class AppleRGB_ : public AdobeRGBBase_
|
||||
{
|
||||
public:
|
||||
AppleRGB_(bool linear_ = false)
|
||||
: AdobeRGBBase_(IllumObserver::getIllumObservers(D65_2), "AppleRGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief REC_709 RGB color space.
|
||||
data from https://en.wikipedia.org/wiki/Rec._709.
|
||||
*/
|
||||
class REC_709_RGB_ : public sRGBBase_
|
||||
{
|
||||
public:
|
||||
REC_709_RGB_(bool linear_)
|
||||
: sRGBBase_(IllumObserver::getIllumObservers(D65_2), "REC_709_RGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief REC_2020 RGB color space.
|
||||
data from https://en.wikipedia.org/wiki/Rec._2020.
|
||||
*/
|
||||
class REC_2020_RGB_ : public sRGBBase_
|
||||
{
|
||||
public:
|
||||
REC_2020_RGB_(bool linear_)
|
||||
: sRGBBase_(IllumObserver::getIllumObservers(D65_2), "REC_2020_RGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief Enum of the possible types of Chromatic Adaptation Models.
|
||||
*/
|
||||
enum ChromaticAdaptationType
|
||||
{
|
||||
IDENTITY,
|
||||
VON_KRIES,
|
||||
BRADFORD
|
||||
};
|
||||
|
||||
|
||||
/** @brief XYZ color space.
|
||||
Chromatic adaption matrices.
|
||||
*/
|
||||
class XYZ : public ColorSpaceBase
|
||||
{
|
||||
public:
|
||||
XYZ(IllumObserver illumobserver_)
|
||||
: ColorSpaceBase(illumobserver_, "XYZ", true) {};
|
||||
Operations cam(IllumObserver dio, ChromaticAdaptationType method = BRADFORD);
|
||||
static std::shared_ptr<XYZ> get(IllumObserver illumobserver);
|
||||
|
||||
private:
|
||||
/** @brief Get cam.
|
||||
@param sio the input IllumObserver of src.
|
||||
@param dio the input IllumObserver of dst.
|
||||
@param method type of Chromatic Adaptation Model.
|
||||
@return the output array, type of cv::Mat.
|
||||
*/
|
||||
Mat cam_(IllumObserver sio, IllumObserver dio, ChromaticAdaptationType method = BRADFORD) const;
|
||||
};
|
||||
|
||||
/** @brief Lab color space.
|
||||
*/
|
||||
class Lab : public ColorSpaceBase
|
||||
{
|
||||
public:
|
||||
Lab(IllumObserver illumobserver_);
|
||||
static std::shared_ptr<Lab> get(IllumObserver illumobserver);
|
||||
|
||||
private:
|
||||
static constexpr double DELTA = (6. / 29.);
|
||||
static constexpr double M = 1. / (3. * DELTA * DELTA);
|
||||
static constexpr double T0 = DELTA * DELTA * DELTA;
|
||||
static constexpr double C = 4. / 29.;
|
||||
|
||||
Vec3d fromxyz(const Vec3d& xyz);
|
||||
|
||||
/** @brief Calculate From.
|
||||
@param src the input array, type of cv::Mat.
|
||||
@return the output array, type of cv::Mat
|
||||
*/
|
||||
Mat fromsrc(Mat& src);
|
||||
|
||||
Vec3d tolab(const Vec3d& lab);
|
||||
|
||||
/** @brief Calculate To.
|
||||
@param src the input array, type of cv::Mat.
|
||||
@return the output array, type of cv::Mat
|
||||
*/
|
||||
Mat tosrc(Mat& src);
|
||||
};
|
||||
|
||||
class GetCS
|
||||
{
|
||||
protected:
|
||||
std::map<enum ColorSpace, std::shared_ptr<ColorSpaceBase>> map_cs;
|
||||
|
||||
GetCS(); // singleton, use getInstance()
|
||||
public:
|
||||
static GetCS& getInstance();
|
||||
|
||||
std::shared_ptr<RGBBase_> getRgb(enum ColorSpace cs_name);
|
||||
std::shared_ptr<ColorSpaceBase> getCS(enum ColorSpace cs_name);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,204 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "distance.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
double deltaCIE76(const Vec3d& lab1, const Vec3d& lab2) { return norm(lab1 - lab2); };
|
||||
|
||||
double deltaCIE94(const Vec3d& lab1, const Vec3d& lab2, double kH,
|
||||
double kC, double kL, double k1, double k2)
|
||||
{
|
||||
double dl = lab1[0] - lab2[0];
|
||||
double c1 = sqrt(pow(lab1[1], 2) + pow(lab1[2], 2));
|
||||
double c2 = sqrt(pow(lab2[1], 2) + pow(lab2[2], 2));
|
||||
double dc = c1 - c2;
|
||||
double da = lab1[1] - lab2[1];
|
||||
double db = lab1[2] - lab2[2];
|
||||
double dh = pow(da, 2) + pow(db, 2) - pow(dc, 2);
|
||||
double sc = 1.0 + k1 * c1;
|
||||
double sh = 1.0 + k2 * c1;
|
||||
double sl = 1.0;
|
||||
double res = pow(dl / (kL * sl), 2) + pow(dc / (kC * sc), 2) + dh / pow(kH * sh, 2);
|
||||
|
||||
return res > 0 ? sqrt(res) : 0;
|
||||
}
|
||||
|
||||
double deltaCIE94GraphicArts(const Vec3d& lab1, const Vec3d& lab2)
|
||||
{
|
||||
return deltaCIE94(lab1, lab2);
|
||||
}
|
||||
|
||||
double toRad(double degree) { return degree / 180 * CV_PI; };
|
||||
|
||||
double deltaCIE94Textiles(const Vec3d& lab1, const Vec3d& lab2)
|
||||
{
|
||||
return deltaCIE94(lab1, lab2, 1.0, 1.0, 2.0, 0.048, 0.014);
|
||||
}
|
||||
|
||||
double deltaCIEDE2000_(const Vec3d& lab1, const Vec3d& lab2, double kL,
|
||||
double kC, double kH)
|
||||
{
|
||||
double deltaLApo = lab2[0] - lab1[0];
|
||||
double lBarApo = (lab1[0] + lab2[0]) / 2.0;
|
||||
double C1 = sqrt(pow(lab1[1], 2) + pow(lab1[2], 2));
|
||||
double C2 = sqrt(pow(lab2[1], 2) + pow(lab2[2], 2));
|
||||
double cBar = (C1 + C2) / 2.0;
|
||||
double G = sqrt(pow(cBar, 7) / (pow(cBar, 7) + pow(25, 7)));
|
||||
double a1Apo = lab1[1] + lab1[1] / 2.0 * (1.0 - G);
|
||||
double a2Apo = lab2[1] + lab2[1] / 2.0 * (1.0 - G);
|
||||
double c1Apo = sqrt(pow(a1Apo, 2) + pow(lab1[2], 2));
|
||||
double c2Apo = sqrt(pow(a2Apo, 2) + pow(lab2[2], 2));
|
||||
double cBarApo = (c1Apo + c2Apo) / 2.0;
|
||||
double deltaCApo = c2Apo - c1Apo;
|
||||
|
||||
double h1Apo;
|
||||
if (c1Apo == 0)
|
||||
{
|
||||
h1Apo = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
h1Apo = atan2(lab1[2], a1Apo);
|
||||
if (h1Apo < 0.0)
|
||||
h1Apo += 2. * CV_PI;
|
||||
}
|
||||
|
||||
double h2Apo;
|
||||
if (c2Apo == 0)
|
||||
{
|
||||
h2Apo = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
h2Apo = atan2(lab2[2], a2Apo);
|
||||
if (h2Apo < 0.0)
|
||||
h2Apo += 2. * CV_PI;
|
||||
}
|
||||
|
||||
double deltaHApo;
|
||||
if (abs(h2Apo - h1Apo) <= CV_PI)
|
||||
{
|
||||
deltaHApo = h2Apo - h1Apo;
|
||||
}
|
||||
else if (h2Apo <= h1Apo)
|
||||
{
|
||||
deltaHApo = h2Apo - h1Apo + 2. * CV_PI;
|
||||
}
|
||||
else
|
||||
{
|
||||
deltaHApo = h2Apo - h1Apo - 2. * CV_PI;
|
||||
}
|
||||
|
||||
double hBarApo;
|
||||
if (c1Apo == 0 || c2Apo == 0)
|
||||
{
|
||||
hBarApo = h1Apo + h2Apo;
|
||||
}
|
||||
else if (abs(h1Apo - h2Apo) <= CV_PI)
|
||||
{
|
||||
hBarApo = (h1Apo + h2Apo) / 2.0;
|
||||
}
|
||||
else if (h1Apo + h2Apo < 2. * CV_PI)
|
||||
{
|
||||
hBarApo = (h1Apo + h2Apo + 2. * CV_PI) / 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
hBarApo = (h1Apo + h2Apo - 2. * CV_PI) / 2.0;
|
||||
}
|
||||
|
||||
double deltaH_Apo = 2.0 * sqrt(c1Apo * c2Apo) * sin(deltaHApo / 2.0);
|
||||
double T = 1.0 - 0.17 * cos(hBarApo - toRad(30.)) + 0.24 * cos(2.0 * hBarApo) + 0.32 * cos(3.0 * hBarApo + toRad(6.0)) - 0.2 * cos(4.0 * hBarApo - toRad(63.0));
|
||||
double sC = 1.0 + 0.045 * cBarApo;
|
||||
double sH = 1.0 + 0.015 * cBarApo * T;
|
||||
double sL = 1.0 + ((0.015 * pow(lBarApo - 50.0, 2.0)) / sqrt(20.0 + pow(lBarApo - 50.0, 2.0)));
|
||||
double rC = 2.0 * sqrt(pow(cBarApo, 7.0) / (pow(cBarApo, 7.0) + pow(25, 7)));
|
||||
double rT = -sin(toRad(60.0) * exp(-pow((hBarApo - toRad(275.0)) / toRad(25.0), 2.0))) * rC;
|
||||
double res = (pow(deltaLApo / (kL * sL), 2.0) + pow(deltaCApo / (kC * sC), 2.0) + pow(deltaH_Apo / (kH * sH), 2.0) + rT * (deltaCApo / (kC * sC)) * (deltaH_Apo / (kH * sH)));
|
||||
return res > 0 ? sqrt(res) : 0;
|
||||
}
|
||||
|
||||
double deltaCIEDE2000(const Vec3d& lab1, const Vec3d& lab2)
|
||||
{
|
||||
return deltaCIEDE2000_(lab1, lab2);
|
||||
}
|
||||
|
||||
double deltaCMC(const Vec3d& lab1, const Vec3d& lab2, double kL, double kC)
|
||||
{
|
||||
double dL = lab2[0] - lab1[0];
|
||||
double da = lab2[1] - lab1[1];
|
||||
double db = lab2[2] - lab1[2];
|
||||
double C1 = sqrt(pow(lab1[1], 2.0) + pow(lab1[2], 2.0));
|
||||
double C2 = sqrt(pow(lab2[1], 2.0) + pow(lab2[2], 2.0));
|
||||
double dC = C2 - C1;
|
||||
double dH = sqrt(pow(da, 2) + pow(db, 2) - pow(dC, 2));
|
||||
|
||||
double H1;
|
||||
if (C1 == 0.)
|
||||
{
|
||||
H1 = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
H1 = atan2(lab1[2], lab1[1]);
|
||||
if (H1 < 0.0)
|
||||
H1 += 2. * CV_PI;
|
||||
}
|
||||
|
||||
double F = pow(C1, 2) / sqrt(pow(C1, 4) + 1900);
|
||||
double T = (H1 > toRad(164) && H1 <= toRad(345))
|
||||
? 0.56 + abs(0.2 * cos(H1 + toRad(168)))
|
||||
: 0.36 + abs(0.4 * cos(H1 + toRad(35)));
|
||||
double sL = lab1[0] < 16. ? 0.511 : (0.040975 * lab1[0]) / (1.0 + 0.01765 * lab1[0]);
|
||||
double sC = (0.0638 * C1) / (1.0 + 0.0131 * C1) + 0.638;
|
||||
double sH = sC * (F * T + 1.0 - F);
|
||||
|
||||
return sqrt(pow(dL / (kL * sL), 2.0) + pow(dC / (kC * sC), 2.0) + pow(dH / sH, 2.0));
|
||||
}
|
||||
|
||||
double deltaCMC1To1(const Vec3d& lab1, const Vec3d& lab2)
|
||||
{
|
||||
return deltaCMC(lab1, lab2);
|
||||
}
|
||||
|
||||
double deltaCMC2To1(const Vec3d& lab1, const Vec3d& lab2)
|
||||
{
|
||||
return deltaCMC(lab1, lab2, 2, 1);
|
||||
}
|
||||
|
||||
Mat distance(Mat src, Mat ref, DistanceType distanceType)
|
||||
{
|
||||
switch (distanceType)
|
||||
{
|
||||
case cv::ccm::DISTANCE_CIE76:
|
||||
return distanceWise(src, ref, deltaCIE76);
|
||||
case cv::ccm::DISTANCE_CIE94_GRAPHIC_ARTS:
|
||||
return distanceWise(src, ref, deltaCIE94GraphicArts);
|
||||
case cv::ccm::DISTANCE_CIE94_TEXTILES:
|
||||
return distanceWise(src, ref, deltaCIE94Textiles);
|
||||
case cv::ccm::DISTANCE_CIE2000:
|
||||
return distanceWise(src, ref, deltaCIEDE2000);
|
||||
case cv::ccm::DISTANCE_CMC_1TO1:
|
||||
return distanceWise(src, ref, deltaCMC1To1);
|
||||
case cv::ccm::DISTANCE_CMC_2TO1:
|
||||
return distanceWise(src, ref, deltaCMC2To1);
|
||||
case cv::ccm::DISTANCE_RGB:
|
||||
return distanceWise(src, ref, deltaCIE76);
|
||||
case cv::ccm::DISTANCE_RGBL:
|
||||
return distanceWise(src, ref, deltaCIE76);
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Wrong distanceType!" );
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace ccm
|
||||
@@ -0,0 +1,80 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_DISTANCE_HPP__
|
||||
#define __OPENCV_CCM_DISTANCE_HPP__
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
/** possibale functions to calculate the distance between
|
||||
colors.see https://en.wikipedia.org/wiki/Color_difference for details;*/
|
||||
|
||||
/** @brief distance between two points in formula CIE76
|
||||
@param lab1 a 3D vector
|
||||
@param lab2 a 3D vector
|
||||
@return distance between lab1 and lab2
|
||||
*/
|
||||
|
||||
double deltaCIE76(const Vec3d& lab1, const Vec3d& lab2);
|
||||
|
||||
/** @brief distance between two points in formula CIE94
|
||||
@param lab1 a 3D vector
|
||||
@param lab2 a 3D vector
|
||||
@param kH Hue scale
|
||||
@param kC Chroma scale
|
||||
@param kL Lightness scale
|
||||
@param k1 first scale parameter
|
||||
@param k2 second scale parameter
|
||||
@return distance between lab1 and lab2
|
||||
*/
|
||||
|
||||
double deltaCIE94(const Vec3d& lab1, const Vec3d& lab2, double kH = 1.0,
|
||||
double kC = 1.0, double kL = 1.0, double k1 = 0.045,
|
||||
double k2 = 0.015);
|
||||
|
||||
double deltaCIE94GraphicArts(const Vec3d& lab1, const Vec3d& lab2);
|
||||
|
||||
double toRad(double degree);
|
||||
|
||||
double deltaCIE94Textiles(const Vec3d& lab1, const Vec3d& lab2);
|
||||
|
||||
/** @brief distance between two points in formula CIE2000
|
||||
@param lab1 a 3D vector
|
||||
@param lab2 a 3D vector
|
||||
@param kL Lightness scale
|
||||
@param kC Chroma scale
|
||||
@param kH Hue scale
|
||||
@return distance between lab1 and lab2
|
||||
*/
|
||||
double deltaCIEDE2000_(const Vec3d& lab1, const Vec3d& lab2, double kL = 1.0,
|
||||
double kC = 1.0, double kH = 1.0);
|
||||
double deltaCIEDE2000(const Vec3d& lab1, const Vec3d& lab2);
|
||||
|
||||
/** @brief distance between two points in formula CMC
|
||||
@param lab1 a 3D vector
|
||||
@param lab2 a 3D vector
|
||||
@param kL Lightness scale
|
||||
@param kC Chroma scale
|
||||
@return distance between lab1 and lab2
|
||||
*/
|
||||
|
||||
double deltaCMC(const Vec3d& lab1, const Vec3d& lab2, double kL = 1, double kC = 1);
|
||||
|
||||
double deltaCMC1To1(const Vec3d& lab1, const Vec3d& lab2);
|
||||
|
||||
double deltaCMC2To1(const Vec3d& lab1, const Vec3d& lab2);
|
||||
|
||||
Mat distance(Mat src,Mat ref, DistanceType distanceType);
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,114 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "illumobserver.hpp"
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
IllumObserver::IllumObserver(std::string illuminant_, std::string observer_)
|
||||
: illuminant(illuminant_)
|
||||
, observer(observer_) {};
|
||||
|
||||
bool IllumObserver::operator<(const IllumObserver& other) const
|
||||
{
|
||||
return (illuminant < other.illuminant || ((illuminant == other.illuminant) && (observer < other.observer)));
|
||||
}
|
||||
|
||||
bool IllumObserver::operator==(const IllumObserver& other) const
|
||||
{
|
||||
return illuminant == other.illuminant && observer == other.observer;
|
||||
};
|
||||
|
||||
IllumObserver IllumObserver::getIllumObservers(IllumObserverType illumobserver)
|
||||
{
|
||||
switch (illumobserver)
|
||||
{
|
||||
case cv::ccm::A_2:
|
||||
{
|
||||
IllumObserver A_2_IllumObserver("A", "2");
|
||||
return A_2_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::A_10:
|
||||
{
|
||||
IllumObserver A_1O_IllumObserver("A", "10");
|
||||
return A_1O_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D50_2:
|
||||
{
|
||||
IllumObserver D50_2_IllumObserver("D50", "2");
|
||||
return D50_2_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D50_10:
|
||||
{
|
||||
IllumObserver D50_10_IllumObserver("D50", "10");
|
||||
return D50_10_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D55_2:
|
||||
{
|
||||
IllumObserver D55_2_IllumObserver("D55", "2");
|
||||
return D55_2_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D55_10:
|
||||
{
|
||||
IllumObserver D55_10_IllumObserver("D55", "10");
|
||||
return D55_10_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D65_2:
|
||||
{
|
||||
IllumObserver D65_2_IllumObserver("D65", "2");
|
||||
return D65_2_IllumObserver;
|
||||
}
|
||||
case cv::ccm::D65_10:
|
||||
{
|
||||
IllumObserver D65_10_IllumObserver("D65", "10");
|
||||
return D65_10_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D75_2:
|
||||
{
|
||||
IllumObserver D75_2_IllumObserver("D75", "2");
|
||||
return D75_2_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D75_10:
|
||||
{
|
||||
IllumObserver D75_10_IllumObserver("D75", "10");
|
||||
return D75_10_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::E_2:
|
||||
{
|
||||
IllumObserver E_2_IllumObserver("E", "2");
|
||||
return E_2_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::E_10:
|
||||
{
|
||||
IllumObserver E_10_IllumObserver("E", "10");
|
||||
return E_10_IllumObserver;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return IllumObserver();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// data from https://en.wikipedia.org/wiki/Standard_illuminant.
|
||||
std::vector<double> xyY2XYZ(const std::vector<double>& xyY)
|
||||
{
|
||||
double Y = xyY.size() >= 3 ? xyY[2] : 1;
|
||||
return { Y * xyY[0] / xyY[1], Y, Y / xyY[1] * (1 - xyY[0] - xyY[1]) };
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
@@ -0,0 +1,53 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_IllumObserver_HPP__
|
||||
#define __OPENCV_CCM_IllumObserver_HPP__
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <map>
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
enum IllumObserverType
|
||||
{
|
||||
A_2,
|
||||
A_10,
|
||||
D50_2,
|
||||
D50_10,
|
||||
D55_2,
|
||||
D55_10,
|
||||
D65_2,
|
||||
D65_10,
|
||||
D75_2,
|
||||
D75_10,
|
||||
E_2,
|
||||
E_10
|
||||
};
|
||||
|
||||
/** @brief IllumObserver is the meaning of illuminant and observer. See notes of ccm.hpp
|
||||
for supported list for illuminant and observer*/
|
||||
class IllumObserver
|
||||
{
|
||||
public:
|
||||
std::string illuminant;
|
||||
std::string observer;
|
||||
IllumObserver() {};
|
||||
IllumObserver(std::string illuminant, std::string observer);
|
||||
virtual ~IllumObserver() {};
|
||||
bool operator<(const IllumObserver& other) const;
|
||||
bool operator==(const IllumObserver& other) const;
|
||||
static IllumObserver getIllumObservers(IllumObserverType illumobserver);
|
||||
};
|
||||
std::vector<double> xyY2XYZ(const std::vector<double>& xyY);
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,284 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "linearize.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
Polyfit::Polyfit() : deg(0) {}
|
||||
|
||||
void Polyfit::write(cv::FileStorage& fs) const {
|
||||
fs << "{" << "deg" << deg << "p" << p << "}";
|
||||
}
|
||||
|
||||
void Polyfit::read(const cv::FileNode& node) {
|
||||
node["deg"] >> deg;
|
||||
node["p"] >> p;
|
||||
}
|
||||
|
||||
// Global functions to support FileStorage for Polyfit
|
||||
void write(cv::FileStorage& fs, const std::string&, const Polyfit& polyfit) {
|
||||
polyfit.write(fs);
|
||||
}
|
||||
void read(const cv::FileNode& node, Polyfit& polyfit, const Polyfit& defaultValue) {
|
||||
if(node.empty())
|
||||
polyfit = defaultValue;
|
||||
else
|
||||
polyfit.read(node);
|
||||
}
|
||||
|
||||
Polyfit::Polyfit(Mat x, Mat y, int deg_)
|
||||
: deg(deg_)
|
||||
{
|
||||
int n = x.cols * x.rows * x.channels();
|
||||
x = x.reshape(1, n);
|
||||
y = y.reshape(1, n);
|
||||
Mat_<double> A = Mat_<double>::ones(n, deg + 1);
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
for (int j = 1; j < A.cols; ++j)
|
||||
{
|
||||
A.at<double>(i, j) = x.at<double>(i) * A.at<double>(i, j - 1);
|
||||
}
|
||||
}
|
||||
Mat y_(y);
|
||||
cv::solve(A, y_, p, DECOMP_SVD);
|
||||
}
|
||||
|
||||
Mat Polyfit::operator()(const Mat& inp)
|
||||
{
|
||||
return elementWise(inp, [this](double x) -> double { return fromEW(x); });
|
||||
};
|
||||
|
||||
double Polyfit::fromEW(double x)
|
||||
{
|
||||
double res = 0;
|
||||
for (int d = 0; d <= deg; ++d)
|
||||
{
|
||||
res += pow(x, d) * p.at<double>(d, 0);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
// Default constructor for LogPolyfit
|
||||
LogPolyfit::LogPolyfit() : deg(0) {}
|
||||
|
||||
void LogPolyfit::write(cv::FileStorage& fs) const {
|
||||
fs << "{" << "deg" << deg << "p" << p << "}";
|
||||
}
|
||||
|
||||
void LogPolyfit::read(const cv::FileNode& node) {
|
||||
node["deg"] >> deg;
|
||||
node["p"] >> p;
|
||||
}
|
||||
|
||||
// Global functions to support FileStorage for LogPolyfit
|
||||
void write(cv::FileStorage& fs, const std::string&, const LogPolyfit& logpolyfit) {
|
||||
logpolyfit.write(fs);
|
||||
}
|
||||
void read(const cv::FileNode& node, LogPolyfit& logpolyfit, const LogPolyfit& defaultValue) {
|
||||
if(node.empty())
|
||||
logpolyfit = defaultValue;
|
||||
else
|
||||
logpolyfit.read(node);
|
||||
}
|
||||
|
||||
LogPolyfit::LogPolyfit(Mat x, Mat y, int deg_)
|
||||
: deg(deg_)
|
||||
{
|
||||
Mat mask_ = (x > 0) & (y > 0);
|
||||
Mat src_, dst_, s_, d_;
|
||||
src_ = maskCopyTo(x, mask_);
|
||||
dst_ = maskCopyTo(y, mask_);
|
||||
log(src_, s_);
|
||||
log(dst_, d_);
|
||||
p = Polyfit(s_, d_, deg);
|
||||
}
|
||||
|
||||
Mat LogPolyfit::operator()(const Mat& inp)
|
||||
{
|
||||
Mat mask_ = inp >= 0;
|
||||
Mat y, y_, res;
|
||||
log(inp, y);
|
||||
y = p(y);
|
||||
exp(y, y_);
|
||||
y_.copyTo(res, mask_);
|
||||
return res;
|
||||
};
|
||||
|
||||
void LinearIdentity::write(cv::FileStorage& fs) const
|
||||
{
|
||||
fs << "{" << "}";
|
||||
}
|
||||
|
||||
void LinearIdentity::read(const cv::FileNode&)
|
||||
{
|
||||
}
|
||||
|
||||
void LinearGamma::write(cv::FileStorage& fs) const
|
||||
{
|
||||
fs << "{" << "gamma" << gamma << "}";
|
||||
}
|
||||
|
||||
void LinearGamma::read(const cv::FileNode& node)
|
||||
{
|
||||
node["gamma"] >> gamma;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void LinearColor<T>::write(cv::FileStorage& fs) const
|
||||
{
|
||||
fs << "{" << "deg" << deg << "pr" << pr << "pg" << pg << "pb" << pb << "}";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void LinearColor<T>::read(const cv::FileNode& node)
|
||||
{
|
||||
node["deg"] >> deg;
|
||||
node["pr"] >> pr;
|
||||
node["pg"] >> pg;
|
||||
node["pb"] >> pb;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void LinearGray<T>::write(cv::FileStorage& fs) const
|
||||
{
|
||||
fs << "{" << "deg" << deg << "p" << p << "}";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void LinearGray<T>::read(const cv::FileNode& node)
|
||||
{
|
||||
node["deg"] >> deg;
|
||||
node["p"] >> p;
|
||||
}
|
||||
|
||||
void Linear::write(cv::FileStorage&) const
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, "This is a base class, so this shouldn't be called");
|
||||
}
|
||||
|
||||
void Linear::read(const cv::FileNode&)
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, "This is a base class, so this shouldn't be called");
|
||||
}
|
||||
|
||||
void write(cv::FileStorage& fs, const std::string&, const Linear& linear)
|
||||
{
|
||||
linear.write(fs);
|
||||
}
|
||||
|
||||
void read(const cv::FileNode& node, Linear& linear, const Linear& defaultValue)
|
||||
{
|
||||
if (node.empty())
|
||||
linear = defaultValue;
|
||||
else
|
||||
linear.read(node);
|
||||
}
|
||||
|
||||
void write(cv::FileStorage& fs, const std::string&, const LinearIdentity& linearidentity)
|
||||
{
|
||||
linearidentity.write(fs);
|
||||
}
|
||||
|
||||
void read(const cv::FileNode& node, LinearIdentity& linearidentity, const LinearIdentity& defaultValue)
|
||||
{
|
||||
if (node.empty())
|
||||
linearidentity = defaultValue;
|
||||
else
|
||||
linearidentity.read(node);
|
||||
}
|
||||
|
||||
void write(cv::FileStorage& fs, const std::string&, const LinearGamma& lineargamma)
|
||||
{
|
||||
lineargamma.write(fs);
|
||||
}
|
||||
|
||||
void read(const cv::FileNode& node, LinearGamma& lineargamma, const LinearGamma& defaultValue)
|
||||
{
|
||||
if (node.empty())
|
||||
lineargamma = defaultValue;
|
||||
else
|
||||
lineargamma.read(node);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(cv::FileStorage& fs, const std::string&, const LinearColor<T>& linearcolor)
|
||||
{
|
||||
linearcolor.write(fs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read(const cv::FileNode& node, LinearColor<T>& linearcolor, const LinearColor<T>& defaultValue)
|
||||
{
|
||||
if (node.empty())
|
||||
linearcolor = defaultValue;
|
||||
else
|
||||
linearcolor.read(node);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(cv::FileStorage& fs, const std::string&, const LinearGray<T>& lineargray)
|
||||
{
|
||||
lineargray.write(fs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read(const cv::FileNode& node, LinearGray<T>& lineargray, const LinearGray<T>& defaultValue)
|
||||
{
|
||||
if (node.empty())
|
||||
lineargray = defaultValue;
|
||||
else
|
||||
lineargray.read(node);
|
||||
}
|
||||
|
||||
Mat Linear::linearize(Mat inp)
|
||||
{
|
||||
return inp;
|
||||
};
|
||||
|
||||
Mat LinearGamma::linearize(Mat inp)
|
||||
{
|
||||
Mat out;
|
||||
gammaCorrection(inp, out, gamma);
|
||||
return out;
|
||||
};
|
||||
|
||||
std::shared_ptr<Linear> getLinear(double gamma, int deg, Mat src, Color dst, Mat mask, RGBBase_ cs, LinearizationType linearizationType)
|
||||
{
|
||||
std::shared_ptr<Linear> p = std::make_shared<Linear>();
|
||||
switch (linearizationType)
|
||||
{
|
||||
case cv::ccm::LINEARIZATION_IDENTITY:
|
||||
p = std::make_shared<LinearIdentity>();
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_GAMMA:
|
||||
p = std::make_shared<LinearGamma>(gamma);
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_COLORPOLYFIT:
|
||||
p = std::make_shared<LinearColor<Polyfit>>(deg, src, dst, mask, cs);
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_COLORLOGPOLYFIT:
|
||||
p = std::make_shared<LinearColor<LogPolyfit>>(deg, src, dst, mask, cs);
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_GRAYPOLYFIT:
|
||||
p = std::make_shared<LinearGray<Polyfit>>(deg, src, dst, mask, cs);
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_GRAYLOGPOLYFIT:
|
||||
p = std::make_shared<LinearGray<LogPolyfit>>(deg, src, dst, mask, cs);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Wrong linearizationType!" );
|
||||
break;
|
||||
}
|
||||
return p;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
@@ -0,0 +1,260 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_LINEARIZE_HPP__
|
||||
#define __OPENCV_CCM_LINEARIZE_HPP__
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <map>
|
||||
#include "color.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
/** @brief Polyfit model.
|
||||
*/
|
||||
class Polyfit
|
||||
{
|
||||
public:
|
||||
int deg;
|
||||
Mat p;
|
||||
Polyfit();
|
||||
|
||||
/** @brief Polyfit method.
|
||||
https://en.wikipedia.org/wiki/Polynomial_regression
|
||||
polynomial: yi = a0 + a1*xi + a2*xi^2 + ... + an*xi^deg (i = 1,2,...,n)
|
||||
and deduct: Ax = y
|
||||
*/
|
||||
Polyfit(Mat x, Mat y, int deg);
|
||||
virtual ~Polyfit() {};
|
||||
Mat operator()(const Mat& inp);
|
||||
|
||||
// Serialization support
|
||||
void write(cv::FileStorage& fs) const;
|
||||
void read(const cv::FileNode& node);
|
||||
|
||||
private:
|
||||
double fromEW(double x);
|
||||
};
|
||||
|
||||
// Global functions for FileStorage for Polyfit
|
||||
void write(cv::FileStorage& fs, const std::string&, const Polyfit& polyfit);
|
||||
void read(const cv::FileNode& node, Polyfit& polyfit, const Polyfit& defaultValue = Polyfit());
|
||||
|
||||
/** @brief Logpolyfit model.
|
||||
*/
|
||||
class LogPolyfit
|
||||
{
|
||||
public:
|
||||
int deg;
|
||||
Polyfit p;
|
||||
|
||||
LogPolyfit();
|
||||
|
||||
/** @brief Logpolyfit method.
|
||||
*/
|
||||
LogPolyfit(Mat x, Mat y, int deg);
|
||||
virtual ~LogPolyfit() {};
|
||||
Mat operator()(const Mat& inp);
|
||||
|
||||
// Serialization support
|
||||
void write(cv::FileStorage& fs) const;
|
||||
void read(const cv::FileNode& node);
|
||||
};
|
||||
|
||||
// Global functions for FileStorage for LogPolyfit
|
||||
void write(cv::FileStorage& fs, const std::string&, const LogPolyfit& logpolyfit);
|
||||
void read(const cv::FileNode& node, LogPolyfit& logpolyfit, const LogPolyfit& defaultValue = LogPolyfit());
|
||||
|
||||
/** @brief Linearization base.
|
||||
*/
|
||||
|
||||
class Linear
|
||||
{
|
||||
public:
|
||||
Linear() {};
|
||||
virtual ~Linear() {};
|
||||
|
||||
/** @brief Inference.
|
||||
@param inp the input array, type of cv::Mat.
|
||||
*/
|
||||
virtual Mat linearize(Mat inp);
|
||||
/** @brief Evaluate linearization model.
|
||||
*/
|
||||
virtual void value(void) {};
|
||||
|
||||
// Serialization support
|
||||
virtual void write(cv::FileStorage& fs) const;
|
||||
virtual void read(const cv::FileNode& node);
|
||||
};
|
||||
|
||||
// Global functions for FileStorage for Linear
|
||||
void write(cv::FileStorage& fs, const std::string&, const Linear& linear);
|
||||
void read(const cv::FileNode& node, Linear& linear, const Linear& defaultValue = Linear());
|
||||
|
||||
/** @brief Linearization identity.
|
||||
make no change.
|
||||
*/
|
||||
class LinearIdentity : public Linear
|
||||
{
|
||||
public:
|
||||
void write(cv::FileStorage& fs) const CV_OVERRIDE;
|
||||
void read(const cv::FileNode& node) CV_OVERRIDE;
|
||||
};
|
||||
|
||||
// Global functions for FileStorage for LinearIdentity
|
||||
void write(cv::FileStorage& fs, const std::string&, const LinearIdentity& linearidentity);
|
||||
void read(const cv::FileNode& node, LinearIdentity& linearidentity, const LinearIdentity& defaultValue = LinearIdentity());
|
||||
|
||||
/** @brief Linearization gamma correction.
|
||||
*/
|
||||
class LinearGamma : public Linear
|
||||
{
|
||||
public:
|
||||
double gamma;
|
||||
|
||||
LinearGamma()
|
||||
: gamma(1.0) {};
|
||||
|
||||
LinearGamma(double gamma_)
|
||||
: gamma(gamma_) {};
|
||||
|
||||
Mat linearize(Mat inp) CV_OVERRIDE;
|
||||
|
||||
// Serialization support
|
||||
void write(cv::FileStorage& fs) const CV_OVERRIDE;
|
||||
void read(const cv::FileNode& node) CV_OVERRIDE;
|
||||
};
|
||||
|
||||
// Global functions for FileStorage for LinearGamma
|
||||
void write(cv::FileStorage& fs, const std::string&, const LinearGamma& lineargamma);
|
||||
void read(const cv::FileNode& node, LinearGamma& lineargamma, const LinearGamma& defaultValue = LinearGamma());
|
||||
|
||||
/** @brief Linearization.
|
||||
Grayscale polynomial fitting.
|
||||
*/
|
||||
template <class T>
|
||||
class LinearGray : public Linear
|
||||
{
|
||||
public:
|
||||
int deg;
|
||||
T p;
|
||||
|
||||
LinearGray(): deg(3) {};
|
||||
|
||||
LinearGray(int deg_, Mat src, Color dst, Mat mask, RGBBase_ cs)
|
||||
: deg(deg_)
|
||||
{
|
||||
dst.getGray();
|
||||
Mat lear_gray_mask = mask & dst.grays;
|
||||
|
||||
// the grayscale function is approximate for src is in relative color space.
|
||||
Mat gray;
|
||||
cvtColor(src, gray, COLOR_RGB2GRAY);
|
||||
gray.copyTo(src);
|
||||
|
||||
Mat dst_ = maskCopyTo(dst.toGray(cs.illumobserver), lear_gray_mask);
|
||||
calc(src, dst_);
|
||||
}
|
||||
|
||||
/** @brief monotonically increase is not guaranteed.
|
||||
@param src the input array, type of cv::Mat.
|
||||
@param dst the input array, type of cv::Mat.
|
||||
*/
|
||||
void calc(const Mat& src, const Mat& dst)
|
||||
{
|
||||
p = T(src, dst, deg);
|
||||
};
|
||||
|
||||
Mat linearize(Mat inp) CV_OVERRIDE
|
||||
{
|
||||
return p(inp);
|
||||
};
|
||||
|
||||
// Serialization support
|
||||
void write(cv::FileStorage& fs) const CV_OVERRIDE;
|
||||
void read(const cv::FileNode& node) CV_OVERRIDE;
|
||||
};
|
||||
|
||||
// Global functions for FileStorage for LinearGray
|
||||
template <typename T>
|
||||
void write(cv::FileStorage& fs, const std::string&, const LinearGray<T>& lineargray);
|
||||
template <typename T>
|
||||
void read(const cv::FileNode& node, LinearGray<T>& lineargray, const LinearGray<T>& defaultValue = LinearGray<T>());
|
||||
|
||||
/** @brief Linearization.
|
||||
Fitting channels respectively.
|
||||
*/
|
||||
template <class T>
|
||||
class LinearColor : public Linear
|
||||
{
|
||||
public:
|
||||
int deg;
|
||||
T pr;
|
||||
T pg;
|
||||
T pb;
|
||||
|
||||
LinearColor(): deg(3) {};
|
||||
|
||||
LinearColor(int deg_, Mat src_, Color dst, Mat mask, RGBBase_ cs)
|
||||
: deg(deg_)
|
||||
{
|
||||
Mat src = maskCopyTo(src_, mask);
|
||||
Mat dst_ = maskCopyTo(dst.to(*cs.l).colors, mask);
|
||||
calc(src, dst_);
|
||||
}
|
||||
|
||||
void calc(const Mat& src, const Mat& dst)
|
||||
{
|
||||
Mat schannels[3];
|
||||
Mat dchannels[3];
|
||||
split(src, schannels);
|
||||
split(dst, dchannels);
|
||||
pr = T(schannels[0], dchannels[0], deg);
|
||||
pg = T(schannels[1], dchannels[1], deg);
|
||||
pb = T(schannels[2], dchannels[2], deg);
|
||||
};
|
||||
|
||||
Mat linearize(Mat inp) CV_OVERRIDE
|
||||
{
|
||||
Mat channels[3];
|
||||
split(inp, channels);
|
||||
std::vector<Mat> channel;
|
||||
Mat res;
|
||||
merge(std::vector<Mat> { pr(channels[0]), pg(channels[1]), pb(channels[2]) }, res);
|
||||
return res;
|
||||
};
|
||||
|
||||
// Serialization support
|
||||
void write(cv::FileStorage& fs) const CV_OVERRIDE;
|
||||
void read(const cv::FileNode& node) CV_OVERRIDE;
|
||||
};
|
||||
|
||||
// Global functions for FileStorage for LinearColor
|
||||
template <typename T>
|
||||
void write(cv::FileStorage& fs, const std::string&, const LinearColor<T>& linearcolor);
|
||||
template <typename T>
|
||||
void read(const cv::FileNode& node, LinearColor<T>& linearcolor, const LinearColor<T>& defaultValue = LinearColor<T>());
|
||||
|
||||
/** @brief Get linearization method.
|
||||
used in ccm model.
|
||||
@param gamma used in LinearGamma.
|
||||
@param deg degrees.
|
||||
@param src the input array, type of cv::Mat.
|
||||
@param dst the input array, type of cv::Mat.
|
||||
@param mask the input array, type of cv::Mat.
|
||||
@param cs type of RGBBase_.
|
||||
@param linearizationType type of linear.
|
||||
*/
|
||||
|
||||
std::shared_ptr<Linear> getLinear(double gamma, int deg, Mat src, Color dst, Mat mask, RGBBase_ cs, LinearizationType linearizationType);
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "operations.hpp"
|
||||
#include "utils.hpp"
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
Mat Operation::operator()(Mat& abc)
|
||||
{
|
||||
if (!linear)
|
||||
{
|
||||
return f(abc);
|
||||
}
|
||||
if (M.empty())
|
||||
{
|
||||
return abc;
|
||||
}
|
||||
return multiple(abc, M);
|
||||
};
|
||||
|
||||
void Operation::add(const Operation& other)
|
||||
{
|
||||
if (M.empty())
|
||||
{
|
||||
M = other.M.clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
M = M * other.M;
|
||||
}
|
||||
};
|
||||
|
||||
void Operation::clear()
|
||||
{
|
||||
M = Mat();
|
||||
};
|
||||
|
||||
Operations& Operations::add(const Operations& other)
|
||||
{
|
||||
ops.insert(ops.end(), other.ops.begin(), other.ops.end());
|
||||
return *this;
|
||||
};
|
||||
|
||||
Mat Operations::run(Mat abc)
|
||||
{
|
||||
Operation hd;
|
||||
for (auto& op : ops)
|
||||
{
|
||||
if (op.linear)
|
||||
{
|
||||
hd.add(op);
|
||||
}
|
||||
else
|
||||
{
|
||||
abc = hd(abc);
|
||||
hd.clear();
|
||||
abc = op(abc);
|
||||
}
|
||||
}
|
||||
abc = hd(abc);
|
||||
return abc;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
@@ -0,0 +1,83 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_OPERATIONS_HPP__
|
||||
#define __OPENCV_CCM_OPERATIONS_HPP__
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
/** @brief Operation class contains some operarions used for color space
|
||||
conversion containing linear transformation and non-linear transformation
|
||||
*/
|
||||
class Operation
|
||||
{
|
||||
public:
|
||||
typedef std::function<Mat(Mat)> MatFunc;
|
||||
bool linear;
|
||||
Mat M;
|
||||
MatFunc f;
|
||||
|
||||
Operation()
|
||||
: linear(true)
|
||||
, M(Mat()) {};
|
||||
Operation(Mat M_)
|
||||
: linear(true)
|
||||
, M(M_) {};
|
||||
Operation(MatFunc f_)
|
||||
: linear(false)
|
||||
, f(f_) {};
|
||||
virtual ~Operation() {};
|
||||
|
||||
/** @brief operator function will run operation
|
||||
*/
|
||||
Mat operator()(Mat& abc);
|
||||
|
||||
/** @brief add function will conbine this operation
|
||||
with other linear transformation operation
|
||||
*/
|
||||
void add(const Operation& other);
|
||||
|
||||
void clear();
|
||||
static Operation& getIdentityOp()
|
||||
{
|
||||
static Operation identity_op([](Mat x) { return x; });
|
||||
return identity_op;
|
||||
}
|
||||
};
|
||||
|
||||
class Operations
|
||||
{
|
||||
public:
|
||||
std::vector<Operation> ops;
|
||||
Operations()
|
||||
: ops {} {};
|
||||
Operations(std::initializer_list<Operation> op)
|
||||
: ops { op } {};
|
||||
virtual ~Operations() {};
|
||||
|
||||
/** @brief add function will conbine this operation with other transformation operations
|
||||
*/
|
||||
Operations& add(const Operations& other);
|
||||
|
||||
/** @brief run operations to make color conversion
|
||||
*/
|
||||
Mat run(Mat abc);
|
||||
static const Operations& getIdentityOps()
|
||||
{
|
||||
static Operations Operation_op {Operation::getIdentityOp()};
|
||||
return Operation_op;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,113 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
void gammaCorrection(InputArray _src, OutputArray _dst, double gamma)
|
||||
{
|
||||
Mat src = _src.getMat();
|
||||
CV_Assert(gamma > 0);
|
||||
|
||||
double maxVal;
|
||||
int depth = src.depth();
|
||||
switch (depth)
|
||||
{
|
||||
case CV_8U: maxVal = 255.0; break;
|
||||
case CV_16U: maxVal = 65535.0; break;
|
||||
case CV_16S: maxVal = 32767.0; break;
|
||||
case CV_32F: maxVal = 1.0; break;
|
||||
case CV_64F: maxVal = 1.0; break;
|
||||
default:
|
||||
CV_Error(Error::StsUnsupportedFormat,
|
||||
"gammaCorrection: unsupported image depth");
|
||||
}
|
||||
|
||||
// Special‐case for uint8 with a LUT
|
||||
if (depth == CV_8U)
|
||||
{
|
||||
Mat lut(1, 256, CV_8U);
|
||||
uchar* p = lut.ptr<uchar>();
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
double fn = std::pow(i / 255.0, gamma) * 255.0;
|
||||
p[i] = cv::saturate_cast<uchar>(fn + 0.5);
|
||||
}
|
||||
_dst.create(src.size(), src.type());
|
||||
Mat dst = _dst.getMat();
|
||||
cv::LUT(src, lut, dst);
|
||||
return;
|
||||
}
|
||||
|
||||
Mat f;
|
||||
src.convertTo(f, CV_64F, 1.0 / maxVal);
|
||||
cv::pow(f, gamma, f);
|
||||
|
||||
_dst.create(src.size(), src.type());
|
||||
Mat dst = _dst.getMat();
|
||||
f.convertTo(dst, src.type(), maxVal);
|
||||
}
|
||||
|
||||
|
||||
Mat maskCopyTo(const Mat& src, const Mat& mask)
|
||||
{
|
||||
Mat fullMasked;
|
||||
src.copyTo(fullMasked, mask);
|
||||
|
||||
std::vector<Point> nonZeroLocations;
|
||||
findNonZero(mask, nonZeroLocations);
|
||||
|
||||
Mat dst(static_cast<int>(nonZeroLocations.size()), 1, src.type());
|
||||
|
||||
int channels = src.channels();
|
||||
if (channels == 1)
|
||||
{
|
||||
for (size_t i = 0; i < nonZeroLocations.size(); i++)
|
||||
{
|
||||
dst.at<double>(static_cast<int>(i), 0) = fullMasked.at<double>(nonZeroLocations[i]);
|
||||
}
|
||||
}
|
||||
else if (channels == 3)
|
||||
{
|
||||
for (size_t i = 0; i < nonZeroLocations.size(); i++)
|
||||
{
|
||||
dst.at<Vec3d>(static_cast<int>(i), 0) = fullMasked.at<Vec3d>(nonZeroLocations[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Error(Error::StsBadArg, "Unsupported number of channels");
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
Mat multiple(const Mat& xyz, const Mat& ccm)
|
||||
{
|
||||
Mat tmp = xyz.reshape(1, xyz.rows * xyz.cols);
|
||||
Mat res = tmp * ccm;
|
||||
res = res.reshape(res.cols, xyz.rows);
|
||||
return res;
|
||||
}
|
||||
|
||||
Mat saturate(Mat& src, double low, double up)
|
||||
{
|
||||
CV_Assert(src.type() == CV_64FC3);
|
||||
Scalar lower_bound(low, low, low);
|
||||
Scalar upper_bound(up, up, up);
|
||||
|
||||
Mat mask;
|
||||
inRange(src, lower_bound, upper_bound, mask);
|
||||
mask /= 255;
|
||||
|
||||
return mask;
|
||||
}
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
@@ -0,0 +1,145 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_UTILS_HPP__
|
||||
#define __OPENCV_CCM_UTILS_HPP__
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
/** @brief gamma correction.
|
||||
\f[
|
||||
C_l=C_n^{\gamma},\qquad C_n\ge0\\
|
||||
C_l=-(-C_n)^{\gamma},\qquad C_n<0\\\\
|
||||
\f]
|
||||
@param src the input array,type of Mat.
|
||||
@param gamma a constant for gamma correction greater than zero.
|
||||
@param dst the output array, type of Mat.
|
||||
*/
|
||||
CV_EXPORTS_W void gammaCorrection(InputArray src, OutputArray dst, double gamma);
|
||||
|
||||
/** @brief maskCopyTo a function to delete unsatisfied elementwise.
|
||||
@param src the input array, type of Mat.
|
||||
@param mask operation mask that used to choose satisfided elementwise.
|
||||
*/
|
||||
Mat maskCopyTo(const Mat& src, const Mat& mask);
|
||||
|
||||
/** @brief multiple the function used to compute an array with n channels
|
||||
mulipied by ccm.
|
||||
@param xyz the input array, type of Mat.
|
||||
@param ccm the ccm matrix to make color correction.
|
||||
*/
|
||||
Mat multiple(const Mat& xyz, const Mat& ccm);
|
||||
|
||||
/** @brief multiple the function used to get the mask of saturated colors,
|
||||
colors between low and up will be choosed.
|
||||
@param src the input array, type of Mat.
|
||||
@param low the threshold to choose saturated colors
|
||||
@param up the threshold to choose saturated colors
|
||||
*/
|
||||
Mat saturate(Mat& src, double low, double up);
|
||||
|
||||
/** @brief function for elementWise operation
|
||||
@param src the input array, type of Mat
|
||||
@param lambda a for operation
|
||||
*/
|
||||
template <typename F>
|
||||
Mat elementWise(const Mat& src, F&& lambda, Mat dst=Mat())
|
||||
{
|
||||
if (dst.empty() || !dst.isContinuous() || dst.total() != src.total() || dst.type() != src.type())
|
||||
dst = Mat(src.rows, src.cols, src.type());
|
||||
const int channel = src.channels();
|
||||
if (src.isContinuous()) {
|
||||
const int num_elements = (int)src.total()*channel;
|
||||
const double *psrc = (double*)src.data;
|
||||
double *pdst = (double*)dst.data;
|
||||
const int batch = getNumThreads() > 1 ? 128 : num_elements;
|
||||
const int N = (num_elements / batch) + ((num_elements % batch) > 0);
|
||||
parallel_for_(Range(0, N),[&](const Range& range) {
|
||||
const int start = range.start * batch;
|
||||
const int end = std::min(range.end*batch, num_elements);
|
||||
for (int i = start; i < end; i++) {
|
||||
pdst[i] = lambda(psrc[i]);
|
||||
}
|
||||
});
|
||||
return dst;
|
||||
}
|
||||
switch (channel)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
|
||||
MatIterator_<double> it, end;
|
||||
for (it = dst.begin<double>(), end = dst.end<double>(); it != end; ++it)
|
||||
{
|
||||
(*it) = lambda((*it));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
MatIterator_<Vec3d> it, end;
|
||||
for (it = dst.begin<Vec3d>(), end = dst.end<Vec3d>(); it != end; ++it)
|
||||
{
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
(*it)[j] = lambda((*it)[j]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Wrong channel!" );
|
||||
break;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
/** @brief function for channel operation
|
||||
@param src the input array, type of Mat
|
||||
@param lambda the function for operation
|
||||
*/
|
||||
template <typename F>
|
||||
Mat channelWise(const Mat& src, F&& lambda)
|
||||
{
|
||||
Mat dst = src.clone();
|
||||
MatIterator_<Vec3d> it, end;
|
||||
for (it = dst.begin<Vec3d>(), end = dst.end<Vec3d>(); it != end; ++it)
|
||||
{
|
||||
*it = lambda(*it);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
/** @brief function for distance operation.
|
||||
@param src the input array, type of Mat.
|
||||
@param ref another input array, type of Mat.
|
||||
@param lambda the computing method for distance .
|
||||
*/
|
||||
template <typename F>
|
||||
Mat distanceWise(Mat& src, Mat& ref, F&& lambda)
|
||||
{
|
||||
Mat dst = Mat(src.size(), CV_64FC1);
|
||||
MatIterator_<Vec3d> it_src = src.begin<Vec3d>(), end_src = src.end<Vec3d>(),
|
||||
it_ref = ref.begin<Vec3d>();
|
||||
MatIterator_<double> it_dst = dst.begin<double>();
|
||||
for (; it_src != end_src; ++it_src, ++it_ref, ++it_dst)
|
||||
{
|
||||
*it_dst = lambda(*it_src, *it_ref);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
Mat multiple(const Mat& xyz, const Mat& ccm);
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,242 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv {
|
||||
|
||||
CV_EXPORTS_W void loadChromaticAberrationParams(const FileNode& fs,
|
||||
OutputArray coeffMat,
|
||||
Size& calib_size,
|
||||
int& degree)
|
||||
{
|
||||
int imgW = 0, imgH = 0;
|
||||
fs["image_width"] >> imgW;
|
||||
fs["image_height"] >> imgH;
|
||||
|
||||
CV_Assert(imgW > 0 && imgH > 0);
|
||||
|
||||
auto readChannel = [&](const char* key,
|
||||
std::vector<double>& coeffs_x,
|
||||
std::vector<double>& coeffs_y,
|
||||
int& deg_out)
|
||||
{
|
||||
FileNode ch = fs[key];
|
||||
if (ch.empty())
|
||||
CV_Error_(Error::StsParseError,
|
||||
("Missing channel \"%s\"", key));
|
||||
|
||||
ch["coeffs_x"] >> coeffs_x;
|
||||
ch["coeffs_y"] >> coeffs_y;
|
||||
|
||||
if (coeffs_x.empty() || coeffs_y.empty())
|
||||
CV_Error_(Error::StsParseError,
|
||||
("%s: coeffs_x/coeffs_y missing", key));
|
||||
|
||||
if (coeffs_x.size() != coeffs_y.size())
|
||||
CV_Error_(Error::StsBadSize,
|
||||
("%s: coeffs_x (%zu) vs coeffs_y (%zu)",
|
||||
key, coeffs_x.size(), coeffs_y.size()));
|
||||
|
||||
if (!checkRange(coeffs_x, true) || !checkRange(coeffs_y, true))
|
||||
CV_Error_(Error::StsBadArg,
|
||||
("%s: coefficient array contains NaN/Inf", key));
|
||||
size_t m = coeffs_x.size();
|
||||
double n_float = (std::sqrt(1.0 + 8.0 * m) - 3.0) / 2.0;
|
||||
int deg = static_cast<int>(std::round(n_float));
|
||||
size_t expected_m = static_cast<size_t>((deg + 1) * (deg + 2) / 2);
|
||||
if (m != expected_m){
|
||||
CV_Error_(Error::StsBadArg,
|
||||
("Coefficient count %zu is not triangular for degree %d "
|
||||
"(expected %zu)", m, deg, expected_m));
|
||||
}
|
||||
deg_out = deg;
|
||||
};
|
||||
|
||||
std::vector<double> red_x, red_y, blue_x, blue_y;
|
||||
int deg_red = 0, deg_blue = 0;
|
||||
readChannel("red_channel", red_x, red_y, deg_red);
|
||||
readChannel("blue_channel", blue_x, blue_y, deg_blue);
|
||||
|
||||
|
||||
if (red_x.size() != blue_x.size()){
|
||||
CV_Error_(Error::StsBadSize,
|
||||
("Red (%zu) and blue (%zu) coefficient counts differ",
|
||||
red_x.size(), blue_x.size()));
|
||||
}
|
||||
if (deg_red != deg_blue){
|
||||
CV_Error_(Error::StsBadArg,
|
||||
("Red (%d) and blue (%d) degrees differ",
|
||||
deg_red, deg_blue));
|
||||
}
|
||||
|
||||
const int mterms = (int)red_x.size();
|
||||
|
||||
Mat tmp(4, mterms, CV_32F);
|
||||
|
||||
float* Bx = tmp.ptr<float>(0);
|
||||
float* By = tmp.ptr<float>(1);
|
||||
float* Rx = tmp.ptr<float>(2);
|
||||
float* Ry = tmp.ptr<float>(3);
|
||||
|
||||
for (int i = 0; i < mterms; ++i) {
|
||||
Bx[i] = static_cast<float>(blue_x[i]);
|
||||
By[i] = static_cast<float>(blue_y[i]);
|
||||
Rx[i] = static_cast<float>(red_x[i]);
|
||||
Ry[i] = static_cast<float>(red_y[i]);
|
||||
}
|
||||
|
||||
calib_size = Size(imgW, imgH);
|
||||
degree = deg_red;
|
||||
tmp.copyTo(coeffMat);
|
||||
}
|
||||
|
||||
static void buildRemapsFromCoeffMat(int height, int width,
|
||||
const Mat& coeffs,
|
||||
int degree,
|
||||
int rowX, int rowY,
|
||||
Mat& map_x, Mat& map_y)
|
||||
{
|
||||
if (coeffs.type() != CV_32F) {
|
||||
CV_Error_(Error::StsUnsupportedFormat,
|
||||
("coeffs Mat must be CV_32F (got type=%d)", coeffs.type()));
|
||||
}
|
||||
|
||||
if (coeffs.rows != 4) {
|
||||
CV_Error_(Error::StsBadSize,
|
||||
("coeffs.rows must be 4 (Bx,By,Rx,Ry); got %d", coeffs.rows));
|
||||
}
|
||||
|
||||
if (rowX < 0 || rowX >= coeffs.rows) {
|
||||
CV_Error_(Error::StsOutOfRange,
|
||||
("rowX index %d out of range [0,%d)", rowX, coeffs.rows));
|
||||
}
|
||||
if (rowY < 0 || rowY >= coeffs.rows) {
|
||||
CV_Error_(Error::StsOutOfRange,
|
||||
("rowY index %d out of range [0,%d)", rowY, coeffs.rows));
|
||||
}
|
||||
|
||||
if (degree < 0) {
|
||||
CV_Error_(Error::StsBadArg,
|
||||
("degree must be non-negative; got %d", degree));
|
||||
}
|
||||
|
||||
const int expected_terms = (degree + 1) * (degree + 2) / 2;
|
||||
if (coeffs.cols != expected_terms) {
|
||||
CV_Error_(Error::StsBadSize,
|
||||
("coeffs.cols (%d) != expected polynomial term count (%d) for degree=%d",
|
||||
coeffs.cols, expected_terms, degree));
|
||||
}
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
CV_Error_(Error::StsBadArg,
|
||||
("width (%d) and height (%d) must be positive", width, height));
|
||||
}
|
||||
|
||||
Mat X(1, width, CV_32F), Y(height, 1, CV_32F);
|
||||
for (int i = 0; i < width; ++i) X.at<float>(0,i) = (float)i;
|
||||
for (int j = 0; j < height; ++j) Y.at<float>(j,0) = (float)j;
|
||||
|
||||
Mat Xgrid, Ygrid;
|
||||
repeat(X, height, 1, Xgrid);
|
||||
repeat(Y, 1, width, Ygrid);
|
||||
|
||||
Mat dx(height, width, CV_32F);
|
||||
Mat dy(height, width, CV_32F);
|
||||
|
||||
const double mean_x = width * 0.5;
|
||||
const double mean_y = height * 0.5;
|
||||
const double inv_std_x = 1.0 / mean_x;
|
||||
const double inv_std_y = 1.0 / mean_y;
|
||||
|
||||
const float* Cx = coeffs.ptr<float>(rowX);
|
||||
const float* Cy = coeffs.ptr<float>(rowY);
|
||||
|
||||
|
||||
parallel_for_(Range(0, height), [&](const Range& rows){
|
||||
std::vector<double> x_pow(degree + 1);
|
||||
std::vector<double> y_pow(degree + 1);
|
||||
for (int y = rows.start; y < rows.end; ++y) {
|
||||
const float* XR = Xgrid.ptr<float>(y);
|
||||
const float* YR = Ygrid.ptr<float>(y);
|
||||
float* DX = dx.ptr<float>(y);
|
||||
float* DY = dy.ptr<float>(y);
|
||||
for (int x = 0; x < width; ++x) {
|
||||
const double xn = (XR[x] - mean_x) * inv_std_x;
|
||||
const double yn = (YR[x] - mean_y) * inv_std_y;
|
||||
|
||||
x_pow[0] = y_pow[0] = 1.0;
|
||||
for (int k = 1; k <= degree; ++k) {
|
||||
x_pow[k] = x_pow[k-1] * xn;
|
||||
y_pow[k] = y_pow[k-1] * yn;
|
||||
}
|
||||
|
||||
double dxv = 0.0, dyv = 0.0;
|
||||
int idx = 0;
|
||||
for (int t = 0; t <= degree; ++t){
|
||||
for (int i = 0; i <= t; ++i){
|
||||
const int j = t - i;
|
||||
const double term = x_pow[i] * y_pow[j];
|
||||
dxv += Cx[idx] * term;
|
||||
dyv += Cy[idx] * term;
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
|
||||
DX[x] = (float)dxv;
|
||||
DY[x] = (float)dyv;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
map_x = Xgrid - dx;
|
||||
map_y = Ygrid - dy;
|
||||
}
|
||||
|
||||
void correctChromaticAberration(InputArray input_image,
|
||||
InputArray coefficients,
|
||||
OutputArray output_image,
|
||||
const Size& calib_size,
|
||||
int calib_degree,
|
||||
int bayer_pattern)
|
||||
{
|
||||
Mat image = input_image.getMat();
|
||||
const Mat coeffMat = coefficients.getMat();
|
||||
|
||||
if (image.channels() == 1) {
|
||||
if (bayer_pattern < 0) {
|
||||
CV_Error_(Error::StsBadArg,
|
||||
("Single-channel input detected: must pass a valid bayer_pattern"));
|
||||
}
|
||||
Mat dem;
|
||||
demosaicing(image, dem, bayer_pattern);
|
||||
image = dem;
|
||||
}
|
||||
|
||||
const int height = image.rows;
|
||||
const int width = image.cols;
|
||||
|
||||
if (height != calib_size.height || width != calib_size.width) {
|
||||
CV_Error_(Error::StsBadArg,
|
||||
("Image size %dx%d does not match calibration %dx%d",
|
||||
width, height, calib_size.width, calib_size.height));
|
||||
}
|
||||
|
||||
std::vector<Mat> channels;
|
||||
split(image, channels);
|
||||
Mat b = channels[0], g = channels[1], r = channels[2];
|
||||
|
||||
Mat map_x_r, map_y_r, map_x_b, map_y_b;
|
||||
buildRemapsFromCoeffMat(height, width, coeffMat, calib_degree, 2, 3, map_x_r, map_y_r);
|
||||
buildRemapsFromCoeffMat(height, width, coeffMat, calib_degree, 0, 1, map_x_b, map_y_b);
|
||||
|
||||
Mat r_corr, b_corr;
|
||||
remap(r, r_corr, map_x_r, map_y_r, INTER_LINEAR, BORDER_REPLICATE);
|
||||
remap(b, b_corr, map_x_b, map_y_b, INTER_LINEAR, BORDER_REPLICATE);
|
||||
|
||||
std::vector<Mat> corrected_channels = {b_corr, g, r_corr};
|
||||
Mat corrected_image;
|
||||
merge(corrected_channels, output_image);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*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 <cmath>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
#include "contrast_preserve.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
void cv::decolor(InputArray _src, OutputArray _dst, OutputArray _color_boost)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat I = _src.getMat();
|
||||
_dst.create(I.size(), CV_8UC1);
|
||||
Mat dst = _dst.getMat();
|
||||
|
||||
_color_boost.create(I.size(), CV_8UC3);
|
||||
Mat color_boost = _color_boost.getMat();
|
||||
|
||||
CV_Assert(!I.empty() && (I.channels()==3));
|
||||
|
||||
// Parameter Setting
|
||||
const int maxIter = 15;
|
||||
const double tol = .0001;
|
||||
int iterCount = 0;
|
||||
double E = 0;
|
||||
double pre_E = std::numeric_limits<double>::infinity();
|
||||
|
||||
Mat img;
|
||||
I.convertTo(img, CV_32FC3, 1.0/255.0);
|
||||
|
||||
// Initialization
|
||||
Decolor obj;
|
||||
|
||||
vector <double> Cg;
|
||||
vector < vector <double> > polyGrad;
|
||||
vector <Vec3i> comb;
|
||||
vector <double> alf;
|
||||
|
||||
obj.grad_system(img,polyGrad,Cg,comb);
|
||||
obj.weak_order(img,alf);
|
||||
|
||||
// Solver
|
||||
Mat Mt = Mat(int(polyGrad.size()),int(polyGrad[0].size()), CV_32FC1);
|
||||
obj.wei_update_matrix(polyGrad,Cg,Mt);
|
||||
|
||||
vector <double> wei;
|
||||
obj.wei_inti(comb,wei);
|
||||
|
||||
//////////////////////////////// main loop starting ////////////////////////////////////////
|
||||
|
||||
vector <double> G_pos(alf.size());
|
||||
vector <double> G_neg(alf.size());
|
||||
vector <double> EXPsum(G_pos.size());
|
||||
vector <double> EXPterm(G_pos.size());
|
||||
vector <double> temp(polyGrad[0].size());
|
||||
vector <double> temp1(polyGrad[0].size());
|
||||
vector <double> temp2(EXPsum.size());
|
||||
vector <double> wei1(polyGrad.size());
|
||||
|
||||
while(std::abs(E-pre_E) > tol)
|
||||
{
|
||||
iterCount +=1;
|
||||
pre_E = E;
|
||||
|
||||
for(size_t i=0; i<polyGrad[0].size(); i++)
|
||||
{
|
||||
double val = 0.0;
|
||||
for(size_t j=0; j<polyGrad.size(); j++)
|
||||
val = val + (polyGrad[j][i] * wei[j]);
|
||||
temp[i] = val - Cg[i];
|
||||
temp1[i] = val + Cg[i];
|
||||
}
|
||||
|
||||
for(size_t i=0; i<alf.size(); i++)
|
||||
{
|
||||
const double sqSigma = obj.sigma * obj.sigma;
|
||||
const double pos = ((1 + alf[i])/2) * exp(-1.0 * 0.5 * (temp[i] * temp[i]) / sqSigma);
|
||||
const double neg = ((1 - alf[i])/2) * exp(-1.0 * 0.5 * (temp1[i] * temp1[i]) / sqSigma);
|
||||
G_pos[i] = pos;
|
||||
G_neg[i] = neg;
|
||||
}
|
||||
|
||||
for(size_t i=0; i<G_pos.size(); i++)
|
||||
EXPsum[i] = G_pos[i]+G_neg[i];
|
||||
|
||||
for(size_t i=0; i<EXPsum.size(); i++)
|
||||
temp2[i] = (EXPsum[i] == 0) ? 1.0 : 0.0;
|
||||
|
||||
for(size_t i=0; i<G_pos.size(); i++)
|
||||
EXPterm[i] = (G_pos[i] - G_neg[i])/(EXPsum[i] + temp2[i]);
|
||||
|
||||
for(int i=0; i<int(polyGrad.size()); i++)
|
||||
{
|
||||
double val1 = 0.0;
|
||||
for(int j=0; j<int(polyGrad[0].size()); j++)
|
||||
{
|
||||
val1 = val1 + (Mt.at<float>(i,j) * EXPterm[j]);
|
||||
}
|
||||
wei1[i] = val1;
|
||||
}
|
||||
|
||||
for(size_t i=0; i<wei.size(); i++)
|
||||
wei[i] = wei1[i];
|
||||
|
||||
E = obj.energyCalcu(Cg, polyGrad, wei);
|
||||
|
||||
if(iterCount > maxIter)
|
||||
break;
|
||||
}
|
||||
|
||||
Mat Gray = Mat::zeros(img.size(),CV_32FC1);
|
||||
obj.grayImContruct(wei, img, Gray);
|
||||
|
||||
Gray.convertTo(dst,CV_8UC1,255);
|
||||
|
||||
/////////////////////////////////// Contrast Boosting /////////////////////////////////
|
||||
|
||||
Mat lab;
|
||||
cvtColor(I,lab,COLOR_BGR2Lab);
|
||||
|
||||
vector <Mat> lab_channel;
|
||||
split(lab,lab_channel);
|
||||
|
||||
dst.copyTo(lab_channel[0]);
|
||||
|
||||
merge(lab_channel,lab);
|
||||
|
||||
cvtColor(lab,color_boost,COLOR_Lab2BGR);
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
/*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 <cmath>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
class Decolor
|
||||
{
|
||||
private:
|
||||
Mat kernelx;
|
||||
Mat kernely;
|
||||
int order;
|
||||
|
||||
public:
|
||||
float sigma;
|
||||
|
||||
Decolor();
|
||||
static vector<double> product(const vector <Vec3i> &comb, const double initRGB[3]);
|
||||
double energyCalcu(const vector <double> &Cg, const vector < vector <double> > &polyGrad, const vector <double> &wei) const;
|
||||
void singleChannelGradx(const Mat &img, Mat& dest) const;
|
||||
void singleChannelGrady(const Mat &img, Mat& dest) const;
|
||||
void gradvector(const Mat &img, vector <double> &grad) const;
|
||||
void colorGrad(const Mat &img, vector <double> &Cg) const;
|
||||
static void add_vector(vector <Vec3i> &comb, int &idx, int r,int g,int b);
|
||||
static void add_to_vector_poly(vector < vector <double> > &polyGrad, const vector <double> &curGrad, int &idx1);
|
||||
void weak_order(const Mat &img, vector <double> &alf) const;
|
||||
void grad_system(const Mat &img, vector < vector < double > > &polyGrad,
|
||||
vector < double > &Cg, vector <Vec3i>& comb) const;
|
||||
static void wei_update_matrix(const vector < vector <double> > &poly, const vector <double> &Cg, Mat &X);
|
||||
static void wei_inti(const vector <Vec3i> &comb, vector <double> &wei);
|
||||
void grayImContruct(vector <double> &wei, const Mat &img, Mat &Gray) const;
|
||||
};
|
||||
|
||||
double Decolor::energyCalcu(const vector <double> &Cg, const vector < vector <double> > &polyGrad, const vector <double> &wei) const
|
||||
{
|
||||
const size_t size = polyGrad[0].size();
|
||||
vector <double> energy(size);
|
||||
vector <double> temp(size);
|
||||
vector <double> temp1(size);
|
||||
|
||||
for(size_t i=0;i< polyGrad[0].size();i++)
|
||||
{
|
||||
double val = 0.0;
|
||||
for(size_t j =0;j<polyGrad.size();j++)
|
||||
val = val + (polyGrad[j][i] * wei[j]);
|
||||
temp[i] = val - Cg[i];
|
||||
temp1[i] = val + Cg[i];
|
||||
}
|
||||
|
||||
for(size_t i=0;i<polyGrad[0].size();i++)
|
||||
energy[i] = -1.0*log(exp(-1.0*std::pow(temp[i],2)/sigma) + exp(-1.0*std::pow(temp1[i],2)/sigma));
|
||||
|
||||
double sum = 0.0;
|
||||
for(size_t i=0;i<polyGrad[0].size();i++)
|
||||
sum +=energy[i];
|
||||
|
||||
return (sum/polyGrad[0].size());
|
||||
|
||||
}
|
||||
|
||||
Decolor::Decolor()
|
||||
{
|
||||
kernelx = Mat(1,2, CV_32FC1);
|
||||
kernely = Mat(2,1, CV_32FC1);
|
||||
kernelx.at<float>(0,0)=1.0;
|
||||
kernelx.at<float>(0,1)=-1.0;
|
||||
kernely.at<float>(0,0)=1.0;
|
||||
kernely.at<float>(1,0)=-1.0;
|
||||
order = 2;
|
||||
sigma = 0.02f;
|
||||
}
|
||||
|
||||
vector<double> Decolor::product(const vector<Vec3i> &comb, const double initRGB[3])
|
||||
{
|
||||
vector <double> res(comb.size());
|
||||
for (size_t i=0;i<comb.size();i++)
|
||||
{
|
||||
double dp = 0.0;
|
||||
for(int j=0;j<3;j++)
|
||||
{
|
||||
dp = dp + (comb[i][j] * initRGB[j]);
|
||||
}
|
||||
res[i] = dp;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void Decolor::singleChannelGradx(const Mat &img, Mat& dest) const
|
||||
{
|
||||
const int w = img.size().width;
|
||||
const Point anchor(kernelx.cols - kernelx.cols/2 - 1, kernelx.rows - kernelx.rows/2 - 1);
|
||||
filter2D(img, dest, -1, kernelx, anchor, 0.0, BORDER_CONSTANT);
|
||||
dest.col(w - 1) = 0.0;
|
||||
}
|
||||
|
||||
void Decolor::singleChannelGrady(const Mat &img, Mat& dest) const
|
||||
{
|
||||
const int h = img.size().height;
|
||||
const Point anchor(kernely.cols - kernely.cols/2 - 1, kernely.rows - kernely.rows/2 - 1);
|
||||
filter2D(img, dest, -1, kernely, anchor, 0.0, BORDER_CONSTANT);
|
||||
dest.row(h - 1) = 0.0;
|
||||
}
|
||||
|
||||
void Decolor::gradvector(const Mat &img, vector <double> &grad) const
|
||||
{
|
||||
Mat dest;
|
||||
Mat dest1;
|
||||
singleChannelGradx(img,dest);
|
||||
singleChannelGrady(img,dest1);
|
||||
|
||||
// the function uses transposed dest and dest1 here and bellow
|
||||
const int height = dest.size().width;
|
||||
const int width = dest.size().height;
|
||||
|
||||
grad.resize(width * height * 2);
|
||||
|
||||
for(int i=0;i<height;i++)
|
||||
for(int j=0;j<width;j++)
|
||||
grad[i*width + j] = dest.at<float>(j, i);
|
||||
|
||||
const int offset = width * height;
|
||||
for(int i=0;i<height;i++)
|
||||
for(int j=0;j<width;j++)
|
||||
grad[offset + i * width + j] = dest1.at<float>(j, i);
|
||||
}
|
||||
|
||||
void Decolor::colorGrad(const Mat &img, vector <double> &Cg) const
|
||||
{
|
||||
Mat lab;
|
||||
|
||||
cvtColor(img,lab,COLOR_BGR2Lab);
|
||||
|
||||
vector <Mat> lab_channel;
|
||||
split(lab,lab_channel);
|
||||
|
||||
vector <double> ImL;
|
||||
vector <double> Ima;
|
||||
vector <double> Imb;
|
||||
|
||||
gradvector(lab_channel[0],ImL);
|
||||
gradvector(lab_channel[1],Ima);
|
||||
gradvector(lab_channel[2],Imb);
|
||||
|
||||
Cg.resize(ImL.size());
|
||||
for(size_t i=0;i<ImL.size();i++)
|
||||
{
|
||||
const double res = sqrt(std::pow(ImL[i],2) + std::pow(Ima[i],2) + std::pow(Imb[i],2))/100;
|
||||
Cg[i] = res;
|
||||
}
|
||||
}
|
||||
|
||||
void Decolor::add_vector(vector <Vec3i> &comb, int &idx, int r,int g,int b)
|
||||
{
|
||||
comb.push_back(Vec3i(r, g, b));
|
||||
idx++;
|
||||
}
|
||||
|
||||
void Decolor::add_to_vector_poly(vector < vector <double> > &polyGrad, const vector <double> &curGrad, int &idx1)
|
||||
{
|
||||
polyGrad.push_back(curGrad);
|
||||
idx1++;
|
||||
}
|
||||
|
||||
void Decolor::weak_order(const Mat &im, vector <double> &alf) const
|
||||
{
|
||||
Mat img;
|
||||
const int h = im.size().height;
|
||||
const int w = im.size().width;
|
||||
if((h + w) > 800)
|
||||
{
|
||||
const double sizefactor = double(800)/(h+w);
|
||||
resize(im, img, Size(cvRound(w*sizefactor), cvRound(h*sizefactor)));
|
||||
}
|
||||
else
|
||||
{
|
||||
img = im;
|
||||
}
|
||||
|
||||
Mat curIm = Mat(img.size(),CV_32FC1);
|
||||
vector <Mat> rgb_channel;
|
||||
split(img,rgb_channel);
|
||||
|
||||
vector <double> Rg, Gg, Bg;
|
||||
gradvector(rgb_channel[2],Rg);
|
||||
gradvector(rgb_channel[1],Gg);
|
||||
gradvector(rgb_channel[0],Bg);
|
||||
|
||||
vector <double> t1(Rg.size()), t2(Rg.size()), t3(Rg.size());
|
||||
vector <double> tmp1(Rg.size()), tmp2(Rg.size()), tmp3(Rg.size());
|
||||
|
||||
const double level = .05;
|
||||
|
||||
for(size_t i=0;i<Rg.size();i++)
|
||||
{
|
||||
t1[i] = (Rg[i] > level) ? 1.0 : 0.0;
|
||||
t2[i] = (Gg[i] > level) ? 1.0 : 0.0;
|
||||
t3[i] = (Bg[i] > level) ? 1.0 : 0.0;
|
||||
tmp1[i] = (Rg[i] < -1.0*level) ? 1.0 : 0.0;
|
||||
tmp2[i] = (Gg[i] < -1.0*level) ? 1.0 : 0.0;
|
||||
tmp3[i] = (Bg[i] < -1.0*level) ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
alf.resize(Rg.size());
|
||||
for(size_t i =0 ;i < Rg.size();i++)
|
||||
alf[i] = (t1[i] * t2[i] * t3[i]);
|
||||
|
||||
for(size_t i =0 ;i < Rg.size();i++)
|
||||
alf[i] -= tmp1[i] * tmp2[i] * tmp3[i];
|
||||
}
|
||||
|
||||
void Decolor::grad_system(const Mat &im, vector < vector < double > > &polyGrad,
|
||||
vector < double > &Cg, vector <Vec3i>& comb) const
|
||||
{
|
||||
Mat img;
|
||||
int h = im.size().height;
|
||||
int w = im.size().width;
|
||||
if((h + w) > 800)
|
||||
{
|
||||
const double sizefactor = double(800)/(h+w);
|
||||
resize(im, img, Size(cvRound(w*sizefactor), cvRound(h*sizefactor)));
|
||||
}
|
||||
else
|
||||
{
|
||||
img = im;
|
||||
}
|
||||
|
||||
h = img.size().height;
|
||||
w = img.size().width;
|
||||
colorGrad(img,Cg);
|
||||
|
||||
Mat curIm = Mat(img.size(),CV_32FC1);
|
||||
vector <Mat> rgb_channel;
|
||||
split(img,rgb_channel);
|
||||
|
||||
int idx = 0, idx1 = 0;
|
||||
for(int r=0 ;r <=order; r++)
|
||||
{
|
||||
for(int g=0; g<=order;g++)
|
||||
{
|
||||
for(int b =0; b <=order;b++)
|
||||
{
|
||||
if((r+g+b)<=order && (r+g+b) > 0)
|
||||
{
|
||||
add_vector(comb,idx,r,g,b);
|
||||
for(int i = 0;i<h;i++)
|
||||
for(int j=0;j<w;j++)
|
||||
curIm.at<float>(i,j)=static_cast<float>(
|
||||
std::pow(rgb_channel[2].at<float>(i,j),r)*std::pow(rgb_channel[1].at<float>(i,j),g)*
|
||||
std::pow(rgb_channel[0].at<float>(i,j),b));
|
||||
vector <double> curGrad;
|
||||
gradvector(curIm,curGrad);
|
||||
add_to_vector_poly(polyGrad,curGrad,idx1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Decolor::wei_update_matrix(const vector < vector <double> > &poly, const vector <double> &Cg, Mat &X)
|
||||
{
|
||||
const int size = static_cast<int>(poly.size());
|
||||
const int size0 = static_cast<int>(poly[0].size());
|
||||
Mat P = Mat(size, size0, CV_32FC1);
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
for (int j = 0; j < size0;j++)
|
||||
P.at<float>(i,j) = static_cast<float>(poly[i][j]);
|
||||
|
||||
Mat B = Mat(size, size0, CV_32FC1);
|
||||
for(int i =0;i < size;i++)
|
||||
{
|
||||
for(int j = 0, end = int(Cg.size()); j < end;j++)
|
||||
B.at<float>(i,j) = static_cast<float>(poly[i][j] * Cg[j]);
|
||||
}
|
||||
|
||||
Mat A;
|
||||
mulTransposed(P, A, false);
|
||||
solve(A, B, X, DECOMP_NORMAL);
|
||||
|
||||
}
|
||||
|
||||
void Decolor::wei_inti(const vector <Vec3i> &comb, vector <double> &wei)
|
||||
{
|
||||
double initRGB[3] = { .33, .33, .33 };
|
||||
|
||||
wei = product(comb,initRGB);
|
||||
|
||||
vector <int> sum(comb.size());
|
||||
|
||||
for(size_t i=0;i<comb.size();i++)
|
||||
sum[i] = (comb[i][0] + comb[i][1] + comb[i][2]);
|
||||
|
||||
for(size_t i=0;i<sum.size();i++)
|
||||
{
|
||||
if(sum[i] == 1)
|
||||
wei[i] = wei[i] * double(1);
|
||||
else
|
||||
wei[i] = wei[i] * double(0);
|
||||
}
|
||||
|
||||
sum.clear();
|
||||
|
||||
}
|
||||
|
||||
void Decolor::grayImContruct(vector <double> &wei, const Mat &img, Mat &Gray) const
|
||||
{
|
||||
const int h = img.size().height;
|
||||
const int w = img.size().width;
|
||||
|
||||
vector <Mat> rgb_channel;
|
||||
split(img,rgb_channel);
|
||||
|
||||
int kk =0;
|
||||
|
||||
for(int r =0;r<=order;r++)
|
||||
{
|
||||
for(int g=0;g<=order;g++)
|
||||
{
|
||||
for(int b=0;b<=order;b++)
|
||||
{
|
||||
if((r + g + b) <=order && (r+g+b) > 0)
|
||||
{
|
||||
for(int i = 0;i<h;i++)
|
||||
for(int j=0;j<w;j++)
|
||||
Gray.at<float>(i,j)=static_cast<float>(Gray.at<float>(i,j) +
|
||||
static_cast<float>(wei[kk])*std::pow(rgb_channel[2].at<float>(i,j),r)*std::pow(rgb_channel[1].at<float>(i,j),g)*
|
||||
std::pow(rgb_channel[0].at<float>(i,j),b));
|
||||
|
||||
kk=kk+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double minval, maxval;
|
||||
minMaxLoc(Gray, &minval, &maxval);
|
||||
|
||||
Gray -= minval;
|
||||
Gray /= maxval - minval;
|
||||
}
|
||||
@@ -0,0 +1,564 @@
|
||||
/*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 "opencv2/core/cuda/common.hpp"
|
||||
#include "opencv2/core/cuda/vec_traits.hpp"
|
||||
#include "opencv2/core/cuda/vec_math.hpp"
|
||||
#include "opencv2/core/cuda/functional.hpp"
|
||||
#include "opencv2/core/cuda/reduce.hpp"
|
||||
#include "opencv2/core/cuda/border_interpolate.hpp"
|
||||
|
||||
using namespace cv::cuda;
|
||||
|
||||
typedef unsigned char uchar;
|
||||
typedef unsigned short ushort;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//// Non Local Means Denosing
|
||||
|
||||
namespace cv { namespace cuda { namespace device
|
||||
{
|
||||
namespace imgproc
|
||||
{
|
||||
__device__ __forceinline__ float norm2(const float& v) { return v*v; }
|
||||
__device__ __forceinline__ float norm2(const float2& v) { return v.x*v.x + v.y*v.y; }
|
||||
__device__ __forceinline__ float norm2(const float3& v) { return v.x*v.x + v.y*v.y + v.z*v.z; }
|
||||
__device__ __forceinline__ float norm2(const float4& v) { return v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; }
|
||||
|
||||
template<typename T, typename B>
|
||||
__global__ void nlm_kernel(const PtrStep<T> src, PtrStepSz<T> dst, const B b, int search_radius, int block_radius, float noise_mult)
|
||||
{
|
||||
typedef typename TypeVec<float, VecTraits<T>::cn>::vec_type value_type;
|
||||
|
||||
const int i = blockDim.y * blockIdx.y + threadIdx.y;
|
||||
const int j = blockDim.x * blockIdx.x + threadIdx.x;
|
||||
|
||||
if (j >= dst.cols || i >= dst.rows)
|
||||
return;
|
||||
|
||||
int bsize = search_radius + block_radius;
|
||||
int search_window = 2 * search_radius + 1;
|
||||
float minus_search_window2_inv = -1.f/(search_window * search_window);
|
||||
|
||||
value_type sum1 = VecTraits<value_type>::all(0);
|
||||
float sum2 = 0.f;
|
||||
|
||||
if (j - bsize >= 0 && j + bsize < dst.cols && i - bsize >= 0 && i + bsize < dst.rows)
|
||||
{
|
||||
for(float y = -search_radius; y <= search_radius; ++y)
|
||||
for(float x = -search_radius; x <= search_radius; ++x)
|
||||
{
|
||||
float dist2 = 0;
|
||||
for(float ty = -block_radius; ty <= block_radius; ++ty)
|
||||
for(float tx = -block_radius; tx <= block_radius; ++tx)
|
||||
{
|
||||
value_type bv = saturate_cast<value_type>(src(i + y + ty, j + x + tx));
|
||||
value_type av = saturate_cast<value_type>(src(i + ty, j + tx));
|
||||
|
||||
dist2 += norm2(av - bv);
|
||||
}
|
||||
|
||||
float w = __expf(dist2 * noise_mult + (x * x + y * y) * minus_search_window2_inv);
|
||||
|
||||
/*if (i == 255 && j == 255)
|
||||
printf("%f %f\n", w, dist2 * minus_h2_inv + (x * x + y * y) * minus_search_window2_inv);*/
|
||||
|
||||
sum1 = sum1 + w * saturate_cast<value_type>(src(i + y, j + x));
|
||||
sum2 += w;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(float y = -search_radius; y <= search_radius; ++y)
|
||||
for(float x = -search_radius; x <= search_radius; ++x)
|
||||
{
|
||||
float dist2 = 0;
|
||||
for(float ty = -block_radius; ty <= block_radius; ++ty)
|
||||
for(float tx = -block_radius; tx <= block_radius; ++tx)
|
||||
{
|
||||
value_type bv = saturate_cast<value_type>(b.at(i + y + ty, j + x + tx, src));
|
||||
value_type av = saturate_cast<value_type>(b.at(i + ty, j + tx, src));
|
||||
dist2 += norm2(av - bv);
|
||||
}
|
||||
|
||||
float w = __expf(dist2 * noise_mult + (x * x + y * y) * minus_search_window2_inv);
|
||||
|
||||
sum1 = sum1 + w * saturate_cast<value_type>(b.at(i + y, j + x, src));
|
||||
sum2 += w;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dst(i, j) = saturate_cast<T>(sum1 / sum2);
|
||||
|
||||
}
|
||||
|
||||
template<typename T, template <typename> class B>
|
||||
void nlm_caller(const PtrStepSzb src, PtrStepSzb dst, int search_radius, int block_radius, float h, cudaStream_t stream)
|
||||
{
|
||||
dim3 block (32, 8);
|
||||
dim3 grid (divUp (src.cols, block.x), divUp (src.rows, block.y));
|
||||
|
||||
B<T> b(src.rows, src.cols);
|
||||
|
||||
int block_window = 2 * block_radius + 1;
|
||||
float minus_h2_inv = -1.f/(h * h * VecTraits<T>::cn);
|
||||
float noise_mult = minus_h2_inv/(block_window * block_window);
|
||||
|
||||
cudaSafeCall( cudaFuncSetCacheConfig (nlm_kernel<T, B<T> >, cudaFuncCachePreferL1) );
|
||||
nlm_kernel<<<grid, block>>>((PtrStepSz<T>)src, (PtrStepSz<T>)dst, b, search_radius, block_radius, noise_mult);
|
||||
cudaSafeCall ( cudaGetLastError () );
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall( cudaDeviceSynchronize() );
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void nlm_bruteforce_gpu(const PtrStepSzb& src, PtrStepSzb dst, int search_radius, int block_radius, float h, int borderMode, cudaStream_t stream)
|
||||
{
|
||||
typedef void (*func_t)(const PtrStepSzb src, PtrStepSzb dst, int search_radius, int block_radius, float h, cudaStream_t stream);
|
||||
|
||||
static func_t funcs[] =
|
||||
{
|
||||
nlm_caller<T, BrdConstant>,
|
||||
nlm_caller<T, BrdReplicate>,
|
||||
nlm_caller<T, BrdReflect>,
|
||||
nlm_caller<T, BrdWrap>,
|
||||
nlm_caller<T, BrdReflect101>
|
||||
};
|
||||
funcs[borderMode](src, dst, search_radius, block_radius, h, stream);
|
||||
}
|
||||
|
||||
template void nlm_bruteforce_gpu<uchar>(const PtrStepSzb&, PtrStepSzb, int, int, float, int, cudaStream_t);
|
||||
template void nlm_bruteforce_gpu<uchar2>(const PtrStepSzb&, PtrStepSzb, int, int, float, int, cudaStream_t);
|
||||
template void nlm_bruteforce_gpu<uchar3>(const PtrStepSzb&, PtrStepSzb, int, int, float, int, cudaStream_t);
|
||||
}
|
||||
}}}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//// Non Local Means Denosing (fast approximate version)
|
||||
|
||||
namespace cv { namespace cuda { namespace device
|
||||
{
|
||||
namespace imgproc
|
||||
{
|
||||
|
||||
template <int cn> struct Unroll;
|
||||
template <> struct Unroll<1>
|
||||
{
|
||||
template <int BLOCK_SIZE>
|
||||
static __device__ __forceinline__ thrust::tuple<volatile float*, volatile float*> smem_tuple(float* smem)
|
||||
{
|
||||
return cv::cuda::device::smem_tuple(smem, smem + BLOCK_SIZE);
|
||||
}
|
||||
|
||||
static __device__ __forceinline__ thrust::tuple<float&, float&> tie(float& val1, float& val2)
|
||||
{
|
||||
return thrust::tie(val1, val2);
|
||||
}
|
||||
|
||||
static __device__ __forceinline__ const thrust::tuple<plus<float>, plus<float> > op()
|
||||
{
|
||||
plus<float> op;
|
||||
return { op, op };
|
||||
}
|
||||
};
|
||||
template <> struct Unroll<2>
|
||||
{
|
||||
template <int BLOCK_SIZE>
|
||||
static __device__ __forceinline__ thrust::tuple<volatile float*, volatile float*, volatile float*> smem_tuple(float* smem)
|
||||
{
|
||||
return cv::cuda::device::smem_tuple(smem, smem + BLOCK_SIZE, smem + 2 * BLOCK_SIZE);
|
||||
}
|
||||
|
||||
static __device__ __forceinline__ thrust::tuple<float&, float&, float&> tie(float& val1, float2& val2)
|
||||
{
|
||||
return thrust::tie(val1, val2.x, val2.y);
|
||||
}
|
||||
|
||||
static __device__ __forceinline__ const thrust::tuple<plus<float>, plus<float>, plus<float> > op()
|
||||
{
|
||||
plus<float> op;
|
||||
return { op, op, op };
|
||||
}
|
||||
};
|
||||
template <> struct Unroll<3>
|
||||
{
|
||||
template <int BLOCK_SIZE>
|
||||
static __device__ __forceinline__ thrust::tuple<volatile float*, volatile float*, volatile float*, volatile float*> smem_tuple(float* smem)
|
||||
{
|
||||
return cv::cuda::device::smem_tuple(smem, smem + BLOCK_SIZE, smem + 2 * BLOCK_SIZE, smem + 3 * BLOCK_SIZE);
|
||||
}
|
||||
|
||||
static __device__ __forceinline__ thrust::tuple<float&, float&, float&, float&> tie(float& val1, float3& val2)
|
||||
{
|
||||
return thrust::tie(val1, val2.x, val2.y, val2.z);
|
||||
}
|
||||
|
||||
static __device__ __forceinline__ const thrust::tuple<plus<float>, plus<float>, plus<float>, plus<float> > op()
|
||||
{
|
||||
plus<float> op;
|
||||
return { op, op, op, op };
|
||||
}
|
||||
};
|
||||
template <> struct Unroll<4>
|
||||
{
|
||||
template <int BLOCK_SIZE>
|
||||
static __device__ __forceinline__ thrust::tuple<volatile float*, volatile float*, volatile float*, volatile float*, volatile float*> smem_tuple(float* smem)
|
||||
{
|
||||
return cv::cuda::device::smem_tuple(smem, smem + BLOCK_SIZE, smem + 2 * BLOCK_SIZE, smem + 3 * BLOCK_SIZE, smem + 4 * BLOCK_SIZE);
|
||||
}
|
||||
|
||||
static __device__ __forceinline__ thrust::tuple<float&, float&, float&, float&, float&> tie(float& val1, float4& val2)
|
||||
{
|
||||
return thrust::tie(val1, val2.x, val2.y, val2.z, val2.w);
|
||||
}
|
||||
|
||||
static __device__ __forceinline__ const thrust::tuple<plus<float>, plus<float>, plus<float>, plus<float>, plus<float> > op()
|
||||
{
|
||||
plus<float> op;
|
||||
return thrust::make_tuple(op, op, op, op, op);
|
||||
}
|
||||
};
|
||||
|
||||
__device__ __forceinline__ int calcDist(const uchar& a, const uchar& b) { return (a-b)*(a-b); }
|
||||
__device__ __forceinline__ int calcDist(const uchar2& a, const uchar2& b) { return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y); }
|
||||
__device__ __forceinline__ int calcDist(const uchar3& a, const uchar3& b) { return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) + (a.z-b.z)*(a.z-b.z); }
|
||||
|
||||
template <class T> struct FastNonLocalMeans
|
||||
{
|
||||
enum
|
||||
{
|
||||
CTA_SIZE = 128,
|
||||
|
||||
TILE_COLS = 128,
|
||||
TILE_ROWS = 32,
|
||||
|
||||
STRIDE = CTA_SIZE
|
||||
};
|
||||
|
||||
struct plus
|
||||
{
|
||||
__device__ __forceinline__ float operator()(float v1, float v2) const { return v1 + v2; }
|
||||
};
|
||||
|
||||
int search_radius;
|
||||
int block_radius;
|
||||
|
||||
int search_window;
|
||||
int block_window;
|
||||
float minus_h2_inv;
|
||||
|
||||
FastNonLocalMeans(int search_window_, int block_window_, float h) : search_radius(search_window_/2), block_radius(block_window_/2),
|
||||
search_window(search_window_), block_window(block_window_), minus_h2_inv(-1.f/(h * h * VecTraits<T>::cn)) {}
|
||||
|
||||
PtrStep<T> src;
|
||||
mutable PtrStepi buffer;
|
||||
|
||||
__device__ __forceinline__ void initSums_BruteForce(int i, int j, int* dist_sums, PtrStepi& col_sums, PtrStepi& up_col_sums) const
|
||||
{
|
||||
for(int index = threadIdx.x; index < search_window * search_window; index += STRIDE)
|
||||
{
|
||||
dist_sums[index] = 0;
|
||||
|
||||
for(int tx = 0; tx < block_window; ++tx)
|
||||
col_sums(tx, index) = 0;
|
||||
|
||||
int y = index / search_window;
|
||||
int x = index - y * search_window;
|
||||
|
||||
int ay = i;
|
||||
int ax = j;
|
||||
|
||||
int by = i + y - search_radius;
|
||||
int bx = j + x - search_radius;
|
||||
|
||||
#if 1
|
||||
for (int tx = -block_radius; tx <= block_radius; ++tx)
|
||||
{
|
||||
int col_sum = 0;
|
||||
for (int ty = -block_radius; ty <= block_radius; ++ty)
|
||||
{
|
||||
int dist = calcDist(src(ay + ty, ax + tx), src(by + ty, bx + tx));
|
||||
|
||||
dist_sums[index] += dist;
|
||||
col_sum += dist;
|
||||
}
|
||||
col_sums(tx + block_radius, index) = col_sum;
|
||||
}
|
||||
#else
|
||||
for (int ty = -block_radius; ty <= block_radius; ++ty)
|
||||
for (int tx = -block_radius; tx <= block_radius; ++tx)
|
||||
{
|
||||
int dist = calcDist(src(ay + ty, ax + tx), src(by + ty, bx + tx));
|
||||
|
||||
dist_sums[index] += dist;
|
||||
col_sums(tx + block_radius, index) += dist;
|
||||
}
|
||||
#endif
|
||||
|
||||
up_col_sums(j, index) = col_sums(block_window - 1, index);
|
||||
}
|
||||
}
|
||||
|
||||
__device__ __forceinline__ void shiftRight_FirstRow(int i, int j, int first, int* dist_sums, PtrStepi& col_sums, PtrStepi& up_col_sums) const
|
||||
{
|
||||
for(int index = threadIdx.x; index < search_window * search_window; index += STRIDE)
|
||||
{
|
||||
int y = index / search_window;
|
||||
int x = index - y * search_window;
|
||||
|
||||
int ay = i;
|
||||
int ax = j + block_radius;
|
||||
|
||||
int by = i + y - search_radius;
|
||||
int bx = j + x - search_radius + block_radius;
|
||||
|
||||
int col_sum = 0;
|
||||
|
||||
for (int ty = -block_radius; ty <= block_radius; ++ty)
|
||||
col_sum += calcDist(src(ay + ty, ax), src(by + ty, bx));
|
||||
|
||||
dist_sums[index] += col_sum - col_sums(first, index);
|
||||
|
||||
col_sums(first, index) = col_sum;
|
||||
up_col_sums(j, index) = col_sum;
|
||||
}
|
||||
}
|
||||
|
||||
__device__ __forceinline__ void shiftRight_UpSums(int i, int j, int first, int* dist_sums, PtrStepi& col_sums, PtrStepi& up_col_sums) const
|
||||
{
|
||||
int ay = i;
|
||||
int ax = j + block_radius;
|
||||
|
||||
T a_up = src(ay - block_radius - 1, ax);
|
||||
T a_down = src(ay + block_radius, ax);
|
||||
|
||||
for(int index = threadIdx.x; index < search_window * search_window; index += STRIDE)
|
||||
{
|
||||
int y = index / search_window;
|
||||
int x = index - y * search_window;
|
||||
|
||||
int by = i + y - search_radius;
|
||||
int bx = j + x - search_radius + block_radius;
|
||||
|
||||
T b_up = src(by - block_radius - 1, bx);
|
||||
T b_down = src(by + block_radius, bx);
|
||||
|
||||
int col_sum = up_col_sums(j, index) + calcDist(a_down, b_down) - calcDist(a_up, b_up);
|
||||
|
||||
dist_sums[index] += col_sum - col_sums(first, index);
|
||||
col_sums(first, index) = col_sum;
|
||||
up_col_sums(j, index) = col_sum;
|
||||
}
|
||||
}
|
||||
|
||||
__device__ __forceinline__ void convolve_window(int i, int j, const int* dist_sums, T& dst) const
|
||||
{
|
||||
typedef typename TypeVec<float, VecTraits<T>::cn>::vec_type sum_type;
|
||||
|
||||
float weights_sum = 0;
|
||||
sum_type sum = VecTraits<sum_type>::all(0);
|
||||
|
||||
float bw2_inv = 1.f/(block_window * block_window);
|
||||
|
||||
int sx = j - search_radius;
|
||||
int sy = i - search_radius;
|
||||
|
||||
for(int index = threadIdx.x; index < search_window * search_window; index += STRIDE)
|
||||
{
|
||||
int y = index / search_window;
|
||||
int x = index - y * search_window;
|
||||
|
||||
float avg_dist = dist_sums[index] * bw2_inv;
|
||||
float weight = __expf(avg_dist * minus_h2_inv);
|
||||
weights_sum += weight;
|
||||
|
||||
sum = sum + weight * saturate_cast<sum_type>(src(sy + y, sx + x));
|
||||
}
|
||||
|
||||
__shared__ float cta_buffer[CTA_SIZE * (VecTraits<T>::cn + 1)];
|
||||
|
||||
reduce<CTA_SIZE>(Unroll<VecTraits<T>::cn>::template smem_tuple<CTA_SIZE>(cta_buffer),
|
||||
Unroll<VecTraits<T>::cn>::tie(weights_sum, sum),
|
||||
threadIdx.x,
|
||||
Unroll<VecTraits<T>::cn>::op());
|
||||
|
||||
if (threadIdx.x == 0)
|
||||
dst = saturate_cast<T>(sum / weights_sum);
|
||||
}
|
||||
|
||||
__device__ __forceinline__ void operator()(PtrStepSz<T>& dst) const
|
||||
{
|
||||
int tbx = blockIdx.x * TILE_COLS;
|
||||
int tby = blockIdx.y * TILE_ROWS;
|
||||
|
||||
int tex = ::min(tbx + TILE_COLS, dst.cols);
|
||||
int tey = ::min(tby + TILE_ROWS, dst.rows);
|
||||
|
||||
PtrStepi col_sums;
|
||||
col_sums.data = buffer.ptr(dst.cols + blockIdx.x * block_window) + blockIdx.y * search_window * search_window;
|
||||
col_sums.step = buffer.step;
|
||||
|
||||
PtrStepi up_col_sums;
|
||||
up_col_sums.data = buffer.data + blockIdx.y * search_window * search_window;
|
||||
up_col_sums.step = buffer.step;
|
||||
|
||||
extern __shared__ int dist_sums[]; //search_window * search_window
|
||||
|
||||
int first = 0;
|
||||
|
||||
for (int i = tby; i < tey; ++i)
|
||||
for (int j = tbx; j < tex; ++j)
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
if (j == tbx)
|
||||
{
|
||||
initSums_BruteForce(i, j, dist_sums, col_sums, up_col_sums);
|
||||
first = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i == tby)
|
||||
shiftRight_FirstRow(i, j, first, dist_sums, col_sums, up_col_sums);
|
||||
else
|
||||
shiftRight_UpSums(i, j, first, dist_sums, col_sums, up_col_sums);
|
||||
|
||||
first = (first + 1) % block_window;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
convolve_window(i, j, dist_sums, dst(i, j));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
__global__ void fast_nlm_kernel(const FastNonLocalMeans<T> fnlm, PtrStepSz<T> dst) { fnlm(dst); }
|
||||
|
||||
void nln_fast_get_buffer_size(const PtrStepSzb& src, int search_window, int block_window, int& buffer_cols, int& buffer_rows)
|
||||
{
|
||||
typedef FastNonLocalMeans<uchar> FNLM;
|
||||
dim3 grid(divUp(src.cols, FNLM::TILE_COLS), divUp(src.rows, FNLM::TILE_ROWS));
|
||||
|
||||
buffer_cols = search_window * search_window * grid.y;
|
||||
buffer_rows = src.cols + block_window * grid.x;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void nlm_fast_gpu(const PtrStepSzb& src, PtrStepSzb dst, PtrStepi buffer,
|
||||
int search_window, int block_window, float h, cudaStream_t stream)
|
||||
{
|
||||
typedef FastNonLocalMeans<T> FNLM;
|
||||
FNLM fnlm(search_window, block_window, h);
|
||||
|
||||
fnlm.src = (PtrStepSz<T>)src;
|
||||
fnlm.buffer = buffer;
|
||||
|
||||
dim3 block(FNLM::CTA_SIZE, 1);
|
||||
dim3 grid(divUp(src.cols, FNLM::TILE_COLS), divUp(src.rows, FNLM::TILE_ROWS));
|
||||
int smem = search_window * search_window * sizeof(int);
|
||||
|
||||
|
||||
fast_nlm_kernel<<<grid, block, smem>>>(fnlm, (PtrStepSz<T>)dst);
|
||||
cudaSafeCall ( cudaGetLastError () );
|
||||
if (stream == 0)
|
||||
cudaSafeCall( cudaDeviceSynchronize() );
|
||||
}
|
||||
|
||||
template void nlm_fast_gpu<uchar>(const PtrStepSzb&, PtrStepSzb, PtrStepi, int, int, float, cudaStream_t);
|
||||
template void nlm_fast_gpu<uchar2>(const PtrStepSzb&, PtrStepSzb, PtrStepi, int, int, float, cudaStream_t);
|
||||
template void nlm_fast_gpu<uchar3>(const PtrStepSzb&, PtrStepSzb, PtrStepi, int, int, float, cudaStream_t);
|
||||
|
||||
|
||||
|
||||
__global__ void fnlm_split_kernel(const PtrStepSz<uchar3> lab, PtrStepb l, PtrStep<uchar2> ab)
|
||||
{
|
||||
int x = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
int y = threadIdx.y + blockIdx.y * blockDim.y;
|
||||
|
||||
if (x < lab.cols && y < lab.rows)
|
||||
{
|
||||
uchar3 p = lab(y, x);
|
||||
ab(y,x) = make_uchar2(p.y, p.z);
|
||||
l(y,x) = p.x;
|
||||
}
|
||||
}
|
||||
|
||||
void fnlm_split_channels(const PtrStepSz<uchar3>& lab, PtrStepb l, PtrStep<uchar2> ab, cudaStream_t stream)
|
||||
{
|
||||
dim3 b(32, 8);
|
||||
dim3 g(divUp(lab.cols, b.x), divUp(lab.rows, b.y));
|
||||
|
||||
fnlm_split_kernel<<<g, b>>>(lab, l, ab);
|
||||
cudaSafeCall ( cudaGetLastError () );
|
||||
if (stream == 0)
|
||||
cudaSafeCall( cudaDeviceSynchronize() );
|
||||
}
|
||||
|
||||
__global__ void fnlm_merge_kernel(const PtrStepb l, const PtrStep<uchar2> ab, PtrStepSz<uchar3> lab)
|
||||
{
|
||||
int x = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
int y = threadIdx.y + blockIdx.y * blockDim.y;
|
||||
|
||||
if (x < lab.cols && y < lab.rows)
|
||||
{
|
||||
uchar2 p = ab(y, x);
|
||||
lab(y, x) = make_uchar3(l(y, x), p.x, p.y);
|
||||
}
|
||||
}
|
||||
|
||||
void fnlm_merge_channels(const PtrStepb& l, const PtrStep<uchar2>& ab, PtrStepSz<uchar3> lab, cudaStream_t stream)
|
||||
{
|
||||
dim3 b(32, 8);
|
||||
dim3 g(divUp(lab.cols, b.x), divUp(lab.rows, b.y));
|
||||
|
||||
fnlm_merge_kernel<<<g, b>>>(l, ab, lab);
|
||||
cudaSafeCall ( cudaGetLastError () );
|
||||
if (stream == 0)
|
||||
cudaSafeCall( cudaDeviceSynchronize() );
|
||||
}
|
||||
}
|
||||
}}}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*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 OpenCV Foundation 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 <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace cv{
|
||||
|
||||
class AddFloatToCharScaled{
|
||||
public:
|
||||
AddFloatToCharScaled(double scale):_scale(scale){}
|
||||
inline double operator()(double a,uchar b){
|
||||
return a+_scale*((double)b);
|
||||
}
|
||||
private:
|
||||
double _scale;
|
||||
};
|
||||
|
||||
using std::transform;
|
||||
|
||||
void denoise_TVL1(const std::vector<Mat>& observations,Mat& result, double lambda, int niters){
|
||||
|
||||
CV_Assert(observations.size()>0 && niters>0 && lambda>0);
|
||||
|
||||
const double L2 = 8.0, tau = 0.02, sigma = 1./(L2*tau), theta = 1.0;
|
||||
double clambda = (double)lambda;
|
||||
double s=0;
|
||||
const int workdepth = CV_64F;
|
||||
|
||||
int i, x, y, rows=observations[0].rows, cols=observations[0].cols,count;
|
||||
for(i=1;i<(int)observations.size();i++){
|
||||
CV_Assert(observations[i].rows==rows && observations[i].cols==cols);
|
||||
}
|
||||
|
||||
Mat X, P = Mat::zeros(rows, cols, CV_MAKETYPE(workdepth, 2));
|
||||
observations[0].convertTo(X, workdepth, 1./255);
|
||||
std::vector< Mat_<double> > Rs(observations.size());
|
||||
for(count=0;count<(int)Rs.size();count++){
|
||||
Rs[count]=Mat::zeros(rows,cols,workdepth);
|
||||
}
|
||||
|
||||
for( i = 0; i < niters; i++ )
|
||||
{
|
||||
double currsigma = i == 0 ? 1 + sigma : sigma;
|
||||
|
||||
// P_ = P + sigma*nabla(X)
|
||||
// P(x,y) = P_(x,y)/max(||P(x,y)||,1)
|
||||
for( y = 0; y < rows; y++ )
|
||||
{
|
||||
const double* x_curr = X.ptr<double>(y);
|
||||
const double* x_next = X.ptr<double>(std::min(y+1, rows-1));
|
||||
Point2d* p_curr = P.ptr<Point2d>(y);
|
||||
double dx, dy, m;
|
||||
for( x = 0; x < cols-1; x++ )
|
||||
{
|
||||
dx = (x_curr[x+1] - x_curr[x])*currsigma + p_curr[x].x;
|
||||
dy = (x_next[x] - x_curr[x])*currsigma + p_curr[x].y;
|
||||
m = 1.0/std::max(std::sqrt(dx*dx + dy*dy), 1.0);
|
||||
p_curr[x].x = dx*m;
|
||||
p_curr[x].y = dy*m;
|
||||
}
|
||||
dy = (x_next[x] - x_curr[x])*currsigma + p_curr[x].y;
|
||||
m = 1.0/std::max(std::abs(dy), 1.0);
|
||||
p_curr[x].x = 0.0;
|
||||
p_curr[x].y = dy*m;
|
||||
}
|
||||
|
||||
|
||||
//Rs = clip(Rs + sigma*(X-imgs), -clambda, clambda)
|
||||
for(count=0;count<(int)Rs.size();count++){
|
||||
transform<MatIterator_<double>,MatConstIterator_<uchar>,MatIterator_<double>,AddFloatToCharScaled>(
|
||||
Rs[count].begin(),Rs[count].end(),observations[count].begin<uchar>(),
|
||||
Rs[count].begin(),AddFloatToCharScaled(-sigma/255.0));
|
||||
Rs[count]+=sigma*X;
|
||||
min(Rs[count],clambda,Rs[count]);
|
||||
max(Rs[count],-clambda,Rs[count]);
|
||||
}
|
||||
|
||||
for( y = 0; y < rows; y++ )
|
||||
{
|
||||
double* x_curr = X.ptr<double>(y);
|
||||
const Point2d* p_curr = P.ptr<Point2d>(y);
|
||||
const Point2d* p_prev = P.ptr<Point2d>(std::max(y - 1, 0));
|
||||
|
||||
// X1 = X + tau*(-nablaT(P))
|
||||
x = 0;
|
||||
s=0.0;
|
||||
for(count=0;count<(int)Rs.size();count++){
|
||||
s=s+Rs[count](y,x);
|
||||
}
|
||||
double x_new = x_curr[x] + tau*(p_curr[x].y - p_prev[x].y)-tau*s;
|
||||
// X = X2 + theta*(X2 - X)
|
||||
x_curr[x] = x_new + theta*(x_new - x_curr[x]);
|
||||
|
||||
|
||||
for(x = 1; x < cols; x++ )
|
||||
{
|
||||
s=0.0;
|
||||
for(count=0;count<(int)Rs.size();count++){
|
||||
s+=Rs[count](y,x);
|
||||
}
|
||||
// X1 = X + tau*(-nablaT(P))
|
||||
x_new = x_curr[x] + tau*(p_curr[x].x - p_curr[x-1].x + p_curr[x].y - p_prev[x].y)-tau*s;
|
||||
// X = X2 + theta*(X2 - X)
|
||||
x_curr[x] = x_new + theta*(x_new - x_curr[x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.create(X.rows,X.cols,CV_8U);
|
||||
X.convertTo(result, CV_8U, 255);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
/*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.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective icvers.
|
||||
//
|
||||
// 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 Intel Corporation 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 "fast_nlmeans_denoising_invoker.hpp"
|
||||
#include "fast_nlmeans_multi_denoising_invoker.hpp"
|
||||
#include "fast_nlmeans_denoising_opencl.hpp"
|
||||
|
||||
template<typename ST, typename IT, typename UIT, typename D>
|
||||
static void fastNlMeansDenoising_( const Mat& src, Mat& dst, const std::vector<float>& h,
|
||||
int templateWindowSize, int searchWindowSize)
|
||||
{
|
||||
int hn = (int)h.size();
|
||||
double granularity = (double)std::max(1., (double)dst.total()/(1 << 17));
|
||||
|
||||
switch (CV_MAT_CN(src.type())) {
|
||||
case 1:
|
||||
parallel_for_(cv::Range(0, src.rows),
|
||||
FastNlMeansDenoisingInvoker<ST, IT, UIT, D, int>(
|
||||
src, dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
break;
|
||||
case 2:
|
||||
if (hn == 1)
|
||||
parallel_for_(cv::Range(0, src.rows),
|
||||
FastNlMeansDenoisingInvoker<Vec<ST, 2>, IT, UIT, D, int>(
|
||||
src, dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
else
|
||||
parallel_for_(cv::Range(0, src.rows),
|
||||
FastNlMeansDenoisingInvoker<Vec<ST, 2>, IT, UIT, D, Vec2i>(
|
||||
src, dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
break;
|
||||
case 3:
|
||||
if (hn == 1)
|
||||
parallel_for_(cv::Range(0, src.rows),
|
||||
FastNlMeansDenoisingInvoker<Vec<ST, 3>, IT, UIT, D, int>(
|
||||
src, dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
else
|
||||
parallel_for_(cv::Range(0, src.rows),
|
||||
FastNlMeansDenoisingInvoker<Vec<ST, 3>, IT, UIT, D, Vec3i>(
|
||||
src, dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
break;
|
||||
case 4:
|
||||
if (hn == 1)
|
||||
parallel_for_(cv::Range(0, src.rows),
|
||||
FastNlMeansDenoisingInvoker<Vec<ST, 4>, IT, UIT, D, int>(
|
||||
src, dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
else
|
||||
parallel_for_(cv::Range(0, src.rows),
|
||||
FastNlMeansDenoisingInvoker<Vec<ST, 4>, IT, UIT, D, Vec4i>(
|
||||
src, dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg,
|
||||
"Unsupported number of channels! Only 1, 2, 3, and 4 are supported");
|
||||
}
|
||||
}
|
||||
|
||||
void cv::fastNlMeansDenoising( InputArray _src, OutputArray _dst, float h,
|
||||
int templateWindowSize, int searchWindowSize)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
fastNlMeansDenoising(_src, _dst, std::vector<float>(1, h),
|
||||
templateWindowSize, searchWindowSize);
|
||||
}
|
||||
|
||||
void cv::fastNlMeansDenoising( InputArray _src, OutputArray _dst, const std::vector<float>& h,
|
||||
int templateWindowSize, int searchWindowSize, int normType)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
int hn = (int)h.size(), type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
|
||||
CV_Assert(!_src.empty());
|
||||
CV_Assert(hn == 1 || hn == cn);
|
||||
|
||||
Size src_size = _src.size();
|
||||
CV_OCL_RUN(_src.dims() <= 2 && (_src.isUMat() || _dst.isUMat()) &&
|
||||
src_size.width > 5 && src_size.height > 5, // low accuracy on small sizes
|
||||
ocl_fastNlMeansDenoising(_src, _dst, &h[0], hn,
|
||||
templateWindowSize, searchWindowSize, normType))
|
||||
|
||||
Mat src = _src.getMat();
|
||||
_dst.create(src_size, src.type());
|
||||
Mat dst = _dst.getMat();
|
||||
|
||||
switch (normType) {
|
||||
case NORM_L2:
|
||||
switch (depth) {
|
||||
case CV_8U:
|
||||
fastNlMeansDenoising_<uchar, int, unsigned, DistSquared>(src, dst, h,
|
||||
templateWindowSize,
|
||||
searchWindowSize);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg,
|
||||
"Unsupported depth! Only CV_8U is supported for NORM_L2");
|
||||
}
|
||||
break;
|
||||
case NORM_L1:
|
||||
switch (depth) {
|
||||
case CV_8U:
|
||||
fastNlMeansDenoising_<uchar, int, unsigned, DistAbs>(src, dst, h,
|
||||
templateWindowSize,
|
||||
searchWindowSize);
|
||||
break;
|
||||
case CV_16U:
|
||||
fastNlMeansDenoising_<ushort, int64, uint64, DistAbs>(src, dst, h,
|
||||
templateWindowSize,
|
||||
searchWindowSize);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg,
|
||||
"Unsupported depth! Only CV_8U and CV_16U are supported for NORM_L1");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg,
|
||||
"Unsupported norm type! Only NORM_L2 and NORM_L1 are supported");
|
||||
}
|
||||
}
|
||||
|
||||
void cv::fastNlMeansDenoisingColored( InputArray _src, OutputArray _dst,
|
||||
float h, float hForColorComponents,
|
||||
int templateWindowSize, int searchWindowSize)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
|
||||
Size src_size = _src.size();
|
||||
if (type != CV_8UC3 && type != CV_8UC4)
|
||||
{
|
||||
CV_Error(Error::StsBadArg, "Type of input image should be CV_8UC3 or CV_8UC4!");
|
||||
return;
|
||||
}
|
||||
|
||||
CV_OCL_RUN(_src.dims() <= 2 && (_dst.isUMat() || _src.isUMat()) &&
|
||||
src_size.width > 5 && src_size.height > 5, // low accuracy on small sizes
|
||||
ocl_fastNlMeansDenoisingColored(_src, _dst, h, hForColorComponents,
|
||||
templateWindowSize, searchWindowSize))
|
||||
|
||||
Mat src = _src.getMat();
|
||||
_dst.create(src_size, type);
|
||||
Mat dst = _dst.getMat();
|
||||
|
||||
Mat src_lab;
|
||||
cvtColor(src, src_lab, COLOR_LBGR2Lab);
|
||||
|
||||
Mat l(src_size, CV_MAKE_TYPE(depth, 1));
|
||||
Mat ab(src_size, CV_MAKE_TYPE(depth, 2));
|
||||
Mat l_ab[] = { l, ab };
|
||||
int from_to[] = { 0,0, 1,1, 2,2 };
|
||||
mixChannels(&src_lab, 1, l_ab, 2, from_to, 3);
|
||||
|
||||
fastNlMeansDenoising(l, l, h, templateWindowSize, searchWindowSize);
|
||||
fastNlMeansDenoising(ab, ab, hForColorComponents, templateWindowSize, searchWindowSize);
|
||||
|
||||
Mat l_ab_denoised[] = { l, ab };
|
||||
Mat dst_lab(src_size, CV_MAKE_TYPE(depth, 3));
|
||||
mixChannels(l_ab_denoised, 2, &dst_lab, 1, from_to, 3);
|
||||
|
||||
cvtColor(dst_lab, dst, COLOR_Lab2LBGR, cn);
|
||||
}
|
||||
|
||||
static void fastNlMeansDenoisingMultiCheckPreconditions(
|
||||
const std::vector<Mat>& srcImgs,
|
||||
int imgToDenoiseIndex, int temporalWindowSize,
|
||||
int templateWindowSize, int searchWindowSize)
|
||||
{
|
||||
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!");
|
||||
}
|
||||
|
||||
if (temporalWindowSize % 2 == 0 ||
|
||||
searchWindowSize % 2 == 0 ||
|
||||
templateWindowSize % 2 == 0) {
|
||||
CV_Error(Error::StsBadArg, "All windows sizes should be odd!");
|
||||
}
|
||||
|
||||
int temporalWindowHalfSize = temporalWindowSize / 2;
|
||||
if (imgToDenoiseIndex - temporalWindowHalfSize < 0 ||
|
||||
imgToDenoiseIndex + temporalWindowHalfSize >= src_imgs_size)
|
||||
{
|
||||
CV_Error(Error::StsBadArg,
|
||||
"imgToDenoiseIndex and temporalWindowSize "
|
||||
"should be chosen corresponding srcImgs size!");
|
||||
}
|
||||
|
||||
for (int i = 1; i < src_imgs_size; i++)
|
||||
if (srcImgs[0].size() != srcImgs[i].size() || srcImgs[0].type() != srcImgs[i].type())
|
||||
{
|
||||
CV_Error(Error::StsBadArg, "Input images should have the same size and type!");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ST, typename IT, typename UIT, typename D>
|
||||
static void fastNlMeansDenoisingMulti_( const std::vector<Mat>& srcImgs, Mat& dst,
|
||||
int imgToDenoiseIndex, int temporalWindowSize,
|
||||
const std::vector<float>& h,
|
||||
int templateWindowSize, int searchWindowSize)
|
||||
{
|
||||
int hn = (int)h.size();
|
||||
double granularity = (double)std::max(1., (double)dst.total()/(1 << 16));
|
||||
|
||||
switch (CV_MAT_CN(srcImgs[0].type())) {
|
||||
case 1:
|
||||
parallel_for_(cv::Range(0, srcImgs[0].rows),
|
||||
FastNlMeansMultiDenoisingInvoker<ST, IT, UIT, D, int>(
|
||||
srcImgs, imgToDenoiseIndex, temporalWindowSize,
|
||||
dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
break;
|
||||
case 2:
|
||||
if (hn == 1)
|
||||
parallel_for_(cv::Range(0, srcImgs[0].rows),
|
||||
FastNlMeansMultiDenoisingInvoker<Vec<ST, 2>, IT, UIT, D, int>(
|
||||
srcImgs, imgToDenoiseIndex, temporalWindowSize,
|
||||
dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
else
|
||||
parallel_for_(cv::Range(0, srcImgs[0].rows),
|
||||
FastNlMeansMultiDenoisingInvoker<Vec<ST, 2>, IT, UIT, D, Vec2i>(
|
||||
srcImgs, imgToDenoiseIndex, temporalWindowSize,
|
||||
dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
break;
|
||||
case 3:
|
||||
if (hn == 1)
|
||||
parallel_for_(cv::Range(0, srcImgs[0].rows),
|
||||
FastNlMeansMultiDenoisingInvoker<Vec<ST, 3>, IT, UIT, D, int>(
|
||||
srcImgs, imgToDenoiseIndex, temporalWindowSize,
|
||||
dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
else
|
||||
parallel_for_(cv::Range(0, srcImgs[0].rows),
|
||||
FastNlMeansMultiDenoisingInvoker<Vec<ST, 3>, IT, UIT, D, Vec3i>(
|
||||
srcImgs, imgToDenoiseIndex, temporalWindowSize,
|
||||
dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
break;
|
||||
case 4:
|
||||
if (hn == 1)
|
||||
parallel_for_(cv::Range(0, srcImgs[0].rows),
|
||||
FastNlMeansMultiDenoisingInvoker<Vec<ST, 4>, IT, UIT, D, int>(
|
||||
srcImgs, imgToDenoiseIndex, temporalWindowSize,
|
||||
dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
else
|
||||
parallel_for_(cv::Range(0, srcImgs[0].rows),
|
||||
FastNlMeansMultiDenoisingInvoker<Vec<ST, 4>, IT, UIT, D, Vec4i>(
|
||||
srcImgs, imgToDenoiseIndex, temporalWindowSize,
|
||||
dst, templateWindowSize, searchWindowSize, &h[0]),
|
||||
granularity);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg,
|
||||
"Unsupported number of channels! Only 1, 2, 3, and 4 are supported");
|
||||
}
|
||||
}
|
||||
|
||||
void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs, OutputArray _dst,
|
||||
int imgToDenoiseIndex, int temporalWindowSize,
|
||||
float h, int templateWindowSize, int searchWindowSize)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
fastNlMeansDenoisingMulti(_srcImgs, _dst, imgToDenoiseIndex, temporalWindowSize,
|
||||
std::vector<float>(1, h), templateWindowSize, searchWindowSize);
|
||||
}
|
||||
|
||||
void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs, OutputArray _dst,
|
||||
int imgToDenoiseIndex, int temporalWindowSize,
|
||||
const std::vector<float>& h,
|
||||
int templateWindowSize, int searchWindowSize, int normType)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
std::vector<Mat> srcImgs;
|
||||
_srcImgs.getMatVector(srcImgs);
|
||||
|
||||
fastNlMeansDenoisingMultiCheckPreconditions(
|
||||
srcImgs, imgToDenoiseIndex,
|
||||
temporalWindowSize, templateWindowSize, searchWindowSize);
|
||||
|
||||
int hn = (int)h.size();
|
||||
int type = srcImgs[0].type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
|
||||
CV_Assert(hn == 1 || hn == cn);
|
||||
|
||||
_dst.create(srcImgs[0].size(), srcImgs[0].type());
|
||||
Mat dst = _dst.getMat();
|
||||
|
||||
switch (normType) {
|
||||
case NORM_L2:
|
||||
switch (depth) {
|
||||
case CV_8U:
|
||||
fastNlMeansDenoisingMulti_<uchar, int, unsigned,
|
||||
DistSquared>(srcImgs, dst,
|
||||
imgToDenoiseIndex, temporalWindowSize,
|
||||
h,
|
||||
templateWindowSize, searchWindowSize);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg,
|
||||
"Unsupported depth! Only CV_8U is supported for NORM_L2");
|
||||
}
|
||||
break;
|
||||
case NORM_L1:
|
||||
switch (depth) {
|
||||
case CV_8U:
|
||||
fastNlMeansDenoisingMulti_<uchar, int, unsigned,
|
||||
DistAbs>(srcImgs, dst,
|
||||
imgToDenoiseIndex, temporalWindowSize,
|
||||
h,
|
||||
templateWindowSize, searchWindowSize);
|
||||
break;
|
||||
case CV_16U:
|
||||
fastNlMeansDenoisingMulti_<ushort, int64, uint64,
|
||||
DistAbs>(srcImgs, dst,
|
||||
imgToDenoiseIndex, temporalWindowSize,
|
||||
h,
|
||||
templateWindowSize, searchWindowSize);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg,
|
||||
"Unsupported depth! Only CV_8U and CV_16U are supported for NORM_L1");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg,
|
||||
"Unsupported norm type! Only NORM_L2 and NORM_L1 are supported");
|
||||
}
|
||||
}
|
||||
|
||||
void cv::fastNlMeansDenoisingColoredMulti( InputArrayOfArrays _srcImgs, OutputArray _dst,
|
||||
int imgToDenoiseIndex, int temporalWindowSize,
|
||||
float h, float hForColorComponents,
|
||||
int templateWindowSize, int searchWindowSize)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
std::vector<Mat> srcImgs;
|
||||
_srcImgs.getMatVector(srcImgs);
|
||||
|
||||
fastNlMeansDenoisingMultiCheckPreconditions(
|
||||
srcImgs, imgToDenoiseIndex,
|
||||
temporalWindowSize, templateWindowSize, searchWindowSize);
|
||||
|
||||
_dst.create(srcImgs[0].size(), srcImgs[0].type());
|
||||
Mat dst = _dst.getMat();
|
||||
|
||||
int type = srcImgs[0].type(), depth = CV_MAT_DEPTH(type);
|
||||
int src_imgs_size = static_cast<int>(srcImgs.size());
|
||||
|
||||
if (type != CV_8UC3)
|
||||
{
|
||||
CV_Error(Error::StsBadArg, "Type of input images should be CV_8UC3!");
|
||||
return;
|
||||
}
|
||||
|
||||
int from_to[] = { 0,0, 1,1, 2,2 };
|
||||
|
||||
// TODO convert only required images
|
||||
std::vector<Mat> src_lab(src_imgs_size);
|
||||
std::vector<Mat> l(src_imgs_size);
|
||||
std::vector<Mat> ab(src_imgs_size);
|
||||
for (int i = 0; i < src_imgs_size; i++)
|
||||
{
|
||||
src_lab[i] = Mat::zeros(srcImgs[0].size(), type);
|
||||
l[i] = Mat::zeros(srcImgs[0].size(), CV_MAKE_TYPE(depth, 1));
|
||||
ab[i] = Mat::zeros(srcImgs[0].size(), CV_MAKE_TYPE(depth, 2));
|
||||
cvtColor(srcImgs[i], src_lab[i], COLOR_LBGR2Lab);
|
||||
|
||||
Mat l_ab[] = { l[i], ab[i] };
|
||||
mixChannels(&src_lab[i], 1, l_ab, 2, from_to, 3);
|
||||
}
|
||||
|
||||
Mat dst_l;
|
||||
Mat dst_ab;
|
||||
|
||||
fastNlMeansDenoisingMulti(
|
||||
l, dst_l, imgToDenoiseIndex, temporalWindowSize,
|
||||
h, templateWindowSize, searchWindowSize);
|
||||
|
||||
fastNlMeansDenoisingMulti(
|
||||
ab, dst_ab, imgToDenoiseIndex, temporalWindowSize,
|
||||
hForColorComponents, templateWindowSize, searchWindowSize);
|
||||
|
||||
Mat l_ab_denoised[] = { dst_l, dst_ab };
|
||||
Mat dst_lab(srcImgs[0].size(), srcImgs[0].type());
|
||||
mixChannels(l_ab_denoised, 2, &dst_lab, 1, from_to, 3);
|
||||
|
||||
cvtColor(dst_lab, dst, COLOR_Lab2LBGR);
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*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/cuda.hpp"
|
||||
|
||||
#include "opencv2/opencv_modules.hpp"
|
||||
|
||||
#if !defined (HAVE_CUDA) || !defined(HAVE_OPENCV_CUDAARITHM) || !defined(HAVE_OPENCV_CUDAIMGPROC)
|
||||
#include "opencv2/core/private/cuda_stubs.hpp"
|
||||
#else
|
||||
#include "opencv2/core/private.cuda.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENCV_CUDAARITHM
|
||||
# include "opencv2/cudaarithm.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENCV_CUDAIMGPROC
|
||||
# include "opencv2/cudaimgproc.hpp"
|
||||
#endif
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::cuda;
|
||||
|
||||
#if !defined (HAVE_CUDA) || !defined(HAVE_OPENCV_CUDAARITHM) || !defined(HAVE_OPENCV_CUDAIMGPROC)
|
||||
|
||||
void cv::cuda::nonLocalMeans(InputArray, OutputArray, float, int, int, int, Stream&) { throw_no_cuda(); }
|
||||
void cv::cuda::fastNlMeansDenoising(InputArray, OutputArray, float, int, int, Stream&) { throw_no_cuda(); }
|
||||
void cv::cuda::fastNlMeansDenoisingColored(InputArray, OutputArray, float, float, int, int, Stream&) { throw_no_cuda(); }
|
||||
|
||||
#else
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//// Non Local Means Denosing (brute force)
|
||||
|
||||
namespace cv { namespace cuda { namespace device
|
||||
{
|
||||
namespace imgproc
|
||||
{
|
||||
template<typename T>
|
||||
void nlm_bruteforce_gpu(const PtrStepSzb& src, PtrStepSzb dst, int search_radius, int block_radius, float h, int borderMode, cudaStream_t stream);
|
||||
}
|
||||
}}}
|
||||
|
||||
void cv::cuda::nonLocalMeans(InputArray _src, OutputArray _dst, float h, int search_window, int block_window, int borderMode, Stream& stream)
|
||||
{
|
||||
using cv::cuda::device::imgproc::nlm_bruteforce_gpu;
|
||||
typedef void (*func_t)(const PtrStepSzb& src, PtrStepSzb dst, int search_radius, int block_radius, float h, int borderMode, cudaStream_t stream);
|
||||
|
||||
static const func_t funcs[4] = { nlm_bruteforce_gpu<uchar>, nlm_bruteforce_gpu<uchar2>, nlm_bruteforce_gpu<uchar3>, 0/*nlm_bruteforce_gpu<uchar4>,*/ };
|
||||
|
||||
const GpuMat src = _src.getGpuMat();
|
||||
|
||||
CV_Assert(src.type() == CV_8U || src.type() == CV_8UC2 || src.type() == CV_8UC3);
|
||||
|
||||
const func_t func = funcs[src.channels() - 1];
|
||||
CV_Assert(func != 0);
|
||||
|
||||
int b = borderMode;
|
||||
CV_Assert(b == BORDER_REFLECT101 || b == BORDER_REPLICATE || b == BORDER_CONSTANT || b == BORDER_REFLECT || b == BORDER_WRAP);
|
||||
|
||||
_dst.create(src.size(), src.type());
|
||||
GpuMat dst = _dst.getGpuMat();
|
||||
|
||||
func(src, dst, search_window/2, block_window/2, h, borderMode, StreamAccessor::getStream(stream));
|
||||
}
|
||||
|
||||
namespace cv { namespace cuda { namespace device
|
||||
{
|
||||
namespace imgproc
|
||||
{
|
||||
void nln_fast_get_buffer_size(const PtrStepSzb& src, int search_window, int block_window, int& buffer_cols, int& buffer_rows);
|
||||
|
||||
template<typename T>
|
||||
void nlm_fast_gpu(const PtrStepSzb& src, PtrStepSzb dst, PtrStepi buffer,
|
||||
int search_window, int block_window, float h, cudaStream_t stream);
|
||||
|
||||
void fnlm_split_channels(const PtrStepSz<uchar3>& lab, PtrStepb l, PtrStep<uchar2> ab, cudaStream_t stream);
|
||||
void fnlm_merge_channels(const PtrStepb& l, const PtrStep<uchar2>& ab, PtrStepSz<uchar3> lab, cudaStream_t stream);
|
||||
}
|
||||
}}}
|
||||
|
||||
void cv::cuda::fastNlMeansDenoising(InputArray _src, OutputArray _dst, float h, int search_window, int block_window, Stream& stream)
|
||||
{
|
||||
const GpuMat src = _src.getGpuMat();
|
||||
|
||||
CV_Assert(src.depth() == CV_8U && src.channels() < 4);
|
||||
|
||||
int border_size = search_window/2 + block_window/2;
|
||||
Size esize = src.size() + Size(border_size, border_size) * 2;
|
||||
|
||||
BufferPool pool(stream);
|
||||
|
||||
GpuMat extended_src = pool.getBuffer(esize, src.type());
|
||||
cv::cuda::copyMakeBorder(src, extended_src, border_size, border_size, border_size, border_size, cv::BORDER_DEFAULT, Scalar(), stream);
|
||||
GpuMat src_hdr = extended_src(Rect(Point2i(border_size, border_size), src.size()));
|
||||
|
||||
int bcols, brows;
|
||||
device::imgproc::nln_fast_get_buffer_size(src_hdr, search_window, block_window, bcols, brows);
|
||||
GpuMat buffer = pool.getBuffer(brows, bcols, CV_32S);
|
||||
|
||||
using namespace cv::cuda::device::imgproc;
|
||||
typedef void (*nlm_fast_t)(const PtrStepSzb&, PtrStepSzb, PtrStepi, int, int, float, cudaStream_t);
|
||||
static const nlm_fast_t funcs[] = { nlm_fast_gpu<uchar>, nlm_fast_gpu<uchar2>, nlm_fast_gpu<uchar3>, 0};
|
||||
|
||||
_dst.create(src.size(), src.type());
|
||||
GpuMat dst = _dst.getGpuMat();
|
||||
|
||||
funcs[src.channels()-1](src_hdr, dst, buffer, search_window, block_window, h, StreamAccessor::getStream(stream));
|
||||
}
|
||||
|
||||
void cv::cuda::fastNlMeansDenoisingColored(InputArray _src, OutputArray _dst, float h_luminance, float h_color, int search_window, int block_window, Stream& stream)
|
||||
{
|
||||
const GpuMat src = _src.getGpuMat();
|
||||
|
||||
CV_Assert(src.type() == CV_8UC3);
|
||||
|
||||
BufferPool pool(stream);
|
||||
|
||||
GpuMat lab = pool.getBuffer(src.size(), src.type());
|
||||
cv::cuda::cvtColor(src, lab, cv::COLOR_BGR2Lab, 0, stream);
|
||||
|
||||
GpuMat l = pool.getBuffer(src.size(), CV_8U);
|
||||
GpuMat ab = pool.getBuffer(src.size(), CV_8UC2);
|
||||
device::imgproc::fnlm_split_channels(lab, l, ab, StreamAccessor::getStream(stream));
|
||||
|
||||
fastNlMeansDenoising(l, l, h_luminance, search_window, block_window, stream);
|
||||
fastNlMeansDenoising(ab, ab, h_color, search_window, block_window, stream);
|
||||
|
||||
device::imgproc::fnlm_merge_channels(l, ab, lab, StreamAccessor::getStream(stream));
|
||||
cv::cuda::cvtColor(lab, _dst, cv::COLOR_Lab2BGR, 0, stream);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,321 @@
|
||||
/*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.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective icvers.
|
||||
//
|
||||
// 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 Intel Corporation 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_FAST_NLMEANS_DENOISING_INVOKER_HPP__
|
||||
#define __OPENCV_FAST_NLMEANS_DENOISING_INVOKER_HPP__
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <limits>
|
||||
|
||||
#include "fast_nlmeans_denoising_invoker_commons.hpp"
|
||||
#include "arrays.hpp"
|
||||
|
||||
using namespace cv;
|
||||
|
||||
template <typename T, typename IT, typename UIT, typename D, typename WT>
|
||||
struct FastNlMeansDenoisingInvoker :
|
||||
public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
FastNlMeansDenoisingInvoker(const Mat& src, Mat& dst,
|
||||
int template_window_size, int search_window_size, const float *h);
|
||||
|
||||
void operator() (const Range& range) const CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
void operator= (const FastNlMeansDenoisingInvoker&);
|
||||
|
||||
const Mat& src_;
|
||||
Mat& dst_;
|
||||
|
||||
Mat extended_src_;
|
||||
int border_size_;
|
||||
|
||||
int template_window_size_;
|
||||
int search_window_size_;
|
||||
|
||||
int template_window_half_size_;
|
||||
int search_window_half_size_;
|
||||
|
||||
typename pixelInfo<WT>::sampleType fixed_point_mult_;
|
||||
int almost_template_window_size_sq_bin_shift_;
|
||||
std::vector<WT> almost_dist2weight_;
|
||||
|
||||
void calcDistSumsForFirstElementInRow(
|
||||
int i, Array2d<int>& dist_sums,
|
||||
Array3d<int>& col_dist_sums,
|
||||
Array3d<int>& up_col_dist_sums) const;
|
||||
|
||||
void calcDistSumsForElementInFirstRow(
|
||||
int i, int j, int first_col_num,
|
||||
Array2d<int>& dist_sums,
|
||||
Array3d<int>& col_dist_sums,
|
||||
Array3d<int>& up_col_dist_sums) const;
|
||||
};
|
||||
|
||||
inline int getNearestPowerOf2(int value)
|
||||
{
|
||||
int p = 0;
|
||||
while( 1 << p < value)
|
||||
++p;
|
||||
return p;
|
||||
}
|
||||
|
||||
template <typename T, typename IT, typename UIT, typename D, typename WT>
|
||||
FastNlMeansDenoisingInvoker<T, IT, UIT, D, WT>::FastNlMeansDenoisingInvoker(
|
||||
const Mat& src, Mat& dst,
|
||||
int template_window_size,
|
||||
int search_window_size,
|
||||
const float *h) :
|
||||
src_(src), dst_(dst)
|
||||
{
|
||||
CV_Assert(src.channels() == pixelInfo<T>::channels);
|
||||
|
||||
template_window_half_size_ = template_window_size / 2;
|
||||
search_window_half_size_ = search_window_size / 2;
|
||||
template_window_size_ = template_window_half_size_ * 2 + 1;
|
||||
search_window_size_ = search_window_half_size_ * 2 + 1;
|
||||
|
||||
border_size_ = search_window_half_size_ + template_window_half_size_;
|
||||
copyMakeBorder(src_, extended_src_, border_size_, border_size_, border_size_, border_size_, BORDER_DEFAULT);
|
||||
|
||||
const IT max_estimate_sum_value =
|
||||
(IT)search_window_size_ * (IT)search_window_size_ * (IT)pixelInfo<T>::sampleMax();
|
||||
fixed_point_mult_ = (int)std::min<IT>(std::numeric_limits<IT>::max() / max_estimate_sum_value,
|
||||
pixelInfo<WT>::sampleMax());
|
||||
|
||||
// precalc weight for every possible l2 dist between blocks
|
||||
// additional optimization of precalced weights to replace division(averaging) by binary shift
|
||||
CV_Assert(template_window_size_ <= 46340); // sqrt(INT_MAX)
|
||||
int template_window_size_sq = template_window_size_ * template_window_size_;
|
||||
almost_template_window_size_sq_bin_shift_ = getNearestPowerOf2(template_window_size_sq);
|
||||
double almost_dist2actual_dist_multiplier = ((double)(1 << almost_template_window_size_sq_bin_shift_)) / template_window_size_sq;
|
||||
|
||||
int max_dist = D::template maxDist<T>();
|
||||
int almost_max_dist = (int)(max_dist / almost_dist2actual_dist_multiplier + 1);
|
||||
almost_dist2weight_.resize(almost_max_dist);
|
||||
|
||||
for (int almost_dist = 0; almost_dist < almost_max_dist; almost_dist++)
|
||||
{
|
||||
double dist = almost_dist * almost_dist2actual_dist_multiplier;
|
||||
almost_dist2weight_[almost_dist] =
|
||||
D::template calcWeight<T, WT>(dist, h, fixed_point_mult_);
|
||||
}
|
||||
|
||||
// additional optimization init end
|
||||
if (dst_.empty())
|
||||
dst_ = Mat::zeros(src_.size(), src_.type());
|
||||
}
|
||||
|
||||
template <typename T, typename IT, typename UIT, typename D, typename WT>
|
||||
void FastNlMeansDenoisingInvoker<T, IT, UIT, D, WT>::operator() (const Range& range) const
|
||||
{
|
||||
int row_from = range.start;
|
||||
int row_to = range.end - 1;
|
||||
|
||||
// sums of cols anf rows for current pixel p
|
||||
Array2d<int> dist_sums(search_window_size_, search_window_size_);
|
||||
|
||||
// for lazy calc optimization (sum of cols for current pixel)
|
||||
Array3d<int> col_dist_sums(template_window_size_, search_window_size_, search_window_size_);
|
||||
|
||||
int first_col_num = -1;
|
||||
// last elements of column sum (for each element in row)
|
||||
Array3d<int> up_col_dist_sums(src_.cols, search_window_size_, search_window_size_);
|
||||
|
||||
for (int i = row_from; i <= row_to; i++)
|
||||
{
|
||||
for (int j = 0; j < src_.cols; j++)
|
||||
{
|
||||
int search_window_y = i - search_window_half_size_;
|
||||
int search_window_x = j - search_window_half_size_;
|
||||
|
||||
// calc dist_sums
|
||||
if (j == 0)
|
||||
{
|
||||
calcDistSumsForFirstElementInRow(i, dist_sums, col_dist_sums, up_col_dist_sums);
|
||||
first_col_num = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// calc cur dist_sums using previous dist_sums
|
||||
if (i == row_from)
|
||||
{
|
||||
calcDistSumsForElementInFirstRow(i, j, first_col_num,
|
||||
dist_sums, col_dist_sums, up_col_dist_sums);
|
||||
}
|
||||
else
|
||||
{
|
||||
int ay = border_size_ + i;
|
||||
int ax = border_size_ + j + template_window_half_size_;
|
||||
|
||||
int start_by = border_size_ + i - search_window_half_size_;
|
||||
int start_bx = border_size_ + j - search_window_half_size_ + template_window_half_size_;
|
||||
|
||||
T a_up = extended_src_.at<T>(ay - template_window_half_size_ - 1, ax);
|
||||
T a_down = extended_src_.at<T>(ay + template_window_half_size_, ax);
|
||||
|
||||
// copy class member to local variable for optimization
|
||||
int search_window_size = search_window_size_;
|
||||
|
||||
for (int y = 0; y < search_window_size; y++)
|
||||
{
|
||||
int * dist_sums_row = dist_sums.row_ptr(y);
|
||||
int * col_dist_sums_row = col_dist_sums.row_ptr(first_col_num, y);
|
||||
int * up_col_dist_sums_row = up_col_dist_sums.row_ptr(j, y);
|
||||
|
||||
const T * b_up_ptr = extended_src_.ptr<T>(start_by - template_window_half_size_ - 1 + y);
|
||||
const T * b_down_ptr = extended_src_.ptr<T>(start_by + template_window_half_size_ + y);
|
||||
|
||||
// MSVC 2015 generates unaligned destination for "movaps" instruction for 32-bit builds
|
||||
#if defined _MSC_VER && _MSC_VER == 1900 && !defined _WIN64
|
||||
#pragma loop(no_vector)
|
||||
#endif
|
||||
for (int x = 0; x < search_window_size; x++)
|
||||
{
|
||||
// remove from current pixel sum column sum with index "first_col_num"
|
||||
dist_sums_row[x] -= col_dist_sums_row[x];
|
||||
|
||||
int bx = start_bx + x;
|
||||
col_dist_sums_row[x] = up_col_dist_sums_row[x] + D::template calcUpDownDist<T>(a_up, a_down, b_up_ptr[bx], b_down_ptr[bx]);
|
||||
|
||||
dist_sums_row[x] += col_dist_sums_row[x];
|
||||
up_col_dist_sums_row[x] = col_dist_sums_row[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
first_col_num = (first_col_num + 1) % template_window_size_;
|
||||
}
|
||||
|
||||
// calc weights
|
||||
IT estimation[pixelInfo<T>::channels], weights_sum[pixelInfo<WT>::channels];
|
||||
for (int channel_num = 0; channel_num < pixelInfo<T>::channels; channel_num++)
|
||||
estimation[channel_num] = 0;
|
||||
for (int channel_num = 0; channel_num < pixelInfo<WT>::channels; channel_num++)
|
||||
weights_sum[channel_num] = 0;
|
||||
|
||||
for (int y = 0; y < search_window_size_; y++)
|
||||
{
|
||||
const T* cur_row_ptr = extended_src_.ptr<T>(border_size_ + search_window_y + y);
|
||||
int* dist_sums_row = dist_sums.row_ptr(y);
|
||||
for (int x = 0; x < search_window_size_; x++)
|
||||
{
|
||||
int almostAvgDist = dist_sums_row[x] >> almost_template_window_size_sq_bin_shift_;
|
||||
WT weight = almost_dist2weight_[almostAvgDist];
|
||||
T p = cur_row_ptr[border_size_ + search_window_x + x];
|
||||
incWithWeight<T, IT, WT>(estimation, weights_sum, weight, p);
|
||||
}
|
||||
}
|
||||
|
||||
divByWeightsSum<IT, UIT, pixelInfo<T>::channels, pixelInfo<WT>::channels>(estimation,
|
||||
weights_sum);
|
||||
dst_.at<T>(i,j) = saturateCastFromArray<T, IT>(estimation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename IT, typename UIT, typename D, typename WT>
|
||||
inline void FastNlMeansDenoisingInvoker<T, IT, UIT, D, WT>::calcDistSumsForFirstElementInRow(
|
||||
int i,
|
||||
Array2d<int>& dist_sums,
|
||||
Array3d<int>& col_dist_sums,
|
||||
Array3d<int>& up_col_dist_sums) const
|
||||
{
|
||||
int j = 0;
|
||||
|
||||
for (int y = 0; y < search_window_size_; y++)
|
||||
for (int x = 0; x < search_window_size_; x++)
|
||||
{
|
||||
dist_sums[y][x] = 0;
|
||||
for (int tx = 0; tx < template_window_size_; tx++)
|
||||
col_dist_sums[tx][y][x] = 0;
|
||||
|
||||
int start_y = i + y - search_window_half_size_;
|
||||
int start_x = j + x - search_window_half_size_;
|
||||
|
||||
for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++)
|
||||
for (int tx = -template_window_half_size_; tx <= template_window_half_size_; tx++)
|
||||
{
|
||||
int dist = D::template calcDist<T>(extended_src_,
|
||||
border_size_ + i + ty, border_size_ + j + tx,
|
||||
border_size_ + start_y + ty, border_size_ + start_x + tx);
|
||||
|
||||
dist_sums[y][x] += dist;
|
||||
col_dist_sums[tx + template_window_half_size_][y][x] += dist;
|
||||
}
|
||||
|
||||
up_col_dist_sums[j][y][x] = col_dist_sums[template_window_size_ - 1][y][x];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename IT, typename UIT, typename D, typename WT>
|
||||
inline void FastNlMeansDenoisingInvoker<T, IT, UIT, D, WT>::calcDistSumsForElementInFirstRow(
|
||||
int i, int j, int first_col_num,
|
||||
Array2d<int>& dist_sums,
|
||||
Array3d<int>& col_dist_sums,
|
||||
Array3d<int>& up_col_dist_sums) const
|
||||
{
|
||||
int ay = border_size_ + i;
|
||||
int ax = border_size_ + j + template_window_half_size_;
|
||||
|
||||
int start_by = border_size_ + i - search_window_half_size_;
|
||||
int start_bx = border_size_ + j - search_window_half_size_ + template_window_half_size_;
|
||||
|
||||
int new_last_col_num = first_col_num;
|
||||
|
||||
for (int y = 0; y < search_window_size_; y++)
|
||||
for (int x = 0; x < search_window_size_; x++)
|
||||
{
|
||||
dist_sums[y][x] -= col_dist_sums[first_col_num][y][x];
|
||||
|
||||
col_dist_sums[new_last_col_num][y][x] = 0;
|
||||
int by = start_by + y;
|
||||
int bx = start_bx + x;
|
||||
for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++)
|
||||
col_dist_sums[new_last_col_num][y][x] += D::template calcDist<T>(extended_src_, ay + ty, ax, by + ty, bx);
|
||||
|
||||
dist_sums[y][x] += col_dist_sums[new_last_col_num][y][x];
|
||||
up_col_dist_sums[j][y][x] = col_dist_sums[new_last_col_num][y][x];
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,489 @@
|
||||
/*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.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective icvers.
|
||||
//
|
||||
// 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 Intel Corporation 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_FAST_NLMEANS_DENOISING_INVOKER_COMMONS_HPP__
|
||||
#define __OPENCV_FAST_NLMEANS_DENOISING_INVOKER_COMMONS_HPP__
|
||||
|
||||
using namespace cv;
|
||||
|
||||
// std::isnan is a part of C++11 and it is not supported in MSVS2010/2012
|
||||
#if defined _MSC_VER && _MSC_VER < 1800 /* MSVC 2013 */
|
||||
#include <float.h>
|
||||
namespace std {
|
||||
template <typename T> bool isnan(T value) { return _isnan(value) != 0; }
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T> struct pixelInfo_
|
||||
{
|
||||
static const int channels = 1;
|
||||
typedef T sampleType;
|
||||
};
|
||||
|
||||
template <typename ET, int n> struct pixelInfo_<Vec<ET, n> >
|
||||
{
|
||||
static const int channels = n;
|
||||
typedef ET sampleType;
|
||||
};
|
||||
|
||||
template <typename T> struct pixelInfo: public pixelInfo_<T>
|
||||
{
|
||||
typedef typename pixelInfo_<T>::sampleType sampleType;
|
||||
|
||||
static inline sampleType sampleMax()
|
||||
{
|
||||
return std::numeric_limits<sampleType>::max();
|
||||
}
|
||||
|
||||
static inline sampleType sampleMin()
|
||||
{
|
||||
return std::numeric_limits<sampleType>::min();
|
||||
}
|
||||
|
||||
static inline size_t sampleBytes()
|
||||
{
|
||||
return sizeof(sampleType);
|
||||
}
|
||||
|
||||
static inline size_t sampleBits()
|
||||
{
|
||||
return 8*sampleBytes();
|
||||
}
|
||||
};
|
||||
|
||||
class DistAbs
|
||||
{
|
||||
template <typename T> struct calcDist_
|
||||
{
|
||||
static inline int f(const T a, const T b)
|
||||
{
|
||||
return std::abs((int)(a-b));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET> struct calcDist_<Vec<ET, 2> >
|
||||
{
|
||||
static inline int f(const Vec<ET, 2> a, const Vec<ET, 2> b)
|
||||
{
|
||||
return std::abs((int)(a[0]-b[0])) + std::abs((int)(a[1]-b[1]));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET> struct calcDist_<Vec<ET, 3> >
|
||||
{
|
||||
static inline int f(const Vec<ET, 3> a, const Vec<ET, 3> b)
|
||||
{
|
||||
return
|
||||
std::abs((int)(a[0]-b[0])) +
|
||||
std::abs((int)(a[1]-b[1])) +
|
||||
std::abs((int)(a[2]-b[2]));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET> struct calcDist_<Vec<ET, 4> >
|
||||
{
|
||||
static inline int f(const Vec<ET, 4> a, const Vec<ET, 4> b)
|
||||
{
|
||||
return
|
||||
std::abs((int)(a[0]-b[0])) +
|
||||
std::abs((int)(a[1]-b[1])) +
|
||||
std::abs((int)(a[2]-b[2])) +
|
||||
std::abs((int)(a[3]-b[3]));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename WT> struct calcWeight_
|
||||
{
|
||||
static inline WT f(double dist, const float *h, WT fixed_point_mult)
|
||||
{
|
||||
double w = std::exp(-dist*dist / (h[0]*h[0] * pixelInfo<T>::channels));
|
||||
if (std::isnan(w)) w = 1.0; // Handle h = 0.0
|
||||
|
||||
static const double WEIGHT_THRESHOLD = 0.001;
|
||||
WT weight = (WT)cvRound(fixed_point_mult * w);
|
||||
if (weight < WEIGHT_THRESHOLD * fixed_point_mult) weight = 0;
|
||||
|
||||
return weight;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename ET, int n> struct calcWeight_<T, Vec<ET, n> >
|
||||
{
|
||||
static inline Vec<ET, n> f(double dist, const float *h, ET fixed_point_mult)
|
||||
{
|
||||
Vec<ET, n> res;
|
||||
for (int i=0; i<n; i++)
|
||||
res[i] = calcWeight<T, ET>(dist, &h[i], fixed_point_mult);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
template <typename T> static inline int calcDist(const T a, const T b)
|
||||
{
|
||||
return calcDist_<T>::f(a, b);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline int calcDist(const Mat& m, int i1, int j1, int i2, int j2)
|
||||
{
|
||||
const T a = m.at<T>(i1, j1);
|
||||
const T b = m.at<T>(i2, j2);
|
||||
return calcDist<T>(a,b);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline int calcUpDownDist(T a_up, T a_down, T b_up, T b_down)
|
||||
{
|
||||
return calcDist<T>(a_down, b_down) - calcDist<T>(a_up, b_up);
|
||||
}
|
||||
|
||||
template <typename T, typename WT>
|
||||
static inline WT calcWeight(double dist, const float *h,
|
||||
typename pixelInfo<WT>::sampleType fixed_point_mult)
|
||||
{
|
||||
return calcWeight_<T, WT>::f(dist, h, fixed_point_mult);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline int maxDist()
|
||||
{
|
||||
return (int)pixelInfo<T>::sampleMax() * pixelInfo<T>::channels;
|
||||
}
|
||||
};
|
||||
|
||||
class DistSquared
|
||||
{
|
||||
template <typename T> struct calcDist_
|
||||
{
|
||||
static inline int f(const T a, const T b)
|
||||
{
|
||||
return (int)(a-b) * (int)(a-b);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET> struct calcDist_<Vec<ET, 2> >
|
||||
{
|
||||
static inline int f(const Vec<ET, 2> a, const Vec<ET, 2> b)
|
||||
{
|
||||
return (int)(a[0]-b[0])*(int)(a[0]-b[0]) + (int)(a[1]-b[1])*(int)(a[1]-b[1]);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET> struct calcDist_<Vec<ET, 3> >
|
||||
{
|
||||
static inline int f(const Vec<ET, 3> a, const Vec<ET, 3> b)
|
||||
{
|
||||
return
|
||||
(int)(a[0]-b[0])*(int)(a[0]-b[0]) +
|
||||
(int)(a[1]-b[1])*(int)(a[1]-b[1]) +
|
||||
(int)(a[2]-b[2])*(int)(a[2]-b[2]);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET> struct calcDist_<Vec<ET, 4> >
|
||||
{
|
||||
static inline int f(const Vec<ET, 4> a, const Vec<ET, 4> b)
|
||||
{
|
||||
return
|
||||
(int)(a[0]-b[0])*(int)(a[0]-b[0]) +
|
||||
(int)(a[1]-b[1])*(int)(a[1]-b[1]) +
|
||||
(int)(a[2]-b[2])*(int)(a[2]-b[2]) +
|
||||
(int)(a[3]-b[3])*(int)(a[3]-b[3]);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct calcUpDownDist_
|
||||
{
|
||||
static inline int f(T a_up, T a_down, T b_up, T b_down)
|
||||
{
|
||||
int A = a_down - b_down;
|
||||
int B = a_up - b_up;
|
||||
return (A-B)*(A+B);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET, int n> struct calcUpDownDist_<Vec<ET, n> >
|
||||
{
|
||||
private:
|
||||
typedef Vec<ET, n> T;
|
||||
public:
|
||||
static inline int f(T a_up, T a_down, T b_up, T b_down)
|
||||
{
|
||||
return calcDist<T>(a_down, b_down) - calcDist<T>(a_up, b_up);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename WT> struct calcWeight_
|
||||
{
|
||||
static inline WT f(double dist, const float *h, WT fixed_point_mult)
|
||||
{
|
||||
double w = std::exp(-dist / (h[0]*h[0] * pixelInfo<T>::channels));
|
||||
if (std::isnan(w)) w = 1.0; // Handle h = 0.0
|
||||
|
||||
static const double WEIGHT_THRESHOLD = 0.001;
|
||||
WT weight = (WT)cvRound(fixed_point_mult * w);
|
||||
if (weight < WEIGHT_THRESHOLD * fixed_point_mult) weight = 0;
|
||||
|
||||
return weight;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename ET, int n> struct calcWeight_<T, Vec<ET, n> >
|
||||
{
|
||||
static inline Vec<ET, n> f(double dist, const float *h, ET fixed_point_mult)
|
||||
{
|
||||
Vec<ET, n> res;
|
||||
for (int i=0; i<n; i++)
|
||||
res[i] = calcWeight<T, ET>(dist, &h[i], fixed_point_mult);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
template <typename T> static inline int calcDist(const T a, const T b)
|
||||
{
|
||||
return calcDist_<T>::f(a, b);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline int calcDist(const Mat& m, int i1, int j1, int i2, int j2)
|
||||
{
|
||||
const T a = m.at<T>(i1, j1);
|
||||
const T b = m.at<T>(i2, j2);
|
||||
return calcDist<T>(a,b);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline int calcUpDownDist(T a_up, T a_down, T b_up, T b_down)
|
||||
{
|
||||
return calcUpDownDist_<T>::f(a_up, a_down, b_up, b_down);
|
||||
}
|
||||
|
||||
template <typename T, typename WT>
|
||||
static inline WT calcWeight(double dist, const float *h,
|
||||
typename pixelInfo<WT>::sampleType fixed_point_mult)
|
||||
{
|
||||
return calcWeight_<T, WT>::f(dist, h, fixed_point_mult);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline int maxDist()
|
||||
{
|
||||
return (int)pixelInfo<T>::sampleMax() * (int)pixelInfo<T>::sampleMax() *
|
||||
pixelInfo<T>::channels;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename IT, typename WT> struct incWithWeight_
|
||||
{
|
||||
static inline void f(IT* estimation, IT* weights_sum, WT weight, T p)
|
||||
{
|
||||
estimation[0] += (IT)weight * p;
|
||||
weights_sum[0] += (IT)weight;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET, typename IT, typename WT> struct incWithWeight_<Vec<ET, 2>, IT, WT>
|
||||
{
|
||||
static inline void f(IT* estimation, IT* weights_sum, WT weight, Vec<ET, 2> p)
|
||||
{
|
||||
estimation[0] += (IT)weight * p[0];
|
||||
estimation[1] += (IT)weight * p[1];
|
||||
weights_sum[0] += (IT)weight;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET, typename IT, typename WT> struct incWithWeight_<Vec<ET, 3>, IT, WT>
|
||||
{
|
||||
static inline void f(IT* estimation, IT* weights_sum, WT weight, Vec<ET, 3> p)
|
||||
{
|
||||
estimation[0] += (IT)weight * p[0];
|
||||
estimation[1] += (IT)weight * p[1];
|
||||
estimation[2] += (IT)weight * p[2];
|
||||
weights_sum[0] += (IT)weight;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET, typename IT, typename WT> struct incWithWeight_<Vec<ET, 4>, IT, WT>
|
||||
{
|
||||
static inline void f(IT* estimation, IT* weights_sum, WT weight, Vec<ET, 4> p)
|
||||
{
|
||||
estimation[0] += (IT)weight * p[0];
|
||||
estimation[1] += (IT)weight * p[1];
|
||||
estimation[2] += (IT)weight * p[2];
|
||||
estimation[3] += (IT)weight * p[3];
|
||||
weights_sum[0] += (IT)weight;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET, typename IT, typename EW> struct incWithWeight_<Vec<ET, 2>, IT, Vec<EW, 2> >
|
||||
{
|
||||
static inline void f(IT* estimation, IT* weights_sum, Vec<EW, 2> weight, Vec<ET, 2> p)
|
||||
{
|
||||
estimation[0] += (IT)weight[0] * p[0];
|
||||
estimation[1] += (IT)weight[1] * p[1];
|
||||
weights_sum[0] += (IT)weight[0];
|
||||
weights_sum[1] += (IT)weight[1];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET, typename IT, typename EW> struct incWithWeight_<Vec<ET, 3>, IT, Vec<EW, 3> >
|
||||
{
|
||||
static inline void f(IT* estimation, IT* weights_sum, Vec<EW, 3> weight, Vec<ET, 3> p)
|
||||
{
|
||||
estimation[0] += (IT)weight[0] * p[0];
|
||||
estimation[1] += (IT)weight[1] * p[1];
|
||||
estimation[2] += (IT)weight[2] * p[2];
|
||||
weights_sum[0] += (IT)weight[0];
|
||||
weights_sum[1] += (IT)weight[1];
|
||||
weights_sum[2] += (IT)weight[2];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET, typename IT, typename EW> struct incWithWeight_<Vec<ET, 4>, IT, Vec<EW, 4> >
|
||||
{
|
||||
static inline void f(IT* estimation, IT* weights_sum, Vec<EW, 4> weight, Vec<ET, 4> p)
|
||||
{
|
||||
estimation[0] += (IT)weight[0] * p[0];
|
||||
estimation[1] += (IT)weight[1] * p[1];
|
||||
estimation[2] += (IT)weight[2] * p[2];
|
||||
estimation[3] += (IT)weight[3] * p[3];
|
||||
weights_sum[0] += (IT)weight[0];
|
||||
weights_sum[1] += (IT)weight[1];
|
||||
weights_sum[2] += (IT)weight[2];
|
||||
weights_sum[3] += (IT)weight[3];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename IT, typename WT>
|
||||
static inline void incWithWeight(IT* estimation, IT* weights_sum, WT weight, T p)
|
||||
{
|
||||
incWithWeight_<T, IT, WT>::f(estimation, weights_sum, weight, p);
|
||||
}
|
||||
|
||||
template <typename IT, typename UIT, int nc, int nw> struct divByWeightsSum_
|
||||
{
|
||||
static inline void f(IT* estimation, IT* weights_sum);
|
||||
};
|
||||
|
||||
template <typename IT, typename UIT> struct divByWeightsSum_<IT, UIT, 1, 1>
|
||||
{
|
||||
static inline void f(IT* estimation, IT* weights_sum)
|
||||
{
|
||||
estimation[0] = (static_cast<UIT>(estimation[0]) + weights_sum[0]/2) / weights_sum[0];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename IT, typename UIT, int n> struct divByWeightsSum_<IT, UIT, n, 1>
|
||||
{
|
||||
static inline void f(IT* estimation, IT* weights_sum)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
estimation[i] = (static_cast<UIT>(estimation[i]) + weights_sum[0]/2) / weights_sum[0];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename IT, typename UIT, int n> struct divByWeightsSum_<IT, UIT, n, n>
|
||||
{
|
||||
static inline void f(IT* estimation, IT* weights_sum)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
estimation[i] = (static_cast<UIT>(estimation[i]) + weights_sum[i]/2) / weights_sum[i];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename IT, typename UIT, int nc, int nw>
|
||||
static inline void divByWeightsSum(IT* estimation, IT* weights_sum)
|
||||
{
|
||||
divByWeightsSum_<IT, UIT, nc, nw>::f(estimation, weights_sum);
|
||||
}
|
||||
|
||||
template <typename T, typename IT> struct saturateCastFromArray_
|
||||
{
|
||||
static inline T f(IT* estimation)
|
||||
{
|
||||
return saturate_cast<T>(estimation[0]);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET, typename IT> struct saturateCastFromArray_<Vec<ET, 2>, IT>
|
||||
{
|
||||
static inline Vec<ET, 2> f(IT* estimation)
|
||||
{
|
||||
Vec<ET, 2> res;
|
||||
res[0] = saturate_cast<ET>(estimation[0]);
|
||||
res[1] = saturate_cast<ET>(estimation[1]);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET, typename IT> struct saturateCastFromArray_<Vec<ET, 3>, IT>
|
||||
{
|
||||
static inline Vec<ET, 3> f(IT* estimation)
|
||||
{
|
||||
Vec<ET, 3> res;
|
||||
res[0] = saturate_cast<ET>(estimation[0]);
|
||||
res[1] = saturate_cast<ET>(estimation[1]);
|
||||
res[2] = saturate_cast<ET>(estimation[2]);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ET, typename IT> struct saturateCastFromArray_<Vec<ET, 4>, IT>
|
||||
{
|
||||
static inline Vec<ET, 4> f(IT* estimation)
|
||||
{
|
||||
Vec<ET, 4> res;
|
||||
res[0] = saturate_cast<ET>(estimation[0]);
|
||||
res[1] = saturate_cast<ET>(estimation[1]);
|
||||
res[2] = saturate_cast<ET>(estimation[2]);
|
||||
res[3] = saturate_cast<ET>(estimation[3]);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename IT> static inline T saturateCastFromArray(IT* estimation)
|
||||
{
|
||||
return saturateCastFromArray_<T, IT>::f(estimation);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,217 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2014, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
#include "precomp.hpp"
|
||||
#ifndef __OPENCV_FAST_NLMEANS_DENOISING_OPENCL_HPP__
|
||||
#define __OPENCV_FAST_NLMEANS_DENOISING_OPENCL_HPP__
|
||||
|
||||
#include "opencl_kernels_photo.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace cv {
|
||||
|
||||
enum
|
||||
{
|
||||
BLOCK_ROWS = 32,
|
||||
BLOCK_COLS = 32,
|
||||
CTA_SIZE_INTEL = 64,
|
||||
CTA_SIZE_DEFAULT = 256
|
||||
};
|
||||
|
||||
template <typename FT, typename ST, typename WT>
|
||||
static bool ocl_calcAlmostDist2Weight(UMat & almostDist2Weight,
|
||||
int searchWindowSize, int templateWindowSize,
|
||||
const FT *h, int hn, int cn, int normType,
|
||||
int & almostTemplateWindowSizeSqBinShift)
|
||||
{
|
||||
const WT maxEstimateSumValue = searchWindowSize * searchWindowSize *
|
||||
std::numeric_limits<ST>::max();
|
||||
int fixedPointMult = (int)std::min<WT>(std::numeric_limits<WT>::max() / maxEstimateSumValue,
|
||||
std::numeric_limits<int>::max());
|
||||
int depth = DataType<FT>::depth;
|
||||
bool doubleSupport = ocl::Device::getDefault().doubleFPConfig() > 0;
|
||||
|
||||
if (depth == CV_64F && !doubleSupport)
|
||||
return false;
|
||||
|
||||
// precalc weight for every possible l2 dist between blocks
|
||||
// additional optimization of precalced weights to replace division(averaging) by binary shift
|
||||
CV_Assert(templateWindowSize <= 46340); // sqrt(INT_MAX)
|
||||
int templateWindowSizeSq = templateWindowSize * templateWindowSize;
|
||||
almostTemplateWindowSizeSqBinShift = getNearestPowerOf2(templateWindowSizeSq);
|
||||
FT almostDist2ActualDistMultiplier = (FT)(1 << almostTemplateWindowSizeSqBinShift) / templateWindowSizeSq;
|
||||
|
||||
const FT WEIGHT_THRESHOLD = 1e-3f;
|
||||
WT maxDist = normType == NORM_L1 ? (WT)std::numeric_limits<ST>::max() * cn :
|
||||
(WT)std::numeric_limits<ST>::max() * (WT)std::numeric_limits<ST>::max() * cn;
|
||||
int almostMaxDist = (int)(maxDist / almostDist2ActualDistMultiplier + 1);
|
||||
FT den[4];
|
||||
CV_Assert(hn > 0 && hn <= 4);
|
||||
for (int i=0; i<hn; i++)
|
||||
den[i] = 1.0f / (h[i] * h[i] * cn);
|
||||
|
||||
almostDist2Weight.create(1, almostMaxDist, CV_32SC(hn == 3 ? 4 : hn));
|
||||
|
||||
char buf[50];
|
||||
ocl::Kernel k("calcAlmostDist2Weight", ocl::photo::nlmeans_oclsrc,
|
||||
format("-D OP_CALC_WEIGHTS -D FT=%s -D w_t=%s"
|
||||
" -D wlut_t=%s -D convert_wlut_t=%s%s%s",
|
||||
ocl::typeToStr(depth), ocl::typeToStr(CV_MAKE_TYPE(depth, hn)),
|
||||
ocl::typeToStr(CV_32SC(hn)), ocl::convertTypeStr(depth, CV_32S, hn, buf, sizeof(buf)),
|
||||
doubleSupport ? " -D DOUBLE_SUPPORT" : "",
|
||||
normType == NORM_L1 ? " -D ABS" : ""));
|
||||
if (k.empty())
|
||||
return false;
|
||||
|
||||
k.args(ocl::KernelArg::PtrWriteOnly(almostDist2Weight), almostMaxDist,
|
||||
almostDist2ActualDistMultiplier, fixedPointMult,
|
||||
ocl::KernelArg::Constant(den, (hn == 3 ? 4 : hn)*sizeof(FT)), WEIGHT_THRESHOLD);
|
||||
|
||||
size_t globalsize[1] = { (size_t)almostMaxDist };
|
||||
return k.run(1, globalsize, NULL, false);
|
||||
}
|
||||
|
||||
static bool ocl_fastNlMeansDenoising(InputArray _src, OutputArray _dst, const float *h, int hn,
|
||||
int templateWindowSize, int searchWindowSize, int normType)
|
||||
{
|
||||
int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
|
||||
int ctaSize = ocl::Device::getDefault().isIntel() ? CTA_SIZE_INTEL : CTA_SIZE_DEFAULT;
|
||||
Size size = _src.size();
|
||||
|
||||
if (cn < 1 || cn > 4 || ((normType != NORM_L2 || depth != CV_8U) &&
|
||||
(normType != NORM_L1 || (depth != CV_8U && depth != CV_16U))))
|
||||
return false;
|
||||
|
||||
int templateWindowHalfWize = templateWindowSize / 2;
|
||||
int searchWindowHalfSize = searchWindowSize / 2;
|
||||
templateWindowSize = templateWindowHalfWize * 2 + 1;
|
||||
searchWindowSize = searchWindowHalfSize * 2 + 1;
|
||||
int nblocksx = divUp(size.width, BLOCK_COLS), nblocksy = divUp(size.height, BLOCK_ROWS);
|
||||
int almostTemplateWindowSizeSqBinShift = -1;
|
||||
|
||||
char buf[4][40];
|
||||
const unsigned psz = (depth == CV_8U ? sizeof(uchar) : sizeof(ushort)) * (cn == 3 ? 4 : cn);
|
||||
String opts = format("-D OP_CALC_FASTNLMEANS -D TEMPLATE_SIZE=%d -D SEARCH_SIZE=%d"
|
||||
" -D pixel_t=%s -D int_t=%s -D wlut_t=%s"
|
||||
" -D weight_t=%s -D convert_weight_t=%s -D sum_t=%s -D convert_sum_t=%s"
|
||||
" -D BLOCK_COLS=%d -D BLOCK_ROWS=%d"
|
||||
" -D CTA_SIZE=%d -D TEMPLATE_SIZE2=%d -D SEARCH_SIZE2=%d"
|
||||
" -D convert_int_t=%s -D cn=%d -D psz=%u -D convert_pixel_t=%s%s",
|
||||
templateWindowSize, searchWindowSize,
|
||||
ocl::typeToStr(type), ocl::typeToStr(CV_32SC(cn)),
|
||||
ocl::typeToStr(CV_32SC(hn)),
|
||||
depth == CV_8U ? ocl::typeToStr(CV_32SC(hn)) :
|
||||
format("long%s", hn > 1 ? format("%d", hn).c_str() : "").c_str(),
|
||||
depth == CV_8U ? ocl::convertTypeStr(CV_32S, CV_32S, hn, buf[0], sizeof(buf[0])) :
|
||||
format("convert_long%s", hn > 1 ? format("%d", hn).c_str() : "").c_str(),
|
||||
depth == CV_8U ? ocl::typeToStr(CV_32SC(cn)) :
|
||||
format("long%s", cn > 1 ? format("%d", cn).c_str() : "").c_str(),
|
||||
depth == CV_8U ? ocl::convertTypeStr(depth, CV_32S, cn, buf[1], sizeof(buf[1])) :
|
||||
format("convert_long%s", cn > 1 ? format("%d", cn).c_str() : "").c_str(),
|
||||
BLOCK_COLS, BLOCK_ROWS,
|
||||
ctaSize, templateWindowHalfWize, searchWindowHalfSize,
|
||||
ocl::convertTypeStr(depth, CV_32S, cn, buf[2], sizeof(buf[2])), cn,
|
||||
psz,
|
||||
ocl::convertTypeStr(CV_32S, depth, cn, buf[3], sizeof(buf[3])),
|
||||
normType == NORM_L1 ? " -D ABS" : "");
|
||||
|
||||
ocl::Kernel k("fastNlMeansDenoising", ocl::photo::nlmeans_oclsrc, opts);
|
||||
if (k.empty())
|
||||
return false;
|
||||
|
||||
UMat almostDist2Weight;
|
||||
if ((depth == CV_8U &&
|
||||
!ocl_calcAlmostDist2Weight<float, uchar, int>(almostDist2Weight,
|
||||
searchWindowSize, templateWindowSize,
|
||||
h, hn, cn, normType,
|
||||
almostTemplateWindowSizeSqBinShift)) ||
|
||||
(depth == CV_16U &&
|
||||
!ocl_calcAlmostDist2Weight<float, ushort, int64>(almostDist2Weight,
|
||||
searchWindowSize, templateWindowSize,
|
||||
h, hn, cn, normType,
|
||||
almostTemplateWindowSizeSqBinShift)))
|
||||
return false;
|
||||
CV_Assert(almostTemplateWindowSizeSqBinShift >= 0);
|
||||
|
||||
UMat srcex;
|
||||
int borderSize = searchWindowHalfSize + templateWindowHalfWize;
|
||||
if (cn == 3) {
|
||||
srcex.create(size.height + 2*borderSize, size.width + 2*borderSize, CV_MAKE_TYPE(depth, 4));
|
||||
UMat src(srcex, Rect(borderSize, borderSize, size.width, size.height));
|
||||
int from_to[] = { 0,0, 1,1, 2,2 };
|
||||
mixChannels(std::vector<UMat>(1, _src.getUMat()), std::vector<UMat>(1, src), from_to, 3);
|
||||
copyMakeBorder(src, srcex, borderSize, borderSize, borderSize, borderSize,
|
||||
BORDER_DEFAULT|BORDER_ISOLATED); // create borders in place
|
||||
}
|
||||
else
|
||||
copyMakeBorder(_src, srcex, borderSize, borderSize, borderSize, borderSize, BORDER_DEFAULT);
|
||||
|
||||
_dst.create(size, type);
|
||||
UMat dst;
|
||||
if (cn == 3)
|
||||
dst.create(size, CV_MAKE_TYPE(depth, 4));
|
||||
else
|
||||
dst = _dst.getUMat();
|
||||
|
||||
int searchWindowSizeSq = searchWindowSize * searchWindowSize;
|
||||
Size upColSumSize(size.width, searchWindowSizeSq * nblocksy);
|
||||
Size colSumSize(nblocksx * templateWindowSize, searchWindowSizeSq * nblocksy);
|
||||
UMat buffer(upColSumSize + colSumSize, CV_32SC(cn));
|
||||
|
||||
srcex = srcex(Rect(Point(borderSize, borderSize), size));
|
||||
k.args(ocl::KernelArg::ReadOnlyNoSize(srcex), ocl::KernelArg::WriteOnly(dst),
|
||||
ocl::KernelArg::PtrReadOnly(almostDist2Weight),
|
||||
ocl::KernelArg::PtrReadOnly(buffer), almostTemplateWindowSizeSqBinShift);
|
||||
|
||||
size_t globalsize[2] = { (size_t)nblocksx * ctaSize, (size_t)nblocksy }, localsize[2] = { (size_t)ctaSize, 1 };
|
||||
if (!k.run(2, globalsize, localsize, false)) return false;
|
||||
|
||||
if (cn == 3) {
|
||||
int from_to[] = { 0,0, 1,1, 2,2 };
|
||||
mixChannels(std::vector<UMat>(1, dst), std::vector<UMat>(1, _dst.getUMat()), from_to, 3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ocl_fastNlMeansDenoisingColored( InputArray _src, OutputArray _dst,
|
||||
float h, float hForColorComponents,
|
||||
int templateWindowSize, int searchWindowSize)
|
||||
{
|
||||
UMat src = _src.getUMat();
|
||||
_dst.create(src.size(), src.type());
|
||||
UMat dst = _dst.getUMat();
|
||||
|
||||
UMat src_lab;
|
||||
cvtColor(src, src_lab, COLOR_LBGR2Lab);
|
||||
|
||||
UMat l(src.size(), CV_8U);
|
||||
UMat ab(src.size(), CV_8UC2);
|
||||
std::vector<UMat> l_ab(2), l_ab_denoised(2);
|
||||
l_ab[0] = l;
|
||||
l_ab[1] = ab;
|
||||
l_ab_denoised[0].create(src.size(), CV_8U);
|
||||
l_ab_denoised[1].create(src.size(), CV_8UC2);
|
||||
|
||||
int from_to[] = { 0,0, 1,1, 2,2 };
|
||||
mixChannels(std::vector<UMat>(1, src_lab), l_ab, from_to, 3);
|
||||
|
||||
fastNlMeansDenoising(l_ab[0], l_ab_denoised[0], h, templateWindowSize, searchWindowSize);
|
||||
fastNlMeansDenoising(l_ab[1], l_ab_denoised[1], hForColorComponents, templateWindowSize, searchWindowSize);
|
||||
|
||||
UMat dst_lab(src.size(), CV_8UC3);
|
||||
mixChannels(l_ab_denoised, std::vector<UMat>(1, dst_lab), from_to, 3);
|
||||
|
||||
cvtColor(dst_lab, dst, COLOR_Lab2LBGR, src.channels());
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,360 @@
|
||||
/*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.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective icvers.
|
||||
//
|
||||
// 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 Intel Corporation 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_FAST_NLMEANS_MULTI_DENOISING_INVOKER_HPP__
|
||||
#define __OPENCV_FAST_NLMEANS_MULTI_DENOISING_INVOKER_HPP__
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <limits>
|
||||
|
||||
#include "fast_nlmeans_denoising_invoker_commons.hpp"
|
||||
#include "arrays.hpp"
|
||||
|
||||
using namespace cv;
|
||||
|
||||
template <typename T, typename IT, typename UIT, typename D, typename WT>
|
||||
struct FastNlMeansMultiDenoisingInvoker :
|
||||
ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
FastNlMeansMultiDenoisingInvoker(const std::vector<Mat>& srcImgs, int imgToDenoiseIndex,
|
||||
int temporalWindowSize, Mat& dst, int template_window_size,
|
||||
int search_window_size, const float *h);
|
||||
|
||||
void operator() (const Range& range) const CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
void operator= (const FastNlMeansMultiDenoisingInvoker&);
|
||||
|
||||
int rows_;
|
||||
int cols_;
|
||||
|
||||
Mat& dst_;
|
||||
|
||||
std::vector<Mat> extended_srcs_;
|
||||
Mat main_extended_src_;
|
||||
int border_size_;
|
||||
|
||||
int template_window_size_;
|
||||
int search_window_size_;
|
||||
int temporal_window_size_;
|
||||
|
||||
int template_window_half_size_;
|
||||
int search_window_half_size_;
|
||||
int temporal_window_half_size_;
|
||||
|
||||
typename pixelInfo<WT>::sampleType fixed_point_mult_;
|
||||
int almost_template_window_size_sq_bin_shift;
|
||||
std::vector<WT> almost_dist2weight;
|
||||
|
||||
void calcDistSumsForFirstElementInRow(int i, Array3d<int>& dist_sums,
|
||||
Array4d<int>& col_dist_sums,
|
||||
Array4d<int>& up_col_dist_sums) const;
|
||||
|
||||
void calcDistSumsForElementInFirstRow(int i, int j, int first_col_num,
|
||||
Array3d<int>& dist_sums, Array4d<int>& col_dist_sums,
|
||||
Array4d<int>& up_col_dist_sums) const;
|
||||
};
|
||||
|
||||
template <typename T, typename IT, typename UIT, typename D, typename WT>
|
||||
FastNlMeansMultiDenoisingInvoker<T, IT, UIT, D, WT>::FastNlMeansMultiDenoisingInvoker(
|
||||
const std::vector<Mat>& srcImgs,
|
||||
int imgToDenoiseIndex,
|
||||
int temporalWindowSize,
|
||||
cv::Mat& dst,
|
||||
int template_window_size,
|
||||
int search_window_size,
|
||||
const float *h) :
|
||||
dst_(dst), extended_srcs_(srcImgs.size())
|
||||
{
|
||||
CV_Assert(srcImgs.size() > 0);
|
||||
CV_Assert(srcImgs[0].channels() == pixelInfo<T>::channels);
|
||||
|
||||
rows_ = srcImgs[0].rows;
|
||||
cols_ = srcImgs[0].cols;
|
||||
|
||||
template_window_half_size_ = template_window_size / 2;
|
||||
search_window_half_size_ = search_window_size / 2;
|
||||
temporal_window_half_size_ = temporalWindowSize / 2;
|
||||
|
||||
template_window_size_ = template_window_half_size_ * 2 + 1;
|
||||
search_window_size_ = search_window_half_size_ * 2 + 1;
|
||||
temporal_window_size_ = temporal_window_half_size_ * 2 + 1;
|
||||
|
||||
border_size_ = search_window_half_size_ + template_window_half_size_;
|
||||
for (int i = 0; i < temporal_window_size_; i++)
|
||||
copyMakeBorder(srcImgs[imgToDenoiseIndex - temporal_window_half_size_ + i], extended_srcs_[i],
|
||||
border_size_, border_size_, border_size_, border_size_, cv::BORDER_DEFAULT);
|
||||
|
||||
main_extended_src_ = extended_srcs_[temporal_window_half_size_];
|
||||
const IT max_estimate_sum_value =
|
||||
(IT)temporal_window_size_ * (IT)search_window_size_ * (IT)search_window_size_ * (IT)pixelInfo<T>::sampleMax();
|
||||
fixed_point_mult_ = (int)std::min<IT>(std::numeric_limits<IT>::max() / max_estimate_sum_value,
|
||||
pixelInfo<WT>::sampleMax());
|
||||
|
||||
// precalc weight for every possible l2 dist between blocks
|
||||
// additional optimization of precalced weights to replace division(averaging) by binary shift
|
||||
int template_window_size_sq = template_window_size_ * template_window_size_;
|
||||
almost_template_window_size_sq_bin_shift = 0;
|
||||
while (1 << almost_template_window_size_sq_bin_shift < template_window_size_sq)
|
||||
almost_template_window_size_sq_bin_shift++;
|
||||
|
||||
int almost_template_window_size_sq = 1 << almost_template_window_size_sq_bin_shift;
|
||||
double almost_dist2actual_dist_multiplier = (double) almost_template_window_size_sq / template_window_size_sq;
|
||||
|
||||
int max_dist = D::template maxDist<T>();
|
||||
int almost_max_dist = (int)(max_dist / almost_dist2actual_dist_multiplier + 1);
|
||||
almost_dist2weight.resize(almost_max_dist);
|
||||
|
||||
for (int almost_dist = 0; almost_dist < almost_max_dist; almost_dist++)
|
||||
{
|
||||
double dist = almost_dist * almost_dist2actual_dist_multiplier;
|
||||
almost_dist2weight[almost_dist] =
|
||||
D::template calcWeight<T, WT>(dist, h, fixed_point_mult_);
|
||||
}
|
||||
|
||||
// additional optimization init end
|
||||
if (dst_.empty())
|
||||
dst_ = Mat::zeros(srcImgs[0].size(), srcImgs[0].type());
|
||||
}
|
||||
|
||||
template <typename T, typename IT, typename UIT, typename D, typename WT>
|
||||
void FastNlMeansMultiDenoisingInvoker<T, IT, UIT, D, WT>::operator() (const Range& range) const
|
||||
{
|
||||
int row_from = range.start;
|
||||
int row_to = range.end - 1;
|
||||
|
||||
Array3d<int> dist_sums(temporal_window_size_, search_window_size_, search_window_size_);
|
||||
|
||||
// for lazy calc optimization
|
||||
Array4d<int> col_dist_sums(template_window_size_, temporal_window_size_, search_window_size_, search_window_size_);
|
||||
|
||||
int first_col_num = -1;
|
||||
Array4d<int> up_col_dist_sums(cols_, temporal_window_size_, search_window_size_, search_window_size_);
|
||||
|
||||
for (int i = row_from; i <= row_to; i++)
|
||||
{
|
||||
for (int j = 0; j < cols_; j++)
|
||||
{
|
||||
int search_window_y = i - search_window_half_size_;
|
||||
int search_window_x = j - search_window_half_size_;
|
||||
|
||||
// calc dist_sums
|
||||
if (j == 0)
|
||||
{
|
||||
calcDistSumsForFirstElementInRow(i, dist_sums, col_dist_sums, up_col_dist_sums);
|
||||
first_col_num = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// calc cur dist_sums using previous dist_sums
|
||||
if (i == row_from)
|
||||
{
|
||||
calcDistSumsForElementInFirstRow(i, j, first_col_num,
|
||||
dist_sums, col_dist_sums, up_col_dist_sums);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
int ay = border_size_ + i;
|
||||
int ax = border_size_ + j + template_window_half_size_;
|
||||
|
||||
int start_by =
|
||||
border_size_ + i - search_window_half_size_;
|
||||
|
||||
int start_bx =
|
||||
border_size_ + j - search_window_half_size_ + template_window_half_size_;
|
||||
|
||||
T a_up = main_extended_src_.at<T>(ay - template_window_half_size_ - 1, ax);
|
||||
T a_down = main_extended_src_.at<T>(ay + template_window_half_size_, ax);
|
||||
|
||||
// copy class member to local variable for optimization
|
||||
int search_window_size = search_window_size_;
|
||||
|
||||
for (int d = 0; d < temporal_window_size_; d++)
|
||||
{
|
||||
Mat cur_extended_src = extended_srcs_[d];
|
||||
Array2d<int> cur_dist_sums = dist_sums[d];
|
||||
Array2d<int> cur_col_dist_sums = col_dist_sums[first_col_num][d];
|
||||
Array2d<int> cur_up_col_dist_sums = up_col_dist_sums[j][d];
|
||||
for (int y = 0; y < search_window_size; y++)
|
||||
{
|
||||
int* dist_sums_row = cur_dist_sums.row_ptr(y);
|
||||
|
||||
int* col_dist_sums_row = cur_col_dist_sums.row_ptr(y);
|
||||
int* up_col_dist_sums_row = cur_up_col_dist_sums.row_ptr(y);
|
||||
|
||||
const T* b_up_ptr = cur_extended_src.ptr<T>(start_by - template_window_half_size_ - 1 + y);
|
||||
const T* b_down_ptr = cur_extended_src.ptr<T>(start_by + template_window_half_size_ + y);
|
||||
|
||||
for (int x = 0; x < search_window_size; x++)
|
||||
{
|
||||
dist_sums_row[x] -= col_dist_sums_row[x];
|
||||
|
||||
col_dist_sums_row[x] = up_col_dist_sums_row[x] +
|
||||
D::template calcUpDownDist<T>(a_up, a_down, b_up_ptr[start_bx + x], b_down_ptr[start_bx + x]);
|
||||
|
||||
dist_sums_row[x] += col_dist_sums_row[x];
|
||||
up_col_dist_sums_row[x] = col_dist_sums_row[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
first_col_num = (first_col_num + 1) % template_window_size_;
|
||||
}
|
||||
|
||||
// calc weights
|
||||
IT estimation[pixelInfo<T>::channels], weights_sum[pixelInfo<WT>::channels];
|
||||
for (size_t channel_num = 0; channel_num < pixelInfo<T>::channels; channel_num++)
|
||||
estimation[channel_num] = 0;
|
||||
for (size_t channel_num = 0; channel_num < pixelInfo<WT>::channels; channel_num++)
|
||||
weights_sum[channel_num] = 0;
|
||||
|
||||
for (int d = 0; d < temporal_window_size_; d++)
|
||||
{
|
||||
const Mat& esrc_d = extended_srcs_[d];
|
||||
for (int y = 0; y < search_window_size_; y++)
|
||||
{
|
||||
const T* cur_row_ptr = esrc_d.ptr<T>(border_size_ + search_window_y + y);
|
||||
|
||||
int* dist_sums_row = dist_sums.row_ptr(d, y);
|
||||
|
||||
for (int x = 0; x < search_window_size_; x++)
|
||||
{
|
||||
int almostAvgDist = dist_sums_row[x] >> almost_template_window_size_sq_bin_shift;
|
||||
|
||||
WT weight = almost_dist2weight[almostAvgDist];
|
||||
T p = cur_row_ptr[border_size_ + search_window_x + x];
|
||||
incWithWeight<T, IT, WT>(estimation, weights_sum, weight, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
divByWeightsSum<IT, UIT, pixelInfo<T>::channels, pixelInfo<WT>::channels>(estimation,
|
||||
weights_sum);
|
||||
dst_.at<T>(i,j) = saturateCastFromArray<T, IT>(estimation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename IT, typename UIT, typename D, typename WT>
|
||||
inline void FastNlMeansMultiDenoisingInvoker<T, IT, UIT, D, WT>::calcDistSumsForFirstElementInRow(
|
||||
int i, Array3d<int>& dist_sums, Array4d<int>& col_dist_sums, Array4d<int>& up_col_dist_sums) const
|
||||
{
|
||||
int j = 0;
|
||||
|
||||
for (int d = 0; d < temporal_window_size_; d++)
|
||||
{
|
||||
Mat cur_extended_src = extended_srcs_[d];
|
||||
for (int y = 0; y < search_window_size_; y++)
|
||||
for (int x = 0; x < search_window_size_; x++)
|
||||
{
|
||||
dist_sums[d][y][x] = 0;
|
||||
for (int tx = 0; tx < template_window_size_; tx++)
|
||||
col_dist_sums[tx][d][y][x] = 0;
|
||||
|
||||
int start_y = i + y - search_window_half_size_;
|
||||
int start_x = j + x - search_window_half_size_;
|
||||
|
||||
int* dist_sums_ptr = &dist_sums[d][y][x];
|
||||
int* col_dist_sums_ptr = &col_dist_sums[0][d][y][x];
|
||||
int col_dist_sums_step = col_dist_sums.step_size(0);
|
||||
for (int tx = -template_window_half_size_; tx <= template_window_half_size_; tx++)
|
||||
{
|
||||
for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++)
|
||||
{
|
||||
int dist = D::template calcDist<T>(
|
||||
main_extended_src_.at<T>(border_size_ + i + ty, border_size_ + j + tx),
|
||||
cur_extended_src.at<T>(border_size_ + start_y + ty, border_size_ + start_x + tx));
|
||||
|
||||
*dist_sums_ptr += dist;
|
||||
*col_dist_sums_ptr += dist;
|
||||
}
|
||||
col_dist_sums_ptr += col_dist_sums_step;
|
||||
}
|
||||
|
||||
up_col_dist_sums[j][d][y][x] = col_dist_sums[template_window_size_ - 1][d][y][x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename IT, typename UIT, typename D, typename WT>
|
||||
inline void FastNlMeansMultiDenoisingInvoker<T, IT, UIT, D, WT>::calcDistSumsForElementInFirstRow(
|
||||
int i, int j, int first_col_num, Array3d<int>& dist_sums,
|
||||
Array4d<int>& col_dist_sums, Array4d<int>& up_col_dist_sums) const
|
||||
{
|
||||
int ay = border_size_ + i;
|
||||
int ax = border_size_ + j + template_window_half_size_;
|
||||
|
||||
int start_by = border_size_ + i - search_window_half_size_;
|
||||
int start_bx = border_size_ + j - search_window_half_size_ + template_window_half_size_;
|
||||
|
||||
int new_last_col_num = first_col_num;
|
||||
|
||||
for (int d = 0; d < temporal_window_size_; d++)
|
||||
{
|
||||
Mat cur_extended_src = extended_srcs_[d];
|
||||
for (int y = 0; y < search_window_size_; y++)
|
||||
for (int x = 0; x < search_window_size_; x++)
|
||||
{
|
||||
dist_sums[d][y][x] -= col_dist_sums[first_col_num][d][y][x];
|
||||
|
||||
col_dist_sums[new_last_col_num][d][y][x] = 0;
|
||||
int by = start_by + y;
|
||||
int bx = start_bx + x;
|
||||
|
||||
int* col_dist_sums_ptr = &col_dist_sums[new_last_col_num][d][y][x];
|
||||
for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++)
|
||||
{
|
||||
*col_dist_sums_ptr += D::template calcDist<T>(
|
||||
main_extended_src_.at<T>(ay + ty, ax),
|
||||
cur_extended_src.at<T>(by + ty, bx));
|
||||
}
|
||||
|
||||
dist_sums[d][y][x] += col_dist_sums[new_last_col_num][d][y][x];
|
||||
|
||||
up_col_dist_sums[j][d][y][x] = col_dist_sums[new_last_col_num][d][y][x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,135 @@
|
||||
/*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 triangleWeights(int length)
|
||||
{
|
||||
// hat function
|
||||
CV_Assert(length >= 2);
|
||||
Mat w(length, 1, CV_32F);
|
||||
int half = length / 2;
|
||||
int maxVal = length - 1;
|
||||
float epsilon = 1e-6f;
|
||||
w.at<float>(0) = epsilon;
|
||||
w.at<float>(length - 1) = epsilon;
|
||||
for (int i = 1; i < length - 1; i++){
|
||||
w.at<float>(i) = (i < half)
|
||||
? static_cast<float>(i)
|
||||
: static_cast<float>(maxVal - i);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
Mat RobertsonWeights(int length)
|
||||
{
|
||||
CV_Assert(length >= 1);
|
||||
Mat weight(length, 1, CV_32FC3);
|
||||
float q = (length - 1) / 4.0f;
|
||||
float e4 = exp(4.f);
|
||||
float scale = e4/(e4 - 1.f);
|
||||
float shift = 1 / (1.f - e4);
|
||||
|
||||
for(int i = 0; i < length; i++) {
|
||||
float value = i / q - 2.0f;
|
||||
value = scale*exp(-value * value) + shift;
|
||||
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, int length)
|
||||
{
|
||||
CV_Assert(length >= 1);
|
||||
Mat response = Mat(length, 1, CV_MAKETYPE(CV_32F, channels));
|
||||
|
||||
if (channels == 1)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
response.at<float>(i) = static_cast<float>(i);
|
||||
}
|
||||
}
|
||||
else if (channels == 3)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
response.at<Vec3f>(i) = Vec3f::all(static_cast<float>(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Error(Error::StsBadArg, "Unsupported number of channels in linearResponse");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 triangleWeights(int length = LDR_SIZE);
|
||||
|
||||
void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation);
|
||||
|
||||
Mat RobertsonWeights(int length = LDR_SIZE);
|
||||
|
||||
Mat linearResponse(int channels, int length = LDR_SIZE);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,800 @@
|
||||
/*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.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective icvers.
|
||||
//
|
||||
// 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 Intel Corporation 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*/
|
||||
|
||||
/* ////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Geometrical transforms on images and matrices: rotation, zoom etc.
|
||||
//
|
||||
// */
|
||||
|
||||
#include <queue>
|
||||
#include <type_traits>
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
using namespace cv;
|
||||
|
||||
#undef CV_MAT_ELEM_PTR_FAST
|
||||
#define CV_MAT_ELEM_PTR_FAST( mat, row, col, pix_size ) \
|
||||
((mat).data.ptr + (size_t)(mat).step*(row) + (pix_size)*(col))
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<std::is_floating_point<T>::value, T>::type round_cast(float val) {
|
||||
return cv::saturate_cast<T>(val);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<!std::is_floating_point<T>::value, T>::type round_cast(float val) {
|
||||
return cv::saturate_cast<T>(val + 0.5);
|
||||
}
|
||||
|
||||
inline float
|
||||
min4( float a, float b, float c, float d )
|
||||
{
|
||||
a = MIN(a,b);
|
||||
c = MIN(c,d);
|
||||
return MIN(a,c);
|
||||
}
|
||||
|
||||
#define KNOWN 0 //known outside narrow band
|
||||
#define BAND 1 //narrow band (known)
|
||||
#define INSIDE 2 //unknown
|
||||
#define CHANGE 3 //servise
|
||||
|
||||
typedef struct CvHeapElem
|
||||
{
|
||||
float T;
|
||||
int i,j;
|
||||
int order; // to keep insertion order
|
||||
|
||||
bool operator > (const CvHeapElem& rhs) const {
|
||||
if (T > rhs.T) {
|
||||
return true;
|
||||
} else if (T < rhs.T) {
|
||||
return false;
|
||||
}
|
||||
return order > rhs.order;
|
||||
}
|
||||
}
|
||||
CvHeapElem;
|
||||
|
||||
|
||||
class CvPriorityQueueFloat
|
||||
{
|
||||
private:
|
||||
CvPriorityQueueFloat(const CvPriorityQueueFloat & ); // copy disabled
|
||||
CvPriorityQueueFloat& operator=(const CvPriorityQueueFloat &); // assign disabled
|
||||
|
||||
protected:
|
||||
std::priority_queue<CvHeapElem, std::vector<CvHeapElem>,std::greater<CvHeapElem> > queue;
|
||||
int next_order;
|
||||
|
||||
public:
|
||||
bool Add(const Mat &f) {
|
||||
int i,j;
|
||||
for (i=0; i<f.rows; i++) {
|
||||
for (j=0; j<f.cols; j++) {
|
||||
if (f.at<uchar>(i, j)!=0) {
|
||||
if (!Push(i,j,0)) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Push(int i, int j, float T) {
|
||||
queue.push({T, i, j, next_order});
|
||||
++next_order;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Pop(int *i, int *j) {
|
||||
if (queue.empty()) {
|
||||
return false;
|
||||
}
|
||||
*i = queue.top().i;
|
||||
*j = queue.top().j;
|
||||
queue.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Pop(int *i, int *j, float *T) {
|
||||
if (queue.empty()) {
|
||||
return false;
|
||||
}
|
||||
*i = queue.top().i;
|
||||
*j = queue.top().j;
|
||||
*T = queue.top().T;
|
||||
queue.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
CvPriorityQueueFloat(void) : queue(), next_order() {
|
||||
}
|
||||
};
|
||||
|
||||
static inline float VectorScalMult(const cv::Point2f& v1, const cv::Point2f& v2)
|
||||
{
|
||||
return v1.x*v2.x+v1.y*v2.y;
|
||||
}
|
||||
|
||||
static inline float VectorLength(const cv::Point2f& v1)
|
||||
{
|
||||
return v1.x*v1.x+v1.y*v1.y;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//HEAP::iterator Heap_Iterator;
|
||||
//HEAP Heap;
|
||||
|
||||
static float FastMarching_solve(int i1,int j1,int i2,int j2, const Mat &f, const Mat &t)
|
||||
{
|
||||
double sol, a11, a22, m12;
|
||||
a11=t.at<float>(i1,j1);
|
||||
a22=t.at<float>(i2,j2);
|
||||
m12=MIN(a11,a22);
|
||||
|
||||
if( f.at<uchar>(i1,j1) != INSIDE )
|
||||
if( f.at<uchar>(i2,j2) != INSIDE )
|
||||
if( fabs(a11-a22) >= 1.0 )
|
||||
sol = 1+m12;
|
||||
else
|
||||
sol = (a11+a22+sqrt((double)(2-(a11-a22)*(a11-a22))))*0.5;
|
||||
else
|
||||
sol = 1+a11;
|
||||
else if( f.at<uchar>(i2,j2) != INSIDE )
|
||||
sol = 1+a22;
|
||||
else
|
||||
sol = 1+m12;
|
||||
|
||||
return (float)sol;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
static void
|
||||
icvCalcFMM(Mat &f, Mat &t, CvPriorityQueueFloat *Heap, bool negate) {
|
||||
int i, j, ii = 0, jj = 0, q;
|
||||
float dist;
|
||||
|
||||
while (Heap->Pop(&ii,&jj)) {
|
||||
|
||||
unsigned known=(negate)?CHANGE:KNOWN;
|
||||
f.at<uchar>(ii,jj) = (uchar)known;
|
||||
|
||||
for (q=0; q<4; q++) {
|
||||
i=0; j=0;
|
||||
if (q==0) {i=ii-1; j=jj;}
|
||||
else if(q==1) {i=ii; j=jj-1;}
|
||||
else if(q==2) {i=ii+1; j=jj;}
|
||||
else {i=ii; j=jj+1;}
|
||||
if ((i<=0)||(j<=0)||(i>f.rows)||(j>f.cols)) continue;
|
||||
|
||||
if (f.at<uchar>(i,j)==INSIDE) {
|
||||
dist = min4(FastMarching_solve(i-1,j,i,j-1,f,t),
|
||||
FastMarching_solve(i+1,j,i,j-1,f,t),
|
||||
FastMarching_solve(i-1,j,i,j+1,f,t),
|
||||
FastMarching_solve(i+1,j,i,j+1,f,t));
|
||||
t.at<float>(i,j) = dist;
|
||||
f.at<uchar>(i,j) = BAND;
|
||||
Heap->Push(i,j,dist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (negate) {
|
||||
for (i=0; i<f.rows; i++) {
|
||||
for(j=0; j<f.cols; j++) {
|
||||
if (f.at<uchar>(i,j) == CHANGE) {
|
||||
f.at<uchar>(i,j) = KNOWN;
|
||||
t.at<float>(i,j) = -t.at<float>(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename data_type>
|
||||
static void
|
||||
icvTeleaInpaintFMM(Mat &f, Mat &t, Mat &out, int range, CvPriorityQueueFloat *Heap ) {
|
||||
int i = 0, j = 0, ii = 0, jj = 0, k, l, q, color = 0;
|
||||
float dist;
|
||||
|
||||
if (out.channels()==3) {
|
||||
typedef Vec<uchar, 3> PixelT;
|
||||
|
||||
while (Heap->Pop(&ii,&jj)) {
|
||||
|
||||
f.at<uchar>(ii,jj) = KNOWN;
|
||||
for(q=0; q<4; q++) {
|
||||
if (q==0) {i=ii-1; j=jj;}
|
||||
else if(q==1) {i=ii; j=jj-1;}
|
||||
else if(q==2) {i=ii+1; j=jj;}
|
||||
else if(q==3) {i=ii; j=jj+1;}
|
||||
if ((i<=0)||(j<=0)||(i>t.rows-1)||(j>t.cols-1)) continue;
|
||||
|
||||
if (f.at<uchar>(i,j)==INSIDE) {
|
||||
dist = min4(FastMarching_solve(i-1,j,i,j-1,f,t),
|
||||
FastMarching_solve(i+1,j,i,j-1,f,t),
|
||||
FastMarching_solve(i-1,j,i,j+1,f,t),
|
||||
FastMarching_solve(i+1,j,i,j+1,f,t));
|
||||
t.at<float>(i,j) = dist;
|
||||
|
||||
cv::Point2f gradT[3];
|
||||
for (color=0; color<=2; color++) {
|
||||
if (f.at<uchar>(i,j+1)!=INSIDE) {
|
||||
if (f.at<uchar>(i,j-1)!=INSIDE) {
|
||||
gradT[color].x=(float)((t.at<float>(i,j+1)-t.at<float>(i,j-1)))*0.5f;
|
||||
} else {
|
||||
gradT[color].x=(float)((t.at<float>(i,j+1)-t.at<float>(i,j)));
|
||||
}
|
||||
} else {
|
||||
if (f.at<uchar>(i,j-1)!=INSIDE) {
|
||||
gradT[color].x=(float)((t.at<float>(i,j)-t.at<float>(i,j-1)));
|
||||
} else {
|
||||
gradT[color].x=0;
|
||||
}
|
||||
}
|
||||
if (f.at<uchar>(i+1,j)!=INSIDE) {
|
||||
if (f.at<uchar>(i-1,j)!=INSIDE) {
|
||||
gradT[color].y=(float)((t.at<float>(i+1,j)-t.at<float>(i-1,j)))*0.5f;
|
||||
} else {
|
||||
gradT[color].y=(float)((t.at<float>(i+1,j)-t.at<float>(i,j)));
|
||||
}
|
||||
} else {
|
||||
if (f.at<uchar>(i-1,j)!=INSIDE) {
|
||||
gradT[color].y=(float)((t.at<float>(i,j)-t.at<float>(i-1,j)));
|
||||
} else {
|
||||
gradT[color].y=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cv::Point2f gradI,r;
|
||||
float Jx[3] = {0,0,0};
|
||||
float Jy[3] = {0,0,0};
|
||||
float Ia[3] = {0,0,0};
|
||||
float s[3] = {1.0e-20f,1.0e-20f,1.0e-20f};
|
||||
float w,dst,lev,dir,sat;
|
||||
|
||||
for (k=i-range; k<=i+range; k++) {
|
||||
int km=k-1+(k==1),kp=k-1-(k==t.rows-2);
|
||||
for (l=j-range; l<=j+range; l++) {
|
||||
int lm=l-1+(l==1),lp=l-1-(l==t.cols-2);
|
||||
if (k>0&&l>0&&k<t.rows-1&&l<t.cols-1) {
|
||||
if ((f.at<uchar>(k,l)!=INSIDE)&&
|
||||
((l-j)*(l-j)+(k-i)*(k-i)<=range*range)) {
|
||||
for (color=0; color<=2; color++) {
|
||||
r.y = (float)(i-k);
|
||||
r.x = (float)(j-l);
|
||||
|
||||
dst = (float)(1./(VectorLength(r)*sqrt((double)VectorLength(r))));
|
||||
lev = (float)(1./(1+fabs(t.at<float>(k,l)-t.at<float>(i,j))));
|
||||
|
||||
dir=VectorScalMult(r,gradT[color]);
|
||||
if (fabs(dir)<=0.01) dir=0.000001f;
|
||||
w = (float)fabs(dst*lev*dir);
|
||||
|
||||
if (f.at<uchar>(k,l+1)!=INSIDE) {
|
||||
if (f.at<uchar>(k,l-1)!=INSIDE) {
|
||||
gradI.x=(float)((out.at<PixelT>(km,lp+1)[color]-out.at<PixelT>(km,lm-1)[color]))*2.0f;
|
||||
} else {
|
||||
gradI.x=(float)((out.at<PixelT>(km,lp+1)[color]-out.at<PixelT>(km,lm)[color]));
|
||||
}
|
||||
} else {
|
||||
if (f.at<uchar>(k,l-1)!=INSIDE) {
|
||||
gradI.x=(float)((out.at<PixelT>(km,lp)[color]-out.at<PixelT>(km,lm-1)[color]));
|
||||
} else {
|
||||
gradI.x=0;
|
||||
}
|
||||
}
|
||||
if (f.at<uchar>(k+1,l)!=INSIDE) {
|
||||
if (f.at<uchar>(k-1,l)!=INSIDE) {
|
||||
gradI.y=(float)((out.at<PixelT>(kp+1,lm)[color]-out.at<PixelT>(km-1,lm)[color]))*2.0f;
|
||||
} else {
|
||||
gradI.y=(float)((out.at<PixelT>(kp+1,lm)[color]-out.at<PixelT>(km,lm)[color]));
|
||||
}
|
||||
} else {
|
||||
if (f.at<uchar>(k-1,l)!=INSIDE) {
|
||||
gradI.y=(float)((out.at<PixelT>(kp,lm)[color]-out.at<PixelT>(km-1,lm)[color]));
|
||||
} else {
|
||||
gradI.y=0;
|
||||
}
|
||||
}
|
||||
Ia[color] += (float)w * (float)(out.at<PixelT>(k-1,l-1)[color]);
|
||||
Jx[color] -= (float)w * (float)(gradI.x*r.x);
|
||||
Jy[color] -= (float)w * (float)(gradI.y*r.y);
|
||||
s[color] += w;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (color=0; color<=2; color++) {
|
||||
sat = (float)(Ia[color]/s[color]+(Jx[color]+Jy[color])/(sqrt(Jx[color]*Jx[color]+Jy[color]*Jy[color])+1.0e-20f));
|
||||
out.at<PixelT>(i-1,j-1)[color] = round_cast<uchar>(sat);
|
||||
}
|
||||
|
||||
f.at<uchar>(i,j) = BAND;
|
||||
Heap->Push(i,j,dist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (out.channels()==1) {
|
||||
|
||||
while (Heap->Pop(&ii,&jj)) {
|
||||
|
||||
f.at<uchar>(ii,jj) = KNOWN;
|
||||
for(q=0; q<4; q++) {
|
||||
if (q==0) {i=ii-1; j=jj;}
|
||||
else if(q==1) {i=ii; j=jj-1;}
|
||||
else if(q==2) {i=ii+1; j=jj;}
|
||||
else if(q==3) {i=ii; j=jj+1;}
|
||||
if ((i<=0)||(j<=0)||(i>t.rows-1)||(j>t.cols-1)) continue;
|
||||
|
||||
if (f.at<uchar>(i,j)==INSIDE) {
|
||||
dist = min4(FastMarching_solve(i-1,j,i,j-1,f,t),
|
||||
FastMarching_solve(i+1,j,i,j-1,f,t),
|
||||
FastMarching_solve(i-1,j,i,j+1,f,t),
|
||||
FastMarching_solve(i+1,j,i,j+1,f,t));
|
||||
t.at<float>(i,j) = dist;
|
||||
|
||||
for (color=0; color<=0; color++) {
|
||||
cv::Point2f gradI,gradT,r;
|
||||
float Ia=0,Jx=0,Jy=0,s=1.0e-20f,w,dst,lev,dir,sat;
|
||||
|
||||
if (f.at<uchar>(i,j+1)!=INSIDE) {
|
||||
if (f.at<uchar>(i,j-1)!=INSIDE) {
|
||||
gradT.x=(float)((t.at<float>(i,j+1)-t.at<float>(i,j-1)))*0.5f;
|
||||
} else {
|
||||
gradT.x=(float)((t.at<float>(i,j+1)-t.at<float>(i,j)));
|
||||
}
|
||||
} else {
|
||||
if (f.at<uchar>(i,j-1)!=INSIDE) {
|
||||
gradT.x=(float)((t.at<float>(i,j)-t.at<float>(i,j-1)));
|
||||
} else {
|
||||
gradT.x=0;
|
||||
}
|
||||
}
|
||||
if (f.at<uchar>(i+1,j)!=INSIDE) {
|
||||
if (f.at<uchar>(i-1,j)!=INSIDE) {
|
||||
gradT.y=(float)((t.at<float>(i+1,j)-t.at<float>(i-1,j)))*0.5f;
|
||||
} else {
|
||||
gradT.y=(float)((t.at<float>(i+1,j)-t.at<float>(i,j)));
|
||||
}
|
||||
} else {
|
||||
if (f.at<uchar>(i-1,j)!=INSIDE) {
|
||||
gradT.y=(float)((t.at<float>(i,j)-t.at<float>(i-1,j)));
|
||||
} else {
|
||||
gradT.y=0;
|
||||
}
|
||||
}
|
||||
for (k=i-range; k<=i+range; k++) {
|
||||
int km=k-1+(k==1),kp=k-1-(k==t.rows-2);
|
||||
for (l=j-range; l<=j+range; l++) {
|
||||
int lm=l-1+(l==1),lp=l-1-(l==t.cols-2);
|
||||
if (k>0&&l>0&&k<t.rows-1&&l<t.cols-1) {
|
||||
if ((f.at<uchar>(k,l)!=INSIDE)&&
|
||||
((l-j)*(l-j)+(k-i)*(k-i)<=range*range)) {
|
||||
r.y = (float)(i-k);
|
||||
r.x = (float)(j-l);
|
||||
|
||||
dst = (float)(1./(VectorLength(r)*sqrt(VectorLength(r))));
|
||||
lev = (float)(1./(1+fabs(t.at<float>(k,l)-t.at<float>(i,j))));
|
||||
|
||||
dir=VectorScalMult(r,gradT);
|
||||
if (fabs(dir)<=0.01) dir=0.000001f;
|
||||
w = (float)fabs(dst*lev*dir);
|
||||
|
||||
if (f.at<uchar>(k,l+1)!=INSIDE) {
|
||||
if (f.at<uchar>(k,l-1)!=INSIDE) {
|
||||
gradI.x=(float)((out.at<data_type>(km,lp+1)-out.at<data_type>(km,lm-1)))*2.0f;
|
||||
} else {
|
||||
gradI.x=(float)((out.at<data_type>(km,lp+1)-out.at<data_type>(km,lm)));
|
||||
}
|
||||
} else {
|
||||
if (f.at<uchar>(k,l-1)!=INSIDE) {
|
||||
gradI.x=(float)((out.at<data_type>(km,lp)-out.at<data_type>(km,lm-1)));
|
||||
} else {
|
||||
gradI.x=0;
|
||||
}
|
||||
}
|
||||
if (f.at<uchar>(k+1,l)!=INSIDE) {
|
||||
if (f.at<uchar>(k-1,l)!=INSIDE) {
|
||||
gradI.y=(float)((out.at<data_type>(kp+1,lm)-out.at<data_type>(km-1,lm)))*2.0f;
|
||||
} else {
|
||||
gradI.y=(float)((out.at<data_type>(kp+1,lm)-out.at<data_type>(km,lm)));
|
||||
}
|
||||
} else {
|
||||
if (f.at<uchar>(k-1,l)!=INSIDE) {
|
||||
gradI.y=(float)((out.at<data_type>(kp,lm)-out.at<data_type>(km-1,lm)));
|
||||
} else {
|
||||
gradI.y=0;
|
||||
}
|
||||
}
|
||||
Ia += (float)w * (float)(out.at<data_type>(k-1,l-1));
|
||||
Jx -= (float)w * (float)(gradI.x*r.x);
|
||||
Jy -= (float)w * (float)(gradI.y*r.y);
|
||||
s += w;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sat = (float)(Ia/s+(Jx+Jy)/(sqrt(Jx*Jx+Jy*Jy)+1.0e-20f));
|
||||
{
|
||||
out.at<data_type>(i-1,j-1) = round_cast<data_type>(sat);
|
||||
}
|
||||
}
|
||||
|
||||
f.at<uchar>(i,j) = BAND;
|
||||
Heap->Push(i,j,dist);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename data_type>
|
||||
static void
|
||||
icvNSInpaintFMM(Mat &f, Mat &t, Mat &out, int range, CvPriorityQueueFloat *Heap) {
|
||||
int i = 0, j = 0, ii = 0, jj = 0, k, l, q, color = 0;
|
||||
float dist;
|
||||
|
||||
if (out.channels()==3) {
|
||||
typedef Vec<uchar, 3> PixelT;
|
||||
|
||||
while (Heap->Pop(&ii,&jj)) {
|
||||
|
||||
f.at<uchar>(ii,jj) = KNOWN;
|
||||
for(q=0; q<4; q++) {
|
||||
if (q==0) {i=ii-1; j=jj;}
|
||||
else if(q==1) {i=ii; j=jj-1;}
|
||||
else if(q==2) {i=ii+1; j=jj;}
|
||||
else if(q==3) {i=ii; j=jj+1;}
|
||||
if ((i<=0)||(j<=0)||(i>t.rows-1)||(j>t.cols-1)) continue;
|
||||
|
||||
if (f.at<uchar>(i,j)==INSIDE) {
|
||||
dist = min4(FastMarching_solve(i-1,j,i,j-1,f,t),
|
||||
FastMarching_solve(i+1,j,i,j-1,f,t),
|
||||
FastMarching_solve(i-1,j,i,j+1,f,t),
|
||||
FastMarching_solve(i+1,j,i,j+1,f,t));
|
||||
t.at<float>(i,j) = dist;
|
||||
|
||||
cv::Point2f gradI,r;
|
||||
float Ia[3]={0,0,0};
|
||||
float s[3]={1.0e-20f,1.0e-20f,1.0e-20f};
|
||||
float w,dst,dir;
|
||||
|
||||
for (k=i-range; k<=i+range; k++) {
|
||||
int km=k-1+(k==1),kp=k-1-(k==f.rows-2);
|
||||
for (l=j-range; l<=j+range; l++) {
|
||||
int lm=l-1+(l==1),lp=l-1-(l==f.cols-2);
|
||||
if (k>0&&l>0&&k<f.rows-1&&l<f.cols-1) {
|
||||
if ((f.at<uchar>(k,l)!=INSIDE)&&
|
||||
((l-j)*(l-j)+(k-i)*(k-i)<=range*range)) {
|
||||
for (color=0; color<=2; color++) {
|
||||
r.y=(float)(k-i);
|
||||
r.x=(float)(l-j);
|
||||
|
||||
dst = 1/(VectorLength(r)*VectorLength(r)+1);
|
||||
|
||||
if (f.at<uchar>(k+1,l)!=INSIDE) {
|
||||
if (f.at<uchar>(k-1,l)!=INSIDE) {
|
||||
gradI.x=(float)(abs(out.at<PixelT>(kp+1,lm)[color]-out.at<PixelT>(kp,lm)[color])+
|
||||
abs(out.at<PixelT>(kp,lm)[color]-out.at<PixelT>(km-1,lm)[color]));
|
||||
} else {
|
||||
gradI.x=(float)(abs(out.at<PixelT>(kp+1,lm)[color]-out.at<PixelT>(kp,lm)[color]))*2.0f;
|
||||
}
|
||||
} else {
|
||||
if (f.at<uchar>(k-1,l)!=INSIDE) {
|
||||
gradI.x=(float)(abs(out.at<PixelT>(kp,lm)[color]-out.at<PixelT>(km-1,lm)[color]))*2.0f;
|
||||
} else {
|
||||
gradI.x=0;
|
||||
}
|
||||
}
|
||||
if (f.at<uchar>(k,l+1)!=INSIDE) {
|
||||
if (f.at<uchar>(k,l-1)!=INSIDE) {
|
||||
gradI.y=(float)(abs(out.at<PixelT>(km,lp+1)[color]-out.at<PixelT>(km,lm)[color])+
|
||||
abs(out.at<PixelT>(km,lm)[color]-out.at<PixelT>(km,lm-1)[color]));
|
||||
} else {
|
||||
gradI.y=(float)(abs(out.at<PixelT>(km,lp+1)[color]-out.at<PixelT>(km,lm)[color]))*2.0f;
|
||||
}
|
||||
} else {
|
||||
if (f.at<uchar>(k,l-1)!=INSIDE) {
|
||||
gradI.y=(float)(abs(out.at<PixelT>(km,lm)[color]-out.at<PixelT>(km,lm-1)[color]))*2.0f;
|
||||
} else {
|
||||
gradI.y=0;
|
||||
}
|
||||
}
|
||||
|
||||
gradI.x=-gradI.x;
|
||||
dir=VectorScalMult(r,gradI);
|
||||
|
||||
if (fabs(dir)<=0.01) {
|
||||
dir=0.000001f;
|
||||
} else {
|
||||
dir = (float)fabs(VectorScalMult(r,gradI)/sqrt(VectorLength(r)*VectorLength(gradI)));
|
||||
}
|
||||
w = dst*dir;
|
||||
Ia[color] += (float)w * (float)(out.at<PixelT>(k-1,l-1)[color]);
|
||||
s[color] += w;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (color=0; color<=2; color++) {
|
||||
out.at<PixelT>(i-1,j-1)[color] = cv::saturate_cast<uchar>((double)Ia[color]/s[color]);
|
||||
}
|
||||
|
||||
f.at<uchar>(i,j) = BAND;
|
||||
Heap->Push(i,j,dist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (out.channels()==1) {
|
||||
|
||||
while (Heap->Pop(&ii,&jj)) {
|
||||
|
||||
f.at<uchar>(ii,jj) = KNOWN;
|
||||
for(q=0; q<4; q++) {
|
||||
if (q==0) {i=ii-1; j=jj;}
|
||||
else if(q==1) {i=ii; j=jj-1;}
|
||||
else if(q==2) {i=ii+1; j=jj;}
|
||||
else if(q==3) {i=ii; j=jj+1;}
|
||||
if ((i<=0)||(j<=0)||(i>t.rows-1)||(j>t.cols-1)) continue;
|
||||
|
||||
if (f.at<uchar>(i,j)==INSIDE) {
|
||||
dist = min4(FastMarching_solve(i-1,j,i,j-1,f,t),
|
||||
FastMarching_solve(i+1,j,i,j-1,f,t),
|
||||
FastMarching_solve(i-1,j,i,j+1,f,t),
|
||||
FastMarching_solve(i+1,j,i,j+1,f,t));
|
||||
t.at<float>(i,j) = dist;
|
||||
|
||||
{
|
||||
cv::Point2f gradI,r;
|
||||
float Ia=0,s=1.0e-20f,w,dst,dir;
|
||||
|
||||
for (k=i-range; k<=i+range; k++) {
|
||||
int km=k-1+(k==1),kp=k-1-(k==t.rows-2);
|
||||
for (l=j-range; l<=j+range; l++) {
|
||||
int lm=l-1+(l==1),lp=l-1-(l==t.cols-2);
|
||||
if (k>0&&l>0&&k<t.rows-1&&l<t.cols-1) {
|
||||
if ((f.at<uchar>(k,l)!=INSIDE)&&
|
||||
((l-j)*(l-j)+(k-i)*(k-i)<=range*range)) {
|
||||
r.y=(float)(i-k);
|
||||
r.x=(float)(j-l);
|
||||
|
||||
dst = 1/(VectorLength(r)*VectorLength(r)+1);
|
||||
|
||||
if (f.at<uchar>(k+1,l)!=INSIDE) {
|
||||
if (f.at<uchar>(k-1,l)!=INSIDE) {
|
||||
gradI.x=(float)(std::abs(out.at<data_type>(kp+1,lm)-out.at<data_type>(kp,lm))+
|
||||
std::abs(out.at<data_type>(kp,lm)-out.at<data_type>(km-1,lm)));
|
||||
} else {
|
||||
gradI.x=(float)(std::abs(out.at<data_type>(kp+1,lm)-out.at<data_type>(kp,lm)))*2.0f;
|
||||
}
|
||||
} else {
|
||||
if (f.at<uchar>(k-1,l)!=INSIDE) {
|
||||
gradI.x=(float)(std::abs(out.at<data_type>(kp,lm)-out.at<data_type>(km-1,lm)))*2.0f;
|
||||
} else {
|
||||
gradI.x=0;
|
||||
}
|
||||
}
|
||||
if (f.at<uchar>(k,l+1)!=INSIDE) {
|
||||
if (f.at<uchar>(k,l-1)!=INSIDE) {
|
||||
gradI.y=(float)(std::abs(out.at<data_type>(km,lp+1)-out.at<data_type>(km,lm))+
|
||||
std::abs(out.at<data_type>(km,lm)-out.at<data_type>(km,lm-1)));
|
||||
} else {
|
||||
gradI.y=(float)(std::abs(out.at<data_type>(km,lp+1)-out.at<data_type>(km,lm)))*2.0f;
|
||||
}
|
||||
} else {
|
||||
if (f.at<uchar>(k,l-1)!=INSIDE) {
|
||||
gradI.y=(float)(std::abs(out.at<data_type>(km,lm)-out.at<data_type>(km,lm-1)))*2.0f;
|
||||
} else {
|
||||
gradI.y=0;
|
||||
}
|
||||
}
|
||||
|
||||
gradI.x=-gradI.x;
|
||||
dir=VectorScalMult(r,gradI);
|
||||
|
||||
if (fabs(dir)<=0.01) {
|
||||
dir=0.000001f;
|
||||
} else {
|
||||
dir = (float)fabs(VectorScalMult(r,gradI)/sqrt(VectorLength(r)*VectorLength(gradI)));
|
||||
}
|
||||
w = dst*dir;
|
||||
Ia += (float)w * (float)(out.at<data_type>(k-1,l-1));
|
||||
s += w;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out.at<data_type>(i-1,j-1) = cv::saturate_cast<data_type>((double)Ia/s);
|
||||
}
|
||||
|
||||
f.at<uchar>(i,j) = BAND;
|
||||
Heap->Push(i,j,dist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#define SET_BORDER1_C1(image,type,value) {\
|
||||
int i,j;\
|
||||
for(j=0; j<image.cols; j++) {\
|
||||
image.at<type>(0,j) = value;\
|
||||
}\
|
||||
for (i=1; i<image.rows-1; i++) {\
|
||||
image.at<type>(i,0) = image.at<type>(i,image.cols-1) = value;\
|
||||
}\
|
||||
for(j=0; j<image.cols; j++) {\
|
||||
image.at<type>(erows-1,j) = value;\
|
||||
}\
|
||||
}
|
||||
|
||||
#define COPY_MASK_BORDER1_C1(src,dst,type) {\
|
||||
int i,j;\
|
||||
for (i=0; i<src.rows; i++) {\
|
||||
for(j=0; j<src.cols; j++) {\
|
||||
if (src.at<type>(i,j)!=0)\
|
||||
dst.at<type>(i+1,j+1) = INSIDE;\
|
||||
}\
|
||||
}\
|
||||
}
|
||||
|
||||
static void
|
||||
icvInpaint( const Mat &input_img, const Mat &inpaint_mask, Mat &output_img,
|
||||
double inpaintRange, int flags )
|
||||
{
|
||||
cv::Mat mask, band, f, t, out;
|
||||
cv::Ptr<CvPriorityQueueFloat> Heap, Out;
|
||||
cv::Mat el_range, el_cross; // structuring elements for dilate
|
||||
|
||||
int range=cvRound(inpaintRange);
|
||||
int erows, ecols;
|
||||
|
||||
if((input_img.size() != output_img.size()) || (input_img.size() != inpaint_mask.size()))
|
||||
CV_Error( cv::Error::StsUnmatchedSizes, "All the input and output images must have the same size" );
|
||||
|
||||
if( (input_img.type() != CV_8U &&
|
||||
input_img.type() != CV_16U &&
|
||||
input_img.type() != CV_32F &&
|
||||
input_img.type() != CV_8UC3) ||
|
||||
(input_img.type() != output_img.type()) )
|
||||
CV_Error( cv::Error::StsUnsupportedFormat,
|
||||
"8-bit, 16-bit unsigned or 32-bit float 1-channel and 8-bit 3-channel input/output images are supported" );
|
||||
|
||||
if( inpaint_mask.type() != CV_8UC1 )
|
||||
CV_Error( cv::Error::StsUnsupportedFormat, "The mask must be 8-bit 1-channel image" );
|
||||
|
||||
range = MAX(range,1);
|
||||
range = MIN(range,100);
|
||||
|
||||
ecols = input_img.cols + 2;
|
||||
erows = input_img.rows + 2;
|
||||
|
||||
f.create(erows, ecols, CV_8UC1);
|
||||
t.create(erows, ecols, CV_32FC1);
|
||||
band.create(erows, ecols, CV_8UC1);
|
||||
mask.create(erows, ecols, CV_8UC1);
|
||||
el_cross = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3), cv::Point(1, 1));
|
||||
|
||||
input_img.copyTo( output_img );
|
||||
mask.setTo(Scalar(KNOWN,0,0,0));
|
||||
COPY_MASK_BORDER1_C1(inpaint_mask,mask,uchar);
|
||||
SET_BORDER1_C1(mask,uchar,0);
|
||||
f.setTo(Scalar(KNOWN,0,0,0));
|
||||
t.setTo(Scalar(1.0e6f,0,0,0));
|
||||
cv::dilate(mask, band, el_cross, cv::Point(1, 1));
|
||||
Heap=cv::makePtr<CvPriorityQueueFloat>();
|
||||
subtract(band, mask, band);
|
||||
SET_BORDER1_C1(band,uchar,0);
|
||||
if (!Heap->Add(band))
|
||||
return;
|
||||
|
||||
f.setTo(Scalar(BAND,0,0,0),band);
|
||||
f.setTo(Scalar(INSIDE,0,0,0),mask);
|
||||
t.setTo(Scalar(0,0,0,0),band);
|
||||
|
||||
if( flags == cv::INPAINT_TELEA )
|
||||
{
|
||||
out.create(erows, ecols, CV_8UC1);
|
||||
el_range = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2 * range + 1, 2 * range + 1));
|
||||
cv::dilate(mask, out, el_range);
|
||||
subtract(out, mask, out);
|
||||
Out=cv::makePtr<CvPriorityQueueFloat>();
|
||||
if (!Out->Add(band))
|
||||
return;
|
||||
subtract(out, band, out);
|
||||
SET_BORDER1_C1(out,uchar,0);
|
||||
icvCalcFMM(out,t,Out,true);
|
||||
switch(output_img.depth())
|
||||
{
|
||||
case CV_8U:
|
||||
icvTeleaInpaintFMM<uchar>(mask,t,output_img,range,Heap);
|
||||
break;
|
||||
case CV_16U:
|
||||
icvTeleaInpaintFMM<ushort>(mask,t,output_img,range,Heap);
|
||||
break;
|
||||
case CV_32F:
|
||||
icvTeleaInpaintFMM<float>(mask,t,output_img,range,Heap);
|
||||
break;
|
||||
default:
|
||||
CV_Error( cv::Error::StsBadArg, "Unsupportedformat of the input image" );
|
||||
}
|
||||
}
|
||||
else if (flags == cv::INPAINT_NS) {
|
||||
switch(output_img.depth())
|
||||
{
|
||||
case CV_8U:
|
||||
icvNSInpaintFMM<uchar>(mask,t,output_img,range,Heap);
|
||||
break;
|
||||
case CV_16U:
|
||||
icvNSInpaintFMM<ushort>(mask,t,output_img,range,Heap);
|
||||
break;
|
||||
case CV_32F:
|
||||
icvNSInpaintFMM<float>(mask,t,output_img,range,Heap);
|
||||
break;
|
||||
default:
|
||||
CV_Error( cv::Error::StsBadArg, "Unsupported format of the input image" );
|
||||
}
|
||||
} else {
|
||||
CV_Error( cv::Error::StsBadArg, "The flags argument must be one of INPAINT_TELEA or INPAINT_NS" );
|
||||
}
|
||||
}
|
||||
|
||||
void cv::inpaint( InputArray _src, InputArray _mask, OutputArray _dst,
|
||||
double inpaintRange, int flags )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat src = _src.getMat(), mask = _mask.getMat();
|
||||
_dst.create( src.size(), src.type() );
|
||||
Mat dst = _dst.getMat();
|
||||
icvInpaint( src, mask, dst, inpaintRange, flags );
|
||||
}
|
||||
@@ -0,0 +1,793 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Copyright (C) 2020, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
|
||||
#include "precomp.hpp"
|
||||
//#include "opencv2/imgproc/segmentation.hpp"
|
||||
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
|
||||
#include <queue> // std::priority_queue
|
||||
|
||||
namespace cv {
|
||||
namespace segmentation {
|
||||
|
||||
namespace {
|
||||
|
||||
// 0 1 2
|
||||
// 3 x 4
|
||||
// 5 6 7
|
||||
static const int neighbors[8][2] = {
|
||||
{ -1, -1 },
|
||||
{ 0, -1 },
|
||||
{ 1, -1 },
|
||||
{ -1, 0 },
|
||||
{ 1, 0 },
|
||||
{ -1, 1 },
|
||||
{ 0, 1 },
|
||||
{ 1, 1 },
|
||||
};
|
||||
|
||||
// encoded reverse direction
|
||||
static const int neighbors_encode[8] = {
|
||||
7+1, 6+1, 5+1,
|
||||
4+1, 3+1,
|
||||
2+1, 1+1, 0+1
|
||||
};
|
||||
|
||||
#define ACOS_TABLE_SIZE 64
|
||||
// acos_table[x + ACOS_TABLE_SIZE] = acos(x / ACOS_TABLE_SIZE) / CV_PI (see add_local_cost)
|
||||
// x = [ -ACOS_TABLE_SIZE .. ACOS_TABLE_SIZE ]
|
||||
float* getAcosTable()
|
||||
{
|
||||
constexpr int N = ACOS_TABLE_SIZE;
|
||||
static bool initialized = false;
|
||||
static float acos_table[2*N + 1] = { 0 };
|
||||
if (!initialized)
|
||||
{
|
||||
const float CV_PI_inv = static_cast<float>(1.0 / CV_PI);
|
||||
for (int i = -N; i <= N; i++)
|
||||
{
|
||||
acos_table[i + N] = acosf(i / (float)N) * CV_PI_inv;
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
return acos_table;
|
||||
}
|
||||
|
||||
} // namespace anon
|
||||
|
||||
struct IntelligentScissorsMB::Impl
|
||||
{
|
||||
// proposed weights from the article (sum = 1.0)
|
||||
float weight_non_edge = 0.43f;
|
||||
float weight_gradient_direction = 0.43f;
|
||||
float weight_gradient_magnitude = 0.14f;
|
||||
|
||||
enum EdgeFeatureMode {
|
||||
FEATURE_ZERO_CROSSING = 0,
|
||||
FEATURE_CANNY
|
||||
};
|
||||
EdgeFeatureMode edge_mode = FEATURE_ZERO_CROSSING;
|
||||
|
||||
// FEATURE_ZERO_CROSSING
|
||||
float edge_gradient_magnitude_min_value = 0.0f;
|
||||
|
||||
// FEATURE_CANNY
|
||||
double edge_canny_threshold1 = 10;
|
||||
double edge_canny_threshold2 = 100;
|
||||
int edge_canny_apertureSize = 3;
|
||||
bool edge_canny_L2gradient = false;
|
||||
|
||||
|
||||
float gradient_magnitude_threshold_max = 0.0f; // disabled thresholding
|
||||
|
||||
int sobelKernelSize = 3; // 1 or 3
|
||||
int laplacianKernelSize = 3; // 1 or 3
|
||||
|
||||
// image features
|
||||
Mat_<Point2f> gradient_direction; ///< I: normalized laplacian x/y components
|
||||
Mat_<float> gradient_magnitude; ///< Fg: gradient cost function
|
||||
Mat_<uchar> non_edge_feature; ///< Fz: zero-crossing function
|
||||
|
||||
float weight_non_edge_compute = 0.0f;
|
||||
|
||||
// encoded paths map (produced by `buildMap()`)
|
||||
Mat_<uchar> optimalPathsMap;
|
||||
|
||||
void resetFeatures_()
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
gradient_direction.release();
|
||||
gradient_magnitude.release();
|
||||
non_edge_feature.release();
|
||||
|
||||
weight_non_edge_compute = weight_non_edge;
|
||||
|
||||
optimalPathsMap.release();
|
||||
}
|
||||
|
||||
Size src_size;
|
||||
Mat image_;
|
||||
Mat grayscale_;
|
||||
void initImage_(InputArray image)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
if (!image_.empty())
|
||||
return;
|
||||
CV_CheckType(image.type(), image.type() == CV_8UC1 || image.type() == CV_8UC3 || image.type() == CV_8UC4, "");
|
||||
src_size = image.size();
|
||||
image_ = image.getMat();
|
||||
}
|
||||
void initGrayscale_(InputArray image)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
if (!grayscale_.empty())
|
||||
return;
|
||||
CV_Assert(!image.empty());
|
||||
CV_CheckType(image.type(), image.type() == CV_8UC1 || image.type() == CV_8UC3 || image.type() == CV_8UC4, "");
|
||||
src_size = image.size();
|
||||
if (image.channels() > 1)
|
||||
cvtColor(image, grayscale_, COLOR_BGR2GRAY);
|
||||
else
|
||||
grayscale_ = image.getMat();
|
||||
}
|
||||
Mat Ix_, Iy_;
|
||||
void initImageDerives_(InputArray image)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
if (!Ix_.empty())
|
||||
return;
|
||||
initGrayscale_(image);
|
||||
Sobel(grayscale_, Ix_, CV_32FC1, 1, 0, sobelKernelSize);
|
||||
Sobel(grayscale_, Iy_, CV_32FC1, 0, 1, sobelKernelSize);
|
||||
}
|
||||
Mat image_magnitude_;
|
||||
void initImageMagnitude_(InputArray image)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
if (!image_magnitude_.empty())
|
||||
return;
|
||||
initImageDerives_(image);
|
||||
magnitude(Ix_, Iy_, image_magnitude_);
|
||||
}
|
||||
|
||||
void cleanupFeaturesTemporaryArrays_()
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
image_.release();
|
||||
grayscale_.release();
|
||||
Ix_.release();
|
||||
Iy_.release();
|
||||
image_magnitude_.release();
|
||||
}
|
||||
|
||||
Impl()
|
||||
{
|
||||
// nothing
|
||||
CV_TRACE_FUNCTION();
|
||||
}
|
||||
|
||||
void setWeights(float weight_non_edge_, float weight_gradient_direction_, float weight_gradient_magnitude_)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
CV_CheckGE(weight_non_edge_, 0.0f, "");
|
||||
CV_CheckGE(weight_gradient_direction_, 0.0f, "");
|
||||
CV_CheckGE(weight_gradient_magnitude_, 0.0f, "");
|
||||
CV_CheckGE(weight_non_edge_ + weight_gradient_direction_ + weight_gradient_magnitude_, FLT_EPSILON, "Sum of weights must be greater than zero");
|
||||
weight_non_edge = weight_non_edge_;
|
||||
weight_gradient_direction = weight_gradient_direction_;
|
||||
weight_gradient_magnitude = weight_gradient_magnitude_;
|
||||
resetFeatures_();
|
||||
}
|
||||
|
||||
void setGradientMagnitudeMaxLimit(float gradient_magnitude_threshold_max_)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
CV_CheckGE(gradient_magnitude_threshold_max_, 0.0f, "");
|
||||
gradient_magnitude_threshold_max = gradient_magnitude_threshold_max_;
|
||||
resetFeatures_();
|
||||
}
|
||||
|
||||
void setEdgeFeatureZeroCrossingParameters(float gradient_magnitude_min_value_)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
CV_CheckGE(gradient_magnitude_min_value_, 0.0f, "");
|
||||
edge_mode = FEATURE_ZERO_CROSSING;
|
||||
edge_gradient_magnitude_min_value = gradient_magnitude_min_value_;
|
||||
resetFeatures_();
|
||||
}
|
||||
|
||||
void setEdgeFeatureCannyParameters(
|
||||
double threshold1, double threshold2,
|
||||
int apertureSize = 3, bool L2gradient = false
|
||||
)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
CV_CheckGE(threshold1, 0.0, "");
|
||||
CV_CheckGE(threshold2, 0.0, "");
|
||||
edge_mode = FEATURE_CANNY;
|
||||
edge_canny_threshold1 = threshold1;
|
||||
edge_canny_threshold2 = threshold2;
|
||||
edge_canny_apertureSize = apertureSize;
|
||||
edge_canny_L2gradient = L2gradient;
|
||||
resetFeatures_();
|
||||
}
|
||||
|
||||
void applyImageFeatures(
|
||||
InputArray non_edge, InputArray gradient_direction_, InputArray gradient_magnitude_,
|
||||
InputArray image
|
||||
)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
resetFeatures_();
|
||||
cleanupFeaturesTemporaryArrays_();
|
||||
|
||||
src_size = Size(0, 0);
|
||||
if (!non_edge.empty())
|
||||
src_size = non_edge.size();
|
||||
if (!gradient_direction_.empty())
|
||||
{
|
||||
Size gradient_direction_size = gradient_direction_.size();
|
||||
if (!src_size.empty())
|
||||
CV_CheckEQ(src_size, gradient_direction_size, "");
|
||||
else
|
||||
src_size = gradient_direction_size;
|
||||
}
|
||||
if (!gradient_magnitude_.empty())
|
||||
{
|
||||
Size gradient_magnitude_size = gradient_magnitude_.size();
|
||||
if (!src_size.empty())
|
||||
CV_CheckEQ(src_size, gradient_magnitude_size, "");
|
||||
else
|
||||
src_size = gradient_magnitude_size;
|
||||
}
|
||||
if (!image.empty())
|
||||
{
|
||||
Size image_size = image.size();
|
||||
if (!src_size.empty())
|
||||
CV_CheckEQ(src_size, image_size, "");
|
||||
else
|
||||
src_size = image_size;
|
||||
}
|
||||
// src_size must be filled
|
||||
CV_Assert(!src_size.empty());
|
||||
|
||||
if (!non_edge.empty())
|
||||
{
|
||||
CV_CheckTypeEQ(non_edge.type(), CV_8UC1, "");
|
||||
non_edge_feature = non_edge.getMat();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (weight_non_edge == 0.0f)
|
||||
{
|
||||
non_edge_feature.create(src_size);
|
||||
non_edge_feature.setTo(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (image.empty())
|
||||
CV_Error(Error::StsBadArg, "Non-edge feature parameter is missing. Input image parameter is required to extract this feature");
|
||||
extractEdgeFeature_(image);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gradient_direction_.empty())
|
||||
{
|
||||
CV_CheckTypeEQ(gradient_direction_.type(), CV_32FC2, "");
|
||||
gradient_direction = gradient_direction_.getMat();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (weight_gradient_direction == 0.0f)
|
||||
{
|
||||
gradient_direction.create(src_size);
|
||||
gradient_direction.setTo(Scalar::all(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (image.empty())
|
||||
CV_Error(Error::StsBadArg, "Gradient direction feature parameter is missing. Input image parameter is required to extract this feature");
|
||||
extractGradientDirection_(image);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gradient_magnitude_.empty())
|
||||
{
|
||||
CV_CheckTypeEQ(gradient_magnitude_.type(), CV_32FC1, "");
|
||||
gradient_magnitude = gradient_magnitude_.getMat();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (weight_gradient_magnitude == 0.0f)
|
||||
{
|
||||
gradient_magnitude.create(src_size);
|
||||
gradient_magnitude.setTo(Scalar::all(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (image.empty())
|
||||
CV_Error(Error::StsBadArg, "Gradient magnitude feature parameter is missing. Input image parameter is required to extract this feature");
|
||||
extractGradientMagnitude_(image);
|
||||
}
|
||||
}
|
||||
|
||||
cleanupFeaturesTemporaryArrays_();
|
||||
}
|
||||
|
||||
|
||||
void extractEdgeFeature_(InputArray image)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
if (edge_mode == FEATURE_CANNY)
|
||||
{
|
||||
CV_LOG_DEBUG(NULL, "Canny(" << edge_canny_threshold1 << ", " << edge_canny_threshold2 << ")");
|
||||
Mat img_canny;
|
||||
Canny(image, img_canny, edge_canny_threshold1, edge_canny_threshold2, edge_canny_apertureSize, edge_canny_L2gradient);
|
||||
#if 0
|
||||
threshold(img_canny, non_edge_feature, 254, 1, THRESH_BINARY_INV);
|
||||
#else
|
||||
// Canny result values are 0 or 255
|
||||
bitwise_not(img_canny, non_edge_feature);
|
||||
weight_non_edge_compute = weight_non_edge * (1.0f / 255.0f);
|
||||
#endif
|
||||
}
|
||||
else // if (edge_mode == FEATURE_ZERO_CROSSING)
|
||||
{
|
||||
initGrayscale_(image);
|
||||
Mat_<short> laplacian;
|
||||
Laplacian(grayscale_, laplacian, CV_16S, laplacianKernelSize);
|
||||
Mat_<uchar> zero_crossing(src_size, 1);
|
||||
|
||||
const size_t zstep = zero_crossing.step[0];
|
||||
for (int y = 0; y < src_size.height - 1; y++)
|
||||
{
|
||||
const short* row0 = laplacian.ptr<short>(y);
|
||||
const short* row1 = laplacian.ptr<short>(y + 1);
|
||||
uchar* zrow0 = zero_crossing.ptr<uchar>(y);
|
||||
//uchar* zrow1 = zero_crossing.ptr<uchar>(y + 1);
|
||||
for (int x = 0; x < src_size.width - 1; x++)
|
||||
{
|
||||
const int v = row0[x];
|
||||
const int neg_v = -v;
|
||||
// - * 1
|
||||
// 2 3 4
|
||||
const int v1 = row0[x + 1];
|
||||
const int v2 = (x > 0) ? row1[x - 1] : v;
|
||||
const int v3 = row1[x + 0];
|
||||
const int v4 = row1[x + 1];
|
||||
if (v < 0)
|
||||
{
|
||||
if (v1 > 0)
|
||||
{
|
||||
zrow0[x + ((v1 < neg_v) ? 1 : 0)] = 0;
|
||||
}
|
||||
if (v2 > 0)
|
||||
{
|
||||
zrow0[x + ((v2 < neg_v) ? (zstep - 1) : 0)] = 0;
|
||||
}
|
||||
if (v3 > 0)
|
||||
{
|
||||
zrow0[x + ((v3 < neg_v) ? (zstep + 0) : 0)] = 0;
|
||||
}
|
||||
if (v4 > 0)
|
||||
{
|
||||
zrow0[x + ((v4 < neg_v) ? (zstep + 1) : 0)] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v1 < 0)
|
||||
{
|
||||
zrow0[x + ((v1 > neg_v) ? 1 : 0)] = 0;
|
||||
}
|
||||
if (v2 < 0)
|
||||
{
|
||||
zrow0[x + ((v2 > neg_v) ? (zstep - 1) : 0)] = 0;
|
||||
}
|
||||
if (v3 < 0)
|
||||
{
|
||||
zrow0[x + ((v3 > neg_v) ? (zstep + 0) : 0)] = 0;
|
||||
}
|
||||
if (v4 < 0)
|
||||
{
|
||||
zrow0[x + ((v4 > neg_v) ? (zstep + 1) : 0)] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (edge_gradient_magnitude_min_value > 0)
|
||||
{
|
||||
initImageMagnitude_(image);
|
||||
Mat mask = image_magnitude_ < edge_gradient_magnitude_min_value;
|
||||
zero_crossing.setTo(1, mask); // reset low-amplitude noise
|
||||
}
|
||||
|
||||
non_edge_feature = zero_crossing;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void extractGradientDirection_(InputArray image)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
initImageMagnitude_(image); // calls internally: initImageDerives_(image);
|
||||
gradient_direction.create(src_size);
|
||||
for (int y = 0; y < src_size.height; y++)
|
||||
{
|
||||
const float* magnitude_row = image_magnitude_.ptr<float>(y);
|
||||
const float* Ix_row = Ix_.ptr<float>(y);
|
||||
const float* Iy_row = Iy_.ptr<float>(y);
|
||||
Point2f* gradient_direction_row = gradient_direction.ptr<Point2f>(y);
|
||||
for (int x = 0; x < src_size.width; x++)
|
||||
{
|
||||
const float m = magnitude_row[x];
|
||||
if (m > FLT_EPSILON)
|
||||
{
|
||||
float m_inv = 1.0f / m;
|
||||
gradient_direction_row[x] = Point2f(Ix_row[x] * m_inv, Iy_row[x] * m_inv);
|
||||
}
|
||||
else
|
||||
{
|
||||
gradient_direction_row[x] = Point2f(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void extractGradientMagnitude_(InputArray image)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
initImageMagnitude_(image); // calls internally: initImageDerives_(image);
|
||||
Mat m;
|
||||
double max_m = 0;
|
||||
if (gradient_magnitude_threshold_max > 0)
|
||||
{
|
||||
threshold(image_magnitude_, m, gradient_magnitude_threshold_max, 0, THRESH_TRUNC);
|
||||
max_m = gradient_magnitude_threshold_max;
|
||||
}
|
||||
else
|
||||
{
|
||||
m = image_magnitude_;
|
||||
minMaxLoc(m, 0, &max_m);
|
||||
}
|
||||
if (max_m <= FLT_EPSILON)
|
||||
{
|
||||
CV_LOG_INFO(NULL, "IntelligentScissorsMB: input image gradient is almost zero")
|
||||
gradient_magnitude.create(src_size);
|
||||
gradient_magnitude.setTo(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
m.convertTo(gradient_magnitude, CV_32F, -1.0 / max_m, 1.0); // normalize and inverse to range 0..1
|
||||
}
|
||||
}
|
||||
|
||||
void applyImage(InputArray image)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
CV_CheckType(image.type(), image.type() == CV_8UC1 || image.type() == CV_8UC3 || image.type() == CV_8UC4, "");
|
||||
|
||||
resetFeatures_();
|
||||
cleanupFeaturesTemporaryArrays_();
|
||||
extractEdgeFeature_(image);
|
||||
extractGradientDirection_(image);
|
||||
extractGradientMagnitude_(image);
|
||||
cleanupFeaturesTemporaryArrays_();
|
||||
}
|
||||
|
||||
|
||||
// details: see section 3.1 of the article
|
||||
const float* acos_table = getAcosTable();
|
||||
const float sqrt2_inv = 0.7071067811865475f; // 1.0 / sqrt(2)
|
||||
|
||||
/** @brief Adds local_cost(p, q) to cost_p.
|
||||
*
|
||||
* local_cost(p, q) is computed as
|
||||
weight_non_edge_compute * non_edge_feature.at<uchar>(q) +
|
||||
weight_gradient_direction * fD +
|
||||
weight_gradient_magnitude * fG
|
||||
*
|
||||
* @param p point p (input)
|
||||
* @param q point q (input)
|
||||
* @param cost_p cost for p (input/output)
|
||||
* @param cost_q cost for q (input)
|
||||
*
|
||||
* @return The boolean result of the (cost_p < cost_q) comparison.
|
||||
*
|
||||
* @note The computed output cost_p can be partial if (cost_p < cost_q) is false.
|
||||
*/
|
||||
bool add_local_cost(const Point& p, const Point& q, float& cost_p, const float cost_q) const
|
||||
{
|
||||
if ((cost_p += weight_non_edge_compute * non_edge_feature.at<uchar>(q)) < cost_q)
|
||||
{
|
||||
const bool isDiag = (p.x != q.x) && (p.y != q.y);
|
||||
|
||||
float fG = gradient_magnitude.at<float>(q);
|
||||
if (!isDiag)
|
||||
{
|
||||
fG *= sqrt2_inv;
|
||||
}
|
||||
|
||||
if ((cost_p += weight_gradient_magnitude * fG) < cost_q)
|
||||
{
|
||||
|
||||
const Point2f diff((float)(q.x - p.x), (float)(q.y - p.y));
|
||||
|
||||
const Point2f Ip = gradient_direction(p);
|
||||
const Point2f Iq = gradient_direction(q);
|
||||
|
||||
const Point2f Dp(Ip.y, -Ip.x); // D(p) - 90 degrees clockwise
|
||||
const Point2f Dq(Iq.y, -Iq.x); // D(q) - 90 degrees clockwise
|
||||
|
||||
float dp = Dp.dot(diff); // dp(p, q)
|
||||
float dq = Dq.dot(diff); // dq(p, q)
|
||||
if (dp < 0)
|
||||
{
|
||||
dp = -dp; // ensure dp >= 0
|
||||
dq = -dq;
|
||||
}
|
||||
|
||||
if (isDiag)
|
||||
{
|
||||
dp *= sqrt2_inv; // normalize length of (q - p)
|
||||
dq *= sqrt2_inv; // normalize length of (q - p)
|
||||
}
|
||||
|
||||
#if 1
|
||||
int dp_i = cvFloor(dp * ACOS_TABLE_SIZE); // dp is in range 0..1
|
||||
dp_i = std::min(ACOS_TABLE_SIZE, std::max(0, dp_i));
|
||||
int dq_i = cvFloor(dq * ACOS_TABLE_SIZE); // dq is in range -1..1
|
||||
dq_i = std::min(ACOS_TABLE_SIZE, std::max(-ACOS_TABLE_SIZE, dq_i));
|
||||
const float fD = acos_table[dp_i + ACOS_TABLE_SIZE] + acos_table[dq_i + ACOS_TABLE_SIZE];
|
||||
#else
|
||||
const float CV_PI_inv = static_cast<float>(1.0 / CV_PI);
|
||||
const float fD = (acosf(dp) + acosf(dq)) * CV_PI_inv; // TODO optimize acos calls (through tables)
|
||||
#endif
|
||||
|
||||
cost_p += weight_gradient_direction * fD;
|
||||
}
|
||||
}
|
||||
return cost_p < cost_q;
|
||||
}
|
||||
|
||||
struct Pix
|
||||
{
|
||||
Point pt;
|
||||
float cost; // NOTE: do not remove cost from here through replacing by cost(pt) map access
|
||||
|
||||
inline bool operator > (const Pix &b) const
|
||||
{
|
||||
return cost > b.cost;
|
||||
}
|
||||
};
|
||||
|
||||
void buildMap(const Point& start_point)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
CV_Assert(!src_size.empty());
|
||||
CV_Assert(!gradient_magnitude.empty() && "Features are missing. applyImage() must be called first");
|
||||
|
||||
CV_CheckGE(weight_non_edge + weight_gradient_direction + weight_gradient_magnitude, FLT_EPSILON, "");
|
||||
|
||||
#if 0 // debug
|
||||
Rect wholeImage(0, 0, src_size.width, src_size.height);
|
||||
Rect roi = Rect(start_point.x - 5, start_point.y - 5, 11, 11) & wholeImage;
|
||||
std::cout << roi << std::endl;
|
||||
std::cout << gradient_magnitude(roi) << std::endl;
|
||||
std::cout << gradient_direction(roi) << std::endl;
|
||||
std::cout << non_edge_feature(roi) << std::endl;
|
||||
#endif
|
||||
|
||||
optimalPathsMap.release();
|
||||
optimalPathsMap.create(src_size);
|
||||
optimalPathsMap.setTo(0); // optimalPathsMap(start_point) = 0;
|
||||
|
||||
//
|
||||
// Section 3.2
|
||||
// Live-Wire 2-D DP graph search.
|
||||
//
|
||||
|
||||
Mat_<float> cost_map(src_size, FLT_MAX); // g(q)
|
||||
Mat_<uchar> processed(src_size, (uchar)0); // e(q)
|
||||
|
||||
// Note: std::vector is faster than std::deque
|
||||
// TODO check std::set
|
||||
std::priority_queue< Pix, std::vector<Pix>, std::greater<Pix> > L;
|
||||
|
||||
cost_map(start_point) = 0;
|
||||
L.emplace(Pix{ start_point, 0/*cost*/ });
|
||||
|
||||
while (!L.empty())
|
||||
{
|
||||
Pix pix = L.top(); L.pop();
|
||||
Point q = pix.pt; // 'q' from the article
|
||||
if (processed(q))
|
||||
continue; // already processed (with lower cost, see note below)
|
||||
processed(q) = 1;
|
||||
#if 1
|
||||
const float cost_q = pix.cost;
|
||||
#else
|
||||
const float cost_q = cost_map(q);
|
||||
CV_Assert(cost_q == pix.cost);
|
||||
#endif
|
||||
for (int n = 0; n < 8; n++) // scan neighbours
|
||||
{
|
||||
Point r(q.x + neighbors[n][0], q.y + neighbors[n][1]); // 'r' from the article
|
||||
if (r.x < 0 || r.x >= src_size.width || r.y < 0 || r.y >= src_size.height)
|
||||
continue; // out of range
|
||||
|
||||
#if !defined(__EMSCRIPTEN__) // slower in JS
|
||||
float& cost_r = cost_map(r);
|
||||
if (cost_r < cost_q)
|
||||
continue; // already processed
|
||||
#else
|
||||
if (processed(r))
|
||||
continue; // already processed
|
||||
|
||||
float& cost_r = cost_map(r);
|
||||
CV_DbgCheckLE(cost_q, cost_r, "INTERNAL ERROR: sorted queue is corrupted");
|
||||
#endif
|
||||
|
||||
float cost = cost_q;
|
||||
if (add_local_cost(q, r, cost, cost_r))
|
||||
{
|
||||
#if 0 // avoid compiler warning
|
||||
if (cost_r != FLT_MAX)
|
||||
{
|
||||
// In article the point 'r' is removed from the queue L
|
||||
// to be re-inserted again with sorting against new optimized cost.
|
||||
// We can do nothing, because "new point" will be placed before in the sorted queue.
|
||||
// Old point will be skipped through "if (processed(q))" check above after processing of new optimal candidate.
|
||||
//
|
||||
// This approach leads to some performance impact, however it is much smaller than element removal from the sorted queue.
|
||||
// So, do nothing.
|
||||
}
|
||||
#endif
|
||||
cost_r = cost;
|
||||
L.emplace(Pix{ r, cost });
|
||||
optimalPathsMap(r) = (uchar)neighbors_encode[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getContour(const Point& target, OutputArray contour_, bool backward)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
CV_Assert(!optimalPathsMap.empty() && "buildMap() must be called before getContour()");
|
||||
|
||||
const int cols = optimalPathsMap.cols;
|
||||
const int rows = optimalPathsMap.rows;
|
||||
|
||||
std::vector<Point> result; result.reserve(512);
|
||||
|
||||
size_t loop_check = 4096;
|
||||
Point pt = target;
|
||||
for (size_t i = 0; i < (size_t)rows * cols; i++) // don't hang on invalid maps
|
||||
{
|
||||
CV_CheckLT(pt.x, cols, "");
|
||||
CV_CheckLT(pt.y, rows, "");
|
||||
result.push_back(pt);
|
||||
int direction = (int)optimalPathsMap(pt);
|
||||
if (direction == 0)
|
||||
break; // stop, start point is reached
|
||||
CV_CheckLT(direction, 9, "Map is invalid");
|
||||
Point next(pt.x + neighbors[direction - 1][0], pt.y + neighbors[direction - 1][1]);
|
||||
pt = next;
|
||||
|
||||
if (result.size() == loop_check) // optional sanity check of invalid maps with loops (don't eat huge amount of memory)
|
||||
{
|
||||
loop_check *= 4; // next limit for loop check
|
||||
for (const auto& pt_check : result)
|
||||
{
|
||||
CV_CheckNE(pt_check, pt, "Map is invalid. Contour loop is detected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (backward)
|
||||
{
|
||||
_InputArray(result).copyTo(contour_);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int N = (int)result.size();
|
||||
const int sz[1] = { N };
|
||||
contour_.create(1, sz, CV_32SC2);
|
||||
Mat_<Point> contour = contour_.getMat();
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
contour.at<Point>(i) = result[N - (i + 1)];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
IntelligentScissorsMB::IntelligentScissorsMB()
|
||||
: impl(std::make_shared<Impl>())
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
IntelligentScissorsMB& IntelligentScissorsMB::setWeights(float weight_non_edge, float weight_gradient_direction, float weight_gradient_magnitude)
|
||||
{
|
||||
CV_DbgAssert(impl);
|
||||
impl->setWeights(weight_non_edge, weight_gradient_direction, weight_gradient_magnitude);
|
||||
return *this;
|
||||
}
|
||||
|
||||
IntelligentScissorsMB& IntelligentScissorsMB::setGradientMagnitudeMaxLimit(float gradient_magnitude_threshold_max)
|
||||
{
|
||||
CV_DbgAssert(impl);
|
||||
impl->setGradientMagnitudeMaxLimit(gradient_magnitude_threshold_max);
|
||||
return *this;
|
||||
}
|
||||
|
||||
IntelligentScissorsMB& IntelligentScissorsMB::setEdgeFeatureZeroCrossingParameters(float gradient_magnitude_min_value)
|
||||
{
|
||||
CV_DbgAssert(impl);
|
||||
impl->setEdgeFeatureZeroCrossingParameters(gradient_magnitude_min_value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
IntelligentScissorsMB& IntelligentScissorsMB::setEdgeFeatureCannyParameters(
|
||||
double threshold1, double threshold2,
|
||||
int apertureSize, bool L2gradient
|
||||
)
|
||||
{
|
||||
CV_DbgAssert(impl);
|
||||
impl->setEdgeFeatureCannyParameters(threshold1, threshold2, apertureSize, L2gradient);
|
||||
return *this;
|
||||
}
|
||||
|
||||
IntelligentScissorsMB& IntelligentScissorsMB::applyImage(InputArray image)
|
||||
{
|
||||
CV_DbgAssert(impl);
|
||||
impl->applyImage(image);
|
||||
return *this;
|
||||
}
|
||||
|
||||
IntelligentScissorsMB& IntelligentScissorsMB::applyImageFeatures(
|
||||
InputArray non_edge, InputArray gradient_direction, InputArray gradient_magnitude,
|
||||
InputArray image
|
||||
)
|
||||
{
|
||||
CV_DbgAssert(impl);
|
||||
impl->applyImageFeatures(non_edge, gradient_direction, gradient_magnitude, image);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void IntelligentScissorsMB::buildMap(const Point& pt)
|
||||
{
|
||||
CV_DbgAssert(impl);
|
||||
impl->buildMap(pt);
|
||||
}
|
||||
|
||||
void IntelligentScissorsMB::getContour(const Point& target, OutputArray contour, bool backward) const
|
||||
{
|
||||
CV_DbgAssert(impl);
|
||||
impl->getContour(target, contour, backward);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,426 @@
|
||||
/*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 CV_FINAL : public MergeDebevec
|
||||
{
|
||||
public:
|
||||
MergeDebevecImpl() :
|
||||
name("MergeDebevec"),
|
||||
weights(triangleWeights())
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times, InputArray input_response) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
std::vector<Mat> images;
|
||||
src.getMatVector(images);
|
||||
Mat times = _times.getMat();
|
||||
|
||||
CV_Assert(images.size() == times.total());
|
||||
checkImageDimensions(images);
|
||||
int depth = images[0].depth();
|
||||
CV_Assert(depth == CV_8U || depth == CV_16U || depth == CV_32F);
|
||||
|
||||
int channels = images[0].channels();
|
||||
Size size = images[0].size();
|
||||
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
|
||||
|
||||
const bool use16bitLUT = (depth == CV_16U || depth == CV_32F);
|
||||
const int lutLength = use16bitLUT ? 65536 : LDR_SIZE;
|
||||
|
||||
std::vector<Mat> lutImages(images.size());
|
||||
if (depth == CV_8U || depth == CV_16U)
|
||||
{
|
||||
lutImages = images;
|
||||
}
|
||||
else
|
||||
{
|
||||
const double scale = static_cast<double>(lutLength - 1);
|
||||
for (size_t i = 0; i < images.size(); ++i)
|
||||
{
|
||||
Mat clipped;
|
||||
cv::max(images[i], 0.0, clipped);
|
||||
cv::min(clipped, 1.0, clipped);
|
||||
clipped.convertTo(lutImages[i], CV_16U, scale);
|
||||
}
|
||||
}
|
||||
|
||||
dst.create(images[0].size(), CV_32FCC);
|
||||
Mat result = dst.getMat();
|
||||
|
||||
Mat response = input_response.getMat();
|
||||
|
||||
if(response.empty()) {
|
||||
response = linearResponse(channels, lutLength);
|
||||
response.at<Vec3f>(0) = response.at<Vec3f>(1);
|
||||
}
|
||||
|
||||
Mat log_response;
|
||||
log(response, log_response);
|
||||
CV_Assert(log_response.rows == lutLength && log_response.cols == 1 &&
|
||||
log_response.channels() == channels);
|
||||
|
||||
Mat exp_values(times.clone());
|
||||
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);
|
||||
|
||||
Mat weights_lut = use16bitLUT
|
||||
? triangleWeights(lutLength)
|
||||
: weights;
|
||||
|
||||
for(size_t i = 0; i < images.size(); i++) {
|
||||
std::vector<Mat> splitted;
|
||||
split(lutImages[i], splitted);
|
||||
|
||||
Mat w = Mat::zeros(size, CV_32F);
|
||||
for(int c = 0; c < channels; c++) {
|
||||
LUT(splitted[c], weights_lut, splitted[c]);
|
||||
w += splitted[c];
|
||||
}
|
||||
w /= channels;
|
||||
|
||||
Mat response_img;
|
||||
LUT(lutImages[i], log_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>((int)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) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
process(src, dst, times, Mat());
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
Mat weights;
|
||||
};
|
||||
|
||||
Ptr<MergeDebevec> createMergeDebevec()
|
||||
{
|
||||
return makePtr<MergeDebevecImpl>();
|
||||
}
|
||||
|
||||
class MergeMertensImpl CV_FINAL : 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) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
process(src, dst);
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, OutputArray dst) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
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);
|
||||
Mutex weight_sum_mutex;
|
||||
|
||||
parallel_for_(Range(0, static_cast<int>(images.size())), [&](const Range& range) {
|
||||
for(int i = range.start; i < range.end; 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);
|
||||
}
|
||||
images[i] = img;
|
||||
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 expo = splitted[c] - 0.5f;
|
||||
pow(expo, 2.0f, expo);
|
||||
expo = -expo / 0.08f;
|
||||
exp(expo, expo);
|
||||
wellexp = wellexp.mul(expo);
|
||||
}
|
||||
|
||||
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) + 1e-12f;
|
||||
|
||||
AutoLock lock(weight_sum_mutex);
|
||||
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);
|
||||
std::vector<Mutex> res_pyr_mutexes(maxlevel + 1);
|
||||
|
||||
parallel_for_(Range(0, static_cast<int>(images.size())), [&](const Range& range) {
|
||||
for(int i = range.start; i < range.end; i++) {
|
||||
weights[i] /= weight_sum;
|
||||
|
||||
std::vector<Mat> img_pyr, weight_pyr;
|
||||
buildPyramid(images[i], 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]);
|
||||
|
||||
AutoLock lock(res_pyr_mutexes[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);
|
||||
}
|
||||
|
||||
float getContrastWeight() const CV_OVERRIDE { return wcon; }
|
||||
void setContrastWeight(float val) CV_OVERRIDE { wcon = val; }
|
||||
|
||||
float getSaturationWeight() const CV_OVERRIDE { return wsat; }
|
||||
void setSaturationWeight(float val) CV_OVERRIDE { wsat = val; }
|
||||
|
||||
float getExposureWeight() const CV_OVERRIDE { return wexp; }
|
||||
void setExposureWeight(float val) CV_OVERRIDE { wexp = val; }
|
||||
|
||||
void write(FileStorage& fs) const CV_OVERRIDE
|
||||
{
|
||||
writeFormat(fs);
|
||||
fs << "name" << name
|
||||
<< "contrast_weight" << wcon
|
||||
<< "saturation_weight" << wsat
|
||||
<< "exposure_weight" << wexp;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn) CV_OVERRIDE
|
||||
{
|
||||
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 CV_FINAL : public MergeRobertson
|
||||
{
|
||||
public:
|
||||
MergeRobertsonImpl() :
|
||||
name("MergeRobertson"),
|
||||
weight(RobertsonWeights())
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times, InputArray input_response) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
std::vector<Mat> images;
|
||||
src.getMatVector(images);
|
||||
Mat times = _times.getMat();
|
||||
|
||||
CV_Assert(images.size() == times.total());
|
||||
checkImageDimensions(images);
|
||||
int depth = images[0].depth();
|
||||
CV_Assert(depth == CV_8U || depth == CV_16U || depth == CV_32F);
|
||||
|
||||
int channels = images[0].channels();
|
||||
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
|
||||
|
||||
const bool use16bitLUT = (depth == CV_16U || depth == CV_32F);
|
||||
const int lutLength = use16bitLUT ? 65536 : LDR_SIZE;
|
||||
|
||||
// Build LUT index images (see MergeDebevecImpl for details).
|
||||
std::vector<Mat> lutImages(images.size());
|
||||
if (depth == CV_8U || depth == CV_16U)
|
||||
{
|
||||
lutImages = images;
|
||||
}
|
||||
else // CV_32F
|
||||
{
|
||||
const double scale = static_cast<double>(lutLength - 1);
|
||||
for (size_t i = 0; i < images.size(); ++i)
|
||||
{
|
||||
images[i].convertTo(lutImages[i], CV_16U, scale);
|
||||
}
|
||||
}
|
||||
|
||||
dst.create(images[0].size(), CV_32FCC);
|
||||
Mat result = dst.getMat();
|
||||
|
||||
Mat response = input_response.getMat();
|
||||
if(response.empty()) {
|
||||
float middle = static_cast<float>(lutLength) / 2.0f;
|
||||
response = linearResponse(channels, lutLength) / middle;
|
||||
}
|
||||
CV_Assert(response.rows == lutLength && response.cols == 1 &&
|
||||
response.channels() == channels);
|
||||
|
||||
result = Mat::zeros(images[0].size(), CV_32FCC);
|
||||
Mat wsum = Mat::zeros(images[0].size(), CV_32FCC);
|
||||
|
||||
Mat weight_lut = use16bitLUT
|
||||
? RobertsonWeights(lutLength)
|
||||
: weight;
|
||||
|
||||
for(size_t i = 0; i < images.size(); i++) {
|
||||
Mat im, w;
|
||||
LUT(lutImages[i], weight_lut, w);
|
||||
LUT(lutImages[i], response, im);
|
||||
|
||||
result += times.at<float>((int)i) * w.mul(im);
|
||||
wsum += times.at<float>((int)i) * times.at<float>((int)i) * w;
|
||||
}
|
||||
result = result.mul(1 / (wsum + Scalar::all(DBL_EPSILON)));
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, OutputArray dst, InputArray times) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
process(src, dst, times, Mat());
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
Mat weight;
|
||||
};
|
||||
|
||||
Ptr<MergeRobertson> createMergeRobertson()
|
||||
{
|
||||
return makePtr<MergeRobertsonImpl>();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*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 <iostream>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "npr.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
void cv::edgePreservingFilter(InputArray _src, OutputArray dst, int flags, float sigma_s, float sigma_r)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat I = _src.getMat();
|
||||
|
||||
Domain_Filter obj;
|
||||
|
||||
Mat img;
|
||||
I.convertTo(img,CV_32FC3,1.0/255.0);
|
||||
|
||||
Mat res;
|
||||
obj.filter(img, res, sigma_s, sigma_r, flags);
|
||||
|
||||
convertScaleAbs(res, dst, 255,0);
|
||||
}
|
||||
|
||||
void cv::detailEnhance(InputArray _src, OutputArray dst, float sigma_s, float sigma_r)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat I = _src.getMat();
|
||||
|
||||
float factor = 3.0f;
|
||||
|
||||
Mat img;
|
||||
I.convertTo(img,CV_32FC3,1.0/255.0);
|
||||
|
||||
Mat lab;
|
||||
vector <Mat> lab_channel;
|
||||
cvtColor(img,lab,COLOR_BGR2Lab);
|
||||
split(lab,lab_channel);
|
||||
|
||||
Mat L;
|
||||
lab_channel[0].convertTo(L,CV_32FC1,1.0/255.0);
|
||||
|
||||
Domain_Filter obj;
|
||||
|
||||
Mat res;
|
||||
obj.filter(L, res, sigma_s, sigma_r, 1);
|
||||
|
||||
Mat detail = L - res;
|
||||
multiply(detail,factor,detail);
|
||||
L = res + detail;
|
||||
|
||||
L.convertTo(lab_channel[0],CV_32FC1,255);
|
||||
|
||||
merge(lab_channel,lab);
|
||||
|
||||
cvtColor(lab,res,COLOR_Lab2BGR);
|
||||
res.convertTo(dst,CV_8UC3,255);
|
||||
}
|
||||
|
||||
void cv::pencilSketch(InputArray _src, OutputArray _dst1, OutputArray _dst2, float sigma_s, float sigma_r, float shade_factor)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat I = _src.getMat();
|
||||
_dst1.create(I.size(), CV_8UC1);
|
||||
Mat dst1 = _dst1.getMat();
|
||||
|
||||
_dst2.create(I.size(), CV_8UC3);
|
||||
Mat dst2 = _dst2.getMat();
|
||||
|
||||
Mat img = Mat(I.size(),CV_32FC3);
|
||||
I.convertTo(img,CV_32FC3,1.0/255.0);
|
||||
|
||||
Domain_Filter obj;
|
||||
|
||||
Mat sketch = Mat(I.size(),CV_32FC1);
|
||||
Mat color_sketch = Mat(I.size(),CV_32FC3);
|
||||
|
||||
obj.pencil_sketch(img, sketch, color_sketch, sigma_s, sigma_r, shade_factor);
|
||||
|
||||
sketch.convertTo(dst1,CV_8UC1,255);
|
||||
color_sketch.convertTo(dst2,CV_8UC3,255);
|
||||
|
||||
}
|
||||
|
||||
void cv::stylization(InputArray _src, OutputArray _dst, float sigma_s, float sigma_r)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat I = _src.getMat();
|
||||
_dst.create(I.size(), CV_8UC3);
|
||||
Mat dst = _dst.getMat();
|
||||
|
||||
Mat img;
|
||||
I.convertTo(img,CV_32FC3,1.0/255.0);
|
||||
|
||||
int h = img.size().height;
|
||||
int w = img.size().width;
|
||||
|
||||
Mat res;
|
||||
Mat magnitude = Mat(h,w,CV_32FC1);
|
||||
|
||||
Domain_Filter obj;
|
||||
obj.filter(img, res, sigma_s, sigma_r, NORMCONV_FILTER);
|
||||
|
||||
obj.find_magnitude(res,magnitude);
|
||||
|
||||
Mat stylized;
|
||||
|
||||
vector <Mat> temp;
|
||||
split(res,temp);
|
||||
multiply(temp[0],magnitude,temp[0]);
|
||||
multiply(temp[1],magnitude,temp[1]);
|
||||
multiply(temp[2],magnitude,temp[2]);
|
||||
merge(temp,stylized);
|
||||
|
||||
stylized.convertTo(dst,CV_8UC3,255);
|
||||
}
|
||||
@@ -0,0 +1,591 @@
|
||||
/*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 <iostream>
|
||||
#include <stdlib.h>
|
||||
#include <limits>
|
||||
#include "math.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
double myinf = std::numeric_limits<double>::infinity();
|
||||
|
||||
class Domain_Filter
|
||||
{
|
||||
public:
|
||||
Mat ct_H, ct_V, horiz, vert, O, O_t, lower_idx, upper_idx;
|
||||
void init(const Mat &img, int flags, float sigma_s, float sigma_r);
|
||||
void getGradientx( const Mat &img, Mat &gx);
|
||||
void getGradienty( const Mat &img, Mat &gy);
|
||||
void diffx(const Mat &img, Mat &temp);
|
||||
void diffy(const Mat &img, Mat &temp);
|
||||
void find_magnitude(Mat &img, Mat &mag);
|
||||
void compute_boxfilter(Mat &output, Mat &hz, Mat &psketch, float radius);
|
||||
void compute_Rfilter(Mat &O, Mat &horiz, float sigma_h);
|
||||
void compute_NCfilter(Mat &O, Mat &horiz, Mat &psketch, float radius);
|
||||
void filter(const Mat &img, Mat &res, float sigma_s, float sigma_r, int flags);
|
||||
void pencil_sketch(const Mat &img, Mat &sketch, Mat &color_res, float sigma_s, float sigma_r, float shade_factor);
|
||||
void Depth_of_field(const Mat &img, Mat &img1, float sigma_s, float sigma_r);
|
||||
};
|
||||
|
||||
void Domain_Filter::diffx(const Mat &img, Mat &temp)
|
||||
{
|
||||
int channel = img.channels();
|
||||
|
||||
for(int i = 0; i < img.size().height; i++)
|
||||
for(int j = 0; j < img.size().width-1; j++)
|
||||
{
|
||||
for(int c =0; c < channel; c++)
|
||||
{
|
||||
temp.at<float>(i,j*channel+c) =
|
||||
img.at<float>(i,(j+1)*channel+c) - img.at<float>(i,j*channel+c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Domain_Filter::diffy(const Mat &img, Mat &temp)
|
||||
{
|
||||
int channel = img.channels();
|
||||
|
||||
for(int i = 0; i < img.size().height-1; i++)
|
||||
for(int j = 0; j < img.size().width; j++)
|
||||
{
|
||||
for(int c =0; c < channel; c++)
|
||||
{
|
||||
temp.at<float>(i,j*channel+c) =
|
||||
img.at<float>((i+1),j*channel+c) - img.at<float>(i,j*channel+c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Domain_Filter::getGradientx( const Mat &img, Mat &gx)
|
||||
{
|
||||
int w = img.cols;
|
||||
int h = img.rows;
|
||||
int channel = img.channels();
|
||||
|
||||
for(int i=0;i<h;i++)
|
||||
for(int j=0;j<w;j++)
|
||||
for(int c=0;c<channel;++c)
|
||||
{
|
||||
gx.at<float>(i,j*channel+c) =
|
||||
img.at<float>(i,(j+1)*channel+c) - img.at<float>(i,j*channel+c);
|
||||
}
|
||||
}
|
||||
|
||||
void Domain_Filter::getGradienty( const Mat &img, Mat &gy)
|
||||
{
|
||||
int w = img.cols;
|
||||
int h = img.rows;
|
||||
int channel = img.channels();
|
||||
|
||||
for(int i=0;i<h;i++)
|
||||
for(int j=0;j<w;j++)
|
||||
for(int c=0;c<channel;++c)
|
||||
{
|
||||
gy.at<float>(i,j*channel+c) =
|
||||
img.at<float>(i+1,j*channel+c) - img.at<float>(i,j*channel+c);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Domain_Filter::find_magnitude(Mat &img, Mat &mag)
|
||||
{
|
||||
int h = img.rows;
|
||||
int w = img.cols;
|
||||
|
||||
vector <Mat> planes;
|
||||
split(img, planes);
|
||||
|
||||
Mat magXR = Mat(h, w, CV_32FC1);
|
||||
Mat magYR = Mat(h, w, CV_32FC1);
|
||||
|
||||
Mat magXG = Mat(h, w, CV_32FC1);
|
||||
Mat magYG = Mat(h, w, CV_32FC1);
|
||||
|
||||
Mat magXB = Mat(h, w, CV_32FC1);
|
||||
Mat magYB = Mat(h, w, CV_32FC1);
|
||||
|
||||
Sobel(planes[0], magXR, CV_32FC1, 1, 0, 3);
|
||||
Sobel(planes[0], magYR, CV_32FC1, 0, 1, 3);
|
||||
|
||||
Sobel(planes[1], magXG, CV_32FC1, 1, 0, 3);
|
||||
Sobel(planes[1], magYG, CV_32FC1, 0, 1, 3);
|
||||
|
||||
Sobel(planes[2], magXB, CV_32FC1, 1, 0, 3);
|
||||
Sobel(planes[2], magYB, CV_32FC1, 0, 1, 3);
|
||||
|
||||
Mat mag1 = Mat(h,w,CV_32FC1);
|
||||
Mat mag2 = Mat(h,w,CV_32FC1);
|
||||
Mat mag3 = Mat(h,w,CV_32FC1);
|
||||
|
||||
magnitude(magXR,magYR,mag1);
|
||||
magnitude(magXG,magYG,mag2);
|
||||
magnitude(magXB,magYB,mag3);
|
||||
|
||||
mag = mag1 + mag2 + mag3;
|
||||
mag = 1.0f - mag;
|
||||
}
|
||||
|
||||
void Domain_Filter::compute_Rfilter(Mat &output, Mat &hz, float sigma_h)
|
||||
{
|
||||
int h = output.rows;
|
||||
int w = output.cols;
|
||||
int channel = output.channels();
|
||||
|
||||
float a = (float) exp((-1.0 * sqrt(2.0)) / sigma_h);
|
||||
|
||||
Mat temp = Mat(h,w,CV_32FC3);
|
||||
|
||||
output.copyTo(temp);
|
||||
Mat V = Mat(h,w,CV_32FC1);
|
||||
|
||||
for(int i=0;i<h;i++)
|
||||
for(int j=0;j<w;j++)
|
||||
V.at<float>(i,j) = std::pow(a,hz.at<float>(i,j));
|
||||
|
||||
for(int i=0; i<h; i++)
|
||||
{
|
||||
for(int j =1; j < w; j++)
|
||||
{
|
||||
for(int c = 0; c<channel; c++)
|
||||
{
|
||||
temp.at<float>(i,j*channel+c) = temp.at<float>(i,j*channel+c) +
|
||||
(temp.at<float>(i,(j-1)*channel+c) - temp.at<float>(i,j*channel+c)) * V.at<float>(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int i=0; i<h; i++)
|
||||
{
|
||||
for(int j =w-2; j >= 0; j--)
|
||||
{
|
||||
for(int c = 0; c<channel; c++)
|
||||
{
|
||||
temp.at<float>(i,j*channel+c) = temp.at<float>(i,j*channel+c) +
|
||||
(temp.at<float>(i,(j+1)*channel+c) - temp.at<float>(i,j*channel+c))*V.at<float>(i,j+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
temp.copyTo(output);
|
||||
}
|
||||
|
||||
void Domain_Filter::compute_boxfilter(Mat &output, Mat &hz, Mat &psketch, float radius)
|
||||
{
|
||||
int h = output.rows;
|
||||
int w = output.cols;
|
||||
Mat lower_pos = Mat(h,w,CV_32FC1);
|
||||
Mat upper_pos = Mat(h,w,CV_32FC1);
|
||||
|
||||
lower_pos = hz - radius;
|
||||
upper_pos = hz + radius;
|
||||
|
||||
lower_idx = Mat::zeros(h,w,CV_32FC1);
|
||||
upper_idx = Mat::zeros(h,w,CV_32FC1);
|
||||
|
||||
Mat domain_row = Mat::zeros(1,w+1,CV_32FC1);
|
||||
|
||||
for(int i=0;i<h;i++)
|
||||
{
|
||||
for(int j=0;j<w;j++)
|
||||
domain_row.at<float>(0,j) = hz.at<float>(i,j);
|
||||
domain_row.at<float>(0,w) = (float) myinf;
|
||||
|
||||
Mat lower_pos_row = Mat::zeros(1,w,CV_32FC1);
|
||||
Mat upper_pos_row = Mat::zeros(1,w,CV_32FC1);
|
||||
|
||||
for(int j=0;j<w;j++)
|
||||
{
|
||||
lower_pos_row.at<float>(0,j) = lower_pos.at<float>(i,j);
|
||||
upper_pos_row.at<float>(0,j) = upper_pos.at<float>(i,j);
|
||||
}
|
||||
|
||||
Mat temp_lower_idx = Mat::zeros(1,w,CV_32FC1);
|
||||
Mat temp_upper_idx = Mat::zeros(1,w,CV_32FC1);
|
||||
|
||||
for(int j=0;j<w;j++)
|
||||
{
|
||||
if(domain_row.at<float>(0,j) > lower_pos_row.at<float>(0,0))
|
||||
{
|
||||
temp_lower_idx.at<float>(0,0) = (float) j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(int j=0;j<w;j++)
|
||||
{
|
||||
if(domain_row.at<float>(0,j) > upper_pos_row.at<float>(0,0))
|
||||
{
|
||||
temp_upper_idx.at<float>(0,0) = (float) j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int temp = 0;
|
||||
for(int j=1;j<w;j++)
|
||||
{
|
||||
int count=0;
|
||||
for(int k=(int) temp_lower_idx.at<float>(0,j-1);k<w+1;k++)
|
||||
{
|
||||
if(domain_row.at<float>(0,k) > lower_pos_row.at<float>(0,j))
|
||||
{
|
||||
temp = count;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
temp_lower_idx.at<float>(0,j) = temp_lower_idx.at<float>(0,j-1) + temp;
|
||||
|
||||
count = 0;
|
||||
for(int k=(int) temp_upper_idx.at<float>(0,j-1);k<w+1;k++)
|
||||
{
|
||||
|
||||
|
||||
if(domain_row.at<float>(0,k) > upper_pos_row.at<float>(0,j))
|
||||
{
|
||||
temp = count;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
temp_upper_idx.at<float>(0,j) = temp_upper_idx.at<float>(0,j-1) + temp;
|
||||
}
|
||||
|
||||
for(int j=0;j<w;j++)
|
||||
{
|
||||
lower_idx.at<float>(i,j) = temp_lower_idx.at<float>(0,j) + 1;
|
||||
upper_idx.at<float>(i,j) = temp_upper_idx.at<float>(0,j) + 1;
|
||||
}
|
||||
|
||||
}
|
||||
psketch = upper_idx - lower_idx;
|
||||
}
|
||||
void Domain_Filter::compute_NCfilter(Mat &output, Mat &hz, Mat &psketch, float radius)
|
||||
{
|
||||
int h = output.rows;
|
||||
int w = output.cols;
|
||||
int channel = output.channels();
|
||||
|
||||
compute_boxfilter(output,hz,psketch,radius);
|
||||
|
||||
Mat box_filter = Mat::zeros(h,w+1,CV_32FC3);
|
||||
|
||||
for(int i = 0; i < h; i++)
|
||||
{
|
||||
box_filter.at<float>(i,1*channel+0) = output.at<float>(i,0*channel+0);
|
||||
box_filter.at<float>(i,1*channel+1) = output.at<float>(i,0*channel+1);
|
||||
box_filter.at<float>(i,1*channel+2) = output.at<float>(i,0*channel+2);
|
||||
for(int j = 2; j < w+1; j++)
|
||||
{
|
||||
for(int c=0;c<channel;c++)
|
||||
box_filter.at<float>(i,j*channel+c) = output.at<float>(i,(j-1)*channel+c) + box_filter.at<float>(i,(j-1)*channel+c);
|
||||
}
|
||||
}
|
||||
|
||||
Mat indices = Mat::zeros(h,w,CV_32FC1);
|
||||
Mat final = Mat::zeros(h,w,CV_32FC3);
|
||||
|
||||
for(int i=0;i<h;i++)
|
||||
for(int j=0;j<w;j++)
|
||||
indices.at<float>(i,j) = (float) i+1;
|
||||
|
||||
Mat a = Mat::zeros(h,w,CV_32FC1);
|
||||
Mat b = Mat::zeros(h,w,CV_32FC1);
|
||||
|
||||
// Compute the box filter using a summed area table.
|
||||
for(int c=0;c<channel;c++)
|
||||
{
|
||||
Mat flag = Mat::ones(h,w,CV_32FC1);
|
||||
multiply(flag,c+1,flag);
|
||||
|
||||
Mat temp1, temp2;
|
||||
multiply(flag - 1,h*(w+1),temp1);
|
||||
multiply(lower_idx - 1,h,temp2);
|
||||
a = temp1 + temp2 + indices;
|
||||
|
||||
multiply(flag - 1,h*(w+1),temp1);
|
||||
multiply(upper_idx - 1,h,temp2);
|
||||
b = temp1 + temp2 + indices;
|
||||
|
||||
int p,q,r,rem;
|
||||
int p1,q1,r1,rem1;
|
||||
|
||||
// Calculating indices
|
||||
for(int i=0;i<h;i++)
|
||||
{
|
||||
for(int j=0;j<w;j++)
|
||||
{
|
||||
|
||||
r = (int) b.at<float>(i,j)/(h*(w+1));
|
||||
rem = (int) b.at<float>(i,j) - r*h*(w+1);
|
||||
q = rem/h;
|
||||
p = rem - q*h;
|
||||
if(q==0)
|
||||
{
|
||||
p=h;
|
||||
q=w;
|
||||
r=r-1;
|
||||
}
|
||||
if(p==0)
|
||||
{
|
||||
p=h;
|
||||
q=q-1;
|
||||
}
|
||||
|
||||
r1 = (int) a.at<float>(i,j)/(h*(w+1));
|
||||
rem1 = (int) a.at<float>(i,j) - r1*h*(w+1);
|
||||
q1 = rem1/h;
|
||||
p1 = rem1 - q1*h;
|
||||
if(p1==0)
|
||||
{
|
||||
p1=h;
|
||||
q1=q1-1;
|
||||
}
|
||||
|
||||
final.at<float>(i,j*channel+2-c) = (box_filter.at<float>(p-1,q*channel+(2-r)) - box_filter.at<float>(p1-1,q1*channel+(2-r1)))
|
||||
/(upper_idx.at<float>(i,j) - lower_idx.at<float>(i,j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final.copyTo(output);
|
||||
}
|
||||
void Domain_Filter::init(const Mat &img, int flags, float sigma_s, float sigma_r)
|
||||
{
|
||||
int h = img.size().height;
|
||||
int w = img.size().width;
|
||||
int channel = img.channels();
|
||||
|
||||
//////////////////////////////////// horizontal and vertical partial derivatives /////////////////////////////////
|
||||
|
||||
Mat derivx = Mat::zeros(h,w-1,CV_32FC3);
|
||||
Mat derivy = Mat::zeros(h-1,w,CV_32FC3);
|
||||
|
||||
diffx(img,derivx);
|
||||
diffy(img,derivy);
|
||||
|
||||
Mat distx = Mat::zeros(h,w,CV_32FC1);
|
||||
Mat disty = Mat::zeros(h,w,CV_32FC1);
|
||||
|
||||
//////////////////////// Compute the l1-norm distance of neighbor pixels ////////////////////////////////////////////////
|
||||
|
||||
for(int i = 0; i < h; i++)
|
||||
for(int j = 0,k=1; j < w-1; j++,k++)
|
||||
for(int c = 0; c < channel; c++)
|
||||
{
|
||||
distx.at<float>(i,k) =
|
||||
distx.at<float>(i,k) + abs(derivx.at<float>(i,j*channel+c));
|
||||
}
|
||||
|
||||
for(int i = 0,k=1; i < h-1; i++,k++)
|
||||
for(int j = 0; j < w; j++)
|
||||
for(int c = 0; c < channel; c++)
|
||||
{
|
||||
disty.at<float>(k,j) =
|
||||
disty.at<float>(k,j) + abs(derivy.at<float>(i,j*channel+c));
|
||||
}
|
||||
|
||||
////////////////////// Compute the derivatives of the horizontal and vertical domain transforms. /////////////////////////////
|
||||
|
||||
horiz = Mat(h,w,CV_32FC1);
|
||||
vert = Mat(h,w,CV_32FC1);
|
||||
|
||||
Mat final = Mat(h,w,CV_32FC3);
|
||||
|
||||
Mat tempx,tempy;
|
||||
multiply(distx,sigma_s/sigma_r,tempx);
|
||||
multiply(disty,sigma_s/sigma_r,tempy);
|
||||
|
||||
horiz = 1.0f + tempx;
|
||||
vert = 1.0f + tempy;
|
||||
|
||||
O = Mat(h,w,CV_32FC3);
|
||||
img.copyTo(O);
|
||||
|
||||
O_t = Mat(w,h,CV_32FC3);
|
||||
|
||||
if(flags == 2)
|
||||
{
|
||||
|
||||
ct_H = Mat(h,w,CV_32FC1);
|
||||
ct_V = Mat(h,w,CV_32FC1);
|
||||
|
||||
for(int i = 0; i < h; i++)
|
||||
{
|
||||
ct_H.at<float>(i,0) = horiz.at<float>(i,0);
|
||||
for(int j = 1; j < w; j++)
|
||||
{
|
||||
ct_H.at<float>(i,j) = horiz.at<float>(i,j) + ct_H.at<float>(i,j-1);
|
||||
}
|
||||
}
|
||||
|
||||
for(int j = 0; j < w; j++)
|
||||
{
|
||||
ct_V.at<float>(0,j) = vert.at<float>(0,j);
|
||||
for(int i = 1; i < h; i++)
|
||||
{
|
||||
ct_V.at<float>(i,j) = vert.at<float>(i,j) + ct_V.at<float>(i-1,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Domain_Filter::filter(const Mat &img, Mat &res, float sigma_s = 60, float sigma_r = 0.4, int flags = 1)
|
||||
{
|
||||
int no_of_iter = 3;
|
||||
int h = img.size().height;
|
||||
int w = img.size().width;
|
||||
float sigma_h = sigma_s;
|
||||
|
||||
init(img,flags,sigma_s,sigma_r);
|
||||
|
||||
if(flags == 1)
|
||||
{
|
||||
Mat vert_t = vert.t();
|
||||
|
||||
for(int i=0;i<no_of_iter;i++)
|
||||
{
|
||||
sigma_h = (float) (sigma_s * sqrt(3.0) * std::pow(2,(no_of_iter - (i+1))) / sqrt(std::pow(4,no_of_iter) -1));
|
||||
|
||||
compute_Rfilter(O, horiz, sigma_h);
|
||||
|
||||
O_t = O.t();
|
||||
|
||||
compute_Rfilter(O_t, vert_t, sigma_h);
|
||||
|
||||
O = O_t.t();
|
||||
|
||||
}
|
||||
}
|
||||
else if(flags == 2)
|
||||
{
|
||||
|
||||
Mat vert_t = ct_V.t();
|
||||
Mat temp = Mat(h,w,CV_32FC1);
|
||||
Mat temp1 = Mat(w,h,CV_32FC1);
|
||||
|
||||
float radius;
|
||||
|
||||
for(int i=0;i<no_of_iter;i++)
|
||||
{
|
||||
sigma_h = (float) (sigma_s * sqrt(3.0) * std::pow(2,(no_of_iter - (i+1))) / sqrt(std::pow(4,no_of_iter) -1));
|
||||
|
||||
radius = (float) sqrt(3.0) * sigma_h;
|
||||
|
||||
compute_NCfilter(O, ct_H, temp,radius);
|
||||
|
||||
O_t = O.t();
|
||||
|
||||
compute_NCfilter(O_t, vert_t, temp1, radius);
|
||||
|
||||
O = O_t.t();
|
||||
}
|
||||
}
|
||||
|
||||
res = O.clone();
|
||||
}
|
||||
|
||||
void Domain_Filter::pencil_sketch(const Mat &img, Mat &sketch, Mat &color_res, float sigma_s, float sigma_r, float shade_factor)
|
||||
{
|
||||
|
||||
int no_of_iter = 3;
|
||||
init(img,2,sigma_s,sigma_r);
|
||||
int h = img.size().height;
|
||||
int w = img.size().width;
|
||||
|
||||
/////////////////////// convert to YCBCR model for color pencil drawing //////////////////////////////////////////////////////
|
||||
|
||||
Mat color_sketch = Mat(h,w,CV_32FC3);
|
||||
|
||||
cvtColor(img,color_sketch,COLOR_BGR2YCrCb);
|
||||
|
||||
vector <Mat> YUV_channel;
|
||||
Mat vert_t = ct_V.t();
|
||||
|
||||
float sigma_h = sigma_s;
|
||||
|
||||
Mat penx = Mat(h,w,CV_32FC1);
|
||||
|
||||
Mat pen_res = Mat::zeros(h,w,CV_32FC1);
|
||||
Mat peny = Mat(w,h,CV_32FC1);
|
||||
|
||||
Mat peny_t;
|
||||
|
||||
float radius;
|
||||
|
||||
for(int i=0;i<no_of_iter;i++)
|
||||
{
|
||||
sigma_h = (float) (sigma_s * sqrt(3.0) * std::pow(2,(no_of_iter - (i+1))) / sqrt(std::pow(4,no_of_iter) -1));
|
||||
|
||||
radius = (float) sqrt(3.0) * sigma_h;
|
||||
|
||||
compute_boxfilter(O, ct_H, penx, radius);
|
||||
|
||||
O_t = O.t();
|
||||
|
||||
compute_boxfilter(O_t, vert_t, peny, radius);
|
||||
|
||||
O = O_t.t();
|
||||
|
||||
peny_t = peny.t();
|
||||
|
||||
for(int k=0;k<h;k++)
|
||||
for(int j=0;j<w;j++)
|
||||
pen_res.at<float>(k,j) = (shade_factor * (penx.at<float>(k,j) + peny_t.at<float>(k,j)));
|
||||
|
||||
if(i==0)
|
||||
{
|
||||
sketch = pen_res.clone();
|
||||
split(color_sketch,YUV_channel);
|
||||
pen_res.copyTo(YUV_channel[0]);
|
||||
merge(YUV_channel,color_sketch);
|
||||
cvtColor(color_sketch,color_res,COLOR_YCrCb2BGR);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2014, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
#ifdef cl_amd_printf
|
||||
#pragma OPENCL_EXTENSION cl_amd_printf:enable
|
||||
#endif
|
||||
|
||||
#ifdef DOUBLE_SUPPORT
|
||||
#ifdef cl_amd_fp64
|
||||
#pragma OPENCL EXTENSION cl_amd_fp64:enable
|
||||
#elif defined cl_khr_fp64
|
||||
#pragma OPENCL EXTENSION cl_khr_fp64:enable
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef OP_CALC_WEIGHTS
|
||||
|
||||
__kernel void calcAlmostDist2Weight(__global wlut_t * almostDist2Weight, int almostMaxDist,
|
||||
FT almostDist2ActualDistMultiplier, int fixedPointMult,
|
||||
w_t den, FT WEIGHT_THRESHOLD)
|
||||
{
|
||||
int almostDist = get_global_id(0);
|
||||
|
||||
if (almostDist < almostMaxDist)
|
||||
{
|
||||
FT dist = almostDist * almostDist2ActualDistMultiplier;
|
||||
#ifdef ABS
|
||||
w_t w = exp((w_t)(-dist*dist) * den);
|
||||
#else
|
||||
w_t w = exp((w_t)(-dist) * den);
|
||||
#endif
|
||||
wlut_t weight = convert_wlut_t(fixedPointMult * (isnan(w) ? (w_t)1.0 : w));
|
||||
almostDist2Weight[almostDist] =
|
||||
weight < (wlut_t)(WEIGHT_THRESHOLD * fixedPointMult) ? (wlut_t)0 : weight;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined OP_CALC_FASTNLMEANS
|
||||
|
||||
#define noconvert
|
||||
|
||||
#define SEARCH_SIZE_SQ (SEARCH_SIZE * SEARCH_SIZE)
|
||||
|
||||
inline int calcDist(pixel_t a, pixel_t b)
|
||||
{
|
||||
#ifdef ABS
|
||||
int_t retval = convert_int_t(abs_diff(a, b));
|
||||
#else
|
||||
int_t diff = convert_int_t(a) - convert_int_t(b);
|
||||
int_t retval = diff * diff;
|
||||
#endif
|
||||
|
||||
#if cn == 1
|
||||
return retval;
|
||||
#elif cn == 2
|
||||
return retval.x + retval.y;
|
||||
#elif cn == 3
|
||||
return retval.x + retval.y + retval.z;
|
||||
#elif cn == 4
|
||||
return retval.x + retval.y + retval.z + retval.w;
|
||||
#else
|
||||
#error "cn should be either 1, 2, 3 or 4"
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ABS
|
||||
inline int calcDistUpDown(pixel_t down_value, pixel_t down_value_t, pixel_t up_value, pixel_t up_value_t)
|
||||
{
|
||||
return calcDist(down_value, down_value_t) - calcDist(up_value, up_value_t);
|
||||
}
|
||||
#else
|
||||
inline int calcDistUpDown(pixel_t down_value, pixel_t down_value_t, pixel_t up_value, pixel_t up_value_t)
|
||||
{
|
||||
int_t A = convert_int_t(down_value) - convert_int_t(down_value_t);
|
||||
int_t B = convert_int_t(up_value) - convert_int_t(up_value_t);
|
||||
int_t retval = (A - B) * (A + B);
|
||||
|
||||
#if cn == 1
|
||||
return retval;
|
||||
#elif cn == 2
|
||||
return retval.x + retval.y;
|
||||
#elif cn == 3
|
||||
return retval.x + retval.y + retval.z;
|
||||
#elif cn == 4
|
||||
return retval.x + retval.y + retval.z + retval.w;
|
||||
#else
|
||||
#error "cn should be either 1, 2, 3 or 4"
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#define COND if (x == 0 && y == 0)
|
||||
|
||||
inline void calcFirstElementInRow(__global const uchar * src, int src_step, int src_offset,
|
||||
__local int * dists, int y, int x, int id,
|
||||
__global int * col_dists, __global int * up_col_dists)
|
||||
{
|
||||
y -= TEMPLATE_SIZE2;
|
||||
int sx = x - SEARCH_SIZE2, sy = y - SEARCH_SIZE2;
|
||||
int col_dists_current_private[TEMPLATE_SIZE];
|
||||
|
||||
for (int i = id; i < SEARCH_SIZE_SQ; i += CTA_SIZE)
|
||||
{
|
||||
int dist = 0, value;
|
||||
|
||||
__global const pixel_t * src_template = (__global const pixel_t *)(src +
|
||||
mad24(sy + i / SEARCH_SIZE, src_step, mad24(psz, sx + i % SEARCH_SIZE, src_offset)));
|
||||
__global const pixel_t * src_current = (__global const pixel_t *)(src + mad24(y, src_step, mad24(psz, x, src_offset)));
|
||||
__global int * col_dists_current = col_dists + i * TEMPLATE_SIZE;
|
||||
|
||||
#pragma unroll
|
||||
for (int j = 0; j < TEMPLATE_SIZE; ++j)
|
||||
col_dists_current_private[j] = 0;
|
||||
|
||||
for (int ty = 0; ty < TEMPLATE_SIZE; ++ty)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int tx = -TEMPLATE_SIZE2; tx <= TEMPLATE_SIZE2; ++tx)
|
||||
{
|
||||
value = calcDist(src_template[tx], src_current[tx]);
|
||||
|
||||
col_dists_current_private[tx + TEMPLATE_SIZE2] += value;
|
||||
dist += value;
|
||||
}
|
||||
|
||||
src_current = (__global const pixel_t *)((__global const uchar *)src_current + src_step);
|
||||
src_template = (__global const pixel_t *)((__global const uchar *)src_template + src_step);
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (int j = 0; j < TEMPLATE_SIZE; ++j)
|
||||
col_dists_current[j] = col_dists_current_private[j];
|
||||
|
||||
dists[i] = dist;
|
||||
up_col_dists[0 + i] = col_dists[TEMPLATE_SIZE - 1];
|
||||
}
|
||||
}
|
||||
|
||||
inline void calcElementInFirstRow(__global const uchar * src, int src_step, int src_offset,
|
||||
__local int * dists, int y, int x0, int x, int id, int first,
|
||||
__global int * col_dists, __global int * up_col_dists)
|
||||
{
|
||||
x += TEMPLATE_SIZE2;
|
||||
y -= TEMPLATE_SIZE2;
|
||||
int sx = x - SEARCH_SIZE2, sy = y - SEARCH_SIZE2;
|
||||
|
||||
for (int i = id; i < SEARCH_SIZE_SQ; i += CTA_SIZE)
|
||||
{
|
||||
__global const pixel_t * src_current = (__global const pixel_t *)(src + mad24(y, src_step, mad24(psz, x, src_offset)));
|
||||
__global const pixel_t * src_template = (__global const pixel_t *)(src +
|
||||
mad24(sy + i / SEARCH_SIZE, src_step, mad24(psz, sx + i % SEARCH_SIZE, src_offset)));
|
||||
__global int * col_dists_current = col_dists + TEMPLATE_SIZE * i;
|
||||
|
||||
int col_dist = 0;
|
||||
|
||||
#pragma unroll
|
||||
for (int ty = 0; ty < TEMPLATE_SIZE; ++ty)
|
||||
{
|
||||
col_dist += calcDist(src_current[0], src_template[0]);
|
||||
|
||||
src_current = (__global const pixel_t *)((__global const uchar *)src_current + src_step);
|
||||
src_template = (__global const pixel_t *)((__global const uchar *)src_template + src_step);
|
||||
}
|
||||
|
||||
dists[i] += col_dist - col_dists_current[first];
|
||||
col_dists_current[first] = col_dist;
|
||||
up_col_dists[mad24(x0, SEARCH_SIZE_SQ, i)] = col_dist;
|
||||
}
|
||||
}
|
||||
|
||||
inline void calcElement(__global const uchar * src, int src_step, int src_offset,
|
||||
__local int * dists, int y, int x0, int x, int id, int first,
|
||||
__global int * col_dists, __global int * up_col_dists)
|
||||
{
|
||||
int sx = x + TEMPLATE_SIZE2;
|
||||
int sy_up = y - TEMPLATE_SIZE2 - 1;
|
||||
int sy_down = y + TEMPLATE_SIZE2;
|
||||
|
||||
pixel_t up_value = *(__global const pixel_t *)(src + mad24(sy_up, src_step, mad24(psz, sx, src_offset)));
|
||||
pixel_t down_value = *(__global const pixel_t *)(src + mad24(sy_down, src_step, mad24(psz, sx, src_offset)));
|
||||
|
||||
sx -= SEARCH_SIZE2;
|
||||
sy_up -= SEARCH_SIZE2;
|
||||
sy_down -= SEARCH_SIZE2;
|
||||
|
||||
for (int i = id; i < SEARCH_SIZE_SQ; i += CTA_SIZE)
|
||||
{
|
||||
int wx = i % SEARCH_SIZE, wy = i / SEARCH_SIZE;
|
||||
|
||||
pixel_t up_value_t = *(__global const pixel_t *)(src + mad24(sy_up + wy, src_step, mad24(psz, sx + wx, src_offset)));
|
||||
pixel_t down_value_t = *(__global const pixel_t *)(src + mad24(sy_down + wy, src_step, mad24(psz, sx + wx, src_offset)));
|
||||
|
||||
__global int * col_dists_current = col_dists + mad24(i, TEMPLATE_SIZE, first);
|
||||
__global int * up_col_dists_current = up_col_dists + mad24(x0, SEARCH_SIZE_SQ, i);
|
||||
|
||||
int col_dist = up_col_dists_current[0] + calcDistUpDown(down_value, down_value_t, up_value, up_value_t);
|
||||
|
||||
dists[i] += col_dist - col_dists_current[0];
|
||||
col_dists_current[0] = col_dist;
|
||||
up_col_dists_current[0] = col_dist;
|
||||
}
|
||||
}
|
||||
|
||||
inline void convolveWindow(__global const uchar * src, int src_step, int src_offset,
|
||||
__local int * dists, __global const wlut_t * almostDist2Weight,
|
||||
__global uchar * dst, int dst_step, int dst_offset,
|
||||
int y, int x, int id, __local weight_t * weights_local,
|
||||
__local sum_t * weighted_sum_local, int almostTemplateWindowSizeSqBinShift)
|
||||
{
|
||||
int sx = x - SEARCH_SIZE2, sy = y - SEARCH_SIZE2;
|
||||
weight_t weights = (weight_t)0;
|
||||
sum_t weighted_sum = (sum_t)0;
|
||||
|
||||
for (int i = id; i < SEARCH_SIZE_SQ; i += CTA_SIZE)
|
||||
{
|
||||
int src_index = mad24(sy + i / SEARCH_SIZE, src_step, mad24(i % SEARCH_SIZE + sx, psz, src_offset));
|
||||
sum_t src_value = convert_sum_t(*(__global const pixel_t *)(src + src_index));
|
||||
|
||||
int almostAvgDist = dists[i] >> almostTemplateWindowSizeSqBinShift;
|
||||
weight_t weight = convert_weight_t(almostDist2Weight[almostAvgDist]);
|
||||
|
||||
weights += weight;
|
||||
weighted_sum += (sum_t)weight * src_value;
|
||||
}
|
||||
|
||||
weights_local[id] = weights;
|
||||
weighted_sum_local[id] = weighted_sum;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
for (int lsize = CTA_SIZE >> 1; lsize > 2; lsize >>= 1)
|
||||
{
|
||||
if (id < lsize)
|
||||
{
|
||||
int id2 = lsize + id;
|
||||
weights_local[id] += weights_local[id2];
|
||||
weighted_sum_local[id] += weighted_sum_local[id2];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
int dst_index = mad24(y, dst_step, mad24(psz, x, dst_offset));
|
||||
sum_t weighted_sum_local_0 = weighted_sum_local[0] + weighted_sum_local[1] +
|
||||
weighted_sum_local[2] + weighted_sum_local[3];
|
||||
weight_t weights_local_0 = weights_local[0] + weights_local[1] + weights_local[2] + weights_local[3];
|
||||
|
||||
*(__global pixel_t *)(dst + dst_index) = convert_pixel_t(weighted_sum_local_0 / (sum_t)weights_local_0);
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void fastNlMeansDenoising(__global const uchar * src, int src_step, int src_offset,
|
||||
__global uchar * dst, int dst_step, int dst_offset, int dst_rows, int dst_cols,
|
||||
__global const wlut_t * almostDist2Weight, __global uchar * buffer,
|
||||
int almostTemplateWindowSizeSqBinShift)
|
||||
{
|
||||
int block_x = get_group_id(0), nblocks_x = get_num_groups(0);
|
||||
int block_y = get_group_id(1);
|
||||
int id = get_local_id(0), first;
|
||||
|
||||
__local int dists[SEARCH_SIZE_SQ];
|
||||
__local weight_t weights[CTA_SIZE];
|
||||
__local sum_t weighted_sum[CTA_SIZE];
|
||||
|
||||
int x0 = block_x * BLOCK_COLS, x1 = min(x0 + BLOCK_COLS, dst_cols);
|
||||
int y0 = block_y * BLOCK_ROWS, y1 = min(y0 + BLOCK_ROWS, dst_rows);
|
||||
|
||||
// for each group we need SEARCH_SIZE_SQ * TEMPLATE_SIZE integer buffer for storing part column sum for current element
|
||||
// and SEARCH_SIZE_SQ * BLOCK_COLS integer buffer for storing last column sum for each element of search window of up row
|
||||
int block_data_start = SEARCH_SIZE_SQ * (mad24(block_y, dst_cols, x0) + mad24(block_y, nblocks_x, block_x) * TEMPLATE_SIZE);
|
||||
__global int * col_dists = (__global int *)(buffer + block_data_start * sizeof(int));
|
||||
__global int * up_col_dists = col_dists + SEARCH_SIZE_SQ * TEMPLATE_SIZE;
|
||||
|
||||
for (int y = y0; y < y1; ++y)
|
||||
for (int x = x0; x < x1; ++x)
|
||||
{
|
||||
if (x == x0)
|
||||
{
|
||||
calcFirstElementInRow(src, src_step, src_offset, dists, y, x, id, col_dists, up_col_dists);
|
||||
first = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (y == y0)
|
||||
calcElementInFirstRow(src, src_step, src_offset, dists, y, x - x0, x, id, first, col_dists, up_col_dists);
|
||||
else
|
||||
calcElement(src, src_step, src_offset, dists, y, x - x0, x, id, first, col_dists, up_col_dists);
|
||||
|
||||
first = (first + 1) % TEMPLATE_SIZE;
|
||||
}
|
||||
|
||||
convolveWindow(src, src_step, src_offset, dists, almostDist2Weight, dst, dst_step, dst_offset,
|
||||
y, x, id, weights, weighted_sum, almostTemplateWindowSizeSqBinShift);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/*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*/
|
||||
|
||||
#ifndef __OPENCV_PRECOMP_H__
|
||||
#define __OPENCV_PRECOMP_H__
|
||||
|
||||
#include "opencv2/core/private.hpp"
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
#include "opencv2/core/ocl.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,155 @@
|
||||
/*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/geometry.hpp"
|
||||
|
||||
#include "seamless_cloning.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
static Mat checkMask(InputArray mask, Size size)
|
||||
{
|
||||
Mat gray;
|
||||
if (mask.channels() == 3 || mask.channels() == 4)
|
||||
cvtColor(mask, gray, COLOR_BGRA2GRAY);
|
||||
else
|
||||
{
|
||||
if (mask.empty())
|
||||
gray = Mat(size.height, size.width, CV_8UC1, Scalar(255));
|
||||
else
|
||||
return mask.getMat();
|
||||
}
|
||||
|
||||
return gray;
|
||||
}
|
||||
|
||||
void cv::seamlessClone(InputArray _src, InputArray _dst, InputArray _mask, Point p, OutputArray _blend, int flags)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
CV_Assert(!_src.empty());
|
||||
CV_Assert(!_dst.empty());
|
||||
|
||||
const Mat src = _src.getMat();
|
||||
const Mat dest = _dst.getMat();
|
||||
|
||||
Mat mask = checkMask(_mask, src.size());
|
||||
dest.copyTo(_blend);
|
||||
Mat blend = _blend.getMat();
|
||||
|
||||
Mat mask_inner = mask(Rect(1, 1, mask.cols - 2, mask.rows - 2));
|
||||
copyMakeBorder(mask_inner, mask, 1, 1, 1, 1, BORDER_ISOLATED | BORDER_CONSTANT, Scalar(0));
|
||||
|
||||
Rect roi_s = boundingRect(mask);
|
||||
if (roi_s.empty()) return;
|
||||
|
||||
int l_from_center = p.x - roi_s.width / 2;
|
||||
int t_from_center = p.y - roi_s.height / 2;
|
||||
|
||||
if (flags >= NORMAL_CLONE_WIDE)
|
||||
{
|
||||
l_from_center = p.x - (mask.cols / 2 - roi_s.x);
|
||||
t_from_center = p.y - (mask.rows / 2 - roi_s.y);
|
||||
}
|
||||
|
||||
Rect roi_d(l_from_center, t_from_center, roi_s.width, roi_s.height);
|
||||
Mat destinationROI = dest(roi_d);
|
||||
Mat sourceROI = Mat::zeros(roi_s.height, roi_s.width, src.type());
|
||||
src(roi_s).copyTo(sourceROI,mask(roi_s));
|
||||
|
||||
Mat maskROI = mask(roi_s);
|
||||
Mat recoveredROI = blend(roi_d);
|
||||
|
||||
Cloning obj;
|
||||
obj.normalClone(destinationROI,sourceROI,maskROI,recoveredROI,flags);
|
||||
}
|
||||
|
||||
void cv::colorChange(InputArray _src, InputArray _mask, OutputArray _dst, float red, float green, float blue)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat src = _src.getMat();
|
||||
Mat mask = checkMask(_mask, src.size());
|
||||
_dst.create(src.size(), src.type());
|
||||
Mat blend = _dst.getMat();
|
||||
|
||||
Mat cs_mask = Mat::zeros(src.size(), src.type());
|
||||
src.copyTo(cs_mask, mask);
|
||||
|
||||
Cloning obj;
|
||||
obj.localColorChange(src, cs_mask, mask, blend, red, green, blue);
|
||||
}
|
||||
|
||||
void cv::illuminationChange(InputArray _src, InputArray _mask, OutputArray _dst, float alpha, float beta)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat src = _src.getMat();
|
||||
Mat mask = checkMask(_mask, src.size());
|
||||
_dst.create(src.size(), src.type());
|
||||
Mat blend = _dst.getMat();
|
||||
|
||||
Mat cs_mask = Mat::zeros(src.size(), src.type());
|
||||
src.copyTo(cs_mask, mask);
|
||||
|
||||
Cloning obj;
|
||||
obj.illuminationChange(src, cs_mask, mask, blend, alpha, beta);
|
||||
|
||||
}
|
||||
|
||||
void cv::textureFlattening(InputArray _src, InputArray _mask, OutputArray _dst,
|
||||
float low_threshold, float high_threshold, int kernel_size)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat src = _src.getMat();
|
||||
Mat mask = checkMask(_mask, src.size());
|
||||
_dst.create(src.size(), src.type());
|
||||
Mat blend = _dst.getMat();
|
||||
|
||||
Mat cs_mask = Mat::zeros(src.size(), src.type());
|
||||
src.copyTo(cs_mask, mask);
|
||||
|
||||
Cloning obj;
|
||||
obj.textureFlatten(src, cs_mask, mask, low_threshold, high_threshold, kernel_size, blend);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*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 CV_SEAMLESS_CLONING_HPP___
|
||||
#define CV_SEAMLESS_CLONING_HPP___
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
class Cloning
|
||||
{
|
||||
public:
|
||||
void normalClone(const cv::Mat& destination, const cv::Mat &mask, cv::Mat &wmask, cv::Mat &cloned, int flag);
|
||||
void illuminationChange(cv::Mat &I, cv::Mat &mask, cv::Mat &wmask, cv::Mat &cloned, float alpha, float beta);
|
||||
void localColorChange(cv::Mat &I, cv::Mat &mask, cv::Mat &wmask, cv::Mat &cloned, float red_mul, float green_mul, float blue_mul);
|
||||
void textureFlatten(cv::Mat &I, cv::Mat &mask, cv::Mat &wmask, float low_threshold, float high_threhold, int kernel_size, cv::Mat &cloned);
|
||||
|
||||
protected:
|
||||
|
||||
void initVariables(const cv::Mat &destination, const cv::Mat &binaryMask);
|
||||
void computeDerivatives(const cv::Mat &destination, const cv::Mat &patch, cv::Mat &binaryMask);
|
||||
void scalarProduct(cv::Mat mat, float r, float g, float b);
|
||||
void poisson(const cv::Mat &destination);
|
||||
void evaluate(const cv::Mat &I, cv::Mat &wmask, const cv::Mat &cloned);
|
||||
void dst(const Mat& src, Mat& dest, bool invert = false);
|
||||
void solve(const Mat &img, Mat& mod_diff, Mat &result);
|
||||
|
||||
void poissonSolver(const cv::Mat &img, cv::Mat &gxx , cv::Mat &gyy, cv::Mat &result);
|
||||
|
||||
void arrayProduct(const cv::Mat& lhs, const cv::Mat& rhs, cv::Mat& result) const;
|
||||
|
||||
void computeGradientX(const cv::Mat &img, cv::Mat &gx);
|
||||
void computeGradientY(const cv::Mat &img, cv::Mat &gy);
|
||||
void computeLaplacianX(const cv::Mat &img, cv::Mat &gxx);
|
||||
void computeLaplacianY(const cv::Mat &img, cv::Mat &gyy);
|
||||
|
||||
private:
|
||||
std::vector <cv::Mat> rgbx_channel, rgby_channel, output;
|
||||
cv::Mat destinationGradientX, destinationGradientY;
|
||||
cv::Mat patchGradientX, patchGradientY;
|
||||
cv::Mat binaryMaskFloat, binaryMaskFloatInverted;
|
||||
|
||||
std::vector<float> filter_X, filter_Y;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,451 @@
|
||||
/*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 "seamless_cloning.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
|
||||
void Cloning::computeGradientX( const Mat &img, Mat &gx)
|
||||
{
|
||||
Mat kernel = Mat::zeros(1, 3, CV_8S);
|
||||
kernel.at<char>(0,2) = 1;
|
||||
kernel.at<char>(0,1) = -1;
|
||||
|
||||
if(img.channels() == 3)
|
||||
{
|
||||
filter2D(img, gx, CV_32F, kernel);
|
||||
}
|
||||
else if (img.channels() == 1)
|
||||
{
|
||||
filter2D(img, gx, CV_32F, kernel);
|
||||
cvtColor(gx, gx, COLOR_GRAY2BGR);
|
||||
}
|
||||
}
|
||||
|
||||
void Cloning::computeGradientY( const Mat &img, Mat &gy)
|
||||
{
|
||||
Mat kernel = Mat::zeros(3, 1, CV_8S);
|
||||
kernel.at<char>(2,0) = 1;
|
||||
kernel.at<char>(1,0) = -1;
|
||||
|
||||
if(img.channels() == 3)
|
||||
{
|
||||
filter2D(img, gy, CV_32F, kernel);
|
||||
}
|
||||
else if (img.channels() == 1)
|
||||
{
|
||||
filter2D(img, gy, CV_32F, kernel);
|
||||
cvtColor(gy, gy, COLOR_GRAY2BGR);
|
||||
}
|
||||
}
|
||||
|
||||
void Cloning::computeLaplacianX( const Mat &img, Mat &laplacianX)
|
||||
{
|
||||
Mat kernel = Mat::zeros(1, 3, CV_8S);
|
||||
kernel.at<char>(0,0) = -1;
|
||||
kernel.at<char>(0,1) = 1;
|
||||
filter2D(img, laplacianX, CV_32F, kernel);
|
||||
}
|
||||
|
||||
void Cloning::computeLaplacianY( const Mat &img, Mat &laplacianY)
|
||||
{
|
||||
Mat kernel = Mat::zeros(3, 1, CV_8S);
|
||||
kernel.at<char>(0,0) = -1;
|
||||
kernel.at<char>(1,0) = 1;
|
||||
filter2D(img, laplacianY, CV_32F, kernel);
|
||||
}
|
||||
|
||||
void Cloning::dst(const Mat& src, Mat& dest, bool invert)
|
||||
{
|
||||
Mat temp = Mat::zeros(src.rows, 2 * src.cols + 2, CV_32F);
|
||||
|
||||
int flag = invert ? DFT_ROWS + DFT_SCALE + DFT_INVERSE: DFT_ROWS;
|
||||
|
||||
src.copyTo(temp(Rect(1,0, src.cols, src.rows)));
|
||||
|
||||
for(int j = 0 ; j < src.rows ; ++j)
|
||||
{
|
||||
float * tempLinePtr = temp.ptr<float>(j);
|
||||
const float * srcLinePtr = src.ptr<float>(j);
|
||||
for(int i = 0 ; i < src.cols ; ++i)
|
||||
{
|
||||
tempLinePtr[src.cols + 2 + i] = - srcLinePtr[src.cols - 1 - i];
|
||||
}
|
||||
}
|
||||
|
||||
Mat planes[] = {temp, Mat::zeros(temp.size(), CV_32F)};
|
||||
Mat complex;
|
||||
|
||||
merge(planes, 2, complex);
|
||||
dft(complex, complex, flag);
|
||||
split(complex, planes);
|
||||
temp = Mat::zeros(src.cols, 2 * src.rows + 2, CV_32F);
|
||||
|
||||
for(int j = 0 ; j < src.cols ; ++j)
|
||||
{
|
||||
float * tempLinePtr = temp.ptr<float>(j);
|
||||
for(int i = 0 ; i < src.rows ; ++i)
|
||||
{
|
||||
float val = planes[1].ptr<float>(i)[j + 1];
|
||||
tempLinePtr[i + 1] = val;
|
||||
tempLinePtr[temp.cols - 1 - i] = - val;
|
||||
}
|
||||
}
|
||||
|
||||
Mat planes2[] = {temp, Mat::zeros(temp.size(), CV_32F)};
|
||||
|
||||
merge(planes2, 2, complex);
|
||||
dft(complex, complex, flag);
|
||||
split(complex, planes2);
|
||||
|
||||
temp = planes2[1].t();
|
||||
temp(Rect( 0, 1, src.cols, src.rows)).copyTo(dest);
|
||||
}
|
||||
|
||||
void Cloning::solve(const Mat &img, Mat& mod_diff, Mat &result)
|
||||
{
|
||||
const int w = img.cols;
|
||||
const int h = img.rows;
|
||||
|
||||
Mat res;
|
||||
dst(mod_diff, res);
|
||||
|
||||
for(int j = 0 ; j < h-2; j++)
|
||||
{
|
||||
float * resLinePtr = res.ptr<float>(j);
|
||||
for(int i = 0 ; i < w-2; i++)
|
||||
{
|
||||
resLinePtr[i] /= (filter_X[i] + filter_Y[j] - 4);
|
||||
}
|
||||
}
|
||||
|
||||
dst(res, mod_diff, true);
|
||||
|
||||
unsigned char * resLinePtr = result.ptr<unsigned char>(0);
|
||||
const unsigned char * imgLinePtr = img.ptr<unsigned char>(0);
|
||||
const float * interpLinePtr = NULL;
|
||||
|
||||
//first col
|
||||
for(int i = 0 ; i < w ; ++i)
|
||||
result.ptr<unsigned char>(0)[i] = img.ptr<unsigned char>(0)[i];
|
||||
|
||||
for(int j = 1 ; j < h-1 ; ++j)
|
||||
{
|
||||
resLinePtr = result.ptr<unsigned char>(j);
|
||||
imgLinePtr = img.ptr<unsigned char>(j);
|
||||
interpLinePtr = mod_diff.ptr<float>(j-1);
|
||||
|
||||
//first row
|
||||
resLinePtr[0] = imgLinePtr[0];
|
||||
|
||||
for(int i = 1 ; i < w-1 ; ++i)
|
||||
{
|
||||
//saturate cast is not used here, because it behaves differently from the previous implementation
|
||||
//most notable, saturate_cast rounds before truncating, here it's the opposite.
|
||||
float value = interpLinePtr[i-1];
|
||||
if(value < 0.)
|
||||
resLinePtr[i] = 0;
|
||||
else if (value > 255.0)
|
||||
resLinePtr[i] = 255;
|
||||
else
|
||||
resLinePtr[i] = static_cast<unsigned char>(value);
|
||||
}
|
||||
|
||||
//last row
|
||||
resLinePtr[w-1] = imgLinePtr[w-1];
|
||||
}
|
||||
|
||||
//last col
|
||||
resLinePtr = result.ptr<unsigned char>(h-1);
|
||||
imgLinePtr = img.ptr<unsigned char>(h-1);
|
||||
for(int i = 0 ; i < w ; ++i)
|
||||
resLinePtr[i] = imgLinePtr[i];
|
||||
}
|
||||
|
||||
void Cloning::poissonSolver(const Mat &img, Mat &laplacianX , Mat &laplacianY, Mat &result)
|
||||
{
|
||||
const int w = img.cols;
|
||||
const int h = img.rows;
|
||||
|
||||
Mat lap = laplacianX + laplacianY;
|
||||
|
||||
Mat bound = img.clone();
|
||||
|
||||
rectangle(bound, Point(1, 1), Point(img.cols-2, img.rows-2), Scalar::all(0), -1);
|
||||
Mat boundary_points;
|
||||
Laplacian(bound, boundary_points, CV_32F);
|
||||
|
||||
boundary_points = lap - boundary_points;
|
||||
|
||||
Mat mod_diff = boundary_points(Rect(1, 1, w-2, h-2));
|
||||
|
||||
solve(img,mod_diff,result);
|
||||
}
|
||||
|
||||
void Cloning::initVariables(const Mat &destination, const Mat &binaryMask)
|
||||
{
|
||||
destinationGradientX = Mat(destination.size(),CV_32FC3);
|
||||
destinationGradientY = Mat(destination.size(),CV_32FC3);
|
||||
patchGradientX = Mat(destination.size(),CV_32FC3);
|
||||
patchGradientY = Mat(destination.size(),CV_32FC3);
|
||||
|
||||
binaryMaskFloat = Mat(binaryMask.size(),CV_32FC1);
|
||||
binaryMaskFloatInverted = Mat(binaryMask.size(),CV_32FC1);
|
||||
|
||||
//init of the filters used in the dst
|
||||
const int w = destination.cols;
|
||||
filter_X.resize(w - 2);
|
||||
double scale = CV_PI / (w - 1);
|
||||
for(int i = 0 ; i < w-2 ; ++i)
|
||||
filter_X[i] = 2.0f * (float)std::cos(scale * (i + 1));
|
||||
|
||||
const int h = destination.rows;
|
||||
filter_Y.resize(h - 2);
|
||||
scale = CV_PI / (h - 1);
|
||||
for(int j = 0 ; j < h - 2 ; ++j)
|
||||
filter_Y[j] = 2.0f * (float)std::cos(scale * (j + 1));
|
||||
}
|
||||
|
||||
void Cloning::computeDerivatives(const Mat& destination, const Mat &patch, Mat &binaryMask)
|
||||
{
|
||||
initVariables(destination, binaryMask);
|
||||
|
||||
computeGradientX(destination, destinationGradientX);
|
||||
computeGradientY(destination, destinationGradientY);
|
||||
|
||||
computeGradientX(patch, patchGradientX);
|
||||
computeGradientY(patch, patchGradientY);
|
||||
|
||||
Mat Kernel(Size(3, 3), CV_8UC1);
|
||||
Kernel.setTo(Scalar(1));
|
||||
erode(binaryMask, binaryMask, Kernel, Point(-1,-1), 3);
|
||||
|
||||
binaryMask.convertTo(binaryMaskFloat, CV_32FC1, 1.0/255.0);
|
||||
}
|
||||
|
||||
void Cloning::scalarProduct(Mat mat, float r, float g, float b)
|
||||
{
|
||||
vector <Mat> channels;
|
||||
split(mat,channels);
|
||||
multiply(channels[2],r,channels[2]);
|
||||
multiply(channels[1],g,channels[1]);
|
||||
multiply(channels[0],b,channels[0]);
|
||||
merge(channels,mat);
|
||||
}
|
||||
|
||||
void Cloning::arrayProduct(const cv::Mat& lhs, const cv::Mat& rhs, cv::Mat& result) const
|
||||
{
|
||||
vector <Mat> lhs_channels;
|
||||
vector <Mat> result_channels;
|
||||
|
||||
split(lhs,lhs_channels);
|
||||
split(result,result_channels);
|
||||
|
||||
for(int chan = 0 ; chan < 3 ; ++chan)
|
||||
multiply(lhs_channels[chan],rhs,result_channels[chan]);
|
||||
|
||||
merge(result_channels,result);
|
||||
}
|
||||
|
||||
void Cloning::poisson(const Mat &destination)
|
||||
{
|
||||
Mat laplacianX = destinationGradientX + patchGradientX;
|
||||
Mat laplacianY = destinationGradientY + patchGradientY;
|
||||
|
||||
computeLaplacianX(laplacianX,laplacianX);
|
||||
computeLaplacianY(laplacianY,laplacianY);
|
||||
|
||||
split(laplacianX,rgbx_channel);
|
||||
split(laplacianY,rgby_channel);
|
||||
|
||||
split(destination,output);
|
||||
|
||||
for(int chan = 0 ; chan < 3 ; ++chan)
|
||||
{
|
||||
poissonSolver(output[chan], rgbx_channel[chan], rgby_channel[chan], output[chan]);
|
||||
}
|
||||
}
|
||||
|
||||
void Cloning::evaluate(const Mat &I, Mat &wmask, const Mat &cloned)
|
||||
{
|
||||
bitwise_not(wmask,wmask);
|
||||
|
||||
wmask.convertTo(binaryMaskFloatInverted,CV_32FC1,1.0/255.0);
|
||||
|
||||
arrayProduct(destinationGradientX, binaryMaskFloatInverted, destinationGradientX);
|
||||
arrayProduct(destinationGradientY, binaryMaskFloatInverted, destinationGradientY);
|
||||
|
||||
poisson(I);
|
||||
|
||||
merge(output,cloned);
|
||||
}
|
||||
|
||||
void Cloning::normalClone(const Mat &destination, const Mat &patch, Mat &binaryMask, Mat &cloned, int flag)
|
||||
{
|
||||
const int w = destination.cols;
|
||||
const int h = destination.rows;
|
||||
const int channel = destination.channels();
|
||||
const int n_elem_in_line = w * channel;
|
||||
|
||||
computeDerivatives(destination,patch,binaryMask);
|
||||
|
||||
switch(flag)
|
||||
{
|
||||
case NORMAL_CLONE:
|
||||
case NORMAL_CLONE_WIDE:
|
||||
arrayProduct(patchGradientX, binaryMaskFloat, patchGradientX);
|
||||
arrayProduct(patchGradientY, binaryMaskFloat, patchGradientY);
|
||||
break;
|
||||
|
||||
case MIXED_CLONE:
|
||||
case MIXED_CLONE_WIDE:
|
||||
{
|
||||
AutoBuffer<int> maskIndices(n_elem_in_line);
|
||||
for (int i = 0; i < n_elem_in_line; ++i)
|
||||
maskIndices[i] = i / channel;
|
||||
|
||||
for(int i=0;i < h; i++)
|
||||
{
|
||||
float * patchXLinePtr = patchGradientX.ptr<float>(i);
|
||||
float * patchYLinePtr = patchGradientY.ptr<float>(i);
|
||||
const float * destinationXLinePtr = destinationGradientX.ptr<float>(i);
|
||||
const float * destinationYLinePtr = destinationGradientY.ptr<float>(i);
|
||||
const float * binaryMaskLinePtr = binaryMaskFloat.ptr<float>(i);
|
||||
|
||||
for(int j=0; j < n_elem_in_line; j++)
|
||||
{
|
||||
int maskIndex = maskIndices[j];
|
||||
|
||||
if(abs(patchXLinePtr[j] - patchYLinePtr[j]) >
|
||||
abs(destinationXLinePtr[j] - destinationYLinePtr[j]))
|
||||
{
|
||||
patchXLinePtr[j] *= binaryMaskLinePtr[maskIndex];
|
||||
patchYLinePtr[j] *= binaryMaskLinePtr[maskIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
patchXLinePtr[j] = destinationXLinePtr[j]
|
||||
* binaryMaskLinePtr[maskIndex];
|
||||
patchYLinePtr[j] = destinationYLinePtr[j]
|
||||
* binaryMaskLinePtr[maskIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MONOCHROME_TRANSFER:
|
||||
case MONOCHROME_TRANSFER_WIDE:
|
||||
Mat gray;
|
||||
cvtColor(patch, gray, COLOR_BGR2GRAY );
|
||||
|
||||
computeGradientX(gray,patchGradientX);
|
||||
computeGradientY(gray,patchGradientY);
|
||||
|
||||
arrayProduct(patchGradientX, binaryMaskFloat, patchGradientX);
|
||||
arrayProduct(patchGradientY, binaryMaskFloat, patchGradientY);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
evaluate(destination,binaryMask,cloned);
|
||||
}
|
||||
|
||||
void Cloning::localColorChange(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, float red_mul=1.0,
|
||||
float green_mul=1.0, float blue_mul=1.0)
|
||||
{
|
||||
computeDerivatives(I,mask,wmask);
|
||||
|
||||
arrayProduct(patchGradientX,binaryMaskFloat, patchGradientX);
|
||||
arrayProduct(patchGradientY,binaryMaskFloat, patchGradientY);
|
||||
scalarProduct(patchGradientX,red_mul,green_mul,blue_mul);
|
||||
scalarProduct(patchGradientY,red_mul,green_mul,blue_mul);
|
||||
|
||||
evaluate(I,wmask,cloned);
|
||||
}
|
||||
|
||||
void Cloning::illuminationChange(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, float alpha, float beta)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
computeDerivatives(I,mask,wmask);
|
||||
|
||||
arrayProduct(patchGradientX,binaryMaskFloat, patchGradientX);
|
||||
arrayProduct(patchGradientY,binaryMaskFloat, patchGradientY);
|
||||
|
||||
Mat mag;
|
||||
magnitude(patchGradientX,patchGradientY,mag);
|
||||
|
||||
Mat multX, multY, multx_temp, multy_temp;
|
||||
|
||||
multiply(patchGradientX,pow(alpha,beta),multX);
|
||||
pow(mag,-1*beta, multx_temp);
|
||||
multiply(multX,multx_temp, patchGradientX);
|
||||
patchNaNs(patchGradientX);
|
||||
|
||||
multiply(patchGradientY,pow(alpha,beta),multY);
|
||||
pow(mag,-1*beta, multy_temp);
|
||||
multiply(multY,multy_temp,patchGradientY);
|
||||
patchNaNs(patchGradientY);
|
||||
|
||||
evaluate(I,wmask,cloned);
|
||||
}
|
||||
|
||||
void Cloning::textureFlatten(Mat &I, Mat &mask, Mat &wmask, float low_threshold,
|
||||
float high_threshold, int kernel_size, Mat &cloned)
|
||||
{
|
||||
computeDerivatives(I,mask,wmask);
|
||||
|
||||
Mat out;
|
||||
Canny(mask,out,low_threshold,high_threshold,kernel_size);
|
||||
|
||||
Mat zeros = Mat::zeros(patchGradientX.size(), CV_32FC3);
|
||||
Mat zerosMask = (out != 255);
|
||||
zeros.copyTo(patchGradientX, zerosMask);
|
||||
zeros.copyTo(patchGradientY, zerosMask);
|
||||
|
||||
arrayProduct(patchGradientX,binaryMaskFloat, patchGradientX);
|
||||
arrayProduct(patchGradientY,binaryMaskFloat, patchGradientY);
|
||||
|
||||
evaluate(I,wmask,cloned);
|
||||
}
|
||||
@@ -0,0 +1,476 @@
|
||||
/*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
|
||||
{
|
||||
|
||||
inline void log_(const Mat& src, Mat& dst)
|
||||
{
|
||||
max(src, Scalar::all(1e-4), dst);
|
||||
log(dst, dst);
|
||||
}
|
||||
|
||||
class TonemapImpl CV_FINAL : public Tonemap
|
||||
{
|
||||
public:
|
||||
TonemapImpl(float _gamma) : name("Tonemap"), gamma(_gamma)
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat src = _src.getMat();
|
||||
CV_Assert(!src.empty());
|
||||
CV_Assert(_src.dims() == 2 && _src.type() == CV_32FC3);
|
||||
_dst.create(src.size(), CV_32FC3);
|
||||
Mat dst = _dst.getMat();
|
||||
|
||||
double min, max;
|
||||
minMaxLoc(src, &min, &max);
|
||||
float fmin = static_cast<float>(min);
|
||||
float fmax = static_cast<float>(max);
|
||||
if(max - min > DBL_EPSILON) {
|
||||
dst = (src - fmin) / (fmax - fmin);
|
||||
} else {
|
||||
src.copyTo(dst);
|
||||
}
|
||||
|
||||
pow(dst, 1.0f / gamma, dst);
|
||||
}
|
||||
|
||||
float getGamma() const CV_OVERRIDE { return gamma; }
|
||||
void setGamma(float val) CV_OVERRIDE { gamma = val; }
|
||||
|
||||
void write(FileStorage& fs) const CV_OVERRIDE
|
||||
{
|
||||
writeFormat(fs);
|
||||
fs << "name" << name
|
||||
<< "gamma" << gamma;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn) CV_OVERRIDE
|
||||
{
|
||||
FileNode n = fn["name"];
|
||||
CV_Assert(n.isString() && String(n) == name);
|
||||
gamma = fn["gamma"];
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
float gamma;
|
||||
};
|
||||
|
||||
Ptr<Tonemap> createTonemap(float gamma)
|
||||
{
|
||||
return makePtr<TonemapImpl>(gamma);
|
||||
}
|
||||
|
||||
class TonemapDragoImpl CV_FINAL : public TonemapDrago
|
||||
{
|
||||
public:
|
||||
TonemapDragoImpl(float _gamma, float _saturation, float _bias) :
|
||||
name("TonemapDrago"),
|
||||
gamma(_gamma),
|
||||
saturation(_saturation),
|
||||
bias(_bias)
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat src = _src.getMat();
|
||||
CV_Assert(!src.empty());
|
||||
_dst.create(src.size(), CV_32FC3);
|
||||
Mat img = _dst.getMat();
|
||||
|
||||
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 dmax;
|
||||
minMaxLoc(gray_img, NULL, &dmax);
|
||||
float max = static_cast<float>(dmax);
|
||||
CV_Assert(max > 0);
|
||||
|
||||
Mat map;
|
||||
log(gray_img + 1.0f, map);
|
||||
Mat div;
|
||||
pow(gray_img / static_cast<float>(max), logf(bias) / logf(0.5f), div);
|
||||
log(2.0f + 8.0f * div, div);
|
||||
map = map.mul(1.0f / div);
|
||||
div.release();
|
||||
mapLuminance(img, img, gray_img, map, saturation);
|
||||
|
||||
linear->setGamma(gamma);
|
||||
linear->process(img, img);
|
||||
}
|
||||
|
||||
float getGamma() const CV_OVERRIDE { return gamma; }
|
||||
void setGamma(float val) CV_OVERRIDE { gamma = val; }
|
||||
|
||||
float getSaturation() const CV_OVERRIDE { return saturation; }
|
||||
void setSaturation(float val) CV_OVERRIDE { saturation = val; }
|
||||
|
||||
float getBias() const CV_OVERRIDE { return bias; }
|
||||
void setBias(float val) CV_OVERRIDE { bias = val; }
|
||||
|
||||
void write(FileStorage& fs) const CV_OVERRIDE
|
||||
{
|
||||
writeFormat(fs);
|
||||
fs << "name" << name
|
||||
<< "gamma" << gamma
|
||||
<< "bias" << bias
|
||||
<< "saturation" << saturation;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn) CV_OVERRIDE
|
||||
{
|
||||
FileNode n = fn["name"];
|
||||
CV_Assert(n.isString() && String(n) == name);
|
||||
gamma = fn["gamma"];
|
||||
bias = fn["bias"];
|
||||
saturation = fn["saturation"];
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
float gamma, saturation, bias;
|
||||
};
|
||||
|
||||
Ptr<TonemapDrago> createTonemapDrago(float gamma, float saturation, float bias)
|
||||
{
|
||||
return makePtr<TonemapDragoImpl>(gamma, saturation, bias);
|
||||
}
|
||||
|
||||
class TonemapReinhardImpl CV_FINAL : 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) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
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 dlog_min, dlog_max;
|
||||
minMaxLoc(log_img, &dlog_min, &dlog_max);
|
||||
float log_max = static_cast<float>(dlog_max);
|
||||
float log_min = static_cast<float>(dlog_min);
|
||||
log_img.release();
|
||||
|
||||
float key = (log_max - log_mean) / (log_max - log_min);
|
||||
float map_key = 0.3f + 0.7f * std::pow(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 CV_OVERRIDE { return gamma; }
|
||||
void setGamma(float val) CV_OVERRIDE { gamma = val; }
|
||||
|
||||
float getIntensity() const CV_OVERRIDE { return intensity; }
|
||||
void setIntensity(float val) CV_OVERRIDE { intensity = val; }
|
||||
|
||||
float getLightAdaptation() const CV_OVERRIDE { return light_adapt; }
|
||||
void setLightAdaptation(float val) CV_OVERRIDE { light_adapt = val; }
|
||||
|
||||
float getColorAdaptation() const CV_OVERRIDE { return color_adapt; }
|
||||
void setColorAdaptation(float val) CV_OVERRIDE { color_adapt = val; }
|
||||
|
||||
void write(FileStorage& fs) const CV_OVERRIDE
|
||||
{
|
||||
writeFormat(fs);
|
||||
fs << "name" << name
|
||||
<< "gamma" << gamma
|
||||
<< "intensity" << intensity
|
||||
<< "light_adapt" << light_adapt
|
||||
<< "color_adapt" << color_adapt;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn) CV_OVERRIDE
|
||||
{
|
||||
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 intensity, float light_adapt, float color_adapt)
|
||||
{
|
||||
return makePtr<TonemapReinhardImpl>(gamma, intensity, light_adapt, color_adapt);
|
||||
}
|
||||
|
||||
class TonemapMantiukImpl CV_FINAL : public TonemapMantiuk
|
||||
{
|
||||
public:
|
||||
TonemapMantiukImpl(float _gamma, float _scale, float _saturation) :
|
||||
name("TonemapMantiuk"),
|
||||
gamma(_gamma),
|
||||
scale(_scale),
|
||||
saturation(_saturation)
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat src = _src.getMat();
|
||||
CV_Assert(!src.empty());
|
||||
_dst.create(src.size(), CV_32FC3);
|
||||
Mat img = _dst.getMat();
|
||||
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);
|
||||
double dprod = p.dot(product);
|
||||
CV_Assert(fabs(dprod) > 0);
|
||||
float alpha = rr / static_cast<float>(dprod);
|
||||
|
||||
r -= alpha * product;
|
||||
x += alpha * p;
|
||||
|
||||
float new_rr = static_cast<float>(r.dot(r));
|
||||
CV_Assert(fabs(rr) > 0);
|
||||
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 CV_OVERRIDE { return gamma; }
|
||||
void setGamma(float val) CV_OVERRIDE { gamma = val; }
|
||||
|
||||
float getScale() const CV_OVERRIDE { return scale; }
|
||||
void setScale(float val) CV_OVERRIDE { scale = val; }
|
||||
|
||||
float getSaturation() const CV_OVERRIDE { return saturation; }
|
||||
void setSaturation(float val) CV_OVERRIDE { saturation = val; }
|
||||
|
||||
void write(FileStorage& fs) const CV_OVERRIDE
|
||||
{
|
||||
writeFormat(fs);
|
||||
fs << "name" << name
|
||||
<< "gamma" << gamma
|
||||
<< "scale" << scale
|
||||
<< "saturation" << saturation;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn) CV_OVERRIDE
|
||||
{
|
||||
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), 0, 0, INTER_LINEAR);
|
||||
}
|
||||
}
|
||||
|
||||
void calculateSum(std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast, Mat& sum)
|
||||
{
|
||||
if (x_contrast.empty())
|
||||
return;
|
||||
const int last = (int)x_contrast.size() - 1;
|
||||
sum = Mat::zeros(x_contrast[last].size(), CV_32F);
|
||||
for(int i = last; 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(), 0, 0, INTER_LINEAR);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2014, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_test.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
|
||||
PARAM_TEST_CASE(FastNlMeansDenoisingTestBase, Channels, int, bool, bool)
|
||||
{
|
||||
int cn, normType, templateWindowSize, searchWindowSize;
|
||||
std::vector<float> h;
|
||||
bool use_roi, use_image;
|
||||
|
||||
TEST_DECLARE_INPUT_PARAMETER(src);
|
||||
TEST_DECLARE_OUTPUT_PARAMETER(dst);
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
cn = GET_PARAM(0);
|
||||
normType = GET_PARAM(1);
|
||||
use_roi = GET_PARAM(2);
|
||||
use_image = GET_PARAM(3);
|
||||
|
||||
templateWindowSize = 7;
|
||||
searchWindowSize = 21;
|
||||
|
||||
h.resize(cn);
|
||||
for (int i=0; i<cn; i++)
|
||||
h[i] = 3.0f + 0.5f*i;
|
||||
}
|
||||
|
||||
void generateTestData()
|
||||
{
|
||||
const int type = CV_8UC(cn);
|
||||
Mat image;
|
||||
|
||||
if (use_image) {
|
||||
image = readImage("denoising/lena_noised_gaussian_sigma=10.png",
|
||||
cn == 1 ? IMREAD_GRAYSCALE : IMREAD_COLOR);
|
||||
ASSERT_FALSE(image.empty());
|
||||
}
|
||||
|
||||
Size roiSize = use_image ? image.size() : randomSize(1, MAX_VALUE);
|
||||
Border srcBorder = randomBorder(0, use_roi ? MAX_VALUE : 0);
|
||||
randomSubMat(src, src_roi, roiSize, srcBorder, type, 0, 255);
|
||||
if (use_image) {
|
||||
ASSERT_TRUE(cn > 0 && cn <= 4);
|
||||
if (cn == 2) {
|
||||
int from_to[] = { 0,0, 1,1 };
|
||||
src_roi.create(roiSize, type);
|
||||
mixChannels(&image, 1, &src_roi, 1, from_to, 2);
|
||||
}
|
||||
else if (cn == 4) {
|
||||
int from_to[] = { 0,0, 1,1, 2,2, 1,3};
|
||||
src_roi.create(roiSize, type);
|
||||
mixChannels(&image, 1, &src_roi, 1, from_to, 4);
|
||||
}
|
||||
else image.copyTo(src_roi);
|
||||
}
|
||||
|
||||
Border dstBorder = randomBorder(0, use_roi ? MAX_VALUE : 0);
|
||||
randomSubMat(dst, dst_roi, roiSize, dstBorder, type, 0, 255);
|
||||
|
||||
UMAT_UPLOAD_INPUT_PARAMETER(src);
|
||||
UMAT_UPLOAD_OUTPUT_PARAMETER(dst);
|
||||
}
|
||||
};
|
||||
|
||||
typedef FastNlMeansDenoisingTestBase FastNlMeansDenoising;
|
||||
|
||||
OCL_TEST_P(FastNlMeansDenoising, Mat)
|
||||
{
|
||||
for (int j = 0; j < test_loop_times; j++)
|
||||
{
|
||||
generateTestData();
|
||||
|
||||
OCL_OFF(cv::fastNlMeansDenoising(src_roi, dst_roi, std::vector<float>(1, h[0]), templateWindowSize, searchWindowSize, normType));
|
||||
OCL_ON(cv::fastNlMeansDenoising(usrc_roi, udst_roi, std::vector<float>(1, h[0]), templateWindowSize, searchWindowSize, normType));
|
||||
|
||||
OCL_EXPECT_MATS_NEAR(dst, 1);
|
||||
}
|
||||
}
|
||||
|
||||
typedef FastNlMeansDenoisingTestBase FastNlMeansDenoising_hsep;
|
||||
|
||||
OCL_TEST_P(FastNlMeansDenoising_hsep, Mat)
|
||||
{
|
||||
for (int j = 0; j < test_loop_times; j++)
|
||||
{
|
||||
generateTestData();
|
||||
|
||||
OCL_OFF(cv::fastNlMeansDenoising(src_roi, dst_roi, h, templateWindowSize, searchWindowSize, normType));
|
||||
OCL_ON(cv::fastNlMeansDenoising(usrc_roi, udst_roi, h, templateWindowSize, searchWindowSize, normType));
|
||||
|
||||
OCL_EXPECT_MATS_NEAR(dst, 1);
|
||||
}
|
||||
}
|
||||
|
||||
typedef FastNlMeansDenoisingTestBase FastNlMeansDenoisingColored;
|
||||
|
||||
OCL_TEST_P(FastNlMeansDenoisingColored, Mat)
|
||||
{
|
||||
for (int j = 0; j < test_loop_times; j++)
|
||||
{
|
||||
generateTestData();
|
||||
|
||||
OCL_OFF(cv::fastNlMeansDenoisingColored(src_roi, dst_roi, h[0], h[0], templateWindowSize, searchWindowSize));
|
||||
OCL_ON(cv::fastNlMeansDenoisingColored(usrc_roi, udst_roi, h[0], h[0], templateWindowSize, searchWindowSize));
|
||||
|
||||
OCL_EXPECT_MATS_NEAR(dst, 1);
|
||||
}
|
||||
}
|
||||
|
||||
OCL_INSTANTIATE_TEST_CASE_P(Photo, FastNlMeansDenoising,
|
||||
Combine(Values(1, 2, 3, 4), Values((int)NORM_L2, (int)NORM_L1),
|
||||
Bool(), Values(true)));
|
||||
OCL_INSTANTIATE_TEST_CASE_P(Photo, FastNlMeansDenoising_hsep,
|
||||
Combine(Values(1, 2, 3, 4), Values((int)NORM_L2, (int)NORM_L1),
|
||||
Bool(), Values(true)));
|
||||
OCL_INSTANTIATE_TEST_CASE_P(Photo, FastNlMeansDenoisingColored,
|
||||
Combine(Values(3, 4), Values((int)NORM_L2), Bool(), Values(false)));
|
||||
|
||||
} } // namespace opencv_test::ocl
|
||||
|
||||
#endif // HAVE_OPENCL
|
||||
@@ -0,0 +1,302 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
Mat s = (Mat_<Vec3d>(24, 1) <<
|
||||
Vec3d(214.11, 98.67, 37.97),
|
||||
Vec3d(231.94, 153.1, 85.27),
|
||||
Vec3d(204.08, 143.71, 78.46),
|
||||
Vec3d(190.58, 122.99, 30.84),
|
||||
Vec3d(230.93, 148.46, 100.84),
|
||||
Vec3d(228.64, 206.97, 97.5),
|
||||
Vec3d(229.09, 137.07, 55.29),
|
||||
Vec3d(189.21, 111.22, 92.66),
|
||||
Vec3d(223.5, 96.42, 75.45),
|
||||
Vec3d(201.82, 69.71, 50.9),
|
||||
Vec3d(240.52, 196.47, 59.3),
|
||||
Vec3d(235.73, 172.13, 54.),
|
||||
Vec3d(131.6, 75.04, 68.86),
|
||||
Vec3d(189.04, 170.43, 42.05),
|
||||
Vec3d(222.23, 74., 71.95),
|
||||
Vec3d(241.01, 199.1, 61.15),
|
||||
Vec3d(224.99, 101.4, 100.24),
|
||||
Vec3d(174.58, 152.63, 91.52),
|
||||
Vec3d(248.06, 227.69, 140.5),
|
||||
Vec3d(241.15, 201.38, 115.58),
|
||||
Vec3d(236.49, 175.87, 88.86),
|
||||
Vec3d(212.19, 133.49, 54.79),
|
||||
Vec3d(181.17, 102.94, 36.18),
|
||||
Vec3d(115.1, 53.77, 15.23));
|
||||
|
||||
TEST(Photo_ColorCorrection, test_model)
|
||||
{
|
||||
cv::ccm::ColorCorrectionModel model(s / 255, cv::ccm::COLORCHECKER_MACBETH);
|
||||
Mat colorCorrectionMat = model.compute();
|
||||
Mat srcRgbl = (Mat_<Vec3d>(24, 1) <<
|
||||
Vec3d(0.68078957, 0.12382801, 0.01514889),
|
||||
Vec3d(0.81177942, 0.32550452, 0.089818),
|
||||
Vec3d(0.61259378, 0.2831933, 0.07478902),
|
||||
Vec3d(0.52696493, 0.20105976, 0.00958657),
|
||||
Vec3d(0.80402284, 0.30419523, 0.12989841),
|
||||
Vec3d(0.78658646, 0.63184111, 0.12062068),
|
||||
Vec3d(0.78999637, 0.25520249, 0.03462853),
|
||||
Vec3d(0.51866697, 0.16114393, 0.1078387),
|
||||
Vec3d(0.74820768, 0.11770076, 0.06862177),
|
||||
Vec3d(0.59776825, 0.05765816, 0.02886627),
|
||||
Vec3d(0.8793145, 0.56346033, 0.0403954),
|
||||
Vec3d(0.84124847, 0.42120746, 0.03287592),
|
||||
Vec3d(0.23333214, 0.06780408, 0.05612276),
|
||||
Vec3d(0.5176423, 0.41210976, 0.01896255),
|
||||
Vec3d(0.73888613, 0.06575388, 0.06181293),
|
||||
Vec3d(0.88326036, 0.58018751, 0.04321991),
|
||||
Vec3d(0.75922531, 0.13149072, 0.1282041),
|
||||
Vec3d(0.4345097, 0.32331019, 0.10494139),
|
||||
Vec3d(0.94110142, 0.77941419, 0.26946323),
|
||||
Vec3d(0.88438952, 0.5949049 , 0.17536928),
|
||||
Vec3d(0.84722687, 0.44160449, 0.09834799),
|
||||
Vec3d(0.66743106, 0.24076803, 0.03394333),
|
||||
Vec3d(0.47141286, 0.13592419, 0.01362205),
|
||||
Vec3d(0.17377101, 0.03256864, 0.00203026));
|
||||
EXPECT_MAT_NEAR(srcRgbl, model.getSrcLinearRGB(), 1e-4);
|
||||
|
||||
Mat dstRgbl = (Mat_<Vec3d>(24, 1) <<
|
||||
Vec3d(0.17303173, 0.08211037, 0.05672686),
|
||||
Vec3d(0.56832031, 0.29269488, 0.21835529),
|
||||
Vec3d(0.10365019, 0.19588357, 0.33140475),
|
||||
Vec3d(0.10159676, 0.14892193, 0.05188294),
|
||||
Vec3d(0.22159627, 0.21584476, 0.43461196),
|
||||
Vec3d(0.10806379, 0.51437196, 0.41264213),
|
||||
Vec3d(0.74736423, 0.20062878, 0.02807988),
|
||||
Vec3d(0.05757947, 0.10516793, 0.40296109),
|
||||
Vec3d(0.56676218, 0.08424805, 0.11969461),
|
||||
Vec3d(0.11099515, 0.04230796, 0.14292554),
|
||||
Vec3d(0.34546869, 0.50872001, 0.04944204),
|
||||
Vec3d(0.79461323, 0.35942459, 0.02051968),
|
||||
Vec3d(0.01710416, 0.05022043, 0.29220674),
|
||||
Vec3d(0.05598012, 0.30021149, 0.06871162),
|
||||
Vec3d(0.45585457, 0.03033727, 0.04085654),
|
||||
Vec3d(0.85737614, 0.56757335, 0.0068503),
|
||||
Vec3d(0.53348585, 0.08861148, 0.30750446),
|
||||
Vec3d(-0.0374061, 0.24699498, 0.40041217),
|
||||
Vec3d(0.91262695, 0.91493909, 0.89367049),
|
||||
Vec3d(0.57981916, 0.59200418, 0.59328881),
|
||||
Vec3d(0.35490581, 0.36544831, 0.36755375),
|
||||
Vec3d(0.19007357, 0.19186587, 0.19308397),
|
||||
Vec3d(0.08529188, 0.08887994, 0.09257601),
|
||||
Vec3d(0.0303193, 0.03113818, 0.03274845));
|
||||
EXPECT_MAT_NEAR(dstRgbl, model.getRefLinearRGB(), 1e-4);
|
||||
|
||||
Mat mask = Mat::ones(24, 1, CV_8U);
|
||||
EXPECT_MAT_NEAR(model.getMask(), mask, 0.0);
|
||||
|
||||
Mat refColorMat = (Mat_<double>(3, 3) <<
|
||||
0.37406520, 0.02066507, 0.05804047,
|
||||
0.12719672, 0.77389268, -0.01569404,
|
||||
-0.27627010, 0.00603427, 2.74272981);
|
||||
EXPECT_MAT_NEAR(colorCorrectionMat, refColorMat, 1e-4);
|
||||
}
|
||||
|
||||
TEST(Photo_ColorCorrection, test_model_with_color_patches_mask)
|
||||
{
|
||||
Mat dstData = (Mat_<Vec3d>(24, 1) <<
|
||||
Vec3d(37.986, 13.555, 14.059),
|
||||
Vec3d(65.711, 18.13, 17.81),
|
||||
Vec3d(49.927, -4.88, -21.925),
|
||||
Vec3d(43.139, -13.095, 21.905),
|
||||
Vec3d(55.112, 8.843999999999999, -25.399),
|
||||
Vec3d(70.71899999999999, -33.397, -0.199),
|
||||
Vec3d(62.661, 36.067, 57.096),
|
||||
Vec3d(40.02, 10.41, -45.964),
|
||||
Vec3d(51.124, 48.239, 16.248),
|
||||
Vec3d(30.325, 22.976, -21.587),
|
||||
Vec3d(72.532, -23.709, 57.255),
|
||||
Vec3d(71.941, 19.363, 67.857),
|
||||
Vec3d(28.778, 14.179, -50.297),
|
||||
Vec3d(55.261, -38.342, 31.37),
|
||||
Vec3d(42.101, 53.378, 28.19),
|
||||
Vec3d(81.733, 4.039, 79.819),
|
||||
Vec3d(51.935, 49.986, -14.574),
|
||||
Vec3d(51.038, -28.631, -28.638),
|
||||
Vec3d(96.539, -0.425, 1.186),
|
||||
Vec3d(81.25700000000001, -0.638, -0.335),
|
||||
Vec3d(66.76600000000001, -0.734, -0.504),
|
||||
Vec3d(50.867, -0.153, -0.27),
|
||||
Vec3d(35.656, -0.421, -1.231),
|
||||
Vec3d(20.461, -0.079, -0.973)
|
||||
);
|
||||
|
||||
Mat coloredMask = (Mat_<uchar>(24, 1) <<
|
||||
1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0);
|
||||
|
||||
cv::ccm::ColorCorrectionModel model(s/255, dstData, cv::ccm::COLOR_SPACE_LAB_D50_2, coloredMask);
|
||||
Mat colorCorrectionMat = model.compute();
|
||||
|
||||
Mat refColorMat = (Mat_<double>(3, 3) <<
|
||||
0.37406520, 0.02066507, 0.05804047,
|
||||
0.12719672, 0.77389268, -0.01569404,
|
||||
-0.27627010, 0.00603427, 2.74272981);
|
||||
EXPECT_MAT_NEAR(colorCorrectionMat, refColorMat, 1e-4);
|
||||
}
|
||||
|
||||
TEST(Photo_ColorCorrection, test_masks_weights_1)
|
||||
{
|
||||
Mat weightsList_ = (Mat_<double>(24, 1) <<
|
||||
1.1, 0, 0, 1.2, 0, 0,
|
||||
1.3, 0, 0, 1.4, 0, 0,
|
||||
0.5, 0, 0, 0.6, 0, 0,
|
||||
0.7, 0, 0, 0.8, 0, 0);
|
||||
cv::ccm::ColorCorrectionModel model1(s / 255,cv::ccm::COLORCHECKER_MACBETH);
|
||||
model1.setColorSpace(cv::ccm::COLOR_SPACE_SRGB);
|
||||
model1.setCcmType(cv::ccm::CCM_LINEAR);
|
||||
model1.setDistance(cv::ccm::DISTANCE_CIE2000);
|
||||
model1.setLinearization(cv::ccm::LINEARIZATION_GAMMA);
|
||||
model1.setLinearizationGamma(2.2);
|
||||
model1.setLinearizationDegree(3);
|
||||
model1.setSaturatedThreshold(0, 0.98);
|
||||
model1.setWeightsList(weightsList_);
|
||||
model1.setWeightCoeff(1.5);
|
||||
Mat colorCorrectionMat = model1.compute();
|
||||
Mat weights = (Mat_<double>(8, 1) <<
|
||||
1.15789474, 1.26315789, 1.36842105, 1.47368421,
|
||||
0.52631579, 0.63157895, 0.73684211, 0.84210526);
|
||||
EXPECT_MAT_NEAR(model1.getWeights(), weights, 1e-4);
|
||||
|
||||
Mat mask = (Mat_<uchar>(24, 1) <<
|
||||
true, false, false, true, false, false,
|
||||
true, false, false, true, false, false,
|
||||
true, false, false, true, false, false,
|
||||
true, false, false, true, false, false);
|
||||
EXPECT_MAT_NEAR(model1.getMask(), mask, 0.0);
|
||||
}
|
||||
|
||||
TEST(Photo_ColorCorrection, test_masks_weights_2)
|
||||
{
|
||||
cv::ccm::ColorCorrectionModel model2(s / 255, cv::ccm::COLORCHECKER_MACBETH);
|
||||
model2.setCcmType(cv::ccm::CCM_LINEAR);
|
||||
model2.setDistance(cv::ccm::DISTANCE_CIE2000);
|
||||
model2.setLinearization(cv::ccm::LINEARIZATION_GAMMA);
|
||||
model2.setLinearizationGamma(2.2);
|
||||
model2.setLinearizationDegree(3);
|
||||
model2.setSaturatedThreshold(0.05, 0.93);
|
||||
model2.setWeightsList(Mat());
|
||||
model2.setWeightCoeff(1.5);
|
||||
Mat colorCorrectionMat = model2.compute();
|
||||
Mat weights = (Mat_<double>(20, 1) <<
|
||||
0.65554256, 1.49454705, 1.00499244, 0.79735434, 1.16327759,
|
||||
1.68623868, 1.37973155, 0.73213388, 1.0169629, 0.47430246,
|
||||
1.70312161, 0.45414218, 1.15910007, 0.7540434, 1.05049802,
|
||||
1.04551645, 1.54082353, 1.02453421, 0.6015915, 0.26154558);
|
||||
EXPECT_MAT_NEAR(model2.getWeights(), weights, 1e-4);
|
||||
|
||||
Mat mask = (Mat_<uchar>(24, 1) <<
|
||||
true, true, true, true, true, true,
|
||||
true, true, true, true, false, true,
|
||||
true, true, true, false, true, true,
|
||||
false, false, true, true, true, true);
|
||||
EXPECT_MAT_NEAR(model2.getMask(), mask, 0.0);
|
||||
}
|
||||
|
||||
TEST(Photo_ColorCorrection, compute_color_correction_matrix)
|
||||
{
|
||||
// read gold chartsRGB
|
||||
string path = cvtest::findDataFile("mcc/mcc_ccm_test.yml");
|
||||
FileStorage fs(path, FileStorage::READ);
|
||||
Mat chartsRGB;
|
||||
FileNode node = fs["chartsRGB"];
|
||||
node >> chartsRGB;
|
||||
ASSERT_FALSE(chartsRGB.empty()) << "chartsRGB is empty after loading from: " << path;
|
||||
|
||||
// compute CCM
|
||||
cv::ccm::ColorCorrectionModel model(chartsRGB.col(1).clone().reshape(3, chartsRGB.rows/3) / 255., cv::ccm::COLORCHECKER_MACBETH);
|
||||
Mat colorCorrectionMat = model.compute();
|
||||
|
||||
// read gold CCM
|
||||
node = fs["ccm"];
|
||||
ASSERT_FALSE(node.empty());
|
||||
Mat gold_ccm;
|
||||
node >> gold_ccm;
|
||||
fs.release();
|
||||
|
||||
// check CCM
|
||||
EXPECT_MAT_NEAR(gold_ccm, colorCorrectionMat, 1e-8);
|
||||
|
||||
const double gold_loss = 4.6386569120323129;
|
||||
// check loss
|
||||
const double loss = model.getLoss();
|
||||
EXPECT_NEAR(gold_loss, loss, 1e-8);
|
||||
}
|
||||
|
||||
TEST(Photo_ColorCorrection, correct_image)
|
||||
{
|
||||
string path = cvtest::findDataFile("mcc/mcc_ccm_test.jpg");
|
||||
Mat img = imread(path, IMREAD_COLOR);
|
||||
// read gold calibrate img
|
||||
path = cvtest::findDataFile("mcc/mcc_ccm_test_res.png");
|
||||
Mat gold_img = imread(path);
|
||||
|
||||
// read gold chartsRGB
|
||||
path = cvtest::findDataFile("mcc/mcc_ccm_test.yml");
|
||||
FileStorage fs(path, FileStorage::READ);
|
||||
Mat chartsRGB;
|
||||
FileNode node = fs["chartsRGB"];
|
||||
node >> chartsRGB;
|
||||
fs.release();
|
||||
ASSERT_FALSE(chartsRGB.empty()) << "chartsRGB is empty after loading from: " << path;
|
||||
|
||||
// compute CCM
|
||||
cv::ccm::ColorCorrectionModel model(chartsRGB.col(1).clone().reshape(3, chartsRGB.rows/3) / 255., cv::ccm::COLORCHECKER_MACBETH);
|
||||
Mat colorCorrectionMat = model.compute();
|
||||
|
||||
// compute calibrate image
|
||||
Mat calibratedImage;
|
||||
model.correctImage(img, calibratedImage);
|
||||
// check calibrated image
|
||||
EXPECT_MAT_NEAR(gold_img, calibratedImage, 0.1);
|
||||
}
|
||||
|
||||
TEST(Photo_ColorCorrection, serialization)
|
||||
{
|
||||
auto path = cvtest::findDataFile("mcc/mcc_ccm_test.yml");
|
||||
FileStorage fs(path, FileStorage::READ);
|
||||
Mat chartsRGB;
|
||||
FileNode node = fs["chartsRGB"];
|
||||
node >> chartsRGB;
|
||||
fs.release();
|
||||
ASSERT_FALSE(chartsRGB.empty()) << "chartsRGB is empty after loading from: " << path;
|
||||
|
||||
// compute CCM
|
||||
cv::ccm::ColorCorrectionModel model(chartsRGB.col(1).clone().reshape(3, chartsRGB.rows/3) / 255., cv::ccm::COLORCHECKER_MACBETH);
|
||||
Mat colorCorrectionMat = model.compute();
|
||||
|
||||
//--- 1. write model to memory -------------------------------------------
|
||||
FileStorage fs1("", FileStorage::WRITE | FileStorage::MEMORY);
|
||||
model.write(fs1);
|
||||
std::string yaml1 = fs1.releaseAndGetString();
|
||||
|
||||
//--- 2. read model back from memory -------------------------------------
|
||||
cv::ccm::ColorCorrectionModel model1;
|
||||
FileStorage fs2(yaml1, FileStorage::READ | FileStorage::MEMORY);
|
||||
model1.read(fs2["ColorCorrectionModel"]);
|
||||
fs2.release();
|
||||
|
||||
//--- 3. write the re-loaded model again to memory -----------------------
|
||||
FileStorage fs3("", FileStorage::WRITE | FileStorage::MEMORY);
|
||||
model1.write(fs3);
|
||||
std::string yaml2 = fs3.releaseAndGetString();
|
||||
|
||||
//--- 4. compare the two YAML strings ------------------------------------
|
||||
EXPECT_EQ(yaml1, yaml2);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opencv_test
|
||||
@@ -0,0 +1,144 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
const unsigned long EXPECTED_COEFFS_SIZE = 78;
|
||||
|
||||
class ChromaticAberrationTest : public testing::Test
|
||||
{
|
||||
protected:
|
||||
std::string test_yaml_file;
|
||||
cv::Mat test_image;
|
||||
cv::Mat coeffMat;
|
||||
cv::Mat corrected;
|
||||
int degree = -1;
|
||||
Size calib_size = {-1, -1};
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
string data_path = cvtest::TS::ptr()->get_data_path();
|
||||
ASSERT_TRUE(!data_path.empty()) << "OPENCV_TEST_DATA_PATH not set";
|
||||
test_yaml_file = std::string(data_path) + "cameracalibration/chromatic_aberration/ca_photo_calib.yaml";
|
||||
test_image = cv::imread(std::string(data_path) + "cameracalibration/chromatic_aberration/ca_photo.png");
|
||||
ASSERT_FALSE(test_image.empty()) << "Failed to load test image";
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ChromaticAberrationTest, LoadCalibAndCorrectImage)
|
||||
{
|
||||
FileStorage fs(test_yaml_file, FileStorage::READ);
|
||||
ASSERT_TRUE(fs.isOpened());
|
||||
ASSERT_NO_THROW(cv::loadChromaticAberrationParams(fs.root(), coeffMat, calib_size, degree));
|
||||
|
||||
ASSERT_FALSE(coeffMat.empty());
|
||||
ASSERT_EQ(coeffMat.type(), CV_32F);
|
||||
ASSERT_EQ(coeffMat.rows, 4);
|
||||
ASSERT_GT(coeffMat.cols, 0);
|
||||
ASSERT_EQ((degree + 1) * (degree + 2) / 2, coeffMat.cols);
|
||||
ASSERT_GT(calib_size.width, 0);
|
||||
ASSERT_GT(calib_size.height, 0);
|
||||
|
||||
ASSERT_EQ(test_image.cols, calib_size.width);
|
||||
ASSERT_EQ(test_image.rows, calib_size.height);
|
||||
|
||||
ASSERT_NO_THROW(cv::correctChromaticAberration(test_image, coeffMat, corrected, calib_size, degree));
|
||||
|
||||
EXPECT_EQ(corrected.size(), test_image.size());
|
||||
EXPECT_EQ(corrected.channels(), test_image.channels());
|
||||
EXPECT_EQ(corrected.type(), test_image.type());
|
||||
|
||||
cv::Mat diff; cv::absdiff(test_image, corrected, diff);
|
||||
cv::Scalar s = cv::sum(diff);
|
||||
EXPECT_GT(s[0] + s[1] + s[2], 0.0);
|
||||
}
|
||||
|
||||
TEST_F(ChromaticAberrationTest, YAMLContentsAsExpected)
|
||||
{
|
||||
cv::FileStorage fs(test_yaml_file, cv::FileStorage::READ);
|
||||
ASSERT_TRUE(fs.isOpened());
|
||||
|
||||
cv::FileNode red_node = fs["red_channel"];
|
||||
cv::FileNode blue_node = fs["blue_channel"];
|
||||
EXPECT_TRUE(red_node.isMap());
|
||||
EXPECT_TRUE(blue_node.isMap());
|
||||
|
||||
std::vector<double> coeffs_x;
|
||||
red_node["coeffs_x"] >> coeffs_x;
|
||||
EXPECT_EQ(coeffs_x.size(), EXPECTED_COEFFS_SIZE);
|
||||
blue_node["coeffs_x"] >> coeffs_x;
|
||||
EXPECT_EQ(coeffs_x.size(), EXPECTED_COEFFS_SIZE);
|
||||
|
||||
std::vector<double> coeffs_y;
|
||||
red_node["coeffs_y"] >> coeffs_y;
|
||||
EXPECT_EQ(coeffs_y.size(), EXPECTED_COEFFS_SIZE);
|
||||
blue_node["coeffs_y"] >> coeffs_y;
|
||||
EXPECT_EQ(coeffs_y.size(), EXPECTED_COEFFS_SIZE);
|
||||
|
||||
fs.release();
|
||||
}
|
||||
|
||||
TEST_F(ChromaticAberrationTest, InvalidSingleChannel)
|
||||
{
|
||||
FileStorage fs(test_yaml_file, FileStorage::READ);
|
||||
ASSERT_TRUE(fs.isOpened());
|
||||
ASSERT_NO_THROW(cv::loadChromaticAberrationParams(fs.root(), coeffMat, calib_size, degree));
|
||||
|
||||
cv::Mat gray;
|
||||
cv::cvtColor(test_image, gray, cv::COLOR_BGR2GRAY);
|
||||
|
||||
EXPECT_THROW(cv::correctChromaticAberration(gray, coeffMat, corrected, calib_size, degree),
|
||||
cv::Exception);
|
||||
}
|
||||
|
||||
TEST_F(ChromaticAberrationTest, EmptyCoeffMat)
|
||||
{
|
||||
FileStorage fs(test_yaml_file, FileStorage::READ);
|
||||
ASSERT_TRUE(fs.isOpened());
|
||||
ASSERT_NO_THROW(cv::loadChromaticAberrationParams(fs.root(), coeffMat, calib_size, degree));
|
||||
|
||||
cv::Mat emptyCoeff;
|
||||
EXPECT_THROW(cv::correctChromaticAberration(test_image, emptyCoeff, corrected, calib_size, degree),
|
||||
cv::Exception);
|
||||
}
|
||||
|
||||
TEST_F(ChromaticAberrationTest, MismatchedImageSize)
|
||||
{
|
||||
FileStorage fs(test_yaml_file, FileStorage::READ);
|
||||
ASSERT_TRUE(fs.isOpened());
|
||||
ASSERT_NO_THROW(cv::loadChromaticAberrationParams(fs.root(), coeffMat, calib_size, degree));
|
||||
|
||||
cv::Mat resized;
|
||||
cv::resize(test_image, resized, cv::Size(test_image.cols/2, test_image.rows/2));
|
||||
EXPECT_THROW(cv::correctChromaticAberration(resized, coeffMat, corrected, calib_size, degree),
|
||||
cv::Exception);
|
||||
}
|
||||
|
||||
TEST_F(ChromaticAberrationTest, WrongCoeffType)
|
||||
{
|
||||
FileStorage fs(test_yaml_file, FileStorage::READ);
|
||||
ASSERT_TRUE(fs.isOpened());
|
||||
ASSERT_NO_THROW(cv::loadChromaticAberrationParams(fs.root(), coeffMat, calib_size, degree));
|
||||
|
||||
cv::Mat wrongType;
|
||||
coeffMat.convertTo(wrongType, CV_64F);
|
||||
EXPECT_THROW(cv::correctChromaticAberration(test_image, wrongType, corrected, calib_size, degree),
|
||||
cv::Exception);
|
||||
}
|
||||
|
||||
TEST_F(ChromaticAberrationTest, DegreeDoesNotMatchCoeffCols)
|
||||
{
|
||||
FileStorage fs(test_yaml_file, FileStorage::READ);
|
||||
ASSERT_TRUE(fs.isOpened());
|
||||
ASSERT_NO_THROW(cv::loadChromaticAberrationParams(fs.root(), coeffMat, calib_size, degree));
|
||||
|
||||
int wrongDegree = std::max(1, degree - 1);
|
||||
ASSERT_NE((wrongDegree + 1) * (wrongDegree + 2) / 2, coeffMat.cols);
|
||||
EXPECT_THROW(cv::correctChromaticAberration(test_image, coeffMat, corrected, calib_size, wrongDegree),
|
||||
cv::Exception);
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,247 @@
|
||||
/*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"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
#define OUTPUT_SAVING 0
|
||||
#if OUTPUT_SAVING
|
||||
#define SAVE(x) std::vector<int> params;\
|
||||
params.push_back(16);\
|
||||
params.push_back(0);\
|
||||
imwrite(folder + "output.png", x ,params);
|
||||
#else
|
||||
#define SAVE(x)
|
||||
#endif
|
||||
|
||||
static const double numerical_precision = 0.05; // 95% of pixels should have exact values
|
||||
|
||||
TEST(Photo_SeamlessClone_normal, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "cloning/Normal_Cloning/";
|
||||
string original_path1 = folder + "source1.png";
|
||||
string original_path2 = folder + "destination1.png";
|
||||
string original_path3 = folder + "mask.png";
|
||||
string reference_path = folder + "reference.png";
|
||||
|
||||
Mat source = imread(original_path1, IMREAD_COLOR);
|
||||
Mat destination = imread(original_path2, IMREAD_COLOR);
|
||||
Mat mask = imread(original_path3, IMREAD_COLOR);
|
||||
|
||||
ASSERT_FALSE(source.empty()) << "Could not load source image " << original_path1;
|
||||
ASSERT_FALSE(destination.empty()) << "Could not load destination image " << original_path2;
|
||||
ASSERT_FALSE(mask.empty()) << "Could not load mask image " << original_path3;
|
||||
|
||||
Mat result;
|
||||
Point p;
|
||||
p.x = destination.size().width/2;
|
||||
p.y = destination.size().height/2;
|
||||
seamlessClone(source, destination, mask, p, result, NORMAL_CLONE);
|
||||
|
||||
Mat reference = imread(reference_path);
|
||||
ASSERT_FALSE(reference.empty()) << "Could not load reference image " << reference_path;
|
||||
|
||||
SAVE(result);
|
||||
|
||||
double errorINF = cvtest::norm(reference, result, NORM_INF);
|
||||
EXPECT_LE(errorINF, 1);
|
||||
double errorL1 = cvtest::norm(reference, result, NORM_L1);
|
||||
EXPECT_LE(errorL1, reference.total() * numerical_precision) << "size=" << reference.size();
|
||||
|
||||
mask = Scalar(0, 0, 0);
|
||||
seamlessClone(source, destination, mask, p, result, NORMAL_CLONE);
|
||||
|
||||
reference = destination;
|
||||
errorINF = cvtest::norm(reference, result, NORM_INF);
|
||||
EXPECT_LE(errorINF, 1);
|
||||
errorL1 = cvtest::norm(reference, result, NORM_L1);
|
||||
EXPECT_LE(errorL1, reference.total() * numerical_precision) << "size=" << reference.size();
|
||||
}
|
||||
|
||||
TEST(Photo_SeamlessClone_mixed, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "cloning/Mixed_Cloning/";
|
||||
string original_path1 = folder + "source1.png";
|
||||
string original_path2 = folder + "destination1.png";
|
||||
string original_path3 = folder + "mask.png";
|
||||
string reference_path = folder + "reference.png";
|
||||
|
||||
Mat source = imread(original_path1, IMREAD_COLOR);
|
||||
Mat destination = imread(original_path2, IMREAD_COLOR);
|
||||
Mat mask = imread(original_path3, IMREAD_COLOR);
|
||||
|
||||
ASSERT_FALSE(source.empty()) << "Could not load source image " << original_path1;
|
||||
ASSERT_FALSE(destination.empty()) << "Could not load destination image " << original_path2;
|
||||
ASSERT_FALSE(mask.empty()) << "Could not load mask image " << original_path3;
|
||||
|
||||
Mat result;
|
||||
Point p;
|
||||
p.x = destination.size().width/2;
|
||||
p.y = destination.size().height/2;
|
||||
seamlessClone(source, destination, mask, p, result, MIXED_CLONE);
|
||||
|
||||
SAVE(result);
|
||||
|
||||
Mat reference = imread(reference_path);
|
||||
ASSERT_FALSE(reference.empty()) << "Could not load reference image " << reference_path;
|
||||
|
||||
double errorINF = cvtest::norm(reference, result, NORM_INF);
|
||||
EXPECT_LE(errorINF, 1);
|
||||
double errorL1 = cvtest::norm(reference, result, NORM_L1);
|
||||
EXPECT_LE(errorL1, reference.total() * numerical_precision) << "size=" << reference.size();
|
||||
}
|
||||
|
||||
TEST(Photo_SeamlessClone_featureExchange, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "cloning/Monochrome_Transfer/";
|
||||
string original_path1 = folder + "source1.png";
|
||||
string original_path2 = folder + "destination1.png";
|
||||
string original_path3 = folder + "mask.png";
|
||||
string reference_path = folder + "reference.png";
|
||||
|
||||
Mat source = imread(original_path1, IMREAD_COLOR);
|
||||
Mat destination = imread(original_path2, IMREAD_COLOR);
|
||||
Mat mask = imread(original_path3, IMREAD_COLOR);
|
||||
|
||||
ASSERT_FALSE(source.empty()) << "Could not load source image " << original_path1;
|
||||
ASSERT_FALSE(destination.empty()) << "Could not load destination image " << original_path2;
|
||||
ASSERT_FALSE(mask.empty()) << "Could not load mask image " << original_path3;
|
||||
|
||||
Mat result;
|
||||
Point p;
|
||||
p.x = destination.size().width/2;
|
||||
p.y = destination.size().height/2;
|
||||
seamlessClone(source, destination, mask, p, result, MONOCHROME_TRANSFER);
|
||||
|
||||
SAVE(result);
|
||||
|
||||
Mat reference = imread(reference_path);
|
||||
ASSERT_FALSE(reference.empty()) << "Could not load reference image " << reference_path;
|
||||
|
||||
double errorINF = cvtest::norm(reference, result, NORM_INF);
|
||||
EXPECT_LE(errorINF, 1);
|
||||
double errorL1 = cvtest::norm(reference, result, NORM_L1);
|
||||
EXPECT_LE(errorL1, reference.total() * numerical_precision) << "size=" << reference.size();
|
||||
}
|
||||
|
||||
TEST(Photo_SeamlessClone_colorChange, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "cloning/color_change/";
|
||||
string original_path1 = folder + "source1.png";
|
||||
string original_path2 = folder + "mask.png";
|
||||
string reference_path = folder + "reference.png";
|
||||
|
||||
Mat source = imread(original_path1, IMREAD_COLOR);
|
||||
Mat mask = imread(original_path2, IMREAD_COLOR);
|
||||
|
||||
ASSERT_FALSE(source.empty()) << "Could not load source image " << original_path1;
|
||||
ASSERT_FALSE(mask.empty()) << "Could not load mask image " << original_path2;
|
||||
|
||||
Mat result;
|
||||
colorChange(source, mask, result, 1.5, .5, .5);
|
||||
|
||||
SAVE(result);
|
||||
|
||||
Mat reference = imread(reference_path);
|
||||
ASSERT_FALSE(reference.empty()) << "Could not load reference image " << reference_path;
|
||||
|
||||
double errorINF = cvtest::norm(reference, result, NORM_INF);
|
||||
EXPECT_LE(errorINF, 1);
|
||||
double errorL1 = cvtest::norm(reference, result, NORM_L1);
|
||||
EXPECT_LE(errorL1, reference.total() * numerical_precision) << "size=" << reference.size();
|
||||
}
|
||||
|
||||
TEST(Photo_SeamlessClone_illuminationChange, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "cloning/Illumination_Change/";
|
||||
string original_path1 = folder + "source1.png";
|
||||
string original_path2 = folder + "mask.png";
|
||||
string reference_path = folder + "reference.png";
|
||||
|
||||
Mat source = imread(original_path1, IMREAD_COLOR);
|
||||
Mat mask = imread(original_path2, IMREAD_COLOR);
|
||||
|
||||
ASSERT_FALSE(source.empty()) << "Could not load source image " << original_path1;
|
||||
ASSERT_FALSE(mask.empty()) << "Could not load mask image " << original_path2;
|
||||
|
||||
Mat result;
|
||||
illuminationChange(source, mask, result, 0.2f, 0.4f);
|
||||
|
||||
SAVE(result);
|
||||
|
||||
Mat reference = imread(reference_path);
|
||||
ASSERT_FALSE(reference.empty()) << "Could not load reference image " << reference_path;
|
||||
|
||||
double errorINF = cvtest::norm(reference, result, NORM_INF);
|
||||
EXPECT_LE(errorINF, 1);
|
||||
double errorL1 = cvtest::norm(reference, result, NORM_L1);
|
||||
EXPECT_LE(errorL1, reference.total() * numerical_precision) << "size=" << reference.size();
|
||||
}
|
||||
|
||||
TEST(Photo_SeamlessClone_textureFlattening, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "cloning/Texture_Flattening/";
|
||||
string original_path1 = folder + "source1.png";
|
||||
string original_path2 = folder + "mask.png";
|
||||
string reference_path = folder + "reference.png";
|
||||
|
||||
Mat source = imread(original_path1, IMREAD_COLOR);
|
||||
Mat mask = imread(original_path2, IMREAD_COLOR);
|
||||
|
||||
ASSERT_FALSE(source.empty()) << "Could not load source image " << original_path1;
|
||||
ASSERT_FALSE(mask.empty()) << "Could not load mask image " << original_path2;
|
||||
|
||||
Mat result;
|
||||
textureFlattening(source, mask, result, 30, 45, 3);
|
||||
|
||||
SAVE(result);
|
||||
|
||||
Mat reference = imread(reference_path);
|
||||
ASSERT_FALSE(reference.empty()) << "Could not load reference image " << reference_path;
|
||||
|
||||
double errorINF = cvtest::norm(reference, result, NORM_INF);
|
||||
EXPECT_LE(errorINF, 1);
|
||||
double errorL1 = cvtest::norm(reference, result, NORM_L1);
|
||||
EXPECT_LE(errorL1, reference.total() * numerical_precision) << "size=" << reference.size();
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,68 @@
|
||||
/*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"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
TEST(Photo_Decolor, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "decolor/";
|
||||
string original_path = folder + "color_image_1.png";
|
||||
|
||||
Mat original = imread(original_path, IMREAD_COLOR);
|
||||
|
||||
ASSERT_FALSE(original.empty()) << "Could not load input image " << original_path;
|
||||
ASSERT_EQ(3, original.channels()) << "Load color input image " << original_path;
|
||||
|
||||
Mat grayscale, color_boost;
|
||||
decolor(original, grayscale, color_boost);
|
||||
|
||||
Mat reference_grayscale = imread(folder + "grayscale_reference.png", 0 /* == grayscale image*/);
|
||||
double gray_psnr = cvtest::PSNR(reference_grayscale, grayscale);
|
||||
EXPECT_GT(gray_psnr, 60.0);
|
||||
|
||||
Mat reference_boost = imread(folder + "boost_reference.png");
|
||||
double boost_psnr = cvtest::PSNR(reference_boost, color_boost);
|
||||
EXPECT_GT(boost_psnr, 60.0);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,129 @@
|
||||
/*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 OpenCV Foundation 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"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
void make_noisy(const cv::Mat& img, cv::Mat& noisy, double sigma, double pepper_salt_ratio,cv::RNG& rng)
|
||||
{
|
||||
noisy.create(img.size(), img.type());
|
||||
cv::Mat noise(img.size(), img.type()), mask(img.size(), CV_8U);
|
||||
rng.fill(noise,cv::RNG::NORMAL,128.0,sigma);
|
||||
cv::addWeighted(img, 1, noise, 1, -128, noisy);
|
||||
cv::randn(noise, cv::Scalar::all(0), cv::Scalar::all(2));
|
||||
noise *= 255;
|
||||
cv::randu(mask, 0, cvRound(1./pepper_salt_ratio));
|
||||
cv::Mat half = mask.colRange(0, img.cols/2);
|
||||
half = cv::Scalar::all(1);
|
||||
noise.setTo(128, mask);
|
||||
cv::addWeighted(noisy, 1, noise, 1, -128, noisy);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void make_spotty(cv::Mat& img,cv::RNG& rng, int r=3,int n=1000)
|
||||
{
|
||||
for(int i=0;i<n;i++)
|
||||
{
|
||||
int x=rng(img.cols-r),y=rng(img.rows-r);
|
||||
if(rng(2)==0)
|
||||
img(cv::Range(y,y+r),cv::Range(x,x+r))=(uchar)0;
|
||||
else
|
||||
img(cv::Range(y,y+r),cv::Range(x,x+r))=(uchar)255;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool validate_pixel(const cv::Mat& image,int x,int y,uchar val)
|
||||
{
|
||||
bool ok = std::abs(image.at<uchar>(x,y) - val) < 10;
|
||||
printf("test: image(%d,%d)=%d vs %d - %s\n",x,y,(int)image.at<uchar>(x,y),val,ok?"ok":"bad");
|
||||
return ok;
|
||||
}
|
||||
|
||||
TEST(Optim_denoise_tvl1, regression_basic)
|
||||
{
|
||||
cv::RNG rng(42);
|
||||
cv::Mat img = cv::imread(cvtest::TS::ptr()->get_data_path() + "shared/lena.png", 0), noisy, res;
|
||||
|
||||
ASSERT_FALSE(img.empty()) << "Error: can't open 'lena.png'";
|
||||
|
||||
const int obs_num=5;
|
||||
std::vector<cv::Mat> images(obs_num, cv::Mat());
|
||||
for(int i=0;i<(int)images.size();i++)
|
||||
{
|
||||
make_noisy(img,images[i], 20, 0.02,rng);
|
||||
//make_spotty(images[i],rng);
|
||||
}
|
||||
|
||||
//cv::imshow("test", images[0]);
|
||||
cv::denoise_TVL1(images, res);
|
||||
//cv::imshow("denoised", res);
|
||||
//cv::waitKey();
|
||||
|
||||
#if 0
|
||||
ASSERT_TRUE(validate_pixel(res,248,334,179));
|
||||
ASSERT_TRUE(validate_pixel(res,489,333,172));
|
||||
ASSERT_TRUE(validate_pixel(res,425,507,104));
|
||||
ASSERT_TRUE(validate_pixel(res,489,486,105));
|
||||
ASSERT_TRUE(validate_pixel(res,223,208,64));
|
||||
ASSERT_TRUE(validate_pixel(res,418,3,78));
|
||||
ASSERT_TRUE(validate_pixel(res,63,76,97));
|
||||
ASSERT_TRUE(validate_pixel(res,29,134,126));
|
||||
ASSERT_TRUE(validate_pixel(res,219,291,174));
|
||||
ASSERT_TRUE(validate_pixel(res,384,124,76));
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
ASSERT_TRUE(validate_pixel(res,248,334,194));
|
||||
ASSERT_TRUE(validate_pixel(res,489,333,171));
|
||||
ASSERT_TRUE(validate_pixel(res,425,507,103));
|
||||
ASSERT_TRUE(validate_pixel(res,489,486,109));
|
||||
ASSERT_TRUE(validate_pixel(res,223,208,72));
|
||||
ASSERT_TRUE(validate_pixel(res,418,3,58));
|
||||
ASSERT_TRUE(validate_pixel(res,63,76,93));
|
||||
ASSERT_TRUE(validate_pixel(res,29,134,127));
|
||||
ASSERT_TRUE(validate_pixel(res,219,291,180));
|
||||
ASSERT_TRUE(validate_pixel(res,384,124,80));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,197 @@
|
||||
/*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 "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
//#define DUMP_RESULTS
|
||||
|
||||
#ifdef DUMP_RESULTS
|
||||
# define DUMP(image, path) imwrite(path, image)
|
||||
#else
|
||||
# define DUMP(image, path)
|
||||
#endif
|
||||
|
||||
|
||||
TEST(Photo_DenoisingGrayscale, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "denoising/";
|
||||
string original_path = folder + "lena_noised_gaussian_sigma=10.png";
|
||||
string expected_path = folder + "lena_noised_denoised_grayscale_tw=7_sw=21_h=10.png";
|
||||
|
||||
Mat original = imread(original_path, IMREAD_GRAYSCALE);
|
||||
Mat expected = imread(expected_path, IMREAD_GRAYSCALE);
|
||||
|
||||
ASSERT_FALSE(original.empty()) << "Could not load input image " << original_path;
|
||||
ASSERT_FALSE(expected.empty()) << "Could not load reference image " << expected_path;
|
||||
|
||||
Mat result;
|
||||
fastNlMeansDenoising(original, result, 10);
|
||||
|
||||
DUMP(result, expected_path + ".res.png");
|
||||
|
||||
ASSERT_EQ(0, cvtest::norm(result, expected, NORM_L2));
|
||||
}
|
||||
|
||||
TEST(Photo_DenoisingColored, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "denoising/";
|
||||
string original_path = folder + "lena_noised_gaussian_sigma=10.png";
|
||||
string expected_path = folder + "lena_noised_denoised_lab12_tw=7_sw=21_h=10_h2=10.png";
|
||||
|
||||
Mat original = imread(original_path, IMREAD_COLOR);
|
||||
Mat expected = imread(expected_path, IMREAD_COLOR);
|
||||
|
||||
ASSERT_FALSE(original.empty()) << "Could not load input image " << original_path;
|
||||
ASSERT_FALSE(expected.empty()) << "Could not load reference image " << expected_path;
|
||||
|
||||
Mat result;
|
||||
fastNlMeansDenoisingColored(original, result, 10, 10);
|
||||
|
||||
DUMP(result, expected_path + ".res.png");
|
||||
|
||||
ASSERT_EQ(0, cvtest::norm(result, expected, NORM_L2));
|
||||
}
|
||||
|
||||
TEST(Photo_DenoisingGrayscaleMulti, regression)
|
||||
{
|
||||
const int imgs_count = 3;
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "denoising/";
|
||||
|
||||
string expected_path = folder + "lena_noised_denoised_multi_tw=7_sw=21_h=15.png";
|
||||
Mat expected = imread(expected_path, IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(expected.empty()) << "Could not load reference image " << expected_path;
|
||||
|
||||
vector<Mat> original(imgs_count);
|
||||
for (int i = 0; i < imgs_count; i++)
|
||||
{
|
||||
string original_path = format("%slena_noised_gaussian_sigma=20_multi_%d.png", folder.c_str(), i);
|
||||
original[i] = imread(original_path, IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(original[i].empty()) << "Could not load input image " << original_path;
|
||||
}
|
||||
|
||||
Mat result;
|
||||
fastNlMeansDenoisingMulti(original, result, imgs_count / 2, imgs_count, 15);
|
||||
|
||||
DUMP(result, expected_path + ".res.png");
|
||||
|
||||
ASSERT_EQ(0, cvtest::norm(result, expected, NORM_L2));
|
||||
}
|
||||
|
||||
TEST(Photo_DenoisingColoredMulti, regression)
|
||||
{
|
||||
const int imgs_count = 3;
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "denoising/";
|
||||
|
||||
string expected_path = folder + "lena_noised_denoised_multi_lab12_tw=7_sw=21_h=10_h2=15.png";
|
||||
Mat expected = imread(expected_path, IMREAD_COLOR);
|
||||
ASSERT_FALSE(expected.empty()) << "Could not load reference image " << expected_path;
|
||||
|
||||
vector<Mat> original(imgs_count);
|
||||
for (int i = 0; i < imgs_count; i++)
|
||||
{
|
||||
string original_path = format("%slena_noised_gaussian_sigma=20_multi_%d.png", folder.c_str(), i);
|
||||
original[i] = imread(original_path, IMREAD_COLOR);
|
||||
ASSERT_FALSE(original[i].empty()) << "Could not load input image " << original_path;
|
||||
}
|
||||
|
||||
Mat result;
|
||||
fastNlMeansDenoisingColoredMulti(original, result, imgs_count / 2, imgs_count, 10, 15);
|
||||
|
||||
DUMP(result, expected_path + ".res.png");
|
||||
|
||||
ASSERT_EQ(0, cvtest::norm(result, expected, NORM_L2));
|
||||
}
|
||||
|
||||
TEST(Photo_White, issue_2646)
|
||||
{
|
||||
cv::Mat img(50, 50, CV_8UC1, cv::Scalar::all(255));
|
||||
cv::Mat filtered;
|
||||
cv::fastNlMeansDenoising(img, filtered);
|
||||
|
||||
int nonWhitePixelsCount = (int)img.total() - cv::countNonZero(filtered == img);
|
||||
|
||||
ASSERT_EQ(0, nonWhitePixelsCount);
|
||||
}
|
||||
|
||||
TEST(Photo_Denoising, speed)
|
||||
{
|
||||
string imgname = string(cvtest::TS::ptr()->get_data_path()) + "shared/5MP.png";
|
||||
Mat src = imread(imgname, IMREAD_GRAYSCALE), dst;
|
||||
|
||||
double t = (double)getTickCount();
|
||||
fastNlMeansDenoising(src, dst, 5, 7, 21);
|
||||
t = (double)getTickCount() - t;
|
||||
printf("execution time: %gms\n", t*1000./getTickFrequency());
|
||||
}
|
||||
|
||||
// Related issue : https://github.com/opencv/opencv/issues/26582
|
||||
TEST(Photo_DenoisingGrayscaleMulti16bitL1, regression)
|
||||
{
|
||||
const int imgs_count = 3;
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "denoising/";
|
||||
|
||||
vector<Mat> original_8u(imgs_count);
|
||||
vector<Mat> original_16u(imgs_count);
|
||||
for (int i = 0; i < imgs_count; i++)
|
||||
{
|
||||
string original_path = format("%slena_noised_gaussian_sigma=20_multi_%d.png", folder.c_str(), i);
|
||||
original_8u[i] = imread(original_path, IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(original_8u[i].empty()) << "Could not load input image " << original_path;
|
||||
original_8u[i].convertTo(original_16u[i], CV_16U);
|
||||
}
|
||||
|
||||
Mat result_8u, result_16u;
|
||||
std::vector<float> h = {15};
|
||||
fastNlMeansDenoisingMulti(original_8u, result_8u, /*imgToDenoiseIndex*/ imgs_count / 2, /*temporalWindowSize*/ imgs_count, h, 7, 21, NORM_L1);
|
||||
fastNlMeansDenoisingMulti(original_16u, result_16u, /*imgToDenoiseIndex*/ imgs_count / 2, /*temporalWindowSize*/ imgs_count, h, 7, 21, NORM_L1);
|
||||
DUMP(result_8u, "8u.res.png");
|
||||
DUMP(result_16u, "16u.res.png");
|
||||
|
||||
cv::Mat expected;
|
||||
result_8u.convertTo(expected, CV_16U);
|
||||
|
||||
EXPECT_MAT_NEAR(result_16u, expected, 1);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,120 @@
|
||||
/*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 "test_precomp.hpp"
|
||||
|
||||
#include "opencv2/photo/cuda.hpp"
|
||||
#include "opencv2/ts/cuda_test.hpp"
|
||||
|
||||
#include "opencv2/opencv_modules.hpp"
|
||||
#include "cvconfig.h"
|
||||
|
||||
#if defined (HAVE_CUDA) && defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAIMGPROC)
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Brute Force Non local means
|
||||
|
||||
TEST(CUDA_BruteForceNonLocalMeans, Regression)
|
||||
{
|
||||
using cv::cuda::GpuMat;
|
||||
|
||||
cv::Mat bgr = readImage("../gpu/denoising/lena_noised_gaussian_sigma=20_multi_0.png", cv::IMREAD_COLOR);
|
||||
ASSERT_FALSE(bgr.empty());
|
||||
cv::resize(bgr, bgr, cv::Size(256, 256));
|
||||
|
||||
cv::Mat gray;
|
||||
cv::cvtColor(bgr, gray, cv::COLOR_BGR2GRAY);
|
||||
|
||||
GpuMat dbgr, dgray;
|
||||
cv::cuda::nonLocalMeans(GpuMat(bgr), dbgr, 20);
|
||||
cv::cuda::nonLocalMeans(GpuMat(gray), dgray, 20);
|
||||
|
||||
#if 0
|
||||
dumpImage("../gpu/denoising/nlm_denoised_lena_bgr.png", cv::Mat(dbgr));
|
||||
dumpImage("../gpu/denoising/nlm_denoised_lena_gray.png", cv::Mat(dgray));
|
||||
#endif
|
||||
|
||||
cv::Mat bgr_gold = readImage("../gpu/denoising/nlm_denoised_lena_bgr.png", cv::IMREAD_COLOR);
|
||||
cv::Mat gray_gold = readImage("../gpu/denoising/nlm_denoised_lena_gray.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(bgr_gold.empty() || gray_gold.empty());
|
||||
cv::resize(bgr_gold, bgr_gold, cv::Size(256, 256));
|
||||
cv::resize(gray_gold, gray_gold, cv::Size(256, 256));
|
||||
|
||||
EXPECT_MAT_NEAR(bgr_gold, dbgr, 1);
|
||||
EXPECT_MAT_NEAR(gray_gold, dgray, 1);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Fast Force Non local means
|
||||
|
||||
TEST(CUDA_FastNonLocalMeans, Regression)
|
||||
{
|
||||
using cv::cuda::GpuMat;
|
||||
|
||||
cv::Mat bgr = readImage("../gpu/denoising/lena_noised_gaussian_sigma=20_multi_0.png", cv::IMREAD_COLOR);
|
||||
ASSERT_FALSE(bgr.empty());
|
||||
|
||||
cv::Mat gray;
|
||||
cv::cvtColor(bgr, gray, cv::COLOR_BGR2GRAY);
|
||||
|
||||
GpuMat dbgr, dgray;
|
||||
|
||||
cv::cuda::fastNlMeansDenoising(GpuMat(gray), dgray, 20);
|
||||
cv::cuda::fastNlMeansDenoisingColored(GpuMat(bgr), dbgr, 20, 10);
|
||||
|
||||
#if 0
|
||||
dumpImage("../gpu/denoising/fnlm_denoised_lena_bgr.png", cv::Mat(dbgr));
|
||||
dumpImage("../gpu/denoising/fnlm_denoised_lena_gray.png", cv::Mat(dgray));
|
||||
#endif
|
||||
|
||||
cv::Mat bgr_gold = readImage("../gpu/denoising/fnlm_denoised_lena_bgr.png", cv::IMREAD_COLOR);
|
||||
cv::Mat gray_gold = readImage("../gpu/denoising/fnlm_denoised_lena_gray.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(bgr_gold.empty() || gray_gold.empty());
|
||||
|
||||
EXPECT_MAT_NEAR(bgr_gold, dbgr, 1);
|
||||
EXPECT_MAT_NEAR(gray_gold, dgray, 1);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
#endif // HAVE_CUDA
|
||||
@@ -0,0 +1,372 @@
|
||||
/*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"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
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, const string& name)
|
||||
{
|
||||
double max = 1.0;
|
||||
minMaxLoc(abs(img0 - img1), NULL, &max);
|
||||
ASSERT_FALSE(max > threshold) << "max=" << max << " threshold=" << threshold << " method=" << name;
|
||||
}
|
||||
|
||||
static vector<float> DEFAULT_VECTOR;
|
||||
void loadExposureSeq(String path, vector<Mat>& images, vector<float>& times = DEFAULT_VECTOR)
|
||||
{
|
||||
std::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);
|
||||
std::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, "Simple");
|
||||
|
||||
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, "Drago");
|
||||
|
||||
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, "Reinhard");
|
||||
|
||||
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, "Mantiuk");
|
||||
}
|
||||
|
||||
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);
|
||||
RNG rng = theRNG();
|
||||
|
||||
for(int i = 0; i < TESTS_COUNT; i++) {
|
||||
Point shift(rng.uniform(0, max_shift), rng.uniform(0, 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, "Mertens");
|
||||
|
||||
Mat uniform(100, 100, CV_8UC3);
|
||||
uniform = Scalar(0, 255, 0);
|
||||
|
||||
images.clear();
|
||||
images.push_back(uniform);
|
||||
|
||||
merge->process(images, result);
|
||||
result.convertTo(result, CV_8UC3, 255);
|
||||
checkEqual(uniform, result, 1e-2f, "Mertens");
|
||||
}
|
||||
|
||||
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, "Debevec");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const float eps = 6.f;
|
||||
checkEqual(expected, result, eps, "MergeRobertson");
|
||||
}
|
||||
|
||||
TEST(Photo_MergeDebevec, regression_depth_consistency)
|
||||
{
|
||||
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
|
||||
|
||||
vector<Mat> images8;
|
||||
vector<float> times;
|
||||
loadExposureSeq(test_path + "exposures/", images8, times);
|
||||
|
||||
vector<Mat> images16(images8.size()), images32(images8.size());
|
||||
for (size_t i = 0; i < images8.size(); ++i)
|
||||
{
|
||||
images8[i].convertTo(images16[i], CV_16UC3, 257.0);
|
||||
images8[i].convertTo(images32[i], CV_32FC3, 1.0 / 255.0);
|
||||
}
|
||||
|
||||
Ptr<MergeDebevec> merge = createMergeDebevec();
|
||||
Ptr<Tonemap> map = createTonemap();
|
||||
|
||||
Mat hdr8, hdr16, hdr32;
|
||||
merge->process(images8, hdr8, times);
|
||||
merge->process(images16, hdr16, times);
|
||||
merge->process(images32, hdr32, times);
|
||||
|
||||
map->process(hdr8, hdr8);
|
||||
map->process(hdr16, hdr16);
|
||||
map->process(hdr32, hdr32);
|
||||
|
||||
checkEqual(hdr8, hdr16, 2e-2f, "Debevec realdata 16U vs 8U");
|
||||
checkEqual(hdr8, hdr32, 2e-2f, "Debevec realdata 32F vs 8U");
|
||||
}
|
||||
|
||||
TEST(Photo_MergeRobertson, regression_depth_consistency)
|
||||
{
|
||||
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
|
||||
|
||||
vector<Mat> images8;
|
||||
vector<float> times;
|
||||
loadExposureSeq(test_path + "exposures/", images8, times);
|
||||
|
||||
vector<Mat> images16(images8.size()), images32(images8.size());
|
||||
for (size_t i = 0; i < images8.size(); ++i)
|
||||
{
|
||||
images8[i].convertTo(images16[i], CV_16UC3, 257.0);
|
||||
images8[i].convertTo(images32[i], CV_32FC3, 1.0 / 255.0);
|
||||
}
|
||||
|
||||
Ptr<MergeRobertson> merge = createMergeRobertson();
|
||||
Ptr<Tonemap> map = createTonemap();
|
||||
|
||||
Mat hdr8, hdr16, hdr32;
|
||||
merge->process(images8, hdr8, times);
|
||||
merge->process(images16, hdr16, times);
|
||||
merge->process(images32, hdr32, times);
|
||||
|
||||
map->process(hdr8, hdr8);
|
||||
map->process(hdr16, hdr16);
|
||||
map->process(hdr32, hdr32);
|
||||
|
||||
checkEqual(hdr8, hdr16, 3e-2f, "Robertson realdata 16U vs 8U");
|
||||
checkEqual(hdr8, hdr32, 3e-2f, "Robertson realdata 32F vs 8U");
|
||||
}
|
||||
|
||||
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);
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
ASSERT_LT(max, 0.25);
|
||||
#elif !defined(HAVE_IPP)
|
||||
ASSERT_LT(max, 0.22);
|
||||
#else
|
||||
ASSERT_LT(max, 0.15);
|
||||
#endif
|
||||
}
|
||||
|
||||
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-1f, "CalibrateRobertson");
|
||||
}
|
||||
|
||||
TEST(Photo_CalibrateRobertson, bug_18180)
|
||||
{
|
||||
vector<Mat> images;
|
||||
vector<cv::String> fn;
|
||||
string test_path = cvtest::TS::ptr()->get_data_path() + "hdr/exposures/bug_18180/";
|
||||
for(int i = 1; i <= 4; ++i)
|
||||
images.push_back(imread(test_path + std::to_string(i) + ".jpg"));
|
||||
vector<float> times {15.0f, 2.5f, 0.25f, 0.33f};
|
||||
Mat response, expected;
|
||||
Ptr<CalibrateRobertson> calibrate = createCalibrateRobertson(2, 0.01f);
|
||||
calibrate->process(images, response, times);
|
||||
Mat response_no_nans = response.clone();
|
||||
patchNaNs(response_no_nans);
|
||||
// since there should be no NaNs, original response vs. response with NaNs patched should be identical
|
||||
EXPECT_EQ(0.0, cv::norm(response, response_no_nans, NORM_L2));
|
||||
}
|
||||
|
||||
TEST(Photo_CalibrateDebevec, bug_24966)
|
||||
{
|
||||
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
|
||||
vector<Mat> all_images;
|
||||
vector<float> all_times;
|
||||
loadExposureSeq(test_path + "exposures/", all_images, all_times);
|
||||
// Use a balanced subset of exposures
|
||||
vector<int> selected_indices = {1,2,3,4,5};
|
||||
vector<Mat> images;
|
||||
vector<float> times;
|
||||
for (int idx : selected_indices) {
|
||||
images.push_back(all_images[idx]);
|
||||
times.push_back(all_times[idx]);
|
||||
}
|
||||
// Run CRF estimation for different sample points
|
||||
vector<int> sample_points = {200,300,400};
|
||||
vector<Mat> responses;
|
||||
for (int samples : sample_points) {
|
||||
Ptr<CalibrateDebevec> calibrate = createCalibrateDebevec(samples);
|
||||
Mat response;
|
||||
calibrate->process(images, response, times);
|
||||
Mat roi = response.rowRange(15, 240); //Checking CRF only in the middle of the image
|
||||
responses.push_back(roi);
|
||||
}
|
||||
|
||||
// Compare consecutive pairs of CRFs
|
||||
for (size_t i = 0; i < responses.size()-1; ++i) {
|
||||
Mat diff = abs(responses[i] - responses[i+1]);
|
||||
double max_diff;
|
||||
minMaxLoc(diff, nullptr, &max_diff);
|
||||
cout << "max_diff = " << max_diff << endl;
|
||||
#if defined(__aarch64__) && defined(__APPLE__)
|
||||
ASSERT_LT(max_diff, 10) << "CRF instability detected between samples="
|
||||
<< sample_points[i] << " and " << sample_points[i+1]
|
||||
<< " (max diff = " << max_diff << ")";
|
||||
#else
|
||||
ASSERT_LT(max_diff, 5) << "CRF instability detected between samples="
|
||||
<< sample_points[i] << " and " << sample_points[i+1]
|
||||
<< " (max diff = " << max_diff << ")";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}} // namespace
|
||||
@@ -0,0 +1,186 @@
|
||||
/*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 "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
class CV_InpaintTest : public cvtest::BaseTest
|
||||
{
|
||||
public:
|
||||
CV_InpaintTest();
|
||||
~CV_InpaintTest();
|
||||
protected:
|
||||
void run(int);
|
||||
};
|
||||
|
||||
CV_InpaintTest::CV_InpaintTest()
|
||||
{
|
||||
}
|
||||
CV_InpaintTest::~CV_InpaintTest() {}
|
||||
|
||||
void CV_InpaintTest::run( int )
|
||||
{
|
||||
string folder = string(ts->get_data_path()) + "inpaint/";
|
||||
Mat orig = imread(folder + "orig.png");
|
||||
Mat exp1 = imread(folder + "exp1.png");
|
||||
Mat exp2 = imread(folder + "exp2.png");
|
||||
Mat mask = imread(folder + "mask.png");
|
||||
|
||||
if (orig.empty() || exp1.empty() || exp2.empty() || mask.empty())
|
||||
{
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
|
||||
return;
|
||||
}
|
||||
|
||||
Mat inv_mask;
|
||||
mask.convertTo(inv_mask, CV_8UC3, -1.0, 255.0);
|
||||
|
||||
Mat mask1ch;
|
||||
cv::cvtColor(mask, mask1ch, COLOR_BGR2GRAY);
|
||||
|
||||
Mat test = orig.clone();
|
||||
test.setTo(Scalar::all(255), mask1ch);
|
||||
|
||||
Mat res1, res2;
|
||||
inpaint( test, mask1ch, res1, 5, INPAINT_NS );
|
||||
inpaint( test, mask1ch, res2, 5, INPAINT_TELEA );
|
||||
|
||||
Mat diff1, diff2;
|
||||
absdiff( orig, res1, diff1 );
|
||||
absdiff( orig, res2, diff2 );
|
||||
|
||||
double n1 = cvtest::norm(diff1.reshape(1), NORM_INF, inv_mask.reshape(1));
|
||||
double n2 = cvtest::norm(diff2.reshape(1), NORM_INF, inv_mask.reshape(1));
|
||||
|
||||
if (n1 != 0 || n2 != 0)
|
||||
{
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH );
|
||||
return;
|
||||
}
|
||||
|
||||
absdiff( exp1, res1, diff1 );
|
||||
absdiff( exp2, res2, diff2 );
|
||||
|
||||
n1 = cvtest::norm(diff1.reshape(1), NORM_INF, mask.reshape(1));
|
||||
n2 = cvtest::norm(diff2.reshape(1), NORM_INF, mask.reshape(1));
|
||||
|
||||
const int jpeg_thres = 3;
|
||||
if (n1 > jpeg_thres || n2 > jpeg_thres)
|
||||
{
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
|
||||
return;
|
||||
}
|
||||
|
||||
ts->set_failed_test_info(cvtest::TS::OK);
|
||||
}
|
||||
|
||||
TEST(Photo_Inpaint, regression) { CV_InpaintTest test; test.safe_run(); }
|
||||
|
||||
typedef testing::TestWithParam<tuple<perf::MatType> > formats;
|
||||
|
||||
TEST_P(formats, basic)
|
||||
{
|
||||
const int type = get<0>(GetParam());
|
||||
Mat src(100, 100, type);
|
||||
src.setTo(Scalar::all(128));
|
||||
Mat ref = src.clone();
|
||||
Mat dst, mask = Mat::zeros(src.size(), CV_8U);
|
||||
|
||||
circle(src, Point(50, 50), 5, Scalar::all(200), 6);
|
||||
circle(mask, Point(50, 50), 5, Scalar::all(200), 6);
|
||||
inpaint(src, mask, dst, 10, INPAINT_NS);
|
||||
|
||||
Mat dst2;
|
||||
inpaint(src, mask, dst2, 10, INPAINT_TELEA);
|
||||
|
||||
ASSERT_EQ(cv::norm(dst, ref, NORM_INF), 0.);
|
||||
ASSERT_EQ(cv::norm(dst2, ref, NORM_INF), 0.);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Photo_Inpaint, formats, testing::Values(CV_32FC1, CV_16UC1, CV_8UC1, CV_8UC3));
|
||||
|
||||
TEST(Photo_InpaintBorders, regression)
|
||||
{
|
||||
Mat img(64, 64, CV_8U);
|
||||
img = 128;
|
||||
img(Rect(0, 0, 16, 64)) = 0;
|
||||
|
||||
Mat mask(64, 64, CV_8U);
|
||||
mask = 0;
|
||||
mask(Rect(0, 0, 16, 64)) = 255;
|
||||
|
||||
Mat inpainted;
|
||||
inpaint(img, mask, inpainted, 1, INPAINT_TELEA);
|
||||
|
||||
Mat diff;
|
||||
cv::absdiff(inpainted, 128*Mat::ones(inpainted.size(), inpainted.type()), diff);
|
||||
ASSERT_TRUE(countNonZero(diff) == 0);
|
||||
}
|
||||
|
||||
typedef testing::TestWithParam<tuple<perf::MatType>> Photo_InpaintSmallBorders;
|
||||
|
||||
TEST_P(Photo_InpaintSmallBorders, regression)
|
||||
{
|
||||
int type = get<0>(GetParam());
|
||||
Mat img(5, 5, type, Scalar::all(128));
|
||||
Mat expected = img.clone();
|
||||
|
||||
Mat mask = Mat::zeros(5, 5, CV_8U);
|
||||
mask(Rect(1, 1, 3, 3)) = 255;
|
||||
|
||||
img.setTo(Scalar::all(0), mask);
|
||||
|
||||
Mat inpainted, diff;
|
||||
|
||||
inpaint(img, mask, inpainted, 1, INPAINT_TELEA);
|
||||
cv::absdiff(inpainted, expected, diff);
|
||||
ASSERT_EQ(countNonZero(diff.reshape(1)), 0);
|
||||
|
||||
inpaint(img, mask, inpainted, 1, INPAINT_NS);
|
||||
cv::absdiff(inpainted, expected, diff);
|
||||
ASSERT_EQ(countNonZero(diff.reshape(1)), 0);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(/*nothing*/, Photo_InpaintSmallBorders, Values(CV_8UC1, CV_8UC3));
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,496 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
//#include "opencv2/imgproc/segmentation.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
|
||||
Mat getTestImageGray()
|
||||
{
|
||||
static Mat m;
|
||||
if (m.empty())
|
||||
{
|
||||
m = imread(findDataFile("shared/lena.png"), IMREAD_GRAYSCALE);
|
||||
}
|
||||
return m.clone();
|
||||
}
|
||||
|
||||
Mat getTestImageColor()
|
||||
{
|
||||
static Mat m;
|
||||
if (m.empty())
|
||||
{
|
||||
m = imread(findDataFile("shared/lena.png"), IMREAD_COLOR);
|
||||
}
|
||||
return m.clone();
|
||||
}
|
||||
|
||||
Mat getTestImage1()
|
||||
{
|
||||
static Mat m;
|
||||
if (m.empty())
|
||||
{
|
||||
m.create(Size(200, 100), CV_8UC1);
|
||||
m.setTo(Scalar::all(128));
|
||||
Rect roi(50, 30, 100, 40);
|
||||
m(roi).setTo(Scalar::all(0));
|
||||
#if 0
|
||||
imshow("image", m);
|
||||
waitKey();
|
||||
#endif
|
||||
}
|
||||
return m.clone();
|
||||
}
|
||||
|
||||
Mat getTestImage2()
|
||||
{
|
||||
static Mat m;
|
||||
if (m.empty())
|
||||
{
|
||||
m.create(Size(200, 100), CV_8UC1);
|
||||
m.setTo(Scalar::all(128));
|
||||
Rect roi(40, 30, 100, 40);
|
||||
m(roi).setTo(Scalar::all(255));
|
||||
#if 0
|
||||
imshow("image", m);
|
||||
waitKey();
|
||||
#endif
|
||||
}
|
||||
return m.clone();
|
||||
}
|
||||
|
||||
Mat getTestImage3()
|
||||
{
|
||||
static Mat m;
|
||||
if (m.empty())
|
||||
{
|
||||
m.create(Size(200, 100), CV_8UC1);
|
||||
m.setTo(Scalar::all(128));
|
||||
Scalar color(0,0,0,0);
|
||||
line(m, Point(30, 50), Point(50, 50), color, 1);
|
||||
line(m, Point(50, 50), Point(80, 30), color, 1);
|
||||
line(m, Point(150, 50), Point(80, 30), color, 1);
|
||||
line(m, Point(150, 50), Point(180, 50), color, 1);
|
||||
|
||||
line(m, Point(80, 10), Point(80, 90), Scalar::all(200), 1);
|
||||
line(m, Point(100, 10), Point(100, 90), Scalar::all(200), 1);
|
||||
line(m, Point(120, 10), Point(120, 90), Scalar::all(200), 1);
|
||||
#if 0
|
||||
imshow("image", m);
|
||||
waitKey();
|
||||
#endif
|
||||
}
|
||||
return m.clone();
|
||||
}
|
||||
|
||||
Mat getTestImage4()
|
||||
{
|
||||
static Mat m;
|
||||
if (m.empty())
|
||||
{
|
||||
m.create(Size(200, 100), CV_8UC1);
|
||||
for (int y = 0; y < m.rows; y++)
|
||||
{
|
||||
for (int x = 0; x < m.cols; x++)
|
||||
{
|
||||
float dx = (float)(x - 100);
|
||||
float dy = (float)(y - 100);
|
||||
float d = sqrtf(dx * dx + dy * dy);
|
||||
m.at<uchar>(y, x) = saturate_cast<uchar>(100 + 100 * sin(d / 10 * CV_PI));
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
imshow("image", m);
|
||||
waitKey();
|
||||
#endif
|
||||
}
|
||||
return m.clone();
|
||||
}
|
||||
|
||||
Mat getTestImage5()
|
||||
{
|
||||
static Mat m;
|
||||
if (m.empty())
|
||||
{
|
||||
m.create(Size(200, 100), CV_8UC1);
|
||||
for (int y = 0; y < m.rows; y++)
|
||||
{
|
||||
for (int x = 0; x < m.cols; x++)
|
||||
{
|
||||
float dx = (float)(x - 100);
|
||||
float dy = (float)(y - 100);
|
||||
float d = sqrtf(dx * dx + dy * dy);
|
||||
m.at<uchar>(y, x) = saturate_cast<uchar>(x / 2 + 100 * sin(d / 10 * CV_PI));
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
imshow("image", m);
|
||||
waitKey();
|
||||
#endif
|
||||
}
|
||||
return m.clone();
|
||||
}
|
||||
|
||||
void show(const Mat& img, const std::vector<Point> pts)
|
||||
{
|
||||
if (cvtest::debugLevel >= 10)
|
||||
{
|
||||
Mat dst = img.clone();
|
||||
std::vector< std::vector<Point> > contours;
|
||||
contours.push_back(pts);
|
||||
polylines(dst, contours, false, Scalar::all(255));
|
||||
imshow("dst", dst);
|
||||
waitKey();
|
||||
}
|
||||
}
|
||||
|
||||
Size estimateContourSize(const std::vector<Point>& pts)
|
||||
{
|
||||
Size s(0,0);
|
||||
for (size_t i = 0; i < pts.size(); i++)
|
||||
{
|
||||
if (s.width < pts[i].x)
|
||||
s.width = pts[i].x;
|
||||
if (s.height < pts[i].y)
|
||||
s.height = pts[i].y;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
int contoursAreaPixelsMismatch(const std::vector<Point>& pts, const std::vector<Point>& gt)
|
||||
{
|
||||
Size ptsSize = estimateContourSize(pts);
|
||||
Size gtSize = estimateContourSize(gt);
|
||||
|
||||
Size imgSize(std::max(ptsSize.width, gtSize.width)+1, std::max(ptsSize.height, gtSize.height)+1);
|
||||
Mat ptsArea = Mat::zeros(imgSize, CV_8UC1);
|
||||
Mat gtArea = Mat::zeros(imgSize, CV_8UC1);
|
||||
|
||||
std::vector<std::vector<Point>> pts_wrapped = {pts};
|
||||
std::vector<std::vector<Point>> gt_wrapped = {gt};
|
||||
drawContours(ptsArea, pts_wrapped, -1, Scalar(255), FILLED);
|
||||
drawContours(gtArea, gt_wrapped, -1, Scalar(255), FILLED);
|
||||
|
||||
Mat uni = ptsArea | gtArea;
|
||||
Mat intersection = ptsArea & gtArea;
|
||||
bitwise_not(intersection, intersection);
|
||||
Mat delta = uni & intersection;
|
||||
|
||||
return countNonZero(delta);
|
||||
}
|
||||
|
||||
void checkContour(std::vector<Point>& pts,
|
||||
const bool backward = false,
|
||||
int allowed_mismatch = 0)
|
||||
{
|
||||
const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||
CV_Assert(test_info);
|
||||
const std::string name = std::string(cvtest::TS::ptr()->get_data_path() + "imgproc/" + test_info->test_case_name() + "-" + test_info->name() + (backward ? "-backward" : "") + ".xml");
|
||||
|
||||
std::vector<Point> reference_pts;
|
||||
#ifdef GENERATE_TEST_DATA
|
||||
{
|
||||
cv::FileStorage fs(name, cv::FileStorage::WRITE);
|
||||
fs << "pts" << pts;
|
||||
}
|
||||
reference_pts = pts;
|
||||
#else
|
||||
FileStorage fs(name, FileStorage::READ);
|
||||
read(fs["pts"], reference_pts, std::vector<Point>());
|
||||
#endif
|
||||
|
||||
if (!allowed_mismatch)
|
||||
EXPECT_EQ(pts, reference_pts);
|
||||
else
|
||||
EXPECT_LE(contoursAreaPixelsMismatch(pts, reference_pts), allowed_mismatch);
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, rect)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
Mat image = getTestImage1();
|
||||
tool.applyImage(image);
|
||||
|
||||
Point source_point(50, 30);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(100, 30);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts);
|
||||
show(image, pts);
|
||||
|
||||
Mat image2 = getTestImage2();
|
||||
tool.applyImage(image2);
|
||||
|
||||
tool.buildMap(source_point);
|
||||
|
||||
std::vector<Point> pts2;
|
||||
tool.getContour(target_point, pts2, true/*backward*/);
|
||||
checkContour(pts2, true/*backward*/);
|
||||
show(image2, pts2);
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, lines)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
Mat image = getTestImage3();
|
||||
tool.applyImage(image);
|
||||
|
||||
Point source_point(30, 50);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(150, 50);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts);
|
||||
show(image, pts);
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, circles)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
tool.setGradientMagnitudeMaxLimit(10);
|
||||
|
||||
Mat image = getTestImage4();
|
||||
tool.applyImage(image);
|
||||
|
||||
Point source_point(50, 50);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(150, 50);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts);
|
||||
show(image, pts);
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, circles_gradient)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
Mat image = getTestImage5();
|
||||
tool.applyImage(image);
|
||||
|
||||
Point source_point(50, 50);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(150, 50);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts);
|
||||
show(image, pts);
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, grayscale)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
|
||||
Mat image = getTestImageGray();
|
||||
tool.applyImage(image);
|
||||
|
||||
Point source_point(275, 63);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(413, 155);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts, false, 2);
|
||||
show(image, pts);
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, check_features_grayscale_1_0_0_zerro_crossing_with_limit)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
tool.setEdgeFeatureZeroCrossingParameters(64);
|
||||
tool.setWeights(1.0f, 0.0f, 0.0f);
|
||||
|
||||
Mat image = getTestImageGray();
|
||||
tool.applyImage(image);
|
||||
|
||||
Point source_point(275, 63);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(413, 155);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts, false, 11);
|
||||
show(image, pts);
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, check_features_grayscale_1_0_0_canny)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
tool.setEdgeFeatureCannyParameters(50, 100);
|
||||
tool.setWeights(1.0f, 0.0f, 0.0f);
|
||||
|
||||
Mat image = getTestImageGray();
|
||||
tool.applyImage(image);
|
||||
|
||||
Point source_point(275, 63);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(413, 155);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts, false, 6);
|
||||
show(image, pts);
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, check_features_grayscale_0_1_0)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
tool.setWeights(0.0f, 1.0f, 0.0f);
|
||||
|
||||
Mat image = getTestImageGray();
|
||||
tool.applyImage(image);
|
||||
|
||||
Point source_point(275, 63);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(413, 155);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts, false, 4);
|
||||
show(image, pts);
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, check_features_grayscale_0_0_1)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
tool.setWeights(0.0f, 0.0f, 1.0f);
|
||||
|
||||
Mat image = getTestImageGray();
|
||||
tool.applyImage(image);
|
||||
|
||||
Point source_point(275, 63);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(413, 155);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts, false, 2);
|
||||
show(image, pts);
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, color)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
|
||||
Mat image = getTestImageColor();
|
||||
tool.applyImage(image);
|
||||
|
||||
Point source_point(275, 63);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(413, 155);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts, false, 2);
|
||||
show(image, pts);
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, color_canny)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
tool.setEdgeFeatureCannyParameters(32, 100);
|
||||
|
||||
Mat image = getTestImageColor();
|
||||
tool.applyImage(image);
|
||||
|
||||
Point source_point(275, 63);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(413, 155);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts, false, 2);
|
||||
show(image, pts);
|
||||
}
|
||||
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, color_custom_features_invalid)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
ASSERT_ANY_THROW(tool.applyImageFeatures(noArray(), noArray(), noArray()));
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, color_custom_features_edge)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
|
||||
Mat image = getTestImageColor();
|
||||
|
||||
Mat canny_edges;
|
||||
Canny(image, canny_edges, 32, 100, 5);
|
||||
Mat binary_edge_feature;
|
||||
cv::threshold(canny_edges, binary_edge_feature, 254, 1, THRESH_BINARY_INV);
|
||||
tool.applyImageFeatures(binary_edge_feature, noArray(), noArray(), image);
|
||||
|
||||
Point source_point(275, 63);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(413, 155);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts, false, 2);
|
||||
show(image, pts);
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, color_custom_features_all)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
|
||||
tool.setWeights(0.9f, 0.0f, 0.1f);
|
||||
|
||||
Mat image = getTestImageColor();
|
||||
|
||||
Mat canny_edges;
|
||||
Canny(image, canny_edges, 50, 100, 5);
|
||||
Mat binary_edge_feature; // 0, 1 values
|
||||
cv::threshold(canny_edges, binary_edge_feature, 254, 1, THRESH_BINARY_INV);
|
||||
|
||||
Mat_<Point2f> gradient_direction(image.size(), Point2f(0, 0)); // normalized
|
||||
Mat_<float> gradient_magnitude(image.size(), 0); // cost function
|
||||
tool.applyImageFeatures(binary_edge_feature, gradient_direction, gradient_magnitude);
|
||||
|
||||
Point source_point(275, 63);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(413, 155);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts, false, 9);
|
||||
show(image, pts);
|
||||
}
|
||||
|
||||
TEST(Imgproc_IntelligentScissorsMB, color_custom_features_edge_magnitude)
|
||||
{
|
||||
segmentation::IntelligentScissorsMB tool;
|
||||
|
||||
tool.setWeights(0.9f, 0.0f, 0.1f);
|
||||
|
||||
Mat image = getTestImageColor();
|
||||
|
||||
Mat canny_edges;
|
||||
Canny(image, canny_edges, 50, 100, 5);
|
||||
Mat binary_edge_feature; // 0, 1 values
|
||||
cv::threshold(canny_edges, binary_edge_feature, 254, 1, THRESH_BINARY_INV);
|
||||
|
||||
Mat_<float> gradient_magnitude(image.size(), 0); // cost function
|
||||
tool.applyImageFeatures(binary_edge_feature, noArray(), gradient_magnitude);
|
||||
|
||||
Point source_point(275, 63);
|
||||
tool.buildMap(source_point);
|
||||
|
||||
Point target_point(413, 155);
|
||||
std::vector<Point> pts;
|
||||
tool.getContour(target_point, pts);
|
||||
checkContour(pts, false, 9);
|
||||
show(image, pts);
|
||||
}
|
||||
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,10 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
#if defined(HAVE_HPX)
|
||||
#include <hpx/hpx_main.hpp>
|
||||
#endif
|
||||
|
||||
CV_TEST_MAIN("cv")
|
||||
@@ -0,0 +1,140 @@
|
||||
/*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"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
static const double numerical_precision = 100.;
|
||||
|
||||
TEST(Photo_NPR_EdgePreserveSmoothing_RecursiveFilter, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "npr/";
|
||||
string original_path = folder + "test1.png";
|
||||
|
||||
Mat source = imread(original_path, IMREAD_COLOR);
|
||||
|
||||
ASSERT_FALSE(source.empty()) << "Could not load input image " << original_path;
|
||||
|
||||
Mat result;
|
||||
edgePreservingFilter(source,result,1);
|
||||
|
||||
Mat reference = imread(folder + "smoothened_RF_reference.png");
|
||||
|
||||
double psnr = cvtest::PSNR(reference, result);
|
||||
EXPECT_GT(psnr, 60.0);
|
||||
}
|
||||
|
||||
TEST(Photo_NPR_EdgePreserveSmoothing_NormConvFilter, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "npr/";
|
||||
string original_path = folder + "test1.png";
|
||||
|
||||
Mat source = imread(original_path, IMREAD_COLOR);
|
||||
|
||||
ASSERT_FALSE(source.empty()) << "Could not load input image " << original_path;
|
||||
|
||||
Mat result;
|
||||
edgePreservingFilter(source,result,2);
|
||||
|
||||
Mat reference = imread(folder + "smoothened_NCF_reference.png");
|
||||
|
||||
double psnr = cvtest::PSNR(reference, result);
|
||||
EXPECT_GT(psnr, 60.0);
|
||||
}
|
||||
|
||||
TEST(Photo_NPR_DetailEnhance, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "npr/";
|
||||
string original_path = folder + "test1.png";
|
||||
|
||||
Mat source = imread(original_path, IMREAD_COLOR);
|
||||
|
||||
ASSERT_FALSE(source.empty()) << "Could not load input image " << original_path;
|
||||
|
||||
Mat result;
|
||||
detailEnhance(source,result);
|
||||
|
||||
Mat reference = imread(folder + "detail_enhanced_reference.png");
|
||||
double psnr = cvtest::PSNR(reference, result);
|
||||
EXPECT_GT(psnr, 60.0);
|
||||
}
|
||||
|
||||
TEST(Photo_NPR_PencilSketch, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "npr/";
|
||||
string original_path = folder + "test1.png";
|
||||
|
||||
Mat source = imread(original_path, IMREAD_COLOR);
|
||||
|
||||
ASSERT_FALSE(source.empty()) << "Could not load input image " << original_path;
|
||||
|
||||
Mat pencil_result, color_pencil_result;
|
||||
pencilSketch(source,pencil_result, color_pencil_result, 10, 0.1f, 0.03f);
|
||||
|
||||
Mat pencil_reference = imread(folder + "pencil_sketch_reference.png", 0 /* == grayscale*/);
|
||||
double pencil_error = cvtest::norm(pencil_reference, pencil_result, NORM_L1);
|
||||
EXPECT_LE(pencil_error, numerical_precision);
|
||||
|
||||
Mat color_pencil_reference = imread(folder + "color_pencil_sketch_reference.png");
|
||||
double color_pencil_error = cvtest::norm(color_pencil_reference, color_pencil_result, NORM_L1);
|
||||
EXPECT_LE(color_pencil_error, numerical_precision);
|
||||
}
|
||||
|
||||
TEST(Photo_NPR_Stylization, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "npr/";
|
||||
string original_path = folder + "test1.png";
|
||||
|
||||
Mat source = imread(original_path, IMREAD_COLOR);
|
||||
|
||||
ASSERT_FALSE(source.empty()) << "Could not load input image " << original_path;
|
||||
|
||||
Mat result;
|
||||
stylization(source,result);
|
||||
|
||||
Mat stylized_reference = imread(folder + "stylized_reference.png");
|
||||
double stylized_error = cvtest::norm(stylized_reference, result, NORM_L1);
|
||||
EXPECT_LE(stylized_error, numerical_precision);
|
||||
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,15 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#ifndef OPENCV_PHOTO_TEST_PRECOMP_HPP
|
||||
#define OPENCV_PHOTO_TEST_PRECOMP_HPP
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/ts/ocl_test.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user