vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:10:33 +08:00
commit f7f077da11
6933 changed files with 2335208 additions and 0 deletions
+11
View File
@@ -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()
+967
View File
@@ -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
+322
View File
@@ -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);
}
}
+37
View File
@@ -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"
]
}
}
+8
View File
@@ -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
+103
View File
@@ -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
+189
View File
@@ -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
+64
View File
@@ -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
+60
View File
@@ -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
+15
View File
@@ -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())
+7
View File
@@ -0,0 +1,7 @@
#ifndef __OPENCV_PERF_PRECOMP_HPP__
#define __OPENCV_PERF_PRECOMP_HPP__
#include "opencv2/ts.hpp"
#include "opencv2/photo.hpp"
#endif
+281
View File
@@ -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);
}
}
+184
View File
@@ -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
+311
View File
@@ -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);
}
}
+557
View File
@@ -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
+391
View File
@@ -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
+108
View File
@@ -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
+769
View File
@@ -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
+343
View File
@@ -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
+204
View File
@@ -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
+80
View File
@@ -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
+114
View File
@@ -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
+53
View File
@@ -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
+284
View File
@@ -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
+260
View File
@@ -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
+71
View File
@@ -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
+83
View File
@@ -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
+113
View File
@@ -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
+145
View File
@@ -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);
}
}
+174
View File
@@ -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);
}
+383
View File
@@ -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;
}
+564
View File
@@ -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() );
}
}
}}}
+151
View File
@@ -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);
}
}
+440
View File
@@ -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);
}
+175
View File
@@ -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
+135
View File
@@ -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;
}
}
+62
View File
@@ -0,0 +1,62 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifndef __OPENCV_HDR_COMMON_HPP__
#define __OPENCV_HDR_COMMON_HPP__
#include "precomp.hpp"
#include "opencv2/photo.hpp"
namespace cv
{
void checkImageDimensions(const std::vector<Mat>& images);
Mat 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
+800
View File
@@ -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 );
}
+793
View File
@@ -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
+426
View File
@@ -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>();
}
}
+163
View File
@@ -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);
}
+591
View File
@@ -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);
}
}
}
+302
View File
@@ -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
+52
View File
@@ -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
+155
View File
@@ -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);
}
+89
View File
@@ -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
+451
View File
@@ -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);
}
+476
View File
@@ -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);
}
}
+133
View File
@@ -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
+302
View File
@@ -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);
}
}}
+247
View File
@@ -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
+68
View File
@@ -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
+129
View File
@@ -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
+197
View File
@@ -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
+120
View File
@@ -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
+372
View File
@@ -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
+186
View File
@@ -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
+10
View File
@@ -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")
+140
View File
@@ -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
+15
View File
@@ -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