vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e
This commit is contained in:
@@ -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)
|
||||
@@ -0,0 +1,7 @@
|
||||
Additional photo processing algorithms
|
||||
======================================
|
||||
|
||||
1. Color balance
|
||||
2. Denoising
|
||||
3. Inpainting
|
||||
|
||||
@@ -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},
|
||||
}
|
||||
@@ -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__
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"AdditionalImports" : {
|
||||
"*" : [ "\"xphoto.hpp\"" ]
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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°" % 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;
|
||||
}
|
||||
@@ -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")
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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() );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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>(); }
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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};
|
||||
@@ -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__ */
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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__ */
|
||||
@@ -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>(); }
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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("")
|
||||
@@ -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
|
||||
@@ -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:
|
||||

|
||||
|
||||
Reconstruction:
|
||||

|
||||
|
||||
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 
|
||||
Oil painting effect 
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user