vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:11:13 +08:00
commit 12022378a3
3872 changed files with 2513409 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
set(the_description "Addon to basic photo module")
ocv_define_module(xphoto opencv_core opencv_imgproc opencv_photo WRAP python java objc)
+7
View File
@@ -0,0 +1,7 @@
Additional photo processing algorithms
======================================
1. Color balance
2. Denoising
3. Inpainting
+77
View File
@@ -0,0 +1,77 @@
@incollection{He2012,
title={Statistics of patch offsets for image completion},
author={He, Kaiming and Sun, Jian},
booktitle={Computer Vision--ECCV 2012},
pages={16--29},
year={2012},
publisher={Springer}
}
@inproceedings{Cheng2015,
title={Effective learning-based illuminant estimation using simple features},
author={Cheng, Dongliang and Price, Brian and Cohen, Scott and Brown, Michael S},
booktitle={Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition},
pages={1000--1008},
year={2015}
}
@book{Holzmann1988,
title={Beyond Photography: The Digital Darkroom},
author={GerPublished by ard J. Holzmann},
publisher={Prentice Hall in 1988}
}
@inproceedings{DD02,
author = {Durand, Fr{\'e}do and Dorsey, Julie},
title = {Fast bilateral filtering for the display of high-dynamic-range images},
booktitle = {ACM Transactions on Graphics (TOG)},
year = {2002},
pages = {257--266},
volume = {21},
number = {3},
publisher = {ACM},
url = {https://www.researchgate.net/profile/Julie_Dorsey/publication/220184746_Fast_Bilateral_Filtering_for_the_Display_of_High_-_dynamic_-_range_Images/links/54566b000cf26d5090a95f96/Fast-Bilateral-Filtering-for-the-Display-of-High-dynamic-range-Images.pdf}
}
@INPROCEEDINGS{GenserPCS2018,
author={N. {Genser} and J. {Seiler} and F. {Schilling} and A. {Kaup}},
booktitle={Proc. Picture Coding Symposium (PCS)},
title={Signal and Loss Geometry Aware Frequency Selective Extrapolation for Error Concealment},
year={2018},
pages={159-163},
keywords={extrapolation;image reconstruction;video coding;loss geometry aware frequency selective extrapolation;error concealment;complex models;moderate computational complexity;Full HD image;error pattern;adjacent samples;undistorted samples;reconstruction parameters;processing order;High Efficiency Video Coding;content based partitioning;signal characteristics;block based frequency selective extrapolation;Image reconstruction;Extrapolation;Geometry;Partitioning algorithms;Task analysis;Computational modeling;Standards},
doi={10.1109/PCS.2018.8456259},
month={June},
}
@ARTICLE{SeilerTIP2015,
author={J. {Seiler} and M. {Jonscher} and M. {Schöberl} and A. {Kaup}},
journal={IEEE Transactions on Image Processing},
title={Resampling Images to a Regular Grid From a Non-Regular Subset of Pixel Positions Using Frequency Selective Reconstruction},
year={2015},
volume={24},
number={11},
pages={4540-4555},
keywords={Fourier transforms;image reconstruction;resampling images;regular grid;nonregular subset;pixel positions;frequency selective reconstruction;displaying image signals;image signal reconstruction algorithm;Fourier domain;optical transfer function;visual quality;peak signal-to-noise ratio;Image reconstruction;Signal processing algorithms;Reconstruction algorithms;Signal processing;Spatial resolution;;Image reconstruction;non-regular sampling;interpolation},
doi={10.1109/TIP.2015.2463084},
month={Nov},
}
@INPROCEEDINGS{GroscheICIP2018,
author={S. {Grosche} and J. {Seiler} and A. {Kaup}},
booktitle={Proc. 25th IEEE International Conference on Image Processing (ICIP)},
title={Iterative Optimization of Quarter Sampling Masks for Non-Regular Sampling Sensors},
year={2018},
pages={26-30},
keywords={extrapolation;image enhancement;image reconstruction;image resolution;image sampling;image sensors;interpolation;iterative methods;optimisation;regression analysis;iterative optimization;nonregular sampling sensors;iterative algorithm;arbitrary quarter sampling mask;reconstruction algorithms;random quarter sampling mask;optimized mask;frequency selective extrapolation;steering kernel regression;nearest neighbor interpolation;linear interpolation;regular imaging sensor;reconstruction quality;noise figure 0.31 dB to 0.68 dB;Image resolution;Image reconstruction;Sensors;Optimization;Energy resolution;Reconstruction algorithms;Image sensors;Non-Regular Sampling;Image reconstruction},
doi={10.1109/ICIP.2018.8451658},
month={Oct},
}
@INPROCEEDINGS{GroscheIST2018,
author={S. {Grosche} and J. {Seiler} and A. {Kaup}},
booktitle={Proc. IEEE International Conference on Imaging Systems and Techniques (IST)},
title={Design Techniques for Incremental Non-Regular Image Sampling Patterns},
year={2018},
pages={1-6},
keywords={image reconstruction;image resolution;image sampling;design techniques;incremental nonregular image sampling patterns;image signals;regular two dimensional grid;nonregular sampling patterns;sampling positions;random patterns;regular patterns;arbitrary sampling densities;incremental sampling patterns;sampling density;Image reconstruction;Scanning electron microscopy;Probability distribution;Atomic force microscopy;Reconstruction algorithms;Measurement by laser beam;Image Reconstruction;non-Regular Sampling},
doi={10.1109/IST.2018.8577090},
month={Oct},
}
+56
View File
@@ -0,0 +1,56 @@
/*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_XPHOTO_HPP__
#define __OPENCV_XPHOTO_HPP__
/** @defgroup xphoto Additional photo processing algorithms
*/
#include "xphoto/inpainting.hpp"
#include "xphoto/white_balance.hpp"
#include "xphoto/dct_image_denoising.hpp"
#include "xphoto/bm3d_image_denoising.hpp"
#include "xphoto/oilpainting.hpp"
#include "xphoto/tonemap.hpp"
#endif
@@ -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-2011, 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_BM3D_IMAGE_DENOISING_HPP__
#define __OPENCV_BM3D_IMAGE_DENOISING_HPP__
/** @file
@date Jul 19, 2016
@author Bartek Pawlik
*/
#include <opencv2/core.hpp>
namespace cv
{
namespace xphoto
{
//! @addtogroup xphoto
//! @{
//! BM3D transform types
enum TransformTypes
{
/** Un-normalized Haar transform */
HAAR = 0
};
//! BM3D algorithm steps
enum Bm3dSteps
{
/** Execute all steps of the algorithm */
BM3D_STEPALL = 0,
/** Execute only first step of the algorithm */
BM3D_STEP1 = 1,
/** Execute only second step of the algorithm */
BM3D_STEP2 = 2
};
/** @brief Performs image denoising using the Block-Matching and 3D-filtering algorithm
<http://www.cs.tut.fi/~foi/GCF-BM3D/BM3D_TIP_2007.pdf> with several computational
optimizations. Noise expected to be a gaussian white noise.
@param src Input 8-bit or 16-bit 1-channel image.
@param dstStep1 Output image of the first step of BM3D with the same size and type as src.
@param dstStep2 Output image of the second step of BM3D 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 templateWindowSize Size in pixels of the template patch that is used for block-matching.
Should be power of 2.
@param searchWindowSize Size in pixels of the window that is used to perform block-matching.
Affect performance linearly: greater searchWindowsSize - greater denoising time.
Must be larger than templateWindowSize.
@param blockMatchingStep1 Block matching threshold for the first step of BM3D (hard thresholding),
i.e. maximum distance for which two blocks are considered similar.
Value expressed in euclidean distance.
@param blockMatchingStep2 Block matching threshold for the second step of BM3D (Wiener filtering),
i.e. maximum distance for which two blocks are considered similar.
Value expressed in euclidean distance.
@param groupSize Maximum size of the 3D group for collaborative filtering.
@param slidingStep Sliding step to process every next reference block.
@param beta Kaiser window parameter that affects the sidelobe attenuation of the transform of the
window. Kaiser window is used in order to reduce border effects. To prevent usage of the window,
set beta to zero.
@param normType Norm used to calculate distance between blocks. L2 is slower than L1
but yields more accurate results.
@param step Step of BM3D to be executed. Possible variants are: step 1, step 2, both steps.
@param transformType Type of the orthogonal transform used in collaborative filtering step.
Currently only Haar transform is supported.
This function expected to be applied to grayscale images. Advanced usage of this function
can be manual denoising of colored image in different colorspaces.
@sa
fastNlMeansDenoising
*/
CV_EXPORTS_W void bm3dDenoising(
InputArray src,
InputOutputArray dstStep1,
OutputArray dstStep2,
float h = 1,
int templateWindowSize = 4,
int searchWindowSize = 16,
int blockMatchingStep1 = 2500,
int blockMatchingStep2 = 400,
int groupSize = 8,
int slidingStep = 1,
float beta = 2.0f,
int normType = cv::NORM_L2,
int step = cv::xphoto::BM3D_STEPALL,
int transformType = cv::xphoto::HAAR);
/** @brief Performs image denoising using the Block-Matching and 3D-filtering algorithm
<http://www.cs.tut.fi/~foi/GCF-BM3D/BM3D_TIP_2007.pdf> with several computational
optimizations. Noise expected to be a gaussian white noise.
@param src Input 8-bit or 16-bit 1-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 templateWindowSize Size in pixels of the template patch that is used for block-matching.
Should be power of 2.
@param searchWindowSize Size in pixels of the window that is used to perform block-matching.
Affect performance linearly: greater searchWindowsSize - greater denoising time.
Must be larger than templateWindowSize.
@param blockMatchingStep1 Block matching threshold for the first step of BM3D (hard thresholding),
i.e. maximum distance for which two blocks are considered similar.
Value expressed in euclidean distance.
@param blockMatchingStep2 Block matching threshold for the second step of BM3D (Wiener filtering),
i.e. maximum distance for which two blocks are considered similar.
Value expressed in euclidean distance.
@param groupSize Maximum size of the 3D group for collaborative filtering.
@param slidingStep Sliding step to process every next reference block.
@param beta Kaiser window parameter that affects the sidelobe attenuation of the transform of the
window. Kaiser window is used in order to reduce border effects. To prevent usage of the window,
set beta to zero.
@param normType Norm used to calculate distance between blocks. L2 is slower than L1
but yields more accurate results.
@param step Step of BM3D to be executed. Allowed are only BM3D_STEP1 and BM3D_STEPALL.
BM3D_STEP2 is not allowed as it requires basic estimate to be present.
@param transformType Type of the orthogonal transform used in collaborative filtering step.
Currently only Haar transform is supported.
This function expected to be applied to grayscale images. Advanced usage of this function
can be manual denoising of colored image in different colorspaces.
@sa
fastNlMeansDenoising
*/
CV_EXPORTS_W void bm3dDenoising(
InputArray src,
OutputArray dst,
float h = 1,
int templateWindowSize = 4,
int searchWindowSize = 16,
int blockMatchingStep1 = 2500,
int blockMatchingStep2 = 400,
int groupSize = 8,
int slidingStep = 1,
float beta = 2.0f,
int normType = cv::NORM_L2,
int step = cv::xphoto::BM3D_STEPALL,
int transformType = cv::xphoto::HAAR);
//! @}
}
}
#endif // __OPENCV_BM3D_IMAGE_DENOISING_HPP__
@@ -0,0 +1,79 @@
/*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-2011, 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_DCT_IMAGE_DENOISING_HPP__
#define __OPENCV_DCT_IMAGE_DENOISING_HPP__
/** @file
@date Jun 26, 2014
@author Yury Gitman
*/
#include <opencv2/core.hpp>
namespace cv
{
namespace xphoto
{
//! @addtogroup xphoto
//! @{
/** @brief The function implements simple dct-based denoising
<http://www.ipol.im/pub/art/2011/ys-dct/>.
@param src source image
@param dst destination image
@param sigma expected noise standard deviation
@param psize size of block side where dct is computed
@sa
fastNlMeansDenoising
*/
CV_EXPORTS_W void dctDenoising(const Mat &src, Mat &dst, const double sigma, const int psize = 16);
//! @}
}
}
#endif // __OPENCV_DCT_IMAGE_DENOISING_HPP__
@@ -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
// (3-clause BSD License)
//
// Copyright (C) 2000-2019, Intel Corporation, all rights reserved.
// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
// Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved.
// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
// Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved.
// Copyright (C) 2015-2016, Itseez 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.
//
// * Neither the names of the copyright holders nor the names of the contributors
// may 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_INPAINTING_HPP__
#define __OPENCV_INPAINTING_HPP__
/** @file
@date Jul 22, 2014
@author Yury Gitman
*/
#include <opencv2/core.hpp>
namespace cv
{
namespace xphoto
{
//! @addtogroup xphoto
//! @{
//! @brief Various inpainting algorithms
//! @sa inpaint
enum InpaintTypes
{
/** This algorithm searches for dominant correspondences (transformations) of
image patches and tries to seamlessly fill-in the area to be inpainted using this
transformations */
INPAINT_SHIFTMAP = 0,
/** Performs Frequency Selective Reconstruction (FSR).
One of the two quality profiles BEST and FAST can be chosen, depending on the time available for reconstruction.
See @cite GenserPCS2018 and @cite SeilerTIP2015 for details.
The algorithm may be utilized for the following areas of application:
1. %Error Concealment (Inpainting).
The sampling mask indicates the missing pixels of the distorted input
image to be reconstructed.
2. Non-Regular Sampling.
For more information on how to choose a good sampling mask, please review
@cite GroscheICIP2018 and @cite GroscheIST2018.
1-channel grayscale or 3-channel BGR image are accepted.
Conventional accepted ranges:
- 0-255 for CV_8U
- 0-65535 for CV_16U
- 0-1 for CV_32F/CV_64F.
*/
INPAINT_FSR_BEST = 1,
INPAINT_FSR_FAST = 2 //!< See #INPAINT_FSR_BEST
};
/** @brief The function implements different single-image inpainting algorithms.
See the original papers @cite He2012 (Shiftmap) or @cite GenserPCS2018 and @cite SeilerTIP2015 (FSR) for details.
@param src source image
- #INPAINT_SHIFTMAP: it could be of any type and any number of channels from 1 to 4. In case of
3- and 4-channels images the function expect them in CIELab colorspace or similar one, where first
color component shows intensity, while second and third shows colors. Nonetheless you can try any
colorspaces.
- #INPAINT_FSR_BEST or #INPAINT_FSR_FAST: 1-channel grayscale or 3-channel BGR image.
@param mask mask (#CV_8UC1), where non-zero pixels indicate valid image area, while zero pixels
indicate area to be inpainted
@param dst destination image
@param algorithmType see xphoto::InpaintTypes
*/
CV_EXPORTS_W void inpaint(const Mat &src, const Mat &mask, Mat &dst, const int algorithmType);
//! @}
}
}
#endif // __OPENCV_INPAINTING_HPP__
@@ -0,0 +1,41 @@
// 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_OIL_PAINTING_HPP__
#define __OPENCV_OIL_PAINTING_HPP__
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
namespace cv
{
namespace xphoto
{
//! @addtogroup xphoto
//! @{
/** @brief oilPainting
See the book @cite Holzmann1988 for details.
@param src Input three-channel or one channel image (either CV_8UC3 or CV_8UC1)
@param dst Output image of the same size and type as src.
@param size neighbouring size is 2-size+1
@param dynRatio image is divided by dynRatio before histogram processing
@param code color space conversion code(see ColorConversionCodes). Histogram will used only first plane
*/
CV_EXPORTS_W void oilPainting(InputArray src, OutputArray dst, int size, int dynRatio, int code);
/** @brief oilPainting
See the book @cite Holzmann1988 for details.
@param src Input three-channel or one channel image (either CV_8UC3 or CV_8UC1)
@param dst Output image of the same size and type as src.
@param size neighbouring size is 2-size+1
@param dynRatio image is divided by dynRatio before histogram processing
*/
CV_EXPORTS_W void oilPainting(InputArray src, OutputArray dst, int size, int dynRatio);
//! @}
}
}
#endif // __OPENCV_OIL_PAINTING_HPP__
@@ -0,0 +1,56 @@
// 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_XPHOTO_TONEMAP_HPP
#define OPENCV_XPHOTO_TONEMAP_HPP
#include "opencv2/photo.hpp"
namespace cv { namespace xphoto {
//! @addtogroup xphoto
//! @{
/** @brief This algorithm decomposes image into two layers: base layer and detail layer using bilateral filter
and compresses contrast of the base layer thus preserving all the details.
This implementation uses regular bilateral filter from OpenCV.
Saturation enhancement is possible as in cv::TonemapDrago.
For more information see @cite DD02 .
*/
class CV_EXPORTS_W TonemapDurand : public Tonemap
{
public:
CV_WRAP virtual float getSaturation() const = 0;
CV_WRAP virtual void setSaturation(float saturation) = 0;
CV_WRAP virtual float getContrast() const = 0;
CV_WRAP virtual void setContrast(float contrast) = 0;
CV_WRAP virtual float getSigmaSpace() const = 0;
CV_WRAP virtual void setSigmaSpace(float sigma_space) = 0;
CV_WRAP virtual float getSigmaColor() const = 0;
CV_WRAP virtual void setSigmaColor(float sigma_color) = 0;
};
/** @brief Creates TonemapDurand object
You need to set the OPENCV_ENABLE_NONFREE option in cmake to use those. Use them at your own risk.
@param gamma gamma value for gamma correction. See createTonemap
@param contrast resulting contrast on logarithmic scale, i. e. log(max / min), where max and min
are maximum and minimum luminance values of the resulting image.
@param saturation saturation enhancement value. See createTonemapDrago
@param sigma_color bilateral filter sigma in color space
@param sigma_space bilateral filter sigma in coordinate space
*/
CV_EXPORTS_W Ptr<TonemapDurand>
createTonemapDurand(float gamma = 1.0f, float contrast = 4.0f, float saturation = 1.0f, float sigma_color = 2.0f, float sigma_space = 2.0f);
}} // namespace
#endif // OPENCV_XPHOTO_TONEMAP_HPP
@@ -0,0 +1,230 @@
/*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-2011, 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_SIMPLE_COLOR_BALANCE_HPP__
#define __OPENCV_SIMPLE_COLOR_BALANCE_HPP__
/** @file
@date Jun 26, 2014
@author Yury Gitman
*/
#include <opencv2/core.hpp>
namespace cv
{
namespace xphoto
{
//! @addtogroup xphoto
//! @{
/** @brief The base class for auto white balance algorithms.
*/
class CV_EXPORTS_W WhiteBalancer : public Algorithm
{
public:
/** @brief Applies white balancing to the input image
@param src Input image
@param dst White balancing result
@sa cvtColor, equalizeHist
*/
CV_WRAP virtual void balanceWhite(InputArray src, OutputArray dst) = 0;
};
/** @brief A simple white balance algorithm that works by independently stretching
each of the input image channels to the specified range. For increased robustness
it ignores the top and bottom \f$p\%\f$ of pixel values.
*/
class CV_EXPORTS_W SimpleWB : public WhiteBalancer
{
public:
/** @brief Input image range minimum value
@see setInputMin */
CV_WRAP virtual float getInputMin() const = 0;
/** @copybrief getInputMin @see getInputMin */
CV_WRAP virtual void setInputMin(float val) = 0;
/** @brief Input image range maximum value
@see setInputMax */
CV_WRAP virtual float getInputMax() const = 0;
/** @copybrief getInputMax @see getInputMax */
CV_WRAP virtual void setInputMax(float val) = 0;
/** @brief Output image range minimum value
@see setOutputMin */
CV_WRAP virtual float getOutputMin() const = 0;
/** @copybrief getOutputMin @see getOutputMin */
CV_WRAP virtual void setOutputMin(float val) = 0;
/** @brief Output image range maximum value
@see setOutputMax */
CV_WRAP virtual float getOutputMax() const = 0;
/** @copybrief getOutputMax @see getOutputMax */
CV_WRAP virtual void setOutputMax(float val) = 0;
/** @brief Percent of top/bottom values to ignore
@see setP */
CV_WRAP virtual float getP() const = 0;
/** @copybrief getP @see getP */
CV_WRAP virtual void setP(float val) = 0;
};
/** @brief Creates an instance of SimpleWB
*/
CV_EXPORTS_W Ptr<SimpleWB> createSimpleWB();
/** @brief Gray-world white balance algorithm
This algorithm scales the values of pixels based on a
gray-world assumption which states that the average of all channels
should result in a gray image.
It adds a modification which thresholds pixels based on their
saturation value and only uses pixels below the provided threshold in
finding average pixel values.
Saturation is calculated using the following for a 3-channel RGB image per
pixel I and is in the range [0, 1]:
\f[ \texttt{Saturation} [I] = \frac{\textrm{max}(R,G,B) - \textrm{min}(R,G,B)
}{\textrm{max}(R,G,B)} \f]
A threshold of 1 means that all pixels are used to white-balance, while a
threshold of 0 means no pixels are used. Lower thresholds are useful in
white-balancing saturated images.
Currently supports images of type @ref CV_8UC3 and @ref CV_16UC3.
*/
class CV_EXPORTS_W GrayworldWB : public WhiteBalancer
{
public:
/** @brief Maximum saturation for a pixel to be included in the
gray-world assumption
@see setSaturationThreshold */
CV_WRAP virtual float getSaturationThreshold() const = 0;
/** @copybrief getSaturationThreshold @see getSaturationThreshold */
CV_WRAP virtual void setSaturationThreshold(float val) = 0;
};
/** @brief Creates an instance of GrayworldWB
*/
CV_EXPORTS_W Ptr<GrayworldWB> createGrayworldWB();
/** @brief More sophisticated learning-based automatic white balance algorithm.
As @ref GrayworldWB, this algorithm works by applying different gains to the input
image channels, but their computation is a bit more involved compared to the
simple gray-world assumption. More details about the algorithm can be found in
@cite Cheng2015 .
To mask out saturated pixels this function uses only pixels that satisfy the
following condition:
\f[ \frac{\textrm{max}(R,G,B)}{\texttt{range_max_val}} < \texttt{saturation_thresh} \f]
Currently supports images of type @ref CV_8UC3 and @ref CV_16UC3.
*/
class CV_EXPORTS_W LearningBasedWB : public WhiteBalancer
{
public:
/** @brief Implements the feature extraction part of the algorithm.
In accordance with @cite Cheng2015 , computes the following features for the input image:
1. Chromaticity of an average (R,G,B) tuple
2. Chromaticity of the brightest (R,G,B) tuple (while ignoring saturated pixels)
3. Chromaticity of the dominant (R,G,B) tuple (the one that has the highest value in the RGB histogram)
4. Mode of the chromaticity palette, that is constructed by taking 300 most common colors according to
the RGB histogram and projecting them on the chromaticity plane. Mode is the most high-density point
of the palette, which is computed by a straightforward fixed-bandwidth kernel density estimator with
a Epanechnikov kernel function.
@param src Input three-channel image (BGR color space is assumed).
@param dst An array of four (r,g) chromaticity tuples corresponding to the features listed above.
*/
CV_WRAP virtual void extractSimpleFeatures(InputArray src, OutputArray dst) = 0;
/** @brief Maximum possible value of the input image (e.g. 255 for 8 bit images,
4095 for 12 bit images)
@see setRangeMaxVal */
CV_WRAP virtual int getRangeMaxVal() const = 0;
/** @copybrief getRangeMaxVal @see getRangeMaxVal */
CV_WRAP virtual void setRangeMaxVal(int val) = 0;
/** @brief Threshold that is used to determine saturated pixels, i.e. pixels where at least one of the
channels exceeds \f$\texttt{saturation_threshold}\times\texttt{range_max_val}\f$ are ignored.
@see setSaturationThreshold */
CV_WRAP virtual float getSaturationThreshold() const = 0;
/** @copybrief getSaturationThreshold @see getSaturationThreshold */
CV_WRAP virtual void setSaturationThreshold(float val) = 0;
/** @brief Defines the size of one dimension of a three-dimensional RGB histogram that is used internally
by the algorithm. It often makes sense to increase the number of bins for images with higher bit depth
(e.g. 256 bins for a 12 bit image).
@see setHistBinNum */
CV_WRAP virtual int getHistBinNum() const = 0;
/** @copybrief getHistBinNum @see getHistBinNum */
CV_WRAP virtual void setHistBinNum(int val) = 0;
};
/** @brief Creates an instance of LearningBasedWB
@param path_to_model Path to a .yml file with the model. If not specified, the default model is used
*/
CV_EXPORTS_W Ptr<LearningBasedWB> createLearningBasedWB(const String& path_to_model = String());
/** @brief Implements an efficient fixed-point approximation for applying channel gains, which is
the last step of multiple white balance algorithms.
@param src Input three-channel image in the BGR color space (either CV_8UC3 or CV_16UC3)
@param dst Output image of the same size and type as src.
@param gainB gain for the B channel
@param gainG gain for the G channel
@param gainR gain for the R channel
*/
CV_EXPORTS_W void applyChannelGains(InputArray src, OutputArray dst, float gainB, float gainG, float gainR);
//! @}
}
}
#endif // __OPENCV_SIMPLE_COLOR_BALANCE_HPP__
+5
View File
@@ -0,0 +1,5 @@
{
"AdditionalImports" : {
"*" : [ "\"xphoto.hpp\"" ]
}
}
+34
View File
@@ -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 tuple<Size, float> Size_WBThresh_t;
typedef perf::TestBaseWithParam<Size_WBThresh_t> Size_WBThresh;
PERF_TEST_P( Size_WBThresh, autowbGrayworld,
testing::Combine(
SZ_ALL_HD,
testing::Values( 0.1, 0.5, 1.0 )
)
)
{
Size size = get<0>(GetParam());
float wb_thresh = get<1>(GetParam());
Mat src(size, CV_8UC3);
Mat dst(size, CV_8UC3);
declare.in(src, WARMUP_RNG).out(dst);
Ptr<xphoto::GrayworldWB> wb = xphoto::createGrayworldWB();
wb->setSaturationThreshold(wb_thresh);
TEST_CYCLE() wb->balanceWhite(src, dst);
SANITY_CHECK(dst);
}
}} // namespace
@@ -0,0 +1,40 @@
// 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 tuple<Size, MatType> learningBasedWBParams;
typedef perf::TestBaseWithParam<learningBasedWBParams> learningBasedWBPerfTest;
PERF_TEST_P(learningBasedWBPerfTest, perf, Combine(SZ_ALL_HD, Values(CV_8UC3, CV_16UC3)))
{
Size size = get<0>(GetParam());
MatType t = get<1>(GetParam());
Mat src(size, t);
Mat dst(size, t);
int range_max_val = 255, hist_bin_num = 64;
if (t == CV_16UC3)
{
range_max_val = 65535;
hist_bin_num = 256;
}
Mat src_dscl(Size(size.width / 16, size.height / 16), t);
RNG rng(1234);
rng.fill(src_dscl, RNG::UNIFORM, 0, range_max_val);
resize(src_dscl, src, src.size(), 0, 0, INTER_LINEAR_EXACT);
Ptr<xphoto::LearningBasedWB> wb = xphoto::createLearningBasedWB();
wb->setRangeMaxVal(range_max_val);
wb->setSaturationThreshold(0.98f);
wb->setHistBinNum(hist_bin_num);
TEST_CYCLE() wb->balanceWhite(src, dst);
SANITY_CHECK_NOTHING();
}
}} // namespace
+6
View File
@@ -0,0 +1,6 @@
// 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"
CV_PERF_TEST_MAIN(xphoto)
+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.
#ifndef __OPENCV_PERF_PRECOMP_HPP__
#define __OPENCV_PERF_PRECOMP_HPP__
#include "opencv2/ts.hpp"
#include "opencv2/xphoto.hpp"
#endif
@@ -0,0 +1,73 @@
#include "opencv2/xphoto.hpp"
#include "opencv2/highgui.hpp"
const char* keys =
{
"{i || input image name}"
"{o || output image name}"
"{sigma || expected noise standard deviation}"
"{tw |4| template window size}"
"{sw |16| search window size}"
};
int main(int argc, const char** argv)
{
bool printHelp = (argc == 1);
printHelp = printHelp || (argc == 2 && std::string(argv[1]) == "--help");
printHelp = printHelp || (argc == 2 && std::string(argv[1]) == "-h");
if (printHelp)
{
printf("\nThis sample demonstrates BM3D image denoising\n"
"Call:\n"
" bm3d_image_denoising -i=<string> -sigma=<double> -tw=<int> -sw=<int> [-o=<string>]\n\n");
return 0;
}
cv::CommandLineParser parser(argc, argv, keys);
if (!parser.check())
{
parser.printErrors();
return -1;
}
std::string inFilename = parser.get<std::string>("i");
std::string outFilename = parser.get<std::string>("o");
cv::Mat src = cv::imread(inFilename, cv::IMREAD_GRAYSCALE);
if (src.empty())
{
printf("Cannot read image file: %s\n", inFilename.c_str());
return -1;
}
float sigma = parser.get<float>("sigma");
if (sigma == 0.0)
sigma = 15.0;
int templateWindowSize = parser.get<int>("tw");
if (templateWindowSize == 0)
templateWindowSize = 4;
int searchWindowSize = parser.get<int>("sw");
if (searchWindowSize == 0)
searchWindowSize = 16;
cv::Mat res(src.size(), src.type());
cv::xphoto::bm3dDenoising(src, res, sigma, templateWindowSize, searchWindowSize);
if (outFilename.empty())
{
cv::namedWindow("input image", cv::WINDOW_NORMAL);
cv::imshow("input image", src);
cv::namedWindow("denoising result", cv::WINDOW_NORMAL);
cv::imshow("denoising result", res);
cv::waitKey(0);
}
else
{
cv::imwrite(outFilename, res);
}
return 0;
}
+68
View File
@@ -0,0 +1,68 @@
#include "opencv2/xphoto.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
using namespace std;
const char *keys = { "{help h usage ? | | print this message}"
"{i | | input image name }"
"{o | | output image name }"
"{a |grayworld| color balance algorithm (simple, grayworld or learning_based)}"
"{m | | path to the model for the learning-based algorithm (optional) }" };
int main(int argc, const char **argv)
{
CommandLineParser parser(argc, argv, keys);
parser.about("OpenCV color balance demonstration sample");
if (parser.has("help") || argc < 2)
{
parser.printMessage();
return 0;
}
string inFilename = parser.get<string>("i");
string outFilename = parser.get<string>("o");
string algorithm = parser.get<string>("a");
string modelFilename = parser.get<string>("m");
if (!parser.check())
{
parser.printErrors();
return -1;
}
Mat src = imread(inFilename, 1);
if (src.empty())
{
printf("Cannot read image file: %s\n", inFilename.c_str());
return -1;
}
Mat res;
Ptr<xphoto::WhiteBalancer> wb;
if (algorithm == "simple")
wb = xphoto::createSimpleWB();
else if (algorithm == "grayworld")
wb = xphoto::createGrayworldWB();
else if (algorithm == "learning_based")
wb = xphoto::createLearningBasedWB(modelFilename);
else
{
printf("Unsupported algorithm: %s\n", algorithm.c_str());
return -1;
}
wb->balanceWhite(src, res);
if (outFilename == "")
{
namedWindow("after white balance", 1);
imshow("after white balance", res);
waitKey(0);
}
else
imwrite(outFilename, res);
return 0;
}
@@ -0,0 +1,268 @@
#!/usr/bin/env python
from __future__ import print_function
import os, sys, argparse, json
import numpy as np
import scipy.io
import cv2 as cv
import timeit
from learn_color_balance import load_ground_truth
def load_json(path):
f = open(path, "r")
data = json.load(f)
return data
def save_json(obj, path):
tmp_file = path + ".bak"
f = open(tmp_file, "w")
json.dump(obj, f, indent=2)
f.flush()
os.fsync(f.fileno())
f.close()
try:
os.rename(tmp_file, path)
except:
os.remove(path)
os.rename(tmp_file, path)
def parse_sequence(input_str):
if len(input_str) == 0:
return []
else:
return [o.strip() for o in input_str.split(",") if o]
def stretch_to_8bit(arr, clip_percentile = 2.5):
arr = np.clip(arr * (255.0 / np.percentile(arr, 100 - clip_percentile)), 0, 255)
return arr.astype(np.uint8)
def evaluate(im, algo, gt_illuminant, i, range_thresh, bin_num, dst_folder, model_folder):
new_im = None
start_time = timeit.default_timer()
if algo=="grayworld":
inst = cv.xphoto.createGrayworldWB()
inst.setSaturationThreshold(0.95)
new_im = inst.balanceWhite(im)
elif algo=="nothing":
new_im = im
elif algo.split(":")[0]=="learning_based":
model_path = ""
if len(algo.split(":"))>1:
model_path = os.path.join(model_folder, algo.split(":")[1])
inst = cv.xphoto.createLearningBasedWB(model_path)
inst.setRangeMaxVal(range_thresh)
inst.setSaturationThreshold(0.98)
inst.setHistBinNum(bin_num)
new_im = inst.balanceWhite(im)
elif algo=="GT":
gains = gt_illuminant / min(gt_illuminant)
g1 = float(1.0 / gains[2])
g2 = float(1.0 / gains[1])
g3 = float(1.0 / gains[0])
new_im = cv.xphoto.applyChannelGains(im, g1, g2, g3)
time = 1000*(timeit.default_timer() - start_time) #time in ms
if len(dst_folder)>0:
if not os.path.exists(dst_folder):
os.makedirs(dst_folder)
im_name = ("%04d_" % i) + algo.replace(":","_") + ".jpg"
cv.imwrite(os.path.join(dst_folder, im_name), stretch_to_8bit(new_im))
#recover the illuminant from the color balancing result, assuming the standard model:
estimated_illuminant = [0, 0, 0]
eps = 0.01
estimated_illuminant[2] = np.percentile((im[:,:,0] + eps) / (new_im[:,:,0] + eps), 50)
estimated_illuminant[1] = np.percentile((im[:,:,1] + eps) / (new_im[:,:,1] + eps), 50)
estimated_illuminant[0] = np.percentile((im[:,:,2] + eps) / (new_im[:,:,2] + eps), 50)
res = np.arccos(np.dot(gt_illuminant,estimated_illuminant)/
(np.linalg.norm(gt_illuminant) * np.linalg.norm(estimated_illuminant)))
return (time, (res / np.pi) * 180)
def build_html_table(out, state, stat_list, img_range):
stat_dict = {'mean': ('Mean error', lambda arr: np.mean(arr)),
'median': ('Median error',lambda arr: np.percentile(arr, 50)),
'p05': ('5<sup>th</sup> percentile',lambda arr: np.percentile(arr, 5)),
'p20': ('20<sup>th</sup> percentile',lambda arr: np.percentile(arr, 20)),
'p80': ('80<sup>th</sup> percentile',lambda arr: np.percentile(arr, 80)),
'p95': ('95<sup>th</sup> percentile',lambda arr: np.percentile(arr, 95))
}
html_out = ['<style type="text/css">\n',
' html, body {font-family: Lucida Console, Courier New, Courier;font-size: 16px;color:#3e4758;}\n',
' .tbl{background:none repeat scroll 0 0 #FFFFFF;border-collapse:collapse;font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;font-size:14px;margin:20px;text-align:left;width:480px;margin-left: auto;margin-right: auto;white-space:nowrap;}\n',
' .tbl span{display:block;white-space:nowrap;}\n',
' .tbl thead tr:last-child th {padding-bottom:5px;}\n',
' .tbl tbody tr:first-child td {border-top:3px solid #6678B1;}\n',
' .tbl th{border:none;color:#003399;font-size:16px;font-weight:normal;white-space:nowrap;padding:3px 10px;}\n',
' .tbl td{border:none;border-bottom:1px solid #CCCCCC;color:#666699;padding:6px 8px;white-space:nowrap;}\n',
' .tbl tbody tr:hover td{color:#000099;}\n',
' .tbl caption{font:italic 16px "Trebuchet MS",Verdana,Arial,Helvetica,sans-serif;padding:0 0 5px;text-align:right;white-space:normal;}\n',
' .firstingroup {border-top:2px solid #6678B1;}\n',
'</style>\n\n']
html_out += ['<table class="tbl">\n',
' <thead>\n',
' <tr>\n',
' <th align="center" valign="top"> Algorithm Name </th>\n',
' <th align="center" valign="top"> Average Time </th>\n']
for stat in stat_list:
if stat not in stat_dict.keys():
print("Error: unsupported statistic " + stat)
sys.exit(1)
html_out += [' <th align="center" valign="top"> ' +
stat_dict[stat][0] +
' </th>\n']
html_out += [' </tr>\n',
' </thead>\n',
' <tbody>\n']
for algorithm in state.keys():
arr = [state[algorithm][file]["angular_error"] for file in state[algorithm].keys() if file>=img_range[0] and file<=img_range[1]]
average_time = "%.2f ms" % np.mean([state[algorithm][file]["time"] for file in state[algorithm].keys()
if file>=img_range[0] and file<=img_range[1]])
html_out += [' <tr>\n',
' <td>' + algorithm + '</td>\n',
' <td>' + average_time + '</td>\n']
for stat in stat_list:
html_out += [' <td> ' +
"%.2f&deg" % stat_dict[stat][1](arr) +
' </td>\n']
html_out += [' </tr>\n']
html_out += [' </tbody>\n',
'</table>\n']
f = open(out, 'w')
f.writelines(html_out)
f.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description=("A benchmarking script for color balance algorithms"),
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
"-a",
"--algorithms",
metavar="ALGORITHMS",
default="",
help=("Comma-separated list of color balance algorithms to evaluate. "
"Currently available: GT,learning_based,grayworld,nothing. "
"Use a colon to set a specific model for the learning-based "
"algorithm, e.g. learning_based:model1.yml,learning_based:model2.yml"))
parser.add_argument(
"-i",
"--input_folder",
metavar="INPUT_FOLDER",
default="",
help=("Folder containing input images to evaluate on. Assumes minimally "
"processed png images like in the Gehler-Shi (http://www.cs.sfu.ca/~colour/data/shi_gehler/) "
"or NUS 8-camera (http://www.comp.nus.edu.sg/~whitebal/illuminant/illuminant.html) datasets"))
parser.add_argument(
"-g",
"--ground_truth",
metavar="GROUND_TRUTH",
default="real_illum_568..mat",
help=("Path to the mat file containing ground truth illuminations. Currently "
"supports formats supplied by the Gehler-Shi and NUS 8-camera datasets."))
parser.add_argument(
"-o",
"--out",
metavar="OUT",
default="./white_balance_eval_result.html",
help="Path to the output html table")
parser.add_argument(
"-s",
"--state",
metavar="STATE_JSON",
default="./WB_evaluation_state.json",
help=("Path to a json file that stores the current evaluation state"))
parser.add_argument(
"-t",
"--stats",
metavar="STATS",
default="mean,median,p05,p20,p80,p95",
help=("Comma-separated list of error statistics to compute and list "
"in the output table. All the available ones are used by default"))
parser.add_argument(
"-b",
"--input_bit_depth",
metavar="INPUT_BIT_DEPTH",
default="",
help=("Assumed bit depth for input images. Should be specified in order to "
"use full bit depth for evaluation (for instance, -b 12 for 12 bit images). "
"Otherwise, input images are converted to 8 bit prior to the evaluation."))
parser.add_argument(
"-d",
"--dst_folder",
metavar="DST_FOLDER",
default="",
help=("If specified, this folder will be used to store the color correction results"))
parser.add_argument(
"-r",
"--range",
metavar="RANGE",
default="0,0",
help=("Comma-separated range of images from the dataset to evaluate on (for instance: 0,568). "
"All available images are used by default."))
parser.add_argument(
"-m",
"--model_folder",
metavar="MODEL_FOLDER",
default="",
help=("Path to the folder containing models for the learning-based color balance algorithm (optional)"))
args, other_args = parser.parse_known_args()
if not os.path.exists(args.input_folder):
print("Error: " + args.input_folder + (" does not exist. Please, correctly "
"specify the -i parameter"))
sys.exit(1)
if not os.path.exists(args.ground_truth):
print("Error: " + args.ground_truth + (" does not exist. Please, correctly "
"specify the -g parameter"))
sys.exit(1)
state = {}
if os.path.isfile(args.state):
state = load_json(args.state)
algorithm_list = parse_sequence(args.algorithms)
img_range = list(map(int, parse_sequence(args.range)))
if len(img_range)!=2:
print("Error: Please specify the -r parameter in form <first_image_index>,<last_image_index>")
sys.exit(1)
img_files = sorted(os.listdir(args.input_folder))
(gt_illuminants,black_levels) = load_ground_truth(args.ground_truth)
for algorithm in algorithm_list:
i = 0
if algorithm not in state.keys():
state[algorithm] = {}
sz = len(img_files)
for file in img_files:
if file not in state[algorithm].keys() and\
((i>=img_range[0] and i<img_range[1]) or img_range[0]==img_range[1]==0):
cur_path = os.path.join(args.input_folder, file)
im = cv.imread(cur_path, -1).astype(np.float32)
im -= black_levels[i]
range_thresh = 255
if len(args.input_bit_depth)>0:
range_thresh = 2**int(args.input_bit_depth) - 1
im = np.clip(im, 0, range_thresh).astype(np.uint16)
else:
im = stretch_to_8bit(im)
(time,angular_err) = evaluate(im, algorithm, gt_illuminants[i], i, range_thresh,
256 if range_thresh > 255 else 64, args.dst_folder, args.model_folder)
state[algorithm][file] = {"angular_error": angular_err, "time": time}
sys.stdout.write("Algorithm: %-20s Done: [%3d/%3d]\r" % (algorithm, i, sz)),
sys.stdout.flush()
save_json(state, args.state)
i+=1
save_json(state, args.state)
build_html_table(args.out, state, parse_sequence(args.stats), [img_files[img_range[0]], img_files[img_range[1]-1]])
@@ -0,0 +1,69 @@
#include "opencv2/xphoto.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/core/utility.hpp"
const char* keys =
{
"{i || input image name}"
"{o || output image name}"
"{sigma || expected noise standard deviation}"
"{psize |16| expected noise standard deviation}"
};
int main( int argc, const char** argv )
{
bool printHelp = ( argc == 1 );
printHelp = printHelp || ( argc == 2 && std::string(argv[1]) == "--help" );
printHelp = printHelp || ( argc == 2 && std::string(argv[1]) == "-h" );
if ( printHelp )
{
printf("\nThis sample demonstrates dct-based image denoising\n"
"Call:\n"
" dct_image_denoising -i=<string> -sigma=<double> -psize=<int> [-o=<string>]\n\n");
return 0;
}
cv::CommandLineParser parser(argc, argv, keys);
if ( !parser.check() )
{
parser.printErrors();
return -1;
}
std::string inFilename = parser.get<std::string>("i");
std::string outFilename = parser.get<std::string>("o");
cv::Mat src = cv::imread(inFilename, 1);
if ( src.empty() )
{
printf("Cannot read image file: %s\n", inFilename.c_str());
return -1;
}
double sigma = parser.get<double>("sigma");
if (sigma == 0.0)
sigma = 15.0;
int psize = parser.get<int>("psize");
if (psize == 0)
psize = 16;
cv::Mat res(src.size(), src.type());
cv::xphoto::dctDenoising(src, res, sigma, psize);
if ( outFilename == "" )
{
cv::namedWindow("denoising result", 1);
cv::imshow("denoising result", res);
cv::waitKey(0);
}
else
cv::imwrite(outFilename, res);
return 0;
}
+78
View File
@@ -0,0 +1,78 @@
#include "opencv2/xphoto.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <ctime>
#include <iostream>
const char* keys =
{
"{i || input image name}"
"{m || mask image name}"
"{o || output image name}"
};
int main( int argc, const char** argv )
{
bool printHelp = ( argc == 1 );
printHelp = printHelp || ( argc == 2 && std::string(argv[1]) == "--help" );
printHelp = printHelp || ( argc == 2 && std::string(argv[1]) == "-h" );
if ( printHelp )
{
printf("\nThis sample demonstrates shift-map image inpainting\n"
"Call:\n"
" inpainting -i=<string> -m=<string> [-o=<string>]\n\n");
return 0;
}
cv::CommandLineParser parser(argc, argv, keys);
if ( !parser.check() )
{
parser.printErrors();
return -1;
}
std::string inFilename = parser.get<std::string>("i");
std::string maskFilename = parser.get<std::string>("m");
std::string outFilename = parser.get<std::string>("o");
cv::Mat src = cv::imread(inFilename, cv::IMREAD_UNCHANGED);
if ( src.empty() )
{
printf( "Cannot read image file: %s\n", inFilename.c_str() );
return -1;
}
cv::cvtColor(src, src, cv::COLOR_BGR2Lab);
cv::Mat mask = cv::imread(maskFilename, cv::IMREAD_GRAYSCALE);
if ( mask.empty() )
{
printf( "Cannot read image file: %s\n", maskFilename.c_str() );
return -1;
}
cv::threshold(mask, mask, 128, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
cv::Mat res(src.size(), src.type());
int time = clock();
cv::xphoto::inpaint( src, mask, res, cv::xphoto::INPAINT_SHIFTMAP );
std::cout << "time = " << (clock() - time)
/ double(CLOCKS_PER_SEC) << std::endl;
cv::cvtColor(res, res, cv::COLOR_Lab2BGR);
if ( outFilename == "" )
{
cv::namedWindow("inpainting result", 1);
cv::imshow("inpainting result", res);
cv::waitKey(0);
}
else
cv::imwrite(outFilename, res);
return 0;
}
@@ -0,0 +1,290 @@
#!/usr/bin/env python
from __future__ import print_function
import os, sys, argparse
import numpy as np
import scipy.io
from sklearn.tree import DecisionTreeRegressor
import cv2 as cv
import random
def parse_sequence(input_str):
if len(input_str) == 0:
return []
else:
return [o.strip() for o in input_str.split(",") if o]
def convert_to_8bit(arr, clip_percentile = 2.5):
arr = np.clip(arr * (255.0 / np.percentile(arr, 100 - clip_percentile)), 0, 255)
return arr.astype(np.uint8)
def learn_regression_tree_ensemble(img_features, gt_illuminants, num_trees, max_tree_depth):
eps = 0.001
inst = [[img_features[i], gt_illuminants[i][0] / (sum(gt_illuminants[i]) + eps),
gt_illuminants[i][1] / (sum(gt_illuminants[i]) + eps)] for i in range(len(img_features))]
inst.sort(key = lambda obj: obj[1]) #sort by r chromaticity
stride = int(np.ceil(len(inst) / float(num_trees+1)))
sz = 2*stride
dst_model = []
for tree_idx in range(num_trees):
#local group in the training data is additionally weighted by num_trees
local_group_range = range(tree_idx*stride, min(tree_idx*stride+sz, len(inst)))
X = num_trees * [inst[i][0] for i in local_group_range]
y_r = num_trees * [inst[i][1] for i in local_group_range]
y_g = num_trees * [inst[i][2] for i in local_group_range]
#add the rest of the training data:
X = X + [inst[i][0] for i in range(len(inst)) if i not in local_group_range]
y_r = y_r + [inst[i][1] for i in range(len(inst)) if i not in local_group_range]
y_g = y_g + [inst[i][2] for i in range(len(inst)) if i not in local_group_range]
local_model = []
for feature_idx in range(len(X[0])):
tree_r = DecisionTreeRegressor(max_depth = max_tree_depth, random_state = 1234)
tree_r.fit([el[feature_idx][0] for el in X], y_r)
tree_g = DecisionTreeRegressor(max_depth = max_tree_depth, random_state = 1234)
tree_g.fit([el[feature_idx][0] for el in X], y_g)
local_model.append([tree_r, tree_g])
dst_model.append(local_model)
return dst_model
def get_tree_node_lists(tree, tree_depth):
dst_feature_idx = (2**tree_depth-1) * [0]
dst_thresh_vals = (2**tree_depth-1) * [.5]
dst_leaf_vals = (2**tree_depth) * [-1]
leaf_idx_offset = (2**tree_depth-1)
left = tree.tree_.children_left
right = tree.tree_.children_right
threshold = tree.tree_.threshold
value = tree.tree_.value
feature = tree.tree_.feature
def recurse(left, right, threshold, feature, node, dst_idx, cur_depth):
if (threshold[node] != -2):
dst_feature_idx[dst_idx] = feature[node]
dst_thresh_vals[dst_idx] = threshold[node]
if left[node] != -1:
recurse (left, right, threshold, feature, left[node], 2*dst_idx+1, cur_depth + 1)
if right[node] != -1:
recurse (left, right, threshold, feature, right[node], 2*dst_idx+2, cur_depth + 1)
else:
range_start = 2**(tree_depth - cur_depth) * dst_idx + (2**(tree_depth - cur_depth) - 1) - leaf_idx_offset
range_end = 2**(tree_depth - cur_depth) * dst_idx + (2**(tree_depth - cur_depth+1) - 2) - leaf_idx_offset + 1
dst_leaf_vals[range_start:range_end] = (range_end - range_start) * [value[node][0][0]]
recurse(left, right, threshold, feature, 0, 0, 0)
return (dst_feature_idx, dst_thresh_vals, dst_leaf_vals)
def generate_code(model, input_params, use_YML, out_file):
feature_idx = []
thresh_vals = []
leaf_vals = []
depth = int(input_params["--max_tree_depth"])
for local_model in model:
for feature in local_model:
(local_feature_idx, local_thresh_vals, local_leaf_vals) = get_tree_node_lists(feature[0], depth)
feature_idx += local_feature_idx
thresh_vals += local_thresh_vals
leaf_vals += local_leaf_vals
(local_feature_idx, local_thresh_vals, local_leaf_vals) = get_tree_node_lists(feature[1], depth)
feature_idx += local_feature_idx
thresh_vals += local_thresh_vals
leaf_vals += local_leaf_vals
if use_YML:
fs = cv.FileStorage(out_file, 1)
fs.write("num_trees", len(model))
fs.write("num_tree_nodes", 2**depth)
fs.write("feature_idx", np.array(feature_idx).astype(np.uint8))
fs.write("thresh_vals", np.array(thresh_vals).astype(np.float32))
fs.write("leaf_vals", np.array(leaf_vals).astype(np.float32))
fs.release()
else:
res = "/* This file was automatically generated by learn_color_balance.py script\n" +\
" * using the following parameters:\n"
for key in input_params:
res += " " + key + " " + input_params[key]
res += "\n */\n"
res += "const int num_features = 4;\n"
res += "const int _num_trees = " + str(len(model)) + ";\n"
res += "const int _num_tree_nodes = " + str(2**depth) + ";\n"
res += "unsigned char _feature_idx[_num_trees*num_features*2*(_num_tree_nodes-1)] = {" + str(feature_idx[0])
for i in range(1,len(feature_idx)):
res += "," + str(feature_idx[i])
res += "};\n"
res += "float _thresh_vals[_num_trees*num_features*2*(_num_tree_nodes-1)] = {" + ("%.3ff" % thresh_vals[0])[1:]
for i in range(1,len(thresh_vals)):
res += "," + ("%.3ff" % thresh_vals[i])[1:]
res += "};\n"
res += "float _leaf_vals[_num_trees*num_features*2*_num_tree_nodes] = {" + ("%.3ff" % leaf_vals[0])[1:]
for i in range(1,len(leaf_vals)):
res += "," + ("%.3ff" % leaf_vals[i])[1:]
res += "};\n"
f = open(out_file,"w")
f.write(res)
f.close()
def load_ground_truth(gt_path):
gt = scipy.io.loadmat(gt_path)
base_gt_illuminants = []
black_levels = []
if "groundtruth_illuminants" in gt.keys() and "darkness_level" in gt.keys():
#NUS 8-camera dataset format
base_gt_illuminants = gt["groundtruth_illuminants"]
black_levels = len(base_gt_illuminants) * [gt["darkness_level"][0][0]]
elif "real_rgb" in gt.keys():
#Gehler-Shi dataset format
base_gt_illuminants = gt["real_rgb"]
black_levels = 87 * [0] + (len(base_gt_illuminants) - 87) * [129]
else:
print("Error: unknown ground-truth format, only formats of Gehler-Shi and NUS 8-camera datasets are supported")
sys.exit(1)
return (base_gt_illuminants, black_levels)
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description=("A tool for training the learning-based "
"color balance algorithm. Currently supports "
"training only on the Gehler-Shi and NUS 8-camera datasets."),
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
"-i",
"--input_folder",
metavar="INPUT_FOLDER",
default="",
help=("Folder containing the training dataset. Assumes minimally "
"processed png images like in the Gehler-Shi (http://www.cs.sfu.ca/~colour/data/shi_gehler/) "
"or NUS 8-camera (http://www.comp.nus.edu.sg/~whitebal/illuminant/illuminant.html) datasets"))
parser.add_argument(
"-g",
"--ground_truth",
metavar="GROUND_TRUTH",
default="real_illum_568..mat",
help=("Path to the mat file containing ground truth illuminations. Currently "
"supports formats supplied by the Gehler-Shi and NUS 8-camera datasets."))
parser.add_argument(
"-r",
"--range",
metavar="RANGE",
default="0,0",
help="Range of images from the input dataset to use for training")
parser.add_argument(
"-o",
"--out",
metavar="OUT",
default="color_balance_model.yml",
help="Path to the output learnt model. Either a .yml (for loading during runtime) "
"or .hpp (for compiling with the main code) file ")
parser.add_argument(
"--hist_bin_num",
metavar="HIST_BIN_NUM",
default="64",
help=("Size of one dimension of a three-dimensional RGB histogram employed in the "
"feature extraction step."))
parser.add_argument(
"--num_trees",
metavar="NUM_TREES",
default="20",
help=("Parameter to control the size of the regression tree ensemble"))
parser.add_argument(
"--max_tree_depth",
metavar="MAX_TREE_DEPTH",
default="4",
help=("Maxmimum depth of regression trees constructed during training."))
parser.add_argument(
"-a",
"--num_augmented",
metavar="NUM_AUGMENTED",
default="2",
help=("Number of augmented samples per one training image. Training set "
"augmentation tends to improve the learnt model robustness."))
args, other_args = parser.parse_known_args()
if not os.path.exists(args.input_folder):
print("Error: " + args.input_folder + (" does not exist. Please, correctly "
"specify the -i parameter"))
sys.exit(1)
if not os.path.exists(args.ground_truth):
print("Error: " + args.ground_truth + (" does not exist. Please, correctly "
"specify the -g parameter"))
sys.exit(1)
img_range = list(map(int,parse_sequence(args.range)))
if len(img_range)!=2:
print("Error: Please specify the -r parameter in form <first_image_index>,<last_image_index>")
sys.exit(1)
use_YML = None
if args.out.endswith(".yml"):
use_YML = True
elif args.out.endswith(".hpp"):
use_YML = False
else:
print("Error: Only .hpp and .yml are supported as output formats")
sys.exit(1)
hist_bin_num = int(args.hist_bin_num)
num_trees = int(args.num_trees)
max_tree_depth = int(args.max_tree_depth)
img_files = sorted(os.listdir(args.input_folder))
(base_gt_illuminants,black_levels) = load_ground_truth(args.ground_truth)
features = []
gt_illuminants = []
i=0
sz = len(img_files)
random.seed(1234)
inst = cv.xphoto.createLearningBasedWB()
inst.setRangeMaxVal(255)
inst.setSaturationThreshold(0.98)
inst.setHistBinNum(hist_bin_num)
for file in img_files:
if (i>=img_range[0] and i<img_range[1]) or (img_range[0]==img_range[1]==0):
cur_path = os.path.join(args.input_folder,file)
im = cv.imread(cur_path, -1).astype(np.float32)
im -= black_levels[i]
im_8bit = convert_to_8bit(im)
cur_img_features = inst.extractSimpleFeatures(im_8bit, None)
features.append(cur_img_features.tolist())
gt_illuminants.append(base_gt_illuminants[i].tolist())
for iter in range(int(args.num_augmented)):
R_coef = random.uniform(0.2, 5.0)
G_coef = random.uniform(0.2, 5.0)
B_coef = random.uniform(0.2, 5.0)
im_8bit = im
im_8bit[:,:,0] *= B_coef
im_8bit[:,:,1] *= G_coef
im_8bit[:,:,2] *= R_coef
im_8bit = convert_to_8bit(im)
cur_img_features = inst.extractSimpleFeatures(im_8bit, None)
features.append(cur_img_features.tolist())
illum = base_gt_illuminants[i]
illum[0] *= R_coef
illum[1] *= G_coef
illum[2] *= B_coef
gt_illuminants.append(illum.tolist())
sys.stdout.write("Computing features: [%3d/%3d]\r" % (i, sz)),
sys.stdout.flush()
i+=1
print("\nLearning the model...")
model = learn_regression_tree_ensemble(features, gt_illuminants, num_trees, max_tree_depth)
print("Writing the model...")
generate_code(model,{"-r":args.range, "--hist_bin_num": args.hist_bin_num, "--num_trees": args.num_trees,
"--max_tree_depth": args.max_tree_depth, "--num_augmented": args.num_augmented},
use_YML, args.out)
print("Done")
+103
View File
@@ -0,0 +1,103 @@
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/xphoto.hpp>
#include "opencv2/xphoto/oilpainting.hpp"
#include <iostream>
using namespace cv;
using namespace std;
static void TrackSlider(int , void *);
static void addSlider(String sliderName, String windowName, int minSlider, int maxSlider, int valDefault, int *valSlider, void(*f)(int, void *), void *r);
vector<int> colorSpace = { COLOR_BGR2GRAY,COLOR_BGR2HSV,COLOR_BGR2YUV,COLOR_BGR2XYZ };
struct OilImage {
String winName = "Oil painting";
int size;
int dynRatio;
int colorSpace;
Mat img;
};
const String keys =
"{Help h usage ? help | | Print this message }"
"{v | 0 | video index }"
"{a | 700 | API index }"
"{s | 10 | neighbouring size }"
"{d | 1 | dynamic ratio }"
"{c | 0 | color space }"
"{@arg1 | | file path}"
;
int main(int argc, char* argv[])
{
CommandLineParser parser(argc, argv, keys);
if (parser.has("help"))
{
parser.printMessage();
return 0;
}
String filename = parser.get<String>(0);
OilImage p;
p.dynRatio = parser.get<int>("d");
p.size = parser.get<int>("s");
p.colorSpace = parser.get<int>("c");
if (p.colorSpace < 0 || p.colorSpace >= static_cast<int>(colorSpace.size()))
{
std::cout << "Color space must be >= 0 and <"<< colorSpace.size()<<"\n";
return EXIT_FAILURE;
}
if (!filename.empty())
{
p.img = imread(filename);
if (p.img.empty())
{
std::cout << "Check file path!\n";
return EXIT_FAILURE;
}
Mat dst;
xphoto::oilPainting(p.img, dst, p.size, p.dynRatio, colorSpace[p.colorSpace]);
imshow("oil painting effect", dst);
waitKey();
return 0;
}
VideoCapture v(parser.get<int>("v")+ parser.get<int>("a"));
v>> p.img;
p.winName="Oil Painting";
namedWindow(p.winName);
addSlider("DynRatio", p.winName, 1,127,p.dynRatio,&p.dynRatio, TrackSlider, &p);
addSlider("Size", p.winName, 1, 100, p.size, &p.size, TrackSlider, &p);
addSlider("ColorSpace", p.winName, 0, static_cast<int>(colorSpace.size()-1), p.colorSpace, &p.colorSpace, TrackSlider, &p);
while (waitKey(20) != 27)
{
v>>p.img;
imshow("Original", p.img);
TrackSlider(0, &p);
waitKey(10);
}
return 0;
}
void addSlider(String sliderName, String windowName, int minSlider, int maxSlider, int valDefault, int *valSlider, void(*f)(int, void *), void *r)
{
createTrackbar(sliderName, windowName, valSlider, 1, f, r);
setTrackbarMin(sliderName, windowName, minSlider);
setTrackbarMax(sliderName, windowName, maxSlider);
setTrackbarPos(sliderName, windowName, valDefault);
}
void TrackSlider(int , void *r)
{
OilImage *p = (OilImage *)r;
Mat dst;
p->img = p->img / p->dynRatio;
p->img = p->img*p->dynRatio;
xphoto::oilPainting(p->img, dst, p->size, p->dynRatio,colorSpace[p->colorSpace]);
if (!dst.empty())
{
imshow(p->winName, dst);
}
}
+66
View File
@@ -0,0 +1,66 @@
/*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-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// * 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 __ADVANCED_TYPES_HPP__
#define __ADVANCED_TYPES_HPP__
#ifdef __cplusplus
#include <opencv2/core.hpp>
/********************* Functions *********************/
namespace cv
{
template <typename _Tp, typename _Tp2> static inline
cv::Size_<_Tp> operator * (const _Tp2 x, const cv::Size_<_Tp> &sz)
{
return cv::Size_<_Tp>(cv::saturate_cast<_Tp>(x*sz.width), cv::saturate_cast<_Tp>(x*sz.height));
}
template <typename _Tp, typename _Tp2> static inline
cv::Size_<_Tp> operator / (const cv::Size_<_Tp> &sz, const _Tp2 x)
{
return cv::Size_<_Tp>(cv::saturate_cast<_Tp>(sz.width/x), cv::saturate_cast<_Tp>(sz.height/x));
}
} // cv
#endif
#endif /* __ADVANCED_TYPES_HPP__ */
+297
View File
@@ -0,0 +1,297 @@
/*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-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// * 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 __ANNF_HPP__
#define __ANNF_HPP__
#include <vector>
#include <stack>
#include <limits>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <fstream>
#include <time.h>
#include <functional>
#include "norm2.hpp"
#include "whs.hpp"
/************************* KDTree class *************************/
template <typename ForwardIterator> void
generate_seq(ForwardIterator it, int first, int last)
{
for (int i = first; i < last; ++i, ++it)
*it = i;
}
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
template <typename Tp, int cn> class KDTree
{
private:
class KDTreeComparator
{
const KDTree <Tp, cn> *main; // main class
int dimIdx; // dimension to compare
public:
bool operator () (const int &x, const int &y) const
{
cv::Vec <Tp, cn> u = main->data[main->idx[x]];
cv::Vec <Tp, cn> v = main->data[main->idx[y]];
return u[dimIdx] < v[dimIdx];
}
KDTreeComparator(const KDTree <Tp, cn> *_main, int _dimIdx)
: main(_main), dimIdx(_dimIdx) {}
};
const int height, width;
const int leafNumber; // maximum number of point per leaf
const int zeroThresh; // radius of prohibited shifts
std::vector <cv::Vec <Tp, cn> > data;
std::vector <int> idx;
std::vector <cv::Point2i> nodes;
int getMaxSpreadN(const int left, const int right) const;
void operator =(const KDTree <Tp, cn> &) const {};
public:
void updateDist(const int leaf, const int &idx0, int &bestIdx, double &dist);
KDTree(const cv::Mat &data, const int leafNumber = 8, const int zeroThresh = 16);
~KDTree(){};
};
template <typename Tp, int cn> int KDTree <Tp, cn>::
getMaxSpreadN(const int left, const int right) const
{
cv::Vec<Tp, cn> maxValue = data[ idx[left] ],
minValue = data[ idx[left] ];
for (int i = left + 1; i < right; ++i)
for (int j = 0; j < cn; ++j)
{
minValue[j] = std::min( minValue[j], data[idx[i]][j] );
maxValue[j] = std::max( maxValue[j], data[idx[i]][j] );
}
cv::Vec<Tp, cn> spread = maxValue - minValue;
Tp *begIt = &spread[0];
return int(std::max_element(begIt, begIt + cn) - begIt);
}
template <typename Tp, int cn> KDTree <Tp, cn>::
KDTree(const cv::Mat &img, const int _leafNumber, const int _zeroThresh)
: height(img.rows), width(img.cols),
leafNumber(_leafNumber), zeroThresh(_zeroThresh)
///////////////////////////////////////////////////
{
int imgch = img.channels();
CV_Assert( img.isContinuous() && imgch <= cn);
for(size_t i = 0; i < img.total(); i++)
{
cv::Vec<Tp, cn> v = cv::Vec<Tp, cn>::all((Tp)0);
for (int c = 0; c < imgch; c++)
{
v[c] = *((Tp*)(img.data) + i*imgch + c);
}
data.push_back(v);
}
generate_seq( std::back_inserter(idx), 0, int(data.size()) );
std::fill_n( std::back_inserter(nodes),
int(data.size()), cv::Point2i(0, 0) );
std::stack <int> left, right;
left.push( 0 );
right.push( int(idx.size()) );
while ( !left.empty() )
{
int _left = left.top(); left.pop();
int _right = right.top(); right.pop();
if ( _right - _left <= leafNumber)
{
for (int i = _left; i < _right; ++i)
nodes[idx[i]] = cv::Point2i(_left, _right);
continue;
}
int nth = _left + (_right - _left)/2;
int dimIdx = getMaxSpreadN(_left, _right);
KDTreeComparator comp( this, dimIdx );
std::vector<int> _idx(idx.begin(), idx.end());
std::nth_element(/**/
_idx.begin() + _left,
_idx.begin() + nth,
_idx.begin() + _right, comp
/**/);
idx = _idx;
left.push(_left); right.push(nth + 1);
left.push(nth + 1); right.push(_right);
}
}
template <typename Tp, int cn> void KDTree <Tp, cn>::
updateDist(const int leaf, const int &idx0, int &bestIdx, double &dist)
{
for (int k = nodes[leaf].x; k < nodes[leaf].y; ++k)
{
int y = idx0/width, ny = idx[k]/width;
int x = idx0%width, nx = idx[k]%width;
if (abs(ny - y) < zeroThresh &&
abs(nx - x) < zeroThresh)
continue;
if (nx >= width - 1 || nx < 1 ||
ny >= height - 1 || ny < 1 )
continue;
double ndist = norm2(data[idx0], data[idx[k]]);
if (ndist < dist)
{
dist = ndist;
bestIdx = idx[k];
}
}
}
/************************** ANNF search **************************/
static void dominantTransforms(const cv::Mat &img, std::vector <cv::Point2i> &transforms,
const int nTransform, const int psize)
{
const int zeroThresh = 2*psize;
const int leafNum = 64;
/** Walsh-Hadamard Transformation **/
std::vector <cv::Mat> channels;
cv::split(img, channels);
int cncase = std::max(img.channels() - 2, 0);
const int np[] = {cncase == 0 ? 12 : (cncase == 1 ? 16 : 10),
cncase == 0 ? 12 : (cncase == 1 ? 04 : 02),
cncase == 0 ? 00 : (cncase == 1 ? 04 : 02),
cncase == 0 ? 00 : (cncase == 1 ? 00 : 10)};
for (int i = 0; i < img.channels(); ++i)
rgb2whs(channels[i], channels[i], np[i], psize);
cv::Mat whs; // Walsh-Hadamard series
cv::merge(channels, whs);
KDTree <float, 24> kdTree(whs, leafNum, zeroThresh);
std::vector <int> annf( whs.total(), 0 );
/** Propagation-assisted kd-tree search **/
for (int i = 0; i < whs.rows; ++i)
for (int j = 0; j < whs.cols; ++j)
{
double dist = std::numeric_limits <double>::max();
int current = i*whs.cols + j;
int dy[] = {0, 1, 0}, dx[] = {0, 0, 1};
for (int k = 0; k < int( sizeof(dy)/sizeof(int) ); ++k)
if ( i - dy[k] >= 0 && j - dx[k] >= 0 )
{
int neighbor = (i - dy[k])*whs.cols + (j - dx[k]);
int leafIdx = (dx[k] == 0 && dy[k] == 0)
? neighbor : annf[neighbor] + dy[k]*whs.cols + dx[k];
kdTree.updateDist(leafIdx, current,
annf[i*whs.cols + j], dist);
}
}
/** Local maxima extraction **/
cv::Mat_<double> annfHist(2*whs.rows - 1, 2*whs.cols - 1, 0.0),
_annfHist(2*whs.rows - 1, 2*whs.cols - 1, 0.0);
for (size_t i = 0; i < annf.size(); ++i)
++annfHist( annf[i]/whs.cols - int(i)/whs.cols + whs.rows - 1,
annf[i]%whs.cols - int(i)%whs.cols + whs.cols - 1 );
cv::GaussianBlur( annfHist, annfHist,
cv::Size(0, 0), std::sqrt(2.0), 0.0, cv::BORDER_CONSTANT);
cv::dilate( annfHist, _annfHist,
cv::Matx<uint8_t, 9, 9>::ones() );
std::vector < std::pair<double, int> > amount;
std::vector <cv::Point2i> shiftM;
for (int i = 0, t = 0; i < annfHist.rows; ++i)
{
double *pAnnfHist = annfHist.ptr<double>(i);
double *_pAnnfHist = _annfHist.ptr<double>(i);
for (int j = 0; j < annfHist.cols; ++j)
if ( pAnnfHist[j] != 0 && pAnnfHist[j] == _pAnnfHist[j] )
{
amount.push_back( std::make_pair(pAnnfHist[j], t++) );
shiftM.push_back( cv::Point2i(j - whs.cols + 1,
i - whs.rows + 1) );
}
}
int num = std::min((int)amount.size(), (int)nTransform);
std::partial_sort( amount.begin(), amount.begin() + num,
amount.end(), std::greater< std::pair<double, int> >() );
transforms.resize(num);
for (int i = 0; i < num; ++i)
{
int idx = amount[i].second;
transforms[i] = cv::Point2i( shiftM[idx].x, shiftM[idx].y );
}
}
#endif /* __ANNF_HPP__ */
+45
View File
@@ -0,0 +1,45 @@
/*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-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// * 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 __BLENDING_HPP__
#define __BLENDING_HPP__
#endif /* __BLENDING_HPP__ */
@@ -0,0 +1,165 @@
/*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_BM3D_DENOISING_INVOKER_COMMONS_HPP__
#define __OPENCV_BM3D_DENOISING_INVOKER_COMMONS_HPP__
#include "bm3d_denoising_invoker_structs.hpp"
// 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
namespace cv
{
namespace xphoto
{
// Returns largest power of 2 smaller than the input value
inline int getLargestPowerOf2SmallerThan(unsigned x)
{
x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
x = x | (x >> 16);
return x - (x >> 1);
}
// Returns true if x is a power of 2. Otherwise false.
inline bool isPowerOf2(int x)
{
return (x > 0) && !(x & (x - 1));
}
template <typename T>
inline static void shrink(T &val, T &nonZeroCount, const T &threshold)
{
if (std::abs(val) < threshold)
val = 0;
else
++nonZeroCount;
}
template <typename T>
inline static void hardThreshold2D(T *dst, T *thrMap, const int &templateWindowSizeSq)
{
for (int i = 1; i < templateWindowSizeSq; ++i)
{
if (std::abs(dst[i]) < thrMap[i])
dst[i] = 0;
}
}
template <int N, typename T, typename DT, typename CT>
inline static T HardThreshold(BlockMatch<T, DT, CT> *z, const int &n, T *&thrMap)
{
T nonZeroCount = 0;
for (int i = 0; i < N; ++i)
shrink(z[i][n], nonZeroCount, *thrMap++);
return nonZeroCount;
}
template <typename T, typename DT, typename CT>
inline static T HardThreshold(BlockMatch<T, DT, CT> *z, const int &n, T *&thrMap, const int &N)
{
T nonZeroCount = 0;
for (int i = 0; i < N; ++i)
shrink(z[i][n], nonZeroCount, *thrMap++);
return nonZeroCount;
}
template <int N, typename T, typename DT, typename CT>
inline static int WienerFiltering(BlockMatch<T, DT, CT> *zSrc, BlockMatch<T, DT, CT> *zBasic, const int &n, T *&thrMap)
{
int wienerCoeffs = 0;
for (int i = 0; i < N; ++i)
{
// Possible optimization point here to get rid of floats and casts
int basicSq = zBasic[i][n] * zBasic[i][n];
int sigmaSq = *thrMap * *thrMap;
int denom = basicSq + sigmaSq;
float wie = (denom == 0) ? 1.0f : ((float)basicSq / (float)denom);
zBasic[i][n] = (T)(zSrc[i][n] * wie);
wienerCoeffs += (int)wie;
++thrMap;
}
return wienerCoeffs;
}
template <typename T, typename DT, typename CT>
inline static int WienerFiltering(BlockMatch<T, DT, CT> *zSrc, BlockMatch<T, DT, CT> *zBasic, const int &n, T *&thrMap, const unsigned &N)
{
int wienerCoeffs = 0;
for (unsigned i = 0; i < N; ++i)
{
// Possible optimization point here to get rid of floats and casts
int basicSq = zBasic[i][n] * zBasic[i][n];
int sigmaSq = *thrMap * *thrMap;
int denom = basicSq + sigmaSq;
float wie = (denom == 0) ? 1.0f : ((float)basicSq / (float)denom);
zBasic[i][n] = (T)(zSrc[i][n] * wie);
wienerCoeffs += (int)wie;
++thrMap;
}
return wienerCoeffs;
}
} // namespace xphoto
} // namespace cv
#endif
@@ -0,0 +1,517 @@
/*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_BM3D_DENOISING_INVOKER_STEP1_HPP__
#define __OPENCV_BM3D_DENOISING_INVOKER_STEP1_HPP__
#include "bm3d_denoising_invoker_commons.hpp"
#include "bm3d_denoising_transforms.hpp"
#include "kaiser_window.hpp"
namespace cv
{
namespace xphoto
{
template <typename T, typename D, typename WT, typename TT, typename TC>
struct Bm3dDenoisingInvokerStep1 : public ParallelLoopBody
{
public:
Bm3dDenoisingInvokerStep1(
const Mat& src,
Mat& dst,
const int &templateWindowSize,
const int &searchWindowSize,
const float &h,
const int &hBM,
const int &groupSize,
const int &slidingStep,
const float &beta);
virtual ~Bm3dDenoisingInvokerStep1();
void operator() (const Range& range) const CV_OVERRIDE;
private:
// Unimplemented operator in order to satisfy compiler warning.
void operator= (const Bm3dDenoisingInvokerStep1&);
void calcDistSumsForFirstElementInRow(
int i,
Array2d<int>& distSums,
Array3d<int>& colDistSums,
Array3d<int>& lastColDistSums,
BlockMatch<TT, int, TT> *bm,
int &elementSize) const;
void calcDistSumsForAllElementsInFirstRow(
int i,
int j,
int firstColNum,
Array2d<int>& distSums,
Array3d<int>& colDistSums,
Array3d<int>& lastColDistSums,
BlockMatch<TT, int, TT> *bm,
int &elementSize) const;
// Image containers
const Mat& src_;
Mat& dst_;
Mat srcExtended_;
// Border size of the extended src and basic images
int borderSize_;
// Template and window size
int templateWindowSize_;
int searchWindowSize_;
// Half template and window size
int halfTemplateWindowSize_;
int halfSearchWindowSize_;
// Squared template and window size
int templateWindowSizeSq_;
int searchWindowSizeSq_;
// Block matching threshold
int hBM_;
// Maximum size of 3D group
int groupSize_;
// Sliding step
const int slidingStep_;
// Threshold map
TT *thrMap_;
// Kaiser window
float *kaiser_;
};
template <typename T, typename D, typename WT, typename TT, typename TC>
Bm3dDenoisingInvokerStep1<T, D, WT, TT, TC>::Bm3dDenoisingInvokerStep1(
const Mat& src,
Mat& dst,
const int &templateWindowSize,
const int &searchWindowSize,
const float &h,
const int &hBM,
const int &groupSize,
const int &slidingStep,
const float &beta) :
src_(src), dst_(dst), groupSize_(groupSize), slidingStep_(slidingStep), thrMap_(NULL), kaiser_(NULL)
{
groupSize_ = getLargestPowerOf2SmallerThan(groupSize);
CV_Assert(groupSize > 0);
halfTemplateWindowSize_ = templateWindowSize >> 1;
halfSearchWindowSize_ = searchWindowSize >> 1;
templateWindowSize_ = templateWindowSize;
searchWindowSize_ = searchWindowSize;
templateWindowSizeSq_ = templateWindowSize_ * templateWindowSize_;
searchWindowSizeSq_ = searchWindowSize_ * searchWindowSize_;
// Extend image to avoid border problem
borderSize_ = halfSearchWindowSize_ + halfTemplateWindowSize_;
copyMakeBorder(src_, srcExtended_, borderSize_, borderSize_, borderSize_, borderSize_, BORDER_DEFAULT);
// Calculate block matching threshold
hBM_ = D::template calcBlockMatchingThreshold<int>(hBM, templateWindowSizeSq_);
// Select transforms depending on the template size
TC::RegisterTransforms2D(templateWindowSize_);
// Precompute threshold map
TC::calcThresholdMap3D(thrMap_, h, templateWindowSize_, groupSize_);
// Generate kaiser window
calcKaiserWindow2D(kaiser_, templateWindowSize_, beta);
}
template<typename T, typename D, typename WT, typename TT, typename TC>
inline Bm3dDenoisingInvokerStep1<T, D, WT, TT, TC>::~Bm3dDenoisingInvokerStep1()
{
delete[] thrMap_;
delete[] kaiser_;
}
template <typename T, typename D, typename WT, typename TT, typename TC>
void Bm3dDenoisingInvokerStep1<T, D, WT, TT, TC>::operator() (const Range& range) const
{
const int size = (range.size() + 2 * borderSize_) * srcExtended_.cols;
std::vector<WT> weightedSum(size, 0.0);
std::vector<WT> weights(size, 0.0);
int row_from = range.start;
int row_to = range.end - 1;
// Local vars for faster processing
const int blockSize = templateWindowSize_;
const int blockSizeSq = templateWindowSizeSq_;
const int halfBlockSize = halfTemplateWindowSize_;
const int searchWindowSize = searchWindowSize_;
const int searchWindowSizeSq = searchWindowSizeSq_;
const TT halfSearchWindowSize = (TT)halfSearchWindowSize_;
const int hBM = hBM_;
const int groupSize = groupSize_;
const int step = srcExtended_.cols;
const int dstStep = srcExtended_.cols;
const int weiStep = srcExtended_.cols;
const int dstcstep = dstStep - blockSize;
const int weicstep = weiStep - blockSize;
// Buffer to store 3D group
BlockMatch<TT, int, TT> *bm = new BlockMatch<TT, int, TT>[searchWindowSizeSq];
for (int i = 0; i < searchWindowSizeSq; ++i)
bm[i].init(blockSizeSq);
// First element in a group is always the reference patch. Hence distance is 0.
bm[0](0, halfSearchWindowSize, halfSearchWindowSize);
// Sums of columns and rows for current pixel
Array2d<int> distSums(searchWindowSize, searchWindowSize);
// Sums of columns for current pixel (for lazy calc optimization)
Array3d<int> colDistSums(blockSize, searchWindowSize, searchWindowSize);
// Last elements of column sum (for each element in a row)
Array3d<int> lastColDistSums(src_.cols, searchWindowSize, searchWindowSize);
int firstColNum = -1;
for (int j = row_from, jj = 0; j <= row_to; j += slidingStep_, jj += slidingStep_)
{
for (int i = 0; i < src_.cols; i += slidingStep_)
{
const T *currentPixel = srcExtended_.ptr<T>(0) + step*j + i;
int elementSize = 1;
// Calculate distSums using moving average filter approach.
if (i == 0)
{
// Calculate distSums for the first element in a row
calcDistSumsForFirstElementInRow(j, distSums, colDistSums, lastColDistSums, bm, elementSize);
firstColNum = 0;
}
else
{
if (j == row_from)
{
// Calculate distSums for all elements in the first row
calcDistSumsForAllElementsInFirstRow(
j, i, firstColNum, distSums, colDistSums, lastColDistSums, bm, elementSize);
}
else
{
const int start_bx = blockSize + i - 1;
const int start_by = j - 1;
const int ax = halfSearchWindowSize + start_bx;
const int ay = halfSearchWindowSize + start_by;
const T a_up = srcExtended_.at<T>(ay, ax);
const T a_down = srcExtended_.at<T>(ay + blockSize, ax);
for (TT y = 0; y < searchWindowSize; y++)
{
int *distSumsRow = distSums.row_ptr(y);
int *colDistSumsRow = colDistSums.row_ptr(firstColNum, y);
int *lastColDistSumsRow = lastColDistSums.row_ptr(i, y);
const T *b_up_ptr = srcExtended_.ptr<T>(start_by + y);
const T *b_down_ptr = srcExtended_.ptr<T>(start_by + y + blockSize);
for (TT x = 0; x < searchWindowSize; x++)
{
// Remove from current pixel sum column sum with index "firstColNum"
distSumsRow[x] -= colDistSumsRow[x];
const int bx = start_bx + x;
colDistSumsRow[x] = lastColDistSumsRow[x] +
D::template calcUpDownDist<T>(a_up, a_down, b_up_ptr[bx], b_down_ptr[bx]);
distSumsRow[x] += colDistSumsRow[x];
lastColDistSumsRow[x] = colDistSumsRow[x];
if (x == halfSearchWindowSize && y == halfSearchWindowSize)
continue;
// Save the distance, coordinate and increase the counter
if (distSumsRow[x] < hBM)
bm[elementSize++](distSumsRow[x], x, y);
}
}
}
firstColNum = (firstColNum + 1) % blockSize;
}
// Sort bm by distance (first element is already sorted)
std::sort(bm + 1, bm + elementSize);
// Find the nearest power of 2 and cap the group size from the top
elementSize = getLargestPowerOf2SmallerThan(elementSize);
if (elementSize > groupSize)
elementSize = groupSize;
// Transform 2D patches
for (int n = 0; n < elementSize; ++n)
{
const T *candidatePatch = currentPixel + step * bm[n].coord_y + bm[n].coord_x;
TC::forwardTransform2D(candidatePatch, bm[n].data(), step, blockSize);
}
// Transform and shrink 1D columns
TT sumNonZero = 0;
TT *thrMapPtr1D = thrMap_ + (elementSize - 1) * blockSizeSq;
switch (elementSize)
{
case 16:
for (int n = 0; n < blockSizeSq; n++)
{
TC::forwardTransform16(bm, n);
sumNonZero += HardThreshold<16>(bm, n, thrMapPtr1D);
TC::inverseTransform16(bm, n);
}
break;
case 8:
for (int n = 0; n < blockSizeSq; n++)
{
TC::forwardTransform8(bm, n);
sumNonZero += HardThreshold<8>(bm, n, thrMapPtr1D);
TC::inverseTransform8(bm, n);
}
break;
case 4:
for (int n = 0; n < blockSizeSq; n++)
{
TC::forwardTransform4(bm, n);
sumNonZero += HardThreshold<4>(bm, n, thrMapPtr1D);
TC::inverseTransform4(bm, n);
}
break;
case 2:
for (int n = 0; n < blockSizeSq; n++)
{
TC::forwardTransform2(bm, n);
TC::forwardTransform2(bm, n);
sumNonZero += HardThreshold<2>(bm, n, thrMapPtr1D);
TC::inverseTransform2(bm, n);
}
break;
case 1:
{
TT *block = bm[0].data();
for (int n = 0; n < blockSizeSq; n++)
shrink(block[n], sumNonZero, *thrMapPtr1D++);
}
break;
default:
for (int n = 0; n < blockSizeSq; n++)
{
TC::forwardTransformN(bm, n, elementSize);
sumNonZero += HardThreshold(bm, n, thrMapPtr1D, elementSize);
TC::inverseTransformN(bm, n, elementSize);
}
}
// Inverse 2D transform
for (int n = 0; n < elementSize; ++n)
TC::inverseTransform2D(bm[n].data(), blockSize);
// Aggregate the results (increase sumNonZero to avoid division by zero)
float weight = 1.0f / (float)(++sumNonZero);
// Scale weight by element size
weight *= elementSize;
weight /= groupSize;
// Put patches back to their original positions
WT *dstPtr = weightedSum.data() + jj * dstStep + i;
WT *weiPtr = weights.data() + jj * dstStep + i;
const float *kaiser = kaiser_;
for (int l = 0; l < elementSize; ++l)
{
const TT *block = bm[l].data();
int offset = bm[l].coord_y * dstStep + bm[l].coord_x;
WT *d = dstPtr + offset;
WT *dw = weiPtr + offset;
for (int n = 0; n < blockSize; ++n)
{
for (int m = 0; m < blockSize; ++m)
{
unsigned idx = n * blockSize + m;
*d += kaiser[idx] * block[idx] * weight;
*dw += kaiser[idx] * weight;
++d, ++dw;
}
d += dstcstep;
dw += weicstep;
}
}
} // i
} // j
// Cleanup
for (int i = 0; i < searchWindowSizeSq; ++i)
bm[i].release();
delete[] bm;
// Divide accumulation buffer by the corresponding weights
for (int i = row_from, ii = 0; i <= row_to; ++i, ++ii)
{
T *d = dst_.ptr<T>(i);
float *dE = weightedSum.data() + (ii + halfSearchWindowSize + halfBlockSize) * dstStep + halfSearchWindowSize;
float *dw = weights.data() + (ii + halfSearchWindowSize + halfBlockSize) * dstStep + halfSearchWindowSize;
for (int j = 0; j < dst_.cols; ++j)
d[j] = cv::saturate_cast<T>(dE[j + halfBlockSize] / dw[j + halfBlockSize]);
}
}
template <typename T, typename D, typename WT, typename TT, typename TC>
inline void Bm3dDenoisingInvokerStep1<T, D, WT, TT, TC>::calcDistSumsForFirstElementInRow(
int i,
Array2d<int>& distSums,
Array3d<int>& colDistSums,
Array3d<int>& lastColDistSums,
BlockMatch<TT, int, TT> *bm,
int &elementSize) const
{
int j = 0;
const int hBM = hBM_;
const int blockSize = templateWindowSize_;
const int searchWindowSize = searchWindowSize_;
const TT halfSearchWindowSize = (TT)halfSearchWindowSize_;
const int ay = halfSearchWindowSize + i;
const int ax = halfSearchWindowSize + j;
for (TT y = 0; y < searchWindowSize; ++y)
{
for (TT x = 0; x < searchWindowSize; ++x)
{
// Zeroize arrays
distSums[y][x] = 0;
for (int tx = 0; tx < blockSize; tx++)
colDistSums[tx][y][x] = 0;
int start_y = i + y;
int start_x = j + x;
for (int ty = 0; ty < blockSize; ty++)
for (int tx = 0; tx < blockSize; tx++)
{
int dist = D::template calcDist<T>(
srcExtended_,
ay + ty,
ax + tx,
start_y + ty,
start_x + tx);
distSums[y][x] += dist;
colDistSums[tx][y][x] += dist;
}
lastColDistSums[j][y][x] = colDistSums[blockSize - 1][y][x];
if (x == halfSearchWindowSize && y == halfSearchWindowSize)
continue;
if (distSums[y][x] < hBM)
bm[elementSize++](distSums[y][x], x, y);
}
}
}
template <typename T, typename D, typename WT, typename TT, typename TC>
inline void Bm3dDenoisingInvokerStep1<T, D, WT, TT, TC>::calcDistSumsForAllElementsInFirstRow(
int i,
int j,
int firstColNum,
Array2d<int>& distSums,
Array3d<int>& colDistSums,
Array3d<int>& lastColDistSums,
BlockMatch<TT, int, TT> *bm,
int &elementSize) const
{
const int hBM = hBM_;
const int blockSize = templateWindowSize_;
const int searchWindowSize = searchWindowSize_;
const TT halfSearchWindowSize = (TT)halfSearchWindowSize_;
const int bx_start = blockSize - 1 + j;
const int ax = halfSearchWindowSize + bx_start;
const int ay = halfSearchWindowSize + i;
for (TT y = 0; y < searchWindowSize; ++y)
{
for (TT x = 0; x < searchWindowSize; ++x)
{
distSums[y][x] -= colDistSums[firstColNum][y][x];
colDistSums[firstColNum][y][x] = 0;
int by = i + y;
int bx = bx_start + x;
for (int ty = 0; ty < blockSize; ty++)
colDistSums[firstColNum][y][x] += D::template calcDist<T>(
srcExtended_,
ay + ty,
ax,
by + ty,
bx);
distSums[y][x] += colDistSums[firstColNum][y][x];
lastColDistSums[j][y][x] = colDistSums[firstColNum][y][x];
if (x == halfSearchWindowSize && y == halfSearchWindowSize)
continue;
if (distSums[y][x] < hBM)
bm[elementSize++](distSums[y][x], x, y);
}
}
}
} // namespace xphoto
} // namespace cv
#endif
@@ -0,0 +1,540 @@
/*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_BM3D_DENOISING_INVOKER_STEP2_HPP__
#define __OPENCV_BM3D_DENOISING_INVOKER_STEP2_HPP__
#include "bm3d_denoising_invoker_commons.hpp"
#include "bm3d_denoising_transforms.hpp"
#include "kaiser_window.hpp"
namespace cv
{
namespace xphoto
{
template <typename T, typename D, typename WT, typename TT, typename TC>
struct Bm3dDenoisingInvokerStep2 : public ParallelLoopBody
{
public:
Bm3dDenoisingInvokerStep2(
const Mat& src,
const Mat& basic,
Mat& dst,
const int &templateWindowSize,
const int &searchWindowSize,
const float &h,
const int &hBM,
const int &groupSize,
const int &slidingStep,
const float &beta);
virtual ~Bm3dDenoisingInvokerStep2();
void operator() (const Range& range) const CV_OVERRIDE;
private:
// Unimplemented operator in order to satisfy compiler warning.
void operator= (const Bm3dDenoisingInvokerStep2&);
void calcDistSumsForFirstElementInRow(
int i,
Array2d<int>& distSums,
Array3d<int>& colDistSums,
Array3d<int>& lastColDistSums,
BlockMatch<TT, int, TT> *bm,
int &elementSize) const;
void calcDistSumsForAllElementsInFirstRow(
int i,
int j,
int firstColNum,
Array2d<int>& distSums,
Array3d<int>& colDistSums,
Array3d<int>& lastColDistSums,
BlockMatch<TT, int, TT> *bm,
int &elementSize) const;
// Image containers
const Mat& src_;
const Mat& basic_;
Mat& dst_;
Mat srcExtended_;
Mat basicExtended_;
// Border size of the extended src and basic images
int borderSize_;
// Template and window size
int templateWindowSize_;
int searchWindowSize_;
// Half template and window size
int halfTemplateWindowSize_;
int halfSearchWindowSize_;
// Squared template and window size
int templateWindowSizeSq_;
int searchWindowSizeSq_;
// Block matching threshold
int hBM_;
// Maximum size of 3D group
int groupSize_;
// Sliding step
const int slidingStep_;
// Threshold map
TT *thrMap_;
// Kaiser window
float *kaiser_;
};
template <typename T, typename D, typename WT, typename TT, typename TC>
Bm3dDenoisingInvokerStep2<T, D, WT, TT, TC>::Bm3dDenoisingInvokerStep2(
const Mat& src,
const Mat& basic,
Mat& dst,
const int &templateWindowSize,
const int &searchWindowSize,
const float &h,
const int &hBM,
const int &groupSize,
const int &slidingStep,
const float &beta) :
src_(src), basic_(basic), dst_(dst), groupSize_(groupSize), slidingStep_(slidingStep), thrMap_(NULL), kaiser_(NULL)
{
groupSize_ = getLargestPowerOf2SmallerThan(groupSize);
CV_Assert(groupSize > 0);
halfTemplateWindowSize_ = templateWindowSize >> 1;
halfSearchWindowSize_ = searchWindowSize >> 1;
templateWindowSize_ = templateWindowSize;
searchWindowSize_ = searchWindowSize;
templateWindowSizeSq_ = templateWindowSize_ * templateWindowSize_;
searchWindowSizeSq_ = searchWindowSize_ * searchWindowSize_;
// Extend image to avoid border problem
borderSize_ = halfSearchWindowSize_ + halfTemplateWindowSize_;
copyMakeBorder(src_, srcExtended_, borderSize_, borderSize_, borderSize_, borderSize_, BORDER_DEFAULT);
copyMakeBorder(basic_, basicExtended_, borderSize_, borderSize_, borderSize_, borderSize_, BORDER_DEFAULT);
// Calculate block matching threshold
hBM_ = D::template calcBlockMatchingThreshold<int>(hBM, templateWindowSizeSq_);
// Select transforms depending on the template size
TC::RegisterTransforms2D(templateWindowSize_);
// Precompute threshold map
TC::calcThresholdMap3D(thrMap_, h, templateWindowSize_, groupSize_);
// Generate kaiser window
calcKaiserWindow2D(kaiser_, templateWindowSize_, beta);
}
template<typename T, typename D, typename WT, typename TT, typename TC>
inline Bm3dDenoisingInvokerStep2<T, D, WT, TT, TC>::~Bm3dDenoisingInvokerStep2()
{
delete[] thrMap_;
delete[] kaiser_;
}
template <typename T, typename D, typename WT, typename TT, typename TC>
void Bm3dDenoisingInvokerStep2<T, D, WT, TT, TC>::operator() (const Range& range) const
{
const int size = (range.size() + 2 * borderSize_) * srcExtended_.cols;
std::vector<WT> weightedSum(size, 0.0);
std::vector<WT> weights(size, 0.0);
int row_from = range.start;
int row_to = range.end - 1;
// Local vars for faster processing
const int blockSize = templateWindowSize_;
const int blockSizeSq = templateWindowSizeSq_;
const int halfBlockSize = halfTemplateWindowSize_;
const int searchWindowSize = searchWindowSize_;
const int searchWindowSizeSq = searchWindowSizeSq_;
const TT halfSearchWindowSize = (TT)halfSearchWindowSize_;
const int hBM = hBM_;
const int groupSize = groupSize_;
const int step = srcExtended_.cols;
const int dstStep = srcExtended_.cols;
const int weiStep = srcExtended_.cols;
const int dstcstep = dstStep - blockSize;
const int weicstep = weiStep - blockSize;
// Buffer to store 3D group
BlockMatch<TT, int, TT> *bmBasic = new BlockMatch<TT, int, TT>[searchWindowSizeSq];
BlockMatch<TT, int, TT> *bmSrc = new BlockMatch<TT, int, TT>[searchWindowSizeSq];
for (int i = 0; i < searchWindowSizeSq; ++i)
{
bmBasic[i].init(blockSizeSq);
bmSrc[i].init(blockSizeSq);
}
// First element in a group is always the reference patch. Hence distance is 0.
bmBasic[0](0, halfSearchWindowSize, halfSearchWindowSize);
bmSrc[0](0, halfSearchWindowSize, halfSearchWindowSize);
// Sums of columns and rows for current pixel
Array2d<int> distSums(searchWindowSize, searchWindowSize);
// Sums of columns for current pixel (for lazy calc optimization)
Array3d<int> colDistSums(blockSize, searchWindowSize, searchWindowSize);
// Last elements of column sum (for each element in a row)
Array3d<int> lastColDistSums(src_.cols, searchWindowSize, searchWindowSize);
int firstColNum = -1;
for (int j = row_from, jj = 0; j <= row_to; j += slidingStep_, jj += slidingStep_)
{
for (int i = 0; i < src_.cols; i += slidingStep_)
{
const T *currentPixelSrc = srcExtended_.ptr<T>(0) + step*j + i;
const T *currentPixelBasic = basicExtended_.ptr<T>(0) + step*j + i;
int elementSize = 1;
// Calculate distSums using moving average filter approach.
if (i == 0)
{
// Calculate distSums for the first element in a row
calcDistSumsForFirstElementInRow(j, distSums, colDistSums, lastColDistSums, bmBasic, elementSize);
firstColNum = 0;
}
else
{
if (j == row_from)
{
// Calculate distSums for all elements in the first row
calcDistSumsForAllElementsInFirstRow(
j, i, firstColNum, distSums, colDistSums, lastColDistSums, bmBasic, elementSize);
}
else
{
const int start_bx = blockSize + i - 1;
const int start_by = j - 1;
const int ax = halfSearchWindowSize + start_bx;
const int ay = halfSearchWindowSize + start_by;
const T a_up = basicExtended_.at<T>(ay, ax);
const T a_down = basicExtended_.at<T>(ay + blockSize, ax);
for (TT y = 0; y < searchWindowSize; y++)
{
int *distSumsRow = distSums.row_ptr(y);
int *colDistSumsRow = colDistSums.row_ptr(firstColNum, y);
int *lastColDistSumsRow = lastColDistSums.row_ptr(i, y);
const T *b_up_ptr = basicExtended_.ptr<T>(start_by + y);
const T *b_down_ptr = basicExtended_.ptr<T>(start_by + y + blockSize);
for (TT x = 0; x < searchWindowSize; x++)
{
// Remove from current pixel sum column sum with index "firstColNum"
distSumsRow[x] -= colDistSumsRow[x];
const int bx = start_bx + x;
colDistSumsRow[x] = lastColDistSumsRow[x] +
D::template calcUpDownDist<T>(a_up, a_down, b_up_ptr[bx], b_down_ptr[bx]);
distSumsRow[x] += colDistSumsRow[x];
lastColDistSumsRow[x] = colDistSumsRow[x];
if (x == halfSearchWindowSize && y == halfSearchWindowSize)
continue;
// Save the distance, coordinate and increase the counter
if (distSumsRow[x] < hBM)
bmBasic[elementSize++](distSumsRow[x], x, y);
}
}
}
firstColNum = (firstColNum + 1) % blockSize;
}
// Sort bmBasic by distance (first element is already sorted)
std::sort(bmBasic + 1, bmBasic + elementSize);
// Find the nearest power of 2 and cap the group size from the top
elementSize = getLargestPowerOf2SmallerThan(elementSize);
if (elementSize > groupSize)
elementSize = groupSize;
// Transform 2D patches
for (int n = 0; n < elementSize; ++n)
{
const T *candidatePatchSrc = currentPixelSrc + step * bmBasic[n].coord_y + bmBasic[n].coord_x;
const T *candidatePatchBasic = currentPixelBasic + step * bmBasic[n].coord_y + bmBasic[n].coord_x;
TC::forwardTransform2D(candidatePatchSrc, bmSrc[n].data(), step, blockSize);
TC::forwardTransform2D(candidatePatchBasic, bmBasic[n].data(), step, blockSize);
}
// Transform and shrink 1D columns
int wienerCoefficients = 0;
TT *thrMapPtr1D = thrMap_ + (elementSize - 1) * blockSizeSq;
switch (elementSize)
{
case 16:
for (int n = 0; n < blockSizeSq; n++)
{
TC::forwardTransform16(bmSrc, n);
TC::forwardTransform16(bmBasic, n);
wienerCoefficients += WienerFiltering<16>(bmSrc, bmBasic, n, thrMapPtr1D);
TC::inverseTransform16(bmBasic, n);
}
break;
case 8:
for (int n = 0; n < blockSizeSq; n++)
{
TC::forwardTransform8(bmSrc, n);
TC::forwardTransform8(bmBasic, n);
wienerCoefficients += WienerFiltering<8>(bmSrc, bmBasic, n, thrMapPtr1D);
TC::inverseTransform8(bmBasic, n);
}
break;
case 4:
for (int n = 0; n < blockSizeSq; n++)
{
TC::forwardTransform4(bmSrc, n);
TC::forwardTransform4(bmBasic, n);
wienerCoefficients += WienerFiltering<4>(bmSrc, bmBasic, n, thrMapPtr1D);
TC::inverseTransform4(bmBasic, n);
}
break;
case 2:
for (int n = 0; n < blockSizeSq; n++)
{
TC::forwardTransform2(bmSrc, n);
TC::forwardTransform2(bmBasic, n);
wienerCoefficients += WienerFiltering<2>(bmSrc, bmBasic, n, thrMapPtr1D);
TC::inverseTransform2(bmBasic, n);
}
break;
case 1:
{
for (int n = 0; n < blockSizeSq; n++)
wienerCoefficients += WienerFiltering<1>(bmSrc, bmBasic, n, thrMapPtr1D);
}
break;
default:
for (int n = 0; n < blockSizeSq; n++)
{
TC::forwardTransformN(bmSrc, n, elementSize);
TC::forwardTransformN(bmBasic, n, elementSize);
wienerCoefficients += WienerFiltering(bmSrc, bmBasic, n, thrMapPtr1D, elementSize);
TC::inverseTransformN(bmBasic, n, elementSize);
}
}
// Inverse 2D transform
for (int n = 0; n < elementSize; ++n)
TC::inverseTransform2D(bmBasic[n].data(), blockSize);
// Aggregate the results (increase sumNonZero to avoid division by zero)
float weight = 1.0f / (float)(++wienerCoefficients);
// Scale weight by element size
weight *= elementSize;
weight /= groupSize;
// Put patches back to their original positions
WT *dstPtr = weightedSum.data() + jj * dstStep + i;
WT *weiPtr = weights.data() + jj * dstStep + i;
const float *kaiser = kaiser_;
for (int l = 0; l < elementSize; ++l)
{
const TT *block = bmBasic[l].data();
int offset = bmBasic[l].coord_y * dstStep + bmBasic[l].coord_x;
WT *d = dstPtr + offset;
WT *dw = weiPtr + offset;
for (int n = 0; n < blockSize; ++n)
{
for (int m = 0; m < blockSize; ++m)
{
unsigned idx = n * blockSize + m;
*d += kaiser[idx] * block[idx] * weight;
*dw += kaiser[idx] * weight;
++d, ++dw;
}
d += dstcstep;
dw += weicstep;
}
}
} // i
} // j
// Cleanup
for (int i = 0; i < searchWindowSizeSq; ++i)
{
bmBasic[i].release();
bmSrc[i].release();
}
delete[] bmSrc;
delete[] bmBasic;
// Divide accumulation buffer by the corresponding weights
for (int i = row_from, ii = 0; i <= row_to; ++i, ++ii)
{
T *d = dst_.ptr<T>(i);
float *dE = weightedSum.data() + (ii + halfSearchWindowSize + halfBlockSize) * dstStep + halfSearchWindowSize;
float *dw = weights.data() + (ii + halfSearchWindowSize + halfBlockSize) * dstStep + halfSearchWindowSize;
for (int j = 0; j < dst_.cols; ++j)
d[j] = cv::saturate_cast<T>(dE[j + halfBlockSize] / dw[j + halfBlockSize]);
}
}
template <typename T, typename D, typename WT, typename TT, typename TC>
inline void Bm3dDenoisingInvokerStep2<T, D, WT, TT, TC>::calcDistSumsForFirstElementInRow(
int i,
Array2d<int>& distSums,
Array3d<int>& colDistSums,
Array3d<int>& lastColDistSums,
BlockMatch<TT, int, TT> *bm,
int &elementSize) const
{
int j = 0;
const int hBM = hBM_;
const int blockSize = templateWindowSize_;
const int searchWindowSize = searchWindowSize_;
const TT halfSearchWindowSize = (TT)halfSearchWindowSize_;
const int ay = halfSearchWindowSize + i;
const int ax = halfSearchWindowSize + j;
for (TT y = 0; y < searchWindowSize; ++y)
{
for (TT x = 0; x < searchWindowSize; ++x)
{
// Zeroize arrays
distSums[y][x] = 0;
for (int tx = 0; tx < blockSize; tx++)
colDistSums[tx][y][x] = 0;
int start_y = i + y;
int start_x = j + x;
for (int ty = 0; ty < blockSize; ty++)
for (int tx = 0; tx < blockSize; tx++)
{
int dist = D::template calcDist<T>(
basicExtended_,
ay + ty,
ax + tx,
start_y + ty,
start_x + tx);
distSums[y][x] += dist;
colDistSums[tx][y][x] += dist;
}
lastColDistSums[j][y][x] = colDistSums[blockSize - 1][y][x];
if (x == halfSearchWindowSize && y == halfSearchWindowSize)
continue;
if (distSums[y][x] < hBM)
bm[elementSize++](distSums[y][x], x, y);
}
}
}
template <typename T, typename D, typename WT, typename TT, typename TC>
inline void Bm3dDenoisingInvokerStep2<T, D, WT, TT, TC>::calcDistSumsForAllElementsInFirstRow(
int i,
int j,
int firstColNum,
Array2d<int>& distSums,
Array3d<int>& colDistSums,
Array3d<int>& lastColDistSums,
BlockMatch<TT, int, TT> *bm,
int &elementSize) const
{
const int hBM = hBM_;
const int blockSize = templateWindowSize_;
const int searchWindowSize = searchWindowSize_;
const TT halfSearchWindowSize = (TT)halfSearchWindowSize_;
const int bx_start = blockSize - 1 + j;
const int ax = halfSearchWindowSize + bx_start;
const int ay = halfSearchWindowSize + i;
for (TT y = 0; y < searchWindowSize; ++y)
{
for (TT x = 0; x < searchWindowSize; ++x)
{
distSums[y][x] -= colDistSums[firstColNum][y][x];
colDistSums[firstColNum][y][x] = 0;
int by = i + y;
int bx = bx_start + x;
for (int ty = 0; ty < blockSize; ty++)
colDistSums[firstColNum][y][x] += D::template calcDist<T>(
basicExtended_,
ay + ty,
ax,
by + ty,
bx);
distSums[y][x] += colDistSums[firstColNum][y][x];
lastColDistSums[j][y][x] = colDistSums[firstColNum][y][x];
if (x == halfSearchWindowSize && y == halfSearchWindowSize)
continue;
if (distSums[y][x] < hBM)
bm[elementSize++](distSums[y][x], x, y);
}
}
}
} // namespace xphoto
} // namespace cv
#endif
@@ -0,0 +1,366 @@
/*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_BM3D_DENOISING_INVOKER_STRUCTS_HPP__
#define __OPENCV_BM3D_DENOISING_INVOKER_STRUCTS_HPP__
namespace cv
{
namespace xphoto
{
template <typename T, typename DT, typename CT>
class BlockMatch
{
public:
// Data accessor
T* data()
{
return data_;
}
// Const version of data accessor
const T* data() const
{
return data_;
}
// Allocate memory for data
void init(const int &blockSizeSq)
{
data_ = new T[blockSizeSq];
}
// Release data memory
void release()
{
delete[] data_;
}
// Overloaded operator for convenient assignment
void operator()(const DT &_dist, const CT &_coord_x, const CT &_coord_y)
{
dist = _dist;
coord_x = _coord_x;
coord_y = _coord_y;
}
// Overloaded array subscript operator
T& operator[](const std::size_t &idx)
{
return data_[idx];
};
// Overloaded const array subscript operator
const T& operator[](const std::size_t &idx) const
{
return data_[idx];
};
// Overloaded comparison operator for sorting
bool operator<(const BlockMatch& right) const
{
return dist < right.dist;
}
// Block matching distance
DT dist;
// Relative coordinates to the current search window
CT coord_x;
CT coord_y;
private:
// Pointer to the pixel values of the block
T *data_;
};
class DistAbs
{
template <typename T>
struct calcDist_
{
static inline int f(const T &a, const T &b)
{
return std::abs(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]));
}
};
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>
static inline T calcBlockMatchingThreshold(const T &blockMatchThrL2, const T &blockSizeSq)
{
return (T)(std::sqrt((double)blockMatchThrL2) * blockSizeSq);
}
};
class DistSquared
{
template <typename T>
struct calcDist_
{
static inline int f(const T &a, const T &b)
{
return (a - b) * (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);
}
};
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>
static inline T calcBlockMatchingThreshold(const T &blockMatchThrL2, const T &blockSizeSq)
{
return blockMatchThrL2 * blockSizeSq;
}
};
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;
}
};
} // namespace xphoto
} // namespace cv
#endif
@@ -0,0 +1,73 @@
/*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_BM3D_DENOISING_TRANSFORMS_HPP__
#define __OPENCV_BM3D_DENOISING_TRANSFORMS_HPP__
#include "bm3d_denoising_transforms_haar.hpp"
namespace cv
{
namespace xphoto
{
// Following class contains interface of the transform domain functions.
template <typename T, typename TT>
class Transform
{
public:
// 2D transforms
typedef void(*Forward2D)(const T *ptr, TT *dst, const int &step, const int blockSize);
typedef void(*Inverse2D)(TT *src, const int blockSize);
// 1D transforms
typedef void(*Forward1D)(BlockMatch<TT, int, TT> *z, const int &n, const unsigned &N);
typedef void(*Inverse1D)(BlockMatch<TT, int, TT> *z, const int &n, const unsigned &N);
// Specialized 1D transforms
typedef void(*Forward1Ds)(BlockMatch<TT, int, TT> *z, const int &n);
typedef void(*Inverse1Ds)(BlockMatch<TT, int, TT> *z, const int &n);
};
} // namespace xphoto
} // namespace cv
#endif
@@ -0,0 +1,376 @@
/*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_BM3D_DENOISING_TRANSFORMS_1D_HPP__
#define __OPENCV_BM3D_DENOISING_TRANSFORMS_1D_HPP__
namespace cv
{
namespace xphoto
{
class HaarTransform1D
{
static void CalculateIndicesN(unsigned *diffIndices, const unsigned &size, const unsigned &N)
{
unsigned diffIdx = 1;
unsigned diffAllIdx = 0;
for (unsigned i = 1; i <= N; i <<= 1)
{
diffAllIdx += (i >> 1);
for (unsigned j = 0; j < (i >> 1); ++j)
diffIndices[diffIdx++] = size - (--diffAllIdx);
diffAllIdx += i;
}
}
public:
/// 1D forward transformations of array of arbitrary size
template <typename T, typename DT, typename CT>
inline static void ForwardTransformN(BlockMatch<T, DT, CT> *src, const int &n, const unsigned &N)
{
const unsigned size = N + (N << 1) - 2;
T *dstX = new T[size];
// Fill dstX with source values
for (unsigned i = 0; i < N; ++i)
dstX[i] = src[i][n];
unsigned idx = 0, dstIdx = N;
for (unsigned i = N; i > 1; i >>= 1)
{
// Get sums
for (unsigned j = 0; j < (i >> 1); ++j)
dstX[dstIdx++] = (dstX[idx + 2 * j] + dstX[idx + j * 2 + 1] + 1) >> 1;
// Get diffs
for (unsigned j = 0; j < (i >> 1); ++j)
dstX[dstIdx++] = dstX[idx + 2 * j] - dstX[idx + j * 2 + 1];
idx = dstIdx - i;
}
// Calculate indices in the destination matrix.
unsigned *diffIndices = new unsigned[N];
CalculateIndicesN(diffIndices, size, N);
// Fill in destination matrix
src[0][n] = dstX[size - 2];
for (unsigned i = 1; i < N; ++i)
src[i][n] = dstX[diffIndices[i]];
delete[] dstX;
delete[] diffIndices;
}
/// 1D inverse transformation of array of arbitrary size
template <typename T, typename DT, typename CT>
inline static void InverseTransformN(BlockMatch<T, DT, CT> *src, const int &n, const unsigned &N)
{
const unsigned dstSize = (N << 1) - 2;
T *dstX = new T[dstSize];
T *srcX = new T[N];
// Fill srcX with source values
srcX[0] = src[0][n] * 2;
for (unsigned i = 1; i < N; ++i)
srcX[i] = src[i][n];
// Take care of first two elements
dstX[0] = srcX[0] + srcX[1];
dstX[1] = srcX[0] - srcX[1];
unsigned idx = 0, dstIdx = 2;
for (unsigned i = 4; i < N; i <<= 1)
{
for (unsigned j = 0; j < (i >> 1); ++j)
{
dstX[dstIdx++] = dstX[idx + j] + srcX[idx + 2 + j];
dstX[dstIdx++] = dstX[idx + j] - srcX[idx + 2 + j];
}
idx += (i >> 1);
}
// Handle the last X elements
dstIdx = 0;
for (unsigned j = 0; j < (N >> 1); ++j)
{
src[dstIdx++][n] = (dstX[idx + j] + srcX[idx + 2 + j]) >> 1;
src[dstIdx++][n] = (dstX[idx + j] - srcX[idx + 2 + j]) >> 1;
}
delete[] srcX;
delete[] dstX;
}
/// 1D forward transformations of fixed array size: 2, 4, 8 and 16
template <typename T, typename DT, typename CT>
inline static void ForwardTransform2(BlockMatch<T, DT, CT> *z, const int &n)
{
T sum = (z[0][n] + z[1][n] + 1) >> 1;
T dif = z[0][n] - z[1][n];
z[0][n] = sum;
z[1][n] = dif;
}
template <typename T, typename DT, typename CT>
inline static void ForwardTransform4(BlockMatch<T, DT, CT> *z, const int &n)
{
T sum0 = (z[0][n] + z[1][n] + 1) >> 1;
T sum1 = (z[2][n] + z[3][n] + 1) >> 1;
T dif0 = z[0][n] - z[1][n];
T dif1 = z[2][n] - z[3][n];
T sum00 = (sum0 + sum1 + 1) >> 1;
T dif00 = sum0 - sum1;
z[0][n] = sum00;
z[1][n] = dif00;
z[2][n] = dif0;
z[3][n] = dif1;
}
template <typename T, typename DT, typename CT>
inline static void ForwardTransform8(BlockMatch<T, DT, CT> *z, const int &n)
{
T sum0 = (z[0][n] + z[1][n] + 1) >> 1;
T sum1 = (z[2][n] + z[3][n] + 1) >> 1;
T sum2 = (z[4][n] + z[5][n] + 1) >> 1;
T sum3 = (z[6][n] + z[7][n] + 1) >> 1;
T dif0 = z[0][n] - z[1][n];
T dif1 = z[2][n] - z[3][n];
T dif2 = z[4][n] - z[5][n];
T dif3 = z[6][n] - z[7][n];
T sum00 = (sum0 + sum1 + 1) >> 1;
T sum11 = (sum2 + sum3 + 1) >> 1;
T dif00 = sum0 - sum1;
T dif11 = sum2 - sum3;
T sum000 = (sum00 + sum11 + 1) >> 1;
T dif000 = sum00 - sum11;
z[0][n] = sum000;
z[1][n] = dif000;
z[2][n] = dif00;
z[3][n] = dif11;
z[4][n] = dif0;
z[5][n] = dif1;
z[6][n] = dif2;
z[7][n] = dif3;
}
template <typename T, typename DT, typename CT>
inline static void ForwardTransform16(BlockMatch<T, DT, CT> *z, const int &n)
{
T sum0 = (z[0][n] + z[1][n] + 1) >> 1;
T sum1 = (z[2][n] + z[3][n] + 1) >> 1;
T sum2 = (z[4][n] + z[5][n] + 1) >> 1;
T sum3 = (z[6][n] + z[7][n] + 1) >> 1;
T sum4 = (z[8][n] + z[9][n] + 1) >> 1;
T sum5 = (z[10][n] + z[11][n] + 1) >> 1;
T sum6 = (z[12][n] + z[13][n] + 1) >> 1;
T sum7 = (z[14][n] + z[15][n] + 1) >> 1;
T dif0 = z[0][n] - z[1][n];
T dif1 = z[2][n] - z[3][n];
T dif2 = z[4][n] - z[5][n];
T dif3 = z[6][n] - z[7][n];
T dif4 = z[8][n] - z[9][n];
T dif5 = z[10][n] - z[11][n];
T dif6 = z[12][n] - z[13][n];
T dif7 = z[14][n] - z[15][n];
T sum00 = (sum0 + sum1 + 1) >> 1;
T sum11 = (sum2 + sum3 + 1) >> 1;
T sum22 = (sum4 + sum5 + 1) >> 1;
T sum33 = (sum6 + sum7 + 1) >> 1;
T dif00 = sum0 - sum1;
T dif11 = sum2 - sum3;
T dif22 = sum4 - sum5;
T dif33 = sum6 - sum7;
T sum000 = (sum00 + sum11 + 1) >> 1;
T sum111 = (sum22 + sum33 + 1) >> 1;
T dif000 = sum00 - sum11;
T dif111 = sum22 - sum33;
T sum0000 = (sum000 + sum111 + 1) >> 1;
T dif0000 = dif000 - dif111;
z[0][n] = sum0000;
z[1][n] = dif0000;
z[2][n] = dif000;
z[3][n] = dif111;
z[4][n] = dif00;
z[5][n] = dif11;
z[6][n] = dif22;
z[7][n] = dif33;
z[8][n] = dif0;
z[9][n] = dif1;
z[10][n] = dif2;
z[11][n] = dif3;
z[12][n] = dif4;
z[13][n] = dif5;
z[14][n] = dif6;
z[15][n] = dif7;
}
/// 1D inverse transformations of fixed array size: 2, 4, 8 and 16
template <typename T, typename DT, typename CT>
inline static void InverseTransform2(BlockMatch<T, DT, CT> *src, const int &n)
{
T src0 = src[0][n] * 2;
T src1 = src[1][n];
src[0][n] = (src0 + src1) >> 1;
src[1][n] = (src0 - src1) >> 1;
}
template <typename T, typename DT, typename CT>
inline static void InverseTransform4(BlockMatch<T, DT, CT> *src, const int &n)
{
T src0 = src[0][n] * 2;
T src1 = src[1][n];
T src2 = src[2][n];
T src3 = src[3][n];
T sum0 = src0 + src1;
T dif0 = src0 - src1;
src[0][n] = (sum0 + src2) >> 1;
src[1][n] = (sum0 - src2) >> 1;
src[2][n] = (dif0 + src3) >> 1;
src[3][n] = (dif0 - src3) >> 1;
}
template <typename T, typename DT, typename CT>
inline static void InverseTransform8(BlockMatch<T, DT, CT> *src, const int &n)
{
T src0 = src[0][n] * 2;
T src1 = src[1][n];
T src2 = src[2][n];
T src3 = src[3][n];
T src4 = src[4][n];
T src5 = src[5][n];
T src6 = src[6][n];
T src7 = src[7][n];
T sum0 = src0 + src1;
T dif0 = src0 - src1;
T sum00 = sum0 + src2;
T dif00 = sum0 - src2;
T sum11 = dif0 + src3;
T dif11 = dif0 - src3;
src[0][n] = (sum00 + src4) >> 1;
src[1][n] = (sum00 - src4) >> 1;
src[2][n] = (dif00 + src5) >> 1;
src[3][n] = (dif00 - src5) >> 1;
src[4][n] = (sum11 + src6) >> 1;
src[5][n] = (sum11 - src6) >> 1;
src[6][n] = (dif11 + src7) >> 1;
src[7][n] = (dif11 - src7) >> 1;
}
template <typename T, typename DT, typename CT>
inline static void InverseTransform16(BlockMatch<T, DT, CT> *src, const int &n)
{
T src0 = src[0][n] * 2;
T src1 = src[1][n];
T src2 = src[2][n];
T src3 = src[3][n];
T src4 = src[4][n];
T src5 = src[5][n];
T src6 = src[6][n];
T src7 = src[7][n];
T src8 = src[8][n];
T src9 = src[9][n];
T src10 = src[10][n];
T src11 = src[11][n];
T src12 = src[12][n];
T src13 = src[13][n];
T src14 = src[14][n];
T src15 = src[15][n];
T sum0 = src0 + src1;
T dif0 = src0 - src1;
T sum00 = sum0 + src2;
T dif00 = sum0 - src2;
T sum11 = dif0 + src3;
T dif11 = dif0 - src3;
T sum000 = sum00 + src4;
T dif000 = sum00 - src4;
T sum111 = dif00 + src5;
T dif111 = dif00 - src5;
T sum222 = sum11 + src6;
T dif222 = sum11 - src6;
T sum333 = dif11 + src7;
T dif333 = dif11 - src7;
src[0][n] = (sum000 + src8) >> 1;
src[1][n] = (sum000 - src8) >> 1;
src[2][n] = (dif000 + src9) >> 1;
src[3][n] = (dif000 - src9) >> 1;
src[4][n] = (sum111 + src10) >> 1;
src[5][n] = (sum111 - src10) >> 1;
src[6][n] = (dif111 + src11) >> 1;
src[7][n] = (dif111 - src11) >> 1;
src[8][n] = (sum222 + src12) >> 1;
src[9][n] = (sum222 - src12) >> 1;
src[10][n] = (dif222 + src13) >> 1;
src[11][n] = (dif222 - src13) >> 1;
src[12][n] = (sum333 + src14) >> 1;
src[13][n] = (sum333 - src14) >> 1;
src[14][n] = (dif333 + src15) >> 1;
src[15][n] = (dif333 - src15) >> 1;
}
};
} // namespace xphoto
} // namespace cv
#endif
@@ -0,0 +1,511 @@
/*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_BM3D_DENOISING_TRANSFORMS_2D_HPP__
#define __OPENCV_BM3D_DENOISING_TRANSFORMS_2D_HPP__
namespace cv
{
namespace xphoto
{
class HaarTransform2D
{
template <int X>
static void CalculateIndices(unsigned *diffIndices, const unsigned &size)
{
unsigned diffIdx = 1;
unsigned diffAllIdx = 0;
for (unsigned i = 1; i <= X; i <<= 1)
{
diffAllIdx += (i >> 1);
for (unsigned j = 0; j < (i >> 1); ++j)
diffIndices[diffIdx++] = size - (--diffAllIdx);
diffAllIdx += i;
}
}
public:
/// Transforms for 2D block of arbitrary size
template <typename T, typename TT, int X, int N>
inline static void ForwardTransformX(const T *src, TT *dst, const int &step)
{
const unsigned size = X + (X << 1) - 2;
TT dstX[size];
// Fill dstX with source values
for (unsigned i = 0; i < X; ++i)
dstX[i] = *(src + i * step);
unsigned idx = 0, dstIdx = X;
for (unsigned i = X; i > 1; i >>= 1)
{
// Get sums
for (unsigned j = 0; j < (i >> 1); ++j)
dstX[dstIdx++] = (dstX[idx + 2 * j] + dstX[idx + j * 2 + 1] + 1) >> 1;
// Get diffs
for (unsigned j = 0; j < (i >> 1); ++j)
dstX[dstIdx++] = dstX[idx + 2 * j] - dstX[idx + j * 2 + 1];
idx = dstIdx - i;
}
// Calculate indices in the destination matrix.
unsigned diffIndices[X];
CalculateIndices<X>(diffIndices, size);
// Fill in destination matrix
dst[0] = dstX[size - 2];
for (int i = 1; i < X; ++i)
dst[i * N] = dstX[diffIndices[i]];
}
template <typename T, typename TT, int X>
inline static void ForwardTransformXxX(const T *ptr, TT *dst, const int &step, const int /*blockSize*/)
{
TT temp[X * X];
// Transform columns first
for (unsigned i = 0; i < X; ++i)
ForwardTransformX<T, TT, X, X>(ptr + i, temp + i, step);
// Then transform rows
for (unsigned i = 0; i < X; ++i)
ForwardTransformX<TT, TT, X, 1>(temp + i * X, dst + i * X, 1);
}
template <typename T, int X, int N>
inline static void InverseTransformX(T *src, T *dst)
{
const unsigned dstSize = (X << 1) - 2;
T dstX[dstSize];
T srcX[X];
// Fill srcX with source values
srcX[0] = src[0] * 2;
for (int i = 1; i < X; ++i)
srcX[i] = src[i * N];
// Take care of first two elements
dstX[0] = srcX[0] + srcX[1];
dstX[1] = srcX[0] - srcX[1];
unsigned idx = 0, dstIdx = 2;
for (int i = 4; i < X; i <<= 1)
{
for (int j = 0; j < (i >> 1); ++j)
{
dstX[dstIdx++] = dstX[idx + j] + srcX[idx + 2 + j];
dstX[dstIdx++] = dstX[idx + j] - srcX[idx + 2 + j];
}
idx += (i >> 1);
}
// Handle the last X elements
dstIdx = 0;
for (int j = 0; j < (X >> 1); ++j)
{
dst[dstIdx++ * N] = (dstX[idx + j] + srcX[idx + 2 + j]) >> 1;
dst[dstIdx++ * N] = (dstX[idx + j] - srcX[idx + 2 + j]) >> 1;
}
}
template <typename T, int X>
inline static void InverseTransformXxX(T *src, const int /*blockSize*/)
{
T temp[X * X];
// Invert columns first
for (int i = 0; i < X; ++i)
InverseTransformX<T, X, X>(src + i, temp + i);
// Then invert rows
for (int i = 0; i < X; ++i)
InverseTransformX<T, X, 1>(temp + i * X, src + i * X);
}
// Same as above but X and N are arguments, not template parameters.
static void CalculateIndices(int *diffIndices, const int &size, const int &X)
{
int diffIdx = 1;
int diffAllIdx = 0;
for (int i = 1; i <= X; i <<= 1)
{
diffAllIdx += (i >> 1);
for (int j = 0; j < (i >> 1); ++j)
diffIndices[diffIdx++] = size - (--diffAllIdx);
diffAllIdx += i;
}
}
template <typename T, typename TT>
inline static void ForwardTransformX(const T *src, TT *dst, const int &step, const int &X, const int &N)
{
const int size = X + (X << 1) - 2;
TT *dstX = new TT[size];
// Fill dstX with source values
for (int i = 0; i < X; ++i)
dstX[i] = *(src + i * step);
int idx = 0, dstIdx = X;
for (int i = X; i > 1; i >>= 1)
{
// Get sums
for (int j = 0; j < (i >> 1); ++j)
dstX[dstIdx++] = (dstX[idx + 2 * j] + dstX[idx + j * 2 + 1] + 1) >> 1;
// Get diffs
for (int j = 0; j < (i >> 1); ++j)
dstX[dstIdx++] = dstX[idx + 2 * j] - dstX[idx + j * 2 + 1];
idx = dstIdx - i;
}
// Calculate indices in the destination matrix.
int *diffIndices = new int[X];
CalculateIndices(diffIndices, size, X);
// Fill in destination matrix
dst[0] = dstX[size - 2];
for (int i = 1; i < X; ++i)
dst[i * N] = dstX[diffIndices[i]];
delete[] diffIndices;
delete[] dstX;
}
template <typename T, typename TT>
inline static void ForwardTransformXxX(const T *ptr, TT *dst, const int &step, const int X)
{
TT *temp = new TT[X * X];
// Transform columns first
for (int i = 0; i < X; ++i)
ForwardTransformX<T, TT>(ptr + i, temp + i, step, X, X);
// Then transform rows
for (int i = 0; i < X; ++i)
ForwardTransformX<TT, TT>(temp + i * X, dst + i * X, 1, X, 1);
delete[] temp;
}
template <typename T>
inline static void InverseTransformX(T *src, T *dst, const int &X, const int &N)
{
const unsigned dstSize = (X << 1) - 2;
T *dstX = new T[dstSize];
T *srcX = new T[X];
// Fill srcX with source values
srcX[0] = src[0] * 2;
for (int i = 1; i < X; ++i)
srcX[i] = src[i * N];
// Take care of first two elements
dstX[0] = srcX[0] + srcX[1];
dstX[1] = srcX[0] - srcX[1];
unsigned idx = 0, dstIdx = 2;
for (int i = 4; i < X; i <<= 1)
{
for (int j = 0; j < (i >> 1); ++j)
{
dstX[dstIdx++] = dstX[idx + j] + srcX[idx + 2 + j];
dstX[dstIdx++] = dstX[idx + j] - srcX[idx + 2 + j];
}
idx += (i >> 1);
}
// Handle the last X elements
dstIdx = 0;
for (int j = 0; j < (X >> 1); ++j)
{
dst[dstIdx++ * N] = (dstX[idx + j] + srcX[idx + 2 + j]) >> 1;
dst[dstIdx++ * N] = (dstX[idx + j] - srcX[idx + 2 + j]) >> 1;
}
delete[] dstX;
delete[] srcX;
}
template <typename T>
inline static void InverseTransformXxX(T *src, const int X)
{
T *temp = new T[X * X];
// Invert columns first
for (int i = 0; i < X; ++i)
InverseTransformX<T>(src + i, temp + i, X, X);
// Then invert rows
for (int i = 0; i < X; ++i)
InverseTransformX<T>(temp + i * X, src + i * X, X, 1);
delete[] temp;
}
/// Transforms for 2x2 2D block
template <typename T, typename TT, int N>
inline static void ForwardTransform2(const T *src, TT *dst, const int &step)
{
const T *src0 = src;
const T *src1 = src + 1 * step;
dst[0 * N] = (*src0 + *src1 + 1) >> 1;
dst[1 * N] = *src0 - *src1;
}
template <typename T, typename TT>
inline static void ForwardTransform2x2(const T *ptr, TT *dst, const int &step, const int /*blockSize*/)
{
TT temp[4];
// Transform columns first
for (int i = 0; i < 2; ++i)
ForwardTransform2<T, TT, 2>(ptr + i, temp + i, step);
// Then transform rows
for (int i = 0; i < 2; ++i)
ForwardTransform2<TT, TT, 1>(temp + i * 2, dst + i * 2, 1);
}
template <typename TT, int N>
inline static void InverseTransform2(TT *src, TT *dst)
{
TT src0 = src[0 * N] * 2;
TT src1 = src[1 * N];
dst[0 * N] = (src0 + src1) >> 1;
dst[1 * N] = (src0 - src1) >> 1;
}
template <typename T>
inline static void InverseTransform2x2(T *src, const int /*blockSize*/)
{
T temp[4];
// Invert columns first
for (int i = 0; i < 2; ++i)
InverseTransform2<T, 2>(src + i, temp + i);
// Then invert rows
for (int i = 0; i < 2; ++i)
InverseTransform2<T, 1>(temp + i * 2, src + i * 2);
}
/// Transforms for 4x4 2D block
template <typename T, typename TT, int N>
inline static void ForwardTransform4(const T *src, TT *dst, const int &step)
{
const T *src0 = src;
const T *src1 = src + 1 * step;
const T *src2 = src + 2 * step;
const T *src3 = src + 3 * step;
TT sum0 = (*src0 + *src1 + 1) >> 1;
TT sum1 = (*src2 + *src3 + 1) >> 1;
TT dif0 = *src0 - *src1;
TT dif1 = *src2 - *src3;
TT sum00 = (sum0 + sum1 + 1) >> 1;
TT dif00 = sum0 - sum1;
dst[0 * N] = sum00;
dst[1 * N] = dif00;
dst[2 * N] = dif0;
dst[3 * N] = dif1;
}
template <typename T, typename TT>
inline static void ForwardTransform4x4(const T *ptr, TT *dst, const int &step, const int /*blockSize*/)
{
TT temp[16];
// Transform columns first
for (int i = 0; i < 4; ++i)
ForwardTransform4<T, TT, 4>(ptr + i, temp + i, step);
// Then transform rows
for (int i = 0; i < 4; ++i)
ForwardTransform4<TT, TT, 1>(temp + i * 4, dst + i * 4, 1);
}
template <typename TT, int N>
inline static void InverseTransform4(TT *src, TT *dst)
{
TT src0 = src[0 * N] * 2;
TT src1 = src[1 * N];
TT src2 = src[2 * N];
TT src3 = src[3 * N];
TT sum0 = src0 + src1;
TT dif0 = src0 - src1;
dst[0 * N] = (sum0 + src2) >> 1;
dst[1 * N] = (sum0 - src2) >> 1;
dst[2 * N] = (dif0 + src3) >> 1;
dst[3 * N] = (dif0 - src3) >> 1;
}
template <typename T>
inline static void InverseTransform4x4(T *src, const int /*blockSize*/)
{
T temp[16];
// Invert columns first
for (int i = 0; i < 4; ++i)
InverseTransform4<T, 4>(src + i, temp + i);
// Then invert rows
for (int i = 0; i < 4; ++i)
InverseTransform4<T, 1>(temp + i * 4, src + i * 4);
}
/// Transforms for 8x8 2D block
template <typename T, typename TT, int N>
inline static void ForwardTransform8(const T *src, TT *dst, const int &step)
{
const T *src0 = src;
const T *src1 = src + 1 * step;
const T *src2 = src + 2 * step;
const T *src3 = src + 3 * step;
const T *src4 = src + 4 * step;
const T *src5 = src + 5 * step;
const T *src6 = src + 6 * step;
const T *src7 = src + 7 * step;
TT sum0 = (*src0 + *src1 + 1) >> 1;
TT sum1 = (*src2 + *src3 + 1) >> 1;
TT sum2 = (*src4 + *src5 + 1) >> 1;
TT sum3 = (*src6 + *src7 + 1) >> 1;
TT dif0 = *src0 - *src1;
TT dif1 = *src2 - *src3;
TT dif2 = *src4 - *src5;
TT dif3 = *src6 - *src7;
TT sum00 = (sum0 + sum1 + 1) >> 1;
TT sum11 = (sum2 + sum3 + 1) >> 1;
TT dif00 = sum0 - sum1;
TT dif11 = sum2 - sum3;
TT sum000 = (sum00 + sum11 + 1) >> 1;
TT dif000 = sum00 - sum11;
dst[0 * N] = sum000;
dst[1 * N] = dif000;
dst[2 * N] = dif00;
dst[3 * N] = dif11;
dst[4 * N] = dif0;
dst[5 * N] = dif1;
dst[6 * N] = dif2;
dst[7 * N] = dif3;
}
template <typename T, typename TT>
inline static void ForwardTransform8x8(const T *ptr, TT *dst, const int &step, const int /*blockSize*/)
{
TT temp[64];
// Transform columns first
for (int i = 0; i < 8; ++i)
ForwardTransform8<T, TT, 8>(ptr + i, temp + i, step);
// Then transform rows
for (int i = 0; i < 8; ++i)
ForwardTransform8<TT, TT, 1>(temp + i * 8, dst + i * 8, 1);
}
template <typename T, int N>
inline static void InverseTransform8(T *src, T *dst)
{
T src0 = src[0] * 2;
T src1 = src[1 * N];
T src2 = src[2 * N];
T src3 = src[3 * N];
T src4 = src[4 * N];
T src5 = src[5 * N];
T src6 = src[6 * N];
T src7 = src[7 * N];
T sum0 = src0 + src1;
T dif0 = src0 - src1;
T sum00 = sum0 + src2;
T dif00 = sum0 - src2;
T sum11 = dif0 + src3;
T dif11 = dif0 - src3;
dst[0 * N] = (sum00 + src4) >> 1;
dst[1 * N] = (sum00 - src4) >> 1;
dst[2 * N] = (dif00 + src5) >> 1;
dst[3 * N] = (dif00 - src5) >> 1;
dst[4 * N] = (sum11 + src6) >> 1;
dst[5 * N] = (sum11 - src6) >> 1;
dst[6 * N] = (dif11 + src7) >> 1;
dst[7 * N] = (dif11 - src7) >> 1;
}
template <typename T>
inline static void InverseTransform8x8(T *src, const int /*blockSize*/)
{
T temp[64];
// Invert columns first
for (int i = 0; i < 8; ++i)
InverseTransform8<T, 8>(src + i, temp + i);
// Then invert rows
for (int i = 0; i < 8; ++i)
InverseTransform8<T, 1>(temp + i * 8, src + i * 8);
}
};
} // namespace xphoto
} // namespace cv
#endif
@@ -0,0 +1,290 @@
/*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_BM3D_DENOISING_TRANSFORMS_HAAR_HPP__
#define __OPENCV_BM3D_DENOISING_TRANSFORMS_HAAR_HPP__
#include "bm3d_denoising_transforms_1D.hpp"
#include "bm3d_denoising_transforms_2D.hpp"
namespace cv
{
namespace xphoto
{
// Forward declaration
template <typename T, typename TT>
class Transform;
template <typename T, typename TT>
class HaarTransform
{
static void calcCoefficients1D(cv::Mat &coeff1D, const int &numberOfElements)
{
// Generate base array and initialize with zeros
cv::Mat baseArr = cv::Mat::zeros(numberOfElements, numberOfElements, CV_32FC1);
// Calculate base array coefficients.
int currentRow = 0;
for (int i = numberOfElements; i > 0; i /= 2)
{
for (int k = 0, sign = -1; k < numberOfElements; ++k)
{
// Alternate sign every i-th element
if (k % i == 0)
sign *= -1;
// Move to the next row every 2*i-th element
if (k != 0 && (k % (2 * i) == 0))
++currentRow;
baseArr.at<float>(currentRow, k) = sign * 1.0f / i;
}
++currentRow;
}
// Square each elements of the base array
float *ptr = baseArr.ptr<float>(0);
for (unsigned i = 0; i < baseArr.total(); ++i)
ptr[i] = ptr[i] * ptr[i];
// Multiply baseArray with 1D vector of ones
cv::Mat unitaryArr = cv::Mat::ones(numberOfElements, 1, CV_32FC1);
coeff1D = baseArr * unitaryArr;
}
// Method to generate threshold coefficients for 1D transform depending on the number of elements.
static void fillHaarCoefficients1D(float *thrCoeff1D, int &idx, const int &numberOfElements)
{
cv::Mat coeff1D;
calcCoefficients1D(coeff1D, numberOfElements);
// Square root the array to get standard deviation
float *ptr = coeff1D.ptr<float>(0);
for (unsigned i = 0; i < coeff1D.total(); ++i)
{
ptr[i] = std::sqrt(ptr[i]);
thrCoeff1D[idx++] = ptr[i];
}
}
// Method to generate threshold coefficients for 2D transform depending on the number of elements.
static void fillHaarCoefficients2D(float *thrCoeff2D, const int &templateWindowSize)
{
cv::Mat coeff1D;
calcCoefficients1D(coeff1D, templateWindowSize);
// Calculate 2D array
cv::Mat coeff1Dt;
cv::transpose(coeff1D, coeff1Dt);
cv::Mat coeff2D = coeff1D * coeff1Dt;
// Square root the array to get standard deviation
float *ptr = coeff2D.ptr<float>(0);
for (unsigned i = 0; i < coeff2D.total(); ++i)
thrCoeff2D[i] = std::sqrt(ptr[i]);
}
public:
// Method to calculate 1D threshold map based on the maximum number of elements
// Allocates memory for the output array.
static void calcThresholdMap1D(float *&thrMap1D, const int &numberOfElements)
{
CV_Assert(numberOfElements > 0);
// Allocate memory for the array
const int arrSize = (numberOfElements << 1) - 1;
if (thrMap1D == NULL)
thrMap1D = new float[arrSize];
for (int i = 1, idx = 0; i <= numberOfElements; i *= 2)
fillHaarCoefficients1D(thrMap1D, idx, i);
}
// Method to calculate 2D threshold map based on the maximum number of elements
// Allocates memory for the output array.
static void calcThresholdMap2D(float *&thrMap2D, const int &templateWindowSize)
{
// Allocate memory for the array
if (thrMap2D == NULL)
thrMap2D = new float[templateWindowSize * templateWindowSize];
fillHaarCoefficients2D(thrMap2D, templateWindowSize);
}
// Method to calculate 3D threshold map based on the maximum number of elements.
// Allocates memory for the output array.
static void calcThresholdMap3D(
TT *&outThrMap1D,
const float &hardThr1D,
const int &templateWindowSize,
const int &groupSize)
{
const int templateWindowSizeSq = templateWindowSize * templateWindowSize;
// Allocate memory for the output array
if (outThrMap1D == NULL)
outThrMap1D = new TT[templateWindowSizeSq * ((groupSize << 1) - 1)];
// Generate 1D coefficients map
float *thrMap1D = NULL;
calcThresholdMap1D(thrMap1D, groupSize);
// Generate 2D coefficients map
float *thrMap2D = NULL;
calcThresholdMap2D(thrMap2D, templateWindowSize);
// Generate 3D threshold map
TT *thrMapPtr1D = outThrMap1D;
for (int i = 1, ii = 0; i <= groupSize; ++ii, i *= 2)
{
float coeff = (i == 1) ? 1.0f : std::sqrt(2.0f * std::log((float)i));
for (int jj = 0; jj < templateWindowSizeSq; ++jj)
{
for (int ii1 = 0; ii1 < (1 << ii); ++ii1)
{
int indexIn1D = (1 << ii) - 1 + ii1;
int indexIn2D = jj;
int thr = static_cast<int>(thrMap1D[indexIn1D] * thrMap2D[indexIn2D] * hardThr1D * coeff);
// Set DC component to zero
if (jj == 0 && ii1 == 0)
thr = 0;
*thrMapPtr1D++ = cv::saturate_cast<TT>(thr);
}
}
}
delete[] thrMap1D;
delete[] thrMap2D;
}
// Method that registers 2D transform calls
static void RegisterTransforms2D(const int &templateWindowSize)
{
// Check if template window size is a power of two
if (!isPowerOf2(templateWindowSize))
CV_Error(Error::StsBadArg, "Unsupported template size! Template size must be power of two!");
switch (templateWindowSize)
{
case 2:
forwardTransform2D = HaarTransform2D::ForwardTransform2x2<T, TT>;
inverseTransform2D = HaarTransform2D::InverseTransform2x2<TT>;
break;
case 4:
forwardTransform2D = HaarTransform2D::ForwardTransform4x4<T, TT>;
inverseTransform2D = HaarTransform2D::InverseTransform4x4<TT>;
break;
case 8:
forwardTransform2D = HaarTransform2D::ForwardTransform8x8<T, TT>;
inverseTransform2D = HaarTransform2D::InverseTransform8x8<TT>;
break;
case 16:
forwardTransform2D = HaarTransform2D::ForwardTransformXxX<T, TT, 16>;
inverseTransform2D = HaarTransform2D::InverseTransformXxX<TT, 16>;
break;
case 32:
forwardTransform2D = HaarTransform2D::ForwardTransformXxX<T, TT, 32>;
inverseTransform2D = HaarTransform2D::InverseTransformXxX<TT, 32>;
break;
case 64:
forwardTransform2D = HaarTransform2D::ForwardTransformXxX<T, TT, 64>;
inverseTransform2D = HaarTransform2D::InverseTransformXxX<TT, 64>;
break;
default:
forwardTransform2D = HaarTransform2D::ForwardTransformXxX<T, TT>;
inverseTransform2D = HaarTransform2D::InverseTransformXxX<TT>;
}
}
// 2D transform pointers
static typename Transform<T, TT>::Forward2D forwardTransform2D;
static typename Transform<T, TT>::Inverse2D inverseTransform2D;
// 1D transform pointers
static typename Transform<T, TT>::Forward1D forwardTransformN;
static typename Transform<T, TT>::Inverse1D inverseTransformN;
// Specialized 1D forward transform pointers
static typename Transform<T, TT>::Forward1Ds forwardTransform2;
static typename Transform<T, TT>::Forward1Ds forwardTransform4;
static typename Transform<T, TT>::Forward1Ds forwardTransform8;
static typename Transform<T, TT>::Forward1Ds forwardTransform16;
// Specialized 1D inverse transform pointers
static typename Transform<T, TT>::Inverse1Ds inverseTransform2;
static typename Transform<T, TT>::Inverse1Ds inverseTransform4;
static typename Transform<T, TT>::Inverse1Ds inverseTransform8;
static typename Transform<T, TT>::Inverse1Ds inverseTransform16;
};
/// Explicit static members initialization
#define INITIALIZE_HAAR_TRANSFORM(type, member, value) \
template <typename T, typename TT> \
typename Transform<T, TT>::type HaarTransform<T, TT>::member = value;
// 2D transforms
INITIALIZE_HAAR_TRANSFORM(Forward2D, forwardTransform2D, NULL)
INITIALIZE_HAAR_TRANSFORM(Inverse2D, inverseTransform2D, NULL)
// 1D transforms
INITIALIZE_HAAR_TRANSFORM(Forward1D, forwardTransformN, HaarTransform1D::ForwardTransformN)
INITIALIZE_HAAR_TRANSFORM(Inverse1D, inverseTransformN, HaarTransform1D::InverseTransformN)
// Specialized 1D forward transforms
INITIALIZE_HAAR_TRANSFORM(Forward1Ds, forwardTransform2, HaarTransform1D::ForwardTransform2)
INITIALIZE_HAAR_TRANSFORM(Forward1Ds, forwardTransform4, HaarTransform1D::ForwardTransform4)
INITIALIZE_HAAR_TRANSFORM(Forward1Ds, forwardTransform8, HaarTransform1D::ForwardTransform8)
INITIALIZE_HAAR_TRANSFORM(Forward1Ds, forwardTransform16, HaarTransform1D::ForwardTransform16)
// Specialized 1D inverse transforms
INITIALIZE_HAAR_TRANSFORM(Inverse1Ds, inverseTransform2, HaarTransform1D::InverseTransform2)
INITIALIZE_HAAR_TRANSFORM(Inverse1Ds, inverseTransform4, HaarTransform1D::InverseTransform4)
INITIALIZE_HAAR_TRANSFORM(Inverse1Ds, inverseTransform8, HaarTransform1D::InverseTransform8)
INITIALIZE_HAAR_TRANSFORM(Inverse1Ds, inverseTransform16, HaarTransform1D::InverseTransform16)
} // namespace xphoto
} // namespace cv
#endif
+349
View File
@@ -0,0 +1,349 @@
/*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/xphoto.hpp"
#include "opencv2/core.hpp"
#ifdef OPENCV_ENABLE_NONFREE
#include "bm3d_denoising_invoker_step1.hpp"
#include "bm3d_denoising_invoker_step2.hpp"
#include "bm3d_denoising_transforms.hpp"
#endif
namespace cv
{
namespace xphoto
{
#ifdef OPENCV_ENABLE_NONFREE
template<typename ST, typename D, typename TT>
static void bm3dDenoising_(
const Mat& src,
Mat& basic,
Mat& dst,
const float& h,
const int &templateWindowSize,
const int &searchWindowSize,
const int &hBMStep1,
const int &hBMStep2,
const int &groupSize,
const int &slidingStep,
const float &beta,
const int &step)
{
double granularity = (double)std::max(1., (double)src.total() / (1 << 16));
switch (CV_MAT_CN(src.type())) {
case 1:
if (step == BM3D_STEP1 || step == BM3D_STEPALL)
{
parallel_for_(cv::Range(0, src.rows),
Bm3dDenoisingInvokerStep1<ST, D, float, TT, HaarTransform<ST, TT> >(
src,
basic,
templateWindowSize,
searchWindowSize,
h,
hBMStep1,
groupSize,
slidingStep,
beta),
granularity);
}
if (step == BM3D_STEP2 || step == BM3D_STEPALL)
{
parallel_for_(cv::Range(0, src.rows),
Bm3dDenoisingInvokerStep2<ST, D, float, TT, HaarTransform<ST, TT> >(
src,
basic,
dst,
templateWindowSize,
searchWindowSize,
h,
hBMStep2,
groupSize,
slidingStep,
beta),
granularity);
}
break;
default:
CV_Error(Error::StsBadArg,
"Unsupported number of channels! Only 1 channel is supported at the moment.");
}
}
void bm3dDenoising(
InputArray _src,
InputOutputArray _basic,
OutputArray _dst,
float h,
int templateWindowSize,
int searchWindowSize,
int blockMatchingStep1,
int blockMatchingStep2,
int groupSize,
int slidingStep,
float beta,
int normType,
int step,
int transformType)
{
int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
CV_Assert(1 == cn);
CV_Assert(HAAR == transformType);
CV_Assert(searchWindowSize > templateWindowSize);
CV_Assert(slidingStep > 0 && slidingStep < templateWindowSize);
Size srcSize = _src.size();
switch (step)
{
case BM3D_STEP1:
_basic.create(srcSize, type);
break;
case BM3D_STEP2:
CV_Assert(type == _basic.type());
_dst.create(srcSize, type);
break;
case BM3D_STEPALL:
if (_basic.needed())
_basic.create(srcSize, type);
_dst.create(srcSize, type);
break;
default:
CV_Error(Error::StsBadArg, "Unsupported BM3D step!");
}
Mat src = _src.getMat();
Mat basic = _basic.getMat().empty() ? Mat(srcSize, type) : _basic.getMat();
Mat dst = _dst.getMat();
switch (normType) {
case cv::NORM_L2:
switch (depth) {
case CV_8U:
bm3dDenoising_<uchar, DistSquared, short>(
src,
basic,
dst,
h,
templateWindowSize,
searchWindowSize,
blockMatchingStep1,
blockMatchingStep2,
groupSize,
slidingStep,
beta,
step);
break;
default:
CV_Error(Error::StsBadArg,
"Unsupported depth! Only CV_8U is supported for NORM_L2");
}
break;
case cv::NORM_L1:
switch (depth) {
case CV_8U:
bm3dDenoising_<uchar, DistAbs, short>(
src,
basic,
dst,
h,
templateWindowSize,
searchWindowSize,
blockMatchingStep1,
blockMatchingStep2,
groupSize,
slidingStep,
beta,
step);
break;
case CV_16U:
bm3dDenoising_<ushort, DistAbs, int>(
src,
basic,
dst,
h,
templateWindowSize,
searchWindowSize,
blockMatchingStep1,
blockMatchingStep2,
groupSize,
slidingStep,
beta,
step);
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 bm3dDenoising(
InputArray _src,
OutputArray _dst,
float h,
int templateWindowSize,
int searchWindowSize,
int blockMatchingStep1,
int blockMatchingStep2,
int groupSize,
int slidingStep,
float beta,
int normType,
int step,
int transformType)
{
if (step == BM3D_STEP2)
CV_Error(Error::StsBadArg,
"Unsupported step type! To use BM3D_STEP2 one need to provide basic image.");
Mat basic;
bm3dDenoising(
_src,
basic,
_dst,
h,
templateWindowSize,
searchWindowSize,
blockMatchingStep1,
blockMatchingStep2,
groupSize,
slidingStep,
beta,
normType,
step,
transformType);
if (step == BM3D_STEP1)
_dst.assign(basic);
}
#else
void bm3dDenoising(
InputArray _src,
InputOutputArray _basic,
OutputArray _dst,
float h,
int templateWindowSize,
int searchWindowSize,
int blockMatchingStep1,
int blockMatchingStep2,
int groupSize,
int slidingStep,
float beta,
int normType,
int step,
int transformType)
{
// Empty implementation
CV_UNUSED(_src);
CV_UNUSED(_basic);
CV_UNUSED(_dst);
CV_UNUSED(h);
CV_UNUSED(templateWindowSize);
CV_UNUSED(searchWindowSize);
CV_UNUSED(blockMatchingStep1);
CV_UNUSED(blockMatchingStep2);
CV_UNUSED(groupSize);
CV_UNUSED(slidingStep);
CV_UNUSED(beta);
CV_UNUSED(normType);
CV_UNUSED(step);
CV_UNUSED(transformType);
CV_Error(Error::StsNotImplemented,
"This algorithm is patented and is excluded in this configuration;"
"Set OPENCV_ENABLE_NONFREE CMake option and rebuild the library");
}
void bm3dDenoising(
InputArray _src,
OutputArray _dst,
float h,
int templateWindowSize,
int searchWindowSize,
int blockMatchingStep1,
int blockMatchingStep2,
int groupSize,
int slidingStep,
float beta,
int normType,
int step,
int transformType)
{
// Empty implementation
CV_UNUSED(_src);
CV_UNUSED(_dst);
CV_UNUSED(h);
CV_UNUSED(templateWindowSize);
CV_UNUSED(searchWindowSize);
CV_UNUSED(blockMatchingStep1);
CV_UNUSED(blockMatchingStep2);
CV_UNUSED(groupSize);
CV_UNUSED(slidingStep);
CV_UNUSED(beta);
CV_UNUSED(normType);
CV_UNUSED(step);
CV_UNUSED(transformType);
CV_Error(Error::StsNotImplemented,
"This algorithm is patented and is excluded in this configuration;"
"Set OPENCV_ENABLE_NONFREE CMake option and rebuild the library");
}
#endif
} // namespace xphoto
} // namespace cv
+182
View File
@@ -0,0 +1,182 @@
/*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-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// * 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 <vector>
#include <algorithm>
#include <iterator>
#include <iostream>
#include "opencv2/xphoto.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/core.hpp"
#include "opencv2/core/types.hpp"
namespace cv
{
namespace xphoto
{
void grayDctDenoising(const Mat &, Mat &, const double, const int);
void rgbDctDenoising(const Mat &, Mat &, const double, const int);
void dctDenoising(const Mat &, Mat &, const double, const int);
struct grayDctDenoisingInvoker : public ParallelLoopBody
{
public:
grayDctDenoisingInvoker(const Mat &src, std::vector <Mat> &patches, const double sigma, const int psize);
~grayDctDenoisingInvoker(){};
void operator() (const Range &range) const CV_OVERRIDE;
protected:
const Mat &src;
std::vector <Mat> &patches; // image decomposition into sliding patches
const int psize; // size of block to compute dct
const double sigma; // expected noise standard deviation
const double thresh; // thresholding estimate
void operator =(const grayDctDenoisingInvoker&) const {};
};
grayDctDenoisingInvoker::grayDctDenoisingInvoker(const Mat &_src, std::vector <Mat> &_patches,
const double _sigma, const int _psize)
: src(_src), patches(_patches), psize(_psize), sigma(_sigma), thresh(3*_sigma) {}
void grayDctDenoisingInvoker::operator() (const Range &range) const
{
for (int i = range.start; i <= range.end - 1; ++i)
{
int y = i / (src.cols - psize);
int x = i % (src.cols - psize);
Rect patchNum( x, y, psize, psize );
Mat patch(psize, psize, CV_32FC1);
src(patchNum).copyTo( patch );
dct(patch, patch);
float *data = (float *) patch.data;
for (int k = 0; k < psize*psize; ++k)
data[k] *= fabs(data[k]) > thresh;
idct(patch, patches[i]);
}
}
void grayDctDenoising(const Mat &src, Mat &dst, const double sigma, const int psize)
{
CV_Assert( src.type() == CV_MAKE_TYPE(CV_32F, 1) );
int npixels = (src.rows - psize)*(src.cols - psize);
std::vector <Mat> patches;
for (int i = 0; i < npixels; ++i)
patches.push_back( Mat(psize, psize, CV_32FC1) );
parallel_for_( cv::Range(0, npixels),
grayDctDenoisingInvoker(src, patches, sigma, psize) );
Mat res( src.size(), CV_32FC1, 0.0f ),
num( src.size(), CV_32FC1, 0.0f );
for (int k = 0; k < npixels; ++k)
{
int i = k / (src.cols - psize);
int j = k % (src.cols - psize);
res( Rect(j, i, psize, psize) ) += patches[k];
num( Rect(j, i, psize, psize) ) += Mat::ones(psize, psize, CV_32FC1);
}
res /= num;
res.convertTo( dst, src.type() );
}
void rgbDctDenoising(const Mat &src, Mat &dst, const double sigma, const int psize)
{
CV_Assert( src.type() == CV_MAKE_TYPE(CV_32F, 3) );
cv::Matx33f mt(pow(3.0f, -0.5f), pow(3.0f, -0.5f), pow(3.0f, -0.5f),
pow(2.0f, -0.5f), 0.0f, -pow(2.0f, -0.5f),
pow(6.0f, -0.5f), -2.0f*pow(6.0f, -0.5f), pow(6.0f, -0.5f));
cv::transform(src, dst, mt);
std::vector <Mat> mv;
split(dst, mv);
for (size_t i = 0; i < mv.size(); ++i)
grayDctDenoising(mv[i], mv[i], sigma, psize);
merge(mv, dst);
cv::transform( dst, dst, mt.inv() );
}
/*! This function implements simple dct-based image denoising,
* link: http://www.ipol.im/pub/art/2011/ys-dct/
*
* \param src : source image (rgb, or gray)
* \param dst : destination image
* \param sigma : expected noise standard deviation
* \param psize : size of block side where dct is computed
*/
void dctDenoising(const Mat &src, Mat &dst, const double sigma, const int psize)
{
CV_Assert( src.channels() == 3 || src.channels() == 1 );
int xtype = CV_MAKE_TYPE( CV_32F, src.channels() );
Mat img( src.size(), xtype );
src.convertTo(img, xtype);
if ( img.type() == CV_32FC3 )
rgbDctDenoising( img, img, sigma, psize );
else if ( img.type() == CV_32FC1 )
grayDctDenoising( img, img, sigma, psize );
else
CV_Error_( cv::Error::StsNotImplemented,
("Unsupported source image format (=%d)", img.type()) );
img.convertTo( dst, src.type() );
}
}
}
+386
View File
@@ -0,0 +1,386 @@
/*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 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 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 _CV_GCGRAPH_H_
#define _CV_GCGRAPH_H_
template <class TWeight> class GCGraph
{
public:
GCGraph();
GCGraph( unsigned int vtxCount, unsigned int edgeCount );
~GCGraph();
void create( unsigned int vtxCount, unsigned int edgeCount );
int addVtx();
void addEdges( int i, int j, TWeight w, TWeight revw );
void addTermWeights( int i, TWeight sourceW, TWeight sinkW );
TWeight maxFlow();
bool inSourceSegment( int i );
private:
class Vtx
{
public:
Vtx *next; // initialized and used in maxFlow() only
int parent;
int first;
int ts;
int dist;
TWeight weight;
unsigned char t;
};
class Edge
{
public:
int dst;
int next;
TWeight weight;
};
::std::vector<Vtx> vtcs;
::std::vector<Edge> edges;
TWeight flow;
};
template <class TWeight>
GCGraph<TWeight>::GCGraph()
{
flow = 0;
}
template <class TWeight>
GCGraph<TWeight>::GCGraph( unsigned int vtxCount, unsigned int edgeCount )
{
create( vtxCount, edgeCount );
}
template <class TWeight>
GCGraph<TWeight>::~GCGraph()
{
}
template <class TWeight>
void GCGraph<TWeight>::create( unsigned int vtxCount, unsigned int edgeCount )
{
vtcs.reserve( vtxCount );
edges.reserve( edgeCount + 2 );
flow = 0;
}
template <class TWeight>
int GCGraph<TWeight>::addVtx()
{
Vtx v;
memset( &v, 0, sizeof(Vtx));
vtcs.push_back(v);
return (int)vtcs.size() - 1;
}
template <class TWeight>
void GCGraph<TWeight>::addEdges( int i, int j, TWeight w, TWeight revw )
{
CV_Assert( i>=0 && i<(int)vtcs.size() );
CV_Assert( j>=0 && j<(int)vtcs.size() );
CV_Assert( w>=0 && revw>=0 );
CV_Assert( i != j );
if( !edges.size() )
edges.resize( 2 );
Edge fromI, toI;
fromI.dst = j;
fromI.next = vtcs[i].first;
fromI.weight = w;
vtcs[i].first = (int)edges.size();
edges.push_back( fromI );
toI.dst = i;
toI.next = vtcs[j].first;
toI.weight = revw;
vtcs[j].first = (int)edges.size();
edges.push_back( toI );
}
template <class TWeight>
void GCGraph<TWeight>::addTermWeights( int i, TWeight sourceW, TWeight sinkW )
{
CV_Assert( i>=0 && i<(int)vtcs.size() );
TWeight dw = vtcs[i].weight;
if( dw > 0 )
sourceW += dw;
else
sinkW -= dw;
flow += (sourceW < sinkW) ? sourceW : sinkW;
vtcs[i].weight = sourceW - sinkW;
}
template <class TWeight>
TWeight GCGraph<TWeight>::maxFlow()
{
const int TERMINAL = -1, ORPHAN = -2;
Vtx stub, *nilNode = &stub, *first = nilNode, *last = nilNode;
int curr_ts = 0;
stub.next = nilNode;
Vtx *vtxPtr = &vtcs[0];
Edge *edgePtr = &edges[0];
::std::vector<Vtx*> orphans;
// initialize the active queue and the graph vertices
for( int i = 0; i < (int)vtcs.size(); i++ )
{
Vtx* v = vtxPtr + i;
v->ts = 0;
if( v->weight != 0 )
{
last = last->next = v;
v->dist = 1;
v->parent = TERMINAL;
v->t = v->weight < 0;
}
else
v->parent = 0;
}
first = first->next;
last->next = nilNode;
nilNode->next = 0;
// run the search-path -> augment-graph -> restore-trees loop
for(;;)
{
Vtx* v, *u;
int e0 = -1, ei = 0, ej = 0;
TWeight minWeight, weight;
uint8_t vt;
// grow S & T search trees, find an edge connecting them
while( first != nilNode )
{
v = first;
if( v->parent )
{
vt = v->t;
for( ei = v->first; ei != 0; ei = edgePtr[ei].next )
{
if( edgePtr[ei^vt].weight == 0 )
continue;
u = vtxPtr+edgePtr[ei].dst;
if( !u->parent )
{
u->t = vt;
u->parent = ei ^ 1;
u->ts = v->ts;
u->dist = v->dist + 1;
if( !u->next )
{
u->next = nilNode;
last = last->next = u;
}
continue;
}
if( u->t != vt )
{
e0 = ei ^ vt;
break;
}
if( u->dist > v->dist+1 && u->ts <= v->ts )
{
// reassign the parent
u->parent = ei ^ 1;
u->ts = v->ts;
u->dist = v->dist + 1;
}
}
if( e0 > 0 )
break;
}
// exclude the vertex from the active list
first = first->next;
v->next = 0;
}
if( e0 <= 0 )
break;
// find the minimum edge weight along the path
minWeight = edgePtr[e0].weight;
CV_Assert( minWeight > 0 );
// k = 1: source tree, k = 0: destination tree
for( int k = 1; k >= 0; k-- )
{
for( v = vtxPtr+edgePtr[e0^k].dst;; v = vtxPtr+edgePtr[ei].dst )
{
if( (ei = v->parent) < 0 )
break;
weight = edgePtr[ei^k].weight;
minWeight = MIN(minWeight, weight);
CV_Assert( minWeight > 0 );
}
weight = std::abs( TWeight(v->weight) );
minWeight = MIN(minWeight, weight);
CV_Assert( minWeight > 0 );
}
// modify weights of the edges along the path and collect orphans
edgePtr[e0].weight -= minWeight;
edgePtr[e0^1].weight += minWeight;
flow += minWeight;
// k = 1: source tree, k = 0: destination tree
for( int k = 1; k >= 0; k-- )
{
for( v = vtxPtr+edgePtr[e0^k].dst;; v = vtxPtr+edgePtr[ei].dst )
{
if( (ei = v->parent) < 0 )
break;
edgePtr[ei^(k^1)].weight += minWeight;
if( (edgePtr[ei^k].weight -= minWeight) == 0 )
{
orphans.push_back(v);
v->parent = ORPHAN;
}
}
v->weight = v->weight + minWeight*(1-k*2);
if( v->weight == 0 )
{
orphans.push_back(v);
v->parent = ORPHAN;
}
}
// restore the search trees by finding new parents for the orphans
curr_ts++;
while( !orphans.empty() )
{
Vtx* v2 = orphans.back();
orphans.pop_back();
int d, minDist = INT_MAX;
e0 = 0;
vt = v2->t;
for( ei = v2->first; ei != 0; ei = edgePtr[ei].next )
{
if( edgePtr[ei^(vt^1)].weight == 0 )
continue;
u = vtxPtr+edgePtr[ei].dst;
if( u->t != vt || u->parent == 0 )
continue;
// compute the distance to the tree root
for( d = 0;; )
{
if( u->ts == curr_ts )
{
d += u->dist;
break;
}
ej = u->parent;
d++;
if( ej < 0 )
{
if( ej == ORPHAN )
d = INT_MAX-1;
else
{
u->ts = curr_ts;
u->dist = 1;
}
break;
}
u = vtxPtr+edgePtr[ej].dst;
}
// update the distance
if( ++d < INT_MAX )
{
if( d < minDist )
{
minDist = d;
e0 = ei;
}
for( u = vtxPtr+edgePtr[ei].dst; u->ts != curr_ts; u = vtxPtr+edgePtr[u->parent].dst )
{
u->ts = curr_ts;
u->dist = --d;
}
}
}
if( (v2->parent = e0) > 0 )
{
v2->ts = curr_ts;
v2->dist = minDist;
continue;
}
/* no parent is found */
v2->ts = 0;
for( ei = v2->first; ei != 0; ei = edgePtr[ei].next )
{
u = vtxPtr+edgePtr[ei].dst;
ej = u->parent;
if( u->t != vt || !ej )
continue;
if( edgePtr[ei^(vt^1)].weight && !u->next )
{
u->next = nilNode;
last = last->next = u;
}
if( ej > 0 && vtxPtr+edgePtr[ej].dst == v2 )
{
orphans.push_back(u);
u->parent = ORPHAN;
}
}
}
}
return flow;
}
template <class TWeight>
bool GCGraph<TWeight>::inSourceSegment( int i )
{
CV_Assert( i>=0 && i<(int)vtcs.size() );
return vtcs[i].t == 0;
}
#endif
@@ -0,0 +1,350 @@
/*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-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// * 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.hpp"
#include "opencv2/core/hal/intrin.hpp"
#include "opencv2/xphoto.hpp"
namespace cv
{
namespace xphoto
{
void calculateChannelSums(uint &sumB, uint &sumG, uint &sumR, uchar *src_data, int src_len, float thresh);
void calculateChannelSums(uint64 &sumB, uint64 &sumG, uint64 &sumR, ushort *src_data, int src_len, float thresh);
class GrayworldWBImpl CV_FINAL : public GrayworldWB
{
private:
float thresh;
public:
GrayworldWBImpl() { thresh = 0.9f; }
float getSaturationThreshold() const CV_OVERRIDE { return thresh; }
void setSaturationThreshold(float val) CV_OVERRIDE { thresh = val; }
void balanceWhite(InputArray _src, OutputArray _dst) CV_OVERRIDE
{
CV_Assert(!_src.empty());
CV_Assert(_src.isContinuous());
CV_Assert(_src.type() == CV_8UC3 || _src.type() == CV_16UC3);
Mat src = _src.getMat();
int N = src.cols * src.rows, N3 = N * 3;
double dsumB = 0.0, dsumG = 0.0, dsumR = 0.0;
if (src.type() == CV_8UC3)
{
uint sumB = 0, sumG = 0, sumR = 0;
calculateChannelSums(sumB, sumG, sumR, src.ptr<uchar>(), N3, thresh);
dsumB = (double)sumB;
dsumG = (double)sumG;
dsumR = (double)sumR;
}
else if (src.type() == CV_16UC3)
{
uint64 sumB = 0, sumG = 0, sumR = 0;
calculateChannelSums(sumB, sumG, sumR, src.ptr<ushort>(), N3, thresh);
dsumB = (double)sumB;
dsumG = (double)sumG;
dsumR = (double)sumR;
}
// Find inverse of averages
double max_sum = max(dsumB, max(dsumR, dsumG));
const double eps = 0.1;
float dinvB = dsumB < eps ? 0.f : (float)(max_sum / dsumB),
dinvG = dsumG < eps ? 0.f : (float)(max_sum / dsumG),
dinvR = dsumR < eps ? 0.f : (float)(max_sum / dsumR);
// Use the inverse of averages as channel gains:
applyChannelGains(src, _dst, dinvB, dinvG, dinvR);
}
};
/* Computes sums for each channel, while ignoring saturated pixels which are determined by thresh
* (version for CV_8UC3)
*/
void calculateChannelSums(uint &sumB, uint &sumG, uint &sumR, uchar *src_data, int src_len, float thresh)
{
sumB = sumG = sumR = 0;
ushort thresh255 = (ushort)cvRound(thresh * 255);
int i = 0;
#if CV_SIMD128
v_uint8x16 v_inB, v_inG, v_inR, v_min_val, v_max_val;
v_uint16x8 v_iB1, v_iB2, v_iG1, v_iG2, v_iR1, v_iR2;
v_uint16x8 v_min1, v_min2, v_max1, v_max2, v_m1, v_m2;
v_uint16x8 v_255 = v_setall_u16(255), v_thresh = v_setall_u16(thresh255);
v_uint32x4 v_uint1, v_uint2;
v_uint32x4 v_SB = v_setzero_u32(), v_SG = v_setzero_u32(), v_SR = v_setzero_u32();
for (; i < src_len - 47; i += 48)
{
// Load 3x uint8x16 and deinterleave into vectors of each channel
v_load_deinterleave(&src_data[i], v_inB, v_inG, v_inR);
// Get min and max
v_min_val = v_min(v_inB, v_min(v_inG, v_inR));
v_max_val = v_max(v_inB, v_max(v_inG, v_inR));
// Split into two ushort vectors per channel
v_expand(v_inB, v_iB1, v_iB2);
v_expand(v_inG, v_iG1, v_iG2);
v_expand(v_inR, v_iR1, v_iR2);
v_expand(v_min_val, v_min1, v_min2);
v_expand(v_max_val, v_max1, v_max2);
// Calculate masks
v_m1 = v_not(v_gt(v_mul_wrap(v_sub(v_max1, v_min1), v_255), v_mul_wrap(v_thresh, v_max1)));
v_m2 = v_not(v_gt(v_mul_wrap(v_sub(v_max2, v_min2), v_255), v_mul_wrap(v_thresh, v_max2)));
// Apply masks
v_iB1 = v_add(v_and(v_iB1, v_m1), v_and(v_iB2, v_m2));
v_iG1 = v_add(v_and(v_iG1, v_m1), v_and(v_iG2, v_m2));
v_iR1 = v_add(v_and(v_iR1, v_m1), v_and(v_iR2, v_m2));
// Split and add to the sums:
v_expand(v_iB1, v_uint1, v_uint2);
v_SB = v_add(v_SB, v_add(v_uint1, v_uint2));
v_expand(v_iG1, v_uint1, v_uint2);
v_SG = v_add(v_SG, v_add(v_uint1, v_uint2));
v_expand(v_iR1, v_uint1, v_uint2);
v_SR = v_add(v_SR, v_add(v_uint1, v_uint2));
}
sumB = v_reduce_sum(v_SB);
sumG = v_reduce_sum(v_SG);
sumR = v_reduce_sum(v_SR);
#endif
unsigned int minRGB, maxRGB;
for (; i < src_len; i += 3)
{
minRGB = min(src_data[i], min(src_data[i + 1], src_data[i + 2]));
maxRGB = max(src_data[i], max(src_data[i + 1], src_data[i + 2]));
if ((maxRGB - minRGB) * 255 > thresh255 * maxRGB)
continue;
sumB += src_data[i];
sumG += src_data[i + 1];
sumR += src_data[i + 2];
}
}
/* Computes sums for each channel, while ignoring saturated pixels which are determined by thresh
* (version for CV_16UC3)
*/
void calculateChannelSums(uint64 &sumB, uint64 &sumG, uint64 &sumR, ushort *src_data, int src_len, float thresh)
{
sumB = sumG = sumR = 0;
uint thresh65535 = cvRound(thresh * 65535);
int i = 0;
#if CV_SIMD128
v_uint16x8 v_inB, v_inG, v_inR, v_min_val, v_max_val;
v_uint32x4 v_iB1, v_iB2, v_iG1, v_iG2, v_iR1, v_iR2;
v_uint32x4 v_min1, v_min2, v_max1, v_max2, v_m1, v_m2;
v_uint32x4 v_65535 = v_setall_u32(65535), v_thresh = v_setall_u32(thresh65535);
v_uint64x2 v_u64_1, v_u64_2;
v_uint64x2 v_SB = v_setzero_u64(), v_SG = v_setzero_u64(), v_SR = v_setzero_u64();
for (; i < src_len - 23; i += 24)
{
// Load 3x uint16x8 and deinterleave into vectors of each channel
v_load_deinterleave(&src_data[i], v_inB, v_inG, v_inR);
// Get min and max
v_min_val = v_min(v_inB, v_min(v_inG, v_inR));
v_max_val = v_max(v_inB, v_max(v_inG, v_inR));
// Split into two uint vectors per channel
v_expand(v_inB, v_iB1, v_iB2);
v_expand(v_inG, v_iG1, v_iG2);
v_expand(v_inR, v_iR1, v_iR2);
v_expand(v_min_val, v_min1, v_min2);
v_expand(v_max_val, v_max1, v_max2);
// Calculate masks
v_m1 = v_not(v_gt(v_mul(v_sub(v_max1, v_min1), v_65535), v_mul(v_thresh, v_max1)));
v_m2 = v_not(v_gt(v_mul(v_sub(v_max2, v_min2), v_65535), v_mul(v_thresh, v_max2)));
// Apply masks
v_iB1 = v_add(v_and(v_iB1, v_m1), v_and(v_iB2, v_m2));
v_iG1 = v_add(v_and(v_iG1, v_m1), v_and(v_iG2, v_m2));
v_iR1 = v_add(v_and(v_iR1, v_m1), v_and(v_iR2, v_m2));
// Split and add to the sums:
v_expand(v_iB1, v_u64_1, v_u64_2);
v_SB = v_add(v_SB, v_add(v_u64_1, v_u64_2));
v_expand(v_iG1, v_u64_1, v_u64_2);
v_SG = v_add(v_SG, v_add(v_u64_1, v_u64_2));
v_expand(v_iR1, v_u64_1, v_u64_2);
v_SR = v_add(v_SR, v_add(v_u64_1, v_u64_2));
}
// Perform final reduction
uint64 sum_arr[2];
v_store(sum_arr, v_SB);
sumB = sum_arr[0] + sum_arr[1];
v_store(sum_arr, v_SG);
sumG = sum_arr[0] + sum_arr[1];
v_store(sum_arr, v_SR);
sumR = sum_arr[0] + sum_arr[1];
#endif
unsigned int minRGB, maxRGB;
for (; i < src_len; i += 3)
{
minRGB = min(src_data[i], min(src_data[i + 1], src_data[i + 2]));
maxRGB = max(src_data[i], max(src_data[i + 1], src_data[i + 2]));
if ((maxRGB - minRGB) * 65535 > thresh65535 * maxRGB)
continue;
sumB += src_data[i];
sumG += src_data[i + 1];
sumR += src_data[i + 2];
}
}
void applyChannelGains(InputArray _src, OutputArray _dst, float gainB, float gainG, float gainR)
{
Mat src = _src.getMat();
CV_Assert(!src.empty());
CV_Assert(src.isContinuous());
CV_Assert(src.type() == CV_8UC3 || src.type() == CV_16UC3);
_dst.create(src.size(), src.type());
Mat dst = _dst.getMat();
int N3 = 3 * src.cols * src.rows;
int i = 0;
// Scale gains by their maximum (fixed point approximation works only when all gains are <=1)
float gain_max = max(gainB, max(gainG, gainR));
if (gain_max > 0)
{
gainB /= gain_max;
gainG /= gain_max;
gainR /= gain_max;
}
if (src.type() == CV_8UC3)
{
// Fixed point arithmetic, mul by 2^8 then shift back 8 bits
int i_gainB = cvRound(gainB * (1 << 8)), i_gainG = cvRound(gainG * (1 << 8)),
i_gainR = cvRound(gainR * (1 << 8));
const uchar *src_data = src.ptr<uchar>();
uchar *dst_data = dst.ptr<uchar>();
#if CV_SIMD128
v_uint8x16 v_inB, v_inG, v_inR;
v_uint8x16 v_outB, v_outG, v_outR;
v_uint16x8 v_sB1, v_sB2, v_sG1, v_sG2, v_sR1, v_sR2;
v_uint16x8 v_gainB = v_setall_u16((ushort)i_gainB), v_gainG = v_setall_u16((ushort)i_gainG),
v_gainR = v_setall_u16((ushort)i_gainR);
for (; i < N3 - 47; i += 48)
{
// Load 3x uint8x16 and deinterleave into vectors of each channel
v_load_deinterleave(&src_data[i], v_inB, v_inG, v_inR);
// Split into two ushort vectors per channel
v_expand(v_inB, v_sB1, v_sB2);
v_expand(v_inG, v_sG1, v_sG2);
v_expand(v_inR, v_sR1, v_sR2);
// Multiply by gains
v_sB1 = v_shr(v_mul_wrap(v_sB1, v_gainB), 8);
v_sB2 = v_shr(v_mul_wrap(v_sB2, v_gainB), 8);
v_sG1 = v_shr(v_mul_wrap(v_sG1, v_gainG), 8);
v_sG2 = v_shr(v_mul_wrap(v_sG2, v_gainG), 8);
v_sR1 = v_shr(v_mul_wrap(v_sR1, v_gainR), 8);
v_sR2 = v_shr(v_mul_wrap(v_sR2, v_gainR), 8);
// Pack into vectors of v_uint8x16
v_store_interleave(&dst_data[i], v_pack(v_sB1, v_sB2), v_pack(v_sG1, v_sG2), v_pack(v_sR1, v_sR2));
}
#endif
for (; i < N3; i += 3)
{
dst_data[i] = (uchar)((src_data[i] * i_gainB) >> 8);
dst_data[i + 1] = (uchar)((src_data[i + 1] * i_gainG) >> 8);
dst_data[i + 2] = (uchar)((src_data[i + 2] * i_gainR) >> 8);
}
}
else if (src.type() == CV_16UC3)
{
// Fixed point arithmetic, mul by 2^16 then shift back 16 bits
int i_gainB = cvRound(gainB * (1 << 16)), i_gainG = cvRound(gainG * (1 << 16)),
i_gainR = cvRound(gainR * (1 << 16));
const ushort *src_data = src.ptr<ushort>();
ushort *dst_data = dst.ptr<ushort>();
#if CV_SIMD128
v_uint16x8 v_inB, v_inG, v_inR;
v_uint16x8 v_outB, v_outG, v_outR;
v_uint32x4 v_sB1, v_sB2, v_sG1, v_sG2, v_sR1, v_sR2;
v_uint32x4 v_gainB = v_setall_u32((uint)i_gainB), v_gainG = v_setall_u32((uint)i_gainG),
v_gainR = v_setall_u32((uint)i_gainR);
for (; i < N3 - 23; i += 24)
{
// Load 3x uint16x8 and deinterleave into vectors of each channel
v_load_deinterleave(&src_data[i], v_inB, v_inG, v_inR);
// Split into two uint vectors per channel
v_expand(v_inB, v_sB1, v_sB2);
v_expand(v_inG, v_sG1, v_sG2);
v_expand(v_inR, v_sR1, v_sR2);
// Multiply by scaling factors
v_sB1 = v_shr(v_mul(v_sB1, v_gainB), 16);
v_sB2 = v_shr(v_mul(v_sB2, v_gainB), 16);
v_sG1 = v_shr(v_mul(v_sG1, v_gainG), 16);
v_sG2 = v_shr(v_mul(v_sG2, v_gainG), 16);
v_sR1 = v_shr(v_mul(v_sR1, v_gainR), 16);
v_sR2 = v_shr(v_mul(v_sR2, v_gainR), 16);
// Pack into vectors of v_uint16x8
v_store_interleave(&dst_data[i], v_pack(v_sB1, v_sB2), v_pack(v_sG1, v_sG2), v_pack(v_sR1, v_sR2));
}
#endif
for (; i < N3; i += 3)
{
dst_data[i] = (ushort)((src_data[i] * i_gainB) >> 16);
dst_data[i + 1] = (ushort)((src_data[i + 1] * i_gainG) >> 16);
dst_data[i + 2] = (ushort)((src_data[i + 2] * i_gainR) >> 16);
}
}
}
Ptr<GrayworldWB> createGrayworldWB() { return makePtr<GrayworldWBImpl>(); }
}
}
+424
View File
@@ -0,0 +1,424 @@
/*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
// (3-clause BSD License)
//
// Copyright (C) 2000-2019, Intel Corporation, all rights reserved.
// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
// Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved.
// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
// Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved.
// Copyright (C) 2015-2016, Itseez 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.
//
// * Neither the names of the copyright holders nor the names of the contributors
// may 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 <vector>
#include <stack>
#include <limits>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <fstream>
#include <time.h>
#include <functional>
#include <string>
#include <tuple>
#include "opencv2/xphoto.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/core.hpp"
#include "opencv2/core/types.hpp"
#include "photomontage.hpp"
#include "annf.hpp"
#include "advanced_types.hpp"
#include "inpainting_fsr.impl.hpp"
namespace cv
{
namespace xphoto
{
template <typename Tp, unsigned int cn>
static void shiftMapInpaint( const Mat &_src, const Mat &_mask, Mat &dst,
const int nTransform = 60, const int psize = 8, const cv::Point2i dsize = cv::Point2i(800, 600) )
{
/** Preparing input **/
cv::Mat src, mask, img, dmask, ddmask;
const float ls = std::max(/**/ std::min( /*...*/
std::max(_src.rows, _src.cols)/float(dsize.x),
std::min(_src.rows, _src.cols)/float(dsize.y)
), 1.0f /**/);
cv::resize(_mask, mask, _mask.size()/ls, 0, 0, cv::INTER_NEAREST);
cv::resize(_src, src, _src.size()/ls, 0, 0, cv::INTER_AREA);
src.convertTo( img, CV_32F );
img.setTo(0, ~(mask > 0));
cv::erode( mask, dmask, cv::Mat(), cv::Point(-1,-1), 2);
cv::erode(dmask, ddmask, cv::Mat(), cv::Point(-1,-1), 2);
std::vector <Point2i> pPath;
cv::Mat_<int> backref( ddmask.size(), int(-1) );
for (int i = 0; i < ddmask.rows; ++i)
{
uint8_t *dmask_data = (uint8_t *) ddmask.template ptr<uint8_t>(i);
int *backref_data = (int *) backref.template ptr< int >(i);
for (int j = 0; j < ddmask.cols; ++j)
if (dmask_data[j] == 0)
{
backref_data[j] = int(pPath.size());
pPath.push_back( cv::Point(j, i) );
}
}
/** ANNF computation **/
std::vector <cv::Point2i> transforms( nTransform );
dominantTransforms(img, transforms, nTransform, psize);
transforms.push_back( cv::Point2i(0, 0) );
/** Warping **/
std::vector <std::vector <cv::Vec <float, cn> > > pointSeq( pPath.size() ); // source image transformed with transforms
std::vector <int> labelSeq( pPath.size() ); // resulting label sequence
std::vector <std::vector <int> > linkIdx( pPath.size() ); // neighbor links for pointSeq elements
std::vector <std::vector <unsigned char > > maskSeq( pPath.size() ); // corresponding mask
for (size_t i = 0; i < pPath.size(); ++i)
{
uint8_t xmask = dmask.template at<uint8_t>(pPath[i]);
for (int j = 0; j < nTransform + 1; ++j)
{
cv::Point2i u = pPath[i] + transforms[j];
unsigned char vmask = 0;
cv::Vec <float, cn> vimg = 0;
if ( u.y < src.rows && u.y >= 0
&& u.x < src.cols && u.x >= 0 )
{
if ( xmask == 0 || j == nTransform )
vmask = mask.template at<uint8_t>(u);
vimg = img.template at<cv::Vec<float, cn> >(u);
}
maskSeq[i].push_back(vmask);
pointSeq[i].push_back(vimg);
if (vmask != 0)
labelSeq[i] = j;
}
cv::Point2i p[] = {
pPath[i] + cv::Point2i(0, +1),
pPath[i] + cv::Point2i(+1, 0)
};
for (uint j = 0; j < sizeof(p)/sizeof(cv::Point2i); ++j)
if ( p[j].y < src.rows && p[j].y >= 0 &&
p[j].x < src.cols && p[j].x >= 0 )
linkIdx[i].push_back( backref(p[j]) );
else
linkIdx[i].push_back( -1 );
}
/** Stitching **/
photomontage( pointSeq, maskSeq, linkIdx, labelSeq );
/** Upscaling **/
if (ls != 1)
{
_src.convertTo( img, CV_32F );
std::vector <Point2i> __pPath = pPath; pPath.clear();
cv::Mat_<int> __backref( img.size(), -1 );
std::vector <std::vector <cv::Vec <float, cn> > > __pointSeq = pointSeq; pointSeq.clear();
std::vector <int> __labelSeq = labelSeq; labelSeq.clear();
std::vector <std::vector <int> > __linkIdx = linkIdx; linkIdx.clear();
std::vector <std::vector <unsigned char > > __maskSeq = maskSeq; maskSeq.clear();
for (size_t i = 0; i < __pPath.size(); ++i)
{
cv::Point2i p[] = {
__pPath[i] + cv::Point2i(0, -1),
__pPath[i] + cv::Point2i(-1, 0)
};
for (uint j = 0; j < sizeof(p)/sizeof(cv::Point2i); ++j)
if ( p[j].y < src.rows && p[j].y >= 0 &&
p[j].x < src.cols && p[j].x >= 0 )
__linkIdx[i].push_back( backref(p[j]) );
else
__linkIdx[i].push_back( -1 );
}
for (size_t k = 0; k < __pPath.size(); ++k)
{
int clabel = __labelSeq[k];
int nearSeam = 0;
for (size_t i = 0; i < __linkIdx[k].size(); ++i)
nearSeam |= ( __linkIdx[k][i] == -1
|| clabel != __labelSeq[__linkIdx[k][i]] );
if (nearSeam != 0)
for (int i = 0; i < ls; ++i)
for (int j = 0; j < ls; ++j)
{
cv::Point2i u = ls*(__pPath[k] + transforms[__labelSeq[k]]) + cv::Point2i(j, i);
pPath.push_back( ls*__pPath[k] + cv::Point2i(j, i) );
labelSeq.push_back( 0 );
__backref(i, j) = int( pPath.size() );
cv::Point2i dv[] = {
cv::Point2i(0, 0),
cv::Point2i(-1, 0),
cv::Point2i(+1, 0),
cv::Point2i(0, -1),
cv::Point2i(0, +1)
};
std::vector <cv::Vec <float, cn> > pointVec;
std::vector <uint8_t> maskVec;
for (uint q = 0; q < sizeof(dv)/sizeof(cv::Point2i); ++q)
if (u.x + dv[q].x >= 0 && u.x + dv[q].x < img.cols
&& u.y + dv[q].y >= 0 && u.y + dv[q].y < img.rows)
{
pointVec.push_back(img.template at<cv::Vec <float, cn> >(u + dv[q]));
maskVec.push_back(_mask.template at<uint8_t>(u + dv[q]));
}
else
{
pointVec.push_back( cv::Vec <float, cn>::all(0) );
maskVec.push_back( 0 );
}
pointSeq.push_back(pointVec);
maskSeq.push_back(maskVec);
}
else
{
cv::Point2i fromIdx = ls*(__pPath[k] + transforms[__labelSeq[k]]),
toIdx = ls*__pPath[k];
for (int i = 0; i < ls; ++i)
{
cv::Vec <float, cn> *from = img.template ptr<cv::Vec <float, cn> >(fromIdx.y + i) + fromIdx.x;
cv::Vec <float, cn> *to = img.template ptr<cv::Vec <float, cn> >(toIdx.y + i) + toIdx.x;
for (int j = 0; j < ls; ++j)
to[j] = from[j];
}
}
}
for (size_t i = 0; i < pPath.size(); ++i)
{
cv::Point2i p[] = {
pPath[i] + cv::Point2i(0, +1),
pPath[i] + cv::Point2i(+1, 0)
};
std::vector <int> linkVec;
for (uint j = 0; j < sizeof(p)/sizeof(cv::Point2i); ++j)
if ( p[j].y < src.rows && p[j].y >= 0 &&
p[j].x < src.cols && p[j].x >= 0 )
linkVec.push_back( __backref(p[j]) );
else
linkVec.push_back( -1 );
linkIdx.push_back(linkVec);
}
photomontage( pointSeq, maskSeq, linkIdx, labelSeq );
}
/** Writing result **/
for (size_t i = 0; i < labelSeq.size(); ++i)
{
if (pPath[i].x >= img.cols || pPath[i].y >= img.rows)
continue;
cv::Vec <float, cn> val = pointSeq[i][labelSeq[i]];
img.template at<cv::Vec <float, cn> >(pPath[i]) = val;
}
img.convertTo( dst, dst.type() );
}
template <typename Tp, unsigned int cn>
void inpaint(const Mat &src, const Mat &mask, Mat &dst, const int algorithmType)
{
dst.create( src.size(), src.type() );
switch ( algorithmType )
{
case xphoto::INPAINT_SHIFTMAP:
shiftMapInpaint <Tp, cn>(src, mask, dst);
break;
default:
CV_Error_( Error::StsNotImplemented,
("Unsupported algorithm type (=%d)", algorithmType) );
break;
}
}
static
void inpaint_shiftmap(const Mat &src, const Mat &mask, Mat &dst, const int algorithmType)
{
switch ( src.type() )
{
case CV_8SC1:
inpaint <char, 1>( src, mask, dst, algorithmType );
break;
case CV_8SC2:
inpaint <char, 2>( src, mask, dst, algorithmType );
break;
case CV_8SC3:
inpaint <char, 3>( src, mask, dst, algorithmType );
break;
case CV_8SC4:
inpaint <char, 4>( src, mask, dst, algorithmType );
break;
case CV_8UC1:
inpaint <uint8_t, 1>( src, mask, dst, algorithmType );
break;
case CV_8UC2:
inpaint <uint8_t, 2>( src, mask, dst, algorithmType );
break;
case CV_8UC3:
inpaint <uint8_t, 3>( src, mask, dst, algorithmType );
break;
case CV_8UC4:
inpaint <uint8_t, 4>( src, mask, dst, algorithmType );
break;
case CV_16SC1:
inpaint <short, 1>( src, mask, dst, algorithmType );
break;
case CV_16SC2:
inpaint <short, 2>( src, mask, dst, algorithmType );
break;
case CV_16SC3:
inpaint <short, 3>( src, mask, dst, algorithmType );
break;
case CV_16SC4:
inpaint <short, 4>( src, mask, dst, algorithmType );
break;
case CV_16UC1:
inpaint <ushort, 1>( src, mask, dst, algorithmType );
break;
case CV_16UC2:
inpaint <ushort, 2>( src, mask, dst, algorithmType );
break;
case CV_16UC3:
inpaint <ushort, 3>( src, mask, dst, algorithmType );
break;
case CV_16UC4:
inpaint <ushort, 4>( src, mask, dst, algorithmType );
break;
case CV_32SC1:
inpaint <int, 1>( src, mask, dst, algorithmType );
break;
case CV_32SC2:
inpaint <int, 2>( src, mask, dst, algorithmType );
break;
case CV_32SC3:
inpaint <int, 3>( src, mask, dst, algorithmType );
break;
case CV_32SC4:
inpaint <int, 4>( src, mask, dst, algorithmType );
break;
case CV_32FC1:
inpaint <float, 1>( src, mask, dst, algorithmType );
break;
case CV_32FC2:
inpaint <float, 2>( src, mask, dst, algorithmType );
break;
case CV_32FC3:
inpaint <float, 3>( src, mask, dst, algorithmType );
break;
case CV_32FC4:
inpaint <float, 4>( src, mask, dst, algorithmType );
break;
case CV_64FC1:
inpaint <double, 1>( src, mask, dst, algorithmType );
break;
case CV_64FC2:
inpaint <double, 2>( src, mask, dst, algorithmType );
break;
case CV_64FC3:
inpaint <double, 3>( src, mask, dst, algorithmType );
break;
case CV_64FC4:
inpaint <double, 4>( src, mask, dst, algorithmType );
break;
default:
CV_Error_( Error::StsNotImplemented,
("Unsupported source image format (=%d)",
src.type()) );
}
}
void inpaint(const Mat &src, const Mat &mask, Mat &dst, const int algorithmType)
{
CV_Assert(!src.empty());
CV_Assert(!mask.empty());
CV_CheckTypeEQ(mask.type(), CV_8UC1, "");
CV_Assert(src.rows == mask.rows && src.cols == mask.cols);
switch (algorithmType)
{
case xphoto::INPAINT_SHIFTMAP:
return inpaint_shiftmap(src, mask, dst, algorithmType);
case xphoto::INPAINT_FSR_BEST:
case xphoto::INPAINT_FSR_FAST:
return inpaint_fsr(src, mask, dst, algorithmType);
}
CV_Error_(Error::StsNotImplemented, ("Unsupported inpainting algorithm type (=%d)", algorithmType));
}
}} // namespace
+826
View File
@@ -0,0 +1,826 @@
// 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.
// This is not a standalone header, see inpainting.cpp
namespace cv
{
namespace xphoto
{
struct fsr_parameters
{
// default variables
int block_size = 16;
double conc_weighting = 0.5;
double rhos[4] = { 0.80, 0.70, 0.66, 0.64 };
double threshold_stddev_Y[3] = { 0.014, 0.030, 0.090 };
double threshold_stddev_Cx[3] = { 0.006, 0.010, 0.028 };
// quality profile dependent variables
int block_size_min, fft_size, max_iter, min_iter, iter_const;
double orthogonality_correction;
fsr_parameters(const int quality)
{
if (quality == xphoto::INPAINT_FSR_BEST)
{
block_size_min = 2;
fft_size = 64;
max_iter = 400;
min_iter = 50;
iter_const = 2000;
orthogonality_correction = 0.2;
}
else if (quality == xphoto::INPAINT_FSR_FAST)
{
block_size_min = 4;
fft_size = 32;
max_iter = 100;
min_iter = 20;
iter_const = 1000;
orthogonality_correction = 0.5;
}
else
{
CV_Error(Error::StsBadArg, "Unknown quality level set, supported: FAST, BEST");
}
}
};
static void
icvSgnMat(const Mat& src, Mat& dst) {
dst = Mat::zeros(src.size(), CV_64F);
for (int y = 0; y < src.rows; ++y)
{
for (int x = 0; x < src.cols; ++x)
{
double curr_val = src.at<double>(y,x);
if (curr_val > 0)
{
dst.at<double>(y,x) = 1;
}
else if (curr_val)
{
dst.at<double>(y,x) = -1;
}
}
}
}
static double
icvStandardDeviation(const Mat& distorted_block_2d, const Mat& error_mask_2d) {
if (countNonZero(error_mask_2d) < 1)
{
return NAN; // block with no undistorted pixels shouldn't be chosen for processing (only if block_size_min is reached)
}
Scalar tmp_stddev, tmp_mean;
Mat mask8u;
error_mask_2d.convertTo(mask8u, CV_8U, 2.0);
meanStdDev(distorted_block_2d, tmp_mean, tmp_stddev, mask8u);
double sigma_n = tmp_stddev[0] / 255;
if (sigma_n < 0)
{
sigma_n = 0;
}
else if (sigma_n > 1)
{
sigma_n = 1;
}
return sigma_n;
}
static void
icvExtrapolateBlock(Mat& distorted_block, Mat& error_mask, fsr_parameters& fsr_params, double rho, double normedStdDev, Mat& extrapolated_block)
{
double fft_size = fsr_params.fft_size;
double orthogonality_correction = fsr_params.orthogonality_correction;
int M = distorted_block.rows;
int N = distorted_block.cols;
int fft_x_offset = cvFloor((fft_size - N) / 2);
int fft_y_offset = cvFloor((fft_size - M) / 2);
// weighting function
Mat w = Mat::zeros(fsr_params.fft_size, fsr_params.fft_size, CV_64F);
error_mask.copyTo(w(Range(fft_y_offset, fft_y_offset + M), Range(fft_x_offset, fft_x_offset + N)));
for (int u = 0; u < fft_size; ++u)
{
for (int v = 0; v < fft_size; ++v)
{
w.at<double>(u, v) *= std::pow(rho, std::sqrt(std::pow(u + 0.5 - (fft_y_offset + M / 2), 2) + std::pow(v + 0.5 - (fft_x_offset + N / 2), 2)));
}
}
Mat W;
dft(w, W, DFT_COMPLEX_OUTPUT);
Mat W_padded;
hconcat(W, W, W_padded);
vconcat(W_padded, W_padded, W_padded);
// frequency weighting
Mat frequency_weighting = Mat::ones(fsr_params.fft_size, fsr_params.fft_size / 2 + 1, CV_64F);
for (int y = 0; y < fft_size; ++y)
{
for (int x = 0; x < (fft_size / 2 + 1); ++x)
{
double y2 = fft_size / 2 - std::abs(y - fft_size / 2);
double x2 = fft_size / 2 - std::abs(x - fft_size / 2);
frequency_weighting.at<double>(y, x) = 1 - std::sqrt(x2*x2 + y2 * y2)*std::sqrt(2) / fft_size;
}
}
// pad image to fft window size
Mat f(Size(fsr_params.fft_size, fsr_params.fft_size), CV_64F, Scalar::all(0));
distorted_block.copyTo(f(Range(fft_y_offset, fft_y_offset + M), Range(fft_x_offset, fft_x_offset + N)));
// create initial model
Mat G = Mat::zeros(fsr_params.fft_size, fsr_params.fft_size, CV_64FC2); // complex
// calculate initial residual
Mat Rw_tmp, Rw;
dft(f.mul(w), Rw_tmp, DFT_COMPLEX_OUTPUT);
Rw = Rw_tmp(Range(0, fsr_params.fft_size), Range(0, fsr_params.fft_size / 2 + 1));
// estimate ideal number of iterations (GenserIWSSIP2017)
// calculate stddev if not available (e.g., for smallest block size)
if (normedStdDev == 0) {
normedStdDev = icvStandardDeviation(distorted_block, error_mask);
}
int num_iters = cvRound(fsr_params.iter_const * normedStdDev);
if (num_iters < fsr_params.min_iter) {
num_iters = fsr_params.min_iter;
}
else if (num_iters > fsr_params.max_iter) {
num_iters = fsr_params.max_iter;
}
int iter_counter = 0;
while (iter_counter < num_iters)
{ // Spectral Constrained FSE (GenserIWSSIP2018)
Mat projection_distances(Rw.size(), CV_64F);
Mat Rw_mag = Mat(Rw.size(), CV_64F);
std::vector<Mat> channels(2);
split(Rw, channels);
magnitude(channels[0], channels[1], Rw_mag);
projection_distances = Rw_mag.mul(frequency_weighting);
double minVal, maxVal;
int maxLocx = -1;
int maxLocy = -1;
minMaxLoc(projection_distances, &minVal, &maxVal);
for (int y = 0; y < projection_distances.rows; ++y)
{ // assure that first appearance of max Value is selected
for (int x = 0; x < projection_distances.cols; ++x)
{
if (std::abs(projection_distances.at<double>(y, x) - maxVal) < 0.001)
{
maxLocy = y;
maxLocx = x;
break;
}
}
if (maxLocy != -1)
{
break;
}
}
int bf2select = maxLocy + maxLocx * projection_distances.rows;
int v = static_cast<int>(std::max(0.0, std::floor(bf2select / fft_size)));
int u = static_cast<int>(std::max(0, bf2select % fsr_params.fft_size));
// exclude second half of first and middle col
if ((v == 0 && u > fft_size / 2) || (v == fft_size / 2 && u > fft_size / 2))
{
int u_prev = u;
u = fsr_params.fft_size - u;
Rw.at<std::complex<double> >(u, v) = std::conj(Rw.at<std::complex<double> >(u_prev, v));
}
// calculate complex conjugate solution
int u_cj = -1;
int v_cj = -1;
// fill first lower col (copy from first upper col)
if (u >= 1 && u < fft_size / 2 && v == 0)
{
u_cj = fsr_params.fft_size - u;
v_cj = v;
}
// fill middle lower col (copy from first middle col)
if (u >= 1 && u < fft_size / 2 && v == fft_size / 2)
{
u_cj = fsr_params.fft_size - u;
v_cj = v;
}
// fill first row right (copy from first row left)
if (u == 0 && v >= 1 && v < fft_size / 2)
{
u_cj = u;
v_cj = fsr_params.fft_size - v;
}
// fill middle row right (copy from middle row left)
if (u == fft_size / 2 && v >= 1 && v < fft_size / 2)
{
u_cj = u;
v_cj = fsr_params.fft_size - v;
}
// fill cell upper right (copy from lower cell left)
if (u >= fft_size / 2 + 1 && v >= 1 && v < fft_size / 2)
{
u_cj = fsr_params.fft_size - u;
v_cj = fsr_params.fft_size - v;
}
// fill cell lower right (copy from upper cell left)
if (u >= 1 && u < fft_size / 2 && v >= 1 && v < fft_size / 2)
{
u_cj = fsr_params.fft_size - u;
v_cj = fsr_params.fft_size - v;
}
/// add coef to model and update residual
if (u_cj != -1 && v_cj != -1)
{
std::complex< double> expansion_coefficient = orthogonality_correction * Rw.at< std::complex<double> >(u, v) / W.at<std::complex<double> >(0, 0);
G.at< std::complex<double> >(u, v) += fft_size * fft_size * expansion_coefficient;
G.at< std::complex<double> >(u_cj, v_cj) = std::conj(G.at< std::complex<double> >(u, v));
Mat expansion_mat(Rw.size(), CV_64FC2, Scalar(expansion_coefficient.real(), expansion_coefficient.imag()));
Mat W_tmp1 = W_padded(Range(fsr_params.fft_size - u, fsr_params.fft_size - u + Rw.rows), Range(fsr_params.fft_size - v, fsr_params.fft_size - v + Rw.cols));
Mat W_tmp2 = W_padded(Range(fsr_params.fft_size - u_cj, fsr_params.fft_size - u_cj + Rw.rows), Range(fsr_params.fft_size - v_cj, fsr_params.fft_size - v_cj + Rw.cols));
Mat res_1(W_tmp1.size(), W_tmp1.type());
mulSpectrums(expansion_mat, W_tmp1, res_1, 0);
expansion_mat.setTo(Scalar(expansion_coefficient.real(), -expansion_coefficient.imag()));
Mat res_2(W_tmp1.size(), W_tmp1.type());
mulSpectrums(expansion_mat, W_tmp2, res_2, 0);
Rw -= res_1 + res_2;
++iter_counter; // ... as two basis functions were added
}
else
{
std::complex<double> expansion_coefficient = orthogonality_correction * Rw.at< std::complex<double> >(u, v) / W.at< std::complex<double> >(0, 0);
G.at< std::complex<double> >(u, v) += fft_size * fft_size * expansion_coefficient;
Mat expansion_mat(Rw.size(), CV_64FC2, Scalar(expansion_coefficient.real(), expansion_coefficient.imag()));
Mat W_tmp = W_padded(Range(fsr_params.fft_size - u, fsr_params.fft_size - u + Rw.rows), Range(fsr_params.fft_size - v, fsr_params.fft_size - v + Rw.cols));
Mat res_tmp(W_tmp.size(), W_tmp.type());
mulSpectrums(expansion_mat, W_tmp, res_tmp, 0);
Rw -= res_tmp;
}
++iter_counter;
}
// get pixels from model
Mat g;
idft(G, g, DFT_SCALE);
// extract reconstructed pixels
Mat g_real(M, N, CV_64F);
for (int x = 0; x < M; ++x)
{
for (int y = 0; y < N; ++y)
{
g_real.at<double>(x, y) = g.at< std::complex<double> >(fft_y_offset + x, fft_x_offset + y).real();
}
}
g_real.copyTo(extrapolated_block);
Mat orig_samples;
error_mask.convertTo(orig_samples, CV_8U);
distorted_block.copyTo(extrapolated_block, orig_samples); // copy where orig_samples is nonzero
}
static void
icvGetTodoBlocks(Mat& sampled_img, Mat& sampling_mask, std::vector< std::tuple< int, int > >& set_todo, int block_size, int block_size_min, int border_width, double homo_threshold, Mat& set_process_this_block_size, std::vector< std::tuple< int, int > >& set_later, Mat& sigma_n_array)
{
std::vector< std::tuple< int, int > > set_now;
set_later.clear();
size_t list_length = set_todo.size();
int img_height = sampled_img.rows;
int img_width = sampled_img.cols;
Mat reconstructed_img;
sampled_img.copyTo(reconstructed_img);
// calculate block lists
for (size_t entry = 0; entry < list_length; ++entry)
{
int xblock_counter = std::get<0>(set_todo[entry]);
int yblock_counter = std::get<1>(set_todo[entry]);
int left_border = std::min(xblock_counter*block_size, border_width);
int top_border = std::min(yblock_counter*block_size, border_width);
int right_border = std::max(0, std::min(img_width - (xblock_counter + 1)*block_size, border_width));
int bottom_border = std::max(0, std::min(img_height - (yblock_counter + 1)*block_size, border_width));
// extract blocks from images
Mat distorted_block_2d = reconstructed_img(Range(yblock_counter*block_size - top_border, std::min(img_height, (yblock_counter*block_size + block_size + bottom_border))), Range(xblock_counter*block_size - left_border, std::min(img_width, (xblock_counter*block_size + block_size + right_border))));
Mat error_mask_2d = sampling_mask(Range(yblock_counter*block_size - top_border, std::min(img_height, (yblock_counter*block_size + block_size + bottom_border))), Range(xblock_counter*block_size - left_border, std::min(img_width, (xblock_counter*block_size + block_size + right_border))));
// determine normalized and weighted standard deviation
if (block_size > block_size_min && xblock_counter < sigma_n_array.cols && yblock_counter < sigma_n_array.rows)
{
double sigma_n = icvStandardDeviation(distorted_block_2d, error_mask_2d);
sigma_n_array.at<double>( yblock_counter, xblock_counter) = sigma_n;
// homogeneous case
if (sigma_n < homo_threshold)
{
set_now.emplace_back(xblock_counter, yblock_counter);
set_process_this_block_size.at<double>(yblock_counter, xblock_counter) = 255;
}
else
{
int yblock_counter_quadernary = yblock_counter * 2;
int xblock_counter_quadernary = xblock_counter * 2;
int yblock_offset = 0;
int xblock_offset = 0;
for (int quader_counter = 0; quader_counter < 4; ++quader_counter)
{
if (quader_counter == 0)
{
yblock_offset = 0;
xblock_offset = 0;
}
else if (quader_counter == 1)
{
yblock_offset = 0;
xblock_offset = 1;
}
else if (quader_counter == 2)
{
yblock_offset = 1;
xblock_offset = 0;
}
else if (quader_counter == 3)
{
yblock_offset = 1;
xblock_offset = 1;
}
set_later.emplace_back(xblock_counter_quadernary + xblock_offset, yblock_counter_quadernary + yblock_offset);
}
}
}
}
}
static void
icvDetermineProcessingOrder(
const Mat& _sampled_img, const Mat& _sampling_mask,
const int quality, const std::string& channel, Mat& reconstructed_img
)
{
fsr_parameters fsr_params(quality);
int block_size = fsr_params.block_size;
int block_size_max = fsr_params.block_size;
int block_size_min = fsr_params.block_size_min;
double conc_weighting = fsr_params.conc_weighting;
int fft_size = fsr_params.fft_size;
double rho = fsr_params.rhos[0];
Mat sampled_img, sampling_mask;
_sampled_img.convertTo(sampled_img, CV_64F);
reconstructed_img = sampled_img.clone();
_sampling_mask.convertTo(sampling_mask, CV_64F);
double threshold_stddev_LUT[3];
if (channel == "Y")
{
std::copy(fsr_params.threshold_stddev_Y, fsr_params.threshold_stddev_Y + 3, threshold_stddev_LUT);
}
else if (channel == "Cx")
{
std::copy(fsr_params.threshold_stddev_Cx, fsr_params.threshold_stddev_Cx + 3, threshold_stddev_LUT);
}
else
{
CV_Error(Error::StsBadArg, "channel type unsupported!");
}
double threshold_stddev = threshold_stddev_LUT[0];
std::vector< std::tuple< int, int > > set_later;
int img_height = sampled_img.rows;
int img_width = sampled_img.cols;
// initial scan of distorted blocks
std::vector< std::tuple< int, int > > set_todo;
int blocks_column = divUp(img_height, block_size);
int blocks_line = divUp(img_width, block_size);
for (int y = 0; y < blocks_column; ++y)
{
for (int x = 0; x < blocks_line; ++x)
{
Mat curr_block = sampling_mask(Range(y*block_size, std::min(img_height, (y + 1)*block_size)), Range(x*block_size, std::min(img_width, (x + 1)*block_size)));
double min_block, max_block;
minMaxLoc(curr_block, &min_block, &max_block);
if (min_block == 0)
{
set_todo.emplace_back(x, y);
}
}
}
// loop over all distorted blocks and extrapolate them depending on
// their block size
int border_width = 0;
while (block_size >= block_size_min)
{
int blocks_per_column = cvCeil(img_height / block_size);
int blocks_per_line = cvCeil(img_width / block_size);
Mat nen_array = Mat::zeros(blocks_per_column, blocks_per_line, CV_64F);
Mat proc_array = Mat::zeros(blocks_per_column, blocks_per_line, CV_64F);
Mat sigma_n_array = Mat::zeros(blocks_per_column, blocks_per_line, CV_64F);
Mat set_process_this_block_size = Mat::zeros(blocks_per_column, blocks_per_line, CV_64F);
if (block_size > block_size_min)
{
if (block_size < block_size_max)
{
set_todo = set_later;
}
border_width = cvFloor(fft_size - block_size) / 2;
icvGetTodoBlocks(sampled_img, sampling_mask, set_todo, block_size, block_size_min, border_width, threshold_stddev, set_process_this_block_size, set_later, sigma_n_array);
}
else
{
set_process_this_block_size.setTo(Scalar(255));
}
// if block to be extrapolated, increase nen of neighboring pixels
for (int yblock_counter = 0; yblock_counter < blocks_per_column; ++yblock_counter)
{
for (int xblock_counter = 0; xblock_counter < blocks_per_line; ++xblock_counter)
{
Mat curr_block = sampling_mask(Range(yblock_counter*block_size, std::min(img_height, (yblock_counter + 1)*block_size)), Range(xblock_counter*block_size, std::min(img_width, (xblock_counter + 1)*block_size)));
double min_block, max_block;
minMaxLoc(curr_block, &min_block, &max_block);
if (min_block == 0)
{
if (yblock_counter > 0 && xblock_counter > 0)
{
nen_array.at<double>(yblock_counter - 1, xblock_counter - 1)++;
}
if (yblock_counter > 0)
{
nen_array.at<double>(yblock_counter - 1, xblock_counter)++;
}
if (yblock_counter > 0 && xblock_counter < (blocks_per_line - 1))
{
nen_array.at<double>(yblock_counter - 1, xblock_counter + 1)++;
}
if (xblock_counter > 0)
{
nen_array.at<double>(yblock_counter, xblock_counter - 1)++;
}
if (xblock_counter < (blocks_per_line - 1))
{
nen_array.at<double>(yblock_counter, xblock_counter + 1)++;
}
if (yblock_counter < (blocks_per_column - 1) && xblock_counter>0)
{
nen_array.at<double>(yblock_counter + 1, xblock_counter - 1)++;
}
if (yblock_counter < (blocks_per_column - 1))
{
nen_array.at<double>(yblock_counter + 1, xblock_counter)++;
}
if (yblock_counter < (blocks_per_column - 1) && xblock_counter < (blocks_per_line - 1))
{
nen_array.at<double>(yblock_counter + 1, xblock_counter + 1)++;
}
}
}
}
// determine if block itself has to be extrapolated
for (int yblock_counter = 0; yblock_counter < blocks_per_column; ++yblock_counter)
{
for (int xblock_counter = 0; xblock_counter < blocks_per_line; ++xblock_counter)
{
Mat curr_block = sampling_mask(Range(yblock_counter*block_size, std::min(img_height, (yblock_counter + 1)*block_size)), Range(xblock_counter*block_size, std::min(img_width, (xblock_counter + 1)*block_size)));
double min_block, max_block;
minMaxLoc(curr_block, &min_block, &max_block);
if (min_block != 0)
{
nen_array.at<double>(yblock_counter, xblock_counter) = -1;
}
else
{
// if border block, increase nen respectively
if (yblock_counter == 0 && xblock_counter == 0)
{
nen_array.at<double>(yblock_counter, xblock_counter) = nen_array.at<double>(yblock_counter, xblock_counter) + 5;
}
if (yblock_counter == 0 && xblock_counter == (blocks_per_line - 1))
{
nen_array.at<double>(yblock_counter, xblock_counter) = nen_array.at<double>(yblock_counter, xblock_counter) + 5;
}
if (yblock_counter == (blocks_per_column - 1) && xblock_counter == 0)
{
nen_array.at<double>(yblock_counter, xblock_counter) = nen_array.at<double>(yblock_counter, xblock_counter) + 5;
}
if (yblock_counter == (blocks_per_column - 1) && xblock_counter == (blocks_per_line - 1))
{
nen_array.at<double>(yblock_counter, xblock_counter) = nen_array.at<double>(yblock_counter, xblock_counter) + 5;
}
if (yblock_counter == 0 && xblock_counter != 0 && xblock_counter != (blocks_per_line - 1))
{
nen_array.at<double>(yblock_counter, xblock_counter) = nen_array.at<double>(yblock_counter, xblock_counter) + 3;
}
if (yblock_counter == (blocks_per_column - 1) && xblock_counter != 0 && xblock_counter != (blocks_per_line - 1))
{
nen_array.at<double>(yblock_counter, xblock_counter) = nen_array.at<double>(yblock_counter, xblock_counter) + 3;
}
if (yblock_counter != 0 && yblock_counter != (blocks_per_column - 1) && xblock_counter == 0)
{
nen_array.at<double>(yblock_counter, xblock_counter) = nen_array.at<double>(yblock_counter, xblock_counter) + 3;
}
if (yblock_counter != 0 && yblock_counter != (blocks_per_column - 1) && xblock_counter == (blocks_per_line - 1))
{
nen_array.at<double>(yblock_counter, xblock_counter) = nen_array.at<double>(yblock_counter, xblock_counter) + 3;
}
}
}
}
// if all blocks have 8 not extrapolated neighbors, penalize nen of blocks without any known samples by one
double min_nen_tmp, max_nen_tmp;
minMaxLoc(nen_array, &min_nen_tmp, &max_nen_tmp);
if (min_nen_tmp == 8) {
for (int yblock_counter = 0; yblock_counter < blocks_per_column; ++yblock_counter)
{
for (int xblock_counter = 0; xblock_counter < blocks_per_line; ++xblock_counter)
{
Mat curr_block = sampling_mask(Range(yblock_counter*block_size, std::min(img_height, (yblock_counter + 1)*block_size)), Range(xblock_counter*block_size, std::min(img_width, (xblock_counter + 1)*block_size)));
double min_block, max_block;
minMaxLoc(curr_block, &min_block, &max_block);
if (max_block == 0)
{
nen_array.at<double>(yblock_counter, xblock_counter)++;
}
}
}
}
// do actual processing per block
int all_blocks_finished = 0;
while (all_blocks_finished == 0) {
// clear proc_array
proc_array.setTo(Scalar(1));
// determine blocks to extrapolate
double min_nen = 99;
int bl_counter = 0;
// add all homogeneous blocks that shall be processed to list
// using same priority
// begins with highest prioroty or lowest nen array value
std::vector< std::tuple< int, int > > block_list;
for (int yblock_counter = 0; yblock_counter < blocks_per_column; ++yblock_counter)
{
for (int xblock_counter = 0; xblock_counter < blocks_per_line; ++xblock_counter)
{
// decision if block contains errors
double tmp_val = nen_array.at<double>(yblock_counter, xblock_counter);
if (tmp_val >= 0 && tmp_val < min_nen && set_process_this_block_size.at<double>(yblock_counter, xblock_counter) == 255) {
bl_counter = 0;
block_list.clear();
min_nen = tmp_val;
proc_array.setTo(Scalar(1));
}
if (tmp_val == min_nen && proc_array.at<double>(yblock_counter, xblock_counter) != 0 && set_process_this_block_size.at<double>(yblock_counter, xblock_counter) == 0) {
nen_array.at<double>(yblock_counter, xblock_counter) = -1;
}
if (tmp_val == min_nen && proc_array.at<double>(yblock_counter, xblock_counter) != 0 && set_process_this_block_size.at<double>(yblock_counter, xblock_counter) != 0) {
block_list.emplace_back(yblock_counter, xblock_counter);
bl_counter++;
// block neighboring blocks from processing
if (yblock_counter > 0 && xblock_counter > 0)
{
proc_array.at<double>(yblock_counter - 1, xblock_counter - 1) = 0;
}
if (yblock_counter > 0)
{
proc_array.at<double>(yblock_counter - 1, xblock_counter) = 0;
}
if (yblock_counter > 0 && xblock_counter > 0)
{
proc_array.at<double>(yblock_counter - 1, xblock_counter - 1) = 0;
}
if (yblock_counter > 0)
{
proc_array.at<double>(yblock_counter - 1, xblock_counter) = 0;
}
if (yblock_counter > 0 && xblock_counter < (blocks_per_line - 1))
{
proc_array.at<double>(yblock_counter - 1, xblock_counter + 1) = 0;
}
if (xblock_counter > 0)
{
proc_array.at<double>(yblock_counter, xblock_counter - 1) = 0;
}
if (xblock_counter < (blocks_per_line - 1))
{
proc_array.at<double>(yblock_counter, xblock_counter + 1) = 0;
}
if (yblock_counter < (blocks_per_column - 1) && xblock_counter > 0)
{
proc_array.at<double>(yblock_counter + 1, xblock_counter - 1) = 0;
}
if (yblock_counter < (blocks_per_column - 1))
{
proc_array.at<double>(yblock_counter + 1, xblock_counter) = 0;
}
if (yblock_counter < (blocks_per_column - 1) && xblock_counter < (blocks_per_line - 1))
{
proc_array.at<double>(yblock_counter + 1, xblock_counter + 1) = 0;
}
}
}
}
int max_bl_counter = bl_counter;
block_list.emplace_back(-1, -1);
if (bl_counter == 0)
{
all_blocks_finished = 1;
}
// blockwise extrapolation of all blocks that can be processed in parallel
for (bl_counter = 0; bl_counter < max_bl_counter; ++bl_counter)
{
int yblock_counter = std::get<0>(block_list[bl_counter]);
int xblock_counter = std::get<1>(block_list[bl_counter]);
// calculation of the extrapolation area's borders
int left_border = std::min(xblock_counter*block_size, border_width);
int top_border = std::min(yblock_counter*block_size, border_width);
int right_border = std::max(0, std::min(img_width - (xblock_counter + 1)*block_size, border_width));
int bottom_border = std::max(0, std::min(img_height - (yblock_counter + 1)*block_size, border_width));
// extract blocks from images
Mat distorted_block_2d = reconstructed_img(Range(yblock_counter*block_size - top_border, std::min(img_height, (yblock_counter*block_size + block_size + bottom_border))), Range(xblock_counter*block_size - left_border, std::min(img_width, (xblock_counter*block_size + block_size + right_border))));
Mat error_mask_2d = sampling_mask(Range(yblock_counter*block_size - top_border, std::min(img_height, (yblock_counter*block_size + block_size + bottom_border))), Range(xblock_counter*block_size - left_border, std::min(img_width, xblock_counter*block_size + block_size + right_border)));
// get actual stddev value as it is needed to estimate the
// best number of iterations
double sigma_n_a = sigma_n_array.at<double>(yblock_counter, xblock_counter);
// actual extrapolation
Mat extrapolated_block_2d;
icvExtrapolateBlock(distorted_block_2d, error_mask_2d, fsr_params, rho, sigma_n_a, extrapolated_block_2d);
// update image and mask
extrapolated_block_2d(Range(top_border, extrapolated_block_2d.rows - bottom_border), Range(left_border, extrapolated_block_2d.cols - right_border)).copyTo(reconstructed_img(Range(yblock_counter*block_size, std::min(img_height, (yblock_counter + 1)*block_size)), Range(xblock_counter*block_size, std::min(img_width, (xblock_counter + 1)*block_size))));
Mat signs;
icvSgnMat(error_mask_2d(Range(top_border, error_mask_2d.rows - bottom_border), Range(left_border, error_mask_2d.cols - right_border)), signs);
Mat tmp_mask = error_mask_2d(Range(top_border, error_mask_2d.rows - bottom_border), Range(left_border, error_mask_2d.cols - right_border)) + (1 - signs) *conc_weighting;
tmp_mask.copyTo(sampling_mask(Range(yblock_counter*block_size, std::min(img_height, (yblock_counter + 1)*block_size)), Range(xblock_counter*block_size, std::min(img_width, (xblock_counter + 1)*block_size))));
// update nen-array
nen_array.at<double>(yblock_counter, xblock_counter) = -1;
if (yblock_counter > 0 && xblock_counter > 0)
{
nen_array.at<double>(yblock_counter - 1, xblock_counter - 1)--;
}
if (yblock_counter > 0)
{
nen_array.at<double>(yblock_counter - 1, xblock_counter)--;
}
if (yblock_counter > 0 && xblock_counter < blocks_per_line - 1)
{
nen_array.at<double>(yblock_counter - 1, xblock_counter + 1)--;
}
if (xblock_counter > 0)
{
nen_array.at<double>(yblock_counter, xblock_counter - 1)--;
}
if (xblock_counter < blocks_per_line - 1)
{
nen_array.at<double>(yblock_counter, xblock_counter + 1)--;
}
if (yblock_counter < blocks_per_column - 1 && xblock_counter>0)
{
nen_array.at<double>(yblock_counter + 1, xblock_counter - 1)--;
}
if (yblock_counter < blocks_per_column - 1)
{
nen_array.at<double>(yblock_counter + 1, xblock_counter)--;
}
if (yblock_counter < blocks_per_column - 1 && xblock_counter < blocks_per_line - 1)
{
nen_array.at<double>(yblock_counter + 1, xblock_counter + 1)--;
}
}
}
// set parameters for next extrapolation tasks (higher texture)
block_size = block_size / 2;
border_width = (fft_size - block_size) / 2;
if (block_size == 8)
{
threshold_stddev = threshold_stddev_LUT[1];
rho = fsr_params.rhos[1];
}
if (block_size == 4)
{
threshold_stddev = threshold_stddev_LUT[2];
rho = fsr_params.rhos[2];
}
if (block_size == 2)
{
rho = fsr_params.rhos[3];
}
// terminate function - no heterogeneous blocks left
if (set_later.empty())
{
break;
}
}
}
static
void inpaint_fsr(Mat src, const Mat &mask, Mat &dst, const int algorithmType)
{
CV_Assert(algorithmType == xphoto::INPAINT_FSR_BEST || algorithmType == xphoto::INPAINT_FSR_FAST);
CV_Check(src.channels(), src.channels() == 1 || src.channels() == 3, "");
switch (src.type())
{
case CV_8UC1:
case CV_8UC3:
break;
case CV_16UC1:
case CV_16UC3:
{
double minRange, maxRange;
minMaxLoc(src, &minRange, &maxRange);
if (minRange < 0 || maxRange > 65535)
{
CV_Error(Error::StsUnsupportedFormat, "Unsupported source image format!");
break;
}
src.convertTo(src, CV_8U, 1/256.0);
break;
}
case CV_32FC1:
case CV_64FC1:
case CV_32FC3:
case CV_64FC3:
{
double minRange, maxRange;
minMaxLoc(src, &minRange, &maxRange);
if (minRange < -FLT_EPSILON || maxRange > (1.0 + FLT_EPSILON))
{
CV_Error(Error::StsUnsupportedFormat, "Unsupported source image format!");
break;
}
src.convertTo(src, CV_8U, 255.0);
break;
}
default:
CV_Error(Error::StsUnsupportedFormat, "Unsupported source image format!");
break;
}
dst.create(src.size(), src.type());
Mat mask_01;
threshold(mask, mask_01, 0.0, 1.0, THRESH_BINARY);
if (src.channels() == 1)
{ // grayscale image
Mat y_reconstructed;
icvDetermineProcessingOrder(src, mask_01, algorithmType, "Y", y_reconstructed);
y_reconstructed.convertTo(dst, CV_8U);
}
else if (src.channels() == 3)
{ // RGB image
Mat ycrcb;
cvtColor(src, ycrcb, COLOR_BGR2YCrCb);
std::vector<Mat> channels(3);
split(ycrcb, channels);
Mat y = channels[0];
Mat cb = channels[2];
Mat cr = channels[1];
Mat y_reconstructed, cb_reconstructed, cr_reconstructed;
y = y.mul(mask_01);
cb = cb.mul(mask_01);
cr = cr.mul(mask_01);
icvDetermineProcessingOrder(y, mask_01, algorithmType, "Y", y_reconstructed);
icvDetermineProcessingOrder(cb, mask_01, algorithmType, "Cx", cb_reconstructed);
icvDetermineProcessingOrder(cr, mask_01, algorithmType, "Cx", cr_reconstructed);
Mat ycrcb_reconstructed;
y_reconstructed.convertTo(channels[0], CV_8U);
cr_reconstructed.convertTo(channels[1], CV_8U);
cb_reconstructed.convertTo(channels[2], CV_8U);
merge(channels, ycrcb_reconstructed);
cvtColor(ycrcb_reconstructed, dst, COLOR_YCrCb2BGR);
}
}
}} // 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.
//
//
// 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_BM3D_DENOISING_KAISER_WINDOW_HPP__
#define __OPENCV_BM3D_DENOISING_KAISER_WINDOW_HPP__
#include "opencv2/core.hpp"
#include <cmath>
namespace cv
{
namespace xphoto
{
static int factorial(int n)
{
if (n == 0)
return 1;
int val = 1;
for (int idx = 1; idx <= n; ++idx)
val *= idx;
return val;
}
template <int MAX_ITER>
static float bessel0(const float &x)
{
float sum = 0.0f;
for (int m = 0; m < MAX_ITER; ++m)
{
float factM = (float)factorial(m);
float inc = std::pow(1.0f / factM * std::pow(x * 0.5f, (float)m), 2.0f);
sum += inc;
if ((inc / sum) < 0.001F)
break;
}
return sum;
}
#define MAX_ITER_BESSEL 100
static void calcKaiserWindow1D(cv::Mat &dst, const int N, const float beta)
{
if (dst.empty())
dst.create(cv::Size(1, N), CV_32FC1);
CV_Assert(dst.total() == (size_t)N);
CV_Assert(dst.type() == CV_32FC1);
CV_Assert(N > 0);
float *p = dst.ptr<float>(0);
for (int i = 0; i < N; ++i)
{
float b = beta * std::sqrt(1.0f - std::pow(2.0f * i / (N - 1.0f) - 1.0f, 2.0f));
p[i] = bessel0<MAX_ITER_BESSEL>(b) / bessel0<MAX_ITER_BESSEL>(beta);
}
}
static void calcKaiserWindow2D(float *&kaiser, const int N, const float beta)
{
if (kaiser == NULL)
kaiser = new float[N * N];
if (beta == 0.0f)
{
for (int i = 0; i < N * N; ++i)
kaiser[i] = 1.0f;
return;
}
cv::Mat kaiser1D;
calcKaiserWindow1D(kaiser1D, N, beta);
cv::Mat kaiser1Dt;
cv::transpose(kaiser1D, kaiser1Dt);
cv::Mat kaiser2D = kaiser1D * kaiser1Dt;
float *p = kaiser2D.ptr<float>(0);
for (unsigned i = 0; i < kaiser2D.total(); ++i)
kaiser[i] = p[i];
}
} // namespace xphoto
} // namespace cv
#endif
@@ -0,0 +1,622 @@
/*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-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// * 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 "learning_based_color_balance_model.hpp"
#include "opencv2/core.hpp"
#include "opencv2/core/hal/intrin.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/xphoto.hpp"
using namespace std;
#define EPS 0.00001f
namespace cv
{
namespace xphoto
{
inline void getChromaticity(Vec2f &dst, float R, float G, float B)
{
dst[0] = R / (R + G + B + EPS);
dst[1] = G / (R + G + B + EPS);
}
struct hist_elem
{
float hist_val;
float r, g;
hist_elem(float _hist_val, Vec2f chromaticity) : hist_val(_hist_val), r(chromaticity[0]), g(chromaticity[1]) {}
};
bool operator<(const hist_elem &a, const hist_elem &b);
bool operator<(const hist_elem &a, const hist_elem &b) { return a.hist_val > b.hist_val; }
class LearningBasedWBImpl : public LearningBasedWB
{
private:
int range_max_val, hist_bin_num, palette_size;
float saturation_thresh, palette_bandwidth, prediction_thresh;
int num_trees, num_tree_nodes, tree_depth;
uchar *feature_idx;
float *thresh_vals, *leaf_vals;
Mat feature_idx_Mat, thresh_vals_Mat, leaf_vals_Mat;
Mat mask;
int src_max_val;
void preprocessing(Mat &src);
void getAverageAndBrightestColorChromaticity(Vec2f &average_chromaticity, Vec2f &brightest_chromaticity, Mat &src);
void getColorPaletteMode(Vec2f &dst, hist_elem *palette);
void getHistogramBasedFeatures(Vec2f &dominant_chromaticity, Vec2f &chromaticity_palette_mode, Mat &src);
float regressionTreePredict(Vec2f src, uchar *tree_feature_idx, float *tree_thresh_vals, float *tree_leaf_vals);
Vec2f predictIlluminant(vector<Vec2f> features);
public:
LearningBasedWBImpl(String path_to_model)
{
range_max_val = 255;
saturation_thresh = 0.98f;
hist_bin_num = 64;
palette_size = 300;
palette_bandwidth = 0.1f;
prediction_thresh = 0.025f;
/* try to load model from file */
FileStorage fs;
if (!path_to_model.empty() && fs.open(path_to_model, FileStorage::READ))
{
if (fs["num_trees"].isReal()) { //workaround for #10506
double nt = fs["num_trees"];
num_trees = int(nt);
double ntn = fs["num_tree_nodes"];
num_tree_nodes = int(ntn);
} else {
num_trees = fs["num_trees"];
num_tree_nodes = fs["num_tree_nodes"];
}
fs["feature_idx"] >> feature_idx_Mat;
fs["thresh_vals"] >> thresh_vals_Mat;
fs["leaf_vals"] >> leaf_vals_Mat;
feature_idx = feature_idx_Mat.ptr<uchar>();
thresh_vals = thresh_vals_Mat.ptr<float>();
leaf_vals = leaf_vals_Mat.ptr<float>();
}
else
{
/* use the default model */
num_trees = _num_trees;
num_tree_nodes = _num_tree_nodes;
feature_idx = _feature_idx;
thresh_vals = _thresh_vals;
leaf_vals = _leaf_vals;
}
}
int getRangeMaxVal() const CV_OVERRIDE { return range_max_val; }
void setRangeMaxVal(int val) CV_OVERRIDE { range_max_val = val; }
float getSaturationThreshold() const CV_OVERRIDE { return saturation_thresh; }
void setSaturationThreshold(float val) CV_OVERRIDE { saturation_thresh = val; }
int getHistBinNum() const CV_OVERRIDE { return hist_bin_num; }
void setHistBinNum(int val) CV_OVERRIDE { hist_bin_num = val; }
void extractSimpleFeatures(InputArray _src, OutputArray _dst) CV_OVERRIDE
{
CV_Assert(!_src.empty());
CV_Assert(_src.isContinuous());
CV_Assert(_src.type() == CV_8UC3 || _src.type() == CV_16UC3);
Mat src = _src.getMat();
vector<Vec2f> dst(num_features);
preprocessing(src);
getAverageAndBrightestColorChromaticity(dst[0], dst[1], src);
getHistogramBasedFeatures(dst[2], dst[3], src);
Mat(dst).convertTo(_dst, CV_32F);
}
void balanceWhite(InputArray _src, OutputArray _dst) CV_OVERRIDE
{
CV_Assert(!_src.empty());
CV_Assert(_src.isContinuous());
CV_Assert(_src.type() == CV_8UC3 || _src.type() == CV_16UC3);
Mat src = _src.getMat();
vector<Vec2f> features;
extractSimpleFeatures(src, features);
Vec2f illuminant = predictIlluminant(features);
float denom = 1 - illuminant[0] - illuminant[1];
float gainB = 1.0f;
float gainG = denom / illuminant[1];
float gainR = denom / illuminant[0];
applyChannelGains(src, _dst, gainB, gainG, gainR);
}
};
/* Computes a mask for non-saturated pixels and maximum pixel value
* which are then used for feature computation
*/
void LearningBasedWBImpl::preprocessing(Mat &src)
{
mask.create(src.size(), CV_8U);
uchar *mask_ptr = mask.ptr<uchar>();
int src_len = src.rows * src.cols;
int thresh = (int)(saturation_thresh * range_max_val);
int i = 0;
int local_max;
src_max_val = -1;
if (src.type() == CV_8UC3)
{
uchar *src_ptr = src.ptr<uchar>();
#if CV_SIMD128
v_uint8x16 v_inB, v_inG, v_inR, v_local_max;
v_uint8x16 v_global_max = v_setall_u8(0), v_mask, v_thresh = v_setall_u8((uchar)thresh);
for (; i < src_len - 15; i += 16)
{
v_load_deinterleave(src_ptr + 3 * i, v_inB, v_inG, v_inR);
v_local_max = v_max(v_inB, v_max(v_inG, v_inR));
v_global_max = v_max(v_local_max, v_global_max);
v_mask = (v_lt(v_local_max, v_thresh));
v_store(mask_ptr + i, v_mask);
}
uchar global_max[16];
v_store(global_max, v_global_max);
for (int j = 0; j < 16; j++)
{
if (global_max[j] > src_max_val)
src_max_val = global_max[j];
}
#endif
for (; i < src_len; i++)
{
local_max = max(src_ptr[3 * i], max(src_ptr[3 * i + 1], src_ptr[3 * i + 2]));
if (local_max > src_max_val)
src_max_val = local_max;
if (local_max < thresh)
mask_ptr[i] = 255;
else
mask_ptr[i] = 0;
}
}
else if (src.type() == CV_16UC3)
{
ushort *src_ptr = src.ptr<ushort>();
#if CV_SIMD128
v_uint16x8 v_inB, v_inG, v_inR, v_local_max;
v_uint16x8 v_global_max = v_setall_u16(0), v_mask, v_thresh = v_setall_u16((ushort)thresh);
for (; i < src_len - 7; i += 8)
{
v_load_deinterleave(src_ptr + 3 * i, v_inB, v_inG, v_inR);
v_local_max = v_max(v_inB, v_max(v_inG, v_inR));
v_global_max = v_max(v_local_max, v_global_max);
v_mask = (v_lt(v_local_max, v_thresh));
v_pack_store(mask_ptr + i, v_mask);
}
ushort global_max[8];
v_store(global_max, v_global_max);
for (int j = 0; j < 8; j++)
{
if (global_max[j] > src_max_val)
src_max_val = global_max[j];
}
#endif
for (; i < src_len; i++)
{
local_max = max(src_ptr[3 * i], max(src_ptr[3 * i + 1], src_ptr[3 * i + 2]));
if (local_max > src_max_val)
src_max_val = local_max;
if (local_max < thresh)
mask_ptr[i] = 255;
else
mask_ptr[i] = 0;
}
}
}
void LearningBasedWBImpl::getAverageAndBrightestColorChromaticity(Vec2f &average_chromaticity,
Vec2f &brightest_chromaticity, Mat &src)
{
int i = 0;
int src_len = src.rows * src.cols;
uchar *mask_ptr = mask.ptr<uchar>();
uint brightestB = 0, brightestG = 0, brightestR = 0;
uint max_sum = 0;
if (src.type() == CV_8UC3)
{
uint sumB = 0, sumG = 0, sumR = 0;
uchar *src_ptr = src.ptr<uchar>();
#if CV_SIMD128
v_uint16x8 v_max_sum = v_setall_u16(0), v_brightestR = v_setall_u16(0), v_brightestG = v_setall_u16(0), v_brightestB = v_setall_u16(0);
v_uint32x4 v_SB = v_setzero_u32(), v_SG = v_setzero_u32(), v_SR = v_setzero_u32();
for (; i < src_len - 15; i += 16)
{
v_uint8x16 v_inB, v_inG, v_inR;
v_load_deinterleave(src_ptr + 3 * i, v_inB, v_inG, v_inR);
v_uint8x16 v_mask = v_load(mask_ptr + i);
v_inB = v_and(v_inB, v_mask);
v_inG = v_and(v_inG, v_mask);
v_inR = v_and(v_inR, v_mask);
v_uint16x8 v_sR1, v_sR2, v_sG1, v_sG2, v_sB1, v_sB2;
v_expand(v_inB, v_sB1, v_sB2);
v_expand(v_inG, v_sG1, v_sG2);
v_expand(v_inR, v_sR1, v_sR2);
// update the brightest (R,G,B) tuple (process left half):
v_uint16x8 v_sum = v_add(v_add(v_sB1, v_sG1), v_sR1);
v_uint16x8 v_max_mask = (v_gt(v_sum, v_max_sum));
v_max_sum = v_max(v_sum, v_max_sum);
v_brightestB = v_add(v_and(v_sB1, v_max_mask), v_and(v_brightestB, v_not(v_max_mask)));
v_brightestG = v_add(v_and(v_sG1, v_max_mask), v_and(v_brightestG, v_not(v_max_mask)));
v_brightestR = v_add(v_and(v_sR1, v_max_mask), v_and(v_brightestR, v_not(v_max_mask)));
// update the brightest (R,G,B) tuple (process right half):
v_sum = v_add(v_add(v_sB2, v_sG2), v_sR2);
v_max_mask = (v_gt(v_sum, v_max_sum));
v_max_sum = v_max(v_sum, v_max_sum);
v_brightestB = v_add(v_and(v_sB2, v_max_mask), v_and(v_brightestB, v_not(v_max_mask)));
v_brightestG = v_add(v_and(v_sG2, v_max_mask), v_and(v_brightestG, v_not(v_max_mask)));
v_brightestR = v_add(v_and(v_sR2, v_max_mask), v_and(v_brightestR, v_not(v_max_mask)));
// update sums:
v_sB1 = v_add(v_sB1, v_sB2);
v_sG1 = v_add(v_sG1, v_sG2);
v_sR1 = v_add(v_sR1, v_sR2);
v_uint32x4 v_uint1, v_uint2;
v_expand(v_sB1, v_uint1, v_uint2);
v_SB = v_add(v_SB, v_add(v_uint1, v_uint2));
v_expand(v_sG1, v_uint1, v_uint2);
v_SG = v_add(v_SG, v_add(v_uint1, v_uint2));
v_expand(v_sR1, v_uint1, v_uint2);
v_SR = v_add(v_SR, v_add(v_uint1, v_uint2));
}
sumB = v_reduce_sum(v_SB);
sumG = v_reduce_sum(v_SG);
sumR = v_reduce_sum(v_SR);
ushort brightestB_arr[8], brightestG_arr[8], brightestR_arr[8], max_sum_arr[8];
v_store(brightestB_arr, v_brightestB);
v_store(brightestG_arr, v_brightestG);
v_store(brightestR_arr, v_brightestR);
v_store(max_sum_arr, v_max_sum);
for (int j = 0; j < 8; j++)
{
if (max_sum_arr[j] > max_sum)
{
max_sum = max_sum_arr[j];
brightestB = brightestB_arr[j];
brightestG = brightestG_arr[j];
brightestR = brightestR_arr[j];
}
}
#endif
for (; i < src_len; i++)
{
uint sum_val = src_ptr[3 * i] + src_ptr[3 * i + 1] + src_ptr[3 * i + 2];
if (mask_ptr[i])
{
sumB += src_ptr[3 * i];
sumG += src_ptr[3 * i + 1];
sumR += src_ptr[3 * i + 2];
if (sum_val > max_sum)
{
max_sum = sum_val;
brightestB = src_ptr[3 * i];
brightestG = src_ptr[3 * i + 1];
brightestR = src_ptr[3 * i + 2];
}
}
}
double maxRGB = (double)max(sumR, max(sumG, sumB));
getChromaticity(average_chromaticity, (float)(sumR / maxRGB), (float)(sumG / maxRGB), (float)(sumB / maxRGB));
getChromaticity(brightest_chromaticity, (float)brightestR, (float)brightestG, (float)brightestB);
}
else if (src.type() == CV_16UC3)
{
uint64 sumB = 0, sumG = 0, sumR = 0;
ushort *src_ptr = src.ptr<ushort>();
#if CV_SIMD128
const v_uint16x8 v_mask_lower = v_setall_u16(255);
v_uint32x4 v_max_sum = v_setall_u32(0), v_brightestR = v_setall_u32(0), v_brightestG = v_setall_u32(0), v_brightestB = v_setall_u32(0);
v_uint64x2 v_SB = v_setzero_u64(), v_SG = v_setzero_u64(), v_SR = v_setzero_u64();
for (; i < src_len - 7; i += 8)
{
v_uint16x8 v_inB, v_inG, v_inR;
v_load_deinterleave(src_ptr + 3 * i, v_inB, v_inG, v_inR);
v_uint16x8 v_mask = v_load_expand(mask_ptr + i);
v_mask = v_or(v_mask, v_shl<8>(v_and(v_mask, v_mask_lower)));
v_inB = v_and(v_inB, v_mask);
v_inG = v_and(v_inG, v_mask);
v_inR = v_and(v_inR, v_mask);
v_uint32x4 v_iR1, v_iR2, v_iG1, v_iG2, v_iB1, v_iB2;
v_expand(v_inB, v_iB1, v_iB2);
v_expand(v_inG, v_iG1, v_iG2);
v_expand(v_inR, v_iR1, v_iR2);
// update the brightest (R,G,B) tuple (process left half):
v_uint32x4 v_sum = v_add(v_add(v_iB1, v_iG1), v_iR1);
v_uint32x4 v_max_mask = (v_gt(v_sum, v_max_sum));
v_max_sum = v_max(v_sum, v_max_sum);
v_brightestB = v_add(v_and(v_iB1, v_max_mask), v_and(v_brightestB, v_not(v_max_mask)));
v_brightestG = v_add(v_and(v_iG1, v_max_mask), v_and(v_brightestG, v_not(v_max_mask)));
v_brightestR = v_add(v_and(v_iR1, v_max_mask), v_and(v_brightestR, v_not(v_max_mask)));
// update the brightest (R,G,B) tuple (process right half):
v_sum = v_add(v_add(v_iB2, v_iG2), v_iR2);
v_max_mask = (v_gt(v_sum, v_max_sum));
v_max_sum = v_max(v_sum, v_max_sum);
v_brightestB = v_add(v_and(v_iB2, v_max_mask), v_and(v_brightestB, v_not(v_max_mask)));
v_brightestG = v_add(v_and(v_iG2, v_max_mask), v_and(v_brightestG, v_not(v_max_mask)));
v_brightestR = v_add(v_and(v_iR2, v_max_mask), v_and(v_brightestR, v_not(v_max_mask)));
// update sums:
v_iB1 = v_add(v_iB1, v_iB2);
v_iG1 = v_add(v_iG1, v_iG2);
v_iR1 = v_add(v_iR1, v_iR2);
v_uint64x2 v_uint64_1, v_uint64_2;
v_expand(v_iB1, v_uint64_1, v_uint64_2);
v_SB = v_add(v_SB, v_add(v_uint64_1, v_uint64_2));
v_expand(v_iG1, v_uint64_1, v_uint64_2);
v_SG = v_add(v_SG, v_add(v_uint64_1, v_uint64_2));
v_expand(v_iR1, v_uint64_1, v_uint64_2);
v_SR = v_add(v_SR, v_add(v_uint64_1, v_uint64_2));
}
uint64 sum_arr[2];
v_store(sum_arr, v_SB);
sumB = sum_arr[0] + sum_arr[1];
v_store(sum_arr, v_SG);
sumG = sum_arr[0] + sum_arr[1];
v_store(sum_arr, v_SR);
sumR = sum_arr[0] + sum_arr[1];
uint brightestB_arr[4], brightestG_arr[4], brightestR_arr[4], max_sum_arr[4];
v_store(brightestB_arr, v_brightestB);
v_store(brightestG_arr, v_brightestG);
v_store(brightestR_arr, v_brightestR);
v_store(max_sum_arr, v_max_sum);
for (int j = 0; j < 4; j++)
{
if (max_sum_arr[j] > max_sum)
{
max_sum = max_sum_arr[j];
brightestB = brightestB_arr[j];
brightestG = brightestG_arr[j];
brightestR = brightestR_arr[j];
}
}
#endif
for (; i < src_len; i++)
{
uint sum_val = src_ptr[3 * i] + src_ptr[3 * i + 1] + src_ptr[3 * i + 2];
if (mask_ptr[i])
{
sumB += src_ptr[3 * i];
sumG += src_ptr[3 * i + 1];
sumR += src_ptr[3 * i + 2];
if (sum_val > max_sum)
{
max_sum = sum_val;
brightestB = src_ptr[3 * i];
brightestG = src_ptr[3 * i + 1];
brightestR = src_ptr[3 * i + 2];
}
}
}
double maxRGB = (double)max(sumR, max(sumG, sumB));
getChromaticity(average_chromaticity, (float)(sumR / maxRGB), (float)(sumG / maxRGB), (float)(sumB / maxRGB));
getChromaticity(brightest_chromaticity, (float)brightestR, (float)brightestG, (float)brightestB);
}
}
/* Returns the most high-density point (i.e. mode) of the color palette.
* Uses a simplistic kernel density estimator with a Epanechnikov kernel and
* fixed bandwidth.
*/
void LearningBasedWBImpl::getColorPaletteMode(Vec2f &dst, hist_elem *palette)
{
float max_density = -1.0f;
float denom = palette_bandwidth * palette_bandwidth;
for (int i = 0; i < palette_size; i++)
{
float cur_density = 0.0f;
float cur_dist_sq;
for (int j = 0; j < palette_size; j++)
{
cur_dist_sq = (palette[i].r - palette[j].r) * (palette[i].r - palette[j].r) +
(palette[i].g - palette[j].g) * (palette[i].g - palette[j].g);
cur_density += max((1.0f - (cur_dist_sq / denom)), 0.0f);
}
if (cur_density > max_density)
{
max_density = cur_density;
dst[0] = palette[i].r;
dst[1] = palette[i].g;
}
}
}
void LearningBasedWBImpl::getHistogramBasedFeatures(Vec2f &dominant_chromaticity, Vec2f &chromaticity_palette_mode,
Mat &src)
{
MatND hist;
int channels[] = {0, 1, 2};
int histSize[] = {hist_bin_num, hist_bin_num, hist_bin_num};
float range[] = {0, (float)max(hist_bin_num, src_max_val)};
const float *ranges[] = {range, range, range};
calcHist(&src, 1, channels, mask, hist, 3, histSize, ranges);
int dominant_B = 0, dominant_G = 0, dominant_R = 0;
double max_hist_val = 0;
float *hist_ptr = hist.ptr<float>();
for (int i = 0; i < hist_bin_num; i++)
for (int j = 0; j < hist_bin_num; j++)
for (int k = 0; k < hist_bin_num; k++)
{
if (*hist_ptr > max_hist_val)
{
max_hist_val = *hist_ptr;
dominant_B = i;
dominant_G = j;
dominant_R = k;
}
hist_ptr++;
}
getChromaticity(dominant_chromaticity, (float)dominant_R, (float)dominant_G, (float)dominant_B);
vector<hist_elem> palette;
palette.reserve(palette_size);
hist_ptr = hist.ptr<float>();
// extract top palette_size most common colors and add them to the palette:
for (int i = 0; i < hist_bin_num; i++)
for (int j = 0; j < hist_bin_num; j++)
for (int k = 0; k < hist_bin_num; k++)
{
float bin_count = *hist_ptr;
if (bin_count < EPS)
{
hist_ptr++;
continue;
}
Vec2f chromaticity;
getChromaticity(chromaticity, (float)k, (float)j, (float)i);
hist_elem el(bin_count, chromaticity);
if (palette.size() < (uint)palette_size)
{
palette.push_back(el);
if (palette.size() == (uint)palette_size)
make_heap(palette.begin(), palette.end());
}
else if (bin_count > palette.front().hist_val)
{
pop_heap(palette.begin(), palette.end());
palette.back() = el;
push_heap(palette.begin(), palette.end());
}
hist_ptr++;
}
getColorPaletteMode(chromaticity_palette_mode, (hist_elem *)(&palette[0]));
}
float LearningBasedWBImpl::regressionTreePredict(Vec2f src, uchar *tree_feature_idx, float *tree_thresh_vals,
float *tree_leaf_vals)
{
int node_idx = 0;
for (int i = 0; i < tree_depth; i++)
{
if (src[tree_feature_idx[node_idx]] <= tree_thresh_vals[node_idx])
node_idx = 2 * node_idx + 1;
else
node_idx = 2 * node_idx + 2;
}
return tree_leaf_vals[node_idx - num_tree_nodes + 1];
}
Vec2f LearningBasedWBImpl::predictIlluminant(vector<Vec2f> features)
{
int feature_model_size = 2 * (num_tree_nodes - 1);
int local_model_size = num_features * feature_model_size;
int feature_model_size_leaf = 2 * num_tree_nodes;
int local_model_size_leaf = num_features * feature_model_size_leaf;
tree_depth = cvRound( (log(static_cast<float>(num_tree_nodes)) / log(2.0f)) );
vector<float> consensus_r, consensus_g;
vector<float> all_r, all_g;
for (int i = 0; i < num_trees; i++)
{
Vec2f local_predictions[num_features];
for (int j = 0; j < num_features; j++)
{
float r = regressionTreePredict(features[j], feature_idx + local_model_size * i + feature_model_size * j,
thresh_vals + local_model_size * i + feature_model_size * j,
leaf_vals + local_model_size_leaf * i + feature_model_size_leaf * j);
float g = regressionTreePredict(
features[j], feature_idx + local_model_size * i + feature_model_size * j + feature_model_size / 2,
thresh_vals + local_model_size * i + feature_model_size * j + feature_model_size / 2,
leaf_vals + local_model_size_leaf * i + feature_model_size_leaf * j + feature_model_size_leaf / 2);
local_predictions[j] = Vec2f(r, g);
all_r.push_back(r);
all_g.push_back(g);
}
int agreement_degree = 0;
for (int j = 0; j < num_features - 1; j++)
for (int k = j + 1; k < num_features; k++)
{
if (norm(local_predictions[j] - local_predictions[k]) < prediction_thresh)
agreement_degree++;
}
if (agreement_degree >= 3)
{
for (int j = 0; j < num_features; j++)
{
consensus_r.push_back(local_predictions[j][0]);
consensus_g.push_back(local_predictions[j][1]);
}
}
}
float illuminant_r, illuminant_g;
if (consensus_r.size() == 0)
{
nth_element(all_r.begin(), all_r.begin() + all_r.size() / 2, all_r.end());
illuminant_r = all_r[all_r.size() / 2];
nth_element(all_g.begin(), all_g.begin() + all_g.size() / 2, all_g.end());
illuminant_g = all_g[all_g.size() / 2];
}
else
{
nth_element(consensus_r.begin(), consensus_r.begin() + consensus_r.size() / 2, consensus_r.end());
illuminant_r = consensus_r[consensus_r.size() / 2];
nth_element(consensus_g.begin(), consensus_g.begin() + consensus_g.size() / 2, consensus_g.end());
illuminant_g = consensus_g[consensus_g.size() / 2];
}
return Vec2f(illuminant_r, illuminant_g);
}
Ptr<LearningBasedWB> createLearningBasedWB(const String& path_to_model)
{
Ptr<LearningBasedWB> inst = makePtr<LearningBasedWBImpl>(path_to_model);
return inst;
}
}
}
@@ -0,0 +1,365 @@
/* This file was automatically generated by learn_color_balance.py script
* using the following parameters:
--num_trees 20 --hist_bin_num 64 --max_tree_depth 4 --num_augmented 2 -r 0,0
*/
const int num_features = 4;
const int _num_trees = 20;
const int _num_tree_nodes = 16;
unsigned char _feature_idx[_num_trees * num_features * 2 * (_num_tree_nodes - 1)] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1,
1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0,
0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0,
1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0,
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0,
1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,
0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1,
0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1,
0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1,
0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1,
1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0,
1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0,
0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
float _thresh_vals[_num_trees * num_features * 2 * (_num_tree_nodes - 1)] = {
.193f, .098f, .455f, .040f, .145f, .316f, .571f, .016f, .058f, .137f, .174f, .276f, .356f, .515f, .730f, .606f, .324f,
.794f, .230f, .440f, .683f, .878f, .134f, .282f, .406f, .532f, .036f, .747f, .830f, .931f, .196f, .145f, .363f, .047f,
.351f, .279f, .519f, .013f, .887f, .191f, .193f, .361f, .316f, .576f, .445f, .524f, .368f, .752f, .271f, .477f, .636f,
.798f, .146f, .249f, .423f, .521f, .446f, .023f, .795f, .908f, .259f, .026f, .557f, .125f, .121f, .432f, .774f, .500f,
.500f, .984f, .202f, .307f, .509f, .038f, .042f, .667f, .500f, .000f, .014f, .560f, .984f, .000f, .125f, .333f, .553f,
.333f, .860f, .000f, .500f, .000f, .193f, .114f, .432f, .032f, .157f, .310f, .567f, .013f, .048f, .127f, .428f, .271f,
.370f, .511f, .762f, .615f, .325f, .833f, .193f, .440f, .728f, .887f, .086f, .230f, .411f, .546f, .671f, .009f, .863f,
.944f, .283f, .174f, .515f, .087f, .209f, .356f, .693f, .059f, .145f, .344f, .254f, .316f, .455f, .571f, .811f, .537f,
.343f, .751f, .243f, .435f, .630f, .813f, .134f, .310f, .391f, .487f, .597f, .683f, .755f, .878f, .307f, .145f, .446f,
.063f, .349f, .457f, .576f, .021f, .792f, .268f, .250f, .421f, .463f, .574f, .610f, .478f, .354f, .636f, .271f, .423f,
.529f, .795f, .146f, .257f, .124f, .458f, .043f, .022f, .752f, .836f, .307f, .026f, .557f, .125f, .135f, .435f, .774f,
.500f, .500f, .984f, .238f, .500f, .509f, .038f, .042f, .600f, .400f, .000f, .014f, .500f, .667f, .000f, .125f, .250f,
.455f, .097f, .333f, .984f, .000f, .000f, .271f, .155f, .511f, .078f, .198f, .372f, .696f, .059f, .123f, .183f, .237f,
.310f, .432f, .564f, .875f, .546f, .324f, .728f, .195f, .423f, .616f, .823f, .131f, .281f, .355f, .490f, .581f, .677f,
.775f, .885f, .300f, .192f, .515f, .109f, .248f, .374f, .693f, .065f, .157f, .580f, .276f, .341f, .455f, .571f, .811f,
.532f, .324f, .679f, .196f, .421f, .630f, .804f, .111f, .252f, .348f, .447f, .565f, .247f, .755f, .866f, .335f, .188f,
.503f, .104f, .270f, .521f, .607f, .021f, .134f, .364f, .332f, .421f, .571f, .576f, .836f, .458f, .294f, .523f, .171f,
.413f, .501f, .651f, .065f, .273f, .354f, .062f, .052f, .489f, .523f, .784f, .307f, .026f, .557f, .125f, .156f, .432f,
.774f, .500f, .500f, .984f, .200f, .399f, .509f, .038f, .042f, .560f, .429f, .000f, .014f, .138f, .667f, .000f, .125f,
.222f, .500f, .522f, .286f, .984f, .500f, .000f, .295f, .186f, .564f, .104f, .293f, .432f, .762f, .059f, .127f, .271f,
.593f, .353f, .511f, .630f, .875f, .538f, .324f, .685f, .195f, .423f, .616f, .791f, .099f, .281f, .389f, .452f, .581f,
.293f, .728f, .842f, .320f, .211f, .521f, .098f, .251f, .403f, .693f, .058f, .158f, .580f, .469f, .188f, .455f, .571f,
.811f, .532f, .324f, .717f, .193f, .415f, .587f, .791f, .122f, .265f, .391f, .457f, .559f, .665f, .753f, .835f, .405f,
.305f, .503f, .135f, .457f, .483f, .607f, .044f, .182f, .394f, .569f, .156f, .484f, .576f, .836f, .455f, .294f, .576f,
.248f, .376f, .482f, .651f, .089f, .273f, .181f, .430f, .472f, .500f, .265f, .784f, .414f, .228f, .557f, .026f, .620f,
.476f, .774f, .500f, .156f, .595f, .304f, .322f, .509f, .038f, .042f, .533f, .391f, .683f, .106f, .175f, .286f, .010f,
.026f, .226f, .500f, .429f, .197f, .655f, .000f, .800f, .386f, .218f, .564f, .082f, .295f, .479f, .762f, .043f, .155f,
.293f, .440f, .432f, .511f, .630f, .875f, .544f, .306f, .705f, .185f, .412f, .606f, .791f, .109f, .263f, .372f, .502f,
.315f, .685f, .741f, .855f, .365f, .221f, .544f, .110f, .307f, .455f, .730f, .059f, .179f, .276f, .468f, .403f, .515f,
.678f, .860f, .520f, .340f, .683f, .196f, .440f, .591f, .754f, .122f, .274f, .394f, .464f, .556f, .622f, .723f, .835f,
.375f, .147f, .503f, .055f, .319f, .436f, .607f, .018f, .773f, .624f, .298f, .467f, .450f, .576f, .836f, .454f, .287f,
.571f, .163f, .376f, .308f, .648f, .086f, .236f, .373f, .427f, .477f, .496f, .333f, .795f, .500f, .307f, .774f, .026f,
.462f, .557f, .042f, .125f, .191f, .406f, .500f, .509f, .038f, .000f, .138f, .600f, .400f, .000f, .073f, .500f, .010f,
.000f, .026f, .275f, .226f, .588f, .984f, .688f, .000f, .000f, .386f, .245f, .564f, .097f, .343f, .479f, .762f, .048f,
.180f, .424f, .466f, .432f, .511f, .630f, .875f, .541f, .335f, .702f, .193f, .413f, .602f, .791f, .116f, .296f, .376f,
.481f, .210f, .641f, .741f, .863f, .341f, .145f, .531f, .087f, .283f, .455f, .729f, .047f, .109f, .189f, .469f, .375f,
.515f, .571f, .811f, .553f, .324f, .683f, .196f, .425f, .607f, .761f, .122f, .252f, .368f, .499f, .248f, .648f, .723f,
.848f, .365f, .135f, .503f, .082f, .279f, .503f, .607f, .044f, .086f, .182f, .319f, .395f, .448f, .576f, .836f, .454f,
.287f, .571f, .163f, .389f, .308f, .752f, .086f, .250f, .373f, .436f, .498f, .496f, .636f, .834f, .500f, .307f, .774f,
.026f, .474f, .557f, .042f, .125f, .000f, .414f, .561f, .509f, .038f, .000f, .138f, .560f, .626f, .000f, .400f, .174f,
.984f, .000f, .073f, .500f, .852f, .226f, .667f, .500f, .000f, .000f, .353f, .141f, .564f, .073f, .265f, .432f, .762f,
.032f, .106f, .193f, .478f, .470f, .511f, .630f, .875f, .605f, .325f, .728f, .193f, .437f, .652f, .855f, .116f, .264f,
.412f, .502f, .202f, .685f, .776f, .912f, .341f, .161f, .531f, .087f, .298f, .455f, .729f, .047f, .109f, .185f, .467f,
.375f, .515f, .571f, .811f, .580f, .324f, .685f, .196f, .425f, .607f, .824f, .122f, .252f, .368f, .512f, .231f, .648f,
.749f, .885f, .365f, .135f, .503f, .082f, .319f, .499f, .607f, .044f, .086f, .702f, .384f, .439f, .465f, .576f, .836f,
.478f, .287f, .594f, .163f, .454f, .502f, .784f, .086f, .250f, .364f, .345f, .500f, .370f, .651f, .836f, .476f, .295f,
.557f, .026f, .474f, .509f, .774f, .125f, .000f, .333f, .364f, .495f, .405f, .038f, .042f, .560f, .626f, .000f, .400f,
.174f, .010f, .000f, .059f, .500f, .852f, .226f, .667f, .667f, .000f, .000f, .419f, .157f, .564f, .078f, .295f, .479f,
.762f, .043f, .124f, .256f, .353f, .432f, .511f, .630f, .875f, .605f, .325f, .713f, .193f, .423f, .677f, .842f, .116f,
.264f, .385f, .502f, .621f, .177f, .756f, .912f, .388f, .174f, .564f, .095f, .310f, .456f, .730f, .047f, .143f, .276f,
.468f, .431f, .515f, .682f, .860f, .352f, .215f, .591f, .124f, .290f, .536f, .723f, .064f, .172f, .244f, .318f, .428f,
.548f, .666f, .825f, .394f, .146f, .503f, .055f, .349f, .499f, .607f, .018f, .773f, .271f, .327f, .439f, .465f, .576f,
.836f, .464f, .287f, .588f, .163f, .377f, .502f, .752f, .086f, .250f, .237f, .454f, .464f, .370f, .636f, .834f, .476f,
.000f, .557f, .282f, .000f, .509f, .774f, .026f, .481f, .000f, .000f, .495f, .405f, .038f, .042f, .556f, .626f, .000f,
.343f, .174f, .010f, .000f, .023f, .559f, .852f, .226f, .667f, .667f, .000f, .000f, .468f, .157f, .567f, .078f, .305f,
.511f, .762f, .043f, .124f, .271f, .464f, .511f, .193f, .630f, .875f, .351f, .208f, .602f, .116f, .302f, .489f, .720f,
.059f, .164f, .281f, .325f, .414f, .282f, .654f, .842f, .388f, .174f, .564f, .095f, .313f, .456f, .730f, .047f, .143f,
.279f, .468f, .431f, .515f, .682f, .860f, .356f, .215f, .591f, .124f, .290f, .539f, .753f, .064f, .174f, .274f, .318f,
.435f, .556f, .647f, .848f, .411f, .146f, .411f, .055f, .349f, .490f, .499f, .018f, .773f, .271f, .327f, .181f, .610f,
.467f, .448f, .287f, .163f, .454f, .086f, .262f, .359f, .636f, .041f, .476f, .200f, .633f, .257f, .428f, .522f, .795f,
.435f, .000f, .557f, .049f, .000f, .509f, .774f, .330f, .282f, .500f, .000f, .462f, .405f, .038f, .042f, .393f, .029f,
.000f, .026f, .226f, .602f, .000f, .500f, .183f, .163f, .337f, .500f, .984f, .500f, .000f, .468f, .157f, .567f, .078f,
.314f, .511f, .762f, .043f, .124f, .271f, .456f, .511f, .193f, .630f, .875f, .355f, .208f, .602f, .116f, .296f, .414f,
.761f, .059f, .179f, .263f, .324f, .381f, .502f, .681f, .855f, .356f, .174f, .537f, .095f, .289f, .455f, .729f, .047f,
.143f, .186f, .471f, .403f, .515f, .588f, .811f, .352f, .215f, .568f, .147f, .282f, .435f, .723f, .077f, .191f, .265f,
.318f, .395f, .543f, .630f, .824f, .439f, .141f, .443f, .051f, .363f, .490f, .479f, .018f, .773f, .279f, .446f, .223f,
.607f, .504f, .491f, .298f, .179f, .454f, .086f, .264f, .350f, .571f, .041f, .122f, .390f, .287f, .324f, .431f, .502f,
.752f, .500f, .307f, .774f, .026f, .500f, .557f, .042f, .125f, .000f, .434f, .524f, .509f, .038f, .000f, .138f, .447f,
.225f, .000f, .026f, .226f, .600f, .000f, .500f, .337f, .667f, .333f, .269f, .984f, .500f, .000f, .425f, .157f, .564f,
.078f, .314f, .511f, .762f, .043f, .124f, .269f, .467f, .479f, .561f, .630f, .875f, .372f, .208f, .602f, .148f, .296f,
.441f, .775f, .072f, .179f, .230f, .324f, .408f, .516f, .652f, .863f, .403f, .179f, .571f, .095f, .337f, .456f, .730f,
.047f, .145f, .267f, .458f, .056f, .521f, .682f, .860f, .352f, .193f, .568f, .095f, .282f, .435f, .696f, .056f, .147f,
.230f, .318f, .395f, .534f, .630f, .808f, .484f, .141f, .454f, .051f, .372f, .605f, .487f, .018f, .773f, .316f, .483f,
.490f, .610f, .486f, .490f, .298f, .200f, .472f, .122f, .275f, .397f, .571f, .061f, .174f, .370f, .364f, .349f, .435f,
.499f, .752f, .500f, .307f, .667f, .026f, .500f, .661f, .774f, .125f, .000f, .398f, .524f, .557f, .167f, .760f, .042f,
.500f, .667f, .000f, .429f, .138f, .984f, .000f, .029f, .364f, .906f, .226f, .667f, .500f, .000f, .000f, .488f, .157f,
.567f, .078f, .366f, .511f, .762f, .043f, .124f, .309f, .487f, .511f, .193f, .630f, .875f, .376f, .208f, .575f, .148f,
.302f, .452f, .687f, .072f, .182f, .230f, .325f, .413f, .516f, .621f, .833f, .452f, .179f, .571f, .095f, .349f, .515f,
.730f, .047f, .145f, .244f, .444f, .456f, .206f, .682f, .860f, .368f, .215f, .568f, .124f, .290f, .444f, .689f, .063f,
.188f, .252f, .343f, .406f, .516f, .630f, .804f, .162f, .085f, .490f, .044f, .086f, .372f, .454f, .015f, .424f, .463f,
.134f, .262f, .478f, .607f, .490f, .462f, .297f, .583f, .163f, .349f, .522f, .702f, .065f, .237f, .315f, .428f, .499f,
.397f, .636f, .795f, .500f, .310f, .667f, .026f, .432f, .661f, .000f, .125f, .146f, .500f, .332f, .557f, .167f, .774f,
.000f, .537f, .667f, .000f, .400f, .138f, .984f, .000f, .029f, .500f, .906f, .226f, .667f, .500f, .000f, .000f, .511f,
.157f, .696f, .078f, .361f, .564f, .875f, .043f, .124f, .223f, .404f, .561f, .630f, .762f, .896f, .376f, .209f, .578f,
.116f, .296f, .452f, .687f, .058f, .179f, .264f, .332f, .413f, .516f, .621f, .833f, .455f, .189f, .571f, .098f, .274f,
.515f, .730f, .056f, .148f, .244f, .354f, .233f, .206f, .682f, .860f, .381f, .230f, .565f, .124f, .315f, .496f, .673f,
.064f, .177f, .282f, .351f, .441f, .528f, .622f, .804f, .146f, .082f, .503f, .044f, .086f, .349f, .607f, .015f, .424f,
.085f, .134f, .262f, .298f, .576f, .836f, .349f, .271f, .466f, .146f, .297f, .413f, .608f, .065f, .200f, .364f, .318f,
.441f, .291f, .531f, .752f, .535f, .000f, .226f, .286f, .000f, .000f, .348f, .026f, .432f, .000f, .000f, .667f, .000f,
.333f, .423f, .500f, .391f, .000f, .073f, .500f, .600f, .000f, .026f, .226f, .455f, .551f, .379f, .984f, .000f, .000f,
.564f, .184f, .762f, .089f, .404f, .630f, .875f, .044f, .141f, .271f, .332f, .614f, .752f, .864f, .896f, .385f, .230f,
.590f, .116f, .332f, .505f, .687f, .059f, .185f, .283f, .506f, .452f, .546f, .626f, .833f, .254f, .161f, .456f, .087f,
.189f, .300f, .571f, .047f, .109f, .179f, .209f, .275f, .375f, .521f, .730f, .395f, .230f, .565f, .112f, .348f, .505f,
.751f, .064f, .177f, .290f, .387f, .441f, .282f, .623f, .848f, .146f, .055f, .503f, .018f, .773f, .316f, .607f, .011f,
.428f, .134f, .887f, .236f, .358f, .576f, .836f, .349f, .271f, .472f, .146f, .364f, .413f, .636f, .065f, .200f, .288f,
.366f, .441f, .398f, .516f, .795f, .592f, .000f, .774f, .282f, .000f, .735f, .042f, .026f, .476f, .000f, .000f, .038f,
.127f, .000f, .138f, .500f, .391f, .000f, .029f, .500f, .522f, .000f, .026f, .226f, .455f, .535f, .260f, .984f, .000f,
.000f, .250f, .155f, .564f, .078f, .198f, .404f, .762f, .043f, .124f, .183f, .223f, .295f, .432f, .630f, .875f, .385f,
.230f, .574f, .116f, .330f, .505f, .728f, .065f, .173f, .283f, .554f, .453f, .546f, .626f, .842f, .285f, .161f, .515f,
.087f, .209f, .441f, .693f, .047f, .109f, .184f, .264f, .334f, .456f, .571f, .811f, .352f, .198f, .555f, .124f, .274f,
.449f, .723f, .076f, .172f, .236f, .318f, .402f, .519f, .622f, .824f, .236f, .146f, .503f, .055f, .147f, .366f, .607f,
.018f, .773f, .146f, .188f, .280f, .499f, .576f, .836f, .329f, .262f, .449f, .163f, .364f, .350f, .588f, .086f, .200f,
.296f, .366f, .331f, .412f, .482f, .784f, .571f, .000f, .774f, .307f, .000f, .234f, .042f, .026f, .406f, .000f, .000f,
.647f, .620f, .000f, .138f, .500f, .400f, .000f, .109f, .068f, .537f, .000f, .026f, .226f, .500f, .447f, .522f, .984f,
.000f, .000f, .268f, .157f, .511f, .078f, .198f, .346f, .696f, .043f, .124f, .183f, .237f, .313f, .458f, .564f, .875f,
.351f, .208f, .571f, .116f, .265f, .460f, .728f, .065f, .164f, .230f, .308f, .412f, .516f, .632f, .842f, .300f, .161f,
.521f, .087f, .209f, .360f, .693f, .047f, .109f, .184f, .276f, .313f, .452f, .571f, .811f, .351f, .196f, .558f, .122f,
.274f, .449f, .723f, .072f, .166f, .236f, .318f, .421f, .520f, .617f, .824f, .190f, .134f, .503f, .051f, .402f, .280f,
.607f, .018f, .773f, .156f, .616f, .389f, .345f, .576f, .836f, .326f, .237f, .466f, .179f, .294f, .350f, .636f, .086f,
.407f, .277f, .338f, .295f, .415f, .562f, .795f, .307f, .026f, .774f, .125f, .000f, .543f, .042f, .500f, .500f, .205f,
.000f, .420f, .321f, .000f, .138f, .447f, .371f, .560f, .026f, .226f, .500f, .000f, .125f, .297f, .187f, .304f, .500f,
.522f, .984f, .000f, .256f, .155f, .564f, .078f, .193f, .314f, .762f, .043f, .124f, .428f, .223f, .258f, .470f, .630f,
.875f, .338f, .193f, .582f, .116f, .264f, .465f, .728f, .065f, .164f, .230f, .296f, .414f, .531f, .652f, .842f, .316f,
.174f, .571f, .095f, .276f, .433f, .730f, .047f, .143f, .209f, .300f, .343f, .521f, .682f, .860f, .343f, .193f, .587f,
.105f, .274f, .438f, .751f, .066f, .166f, .230f, .318f, .381f, .519f, .648f, .848f, .271f, .146f, .503f, .055f, .403f,
.366f, .607f, .018f, .773f, .365f, .445f, .444f, .517f, .576f, .836f, .324f, .237f, .478f, .115f, .287f, .350f, .636f,
.074f, .405f, .387f, .337f, .295f, .431f, .522f, .795f, .307f, .026f, .667f, .125f, .984f, .426f, .000f, .500f, .500f,
.156f, .000f, .500f, .557f, .972f, .000f, .429f, .434f, .000f, .023f, .226f, .560f, .000f, .125f, .222f, .760f, .500f,
.500f, .667f, .000f, .000f, .310f, .157f, .564f, .078f, .256f, .425f, .762f, .043f, .124f, .193f, .258f, .350f, .511f,
.630f, .875f, .337f, .182f, .597f, .101f, .264f, .443f, .728f, .063f, .161f, .216f, .296f, .395f, .522f, .652f, .842f,
.341f, .174f, .516f, .095f, .276f, .431f, .678f, .047f, .143f, .209f, .300f, .391f, .460f, .571f, .811f, .324f, .193f,
.543f, .122f, .254f, .434f, .689f, .066f, .145f, .230f, .431f, .391f, .508f, .597f, .808f, .271f, .146f, .366f, .055f,
.365f, .357f, .503f, .018f, .773f, .249f, .417f, .316f, .444f, .517f, .607f, .308f, .200f, .472f, .115f, .264f, .357f,
.571f, .058f, .138f, .260f, .364f, .338f, .431f, .499f, .752f, .414f, .049f, .557f, .125f, .307f, .438f, .000f, .500f,
.330f, .984f, .333f, .446f, .122f, .014f, .000f, .391f, .333f, .518f, .026f, .174f, .500f, .000f, .125f, .279f, .760f,
.299f, .500f, .408f, .667f, .000f, .310f, .157f, .479f, .078f, .255f, .370f, .564f, .043f, .124f, .193f, .271f, .483f,
.418f, .512f, .762f, .335f, .182f, .597f, .086f, .265f, .423f, .728f, .041f, .129f, .230f, .302f, .375f, .522f, .652f,
.842f, .374f, .184f, .544f, .095f, .300f, .455f, .811f, .047f, .145f, .254f, .341f, .404f, .516f, .569f, .860f, .326f,
.196f, .584f, .118f, .252f, .434f, .751f, .064f, .141f, .236f, .290f, .391f, .508f, .648f, .848f, .316f, .146f, .421f,
.055f, .279f, .454f, .503f, .018f, .773f, .183f, .280f, .347f, .571f, .499f, .605f, .294f, .161f, .499f, .086f, .225f,
.364f, .636f, .058f, .476f, .200f, .264f, .347f, .454f, .571f, .795f, .500f, .520f, .307f, .462f, .571f, .000f, .450f,
.051f, .220f, .557f, .833f, .984f, .000f, .655f, .532f, .355f, .073f, .556f, .026f, .138f, .500f, .000f, .500f, .183f,
.906f, .299f, .410f, .333f, .984f, .000f, .370f, .157f, .559f, .078f, .271f, .483f, .762f, .043f, .124f, .198f, .310f,
.418f, .512f, .752f, .022f, .308f, .161f, .582f, .086f, .230f, .414f, .728f, .042f, .116f, .193f, .281f, .346f, .489f,
.632f, .842f, .455f, .184f, .729f, .095f, .316f, .579f, .811f, .047f, .145f, .276f, .356f, .544f, .666f, .806f, .860f,
.326f, .174f, .584f, .088f, .247f, .435f, .751f, .042f, .141f, .215f, .290f, .368f, .528f, .648f, .848f, .347f, .306f,
.553f, .146f, .364f, .433f, .610f, .055f, .198f, .338f, .451f, .445f, .432f, .096f, .836f, .294f, .161f, .499f, .108f,
.204f, .381f, .636f, .041f, .474f, .198f, .262f, .324f, .362f, .571f, .795f, .500f, .569f, .307f, .101f, .774f, .000f,
.423f, .043f, .465f, .121f, .000f, .984f, .000f, .500f, .524f, .333f, .675f, .560f, .292f, .138f, .429f, .000f, .073f,
.550f, .000f, .195f, .377f, .500f, .984f, .000f, .479f, .183f, .704f, .082f, .310f, .567f, .875f, .043f, .141f, .271f,
.372f, .511f, .630f, .762f, .896f, .325f, .164f, .602f, .086f, .230f, .414f, .761f, .040f, .131f, .197f, .283f, .352f,
.516f, .685f, .855f};
float _leaf_vals[_num_trees * num_features * 2 * _num_tree_nodes] = {
.011f, .029f, .047f, .064f, .075f, .102f, .141f, .172f, .212f, .259f, .308f, .364f, .443f, .497f, .592f, .767f, .069f,
.165f, .241f, .278f, .357f, .412f, .463f, .540f, .562f, .623f, .676f, .734f, .797f, .838f, .894f, .944f, .014f, .040f,
.061f, .033f, .040f, .160f, .181f, .101f, .123f, .047f, .195f, .282f, .374f, .775f, .248f, .068f, .064f, .155f, .177f,
.351f, .409f, .479f, .576f, .451f, .677f, .784f, .817f, .764f, .823f, .860f, .898f, .941f, .154f, .154f, .248f, .248f,
.050f, .081f, .177f, .227f, .252f, .309f, .385f, .428f, .441f, .525f, .616f, .689f, .435f, .137f, .208f, .406f, .457f,
.483f, .518f, .576f, .669f, .844f, .593f, .706f, .853f, .853f, .895f, .925f, .012f, .029f, .047f, .067f, .111f, .134f,
.148f, .178f, .214f, .261f, .311f, .357f, .420f, .476f, .592f, .773f, .057f, .143f, .194f, .262f, .358f, .415f, .465f,
.541f, .602f, .649f, .655f, .739f, .808f, .849f, .894f, .944f, .050f, .068f, .089f, .118f, .146f, .187f, .211f, .230f,
.263f, .308f, .364f, .443f, .497f, .581f, .690f, .832f, .079f, .171f, .263f, .306f, .356f, .401f, .452f, .486f, .538f,
.577f, .629f, .687f, .722f, .766f, .834f, .900f, .046f, .066f, .083f, .064f, .090f, .113f, .143f, .235f, .289f, .416f,
.094f, .204f, .454f, .074f, .697f, .836f, .067f, .156f, .200f, .332f, .266f, .411f, .473f, .514f, .627f, .575f, .758f,
.676f, .775f, .826f, .864f, .900f, .162f, .162f, .248f, .248f, .079f, .102f, .165f, .241f, .281f, .337f, .385f, .428f,
.441f, .525f, .616f, .689f, .397f, .137f, .166f, .307f, .421f, .443f, .525f, .486f, .527f, .585f, .687f, .611f, .767f,
.821f, .942f, .916f, .055f, .073f, .090f, .110f, .165f, .188f, .207f, .225f, .261f, .312f, .358f, .420f, .475f, .579f,
.693f, .875f, .079f, .164f, .238f, .277f, .325f, .378f, .448f, .487f, .527f, .557f, .610f, .648f, .716f, .769f, .830f,
.896f, .038f, .090f, .112f, .131f, .206f, .160f, .224f, .249f, .286f, .334f, .370f, .443f, .497f, .581f, .690f, .832f,
.056f, .153f, .221f, .278f, .311f, .365f, .420f, .463f, .524f, .562f, .625f, .699f, .696f, .762f, .829f, .889f, .024f,
.093f, .104f, .119f, .104f, .154f, .153f, .216f, .273f, .376f, .202f, .138f, .609f, .690f, .814f, .930f, .027f, .098f,
.158f, .252f, .304f, .393f, .706f, .462f, .630f, .554f, .845f, .643f, .852f, .694f, .781f, .858f, .169f, .169f, .248f,
.248f, .105f, .124f, .110f, .197f, .308f, .242f, .385f, .428f, .441f, .525f, .616f, .689f, .375f, .137f, .146f, .314f,
.412f, .437f, .454f, .520f, .510f, .615f, .692f, .576f, .701f, .701f, .780f, .846f, .039f, .091f, .109f, .125f, .209f,
.256f, .251f, .126f, .295f, .350f, .420f, .475f, .568f, .625f, .738f, .875f, .055f, .153f, .236f, .281f, .338f, .390f,
.425f, .462f, .522f, .563f, .609f, .687f, .674f, .721f, .776f, .846f, .034f, .078f, .123f, .148f, .201f, .153f, .215f,
.253f, .239f, .335f, .382f, .446f, .502f, .581f, .690f, .832f, .063f, .145f, .220f, .284f, .340f, .386f, .424f, .467f,
.520f, .550f, .611f, .671f, .718f, .758f, .792f, .854f, .065f, .117f, .138f, .163f, .225f, .371f, .188f, .145f, .457f,
.345f, .102f, .276f, .609f, .690f, .814f, .930f, .032f, .133f, .188f, .247f, .268f, .350f, .427f, .495f, .538f, .578f,
.641f, .835f, .700f, .759f, .780f, .868f, .187f, .187f, .135f, .170f, .218f, .144f, .261f, .340f, .416f, .335f, .388f,
.428f, .441f, .525f, .616f, .689f, .367f, .273f, .143f, .308f, .382f, .439f, .410f, .470f, .524f, .461f, .626f, .528f,
.583f, .702f, .673f, .773f, .031f, .068f, .124f, .154f, .217f, .154f, .255f, .302f, .358f, .405f, .435f, .475f, .568f,
.625f, .738f, .875f, .061f, .144f, .221f, .261f, .325f, .366f, .448f, .495f, .538f, .590f, .618f, .659f, .686f, .739f,
.791f, .858f, .034f, .079f, .149f, .175f, .198f, .231f, .249f, .327f, .353f, .382f, .443f, .489f, .570f, .649f, .740f,
.882f, .076f, .148f, .218f, .296f, .357f, .400f, .444f, .472f, .516f, .554f, .597f, .630f, .678f, .722f, .781f, .864f,
.021f, .055f, .135f, .053f, .180f, .150f, .370f, .214f, .331f, .530f, .219f, .326f, .609f, .690f, .814f, .930f, .049f,
.095f, .149f, .216f, .370f, .294f, .443f, .489f, .526f, .594f, .621f, .747f, .656f, .762f, .780f, .884f, .216f, .248f,
.160f, .190f, .197f, .356f, .296f, .341f, .391f, .428f, .441f, .525f, .593f, .668f, .760f, .637f, .388f, .250f, .155f,
.334f, .419f, .456f, .497f, .448f, .591f, .542f, .552f, .719f, .656f, .709f, .849f, .897f, .034f, .078f, .151f, .184f,
.211f, .253f, .262f, .351f, .358f, .405f, .435f, .475f, .568f, .625f, .738f, .875f, .076f, .148f, .229f, .303f, .341f,
.376f, .444f, .480f, .548f, .510f, .594f, .638f, .685f, .742f, .800f, .882f, .028f, .062f, .089f, .114f, .174f, .196f,
.241f, .294f, .335f, .371f, .443f, .482f, .511f, .590f, .714f, .832f, .075f, .157f, .223f, .281f, .342f, .386f, .450f,
.489f, .542f, .590f, .611f, .653f, .682f, .728f, .783f, .893f, .041f, .076f, .186f, .109f, .175f, .195f, .209f, .227f,
.274f, .355f, .196f, .314f, .609f, .690f, .814f, .930f, .049f, .097f, .161f, .221f, .415f, .304f, .454f, .492f, .527f,
.581f, .629f, .747f, .685f, .758f, .836f, .914f, .225f, .248f, .187f, .074f, .228f, .365f, .295f, .337f, .391f, .428f,
.441f, .525f, .593f, .668f, .760f, .637f, .413f, .277f, .431f, .456f, .115f, .162f, .254f, .334f, .503f, .661f, .515f,
.515f, .696f, .751f, .836f, .897f, .023f, .057f, .090f, .116f, .180f, .197f, .239f, .283f, .338f, .365f, .420f, .475f,
.568f, .625f, .738f, .875f, .074f, .157f, .227f, .282f, .365f, .410f, .451f, .504f, .577f, .610f, .646f, .679f, .728f,
.782f, .855f, .923f, .028f, .062f, .089f, .120f, .165f, .204f, .243f, .304f, .335f, .371f, .443f, .482f, .511f, .590f,
.714f, .832f, .073f, .157f, .220f, .287f, .343f, .393f, .451f, .489f, .567f, .596f, .616f, .650f, .711f, .760f, .840f,
.917f, .041f, .076f, .186f, .092f, .203f, .116f, .222f, .261f, .330f, .438f, .214f, .316f, .609f, .690f, .814f, .930f,
.049f, .104f, .163f, .221f, .414f, .448f, .513f, .561f, .566f, .744f, .614f, .683f, .721f, .761f, .854f, .915f, .228f,
.248f, .196f, .096f, .300f, .225f, .295f, .344f, .466f, .385f, .403f, .468f, .441f, .525f, .616f, .689f, .414f, .307f,
.445f, .460f, .115f, .162f, .254f, .334f, .459f, .495f, .501f, .705f, .680f, .751f, .836f, .897f, .031f, .065f, .100f,
.132f, .201f, .221f, .280f, .333f, .374f, .405f, .435f, .475f, .568f, .625f, .738f, .875f, .073f, .157f, .226f, .288f,
.349f, .401f, .450f, .489f, .589f, .621f, .649f, .680f, .718f, .759f, .843f, .923f, .029f, .067f, .107f, .140f, .207f,
.227f, .279f, .339f, .369f, .393f, .444f, .494f, .575f, .651f, .740f, .882f, .042f, .093f, .147f, .184f, .220f, .256f,
.290f, .323f, .402f, .455f, .495f, .540f, .619f, .687f, .748f, .876f, .021f, .055f, .098f, .053f, .206f, .221f, .389f,
.239f, .343f, .438f, .228f, .316f, .609f, .690f, .814f, .930f, .049f, .104f, .160f, .221f, .235f, .426f, .455f, .529f,
.623f, .551f, .600f, .677f, .697f, .760f, .836f, .914f, .232f, .201f, .231f, .309f, .117f, .096f, .070f, .044f, .466f,
.385f, .403f, .468f, .441f, .525f, .616f, .689f, .418f, .251f, .450f, .394f, .115f, .162f, .254f, .334f, .460f, .488f,
.494f, .703f, .680f, .751f, .836f, .897f, .031f, .065f, .100f, .132f, .207f, .229f, .289f, .342f, .435f, .346f, .461f,
.482f, .568f, .625f, .738f, .875f, .043f, .093f, .146f, .180f, .241f, .278f, .307f, .330f, .391f, .451f, .472f, .524f,
.610f, .651f, .741f, .874f, .029f, .067f, .107f, .140f, .212f, .233f, .269f, .343f, .369f, .393f, .444f, .494f, .575f,
.651f, .740f, .882f, .042f, .093f, .151f, .188f, .238f, .271f, .293f, .321f, .408f, .459f, .513f, .553f, .609f, .672f,
.777f, .893f, .021f, .055f, .098f, .053f, .210f, .226f, .355f, .247f, .439f, .514f, .637f, .836f, .333f, .420f, .227f,
.313f, .019f, .060f, .098f, .133f, .147f, .179f, .237f, .125f, .196f, .407f, .451f, .477f, .572f, .654f, .774f, .903f,
.239f, .375f, .204f, .250f, .150f, .150f, .096f, .057f, .426f, .383f, .403f, .468f, .441f, .525f, .616f, .689f, .407f,
.407f, .126f, .244f, .134f, .203f, .294f, .406f, .449f, .469f, .573f, .482f, .751f, .751f, .836f, .897f, .031f, .065f,
.100f, .132f, .212f, .232f, .281f, .348f, .435f, .346f, .461f, .482f, .568f, .625f, .738f, .875f, .043f, .093f, .152f,
.190f, .235f, .262f, .295f, .330f, .354f, .417f, .455f, .492f, .620f, .685f, .768f, .888f, .029f, .067f, .107f, .140f,
.167f, .219f, .238f, .298f, .352f, .382f, .443f, .485f, .532f, .596f, .714f, .832f, .056f, .105f, .161f, .195f, .230f,
.267f, .289f, .322f, .367f, .414f, .462f, .529f, .579f, .667f, .742f, .875f, .021f, .053f, .094f, .052f, .214f, .235f,
.288f, .235f, .451f, .530f, .632f, .826f, .316f, .233f, .466f, .356f, .019f, .060f, .084f, .110f, .192f, .162f, .235f,
.287f, .418f, .363f, .447f, .482f, .573f, .631f, .724f, .880f, .243f, .248f, .210f, .074f, .237f, .308f, .378f, .334f,
.391f, .428f, .441f, .525f, .593f, .668f, .760f, .637f, .398f, .398f, .235f, .418f, .105f, .166f, .287f, .405f, .458f,
.482f, .589f, .488f, .630f, .630f, .751f, .866f, .031f, .065f, .100f, .132f, .218f, .235f, .269f, .344f, .400f, .435f,
.478f, .396f, .568f, .625f, .738f, .875f, .056f, .106f, .160f, .190f, .215f, .248f, .292f, .331f, .383f, .415f, .459f,
.503f, .594f, .678f, .783f, .898f, .029f, .067f, .108f, .144f, .226f, .241f, .293f, .353f, .275f, .384f, .446f, .502f,
.579f, .651f, .740f, .882f, .038f, .077f, .112f, .161f, .202f, .241f, .289f, .323f, .362f, .410f, .462f, .515f, .582f,
.658f, .727f, .868f, .021f, .053f, .094f, .052f, .227f, .249f, .316f, .237f, .483f, .630f, .726f, .836f, .583f, .493f,
.274f, .426f, .034f, .080f, .109f, .146f, .210f, .181f, .285f, .223f, .385f, .436f, .469f, .544f, .576f, .619f, .714f,
.880f, .250f, .248f, .218f, .074f, .241f, .293f, .378f, .334f, .408f, .522f, .409f, .317f, .547f, .397f, .616f, .689f,
.410f, .308f, .440f, .469f, .111f, .160f, .250f, .328f, .516f, .674f, .506f, .506f, .685f, .751f, .836f, .897f, .031f,
.065f, .100f, .132f, .229f, .267f, .359f, .244f, .442f, .346f, .461f, .482f, .568f, .625f, .738f, .875f, .056f, .106f,
.160f, .190f, .219f, .255f, .302f, .340f, .392f, .421f, .463f, .496f, .578f, .642f, .717f, .869f, .029f, .067f, .108f,
.144f, .223f, .250f, .318f, .362f, .400f, .444f, .476f, .508f, .579f, .651f, .740f, .882f, .032f, .096f, .155f, .192f,
.227f, .255f, .306f, .349f, .381f, .418f, .464f, .519f, .589f, .653f, .721f, .867f, .018f, .049f, .037f, .080f, .201f,
.248f, .091f, .152f, .229f, .253f, .323f, .259f, .632f, .826f, .274f, .428f, .028f, .096f, .165f, .230f, .434f, .361f,
.449f, .500f, .554f, .596f, .610f, .679f, .678f, .743f, .801f, .903f, .260f, .248f, .208f, .243f, .259f, .302f, .414f,
.315f, .408f, .522f, .409f, .317f, .535f, .620f, .357f, .692f, .405f, .266f, .432f, .463f, .111f, .170f, .250f, .328f,
.535f, .656f, .525f, .525f, .693f, .751f, .836f, .897f, .031f, .065f, .100f, .132f, .211f, .249f, .320f, .372f, .478f,
.396f, .568f, .608f, .647f, .738f, .849f, .902f, .032f, .095f, .153f, .190f, .237f, .269f, .305f, .344f, .390f, .423f,
.465f, .514f, .581f, .637f, .718f, .869f, .033f, .072f, .110f, .152f, .214f, .250f, .273f, .316f, .419f, .449f, .476f,
.508f, .579f, .651f, .740f, .882f, .039f, .095f, .144f, .185f, .250f, .296f, .323f, .362f, .416f, .467f, .502f, .531f,
.589f, .643f, .714f, .867f, .018f, .049f, .036f, .079f, .095f, .246f, .091f, .131f, .233f, .268f, .342f, .294f, .609f,
.690f, .814f, .930f, .037f, .093f, .146f, .175f, .270f, .226f, .408f, .339f, .448f, .303f, .472f, .506f, .580f, .640f,
.726f, .880f, .273f, .235f, .283f, .319f, .117f, .096f, .070f, .044f, .475f, .609f, .357f, .692f, .414f, .278f, .536f,
.462f, .374f, .229f, .139f, .344f, .414f, .441f, .505f, .402f, .496f, .572f, .606f, .526f, .680f, .751f, .836f, .897f,
.031f, .072f, .110f, .157f, .239f, .277f, .437f, .352f, .565f, .578f, .631f, .514f, .748f, .578f, .849f, .902f, .039f,
.095f, .152f, .202f, .252f, .305f, .345f, .432f, .423f, .467f, .509f, .544f, .592f, .640f, .713f, .869f, .028f, .062f,
.089f, .120f, .152f, .173f, .191f, .211f, .252f, .277f, .302f, .324f, .446f, .502f, .592f, .767f, .043f, .090f, .136f,
.191f, .256f, .311f, .359f, .390f, .424f, .470f, .492f, .534f, .593f, .655f, .776f, .893f, .012f, .032f, .021f, .058f,
.093f, .135f, .059f, .026f, .228f, .270f, .292f, .324f, .609f, .690f, .814f, .930f, .042f, .097f, .141f, .176f, .218f,
.342f, .143f, .270f, .446f, .303f, .480f, .516f, .580f, .627f, .774f, .903f, .292f, .238f, .299f, .331f, .117f, .096f,
.070f, .044f, .430f, .536f, .612f, .347f, .593f, .668f, .760f, .637f, .386f, .214f, .133f, .342f, .405f, .444f, .507f,
.442f, .464f, .479f, .565f, .517f, .680f, .751f, .836f, .897f, .031f, .065f, .100f, .131f, .165f, .188f, .204f, .222f,
.275f, .303f, .336f, .383f, .568f, .625f, .738f, .875f, .046f, .101f, .141f, .193f, .256f, .302f, .345f, .451f, .425f,
.468f, .509f, .535f, .586f, .649f, .744f, .874f, .028f, .062f, .089f, .120f, .155f, .189f, .214f, .247f, .310f, .338f,
.392f, .444f, .497f, .581f, .690f, .832f, .049f, .101f, .142f, .181f, .211f, .247f, .287f, .325f, .377f, .426f, .473f,
.530f, .587f, .645f, .745f, .875f, .021f, .055f, .098f, .053f, .280f, .306f, .168f, .226f, .257f, .314f, .351f, .309f,
.609f, .690f, .814f, .930f, .048f, .102f, .140f, .185f, .274f, .321f, .143f, .250f, .443f, .359f, .443f, .483f, .542f,
.609f, .746f, .894f, .317f, .252f, .324f, .348f, .117f, .096f, .070f, .044f, .594f, .404f, .499f, .531f, .593f, .668f,
.760f, .637f, .402f, .260f, .124f, .345f, .382f, .444f, .423f, .448f, .473f, .511f, .563f, .516f, .680f, .751f, .836f,
.897f, .031f, .065f, .100f, .132f, .166f, .188f, .207f, .225f, .301f, .322f, .341f, .404f, .475f, .579f, .693f, .875f,
.048f, .100f, .142f, .187f, .216f, .244f, .283f, .318f, .378f, .430f, .473f, .525f, .586f, .642f, .744f, .874f, .028f,
.062f, .089f, .120f, .155f, .189f, .215f, .249f, .274f, .345f, .363f, .409f, .502f, .581f, .690f, .832f, .053f, .095f,
.132f, .175f, .211f, .243f, .288f, .326f, .399f, .434f, .474f, .527f, .579f, .637f, .745f, .875f, .021f, .053f, .092f,
.052f, .054f, .108f, .180f, .116f, .204f, .271f, .321f, .362f, .609f, .690f, .814f, .930f, .054f, .105f, .177f, .148f,
.216f, .260f, .394f, .301f, .226f, .381f, .443f, .484f, .588f, .680f, .774f, .903f, .339f, .339f, .248f, .248f, .222f,
.288f, .111f, .057f, .352f, .366f, .422f, .505f, .593f, .668f, .760f, .637f, .387f, .016f, .160f, .380f, .113f, .221f,
.346f, .410f, .442f, .482f, .474f, .496f, .563f, .508f, .700f, .866f, .031f, .065f, .100f, .131f, .148f, .177f, .202f,
.218f, .338f, .280f, .357f, .420f, .568f, .625f, .738f, .875f, .053f, .098f, .135f, .175f, .212f, .242f, .277f, .307f,
.384f, .434f, .476f, .536f, .594f, .644f, .744f, .874f, .029f, .067f, .107f, .140f, .184f, .215f, .249f, .274f, .335f,
.376f, .406f, .479f, .579f, .651f, .740f, .882f, .056f, .085f, .129f, .177f, .209f, .238f, .289f, .323f, .361f, .413f,
.471f, .539f, .601f, .677f, .776f, .893f, .021f, .055f, .098f, .053f, .138f, .211f, .281f, .183f, .345f, .232f, .387f,
.290f, .609f, .690f, .814f, .930f, .057f, .083f, .167f, .124f, .232f, .182f, .401f, .293f, .226f, .358f, .445f, .497f,
.581f, .654f, .774f, .903f, .353f, .353f, .248f, .248f, .112f, .240f, .290f, .096f, .354f, .374f, .393f, .433f, .595f,
.468f, .648f, .692f, .378f, .016f, .106f, .339f, .119f, .191f, .327f, .397f, .446f, .477f, .512f, .549f, .680f, .751f,
.836f, .897f, .031f, .065f, .100f, .132f, .172f, .211f, .338f, .261f, .349f, .375f, .402f, .437f, .568f, .625f, .738f,
.875f, .057f, .086f, .129f, .173f, .199f, .232f, .280f, .306f, .366f, .419f, .471f, .531f, .592f, .652f, .744f, .874f,
.029f, .067f, .107f, .140f, .184f, .215f, .249f, .286f, .371f, .396f, .420f, .443f, .477f, .533f, .682f, .832f, .047f,
.094f, .134f, .165f, .205f, .237f, .277f, .306f, .366f, .411f, .466f, .519f, .558f, .619f, .727f, .868f, .021f, .055f,
.098f, .053f, .115f, .175f, .238f, .195f, .305f, .390f, .332f, .232f, .424f, .243f, .626f, .826f, .030f, .071f, .108f,
.138f, .195f, .114f, .295f, .240f, .320f, .362f, .424f, .489f, .542f, .613f, .730f, .880f, .385f, .385f, .248f, .375f,
.172f, .262f, .393f, .347f, .364f, .416f, .412f, .433f, .448f, .486f, .648f, .533f, .354f, .016f, .183f, .308f, .111f,
.177f, .269f, .346f, .414f, .440f, .454f, .520f, .507f, .544f, .700f, .866f, .031f, .065f, .100f, .132f, .172f, .210f,
.232f, .261f, .323f, .361f, .390f, .418f, .444f, .468f, .548f, .773f, .030f, .072f, .110f, .153f, .205f, .246f, .283f,
.312f, .355f, .399f, .461f, .532f, .594f, .652f, .744f, .874f, .029f, .067f, .108f, .147f, .206f, .237f, .286f, .334f,
.358f, .424f, .461f, .494f, .522f, .565f, .791f, .882f, .039f, .091f, .132f, .168f, .214f, .244f, .278f, .311f, .373f,
.413f, .465f, .521f, .596f, .677f, .776f, .893f, .021f, .055f, .098f, .053f, .166f, .219f, .411f, .273f, .379f, .461f,
.256f, .206f, .501f, .264f, .587f, .816f, .031f, .066f, .104f, .139f, .154f, .187f, .203f, .241f, .318f, .360f, .414f,
.493f, .621f, .692f, .775f, .903f, .429f, .223f, .430f, .462f, .480f, .504f, .532f, .555f, .170f, .266f, .111f, .057f,
.327f, .407f, .441f, .475f, .307f, .307f, .126f, .257f, .091f, .171f, .253f, .332f, .390f, .420f, .457f, .488f, .573f,
.503f, .700f, .866f, .031f, .065f, .100f, .132f, .176f, .215f, .261f, .311f, .386f, .439f, .466f, .489f, .562f, .514f,
.612f, .773f, .033f, .073f, .102f, .146f, .177f, .216f, .255f, .295f, .323f, .388f, .450f, .502f, .580f, .644f, .744f,
.874f, .029f, .067f, .108f, .147f, .210f, .259f, .308f, .364f, .503f, .543f, .584f, .646f, .723f, .578f, .791f, .882f,
.028f, .068f, .122f, .163f, .194f, .231f, .273f, .310f, .357f, .403f, .464f, .533f, .596f, .677f, .776f, .893f, .048f,
.094f, .177f, .218f, .307f, .432f, .273f, .229f, .500f, .253f, .603f, .513f, .754f, .673f, .825f, .930f, .018f, .062f,
.102f, .129f, .142f, .173f, .188f, .226f, .391f, .313f, .471f, .402f, .621f, .692f, .775f, .903f, .489f, .443f, .207f,
.494f, .541f, .577f, .648f, .720f, .175f, .237f, .111f, .057f, .287f, .335f, .409f, .374f, .264f, .187f, .285f, .318f,
.156f, .106f, .341f, .251f, .380f, .391f, .432f, .475f, .584f, .513f, .700f, .866f, .031f, .068f, .108f, .157f, .212f,
.261f, .312f, .371f, .483f, .517f, .571f, .611f, .665f, .738f, .849f, .902f, .028f, .068f, .120f, .154f, .188f, .219f,
.259f, .305f, .338f, .387f, .454f, .520f, .609f, .691f, .768f, .888f};
+73
View File
@@ -0,0 +1,73 @@
/*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-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// * 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 __NORM2_HPP__
#define __NORM2_HPP__
template<bool B, class T = void> struct iftype {};
template<class T> struct iftype<true, T> { typedef T type; }; // enable_if
template<class T, T v> struct int_const { // integral_constant
static const T value = v;
typedef T value_type;
typedef int_const type;
operator value_type() const { return value; }
value_type operator()() const { return value; }
};
typedef int_const<bool,true> ttype; // true_type
typedef int_const<bool,false> ftype; // false_type
template <class T, class U> struct same_as : ftype {};
template <class T> struct same_as<T, T> : ttype {}; // is_same
template <typename _Tp> struct is_norm2_type :
int_const<bool, !same_as<_Tp, int8_t>::value
&& !same_as<_Tp, uint8_t>::value
&& !same_as<_Tp, uint16_t>::value
&& !same_as<_Tp, uint32_t>::value>{};
template <typename _Tp, int cn> static inline typename iftype< is_norm2_type<_Tp>::value, _Tp >::
type norm2(cv::Vec<_Tp, cn> a, cv::Vec<_Tp, cn> b) { return (a - b).dot(a - b); }
template <typename _Tp> static inline typename iftype< is_norm2_type<_Tp>::value, _Tp >::
type norm2(const _Tp &a, const _Tp &b) { return (a - b)*(a - b); }
#endif /* __NORM2_HPP__ */
+172
View File
@@ -0,0 +1,172 @@
// 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 "opencv2/xphoto.hpp"
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
template<class T>
class Vec3fTo {
public :
cv::Vec3f a;
Vec3fTo(cv::Vec3f x) {
a = x;
};
T extract();
cv::Vec3f make(int);
};
template<>
uint8_t Vec3fTo<uint8_t>::extract()
{
return static_cast<uint8_t>(a[0]);
}
template<>
cv::Vec3b Vec3fTo<cv::Vec3b>::extract()
{
return a;
}
template<>
cv::Vec3f Vec3fTo<uint8_t>::make(int x)
{
return cv::Vec3f((a*x)/x);
}
template<>
cv::Vec3f Vec3fTo<cv::Vec3b>::make(int x)
{
return cv::Vec3f(static_cast<float>(static_cast<int>(a[0]*x)/x),
static_cast<float>(static_cast<int>(a[1] * x) / x),
static_cast<float>(static_cast<int>(a[2] * x) / x));
}
namespace cv
{
namespace xphoto
{
template<typename Type>
class ParallelOilPainting : public ParallelLoopBody
{
private:
Mat & imgSrc;
Mat &dst;
Mat &imgLuminance;
int halfsize;
int dynRatio;
public:
ParallelOilPainting(Mat& img, Mat &d, Mat &iLuminance, int r,int k) :
imgSrc(img),
dst(d),
imgLuminance(iLuminance),
halfsize(r),
dynRatio(k)
{}
virtual void operator()(const Range& range) const CV_OVERRIDE
{
std::vector<int> histogram(256);
std::vector<Vec3f> meanBGR(256);
for (int y = range.start; y < range.end; y++)
{
Type *vDst = dst.ptr<Type>(y);
for (int x = 0; x < imgSrc.cols; x++, vDst++)
{
if (x == 0)
{
histogram.assign(256, 0);
meanBGR.assign(256, Vec3f(0,0,0));
for (int yy = -halfsize; yy <= halfsize; yy++)
{
if (y + yy >= 0 && y + yy < imgSrc.rows)
{
Type *vPtr = imgSrc.ptr<Type>(y + yy) + x - 0;
uint8_t *uc = imgLuminance.ptr(y + yy) + x - 0;
for (int xx = 0; xx <= halfsize; xx++, vPtr++, uc++)
{
if (x + xx >= 0 && x + xx < imgSrc.cols)
{
histogram[*uc]++;
meanBGR[*uc] += Vec3fTo<Type>(*vPtr).make(dynRatio);
}
}
}
}
}
else
{
for (int yy = -halfsize; yy <= halfsize; yy++)
{
if (y + yy >= 0 && y + yy < imgSrc.rows)
{
Type *vPtr = imgSrc.ptr<Type>(y + yy) + x - halfsize - 1;
uint8_t *uc = imgLuminance.ptr(y + yy) + x - halfsize - 1;
int xx = -halfsize - 1;
if (x + xx >= 0 && x + xx < imgSrc.cols)
{
histogram[*uc]--;
meanBGR[*uc] -= Vec3fTo<Type>(*vPtr).make(dynRatio);
}
vPtr = imgSrc.ptr<Type>(y + yy) + x + halfsize;
uc = imgLuminance.ptr(y + yy) + x + halfsize;
xx = halfsize;
if (x + xx >= 0 && x + xx < imgSrc.cols)
{
histogram[*uc]++;
meanBGR[*uc] += Vec3fTo<Type>(*vPtr).make(dynRatio);
}
}
}
}
auto pos = distance(histogram.begin(), std::max_element(histogram.begin(), histogram.end()));
*vDst = Vec3fTo<Type>(meanBGR[pos] / histogram[pos]).extract();
}
}
}
};
void oilPainting(InputArray src, OutputArray dst, int size, int dynValue)
{
oilPainting(src, dst, size, dynValue, COLOR_BGR2GRAY);
}
void oilPainting(InputArray _src, OutputArray _dst, int size, int dynValue,int code)
{
CV_CheckType(_src.type(), _src.type() == CV_8UC1 || _src.type() == CV_8UC3, "only 1 or 3 channels (CV_8UC)");
CV_Assert(_src.kind() == _InputArray::MAT);
CV_Assert(size >= 1);
CV_CheckGT(dynValue , 0,"dynValue must be 0");
CV_CheckLT(dynValue, 128, "dynValue must less than 128 ");
Mat src = _src.getMat();
Mat lum,dst(_src.size(),_src.type());
if (src.type() == CV_8UC3)
{
cvtColor(_src, lum, code);
if (lum.channels() > 1)
{
extractChannel(lum, lum, 0);
}
}
else
lum = src.clone();
double dratio = 1 / double(dynValue);
lum.forEach<uint8_t>([=](uint8_t &pixel, const int * /*position*/) { pixel = saturate_cast<uint8_t>(cvRound(pixel * dratio)); });
if (_src.type() == CV_8UC1)
{
ParallelOilPainting<uint8_t> oilAlgo(src, dst, lum, size, dynValue);
parallel_for_(Range(0, src.rows), oilAlgo);
}
else
{
ParallelOilPainting<Vec3b> oilAlgo(src, dst, lum, size, dynValue);
parallel_for_(Range(0, src.rows), oilAlgo);
}
dst.copyTo(_dst);
dst = (dst / dynValue) * dynValue;
}
}
}
+246
View File
@@ -0,0 +1,246 @@
/*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-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// * 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 __PHOTOMONTAGE_HPP__
#define __PHOTOMONTAGE_HPP__
#include <vector>
#include <stack>
#include <limits>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <fstream>
#include <time.h>
#include <functional>
#include "norm2.hpp"
#include "blending.hpp"
namespace gcoptimization
{
#include "gcgraph.hpp"
typedef float TWeight;
typedef int labelTp;
#define GCInfinity 10*1000*1000
#define eps 0.02
template <typename Tp> static int min_idx(std::vector <Tp> vec)
{
return int( std::min_element(vec.begin(), vec.end()) - vec.begin() );
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
template <typename Tp> class Photomontage
{
private:
const std::vector <std::vector <Tp> > &pointSeq; // points for stitching
const std::vector <std::vector <uint8_t> > &maskSeq; // corresponding masks
const std::vector <std::vector <int> > &linkIdx; // vector of neighbors for pointSeq
std::vector <std::vector <labelTp> > labelings; // vector of labelings
std::vector <TWeight> distances; // vector of max-flow costs for different labeling
std::vector <labelTp> &labelSeq; // current best labeling
TWeight singleExpansion(const int alpha); // single neighbor computing
class ParallelExpansion : public cv::ParallelLoopBody
{
public:
Photomontage <Tp> *main;
ParallelExpansion(Photomontage <Tp> *_main) : main(_main){}
~ParallelExpansion(){};
void operator () (const cv::Range &range) const CV_OVERRIDE
{
for (int i = range.start; i <= range.end - 1; ++i)
main->distances[i] = main->singleExpansion(i);
}
} parallelExpansion;
void operator =(const Photomontage <Tp>&) const {};
protected:
virtual TWeight dist(const Tp &l1p1, const Tp &l1p2, const Tp &l2p1, const Tp &l2p2);
virtual void setWeights(GCGraph <TWeight> &graph,
const int idx1, const int idx2, const int l1, const int l2, const int lx);
public:
void gradientDescent(); // gradient descent in alpha-expansion topology
Photomontage(const std::vector <std::vector <Tp> > &pointSeq,
const std::vector <std::vector <uint8_t> > &maskSeq,
const std::vector <std::vector <int> > &linkIdx,
std::vector <labelTp> &labelSeq);
virtual ~Photomontage(){};
};
template <typename Tp> inline TWeight Photomontage <Tp>::
dist(const Tp &l1p1, const Tp &l1p2, const Tp &l2p1, const Tp &l2p2)
{
return norm2(l1p1, l2p1) + norm2(l1p2, l2p2);
}
template <typename Tp> void Photomontage <Tp>::
setWeights(GCGraph <TWeight> &graph, const int idx1, const int idx2,
const int l1, const int l2, const int lx)
{
if ((size_t)idx1 >= pointSeq.size() || (size_t)idx2 >= pointSeq.size()
|| (size_t)l1 >= pointSeq[idx1].size() || (size_t)l1 >= pointSeq[idx2].size()
|| (size_t)l2 >= pointSeq[idx1].size() || (size_t)l2 >= pointSeq[idx2].size()
|| (size_t)lx >= pointSeq[idx1].size() || (size_t)lx >= pointSeq[idx2].size())
return;
if (l1 == l2)
{
/** Link from A to B **/
TWeight weightAB = dist( pointSeq[idx1][l1], pointSeq[idx2][l1],
pointSeq[idx1][lx], pointSeq[idx2][lx] );
graph.addEdges( idx1, idx2, weightAB, weightAB );
}
else
{
int X = graph.addVtx();
/** Link from X to sink **/
TWeight weightXS = dist( pointSeq[idx1][l1], pointSeq[idx2][l1],
pointSeq[idx1][l2], pointSeq[idx2][l2] );
graph.addTermWeights( X, 0, weightXS );
/** Link from A to X **/
TWeight weightAX = dist( pointSeq[idx1][l1], pointSeq[idx2][l1],
pointSeq[idx1][lx], pointSeq[idx2][lx] );
graph.addEdges( idx1, X, weightAX, weightAX );
/** Link from X to B **/
TWeight weightXB = dist( pointSeq[idx1][lx], pointSeq[idx1][lx],
pointSeq[idx1][l2], pointSeq[idx1][l2] );
graph.addEdges( X, idx2, weightXB, weightXB );
}
}
template <typename Tp> TWeight Photomontage <Tp>::
singleExpansion(const int alpha)
{
GCGraph <TWeight> graph( 3*int(pointSeq.size()), 4*int(pointSeq.size()) );
/** Terminal links **/
for (size_t i = 0; i < maskSeq.size(); ++i)
graph.addTermWeights( graph.addVtx(),
maskSeq[i][alpha] ? TWeight(0) : TWeight(GCInfinity), 0 );
/** Neighbor links **/
for (size_t i = 0; i < pointSeq.size(); ++i)
for (size_t j = 0; j < linkIdx[i].size(); ++j)
if ( linkIdx[i][j] != -1)
setWeights( graph, int(i), linkIdx[i][j],
labelSeq[i], labelSeq[linkIdx[i][j]], alpha );
/** Max-flow computation **/
TWeight result = graph.maxFlow();
/** Writing results **/
for (size_t i = 0; i < pointSeq.size(); ++i)
labelings[i][alpha] = graph.inSourceSegment(int(i)) ? labelSeq[i] : alpha;
return result;
}
template <typename Tp> void Photomontage <Tp>::
gradientDescent()
{
TWeight optValue = std::numeric_limits<TWeight>::max();
for (int num = -1; /**/; num = -1)
{
int range = int( pointSeq[0].size() );
parallel_for_( cv::Range(0, range), parallelExpansion );
int minIndex = min_idx(distances);
TWeight minValue = distances[minIndex];
if (minValue < (1.00 - eps)*optValue)
optValue = distances[num = minIndex];
if (num == -1)
break;
for (size_t i = 0; i < labelSeq.size(); ++i)
labelSeq[i] = labelings[i][num];
}
}
template <typename Tp> Photomontage <Tp>::
Photomontage( const std::vector <std::vector <Tp> > &_pointSeq,
const std::vector <std::vector <uint8_t> > &_maskSeq,
const std::vector <std::vector <int> > &_linkIdx,
std::vector <labelTp> &_labelSeq )
:
pointSeq(_pointSeq), maskSeq(_maskSeq), linkIdx(_linkIdx),
distances(pointSeq[0].size()), labelSeq(_labelSeq), parallelExpansion(this)
{
size_t lsize = pointSeq[0].size();
labelings.assign( pointSeq.size(),
std::vector <labelTp>( lsize ) );
}
}
template <typename Tp> static inline
void photomontage( const std::vector <std::vector <Tp> > &pointSeq,
const std::vector <std::vector <uint8_t> > &maskSeq,
const std::vector <std::vector <int> > &linkIdx,
std::vector <gcoptimization::labelTp> &labelSeq )
{
gcoptimization::Photomontage <Tp>(pointSeq, maskSeq,
linkIdx, labelSeq).gradientDescent();
}
#endif /* __PHOTOMONTAGE_HPP__ */
+193
View File
@@ -0,0 +1,193 @@
/*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-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// * 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 <algorithm>
#include <iostream>
#include <vector>
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/xphoto.hpp"
namespace cv
{
namespace xphoto
{
template <typename T>
void balanceWhiteSimple(std::vector<Mat_<T> > &src, Mat &dst, const float inputMin, const float inputMax,
const float outputMin, const float outputMax, const float p)
{
/********************* Simple white balance *********************/
const float s1 = p; // low quantile
const float s2 = p; // high quantile
int nElements = src[0].depth() == CV_8U ? 256 : 4096;
float minValue0 = inputMin;
float maxValue0 = inputMax;
// deal with cv::calcHist (exclusive upper bound)
if (src[0].depth() == CV_32F || src[0].depth() == CV_64F) // floating
{
maxValue0 += MIN((inputMax - inputMin) / (nElements - 1), 1);
if (inputMax == inputMin) // single value
maxValue0 += 1;
}
else // integer
{
maxValue0 += 1;
}
float interval = (maxValue0 - minValue0) / float(nElements);
for (size_t i = 0; i < src.size(); ++i)
{
float minValue = minValue0;
float maxValue = maxValue0;
Mat img = src[i].reshape(1);
Mat hist;
int channels[] = {0};
int histSize[] = {nElements};
float inputRange[] = {minValue, maxValue};
const float *ranges[] = {inputRange};
calcHist(&img, 1, channels, Mat(), hist, 1, histSize, ranges, true, false);
int total = int(src[i].total());
int p1 = 0, p2 = nElements - 1;
int n1 = 0, n2 = total;
// searching for s1 and s2
while (n1 + hist.at<float>(p1) < s1 * total / 100.0f)
{
n1 += saturate_cast<int>(hist.at<float>(p1++));
minValue += interval;
}
while (n2 - hist.at<float>(p2) > (100.0f - s2) * total / 100.0f)
{
n2 -= saturate_cast<int>(hist.at<float>(p2--));
maxValue -= interval;
}
src[i] = (outputMax - outputMin) * (src[i] - minValue) / (maxValue - minValue) + outputMin;
}
/****************************************************************/
dst.create(/**/ src[0].size(), CV_MAKETYPE(src[0].depth(), int(src.size())) /**/);
cv::merge(src, dst);
}
class SimpleWBImpl CV_FINAL : public SimpleWB
{
private:
float inputMin, inputMax, outputMin, outputMax, p;
public:
SimpleWBImpl()
{
inputMin = 0.0f;
inputMax = 255.0f;
outputMin = 0.0f;
outputMax = 255.0f;
p = 2.0f;
}
float getInputMin() const CV_OVERRIDE { return inputMin; }
void setInputMin(float val) CV_OVERRIDE { inputMin = val; }
float getInputMax() const CV_OVERRIDE { return inputMax; }
void setInputMax(float val) CV_OVERRIDE { inputMax = val; }
float getOutputMin() const CV_OVERRIDE { return outputMin; }
void setOutputMin(float val) CV_OVERRIDE { outputMin = val; }
float getOutputMax() const CV_OVERRIDE { return outputMax; }
void setOutputMax(float val) CV_OVERRIDE { outputMax = val; }
float getP() const CV_OVERRIDE { return p; }
void setP(float val) CV_OVERRIDE { p = val; }
void balanceWhite(InputArray _src, OutputArray _dst) CV_OVERRIDE
{
CV_Assert(!_src.empty());
CV_Assert(_src.depth() == CV_8U || _src.depth() == CV_16S || _src.depth() == CV_32S || _src.depth() == CV_32F);
Mat src = _src.getMat();
Mat &dst = _dst.getMatRef();
switch (src.depth())
{
case CV_8U:
{
std::vector<Mat_<uchar> > mv;
split(src, mv);
balanceWhiteSimple(mv, dst, inputMin, inputMax, outputMin, outputMax, p);
break;
}
case CV_16S:
{
std::vector<Mat_<short> > mv;
split(src, mv);
balanceWhiteSimple(mv, dst, inputMin, inputMax, outputMin, outputMax, p);
break;
}
case CV_32S:
{
std::vector<Mat_<int> > mv;
split(src, mv);
balanceWhiteSimple(mv, dst, inputMin, inputMax, outputMin, outputMax, p);
break;
}
case CV_32F:
{
std::vector<Mat_<float> > mv;
split(src, mv);
balanceWhiteSimple(mv, dst, inputMin, inputMax, outputMin, outputMax, p);
break;
}
}
}
};
Ptr<SimpleWB> createSimpleWB() { return makePtr<SimpleWBImpl>(); }
}
}
+129
View File
@@ -0,0 +1,129 @@
// 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 <opencv2/core.hpp>
#include <opencv2/core/utils/trace.hpp>
#include "opencv2/imgproc.hpp"
#include "opencv2/xphoto.hpp"
namespace cv { namespace xphoto {
#ifdef OPENCV_ENABLE_NONFREE
static inline
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);
}
static inline
void log_(const Mat& src, Mat& dst)
{
max(src, Scalar::all(1e-4), dst);
log(dst, dst);
}
class TonemapDurandImpl CV_FINAL : public TonemapDurand
{
public:
TonemapDurandImpl(float _gamma, float _contrast, float _saturation, float _sigma_color, float _sigma_space) :
name("TonemapDurand"),
gamma(_gamma),
contrast(_contrast),
saturation(_saturation),
sigma_color(_sigma_color),
sigma_space(_sigma_space)
{
}
void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
{
CV_TRACE_FUNCTION();
Mat src = _src.getMat();
CV_Assert(!src.empty());
_dst.create(src.size(), CV_32FC3);
Mat img = _dst.getMat();
Ptr<Tonemap> linear = createTonemap(1.0f);
linear->process(src, img);
Mat gray_img;
cvtColor(img, gray_img, COLOR_RGB2GRAY);
Mat log_img;
log_(gray_img, log_img);
Mat map_img;
bilateralFilter(log_img, map_img, -1, sigma_color, sigma_space);
double min, max;
minMaxLoc(map_img, &min, &max);
float scale = contrast / static_cast<float>(max - min);
exp(map_img * (scale - 1.0f) + log_img, map_img);
log_img.release();
mapLuminance(img, img, gray_img, map_img, saturation);
pow(img, 1.0f / gamma, img);
}
float getGamma() const 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 getContrast() const CV_OVERRIDE { return contrast; }
void setContrast(float val) CV_OVERRIDE { contrast = val; }
float getSigmaColor() const CV_OVERRIDE { return sigma_color; }
void setSigmaColor(float val) CV_OVERRIDE { sigma_color = val; }
float getSigmaSpace() const CV_OVERRIDE { return sigma_space; }
void setSigmaSpace(float val) CV_OVERRIDE { sigma_space = val; }
void write(FileStorage& fs) const CV_OVERRIDE
{
writeFormat(fs);
fs << "name" << name
<< "gamma" << gamma
<< "contrast" << contrast
<< "sigma_color" << sigma_color
<< "sigma_space" << sigma_space
<< "saturation" << saturation;
}
void read(const FileNode& fn) CV_OVERRIDE
{
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name);
gamma = fn["gamma"];
contrast = fn["contrast"];
sigma_color = fn["sigma_color"];
sigma_space = fn["sigma_space"];
saturation = fn["saturation"];
}
protected:
String name;
float gamma, contrast, saturation, sigma_color, sigma_space;
};
Ptr<TonemapDurand> createTonemapDurand(float gamma, float contrast, float saturation, float sigma_color, float sigma_space)
{
return makePtr<TonemapDurandImpl>(gamma, contrast, saturation, sigma_color, sigma_space);
}
#else
Ptr<TonemapDurand> createTonemapDurand(float /*gamma*/, float /*contrast*/, float /*saturation*/, float /*sigma_color*/, float /*sigma_space*/)
{
CV_Error(Error::StsNotImplemented,
"This algorithm is patented and is excluded in this configuration; "
"Set OPENCV_ENABLE_NONFREE CMake option and rebuild the library");
}
#endif // OPENCV_ENABLE_NONFREE
}} // namespace
+148
View File
@@ -0,0 +1,148 @@
/*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-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// * 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 __WHS_HPP__
#define __WHS_HPP__
static inline int hl(int x)
{
int res = 0;
while (x)
{
res += x&1;
x >>= 1;
}
return res;
}
static inline int rp2(int x)
{
int res = 1;
while (res < x)
res <<= 1;
return res;
}
template <typename ForwardIterator>
static void generate_snake(ForwardIterator snake, const int n)
{
cv::Point previous;
if (n > 0)
{
previous = cv::Point(0, 0);
*snake = previous;
}
for (int k = 1, num = 1; num <= n; ++k)
{
const cv::Point2i dv[] = { cv::Point2i( !(k&1), (k&1) ),
cv::Point2i( -(k&1), -!(k&1) ) };
*snake = previous = previous - dv[1];
++num;
for (int i = 0; i < 2; ++i)
for (int j = 0; j < k && num < n; ++j)
{
*snake = previous = previous + dv[i];
++num;
}
}
}
static void nextProjection(std::vector <cv::Mat> &projections, const cv::Point &A,
const cv::Point &B, const int psize)
{
int xsign = (A.x != B.x)*(hl(A.x&B.x) + (B.x > A.x))&1;
int ysign = (A.y != B.y)*(hl(A.y&B.y) + (B.y > A.y))&1;
bool plusToMinusUpdate = xsign || ysign;
int dx = (A.x != B.x) << ( hl(psize - 1) - hl(A.x ^ B.x) );
int dy = (A.y != B.y) << ( hl(psize - 1) - hl(A.y ^ B.y) );
cv::Mat proj = projections[projections.size() - 1],
nproj = -proj.clone();
for (int i = dy; i < nproj.rows; ++i)
{
float *vxNext = nproj.ptr<float>(i - dy);
float *vNext = nproj.ptr<float>(i);
float *vxCurrent = proj.ptr<float>(i - dy);
if (plusToMinusUpdate)
for (int j = dx; j < nproj.cols; ++j)
vNext[j] += vxCurrent[j - dx] - vxNext[j - dx];
else
for (int j = dx; j < nproj.cols; ++j)
vNext[j] -= vxCurrent[j - dx] - vxNext[j - dx];
}
projections.push_back(nproj);
}
static void rgb2whs(const cv::Mat &src, cv::Mat &dst, const int nProjections, const int psize)
{
CV_Assert(nProjections <= psize*psize && src.type() == CV_32FC1);
const int npsize = rp2(psize);
std::vector <cv::Mat> projections;
cv::Mat img, proj;
cv::copyMakeBorder(src, img, npsize, npsize, npsize, npsize,
cv::BORDER_CONSTANT, 0);
cv::boxFilter(img, proj, CV_32F, cv::Size(npsize, npsize),
cv::Point(-1, -1), true, cv::BORDER_REFLECT);
projections.push_back(proj);
std::vector <cv::Point2i> snake_idx;
generate_snake(std::back_inserter(snake_idx), nProjections);
for (int i = 1; i < nProjections; ++i)
nextProjection(projections, snake_idx[i - 1],
snake_idx[i], npsize);
int pad = 0;
cv::merge(projections, img);
img(cv::Rect(npsize + pad, npsize + pad, src.cols - pad,
src.rows - pad)).copyTo(dst);
}
#endif /* __WHS_HPP__ */
@@ -0,0 +1,42 @@
// 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 {
TEST(xphoto_dctimagedenoising, regression)
{
cv::String subfolder = "cv/xphoto/";
cv::String dir = cvtest::TS::ptr()->get_data_path() + subfolder + "dct_image_denoising/";
int nTests = 1;
double thresholds[] = {0.2};
int psize[] = {8};
double sigma[] = {9.0};
for (int i = 0; i < nTests; ++i)
{
cv::String srcName = dir + cv::format( "sources/%02d.png", i + 1);
cv::Mat src = cv::imread( srcName, 1 );
ASSERT_TRUE(!src.empty());
cv::String previousResultName = dir + cv::format( "results/%02d.png", i + 1 );
cv::Mat previousResult = cv::imread( previousResultName, 1 );
ASSERT_TRUE(!src.empty());
cv::Mat currentResult;
cv::xphoto::dctDenoising(src, currentResult, sigma[i], psize[i]);
cv::Mat sqrError = ( currentResult - previousResult )
.mul( currentResult - previousResult );
cv::Scalar mse = cv::sum(sqrError) / cv::Scalar::all( double(sqrError.total()*sqrError.channels()) );
EXPECT_LE( mse[0] + mse[1] + mse[2] + mse[3], thresholds[i] );
}
}
}} // namespace
@@ -0,0 +1,273 @@
// 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 {
TEST(xphoto_simplecolorbalance, uchar_max_value)
{
const uchar oldMax = 120, newMax = 255;
Mat test = Mat::zeros(3,3,CV_8UC1);
test.at<uchar>(0, 0) = oldMax;
test.at<uchar>(0, 1) = oldMax / 2;
test.at<uchar>(0, 2) = oldMax / 4;
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(0);
wb->setInputMax(oldMax);
wb->setOutputMin(0);
wb->setOutputMax(newMax);
wb->balanceWhite(test, test);
double minDst, maxDst;
cv::minMaxIdx(test, &minDst, &maxDst);
ASSERT_NEAR(maxDst, newMax, 1e-4);
}
TEST(xphoto_simplecolorbalance, uchar_min_value)
{
const uchar oldMin = 120, newMin = 0;
Mat test = Mat::zeros(1,3,CV_8UC1);
test.at<uchar>(0, 0) = oldMin;
test.at<uchar>(0, 1) = (256 + oldMin) / 2;
test.at<uchar>(0, 2) = 255;
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(oldMin);
wb->setInputMax(255);
wb->setOutputMin(newMin);
wb->setOutputMax(255);
wb->balanceWhite(test, test);
double minDst, maxDst;
cv::minMaxIdx(test, &minDst, &maxDst);
ASSERT_NEAR(minDst, newMin, 1e-4);
}
TEST(xphoto_simplecolorbalance, uchar_equal_range)
{
const int N = 4;
uchar data[N] = {0, 1, 16, 255};
Mat test = Mat(1, N, CV_8UC1, data);
Mat result = Mat(1, N, CV_8UC1, data);
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(0);
wb->setInputMax(255);
wb->setOutputMin(0);
wb->setOutputMax(255);
wb->balanceWhite(test, test);
double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 1e-4);
}
TEST(xphoto_simplecolorbalance, uchar_single_value)
{
const int N = 4;
uchar data0[N] = {51, 51, 51, 51};
uchar data1[N] = {33, 33, 33, 33};
Mat test = Mat(1, N, CV_8UC1, data0);
Mat result = Mat(1, N, CV_8UC1, data1);
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(51);
wb->setInputMax(51);
wb->setOutputMin(33);
wb->setOutputMax(200);
wb->balanceWhite(test, test);
double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 1e-4);
}
TEST(xphoto_simplecolorbalance, uchar_p)
{
const int N = 5;
uchar data0[N] = {10, 55, 102, 188, 233};
uchar data1[N] = {0, 1, 90, 254, 255};
Mat test = Mat(1, N, CV_8UC1, data0);
Mat result = Mat(1, N, CV_8UC1, data1);
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(10);
wb->setInputMax(233);
wb->setOutputMin(0);
wb->setOutputMax(255);
wb->setP(21);
wb->balanceWhite(test, test);
double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 1e-4);
}
TEST(xphoto_simplecolorbalance, uchar_c3)
{
const int N = 15;
uchar data0[N] = {10, 55, 102, 55, 102, 188, 102, 188, 233, 188, 233, 10, 233, 10, 55};
uchar data1[N] = {0, 1, 90, 1, 90, 254, 90, 254, 255, 254, 255, 0, 255, 0, 1};
Mat test = Mat(1, N / 3, CV_8UC3, data0);
Mat result = Mat(1, N / 3, CV_8UC3, data1);
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(10);
wb->setInputMax(233);
wb->setOutputMin(0);
wb->setOutputMax(255);
wb->setP(21);
wb->balanceWhite(test, test);
double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 1e-4);
}
TEST(xphoto_simplecolorbalance, float_max_value)
{
const float oldMax = 24000.f, newMax = 65536.f;
Mat test = Mat::zeros(3,3,CV_32FC1);
test.at<float>(0, 0) = oldMax;
test.at<float>(0, 1) = oldMax / 2;
test.at<float>(0, 2) = oldMax / 4;
double minSrc, maxSrc;
cv::minMaxIdx(test, &minSrc, &maxSrc);
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin((float)minSrc);
wb->setInputMax((float)maxSrc);
wb->setOutputMin(0);
wb->setOutputMax(newMax);
wb->balanceWhite(test, test);
double minDst, maxDst;
cv::minMaxIdx(test, &minDst, &maxDst);
ASSERT_NEAR(maxDst, newMax, newMax*1e-4);
}
TEST(xphoto_simplecolorbalance, float_min_value)
{
const float oldMin = 24000.f, newMin = 0.f;
Mat test = Mat::zeros(1,3,CV_32FC1);
test.at<float>(0, 0) = oldMin;
test.at<float>(0, 1) = (65536.f + oldMin) / 2;
test.at<float>(0, 2) = 65536.f;
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(oldMin);
wb->setInputMax(65536.f);
wb->setOutputMin(newMin);
wb->setOutputMax(65536.f);
wb->balanceWhite(test, test);
double minDst, maxDst;
cv::minMaxIdx(test, &minDst, &maxDst);
ASSERT_NEAR(minDst, newMin, 65536*1e-4);
}
TEST(xphoto_simplecolorbalance, float_equal_range)
{
const int N = 5;
float data[N] = {0.f, 1.f, 16.2f, 256.3f, 4096.f};
Mat test = Mat(1, N, CV_32FC1, data);
Mat result = Mat(1, N, CV_32FC1, data);
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(0);
wb->setInputMax(4096);
wb->setOutputMin(0);
wb->setOutputMax(4096);
wb->balanceWhite(test, test);
double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 1e-4);
}
TEST(xphoto_simplecolorbalance, float_single_value)
{
const int N = 4;
float data0[N] = {24000.5f, 24000.5f, 24000.5f, 24000.5f};
float data1[N] = {52000.25f, 52000.25f, 52000.25f, 52000.25f};
Mat test = Mat(1, N, CV_32FC1, data0);
Mat result = Mat(1, N, CV_32FC1, data1);
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(24000.5f);
wb->setInputMax(24000.5f);
wb->setOutputMin(52000.25f);
wb->setOutputMax(65536.f);
wb->balanceWhite(test, test);
double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 65536*1e-4);
}
TEST(xphoto_simplecolorbalance, float_p)
{
const int N = 5;
float data0[N] = {16000.f, 20000.5f, 24000.f, 36000.5f, 48000.f};
float data1[N] = {-16381.952f, 0.f, 16381.952f, 65536.f, 114685.952f};
Mat test = Mat(1, N, CV_32FC1, data0);
Mat result = Mat(1, N, CV_32FC1, data1);
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(16000.f);
wb->setInputMax(48000.f);
wb->setOutputMin(0.f);
wb->setOutputMax(65536.f);
wb->setP(21);
wb->balanceWhite(test, test);
double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 65536*1e-4);
}
TEST(xphoto_simplecolorbalance, float_c3)
{
const int N = 15;
float data0[N] = {16000.f, 20000.5f, 24000.f, 20000.5f, 24000.f, 36000.5f, 24000.f, 36000.5f, 48000.f, 36000.5f, 48000.f, 16000.f, 48000.f, 16000.f, 20000.5f};
float data1[N] = {-16381.952f, 0.f, 16381.952f, 0.f, 16381.952f, 65536.f, 16381.952f, 65536.f, 114685.952f, 65536.f, 114685.952f, -16381.952f, 114685.952f, -16381.952f, 0.f};
Mat test = Mat(1, N / 3, CV_32FC3, data0);
Mat result = Mat(1, N / 3, CV_32FC3, data1);
cv::Ptr<cv::xphoto::SimpleWB> wb = cv::xphoto::createSimpleWB();
wb->setInputMin(16000.f);
wb->setInputMax(48000.f);
wb->setOutputMin(0.f);
wb->setOutputMax(65536.f);
wb->setP(21);
wb->balanceWhite(test, test);
double err;
cv::minMaxIdx(cv::abs(test - result), NULL, &err);
ASSERT_LE(err, 65536*1e-4);
}
}} // namespace
+464
View File
@@ -0,0 +1,464 @@
/*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"
//#define DUMP_RESULTS
//#define TEST_TRANSFORMS
#ifdef TEST_TRANSFORMS
#include "..\..\xphoto\src\bm3d_denoising_invoker_commons.hpp"
#include "..\..\xphoto\src\bm3d_denoising_transforms.hpp"
#include "..\..\xphoto\src\kaiser_window.hpp"
using namespace cv::xphoto;
#endif
#ifdef DUMP_RESULTS
# define DUMP(image, path) imwrite(path, image)
#else
# define DUMP(image, path)
#endif
#ifdef OPENCV_ENABLE_NONFREE
namespace opencv_test { namespace {
TEST(xphoto_DenoisingBm3dGrayscale, regression_L2)
{
std::string folder = std::string(cvtest::TS::ptr()->get_data_path()) + "cv/xphoto/bm3d_image_denoising/";
std::string original_path = folder + "lena_noised_gaussian_sigma=10.png";
std::string expected_path = folder + "lena_noised_denoised_bm3d_wiener_grayscale_l2_tw=4_sw=16_h=10_bm=400.png";
cv::Mat original = cv::imread(original_path, cv::IMREAD_GRAYSCALE);
cv::Mat expected = cv::imread(expected_path, cv::IMREAD_GRAYSCALE);
ASSERT_FALSE(original.empty()) << "Could not load input image " << original_path;
ASSERT_FALSE(expected.empty()) << "Could not load reference image " << expected_path;
// BM3D: two different calls doing exactly the same thing
cv::Mat result, resultSec;
cv::xphoto::bm3dDenoising(original, noArray(), resultSec, 10, 4, 16, 2500, 400, 8, 1, 0.0f, cv::NORM_L2, cv::xphoto::BM3D_STEPALL);
cv::xphoto::bm3dDenoising(original, result, 10, 4, 16, 2500, 400, 8, 1, 0.0f, cv::NORM_L2, cv::xphoto::BM3D_STEPALL);
DUMP(result, expected_path + ".res.png");
ASSERT_EQ(cvtest::norm(result, resultSec, cv::NORM_L2), 0);
ASSERT_LT(cvtest::norm(result, expected, cv::NORM_L2), 200);
}
TEST(xphoto_DenoisingBm3dGrayscale, regression_L2_separate)
{
std::string folder = std::string(cvtest::TS::ptr()->get_data_path()) + "cv/xphoto/bm3d_image_denoising/";
std::string original_path = folder + "lena_noised_gaussian_sigma=10.png";
std::string expected_basic_path = folder + "lena_noised_denoised_bm3d_grayscale_l2_tw=4_sw=16_h=10_bm=2500.png";
std::string expected_path = folder + "lena_noised_denoised_bm3d_wiener_grayscale_l2_tw=4_sw=16_h=10_bm=400.png";
cv::Mat original = cv::imread(original_path, cv::IMREAD_GRAYSCALE);
cv::Mat expected_basic = cv::imread(expected_basic_path, cv::IMREAD_GRAYSCALE);
cv::Mat expected = cv::imread(expected_path, cv::IMREAD_GRAYSCALE);
ASSERT_FALSE(original.empty()) << "Could not load input image " << original_path;
ASSERT_FALSE(expected_basic.empty()) << "Could not load reference image " << expected_basic_path;
ASSERT_FALSE(expected.empty()) << "Could not load input image " << expected_path;
cv::Mat basic, result;
// BM3D step 1
cv::xphoto::bm3dDenoising(original, basic, 10, 4, 16, 2500, -1, 8, 1, 0.0f, cv::NORM_L2, cv::xphoto::BM3D_STEP1);
ASSERT_LT(cvtest::norm(basic, expected_basic, cv::NORM_L2), 200);
DUMP(basic, expected_basic_path + ".res.basic.png");
// BM3D step 2
cv::xphoto::bm3dDenoising(original, basic, result, 10, 4, 16, 2500, 400, 8, 1, 0.0f, cv::NORM_L2, cv::xphoto::BM3D_STEP2);
ASSERT_LT(cvtest::norm(basic, expected_basic, cv::NORM_L2), 200);
DUMP(basic, expected_basic_path + ".res.basic2.png");
DUMP(result, expected_path + ".res.png");
ASSERT_LT(cvtest::norm(result, expected, cv::NORM_L2), 200);
}
TEST(xphoto_DenoisingBm3dGrayscale, regression_L1)
{
std::string folder = std::string(cvtest::TS::ptr()->get_data_path()) + "cv/xphoto/bm3d_image_denoising/";
std::string original_path = folder + "lena_noised_gaussian_sigma=10.png";
std::string expected_path = folder + "lena_noised_denoised_bm3d_grayscale_l1_tw=4_sw=16_h=10_bm=2500.png";
cv::Mat original = cv::imread(original_path, cv::IMREAD_GRAYSCALE);
cv::Mat expected = cv::imread(expected_path, cv::IMREAD_GRAYSCALE);
ASSERT_FALSE(original.empty()) << "Could not load input image " << original_path;
ASSERT_FALSE(expected.empty()) << "Could not load reference image " << expected_path;
cv::Mat result;
cv::xphoto::bm3dDenoising(original, result, 10, 4, 16, 2500, -1, 8, 1, 0.0f, cv::NORM_L1, cv::xphoto::BM3D_STEP1);
DUMP(result, expected_path + ".res.png");
ASSERT_LT(cvtest::norm(result, expected, cv::NORM_L2), 200);
}
TEST(xphoto_DenoisingBm3dGrayscale, regression_L2_8x8)
{
std::string folder = std::string(cvtest::TS::ptr()->get_data_path()) + "cv/xphoto/bm3d_image_denoising/";
std::string original_path = folder + "lena_noised_gaussian_sigma=10.png";
std::string expected_path = folder + "lena_noised_denoised_bm3d_grayscale_l2_tw=8_sw=16_h=10_bm=2500.png";
cv::Mat original = cv::imread(original_path, cv::IMREAD_GRAYSCALE);
cv::Mat expected = cv::imread(expected_path, cv::IMREAD_GRAYSCALE);
ASSERT_FALSE(original.empty()) << "Could not load input image " << original_path;
ASSERT_FALSE(expected.empty()) << "Could not load reference image " << expected_path;
cv::Mat result;
cv::xphoto::bm3dDenoising(original, result, 10, 8, 16, 2500, -1, 8, 1, 0.0f, cv::NORM_L2, cv::xphoto::BM3D_STEP1);
DUMP(result, expected_path + ".res.png");
ASSERT_LT(cvtest::norm(result, expected, cv::NORM_L2), 200);
}
#ifdef TEST_TRANSFORMS
TEST(xphoto_DenoisingBm3dKaiserWindow, regression_4)
{
float beta = 2.0f;
int N = 4;
cv::Mat kaiserWindow;
calcKaiserWindow1D(kaiserWindow, N, beta);
float kaiser4[] = {
0.43869004f,
0.92432547f,
0.92432547f,
0.43869004f
};
for (int i = 0; i < N; ++i)
ASSERT_FLOAT_EQ(kaiser4[i], kaiserWindow.at<float>(i));
}
TEST(xphoto_DenoisingBm3dKaiserWindow, regression_8)
{
float beta = 2.0f;
int N = 8;
cv::Mat kaiserWindow;
calcKaiserWindow1D(kaiserWindow, N, beta);
float kaiser8[] = {
0.43869004f,
0.68134475f,
0.87685609f,
0.98582518f,
0.98582518f,
0.87685609f,
0.68134463f,
0.43869004f
};
for (int i = 0; i < N; ++i)
ASSERT_FLOAT_EQ(kaiser8[i], kaiserWindow.at<float>(i));
}
TEST(xphoto_DenoisingBm3dTransforms, regression_2D_generic)
{
const int templateWindowSize = 8;
const int templateWindowSizeSq = templateWindowSize * templateWindowSize;
uchar src[templateWindowSizeSq];
short dst[templateWindowSizeSq];
short dstSec[templateWindowSizeSq];
// Initialize array
for (uchar i = 0; i < templateWindowSizeSq; ++i)
src[i] = (i % 10) * 10;
// Use tailored transforms
HaarTransform<uchar, short>::RegisterTransforms2D(templateWindowSize);
HaarTransform<uchar, short>::forwardTransform2D(src, dst, templateWindowSize, templateWindowSize);
HaarTransform<uchar, short>::inverseTransform2D(dst, templateWindowSize);
// Use generic transforms
HaarTransform2D::ForwardTransformXxX<uchar, short, templateWindowSize>(src, dstSec, templateWindowSize, templateWindowSize);
HaarTransform2D::InverseTransformXxX<short, templateWindowSize>(dstSec, templateWindowSize);
for (unsigned i = 0; i < templateWindowSizeSq; ++i)
ASSERT_EQ(dst[i], dstSec[i]);
}
TEST(xphoto_DenoisingBm3dTransforms, regression_2D_4x4)
{
const int templateWindowSize = 4;
const int templateWindowSizeSq = templateWindowSize * templateWindowSize;
uchar src[templateWindowSizeSq];
short dst[templateWindowSizeSq];
// Initialize array
for (uchar i = 0; i < templateWindowSizeSq; ++i)
{
src[i] = i;
}
HaarTransform2D::ForwardTransform4x4(src, dst, templateWindowSize, templateWindowSize);
HaarTransform2D::InverseTransform4x4(dst, templateWindowSize);
for (uchar i = 0; i < templateWindowSizeSq; ++i)
ASSERT_EQ(static_cast<short>(src[i]), dst[i]);
}
TEST(xphoto_DenoisingBm3dTransforms, regression_2D_8x8)
{
const int templateWindowSize = 8;
const int templateWindowSizeSq = templateWindowSize * templateWindowSize;
uchar src[templateWindowSizeSq];
short dst[templateWindowSizeSq];
// Initialize array
for (uchar i = 0; i < templateWindowSizeSq; ++i)
{
src[i] = i;
}
HaarTransform2D::ForwardTransform8x8(src, dst, templateWindowSize, templateWindowSize);
HaarTransform2D::InverseTransform8x8(dst, templateWindowSize);
for (uchar i = 0; i < templateWindowSizeSq; ++i)
ASSERT_EQ(static_cast<short>(src[i]), dst[i]);
}
template <typename T, typename DT, typename CT>
static void Test1dTransform(
T *thrMap,
int groupSize,
int templateWindowSizeSq,
BlockMatch<T, DT, CT> *bm,
BlockMatch<T, DT, CT> *bmOrig,
int expectedNonZeroCount = -1)
{
if (expectedNonZeroCount < 0)
expectedNonZeroCount = groupSize * templateWindowSizeSq;
// Test group size
short sumNonZero = 0;
T *thrMapPtr1D = thrMap + (groupSize - 1) * templateWindowSizeSq;
for (int n = 0; n < templateWindowSizeSq; n++)
{
switch (groupSize)
{
case 16:
HaarTransform1D::ForwardTransform16(bm, n);
sumNonZero += HardThreshold<16>(bm, n, thrMapPtr1D);
HaarTransform1D::InverseTransform16(bm, n);
break;
case 8:
HaarTransform1D::ForwardTransform8(bm, n);
sumNonZero += HardThreshold<8>(bm, n, thrMapPtr1D);
HaarTransform1D::InverseTransform8(bm, n);
break;
case 4:
HaarTransform1D::ForwardTransform4(bm, n);
sumNonZero += HardThreshold<4>(bm, n, thrMapPtr1D);
HaarTransform1D::InverseTransform4(bm, n);
break;
case 2:
HaarTransform1D::ForwardTransform2(bm, n);
sumNonZero += HardThreshold<2>(bm, n, thrMapPtr1D);
HaarTransform1D::InverseTransform2(bm, n);
break;
default:
HaarTransform1D::ForwardTransformN(bm, n, groupSize);
sumNonZero += HardThreshold(bm, n, thrMapPtr1D, groupSize);
HaarTransform1D::InverseTransformN(bm, n, groupSize);
}
}
// Assert transform
if (expectedNonZeroCount == groupSize * templateWindowSizeSq)
{
for (int i = 0; i < groupSize; ++i)
for (int j = 0; j < templateWindowSizeSq; ++j)
ASSERT_EQ(bm[i][j], bmOrig[i][j]);
}
// Assert shrinkage
ASSERT_EQ(sumNonZero, expectedNonZeroCount);
}
TEST(xphoto_DenoisingBm3dTransforms, regression_1D_transform)
{
const int templateWindowSize = 4;
const int templateWindowSizeSq = templateWindowSize * templateWindowSize;
const int searchWindowSize = 16;
const int searchWindowSizeSq = searchWindowSize * searchWindowSize;
const float h = 10;
int maxGroupSize = 64;
// Precompute separate maps for transform and shrinkage verification
short *thrMapTransform = NULL;
short *thrMapShrinkage = NULL;
HaarTransform<short, short>::calcThresholdMap3D(thrMapTransform, 0, templateWindowSize, maxGroupSize);
HaarTransform<short, short>::calcThresholdMap3D(thrMapShrinkage, h, templateWindowSize, maxGroupSize);
// Generate some data
BlockMatch<short, int, short> *bm = new BlockMatch<short, int, short>[maxGroupSize];
BlockMatch<short, int, short> *bmOrig = new BlockMatch<short, int, short>[maxGroupSize];
for (int i = 0; i < maxGroupSize; ++i)
{
bm[i].init(templateWindowSizeSq);
bmOrig[i].init(templateWindowSizeSq);
}
for (short i = 0; i < maxGroupSize; ++i)
{
for (short j = 0; j < templateWindowSizeSq; ++j)
{
bm[i][j] = (j + 1);
bmOrig[i][j] = bm[i][j];
}
}
// Verify transforms
Test1dTransform<short, int, short>(thrMapTransform, 2, templateWindowSizeSq, bm, bmOrig);
Test1dTransform<short, int, short>(thrMapTransform, 4, templateWindowSizeSq, bm, bmOrig);
Test1dTransform<short, int, short>(thrMapTransform, 8, templateWindowSizeSq, bm, bmOrig);
Test1dTransform<short, int, short>(thrMapTransform, 16, templateWindowSizeSq, bm, bmOrig);
Test1dTransform<short, int, short>(thrMapTransform, 32, templateWindowSizeSq, bm, bmOrig);
Test1dTransform<short, int, short>(thrMapTransform, 64, templateWindowSizeSq, bm, bmOrig);
// Verify shrinkage
Test1dTransform<short, int, short>(thrMapShrinkage, 2, templateWindowSizeSq, bm, bmOrig, 6);
Test1dTransform<short, int, short>(thrMapShrinkage, 4, templateWindowSizeSq, bm, bmOrig, 6);
Test1dTransform<short, int, short>(thrMapShrinkage, 8, templateWindowSizeSq, bm, bmOrig, 6);
Test1dTransform<short, int, short>(thrMapShrinkage, 16, templateWindowSizeSq, bm, bmOrig, 6);
Test1dTransform<short, int, short>(thrMapShrinkage, 32, templateWindowSizeSq, bm, bmOrig, 6);
Test1dTransform<short, int, short>(thrMapShrinkage, 64, templateWindowSizeSq, bm, bmOrig, 14);
}
const float sqrt2 = std::sqrt(2.0f);
TEST(xphoto_DenoisingBm3dTransforms, regression_1D_generate)
{
const int numberOfElements = 8;
const int arrSize = (numberOfElements << 1) - 1;
float *thrMap1D = NULL;
HaarTransform<short, short>::calcThresholdMap1D(thrMap1D, numberOfElements);
// Expected array
const float kThrMap1D[arrSize] = {
1.0f, // 1 element
sqrt2 / 2.0f, sqrt2, // 2 elements
0.5f, 1.0f, sqrt2, sqrt2, // 4 elements
sqrt2 / 4.0f, sqrt2 / 2.0f, 1.0f, 1.0f, sqrt2, sqrt2, sqrt2, sqrt2 // 8 elements
};
for (int j = 0; j < arrSize; ++j)
ASSERT_EQ(thrMap1D[j], kThrMap1D[j]);
delete[] thrMap1D;
}
TEST(xphoto_DenoisingBm3dTransforms, regression_2D_generate_4x4)
{
const int templateWindowSize = 4;
float *thrMap2D = NULL;
HaarTransform<short, short>::calcThresholdMap2D(thrMap2D, templateWindowSize);
// Expected array
const float kThrMap4x4[templateWindowSize * templateWindowSize] = {
0.25f, 0.5f, sqrt2 / 2.0f, sqrt2 / 2.0f,
0.5f, 1.0f, sqrt2, sqrt2,
sqrt2 / 2.0f, sqrt2, 2.0f, 2.0f,
sqrt2 / 2.0f, sqrt2, 2.0f, 2.0f
};
for (int j = 0; j < templateWindowSize * templateWindowSize; ++j)
ASSERT_EQ(thrMap2D[j], kThrMap4x4[j]);
delete[] thrMap2D;
}
TEST(xphoto_DenoisingBm3dTransforms, regression_2D_generate_8x8)
{
const int templateWindowSize = 8;
float *thrMap2D = NULL;
HaarTransform<short, short>::calcThresholdMap2D(thrMap2D, templateWindowSize);
// Expected array
const float kThrMap8x8[templateWindowSize * templateWindowSize] = {
0.125f, 0.25f, sqrt2 / 4.0f, sqrt2 / 4.0f, 0.5f, 0.5f, 0.5f, 0.5f,
0.25f, 0.5f, sqrt2 / 2.0f, sqrt2 / 2.0f, 1.0f, 1.0f, 1.0f, 1.0f,
sqrt2 / 4.0f, sqrt2 / 2.0f, 1.0f, 1.0f, sqrt2, sqrt2, sqrt2, sqrt2,
sqrt2 / 4.0f, sqrt2 / 2.0f, 1.0f, 1.0f, sqrt2, sqrt2, sqrt2, sqrt2,
0.5f, 1.0f, sqrt2, sqrt2, 2.0f, 2.0f, 2.0f, 2.0f,
0.5f, 1.0f, sqrt2, sqrt2, 2.0f, 2.0f, 2.0f, 2.0f,
0.5f, 1.0f, sqrt2, sqrt2, 2.0f, 2.0f, 2.0f, 2.0f,
0.5f, 1.0f, sqrt2, sqrt2, 2.0f, 2.0f, 2.0f, 2.0f
};
for (int j = 0; j < templateWindowSize * templateWindowSize; ++j)
ASSERT_EQ(thrMap2D[j], kThrMap8x8[j]);
delete[] thrMap2D;
}
TEST(xphoto_Bm3dDenoising, powerOf2)
{
ASSERT_EQ(8, getLargestPowerOf2SmallerThan(9));
ASSERT_EQ(16, getLargestPowerOf2SmallerThan(21));
ASSERT_EQ(4, getLargestPowerOf2SmallerThan(7));
ASSERT_EQ(8, getLargestPowerOf2SmallerThan(8));
ASSERT_EQ(4, getLargestPowerOf2SmallerThan(5));
ASSERT_EQ(4, getLargestPowerOf2SmallerThan(4));
ASSERT_EQ(2, getLargestPowerOf2SmallerThan(3));
ASSERT_EQ(1, getLargestPowerOf2SmallerThan(1));
ASSERT_EQ(0, getLargestPowerOf2SmallerThan(0));
}
#endif // TEST_TRANSFORMS
}} // namespace
#endif // OPENCV_ENABLE_NONFREE
+99
View File
@@ -0,0 +1,99 @@
// 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 {
void ref_autowbGrayworld(InputArray _src, OutputArray _dst, float thresh)
{
Mat src = _src.getMat();
_dst.create(src.size(), src.type());
Mat dst = _dst.getMat();
int width = src.cols,
height = src.rows,
N = width*height,
N3 = N*3;
// Calculate sum of pixel values of each channel
const uchar* src_data = src.ptr<uchar>(0);
unsigned long sum1 = 0, sum2 = 0, sum3 = 0;
int i = 0;
unsigned int minRGB, maxRGB, thresh255 = cvRound(thresh * 255);
for ( ; i < N3; i += 3 )
{
minRGB = std::min(src_data[i], std::min(src_data[i + 1], src_data[i + 2]));
maxRGB = std::max(src_data[i], std::max(src_data[i + 1], src_data[i + 2]));
if ( (maxRGB - minRGB) * 255 > thresh255 * maxRGB ) continue;
sum1 += src_data[i];
sum2 += src_data[i + 1];
sum3 += src_data[i + 2];
}
// Find inverse of averages
double inv1 = sum1 == 0 ? 0.f : (double)N / (double)sum1,
inv2 = sum2 == 0 ? 0.f : (double)N / (double)sum2,
inv3 = sum3 == 0 ? 0.f : (double)N / (double)sum3;
// Find maximum
double inv_max = std::max(std::max(inv1, inv2), inv3);
// Scale by maximum
if ( inv_max > 0 )
{
inv1 = (double) inv1 / inv_max;
inv2 = (double) inv2 / inv_max;
inv3 = (double) inv3 / inv_max;
}
// Fixed point arithmetic, mul by 2^8 then shift back 8 bits
int i_inv1 = cvRound(inv1 * (1 << 8)),
i_inv2 = cvRound(inv2 * (1 << 8)),
i_inv3 = cvRound(inv3 * (1 << 8));
// Scale input pixel values
uchar* dst_data = dst.ptr<uchar>(0);
i = 0;
for ( ; i < N3; i += 3 )
{
dst_data[i] = (uchar)((src_data[i] * i_inv1) >> 8);
dst_data[i + 1] = (uchar)((src_data[i + 1] * i_inv2) >> 8);
dst_data[i + 2] = (uchar)((src_data[i + 2] * i_inv3) >> 8);
}
}
TEST(xphoto_grayworld_white_balance, regression)
{
String dir = cvtest::TS::ptr()->get_data_path() + "cv/xphoto/simple_white_balance/";
const int nTests = 8;
const float wb_thresh = 0.5f;
const float acc_thresh = 2.f;
Ptr<xphoto::GrayworldWB> wb = xphoto::createGrayworldWB();
wb->setSaturationThreshold(wb_thresh);
for ( int i = 0; i < nTests; ++i )
{
String srcName = dir + format("sources/%02d.png", i + 1);
Mat src = imread(srcName, IMREAD_COLOR);
ASSERT_TRUE(!src.empty());
Mat referenceResult;
ref_autowbGrayworld(src, referenceResult, wb_thresh);
Mat currentResult;
wb->balanceWhite(src, currentResult);
ASSERT_LE(cv::norm(currentResult, referenceResult, NORM_INF), acc_thresh);
// test the 16-bit depth:
Mat currentResult_16U, src_16U;
src.convertTo(src_16U, CV_16UC3, 256.0);
wb->balanceWhite(src_16U, currentResult_16U);
currentResult_16U.convertTo(currentResult, CV_8UC3, 1/256.0);
ASSERT_LE(cv::norm(currentResult, referenceResult, NORM_INF), acc_thresh);
}
}
}} // namespace
+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.
#include "test_precomp.hpp"
namespace opencv_test { namespace {
using namespace cv::xphoto;
#ifdef OPENCV_ENABLE_NONFREE
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;
}
TEST(Photo_Tonemap, Durand_regression)
{
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "cv/hdr/tonemap/";
Mat img, expected, result;
loadImage(test_path + "image.hdr", img);
float gamma = 2.2f;
Ptr<TonemapDurand> durand = createTonemapDurand(gamma);
durand->process(img, result);
loadImage(test_path + "durand.png", expected);
result.convertTo(result, CV_8UC3, 255);
checkEqual(result, expected, 3, "Durand");
}
TEST(Photo_Tonemap, Durand_property_regression)
{
const float gamma = 1.0f;
const float contrast = 2.0f;
const float saturation = 3.0f;
const float sigma_color = 4.0f;
const float sigma_space = 5.0f;
const Ptr<TonemapDurand> durand1 = createTonemapDurand(gamma, contrast, saturation, sigma_color, sigma_space);
ASSERT_EQ(gamma, durand1->getGamma());
ASSERT_EQ(contrast, durand1->getContrast());
ASSERT_EQ(saturation, durand1->getSaturation());
ASSERT_EQ(sigma_space, durand1->getSigmaSpace());
ASSERT_EQ(sigma_color, durand1->getSigmaColor());
const Ptr<TonemapDurand> durand2 = createTonemapDurand();
durand2->setGamma(gamma);
durand2->setContrast(contrast);
durand2->setSaturation(saturation);
durand2->setSigmaColor(sigma_color);
durand2->setSigmaSpace(sigma_space);
ASSERT_EQ(gamma, durand2->getGamma());
ASSERT_EQ(contrast, durand2->getContrast());
ASSERT_EQ(saturation, durand2->getSaturation());
ASSERT_EQ(sigma_color, durand2->getSigmaColor());
ASSERT_EQ(sigma_space, durand2->getSigmaSpace());
}
#endif // OPENCV_ENABLE_NONFREE
}} // namespace
+75
View File
@@ -0,0 +1,75 @@
// 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 {
using namespace xphoto;
static void test_inpainting(const Size inputSize, InpaintTypes mode, double expected_psnr, ImreadModes inputMode = IMREAD_COLOR)
{
string original_path = cvtest::findDataFile("cv/shared/lena.png");
string mask_path = cvtest::findDataFile("cv/inpaint/mask.png");
Mat original_ = imread(original_path, inputMode);
ASSERT_FALSE(original_.empty()) << "Could not load input image " << original_path;
Mat mask_ = imread(mask_path, IMREAD_GRAYSCALE);
ASSERT_FALSE(mask_.empty()) << "Could not load error mask " << mask_path;
Mat original, mask;
resize(original_, original, inputSize, 0.0, 0.0, INTER_AREA);
resize(mask_, mask, inputSize, 0.0, 0.0, INTER_NEAREST);
Mat mask_valid = (mask == 0);
Mat im_distorted(inputSize, original.type(), Scalar::all(0));
original.copyTo(im_distorted, mask_valid);
Mat reconstructed;
xphoto::inpaint(im_distorted, mask_valid, reconstructed, mode);
double adiff_psnr = cvtest::PSNR(original, reconstructed);
EXPECT_LE(expected_psnr, adiff_psnr);
#if 0
imshow("original", original);
imshow("im_distorted", im_distorted);
imshow("reconstructed", reconstructed);
std::cout << "adiff_psnr=" << adiff_psnr << std::endl;
waitKey();
#endif
}
TEST(xphoto_inpaint, smoke_FSR_FAST) // fast smoke test, input doesn't fit well for tested algorithm
{
test_inpainting(Size(128, 128), INPAINT_FSR_FAST, 30);
}
TEST(xphoto_inpaint, smoke_FSR_BEST) // fast smoke test, input doesn't fit well for tested algorithm
{
applyTestTag(CV_TEST_TAG_LONG);
test_inpainting(Size(128, 128), INPAINT_FSR_BEST, 30);
}
TEST(xphoto_inpaint, smoke_grayscale_FSR_FAST) // fast smoke test, input doesn't fit well for tested algorithm
{
test_inpainting(Size(128, 128), INPAINT_FSR_FAST, 30, IMREAD_GRAYSCALE);
}
TEST(xphoto_inpaint, smoke_grayscale_FSR_BEST) // fast smoke test, input doesn't fit well for tested algorithm
{
test_inpainting(Size(128, 128), INPAINT_FSR_BEST, 30, IMREAD_GRAYSCALE);
}
TEST(xphoto_inpaint, regression_FSR_FAST)
{
test_inpainting(Size(512, 512), INPAINT_FSR_FAST, 39.5);
}
TEST(xphoto_inpaint, regression_FSR_BEST)
{
applyTestTag(CV_TEST_TAG_VERYLONG); // add --test_tag_enable=verylong to run this test
test_inpainting(Size(512, 512), INPAINT_FSR_BEST, 39.6);
}
}} // namespace
@@ -0,0 +1,47 @@
// 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 {
TEST(xphoto_simplefeatures, regression)
{
float acc_thresh = 0.01f;
// Generate a test image:
Mat test_im(1000, 1000, CV_8UC3);
RNG rng(1234);
rng.fill(test_im, RNG::NORMAL, Scalar(64, 100, 128), Scalar(10, 10, 10));
cvtest::threshold(test_im, test_im, 200.0, 255.0, THRESH_TRUNC);
test_im.at<Vec3b>(0, 0) = Vec3b(240, 220, 200);
// Which should have the following features:
Vec2f ref1(128.0f / (64 + 100 + 128), 100.0f / (64 + 100 + 128));
Vec2f ref2(200.0f / (240 + 220 + 200), 220.0f / (240 + 220 + 200));
vector<Vec2f> dst_features;
Ptr<xphoto::LearningBasedWB> wb = xphoto::createLearningBasedWB();
wb->setRangeMaxVal(255);
wb->setSaturationThreshold(0.98f);
wb->setHistBinNum(64);
wb->extractSimpleFeatures(test_im, dst_features);
ASSERT_LE(cv::norm(dst_features[0], ref1, NORM_INF), acc_thresh);
ASSERT_LE(cv::norm(dst_features[1], ref2, NORM_INF), acc_thresh);
ASSERT_LE(cv::norm(dst_features[2], ref1, NORM_INF), acc_thresh);
ASSERT_LE(cv::norm(dst_features[3], ref1, NORM_INF), acc_thresh);
// check 16 bit depth:
test_im.convertTo(test_im, CV_16U, 256.0);
wb->setRangeMaxVal(65535);
wb->setSaturationThreshold(0.98f);
wb->setHistBinNum(128);
wb->extractSimpleFeatures(test_im, dst_features);
ASSERT_LE(cv::norm(dst_features[0], ref1, NORM_INF), acc_thresh);
ASSERT_LE(cv::norm(dst_features[1], ref2, NORM_INF), acc_thresh);
ASSERT_LE(cv::norm(dst_features[2], ref1, NORM_INF), acc_thresh);
ASSERT_LE(cv::norm(dst_features[3], ref1, NORM_INF), acc_thresh);
}
}} // namespace
+6
View File
@@ -0,0 +1,6 @@
// 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"
CV_TEST_MAIN("")
+109
View File
@@ -0,0 +1,109 @@
// 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 testOilPainting(Mat imgSrc, int halfSize, int dynRatio, int colorSpace)
{
vector<int> histogramme;
vector<Vec3f> moyenneRGB;
Mat dst(imgSrc.size(), imgSrc.type());
Mat lum;
if (imgSrc.channels() != 1)
{
cvtColor(imgSrc, lum, colorSpace);
if (lum.channels() > 1)
{
extractChannel(lum, lum, 0);
}
}
else
lum = imgSrc.clone();
lum = lum / dynRatio;
if (dst.channels() == 3)
for (int y = 0; y < imgSrc.rows; y++)
{
Vec3b *vDst = dst.ptr<Vec3b>(y);
for (int x = 0; x < imgSrc.cols; x++, vDst++) //for each pixel
{
Mat mask(lum.size(), CV_8UC1, Scalar::all(0));
Rect r(Point(x - halfSize, y - halfSize), Size(2 * halfSize + 1, 2 * halfSize + 1));
r = r & Rect(Point(0, 0), lum.size());
mask(r).setTo(255);
int histSize[] = { 256 };
float hranges[] = { 0, 256 };
const float* ranges[] = { hranges };
Mat hist;
int channels[] = { 0 };
calcHist(&lum, 1, channels, mask, hist, 1, histSize, ranges, true, false);
double maxVal = 0;
Point pMin, pMax;
minMaxLoc(hist, 0, &maxVal, &pMin, &pMax);
mask.setTo(0, lum != static_cast<int>(pMax.x));
Scalar v = mean(imgSrc, mask);
*vDst = Vec3b(static_cast<uchar>(v[0]), static_cast<uchar>(v[1]), static_cast<uchar>(v[2]));
}
}
else
for (int y = 0; y < imgSrc.rows; y++)
{
uchar *vDst = dst.ptr<uchar>(y);
for (int x = 0; x < imgSrc.cols; x++, vDst++) //for each pixel
{
Mat mask(lum.size(), CV_8UC1, Scalar::all(0));
Rect r(Point(x - halfSize, y - halfSize), Size(2 * halfSize + 1, 2 * halfSize + 1));
r = r & Rect(Point(0, 0), lum.size());
mask(r).setTo(255);
int histSize[] = { 256 };
float hranges[] = { 0, 256 };
const float* ranges[] = { hranges };
Mat hist;
int channels[] = { 0 };
calcHist(&lum, 1, channels, mask, hist, 1, histSize, ranges, true, false);
double maxVal = 0;
Point pMin, pMax;
minMaxLoc(hist, 0, &maxVal, &pMin, &pMax);
mask.setTo(0, lum != static_cast<int>(pMax.x));
Scalar v = mean(imgSrc, mask);
*vDst = static_cast<uchar>(v[0]);
}
}
return dst;
}
TEST(xphoto_oil_painting, regression)
{
string folder = string(cvtest::TS::ptr()->get_data_path()) + "cv/inpaint/";
Mat orig = imread(folder+"exp1.png", IMREAD_COLOR);
ASSERT_TRUE(!orig.empty());
resize(orig, orig, Size(100, 100));
Mat dst1, dst2, dd;
xphoto::oilPainting(orig, dst1, 3, 5, COLOR_BGR2GRAY);
dst2 = testOilPainting(orig, 3, 5, COLOR_BGR2GRAY);
absdiff(dst1, dst2, dd);
vector<Mat> plane;
split(dd, plane);
for (auto p : plane)
{
double maxVal;
Point pIdx;
minMaxLoc(p, NULL, &maxVal, NULL, &pIdx);
int v = p.at<uchar>(pIdx);
ASSERT_LE(v, 2);
}
Mat orig2 = imread(folder + "exp1.png",IMREAD_GRAYSCALE);
ASSERT_TRUE(!orig2.empty());
resize(orig2, orig2, Size(100, 100));
Mat dst3, dst4, ddd;
xphoto::oilPainting(orig2, dst3, 3, 5, COLOR_BGR2GRAY);
dst4 = testOilPainting(orig2, 3, 5, COLOR_BGR2GRAY);
absdiff(dst3, dst4, ddd);
double maxVal;
Point pIdx;
minMaxLoc(ddd, NULL, &maxVal, NULL, &pIdx);
ASSERT_LE(ddd.at<uchar>(pIdx), 2);
}
}} // 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.
#ifndef __OPENCV_TEST_PRECOMP_HPP__
#define __OPENCV_TEST_PRECOMP_HPP__
#include "opencv2/xphoto.hpp"
#include "opencv2/ts.hpp"
#endif
@@ -0,0 +1,77 @@
Image Inpainting {#tutorial_xphoto_inpainting}
================
Introduction
------------
In this tutorial we will show how to use the algorithm Rapid Frequency Selective Reconstructiom (FSR) for image inpainting.
Basics
------
Image Inpainting is the process of reconstructing damaged or missing parts of an image.
This is achieved by replacing distorted pixels by pixels similar to the neighboring ones. There are several algorithms for inpainting, using different approaches for such replacement.
One of those algorithms is called **Rapid Frequency Selectice Reconstruction (FSR)**.
FSR reconstructs image signals by exploiting the property that small areas of images can be represented sparsely in the Fourier domain. See @cite GenserPCS2018 and @cite SeilerTIP2015 for details.
FSR can be utilized for the following areas of application:
-# **Error Concealment (Inpainting)**:
The sampling mask indicates the missing pixels of the distorted input image to be reconstructed.
-# **Non-Regular Sampling**:
For more information on how to choose a good sampling mask, please review @cite GroscheICIP2018 and @cite GroscheIST2018.
Example
-------
The following sample code shows how to use FSR for inpainting.
The non-zero pixels of the error mask indicate valid image area, while zero pixels indicate area to be reconstructed.
You can create an arbitrary mask manually using tools like Paint or GIMP. Start with a plain white image and draw some distortions in black.
@code{.cpp}
#include <opencv2/opencv.hpp>
#include <opencv2/xphoto/inpainting.hpp>
#include <iostream>
using namespace cv;
int main(int argc, char** argv)
{
// read image and error pattern
Mat original_, mask_;
original_ = imread("images/kodim22.png");
mask_ = imread("images/pattern_random.png", IMREAD_GRAYSCALE);
// make sure that mask and source image have the same size
Mat mask;
resize(mask_, mask, original_.size(), 0.0, 0.0, cv::INTER_NEAREST);
// distort image
Mat im_distorted(original_.size(), original_.type(), Scalar::all(0));
original_.copyTo(im_distorted, mask); // copy valid pixels only (i.e. non-zero pixels in mask)
// reconstruct the distorted image
// choose quality profile fast (xphoto::INPAINT_FSR_FAST) or best (xphoto::INPAINT_FSR_BEST)
Mat reconstructed;
xphoto::inpaint(im_distorted, mask, reconstructed, xphoto::INPAINT_FSR_FAST);
imshow("orignal image", original_);
imshow("distorted image", im_distorted);
imshow("reconstructed image", reconstructed);
waitKey();
return 0;
}
@endcode
Original and distorted image:
![image](images/originalVSdistorted.jpg)
Reconstruction:
![image](images/reconstructed_fastVSbest.jpg)
Left image: fast quality profile (run time 8 seconds). Right image: best quality profile (1 minute 51 seconds).
Additional Resources
--------------------
[Comparison of FSR to existing inpainting methods in OpenCV](https://github.com/opencv/opencv_contrib/files/3730212/inpainting_comparison.pdf)
Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

@@ -0,0 +1,23 @@
Oil painting effect {#tutorial_xphoto_oil_painting_effect}
===================================================
Introduction
------------
Image is converted in a color space default color space COLOR_BGR2GRAY.
For every pixel in the image a program calculated a histogram (first plane of color space) of the neighbouring of size 2*size+1.
and assigned the value of the most frequently occurring value. The result looks almost like an oil painting. Parameter 4 of oilPainting is used to decrease image dynamic and hence increase oil painting effect.
Example
--------------------
@code{.cpp}
Mat img;
Mat dst;
img = imread("opencv/samples/data/baboon.jpg");
xphoto::oilPainting(img, dst, 10, 1, COLOR_BGR2Lab);
imshow("oil painting effect", dst);
@endcode
Original ![](images/baboon.jpg)
Oil painting effect ![](images/baboon_oil_painting_effect.jpg)
@@ -0,0 +1,42 @@
Training the learning-based white balance algorithm {#tutorial_xphoto_training_white_balance}
===================================================
Introduction
------------
Many traditional white balance algorithms are statistics-based, i.e. they rely on the fact that certain assumptions should hold in properly white-balanced images
like the well-known grey-world assumption. However, better results can often be achieved by leveraging large datasets of images with ground-truth
illuminants in a learning-based framework. This tutorial demonstrates how to train a learning-based white balance algorithm and evaluate the quality of the results.
How to train a model
--------------------
-# Download a dataset for training. In this tutorial we will use the [Gehler-Shi dataset ](http://www.cs.sfu.ca/~colour/data/shi_gehler/). Extract all 568 training images
in one folder. A file containing ground-truth illuminant values (real_illum_568..mat) is downloaded separately.
-# We will be using a [Python script ](https://github.com/opencv/opencv_contrib/tree/master/modules/xphoto/samples/learn_color_balance.py) for training.
Call it with the following parameters:
@code
python learn_color_balance.py -i <path to the folder with training images> -g <path to real_illum_568..mat> -r 0,378 --num_trees 30 --max_tree_depth 6 --num_augmented 0
@endcode
This should start training a model on the first 378 images (2/3 of the whole dataset). We set the size of the model to be 30 regression tree pairs per feature and limit
the tree depth to be no more then 6. By default the resulting model will be saved to color_balance_model.yml
-# Use the trained model by passing its path when constructing an instance of LearningBasedWB:
@code{.cpp}
Ptr<xphoto::LearningBasedWB> wb = xphoto::createLearningBasedWB(modelFilename);
@endcode
How to evaluate a model
----------------------
-# We will use a [benchmarking script ](https://github.com/opencv/opencv_contrib/tree/master/modules/xphoto/samples/color_balance_benchmark.py) to compare
the model that we've trained with the classic grey-world algorithm on the remaining 1/3 of the dataset. Call the script with the following parameters:
@code
python color_balance_benchmark.py -a grayworld,learning_based:color_balance_model.yml -m <full path to folder containing the model> -i <path to the folder with training images> -g <path to real_illum_568..mat> -r 379,567 -d "img"
@endcode
-# The objective evaluation results are stored in white_balance_eval_result.html and the resulting white-balanced images are stored in the img folder for a qualitative
comparison of algorithms. Different algorithms are compared in terms of angular error between the estimated and ground-truth illuminants.