diff --git a/doc/tutorials/ximgproc/prediction/images/01.jpg b/doc/tutorials/ximgproc/prediction/images/01.jpg new file mode 100644 index 000000000..fddb8e21b Binary files /dev/null and b/doc/tutorials/ximgproc/prediction/images/01.jpg differ diff --git a/doc/tutorials/ximgproc/prediction/images/02.jpg b/doc/tutorials/ximgproc/prediction/images/02.jpg new file mode 100644 index 000000000..4583cf02f Binary files /dev/null and b/doc/tutorials/ximgproc/prediction/images/02.jpg differ diff --git a/doc/tutorials/ximgproc/prediction/images/03.jpg b/doc/tutorials/ximgproc/prediction/images/03.jpg new file mode 100644 index 000000000..16b3adf1e Binary files /dev/null and b/doc/tutorials/ximgproc/prediction/images/03.jpg differ diff --git a/doc/tutorials/ximgproc/prediction/images/04.jpg b/doc/tutorials/ximgproc/prediction/images/04.jpg new file mode 100644 index 000000000..cd157346a Binary files /dev/null and b/doc/tutorials/ximgproc/prediction/images/04.jpg differ diff --git a/doc/tutorials/ximgproc/prediction/images/05.jpg b/doc/tutorials/ximgproc/prediction/images/05.jpg new file mode 100644 index 000000000..6ab0ee159 Binary files /dev/null and b/doc/tutorials/ximgproc/prediction/images/05.jpg differ diff --git a/doc/tutorials/ximgproc/prediction/images/06.jpg b/doc/tutorials/ximgproc/prediction/images/06.jpg new file mode 100644 index 000000000..f42b71228 Binary files /dev/null and b/doc/tutorials/ximgproc/prediction/images/06.jpg differ diff --git a/doc/tutorials/ximgproc/prediction/images/07.jpg b/doc/tutorials/ximgproc/prediction/images/07.jpg new file mode 100644 index 000000000..0bdd457c2 Binary files /dev/null and b/doc/tutorials/ximgproc/prediction/images/07.jpg differ diff --git a/doc/tutorials/ximgproc/prediction/images/08.jpg b/doc/tutorials/ximgproc/prediction/images/08.jpg new file mode 100644 index 000000000..2c96d9900 Binary files /dev/null and b/doc/tutorials/ximgproc/prediction/images/08.jpg differ diff --git a/doc/tutorials/ximgproc/prediction/images/09.jpg b/doc/tutorials/ximgproc/prediction/images/09.jpg new file mode 100644 index 000000000..8e654faa5 Binary files /dev/null and b/doc/tutorials/ximgproc/prediction/images/09.jpg differ diff --git a/doc/tutorials/ximgproc/prediction/images/10.jpg b/doc/tutorials/ximgproc/prediction/images/10.jpg new file mode 100644 index 000000000..311596c65 Binary files /dev/null and b/doc/tutorials/ximgproc/prediction/images/10.jpg differ diff --git a/doc/tutorials/ximgproc/prediction/images/11.jpg b/doc/tutorials/ximgproc/prediction/images/11.jpg new file mode 100644 index 000000000..68ecdfaab Binary files /dev/null and b/doc/tutorials/ximgproc/prediction/images/11.jpg differ diff --git a/doc/tutorials/ximgproc/prediction/images/12.jpg b/doc/tutorials/ximgproc/prediction/images/12.jpg new file mode 100644 index 000000000..891de11f2 Binary files /dev/null and b/doc/tutorials/ximgproc/prediction/images/12.jpg differ diff --git a/doc/tutorials/ximgproc/prediction/prediction.rst b/doc/tutorials/ximgproc/prediction/prediction.rst new file mode 100644 index 000000000..0b8940286 --- /dev/null +++ b/doc/tutorials/ximgproc/prediction/prediction.rst @@ -0,0 +1,155 @@ +.. ximgproc: + +Structured forests for fast edge detection +****************************************** + +Introduction +------------ +Today most digital images and imaging devices use 8 bits per channel thus limiting the dynamic range of the device to two orders of magnitude (actually 256 levels), while human eye can adapt to lighting conditions varying by ten orders of magnitude. When we take photographs of a real world scene bright regions may be overexposed, while the dark ones may be underexposed, so we can't capture all details using a single exposure. HDR imaging works with images that use more that 8 bits per channel (usually 32-bit float values), allowing much wider dynamic range. + +There are different ways to obtain HDR images, but the most common one is to use photographs of the scene taken with different exposure values. To combine this exposures it is useful to know your camera’s response function and there are algorithms to estimate it. After the HDR image has been blended it has to be converted back to 8-bit to view it on usual displays. This process is called tonemapping. Additional complexities arise when objects of the scene or camera move between shots, since images with different exposures should be registered and aligned. + +In this tutorial we show how to generate and display HDR image from an exposure sequence. In our case images are already aligned and there are no moving objects. We also demonstrate an alternative approach called exposure fusion that produces low dynamic range image. Each step of HDR pipeline can be implemented using different algorithms so take a look at the reference manual to see them all. + +Examples +-------- + +.. image:: images/01.jpg + :height: 238pt + :width: 750pt + :alt: First example + :align: center + +.. image:: images/02.jpg + :height: 238pt + :width: 750pt + :alt: First example + :align: center + +.. image:: images/03.jpg + :height: 238pt + :width: 750pt + :alt: First example + :align: center + +.. image:: images/04.jpg + :height: 238pt + :width: 750pt + :alt: First example + :align: center + +.. image:: images/05.jpg + :height: 238pt + :width: 750pt + :alt: First example + :align: center + +.. image:: images/06.jpg + :height: 238pt + :width: 750pt + :alt: First example + :align: center + +.. image:: images/07.jpg + :height: 238pt + :width: 750pt + :alt: First example + :align: center + +.. image:: images/08.jpg + :height: 238pt + :width: 750pt + :alt: First example + :align: center + +.. image:: images/09.jpg + :height: 238pt + :width: 750pt + :alt: First example + :align: center + +.. image:: images/10.jpg + :height: 238pt + :width: 750pt + :alt: First example + :align: center + +.. image:: images/11.jpg + :height: 238pt + :width: 750pt + :alt: First example + :align: center + +.. image:: images/12.jpg + :height: 238pt + :width: 750pt + :alt: First example + :align: center + +**Note :** binarization techniques like Canny edge detector are applicable + to edges produced by both algorithms (``Sobel`` and ``StructuredEdgeDetection::detectEdges``). + +Source Code +----------- + +.. literalinclude:: ../../../../modules/ximpgroc/samples/cpp/structured_edge_detection.cpp + :language: cpp + :linenos: + :tab-width: 4 + +Explanation +----------- + +1. **Load source color image** + + .. code-block:: cpp + + cv::Mat image = cv::imread(inFilename, 1); + if ( image.empty() ) + { + printf("Cannot read image file: %s\n", inFilename.c_str()); + return -1; + } + +2. **Convert source image to [0;1] range** + + .. code-block:: cpp + + image.convertTo(image, cv::DataType::type, 1/255.0); + +3. **Run main algorithm** + + .. code-block:: cpp + + cv::Mat edges(image.size(), image.type()); + + cv::Ptr pDollar = + cv::createStructuredEdgeDetection(modelFilename); + pDollar->detectEdges(image, edges); + +4. **Show results** + + .. code-block:: cpp + + if ( outFilename == "" ) + { + cv::namedWindow("edges", 1); + cv::imshow("edges", edges); + + cv::waitKey(0); + } + else + cv::imwrite(outFilename, 255*edges); + +Literature +---------- +For more information, refer to the following papers : + +.. [Dollar2013] Dollar P., Zitnick C. L., "Structured forests for fast edge detection", + IEEE International Conference on Computer Vision (ICCV), 2013, + pp. 1841-1848. `DOI `_ + +.. [Lim2013] Lim J. J., Zitnick C. L., Dollar P., "Sketch Tokens: A Learned + Mid-level Representation for Contour and Object Detection", + Comoputer Vision and Pattern Recognition (CVPR), 2013, + pp. 3158-3165. `DOI `_ diff --git a/doc/tutorials/ximgproc/training/scripts/modelConvert.m b/doc/tutorials/ximgproc/training/scripts/modelConvert.m new file mode 100644 index 000000000..11790bbba --- /dev/null +++ b/doc/tutorials/ximgproc/training/scripts/modelConvert.m @@ -0,0 +1,73 @@ +function modelConvert(model, outname) +%% script for converting Piotr's matlab model into YAML format + +outfile = fopen(outname, 'w'); + +fprintf(outfile, '%%YAML:1.0\n\n'); + +fprintf(outfile, ['options:\n'... + ' numberOfTrees: 8\n'... + ' numberOfTreesToEvaluate: 4\n'... + ' selfsimilarityGridSize: 5\n'... + ' stride: 2\n'... + ' shrinkNumber: 2\n'... + ' patchSize: 32\n'... + ' patchInnerSize: 16\n'... + ' numberOfGradientOrientations: 4\n'... + ' gradientSmoothingRadius: 0\n'... + ' regFeatureSmoothingRadius: 2\n'... + ' ssFeatureSmoothingRadius: 8\n'... + ' gradientNormalizationRadius: 4\n\n']); + +fprintf(outfile, 'childs:\n'); +printToYML(outfile, model.child', 0); + +fprintf(outfile, 'featureIds:\n'); +printToYML(outfile, model.fids', 0); + +fprintf(outfile, 'thresholds:\n'); +printToYML(outfile, model.thrs', 0); + +N = 1000; +fprintf(outfile, 'edgeBoundaries:\n'); +printToYML(outfile, model.eBnds, N); + +fprintf(outfile, 'edgeBins:\n'); +printToYML(outfile, model.eBins, N); + +fclose(outfile); +gzip(outname); + +end + +function printToYML(outfile, A, N) +%% append matrix A to outfile as +%% - [a11, a12, a13, a14, ..., a1n] +%% - [a21, a22, a23, a24, ..., a2n] +%% ... +%% +%% if size(A, 2) == 1, A is printed by N elemnent per row + + if (length(size(A)) ~= 2) + error('printToYML: second-argument matrix should have two dimensions'); + end + + if (size(A,2) ~= 1) + for i=1:size(A,1) + fprintf(outfile, ' - ['); + fprintf(outfile, '%d,', A(i, 1:end-1)); + fprintf(outfile, '%d]\n', A(i, end)); + end + else + len = length(A); + for i=1:ceil(len/N) + first = (i-1)*N + 1; + last = min(i*N, len) - 1; + + fprintf(outfile, ' - ['); + fprintf(outfile, '%d,', A(first:last)); + fprintf(outfile, '%d]\n', A(last + 1)); + end + end + fprintf(outfile, '\n'); +end \ No newline at end of file diff --git a/doc/tutorials/ximgproc/training/training.rst b/doc/tutorials/ximgproc/training/training.rst new file mode 100644 index 000000000..20a1cfc27 --- /dev/null +++ b/doc/tutorials/ximgproc/training/training.rst @@ -0,0 +1,115 @@ +.. ximgproc: + +Structured forest training +************************** + +Introduction +------------ +In this tutorial we show how to train your own structured forest using author's initial Matlab implementation. + +Training pipeline +----------------- + +1. Download "Piotr's Toolbox" from `link `_ + and put it into separate directory, e.g. PToolbox + +2. Download BSDS500 dataset from `link ` + and put it into separate directory named exactly BSR + +3. Add both directory and their subdirectories to Matlab path. + +4. Download detector code from `link ` + and put it into root directory. Now you should have :: + + . + BSR + PToolbox + models + private + Contents.m + edgesChns.m + edgesDemo.m + edgesDemoRgbd.m + edgesDetect.m + edgesEval.m + edgesEvalDir.m + edgesEvalImg.m + edgesEvalPlot.m + edgesSweeps.m + edgesTrain.m + license.txt + readme.txt + +5. Rename models/forest/modelFinal.mat to models/forest/modelFinal.mat.backup + +6. Open edgesChns.m and comment lines 26--41. Add after commented lines the following: :: + + shrink=opts.shrink; + chns = single(getFeatures( im2double(I) )); + +7. Now it is time to compile promised getFeatures. I do with the following code: + + .. code-block:: cpp + + #include + #include + + #include + #include + + #include "MxArray.hpp" // https://github.com/kyamagu/mexopencv + + class NewRFFeatureGetter : public cv::RFFeatureGetter + { + public: + NewRFFeatureGetter() : name("NewRFFeatureGetter"){} + + virtual void getFeatures(const cv::Mat &src, NChannelsMat &features, + const int gnrmRad, const int gsmthRad, + const int shrink, const int outNum, const int gradNum) const + { + // here your feature extraction code, the default one is: + // resulting features Mat should be n-channels, floating point matrix + } + + protected: + cv::String name; + }; + + MEXFUNCTION_LINKAGE void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) + { + if (nlhs != 1) mexErrMsgTxt("nlhs != 1"); + if (nrhs != 1) mexErrMsgTxt("nrhs != 1"); + + cv::Mat src = MxArray(prhs[0]).toMat(); + src.convertTo(src, cv::DataType::type); + + std::string modelFile = MxArray(prhs[1]).toString(); + NewRFFeatureGetter *pDollar = createNewRFFeatureGetter(); + + cv::Mat edges; + pDollar->getFeatures(src, edges, 4, 0, 2, 13, 4); + // you can use other numbers here + + edges.convertTo(edges, cv::DataType::type); + + plhs[0] = MxArray(edges); + } + +8. Place compiled mex file into root dir and run edgesDemo. + You will need to wait a couple of hours after that the new model + will appear inside models/forest/. + +9. The final step is converting trained model from Matlab binary format + to YAML which you can use with our ocv::StructuredEdgeDetection. + For this purpose run opencv_contrib/doc/tutorials/ximpgroc/training/modelConvert(model, "model.yml") + +How to use your model +--------------------- + +Just use expanded constructor with above defined class NewRFFeatureGetter + + .. code-block:: cpp + + cv::StructuredEdgeDetection pDollar + = cv::createStructuredEdgeDetection( modelName, makePtr() ); diff --git a/modules/ximgproc/CMakeLists.txt b/modules/ximgproc/CMakeLists.txt index 6a6c87b07..6b68a3243 100644 --- a/modules/ximgproc/CMakeLists.txt +++ b/modules/ximgproc/CMakeLists.txt @@ -1,4 +1,4 @@ set(the_description "Extended image processing module. It includes edge-aware filters and etc.") ocv_define_module(ximgproc opencv_imgproc opencv_core opencv_highgui) -target_link_libraries(opencv_ximgproc) \ No newline at end of file +target_link_libraries(opencv_ximgproc) diff --git a/modules/ximgproc/doc/structured_edge_detection.rst b/modules/ximgproc/doc/structured_edge_detection.rst new file mode 100644 index 000000000..55848b8b0 --- /dev/null +++ b/modules/ximgproc/doc/structured_edge_detection.rst @@ -0,0 +1,67 @@ +Structured forests for fast edge detection +****************************************** + +.. highlight:: cpp + +This module contains implementations of modern structured edge detection algorithms, +i.e. algorithms which somehow takes into account pixel affinities in natural images. + +StructuredEdgeDetection +----------------------- + +.. ocv:class:: StructuredEdgeDetection : public Algorithm + +Class implementing edge detection algorithm from [Dollar2013]_ :: + + /*! \class StructuredEdgeDetection + Prediction part of [P. Dollar and C. L. Zitnick. Structured Forests for Fast Edge Detection, 2013]. + */ + class CV_EXPORTS_W StructuredEdgeDetection : public Algorithm + { + public: + + /*! + * The function detects edges in src and draw them to dst + * + * \param src : source image (RGB, float, in [0;1]) to detect edges + * \param dst : destination image (grayscale, float, in [0;1]) + * where edges are drawn + */ + CV_WRAP virtual void detectEdges(const Mat src, Mat dst) = 0; + }; + + /*! + * The only available constructor loading data from model file + * + * \param model : name of the file where the model is stored + */ + CV_EXPORTS_W Ptr createStructuredEdgeDetection(const String &model); + +StructuredEdgeDetection::detectEdges +++++++++++++++++++++++++++++++++++++ +.. ocv:function:: void detectEdges(const Mat src, Mat dst) + + The function detects edges in src and draw them to dst. The algorithm underlies this function + is much more robust to texture presence, than common approaches, e.g. Sobel + + :param src: source image (RGB, float, in [0;1]) to detect edges + :param dst: destination image (grayscale, float, in [0;1]) + where edges are drawn + +.. seealso:: + + :ocv:class:`Sobel`, + :ocv:class:`Canny` + +createStructuredEdgeDetection ++++++++++++++++++++++++++++++ +.. ocv:function:: Ptr createStructuredEdgeDetection(String model) + + The only available constructor + + :param model: model file name + + +.. [Dollar2013] P. Dollár, C. L. Zitnick, "Structured forests for fast edge detection", + IEEE International Conference on Computer Vision (ICCV), 2013, + pp. 1841-1848. `DOI `_ diff --git a/modules/ximgproc/doc/ximgproc.rst b/modules/ximgproc/doc/ximgproc.rst index 8852e3aca..d5554ce22 100644 --- a/modules/ximgproc/doc/ximgproc.rst +++ b/modules/ximgproc/doc/ximgproc.rst @@ -8,3 +8,4 @@ ximgproc. Extended image processing module. :maxdepth: 2 edge_aware_filters + structured_edge_detection diff --git a/modules/ximgproc/include/opencv2/ximgproc.hpp b/modules/ximgproc/include/opencv2/ximgproc.hpp index da0635efa..95e1f094e 100644 --- a/modules/ximgproc/include/opencv2/ximgproc.hpp +++ b/modules/ximgproc/include/opencv2/ximgproc.hpp @@ -38,5 +38,6 @@ #define __OPENCV_XIMGPROC_HPP__ #include "ximgproc/edge_filter.hpp" +#include "ximgproc/structured_edge_detection.hpp" #endif \ No newline at end of file diff --git a/modules/ximgproc/include/opencv2/ximgproc/structured_edge_detection.hpp b/modules/ximgproc/include/opencv2/ximgproc/structured_edge_detection.hpp new file mode 100644 index 000000000..23f0af7e1 --- /dev/null +++ b/modules/ximgproc/include/opencv2/ximgproc/structured_edge_detection.hpp @@ -0,0 +1,127 @@ +/*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_STRUCTURED_EDGE_DETECTION_HPP__ +#define __OPENCV_STRUCTURED_EDGE_DETECTION_HPP__ +#ifdef __cplusplus + +/* + * structured_edge_detection.hpp + * + * Created on: Jun 17, 2014 + * Author: Yury Gitman + */ + +#include + +/*! \namespace cv + Namespace where all the C++ OpenCV functionality resides + */ +namespace cv +{ +namespace ximgproc +{ +/*! \class RFFeatureGetter + Helper class for training part of [P. Dollar and C. L. Zitnick. Structured Forests for Fast Edge Detection, 2013]. + */ +class CV_EXPORTS_W RFFeatureGetter : public Algorithm +{ +public: + + /*! + * This functions extracts feature channels from src. + * Than StructureEdgeDetection uses this feature space + * to detect edges. + * + * \param src : source image to extract features + * \param features : output n-channel floating point feature matrix. + * + * \param gnrmRad : __rf.options.gradientNormalizationRadius + * \param gsmthRad : __rf.options.gradientSmoothingRadius + * \param shrink : __rf.options.shrinkNumber + * \param outNum : __rf.options.numberOfOutputChannels + * \param gradNum : __rf.options.numberOfGradientOrientations + */ + CV_WRAP virtual void getFeatures(const Mat &src, Mat &features, + const int gnrmRad, + const int gsmthRad, + const int shrink, + const int outNum, + const int gradNum) const = 0; +}; + +CV_EXPORTS_W Ptr createRFFeatureGetter(); + + + +/*! \class StructuredEdgeDetection + Prediction part of [P. Dollar and C. L. Zitnick. Structured Forests for Fast Edge Detection, 2013]. + */ +class CV_EXPORTS_W StructuredEdgeDetection : public Algorithm +{ +public: + + /*! + * The function detects edges in src and draw them to dst + * + * \param src : source image (RGB, float, in [0;1]) to detect edges + * \param dst : destination image (grayscale, float, in [0;1]) + * where edges are drawn + */ + CV_WRAP virtual void detectEdges(const Mat &src, Mat &dst) const = 0; +}; + +/*! +* The only constructor +* +* \param model : name of the file where the model is stored +* \param howToGetFeatures : optional object inheriting from RFFeatureGetter. +* You need it only if you would like to train your +* own forest, pass NULL otherwise +*/ +CV_EXPORTS_W Ptr createStructuredEdgeDetection(const String &model, + const RFFeatureGetter *howToGetFeatures = NULL); + +} +} +#endif +#endif /* __OPENCV_STRUCTURED_EDGE_DETECTION_HPP__ */ \ No newline at end of file diff --git a/modules/ximgproc/samples/structured_edge_detection.cpp b/modules/ximgproc/samples/structured_edge_detection.cpp new file mode 100644 index 000000000..fed8da40d --- /dev/null +++ b/modules/ximgproc/samples/structured_edge_detection.cpp @@ -0,0 +1,66 @@ +#include +#include "opencv2/highgui.hpp" +#include "opencv2/core/utility.hpp" + +using namespace cv; +using namespace cv::ximgproc; + +const char* keys = +{ + "{i || input image name}" + "{m || model 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 structured forests for fast edge detection\n" + "Call:\n" + " structured_edge_detection -i=in_image_name -m=model_name [-o=out_image_name]\n\n"); + return 0; + } + + cv::CommandLineParser parser(argc, argv, keys); + if ( !parser.check() ) + { + parser.printErrors(); + return -1; + } + + std::string modelFilename = parser.get("m"); + std::string inFilename = parser.get("i"); + std::string outFilename = parser.get("o"); + + cv::Mat image = cv::imread(inFilename, 1); + if ( image.empty() ) + { + printf("Cannot read image file: %s\n", inFilename.c_str()); + return -1; + } + + image.convertTo(image, cv::DataType::type, 1/255.0); + + cv::Mat edges(image.size(), image.type()); + + cv::Ptr pDollar = + createStructuredEdgeDetection(modelFilename); + pDollar->detectEdges(image, edges); + + if ( outFilename == "" ) + { + cv::namedWindow("edges", 1); + cv::imshow("edges", edges); + + cv::waitKey(0); + } + else + cv::imwrite(outFilename, 255*edges); + + return 0; +} diff --git a/modules/ximgproc/src/advanced_types.hpp b/modules/ximgproc/src/advanced_types.hpp new file mode 100644 index 000000000..3cc19d6ed --- /dev/null +++ b/modules/ximgproc/src/advanced_types.hpp @@ -0,0 +1,91 @@ +/*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 + +/********************* Defines *********************/ + +#ifndef CV_SQR +# define CV_SQR(x) ((x)*(x)) +#endif + +#ifndef CV_CUBE +# define CV_CUBE(x) ((x)*(x)*(x)) +#endif + +#ifndef CV_INIT_VECTOR +# define CV_INIT_VECTOR(vname, type, ...) \ + static const type vname##_a[] = __VA_ARGS__; \ + std::vector vname(vname##_a, \ + vname##_a + sizeof(vname##_a) / sizeof(*vname##_a)) +#endif + +/********************* Types *********************/ + +/*! fictitious type to highlight that function + * can process n-channels arguments */ +typedef cv::Mat NChannelsMat; + +/********************* Functions *********************/ + +namespace cv +{ +namespace ximgproc +{ + +template 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 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__ */ \ No newline at end of file diff --git a/modules/ximgproc/src/structured_edge_detection.cpp b/modules/ximgproc/src/structured_edge_detection.cpp new file mode 100644 index 000000000..3d7a28b3e --- /dev/null +++ b/modules/ximgproc/src/structured_edge_detection.cpp @@ -0,0 +1,693 @@ +/*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 +#include +#include +#include +#include + +#include "precomp.hpp" + +#include "advanced_types.hpp" + +/********************* Helper functions *********************/ + +/*! + * Lightweight wrapper over cv::resize + * + * \param src : source image to resize + * \param dst : destination image size + * \return resized image + */ +static cv::Mat imresize(const cv::Mat &src, const cv::Size &nSize) +{ + cv::Mat dst; + if (nSize.width < src.size().width + && nSize.height < src.size().height) + cv::resize(src, dst, nSize, 0.0, 0.0, cv::INTER_AREA); + else + cv::resize(src, dst, nSize, 0.0, 0.0, cv::INTER_LINEAR); + + return dst; +} + +/*! + * The function filters src with triangle filter with radius equal rad + * + * \param src : source image to filter + * \param rad : radius of filtering kernel + * \return filtering result + */ +static cv::Mat imsmooth(const cv::Mat &src, const int rad) +{ + if (rad == 0) + return src; + else + { + const float p = 12.0f/rad/(rad + 2) - 2; + cv::Mat dst; + + if (rad <= 1) + { + CV_INIT_VECTOR(kernelXY, float, {1/(p + 2), p/(p + 2), 1/(p + 2)}); + cv::sepFilter2D(src, dst, -1, kernelXY, kernelXY); + } + else + { + float nrml = CV_SQR(rad + 1.0f); + + std::vector kernelXY(2*rad + 1); + for (int i = 0; i <= rad; ++i) + { + kernelXY[2*rad - i] = (i + 1) / nrml; + kernelXY[i] = (i + 1) / nrml; + } + sepFilter2D(src, dst, -1, kernelXY, kernelXY); + } + + return dst; + } +} + +/*! + * The function implements rgb to luv conversion in a way similar + * to UCSD computer vision toolbox + * + * \param src : source image (RGB, float, in [0;1]) to convert + * \return converted image in luv colorspace + */ +static cv::Mat rgb2luv(const cv::Mat &src) +{ + cv::Mat dst(src.size(), src.type()); + + const float a = CV_CUBE(29.0f)/27; + const float y0 = 8.0f/a; + + const float mX[] = {0.430574f, 0.341550f, 0.178325f}; + const float mY[] = {0.222015f, 0.706655f, 0.071330f}; + const float mZ[] = {0.020183f, 0.129553f, 0.939180f}; + + const float maxi= 1.0f/270; + const float minu= -88*maxi; + const float minv= -134*maxi; + + const float un = 0.197833f; + const float vn = 0.468331f; + + // build (padded) lookup table for y->l conversion assuming y in [0,1] + std::vector lTable(1024); + for (int i = 0; i < 1024; ++i) + { + float y = i/1024.0f; + float l = y > y0 ? 116*powf(y, 1.0f/3.0f) - 16 : y*a; + + lTable[i] = l*maxi; + } + for (int i = 0; i < 40; ++i) + lTable.push_back(*--lTable.end()); + + const int nchannels = 3; + + for (int i = 0; i < src.rows; ++i) + { + const float *pSrc = src.ptr(i); + float *pDst = dst.ptr(i); + + for (int j = 0; j < src.cols*nchannels; j += nchannels) + { + const float rgb[] = {pSrc[j + 0], pSrc[j + 1], pSrc[j + 2]}; + + const float xyz[] = {mX[0]*rgb[0] + mX[1]*rgb[1] + mX[2]*rgb[2], + mY[0]*rgb[0] + mY[1]*rgb[1] + mY[2]*rgb[2], + mZ[0]*rgb[0] + mZ[1]*rgb[1] + mZ[2]*rgb[2]}; + const float nz = 1.0f / float(xyz[0] + 15*xyz[1] + 3*xyz[2] + 1e-35); + + const float l = pDst[j] = lTable[cvFloor(1024*xyz[1])]; + + pDst[j + 1] = l * (13*4*xyz[0]*nz - 13*un) - minu;; + pDst[j + 2] = l * (13*9*xyz[1]*nz - 13*vn) - minv; + } + } + + return dst; +} + +/*! + * The function computes gradient magnitude and weighted (with magnitude) + * orientation histogram. Magnitude is additionally normalized + * by dividing on imsmooth(M, gnrmRad) + 0.01; + * + * \param src : source image + * \param magnitude : gradient magnitude + * \param histogram : gradient orientation nBins-channels histogram + * \param nBins : number of gradient orientations + * \param pSize : factor to downscale histogram + * \param gnrmRad : radius for magnitude normalization + */ +static void gradientHist(const cv::Mat &src, cv::Mat &magnitude, cv::Mat &histogram, + const int nBins, const int pSize, const int gnrmRad) +{ + cv::Mat phase, Dx, Dy; + + magnitude.create( src.size(), cv::DataType::type ); + phase.create( src.size(), cv::DataType::type ); + histogram.create( cv::Size( cvRound(src.size().width/float(pSize)), + cvRound(src.size().height/float(pSize)) ), + CV_MAKETYPE(cv::DataType::type, nBins) ); + + histogram.setTo(0); + + cv::Sobel( src, Dx, cv::DataType::type, + 1, 0, 1, 1.0, 0.0, cv::BORDER_REFLECT ); + cv::Sobel( src, Dy, cv::DataType::type, + 0, 1, 1, 1.0, 0.0, cv::BORDER_REFLECT ); + + int nchannels = src.channels(); + + for (int i = 0; i < src.rows; ++i) + { + const float *pDx = Dx.ptr(i); + const float *pDy = Dy.ptr(i); + + float *pMagnitude = magnitude.ptr(i); + float *pPhase = phase.ptr(i); + + for (int j = 0; j < src.cols*nchannels; j += nchannels) + { + float fMagn = float(-1e-5), fdx = 0, fdy = 0; + for (int k = 0; k < nchannels; ++k) + { + float cMagn = CV_SQR( pDx[j + k] ) + CV_SQR( pDy[j + k] ); + if (cMagn > fMagn) + { + fMagn = cMagn; + fdx = pDx[j + k]; + fdy = pDy[j + k]; + } + } + + pMagnitude[j/nchannels] = sqrtf(fMagn); + + float angle = cv::fastAtan2(fdy, fdx) / 180.0f - 1.0f * (fdy < 0); + if (std::fabs(fdx) + std::fabs(fdy) < 1e-5) + angle = 0.5f; + pPhase[j/nchannels] = angle; + } + } + + magnitude /= imsmooth( magnitude, gnrmRad ) + + 0.01*cv::Mat::ones( magnitude.size(), magnitude.type() ); + + for (int i = 0; i < phase.rows; ++i) + { + const float *pPhase = phase.ptr(i); + const float *pMagn = magnitude.ptr(i); + + float *pHist = histogram.ptr(i/pSize); + + for (int j = 0; j < phase.cols; ++j) + pHist[cvRound((j/pSize + pPhase[j])*nBins)] += pMagn[j] / CV_SQR(pSize); + } +} + +/********************* RFFeatureGetter class *********************/ + +namespace cv +{ +namespace ximgproc +{ + +class RFFeatureGetterImpl : public RFFeatureGetter +{ +public: + /*! + * Default constructor + */ + RFFeatureGetterImpl() : name("RFFeatureGetter"){} + + /*! + * The method extracts features from img and store them to features. + * Extracted features are appropriate for StructuredEdgeDetection::predictEdges. + * + * \param src : source image (RGB, float, in [0;1]) to extract features + * \param features : destination feature image + * + * \param gnrmRad : __rf.options.gradientNormalizationRadius + * \param gsmthRad : __rf.options.gradientSmoothingRadius + * \param shrink : __rf.options.shrinkNumber + * \param outNum : __rf.options.numberOfOutputChannels + * \param gradNum : __rf.options.numberOfGradientOrientations + */ + virtual void getFeatures(const Mat &src, Mat &features, const int gnrmRad, const int gsmthRad, + const int shrink, const int outNum, const int gradNum) const + { + cv::Mat luvImg = rgb2luv(src); + + std::vector featureArray; + + cv::Size nSize = src.size() / float(shrink); + split( imresize(luvImg, nSize), featureArray ); + + CV_INIT_VECTOR(scales, float, {1.0f, 0.5f}); + + for (size_t i = 0; i < scales.size(); ++i) + { + int pSize = std::max( 1, int(shrink*scales[i]) ); + + cv::Mat magnitude, histogram; + gradientHist(/**/ imsmooth(imresize(luvImg, scales[i]*src.size()), gsmthRad), + magnitude, histogram, gradNum, pSize, gnrmRad /**/); + + featureArray.push_back(/**/ imresize( magnitude, nSize ).clone() /**/); + featureArray.push_back(/**/ imresize( histogram, nSize ).clone() /**/); + } + + // Mixing + int resType = CV_MAKETYPE(cv::DataType::type, outNum); + features.create(nSize, resType); + + std::vector fromTo; + for (int i = 0; i < 2*outNum; ++i) + fromTo.push_back(i/2); + + mixChannels(featureArray, features, fromTo); + } + +protected: + /*! algorithm name */ + String name; +}; + +Ptr createRFFeatureGetter() +{ + return makePtr(); +} + +} +} + +/********************* StructuredEdgeDetection class *********************/ + +namespace cv +{ +namespace ximgproc +{ + +class StructuredEdgeDetectionImpl : public StructuredEdgeDetection +{ +public: + /*! + * This constructor loads __rf model from filename + * + * \param filename : name of the file where the model is stored + */ + StructuredEdgeDetectionImpl(const cv::String &filename, + const RFFeatureGetter *_howToGetFeatures) + : name("StructuredEdgeDetection"), + howToGetFeatures( _howToGetFeatures != NULL + ? _howToGetFeatures + : createRFFeatureGetter() ) + { + cv::FileStorage modelFile(filename, FileStorage::READ); + CV_Assert( modelFile.isOpened() ); + + __rf.options.stride + = modelFile["options"]["stride"]; + __rf.options.shrinkNumber + = modelFile["options"]["shrinkNumber"]; + __rf.options.patchSize + = modelFile["options"]["patchSize"]; + __rf.options.patchInnerSize + = modelFile["options"]["patchInnerSize"]; + + __rf.options.numberOfGradientOrientations + = modelFile["options"]["numberOfGradientOrientations"]; + __rf.options.gradientSmoothingRadius + = modelFile["options"]["gradientSmoothingRadius"]; + __rf.options.regFeatureSmoothingRadius + = modelFile["options"]["regFeatureSmoothingRadius"]; + __rf.options.ssFeatureSmoothingRadius + = modelFile["options"]["ssFeatureSmoothingRadius"]; + __rf.options.gradientNormalizationRadius + = modelFile["options"]["gradientNormalizationRadius"]; + + __rf.options.selfsimilarityGridSize + = modelFile["options"]["selfsimilarityGridSize"]; + + __rf.options.numberOfTrees + = modelFile["options"]["numberOfTrees"]; + __rf.options.numberOfTreesToEvaluate + = modelFile["options"]["numberOfTreesToEvaluate"]; + + __rf.options.numberOfOutputChannels = + 2*(__rf.options.numberOfGradientOrientations + 1) + 3; + //-------------------------------------------- + + cv::FileNode childs = modelFile["childs"]; + cv::FileNode featureIds = modelFile["featureIds"]; + + std::vector currentTree; + + for(cv::FileNodeIterator it = childs.begin(); + it != childs.end(); ++it) + { + (*it) >> currentTree; + std::copy(currentTree.begin(), currentTree.end(), + std::back_inserter(__rf.childs)); + } + + for(cv::FileNodeIterator it = featureIds.begin(); + it != featureIds.end(); ++it) + { + (*it) >> currentTree; + std::copy(currentTree.begin(), currentTree.end(), + std::back_inserter(__rf.featureIds)); + } + + cv::FileNode thresholds = modelFile["thresholds"]; + std::vector fcurrentTree; + + for(cv::FileNodeIterator it = thresholds.begin(); + it != thresholds.end(); ++it) + { + (*it) >> fcurrentTree; + std::copy(fcurrentTree.begin(), fcurrentTree.end(), + std::back_inserter(__rf.thresholds)); + } + + cv::FileNode edgeBoundaries = modelFile["edgeBoundaries"]; + cv::FileNode edgeBins = modelFile["edgeBins"]; + + for(cv::FileNodeIterator it = edgeBoundaries.begin(); + it != edgeBoundaries.end(); ++it) + { + (*it) >> currentTree; + std::copy(currentTree.begin(), currentTree.end(), + std::back_inserter(__rf.edgeBoundaries)); + } + + for(cv::FileNodeIterator it = edgeBins.begin(); + it != edgeBins.end(); ++it) + { + (*it) >> currentTree; + std::copy(currentTree.begin(), currentTree.end(), + std::back_inserter(__rf.edgeBins)); + } + + __rf.numberOfTreeNodes = int( __rf.childs.size() ) / __rf.options.numberOfTrees; + } + + /*! + * The function detects edges in src and draw them to dst + * + * \param src : source image (RGB, float, in [0;1]) to detect edges + * \param dst : destination image (grayscale, float, in [0;1]) + * where edges are drawn + */ + void detectEdges(const cv::Mat &src, cv::Mat &dst) const + { + CV_Assert( src.type() == CV_32FC3 ); + + dst.create( src.size(), cv::DataType::type ); + + int padding = ( __rf.options.patchSize + - __rf.options.patchInnerSize )/2; + + cv::Mat nSrc; + copyMakeBorder( src, nSrc, padding, padding, + padding, padding, BORDER_REFLECT ); + + NChannelsMat features; + createRFFeatureGetter()->getFeatures( nSrc, features, + __rf.options.gradientNormalizationRadius, + __rf.options.gradientSmoothingRadius, + __rf.options.shrinkNumber, + __rf.options.numberOfOutputChannels, + __rf.options.numberOfGradientOrientations ); + predictEdges( features, dst ); + } + +protected: + /*! + * Private method used by process method. The function + * predict edges in n-channel feature image and store them to dst. + * + * \param features : source image (n-channels, float) to detect edges + * \param dst : destination image (grayscale, float, in [0;1]) where edges are drawn + */ + void predictEdges(const NChannelsMat &features, cv::Mat &dst) const + { + int shrink = __rf.options.shrinkNumber; + int rfs = __rf.options.regFeatureSmoothingRadius; + int sfs = __rf.options.ssFeatureSmoothingRadius; + + int nTreesEval = __rf.options.numberOfTreesToEvaluate; + int nTrees = __rf.options.numberOfTrees; + int nTreesNodes = __rf.numberOfTreeNodes; + + const int nchannels = features.channels(); + int pSize = __rf.options.patchSize; + + int nFeatures = CV_SQR(pSize/shrink)*nchannels; + int outNum = __rf.options.numberOfOutputChannels; + + int stride = __rf.options.stride; + int ipSize = __rf.options.patchInnerSize; + int gridSize = __rf.options.selfsimilarityGridSize; + + const int height = cvCeil( double(features.rows*shrink - pSize) / stride ); + const int width = cvCeil( double(features.cols*shrink - pSize) / stride ); + // image size in patches with overlapping + + //------------------------------------------------------------------------- + + NChannelsMat regFeatures = imsmooth(features, cvRound(rfs / float(shrink))); + NChannelsMat ssFeatures = imsmooth(features, cvRound(sfs / float(shrink))); + + NChannelsMat indexes(height, width, CV_MAKETYPE(DataType::type, nTreesEval)); + + std::vector offsetI(/**/ CV_SQR(pSize/shrink)*nchannels, 0); + for (int i = 0; i < CV_SQR(pSize/shrink)*nchannels; ++i) + { + int z = i / CV_SQR(pSize/shrink); + int y = ( i % CV_SQR(pSize/shrink) )/(pSize/shrink); + int x = ( i % CV_SQR(pSize/shrink) )%(pSize/shrink); + + offsetI[i] = x*features.cols*nchannels + y*nchannels + z; + } + // lookup table for mapping linear index to offsets + + std::vector offsetE(/**/ CV_SQR(ipSize)*outNum, 0); + for (int i = 0; i < CV_SQR(ipSize)*outNum; ++i) + { + int z = i / CV_SQR(ipSize); + int y = ( i % CV_SQR(ipSize) )/ipSize; + int x = ( i % CV_SQR(ipSize) )%ipSize; + + offsetE[i] = x*dst.cols*outNum + y*outNum + z; + } + // lookup table for mapping linear index to offsets + + std::vector offsetX( CV_SQR(gridSize)*(CV_SQR(gridSize) - 1)/2 * nchannels, 0); + std::vector offsetY( CV_SQR(gridSize)*(CV_SQR(gridSize) - 1)/2 * nchannels, 0); + + int hc = cvRound( (pSize/shrink) / (2.0*gridSize) ); + // half of cell + std::vector gridPositions; + for(int i = 0; i < gridSize; i++) + gridPositions.push_back( int( (i+1)*(pSize/shrink + 2*hc - 1)/(gridSize + 1.0) - hc + 0.5f ) ); + + for (int i = 0, n = 0; i < CV_SQR(gridSize)*nchannels; ++i) + for (int j = (i%CV_SQR(gridSize)) + 1; j < CV_SQR(gridSize); ++j, ++n) + { + int z = i / CV_SQR(gridSize); + + int x1 = gridPositions[i%CV_SQR(gridSize)%gridSize]; + int y1 = gridPositions[i%CV_SQR(gridSize)/gridSize]; + + int x2 = gridPositions[j%gridSize]; + int y2 = gridPositions[j/gridSize]; + + offsetX[n] = x1*features.cols*nchannels + y1*nchannels + z; + offsetY[n] = x2*features.cols*nchannels + y2*nchannels + z; + } + // lookup tables for mapping linear index to offset pairs + + for (int i = 0; i < height; ++i) + { + float *regFeaturesPtr = regFeatures.ptr(i*stride/shrink); + float *ssFeaturesPtr = ssFeatures.ptr(i*stride/shrink); + + int *indexPtr = indexes.ptr(i); + + for (int j = 0, k = 0; j < width; ++k, j += !(k %= nTreesEval)) + // for j,k in [0;width)x[0;nTreesEval) + { + int baseNode = ( ((i + j)%(2*nTreesEval) + k)%nTrees )*nTreesNodes; + int currentNode = baseNode; + // select root node of the tree to evaluate + + int offset = (j*stride/shrink)*nchannels; + while ( __rf.childs[currentNode] != 0 ) + { + int currentId = __rf.featureIds[currentNode]; + float currentFeature; + + if (currentId >= nFeatures) + { + int xIndex = offsetX[currentId - nFeatures]; + float A = ssFeaturesPtr[offset + xIndex]; + + int yIndex = offsetY[currentId - nFeatures]; + float B = ssFeaturesPtr[offset + yIndex]; + + currentFeature = A - B; + } + else + currentFeature = regFeaturesPtr[offset + offsetI[currentId]]; + + // compare feature to threshold and move left or right accordingly + if (currentFeature < __rf.thresholds[currentNode]) + currentNode = baseNode + __rf.childs[currentNode] - 1; + else + currentNode = baseNode + __rf.childs[currentNode]; + } + + indexPtr[j*nTreesEval + k] = currentNode; + } + } + + NChannelsMat dstM(dst.size(), + CV_MAKETYPE(DataType::type, outNum)); + dstM.setTo(0); + + float step = 2.0f * CV_SQR(stride) / CV_SQR(ipSize) / nTreesEval; + for (int i = 0; i < height; ++i) + { + int *pIndex = indexes.ptr(i); + float *pDst = dstM.ptr(i*stride); + + for (int j = 0, k = 0; j < width; ++k, j += !(k %= nTreesEval)) + {// for j,k in [0;width)x[0;nTreesEval) + + int currentNode = pIndex[j*nTreesEval + k]; + + int start = __rf.edgeBoundaries[currentNode]; + int finish = __rf.edgeBoundaries[currentNode + 1]; + + if (start == finish) + continue; + + int offset = j*stride*outNum; + for (int p = start; p < finish; ++p) + pDst[offset + offsetE[__rf.edgeBins[p]]] += step; + } + } + + cv::reduce( dstM.reshape(1, int( dstM.total() ) ), dstM, 2, CV_REDUCE_SUM); + imsmooth( dstM.reshape(1, dst.rows), 1 ).copyTo(dst); + } + +/********************* Members *********************/ +protected: + /*! algorithm name */ + String name; + + /*! optional feature getter (getFeatures method) */ + const RFFeatureGetter *howToGetFeatures; + + /*! random forest used to detect edges */ + struct RandomForest + { + /*! random forest options, e.g. number of trees */ + struct RandomForestOptions + { + // model params + + int numberOfOutputChannels; /*!< number of edge orientation bins for output */ + + int patchSize; /*!< width of image patches */ + int patchInnerSize; /*!< width of predicted part inside patch*/ + + // feature params + + int regFeatureSmoothingRadius; /*!< radius for smoothing of regular features + * (using convolution with triangle filter) */ + + int ssFeatureSmoothingRadius; /*!< radius for smoothing of additional features + * (using convolution with triangle filter) */ + + int shrinkNumber; /*!< amount to shrink channels */ + + int numberOfGradientOrientations; /*!< number of orientations per gradient scale */ + + int gradientSmoothingRadius; /*!< radius for smoothing of gradients + * (using convolution with triangle filter) */ + + int gradientNormalizationRadius; /*!< gradient normalization radius */ + int selfsimilarityGridSize; /*!< number of self similarity cells */ + + // detection params + int numberOfTrees; /*!< number of trees in forest to train */ + int numberOfTreesToEvaluate; /*!< number of trees to evaluate per location */ + + int stride; /*!< stride at which to compute edges */ + + } options; + + int numberOfTreeNodes; + + std::vector featureIds; /*!< feature coordinate thresholded at k-th node */ + std::vector thresholds; /*!< threshold applied to featureIds[k] at k-th node */ + std::vector childs; /*!< k --> child[k] - 1, child[k] */ + + std::vector edgeBoundaries; /*!< ... */ + std::vector edgeBins; /*!< ... */ + } __rf; +}; + +Ptr createStructuredEdgeDetection(const String &model, + const RFFeatureGetter *howToGetFeatures) +{ + return makePtr(model, howToGetFeatures); +} + +} +} diff --git a/modules/ximgproc/test/test_main.cpp b/modules/ximgproc/test/test_main.cpp index 17ee2225b..a9bab67d6 100644 --- a/modules/ximgproc/test/test_main.cpp +++ b/modules/ximgproc/test/test_main.cpp @@ -1,3 +1,3 @@ #include "test_precomp.hpp" -CV_TEST_MAIN("") \ No newline at end of file +CV_TEST_MAIN("ximpgroc") diff --git a/modules/ximgproc/test/test_precomp.hpp b/modules/ximgproc/test/test_precomp.hpp index 1d9a8ab52..2804a9fb3 100644 --- a/modules/ximgproc/test/test_precomp.hpp +++ b/modules/ximgproc/test/test_precomp.hpp @@ -9,12 +9,13 @@ #ifndef __OPENCV_TEST_PRECOMP_HPP__ #define __OPENCV_TEST_PRECOMP_HPP__ -#include +#include "opencv2/core.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/imgproc/types_c.h" +#include "opencv2/ximgproc.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/ts.hpp" #include -#include #include -#include -#include -#include -#endif \ No newline at end of file +#endif diff --git a/modules/ximgproc/test/test_structured_edge_detection.cpp b/modules/ximgproc/test/test_structured_edge_detection.cpp new file mode 100644 index 000000000..c022ac54f --- /dev/null +++ b/modules/ximgproc/test/test_structured_edge_detection.cpp @@ -0,0 +1,38 @@ +#include "test_precomp.hpp" + +namespace cvtest +{ + +TEST(ximpgroc_StructuredEdgeDetection, regression) +{ + cv::String dir = cvtest::TS::ptr()->get_data_path(); + int nTests = 12; + float threshold = 0.01f; + + cv::String modelName = dir + "model.yml.gz"; + cv::Ptr pDollar = + cv::ximgproc::createStructuredEdgeDetection(modelName); + + 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 ); + + cv::String previousResultName = dir + cv::format( "results/%02d.png", i + 1 ); + cv::Mat previousResult = cv::imread( previousResultName, 0 ); + previousResult.convertTo( previousResult, cv::DataType::type, 1/255.0 ); + + src.convertTo( src, cv::DataType::type, 1/255.0 ); + + cv::Mat currentResult( src.size(), src.type() ); + pDollar->detectEdges( src, currentResult ); + + cv::Mat sqrError = ( currentResult - previousResult ) + .mul( currentResult - previousResult ); + cv::Scalar mse = cv::sum(sqrError) / cv::Scalar::all( double( sqrError.total() ) ); + + EXPECT_LE( mse[0], threshold ); + } +} + +} \ No newline at end of file diff --git a/modules/ximgproc/testdata/model.yml.gz b/modules/ximgproc/testdata/model.yml.gz new file mode 100644 index 000000000..1a0138a40 Binary files /dev/null and b/modules/ximgproc/testdata/model.yml.gz differ diff --git a/modules/ximgproc/testdata/results/01.png b/modules/ximgproc/testdata/results/01.png new file mode 100644 index 000000000..96ee757be Binary files /dev/null and b/modules/ximgproc/testdata/results/01.png differ diff --git a/modules/ximgproc/testdata/results/02.png b/modules/ximgproc/testdata/results/02.png new file mode 100644 index 000000000..e42244f3d Binary files /dev/null and b/modules/ximgproc/testdata/results/02.png differ diff --git a/modules/ximgproc/testdata/results/03.png b/modules/ximgproc/testdata/results/03.png new file mode 100644 index 000000000..d08f21ff2 Binary files /dev/null and b/modules/ximgproc/testdata/results/03.png differ diff --git a/modules/ximgproc/testdata/results/04.png b/modules/ximgproc/testdata/results/04.png new file mode 100644 index 000000000..5980a8e20 Binary files /dev/null and b/modules/ximgproc/testdata/results/04.png differ diff --git a/modules/ximgproc/testdata/results/05.png b/modules/ximgproc/testdata/results/05.png new file mode 100644 index 000000000..8abc9c2cd Binary files /dev/null and b/modules/ximgproc/testdata/results/05.png differ diff --git a/modules/ximgproc/testdata/results/06.png b/modules/ximgproc/testdata/results/06.png new file mode 100644 index 000000000..7b45eb909 Binary files /dev/null and b/modules/ximgproc/testdata/results/06.png differ diff --git a/modules/ximgproc/testdata/results/07.png b/modules/ximgproc/testdata/results/07.png new file mode 100644 index 000000000..d1796e60c Binary files /dev/null and b/modules/ximgproc/testdata/results/07.png differ diff --git a/modules/ximgproc/testdata/results/08.png b/modules/ximgproc/testdata/results/08.png new file mode 100644 index 000000000..8876820b8 Binary files /dev/null and b/modules/ximgproc/testdata/results/08.png differ diff --git a/modules/ximgproc/testdata/results/09.png b/modules/ximgproc/testdata/results/09.png new file mode 100644 index 000000000..3024c8a7d Binary files /dev/null and b/modules/ximgproc/testdata/results/09.png differ diff --git a/modules/ximgproc/testdata/results/10.png b/modules/ximgproc/testdata/results/10.png new file mode 100644 index 000000000..c84655680 Binary files /dev/null and b/modules/ximgproc/testdata/results/10.png differ diff --git a/modules/ximgproc/testdata/results/11.png b/modules/ximgproc/testdata/results/11.png new file mode 100644 index 000000000..49e7380fd Binary files /dev/null and b/modules/ximgproc/testdata/results/11.png differ diff --git a/modules/ximgproc/testdata/results/12.png b/modules/ximgproc/testdata/results/12.png new file mode 100644 index 000000000..7f229e867 Binary files /dev/null and b/modules/ximgproc/testdata/results/12.png differ diff --git a/modules/ximgproc/testdata/sources/01.png b/modules/ximgproc/testdata/sources/01.png new file mode 100644 index 000000000..0146c435c Binary files /dev/null and b/modules/ximgproc/testdata/sources/01.png differ diff --git a/modules/ximgproc/testdata/sources/02.png b/modules/ximgproc/testdata/sources/02.png new file mode 100644 index 000000000..dc4422a9a Binary files /dev/null and b/modules/ximgproc/testdata/sources/02.png differ diff --git a/modules/ximgproc/testdata/sources/03.png b/modules/ximgproc/testdata/sources/03.png new file mode 100644 index 000000000..180dec65e Binary files /dev/null and b/modules/ximgproc/testdata/sources/03.png differ diff --git a/modules/ximgproc/testdata/sources/04.png b/modules/ximgproc/testdata/sources/04.png new file mode 100644 index 000000000..d7c6478e0 Binary files /dev/null and b/modules/ximgproc/testdata/sources/04.png differ diff --git a/modules/ximgproc/testdata/sources/05.png b/modules/ximgproc/testdata/sources/05.png new file mode 100644 index 000000000..78fc43608 Binary files /dev/null and b/modules/ximgproc/testdata/sources/05.png differ diff --git a/modules/ximgproc/testdata/sources/06.png b/modules/ximgproc/testdata/sources/06.png new file mode 100644 index 000000000..ad79cde4a Binary files /dev/null and b/modules/ximgproc/testdata/sources/06.png differ diff --git a/modules/ximgproc/testdata/sources/07.png b/modules/ximgproc/testdata/sources/07.png new file mode 100644 index 000000000..785014c05 Binary files /dev/null and b/modules/ximgproc/testdata/sources/07.png differ diff --git a/modules/ximgproc/testdata/sources/08.png b/modules/ximgproc/testdata/sources/08.png new file mode 100644 index 000000000..ff46953e7 Binary files /dev/null and b/modules/ximgproc/testdata/sources/08.png differ diff --git a/modules/ximgproc/testdata/sources/09.png b/modules/ximgproc/testdata/sources/09.png new file mode 100644 index 000000000..2156bb511 Binary files /dev/null and b/modules/ximgproc/testdata/sources/09.png differ diff --git a/modules/ximgproc/testdata/sources/10.png b/modules/ximgproc/testdata/sources/10.png new file mode 100644 index 000000000..c5bf64c6a Binary files /dev/null and b/modules/ximgproc/testdata/sources/10.png differ diff --git a/modules/ximgproc/testdata/sources/11.png b/modules/ximgproc/testdata/sources/11.png new file mode 100644 index 000000000..0650efa06 Binary files /dev/null and b/modules/ximgproc/testdata/sources/11.png differ diff --git a/modules/ximgproc/testdata/sources/12.png b/modules/ximgproc/testdata/sources/12.png new file mode 100644 index 000000000..57d850520 Binary files /dev/null and b/modules/ximgproc/testdata/sources/12.png differ diff --git a/modules/xphoto/CMakeLists.txt b/modules/xphoto/CMakeLists.txt new file mode 100644 index 000000000..31d04beb2 --- /dev/null +++ b/modules/xphoto/CMakeLists.txt @@ -0,0 +1,2 @@ +set(the_description "Addon to basic photo module") +ocv_define_module(xphoto opencv_core opencv_imgproc OPTIONAL opencv_photo opencv_highgui opencv_photo) \ No newline at end of file diff --git a/modules/xphoto/doc/colorbalance/whitebalance.rst b/modules/xphoto/doc/colorbalance/whitebalance.rst new file mode 100644 index 000000000..a3ff45f2e --- /dev/null +++ b/modules/xphoto/doc/colorbalance/whitebalance.rst @@ -0,0 +1,25 @@ +Automatic white balance correction +********************************** + +.. highlight:: cpp + +balanceWhite +------------ +.. ocv:function:: void balanceWhite(const Mat &src, Mat &dst, const int algorithmType, const float inputMin = 0.0f, const float inputMax = 255.0f, const float outputMin = 0.0f, const float outputMax = 255.0f) + +The function implements different algorithm of automatic white balance, i.e. +it tries to map image's white color to perceptual white (this can be violated +due to specific illumination or camera settings). + + :param src : source image + :param dst : destination image + :param algorithmType : type of the algorithm to use. Use WHITE_BALANCE_SIMPLE to perform smart histogram adjustments (ignoring 4% pixels with minimal and maximal values) for each channel. + :param inputMin : minimum value in the input image + :param inputMax : maximum value in the input image + :param outputMin : minimum value in the output image + :param outputMax : maximum value in the output image + +.. seealso:: + + :ocv:func:`cvtColor`, + :ocv:func:`equalizeHist` \ No newline at end of file diff --git a/modules/xphoto/doc/denoising/denoising.rst b/modules/xphoto/doc/denoising/denoising.rst new file mode 100644 index 000000000..92f569407 --- /dev/null +++ b/modules/xphoto/doc/denoising/denoising.rst @@ -0,0 +1,20 @@ +Image denoising techniques +************************** + +.. highlight:: cpp + +dctDenoising +------------ +.. ocv:function:: void dctDenoising(const Mat &src, Mat &dst, const float sigma) + +The function implements simple dct-based denoising, +link: 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 + +.. seealso:: + + :ocv:func:`fastNlMeansDenoising` diff --git a/modules/xphoto/doc/inpainting/inpainting.rst b/modules/xphoto/doc/inpainting/inpainting.rst new file mode 100644 index 000000000..1bc654a90 --- /dev/null +++ b/modules/xphoto/doc/inpainting/inpainting.rst @@ -0,0 +1,20 @@ +Single image inpainting +*********************** + +.. highlight:: cpp + +Inpainting +---------- +.. ocv:function:: void inpaint(const Mat &src, const Mat &mask, Mat &dst, const int algorithmType) + +The function implements different single-image inpainting algorithms. + + :param src : source image, 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. + :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 : expected noise standard deviation + * INPAINT_SHIFTMAP: This algorithm searches for dominant correspondences (transformations) of image patches and tries to seamlessly fill-in the area to be inpainted using this transformations. Look in the original paper [He2012]_ for details. + + .. [He2012] K. He, J. Sun., "Statistics of Patch Offsets for Image Completion", + IEEE European Conference on Computer Vision (ICCV), 2012, + pp. 16-29. `DOI `_ \ No newline at end of file diff --git a/modules/xphoto/doc/xphoto.rst b/modules/xphoto/doc/xphoto.rst new file mode 100644 index 000000000..f2dc6a4c3 --- /dev/null +++ b/modules/xphoto/doc/xphoto.rst @@ -0,0 +1,10 @@ +********************************** +xphoto. Addon to basic photo modul +********************************** + +.. toctree:: + :maxdepth: 2 + + Color balance + Denoising + Inpainting diff --git a/modules/xphoto/include/opencv2/xphoto.hpp b/modules/xphoto/include/opencv2/xphoto.hpp new file mode 100644 index 000000000..822a8060f --- /dev/null +++ b/modules/xphoto/include/opencv2/xphoto.hpp @@ -0,0 +1,50 @@ +/*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_EDGEDETECTION_HPP__ +#define __OPENCV_EDGEDETECTION_HPP__ + +#include "opencv2/xphoto.hpp" +#include "opencv2/xphoto/inpainting.hpp" +#include "opencv2/xphoto/simple_color_balance.hpp" +#include "opencv2/xphoto/dct_image_denoising.hpp" +#endif diff --git a/modules/xphoto/include/opencv2/xphoto/dct_image_denoising.hpp b/modules/xphoto/include/opencv2/xphoto/dct_image_denoising.hpp new file mode 100644 index 000000000..5260c52c1 --- /dev/null +++ b/modules/xphoto/include/opencv2/xphoto/dct_image_denoising.hpp @@ -0,0 +1,71 @@ +/*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__ + +/* +* dct_image_denoising.hpp +* +* Created on: Jun 26, 2014 +* Author: Yury Gitman +*/ + +#include + +/*! \namespace cv +Namespace where all the C++ OpenCV functionality resides +*/ +namespace cv +{ + /*! This function implements simple dct-based image denoising, + * link: 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 + */ + CV_EXPORTS_W void dctDenoising(const Mat &src, Mat &dst, const double sigma, const int psize = 16); +} + +#endif // __OPENCV_DCT_IMAGE_DENOISING_HPP__ \ No newline at end of file diff --git a/modules/xphoto/include/opencv2/xphoto/inpainting.hpp b/modules/xphoto/include/opencv2/xphoto/inpainting.hpp new file mode 100644 index 000000000..9863905f8 --- /dev/null +++ b/modules/xphoto/include/opencv2/xphoto/inpainting.hpp @@ -0,0 +1,75 @@ +/*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_INPAINTING_HPP__ +#define __OPENCV_INPAINTING_HPP__ + +/* +* inpainting.hpp +* +* Created on: Jul 22, 2014 +* Author: Yury Gitman +*/ + +#include + +/*! \namespace cv +Namespace where all the C++ OpenCV functionality resides +*/ +namespace cv +{ + //! various inpainting algorithms + enum + { + INPAINT_SHIFTMAP = 0 + }; + + /*! The function reconstructs the selected image area from known area. + * \param src : source image. + * \param mask : inpainting mask, 8-bit 1-channel image. Zero pixels indicate the area that needs to be inpainted. + * \param dst : destination image. + * \param algorithmType : inpainting method. + */ + CV_EXPORTS_W void inpaint(const Mat &src, const Mat &mask, Mat &dst, const int algorithmType); +} + +#endif // __OPENCV_INPAINTING_HPP__ \ No newline at end of file diff --git a/modules/xphoto/include/opencv2/xphoto/simple_color_balance.hpp b/modules/xphoto/include/opencv2/xphoto/simple_color_balance.hpp new file mode 100644 index 000000000..e236eee95 --- /dev/null +++ b/modules/xphoto/include/opencv2/xphoto/simple_color_balance.hpp @@ -0,0 +1,81 @@ +/*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__ + +/* +* simple_color_balance.hpp +* +* Created on: Jun 26, 2014 +* Author: Yury Gitman +*/ + +#include + +/*! \namespace cv +Namespace where all the C++ OpenCV functionality resides +*/ +namespace cv +{ + //! various white balance algorithms + enum + { + WHITE_BALANCE_SIMPLE = 0, + WHITE_BALANCE_GRAYWORLD = 1 + }; + + /*! This function implements different white balance algorithms + * \param src : source image + * \param dst : destination image + * \param algorithmType : type of the algorithm to use + * \param inputMin : minimum input value + * \param inputMax : maximum output value + * \param outputMin : minimum input value + * \param outputMax : maximum output value + */ + CV_EXPORTS_W void balanceWhite(const Mat &src, Mat &dst, const int algorithmType, + const float inputMin = 0.0f, const float inputMax = 255.0f, + const float outputMin = 0.0f, const float outputMax = 255.0f); +} + +#endif // __OPENCV_SIMPLE_COLOR_BALANCE_HPP__ \ No newline at end of file diff --git a/modules/xphoto/samples/dct_image_denoising.cpp b/modules/xphoto/samples/dct_image_denoising.cpp new file mode 100644 index 000000000..7548d2b28 --- /dev/null +++ b/modules/xphoto/samples/dct_image_denoising.cpp @@ -0,0 +1,70 @@ +#include "opencv2/xphoto.hpp" + +#include "opencv2/imgproc.hpp" +#include "opencv2/highgui.hpp" + +#include "opencv2/core/utility.hpp" +#include "opencv2/imgproc/types_c.h" + +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= -sigma= -psize= [-o=]\n\n"); + return 0; + } + + cv::CommandLineParser parser(argc, argv, keys); + if ( !parser.check() ) + { + parser.printErrors(); + return -1; + } + + std::string inFilename = parser.get("i"); + std::string outFilename = parser.get("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("sigma"); + if (sigma == 0.0) + sigma = 15.0; + + int psize = parser.get("psize"); + if (psize == 0) + psize = 16; + + cv::Mat res(src.size(), src.type()); + cv::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; +} \ No newline at end of file diff --git a/modules/xphoto/samples/inpainting.cpp b/modules/xphoto/samples/inpainting.cpp new file mode 100644 index 000000000..ff37f9cca --- /dev/null +++ b/modules/xphoto/samples/inpainting.cpp @@ -0,0 +1,73 @@ +#include "opencv2/xphoto.hpp" + +#include "opencv2/imgproc.hpp" +#include "opencv2/highgui.hpp" + +#include "opencv2/core/utility.hpp" +#include "opencv2/imgproc/types_c.h" + + +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= -m= [-o=]\n\n"); + return 0; + } + + cv::CommandLineParser parser(argc, argv, keys); + if ( !parser.check() ) + { + parser.printErrors(); + return -1; + } + + std::string inFilename = parser.get("i"); + std::string maskFilename = parser.get("m"); + std::string outFilename = parser.get("o"); + + cv::Mat src = cv::imread(inFilename, -1); + if ( src.empty() ) + { + printf( "Cannot read image file: %s\n", inFilename.c_str() ); + return -1; + } + + cv::cvtColor(src, src, CV_RGB2Lab); + + cv::Mat mask = cv::imread(maskFilename, 0); + if ( mask.empty() ) + { + printf( "Cannot read image file: %s\n", maskFilename.c_str() ); + return -1; + } + + cv::Mat res(src.size(), src.type()); + cv::inpaint( src, mask, res, cv::INPAINT_SHIFTMAP ); + cv::cvtColor(res, res, CV_Lab2RGB); + + if ( outFilename == "" ) + { + cv::namedWindow("inpainting result", 1); + cv::imshow("inpainting result", res); + + cv::waitKey(0); + } + else + cv::imwrite(outFilename, res); + + return 0; +} \ No newline at end of file diff --git a/modules/xphoto/samples/simple_color_balance.cpp b/modules/xphoto/samples/simple_color_balance.cpp new file mode 100644 index 000000000..e9b9dbbc5 --- /dev/null +++ b/modules/xphoto/samples/simple_color_balance.cpp @@ -0,0 +1,60 @@ +#include "opencv2/xphoto.hpp" + +#include "opencv2/imgproc.hpp" +#include "opencv2/highgui.hpp" + +#include "opencv2/core/utility.hpp" +#include "opencv2/imgproc/types_c.h" + +const char* keys = +{ + "{i || input 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 simple color balance algorithm\n" + "Call:\n" + " simple_color_blance -i=in_image_name [-o=out_image_name]\n\n"); + return 0; + } + + cv::CommandLineParser parser(argc, argv, keys); + if ( !parser.check() ) + { + parser.printErrors(); + return -1; + } + + std::string inFilename = parser.get("i"); + std::string outFilename = parser.get("o"); + + cv::Mat src = cv::imread(inFilename, 1); + if ( src.empty() ) + { + printf("Cannot read image file: %s\n", inFilename.c_str()); + return -1; + } + + cv::Mat res(src.size(), src.type()); + cv::balanceWhite(src, res, cv::WHITE_BALANCE_SIMPLE); + + if ( outFilename == "" ) + { + cv::namedWindow("after white balance", 1); + cv::imshow("after white balance", res); + + cv::waitKey(0); + } + else + cv::imwrite(outFilename, res); + + return 0; +} \ No newline at end of file diff --git a/modules/xphoto/src/annf.hpp b/modules/xphoto/src/annf.hpp new file mode 100644 index 000000000..38775d07e --- /dev/null +++ b/modules/xphoto/src/annf.hpp @@ -0,0 +1,273 @@ +/*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 "norm2.hpp" +#include "whs.hpp" + +/************************* KDTree class *************************/ + +template void +generate_seq(ForwardIterator it, int first, int last) +{ + for (int i = first; i < last; ++i, ++it) + *it = i; +} + +///////////////////////////////////////////////////// +///////////////////////////////////////////////////// + +template class KDTree +{ +private: + class KDTreeComparator + { + const KDTree *main; // main class + int dimIdx; // dimension to compare + + public: + bool operator () (const int &x, const int &y) const + { + cv::Vec u = main->data[main->idx[x]]; + cv::Vec v = main->data[main->idx[y]]; + + return u[dimIdx] < v[dimIdx]; + } + + KDTreeComparator(const KDTree *_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 > data; + std::vector idx; + std::vector nodes; + + int getMaxSpreadN(const int left, const int right) const; + void operator =(const KDTree &) 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 int KDTree :: +getMaxSpreadN(const int _left, const int _right) const +{ + cv::Vec maxValue = data[ idx[_left] ], + minValue = data[ idx[_left] ]; + for (int i = _left + 1; i < _right; i += cn) + 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 spread = maxValue - minValue; + + Tp *begIt = &spread[0]; + return int(std::max_element(begIt, begIt + cn) - begIt); +} + +template KDTree :: +KDTree(const cv::Mat &img, const int _leafNumber, const int _zeroThresh) + : height(img.rows), width(img.cols), + leafNumber(_leafNumber), zeroThresh(_zeroThresh) +/////////////////////////////////////////////////// +{ + CV_Assert( img.isContinuous() ); + + std::copy( (cv::Vec *) img.data, + (cv::Vec *) img.data + img.total(), + std::back_inserter(data) ); + 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 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::nth_element(/**/ + idx.begin() + _left, + idx.begin() + nth, + idx.begin() + _right, comp + /**/); + + left.push(_left); right.push(nth + 1); + left.push(nth + 1); right.push(_right); + } +} + +template void KDTree :: +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 &transforms, + const int nTransform, const int psize) +{ + /** Walsh-Hadamard Transformation **/ + + std::vector 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 kdTree(whs, 16, 32); + std::vector 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 ::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 = 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_ 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] - int(i))/whs.cols + whs.rows - 1, + (annf[i] - 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::ones() ); + + std::vector < std::pair > amount; + std::vector shiftM; + + for (int i = 0, t = 0; i < annfHist.rows; ++i) + { + double *pAnnfHist = annfHist.ptr(i); + double *_pAnnfHist = _annfHist.ptr(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) ); + } + } + + std::partial_sort( amount.begin(), amount.begin() + nTransform, + amount.end(), std::greater< std::pair >() ); + + transforms.resize(nTransform); + for (int i = 0; i < nTransform; ++i) + { + int idx = amount[i].second; + transforms[i] = cv::Matx33f(1, 0, float(shiftM[idx].x), + 0, 1, float(shiftM[idx].y), + 0, 0, 1 ); + } +} + +#endif /* __ANNF_HPP__ */ diff --git a/modules/xphoto/src/dct_image_denoising.cpp b/modules/xphoto/src/dct_image_denoising.cpp new file mode 100644 index 000000000..0081bc68d --- /dev/null +++ b/modules/xphoto/src/dct_image_denoising.cpp @@ -0,0 +1,181 @@ +/*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 +#include +#include +#include + +#include "opencv2/xphoto.hpp" + +#include "opencv2/imgproc.hpp" + +#include "opencv2/core.hpp" +#include "opencv2/core/core_c.h" + +#include "opencv2/core/types.hpp" +#include "opencv2/core/types_c.h" + +namespace cv +{ + 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 &patches, const double sigma, const int psize); + ~grayDctDenoisingInvoker(){}; + + void operator() (const Range &range) const; + + private: + const Mat &src; + std::vector &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 &_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 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(cvInvSqrt(3), cvInvSqrt(3), cvInvSqrt(3), + cvInvSqrt(2), 0.0f, -cvInvSqrt(2), + cvInvSqrt(6), -2.0f*cvInvSqrt(6), cvInvSqrt(6)); + + cv::transform(src, dst, mt); + + std::vector 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_StsNotImplemented, + ("Unsupported source image format (=%d)", img.type()) ); + + img.convertTo( dst, src.type() ); + } + +} diff --git a/modules/xphoto/src/gcgraph.hpp b/modules/xphoto/src/gcgraph.hpp new file mode 100644 index 000000000..a40fcdf8d --- /dev/null +++ b/modules/xphoto/src/gcgraph.hpp @@ -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 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 vtcs; + ::std::vector edges; + TWeight flow; +}; + +template +GCGraph::GCGraph() +{ + flow = 0; +} +template +GCGraph::GCGraph( unsigned int vtxCount, unsigned int edgeCount ) +{ + create( vtxCount, edgeCount ); +} +template +GCGraph::~GCGraph() +{ +} +template +void GCGraph::create( unsigned int vtxCount, unsigned int edgeCount ) +{ + vtcs.reserve( vtxCount ); + edges.reserve( edgeCount + 2 ); + flow = 0; +} + +template +int GCGraph::addVtx() +{ + Vtx v; + memset( &v, 0, sizeof(Vtx)); + vtcs.push_back(v); + return (int)vtcs.size() - 1; +} + +template +void GCGraph::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 +void GCGraph::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 +TWeight GCGraph::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 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; + uchar 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 = fabs(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 +bool GCGraph::inSourceSegment( int i ) +{ + CV_Assert( i>=0 && i<(int)vtcs.size() ); + return vtcs[i].t == 0; +} + +#endif diff --git a/modules/xphoto/src/inpainting.cpp b/modules/xphoto/src/inpainting.cpp new file mode 100644 index 000000000..f1619d989 --- /dev/null +++ b/modules/xphoto/src/inpainting.cpp @@ -0,0 +1,234 @@ +/*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 +#include +#include +#include +#include +#include +#include +#include + +#include "opencv2/xphoto.hpp" + +#include "opencv2/imgproc.hpp" +#include "opencv2/imgproc/imgproc_c.h" + +#include "opencv2/core.hpp" +#include "opencv2/core/core_c.h" + +#include "opencv2/core/types.hpp" +#include "opencv2/core/types_c.h" + +#include "opencv2/highgui.hpp" + +namespace xphotoInternal +{ +# include "photomontage.hpp" +# include "annf.hpp" +} + +namespace cv +{ + template + static void shiftMapInpaint(const Mat &src, const Mat &mask, Mat &dst, + const int nTransform = 60, const int psize = 8) + { + /** Preparing input **/ + cv::Mat img; + src.convertTo( img, CV_32F ); + img.setTo(0, 255 - mask); + + /** ANNF computation **/ + std::vector transforms( nTransform ); + xphotoInternal::dominantTransforms(img, + transforms, nTransform, psize); + + /** Warping **/ + std::vector images( nTransform + 1 ); // source image transformed with transforms + std::vector masks( nTransform + 1 ); // definition domain for current shift + + Mat_ invMask = 255 - mask; + dilate(invMask, invMask, Mat(), Point(-1,-1), 2); + + img.copyTo( images[0] ); + mask.copyTo( masks[0] ); + + for (int i = 0; i < nTransform; ++i) + { + warpPerspective( images[0], images[i + 1], transforms[i], + images[0].size(), INTER_LINEAR ); + + warpPerspective( masks[0], masks[i + 1], transforms[i], + masks[0].size(), INTER_NEAREST); + masks[i + 1] &= invMask; + } + + /** Stitching **/ + Mat photomontageResult; + xphotoInternal::Photomontage < cv::Vec >( images, masks ) + .assignResImage(photomontageResult); + + /** Writing result **/ + photomontageResult.convertTo( dst, dst.type() ); + } + + template + void inpaint(const Mat &src, const Mat &mask, Mat &dst, const int algorithmType) + { + dst.create( src.size(), src.type() ); + + switch ( algorithmType ) + { + case INPAINT_SHIFTMAP: + shiftMapInpaint (src, mask, dst); + break; + default: + CV_Error_( CV_StsNotImplemented, + ("Unsupported algorithm type (=%d)", algorithmType) ); + break; + } + } + + /*! The function reconstructs the selected image area from known area. + * \param src : source image. + * \param mask : inpainting mask, 8-bit 1-channel image. Zero pixels indicate the area that needs to be inpainted. + * \param dst : destination image. + * \param algorithmType : inpainting method. + */ + void inpaint(const Mat &src, const Mat &mask, Mat &dst, const int algorithmType) + { + CV_Assert( mask.channels() == 1 && mask.depth() == CV_8U ); + CV_Assert( src.rows == mask.rows && src.cols == mask.cols ); + + switch ( src.type() ) + { + case CV_8SC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_8SC2: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_8SC3: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_8SC4: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_8UC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_8UC2: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_8UC3: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_8UC4: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16SC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16SC2: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16SC3: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16SC4: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16UC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16UC2: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16UC3: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16UC4: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_32SC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_32SC2: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_32SC3: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_32SC4: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_32FC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_32FC2: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_32FC3: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_32FC4: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_64FC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_64FC2: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_64FC3: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_64FC4: + inpaint ( src, mask, dst, algorithmType ); + break; + default: + CV_Error_( CV_StsNotImplemented, + ("Unsupported source image format (=%d)", + src.type()) ); + break; + } + } +} diff --git a/modules/xphoto/src/norm2.hpp b/modules/xphoto/src/norm2.hpp new file mode 100644 index 000000000..209295f5d --- /dev/null +++ b/modules/xphoto/src/norm2.hpp @@ -0,0 +1,91 @@ +/*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__ + +/************************ General template *************************/ + +template static inline Tp sqr(Tp x) { return x*x; } + +template static inline Tp sqr( cv::Vec x) { return x.dot(x); } + +template static inline Tp norm2(const Tp &a, const Tp &b) { return sqr(a - b); } + +template static inline +Tp norm2(const cv::Vec &a, const cv::Vec &b) { return sqr(a - b); } + + + +/******************* uchar, char, ushort, uint *********************/ + +static inline int norm2(const uchar &a, const uchar &b) { return sqr(int(a) - int(b)); } + +template static inline + int norm2(const cv::Vec &a, const cv::Vec &b) +{ + return sqr( cv::Vec(a) - cv::Vec(b) ); +} + +static inline int norm2(const char &a, const char &b) { return sqr(int(a) - int(b)); } + +template static inline + int norm2(const cv::Vec &a, const cv::Vec &b) +{ + return sqr( cv::Vec(a) - cv::Vec(b) ); +} + +static inline short norm2(const ushort &a, const ushort &b) { return sqr (short(a) - short(b)); } + +template static inline + short norm2(const cv::Vec &a, const cv::Vec &b) +{ + return sqr( cv::Vec(a) - cv::Vec(b) ); +} + +static inline int norm2(const uint &a, const uint &b) { return sqr(int(a) - int(b)); } + +template static inline + int norm2(const cv::Vec &a, const cv::Vec &b) +{ + return sqr( cv::Vec(a) - cv::Vec(b) ); +} + + +#endif /* __NORM2_HPP__ */ \ No newline at end of file diff --git a/modules/xphoto/src/photomontage.hpp b/modules/xphoto/src/photomontage.hpp new file mode 100644 index 000000000..504e4e14e --- /dev/null +++ b/modules/xphoto/src/photomontage.hpp @@ -0,0 +1,264 @@ +/*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 "norm2.hpp" +#include "gcgraph.hpp" + +#define GCInfinity 10*1000*1000*1000.0 +#define eps 0.02 + +template static int min_idx(std::vector vec) +{ + return int( std::min_element(vec.begin(), vec.end()) - vec.begin() ); +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +template class Photomontage +{ +private: + const std::vector &images; // vector of images for different labels + const std::vector &masks; // vector of definition domains for each image + + std::vector labelings; // vector of labelings for different expansions + std::vector distances; // vector of max-flow costs for different labeling + + const int height; + const int width; + const int type; + const int channels; + const int lsize; + + cv::Mat x_i; // current best labeling + + double singleExpansion(const int alpha); // single neighbor computing + void gradientDescent(); // gradient descent in alpha-expansion topology + + class ParallelExpansion : public cv::ParallelLoopBody + { + public: + Photomontage *main; + + ParallelExpansion(Photomontage *_main) : main(_main){} + ~ParallelExpansion(){}; + + void operator () (const cv::Range &range) const + { + for (int i = range.start; i <= range.end - 1; ++i) + main->distances[i] = main->singleExpansion(i); + } + }; + + void operator =(const Photomontage &) const {}; + +protected: + virtual double dist(const Tp &l1p1, const Tp &l1p2, const Tp &l2p1, const Tp &l2p2); + virtual void setWeights(GCGraph &graph, const cv::Point &pA, + const cv::Point &pB, const int lA, const int lB, const int lX); + +public: + Photomontage(const std::vector &images, const std::vector &masks); + virtual ~Photomontage(){}; + + void assignLabeling(cv::Mat &img); + void assignResImage(cv::Mat &img); +}; + +template inline double Photomontage :: +dist(const Tp &l1p1, const Tp &l1p2, const Tp &l2p1, const Tp &l2p2) +{ + return norm2(l1p1, l2p1) + norm2(l1p2, l2p2); +} + +template void Photomontage :: +setWeights(GCGraph &graph, const cv::Point &pA, const cv::Point &pB, const int lA, const int lB, const int lX) +{ + if (lA == lB) + { + /** Link from A to B **/ + double weightAB = dist( images[lA].template at(pA), + images[lA].template at(pB), + images[lX].template at(pA), + images[lX].template at(pB) ); + graph.addEdges( int(pA.y*width + pA.x), int(pB.y*width + pB.x), weightAB, weightAB); + } + else + { + int X = graph.addVtx(); + + /** Link from X to sink **/ + double weightXS = dist( images[lA].template at(pA), + images[lA].template at(pB), + images[lB].template at(pA), + images[lB].template at(pB) ); + graph.addTermWeights(X, 0, weightXS); + + /** Link from A to X **/ + double weightAX = dist( images[lA].template at(pA), + images[lA].template at(pB), + images[lX].template at(pA), + images[lX].template at(pB) ); + graph.addEdges( int(pA.y*width + pA.x), X, weightAX, weightAX); + + /** Link from X to B **/ + double weightXB = dist( images[lX].template at(pA), + images[lX].template at(pB), + images[lB].template at(pA), + images[lB].template at(pB) ); + graph.addEdges(X, int(pB.y*width + pB.x), weightXB, weightXB); + } +} + +template double Photomontage :: +singleExpansion(const int alpha) +{ + int actualEdges = (height - 1)*width + height*(width - 1); + GCGraph graph(actualEdges + height*width, 2*actualEdges); + + /** Terminal links **/ + for (int i = 0; i < height; ++i) + { + const uchar *maskAlphaRow = masks[alpha].template ptr (i); + const int *labelRow = (const int *) x_i.template ptr (i); + + for (int j = 0; j < width; ++j) + graph.addTermWeights( graph.addVtx(), + maskAlphaRow[j] ? 0 : GCInfinity, + masks[ labelRow[j] ].template at(i, j) ? 0 : GCInfinity ); + } + + /** Neighbor links **/ + for (int i = 0; i < height - 1; ++i) + { + const int *currentRow = (const int *) x_i.template ptr (i); + const int *nextRow = (const int *) x_i.template ptr (i + 1); + + for (int j = 0; j < width - 1; ++j) + { + setWeights( graph, cv::Point(j, i), cv::Point(j + 1, i), currentRow[j], currentRow[j + 1], alpha ); + setWeights( graph, cv::Point(j, i), cv::Point(j, i + 1), currentRow[j], nextRow[j], alpha ); + } + + setWeights( graph, cv::Point(width - 1, i), cv::Point(width - 1, i + 1), + currentRow[width - 1], nextRow[width - 1], alpha ); + } + + const int *currentRow = (const int *) x_i.template ptr (height - 1); + for (int i = 0; i < width - 1; ++i) + setWeights( graph, cv::Point(i, height - 1), cv::Point(i + 1, height - 1), + currentRow[i], currentRow[i + 1], alpha ); + + /** Max-flow computation **/ + double result = graph.maxFlow(); + + /** Writing results **/ + labelings[alpha].create( height, width, CV_32SC1 ); + for (int i = 0; i < height; ++i) + { + const int *inRow = (const int *) x_i.template ptr (i); + int *outRow = (int *) labelings[alpha].template ptr (i); + + for (int j = 0; j < width; ++j) + outRow[j] = graph.inSourceSegment(i*width + j) + ? inRow[j] : alpha; + } + + return result; +} + +template void Photomontage :: +gradientDescent() +{ + double optValue = std::numeric_limits::max(); + + for (int num = -1; /**/; num = -1) + { + parallel_for_( cv::Range(0, lsize), + ParallelExpansion(this) ); + + int minIndex = min_idx(distances); + double minValue = distances[minIndex]; + + if (minValue < (1.00 - eps)*optValue) + optValue = distances[num = minIndex]; + + if (num == -1) + break; + labelings[num].copyTo(x_i); + } +} + +template void Photomontage :: +assignLabeling(cv::Mat &img) +{ + x_i.setTo(0); + gradientDescent(); + x_i.copyTo(img); +} + +template void Photomontage :: +assignResImage(cv::Mat &img) +{ + cv::Mat optimalLabeling; + assignLabeling(optimalLabeling); + + img.create( height, width, type ); + + for (int i = 0; i < height; ++i) + for (int j = 0; j < width; ++j) + { + cv::Mat M = images[optimalLabeling.template at(i, j)]; + img.template at(i, j) = M.template at(i, j); + } +} + +template Photomontage :: +Photomontage(const std::vector &_images, const std::vector &_masks) + : + images(_images), masks(_masks), labelings(images.size()), distances(images.size()), + height(int(images[0].rows)), width(int(images[0].cols)), type(images[0].type()), + channels(images[0].channels()), lsize(int(images.size())), x_i(height, width, CV_32SC1){} + + +#endif /* __PHOTOMONTAGE_HPP__ */ diff --git a/modules/xphoto/src/simple_color_balance.cpp b/modules/xphoto/src/simple_color_balance.cpp new file mode 100644 index 000000000..454b4bacb --- /dev/null +++ b/modules/xphoto/src/simple_color_balance.cpp @@ -0,0 +1,207 @@ +/*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 +#include +#include +#include + +#include "opencv2/xphoto.hpp" + +#include "opencv2/imgproc.hpp" + +#include "opencv2/core.hpp" +#include "opencv2/core/core_c.h" + +#include "opencv2/core/types.hpp" +#include "opencv2/core/types_c.h" + +namespace cv +{ + template + void balanceWhite(std::vector < Mat_ > &src, Mat &dst, + const float inputMin, const float inputMax, + const float outputMin, const float outputMax, const int algorithmType) + { + switch ( algorithmType ) + { + case WHITE_BALANCE_SIMPLE: + { + /********************* Simple white balance *********************/ + float s1 = 2.0f; // low quantile + float s2 = 2.0f; // high quantile + + int depth = 2; // depth of histogram tree + if (src[0].depth() != CV_8U) + ++depth; + int bins = 16; // number of bins at each histogram level + + int nElements = int( pow(bins, depth) ); + // number of elements in histogram tree + + for (size_t i = 0; i < src.size(); ++i) + { + std::vector hist(nElements, 0); + + typename Mat_::iterator beginIt = src[i].begin(); + typename Mat_::iterator endIt = src[i].end(); + + for (typename Mat_::iterator it = beginIt; it != endIt; ++it) + // histogram filling + { + int pos = 0; + float minValue = inputMin - 0.5f; + float maxValue = inputMax + 0.5f; + T val = *it; + + float interval = float(maxValue - minValue) / bins; + + for (int j = 0; j < depth; ++j) + { + int currentBin = int( (val - minValue + 1e-4f) / interval ); + ++hist[pos + currentBin]; + + pos = (pos + currentBin)*bins; + + minValue = minValue + currentBin*interval; + maxValue = minValue + interval; + + interval /= bins; + } + } + + int total = int( src[i].total() ); + + int p1 = 0, p2 = bins - 1; + int n1 = 0, n2 = total; + + float minValue = inputMin - 0.5f; + float maxValue = inputMax + 0.5f; + + float interval = (maxValue - minValue) / float(bins); + + for (int j = 0; j < depth; ++j) + // searching for s1 and s2 + { + while (n1 + hist[p1] < s1 * total / 100.0f) + { + n1 += hist[p1++]; + minValue += interval; + } + p1 *= bins; + + while (n2 - hist[p2] > (100.0f - s2) * total / 100.0f) + { + n2 -= hist[p2--]; + maxValue -= interval; + } + p2 = p2*bins - 1; + + interval /= bins; + } + + src[i] = (outputMax - outputMin) * (src[i] - minValue) + / (maxValue - minValue) + outputMin; + } + /****************************************************************/ + break; + } + default: + CV_Error_( CV_StsNotImplemented, + ("Unsupported algorithm type (=%d)", algorithmType) ); + } + + dst.create(/**/ src[0].size(), CV_MAKETYPE( src[0].depth(), int( src.size() ) ) /**/); + cv::merge(src, dst); + } + + /*! + * Wrappers over different white balance algorithm + * + * \param src : source image (RGB) + * \param dst : destination image + * + * \param inputMin : minimum input value + * \param inputMax : maximum input value + * \param outputMin : minimum output value + * \param outputMax : maximum output value + * + * \param algorithmType : type of the algorithm to use + */ + void balanceWhite(const Mat &src, Mat &dst, const int algorithmType, + const float inputMin, const float inputMax, + const float outputMin, const float outputMax) + { + switch ( src.depth() ) + { + case CV_8U: + { + std::vector < Mat_ > mv; + split(src, mv); + balanceWhite(mv, dst, inputMin, inputMax, outputMin, outputMax, algorithmType); + break; + } + case CV_16S: + { + std::vector < Mat_ > mv; + split(src, mv); + balanceWhite(mv, dst, inputMin, inputMax, outputMin, outputMax, algorithmType); + break; + } + case CV_32S: + { + std::vector < Mat_ > mv; + split(src, mv); + balanceWhite(mv, dst, inputMin, inputMax, outputMin, outputMax, algorithmType); + break; + } + case CV_32F: + { + std::vector < Mat_ > mv; + split(src, mv); + balanceWhite(mv, dst, inputMin, inputMax, outputMin, outputMax, algorithmType); + break; + } + default: + CV_Error_( CV_StsNotImplemented, + ("Unsupported source image format (=%d)", src.type()) ); + break; + } + } +} diff --git a/modules/xphoto/src/whs.hpp b/modules/xphoto/src/whs.hpp new file mode 100644 index 000000000..825166379 --- /dev/null +++ b/modules/xphoto/src/whs.hpp @@ -0,0 +1,145 @@ +/*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 +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 &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(i - dy); + float *vNext = nproj.ptr(i); + + float *vxCurrent = proj.ptr(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 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 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); + + cv::merge(projections, img); + img(cv::Rect(npsize, npsize, src.cols, src.rows)).copyTo(dst); +} + + +#endif /* __WHS_HPP__ */ diff --git a/modules/xphoto/test/dct_image_denoising.cpp b/modules/xphoto/test/dct_image_denoising.cpp new file mode 100644 index 000000000..d2e1d8baa --- /dev/null +++ b/modules/xphoto/test/dct_image_denoising.cpp @@ -0,0 +1,34 @@ +#include "test_precomp.hpp" + +namespace cvtest +{ + TEST(xphoto_dctimagedenoising, regression) + { + cv::String dir = cvtest::TS::ptr()->get_data_path() + "dct_image_denoising/"; + int nTests = 1; + + double thresholds[] = {0.1}; + + 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 ); + + cv::String previousResultName = dir + cv::format( "results/%02d.png", i + 1 ); + cv::Mat previousResult = cv::imread( previousResultName, 1 ); + + cv::Mat currentResult, fastNlMeansResult; + + cv::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] ); + } + } +} \ No newline at end of file diff --git a/modules/xphoto/test/simple_color_balance.cpp b/modules/xphoto/test/simple_color_balance.cpp new file mode 100644 index 000000000..449a0c459 --- /dev/null +++ b/modules/xphoto/test/simple_color_balance.cpp @@ -0,0 +1,29 @@ +#include "test_precomp.hpp" + +namespace cvtest +{ + TEST(xphoto_simplecolorbalance, regression) + { + cv::String dir = cvtest::TS::ptr()->get_data_path() + "simple_white_balance/"; + int nTests = 12; + float threshold = 0.005f; + + 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 ); + + cv::String previousResultName = dir + cv::format( "results/%02d.png", i + 1 ); + cv::Mat previousResult = cv::imread( previousResultName, 1 ); + + cv::Mat currentResult; + cv::balanceWhite(src, currentResult, cv::WHITE_BALANCE_SIMPLE); + + 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], threshold ); + } + } +} \ No newline at end of file diff --git a/modules/xphoto/test/test_main.cpp b/modules/xphoto/test/test_main.cpp new file mode 100644 index 000000000..7b7c1ab30 --- /dev/null +++ b/modules/xphoto/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("xphoto") diff --git a/modules/xphoto/test/test_precomp.hpp b/modules/xphoto/test/test_precomp.hpp new file mode 100644 index 000000000..5cd8e80ce --- /dev/null +++ b/modules/xphoto/test/test_precomp.hpp @@ -0,0 +1,23 @@ +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wmissing-declarations" +# if defined __clang__ || defined __APPLE__ +# pragma GCC diagnostic ignored "-Wmissing-prototypes" +# pragma GCC diagnostic ignored "-Wextra" +# endif +#endif + +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/core.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/imgproc/types_c.h" +#include "opencv2/highgui.hpp" +#include "opencv2/photo.hpp" +#include "opencv2/xphoto.hpp" +#include "opencv2/ts.hpp" + +#include +#include + +#endif diff --git a/modules/xphoto/testdata/dct_image_denoising/results/01.png b/modules/xphoto/testdata/dct_image_denoising/results/01.png new file mode 100644 index 000000000..c8e8d407f Binary files /dev/null and b/modules/xphoto/testdata/dct_image_denoising/results/01.png differ diff --git a/modules/xphoto/testdata/dct_image_denoising/sources/01.png b/modules/xphoto/testdata/dct_image_denoising/sources/01.png new file mode 100644 index 000000000..a7ac74db8 Binary files /dev/null and b/modules/xphoto/testdata/dct_image_denoising/sources/01.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/01.png b/modules/xphoto/testdata/simple_white_balance/results/01.png new file mode 100644 index 000000000..0e35a9875 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/01.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/02.png b/modules/xphoto/testdata/simple_white_balance/results/02.png new file mode 100644 index 000000000..9bcbf295b Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/02.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/03.png b/modules/xphoto/testdata/simple_white_balance/results/03.png new file mode 100644 index 000000000..c92e59b68 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/03.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/04.png b/modules/xphoto/testdata/simple_white_balance/results/04.png new file mode 100644 index 000000000..3df7fa933 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/04.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/05.png b/modules/xphoto/testdata/simple_white_balance/results/05.png new file mode 100644 index 000000000..50c51aac5 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/05.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/06.png b/modules/xphoto/testdata/simple_white_balance/results/06.png new file mode 100644 index 000000000..130e980a4 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/06.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/07.png b/modules/xphoto/testdata/simple_white_balance/results/07.png new file mode 100644 index 000000000..256811d36 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/07.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/08.png b/modules/xphoto/testdata/simple_white_balance/results/08.png new file mode 100644 index 000000000..afa68b0f5 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/08.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/09.png b/modules/xphoto/testdata/simple_white_balance/results/09.png new file mode 100644 index 000000000..afa68b0f5 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/09.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/10.png b/modules/xphoto/testdata/simple_white_balance/results/10.png new file mode 100644 index 000000000..7c7378694 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/10.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/11.png b/modules/xphoto/testdata/simple_white_balance/results/11.png new file mode 100644 index 000000000..99919996f Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/11.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/12.png b/modules/xphoto/testdata/simple_white_balance/results/12.png new file mode 100644 index 000000000..4f80234da Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/12.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/13.png b/modules/xphoto/testdata/simple_white_balance/results/13.png new file mode 100644 index 000000000..6d21a0dd3 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/13.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/results/14.png b/modules/xphoto/testdata/simple_white_balance/results/14.png new file mode 100644 index 000000000..4d31aa351 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/results/14.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/01.png b/modules/xphoto/testdata/simple_white_balance/sources/01.png new file mode 100644 index 000000000..d44be3af3 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/01.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/02.png b/modules/xphoto/testdata/simple_white_balance/sources/02.png new file mode 100644 index 000000000..723e2124a Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/02.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/03.png b/modules/xphoto/testdata/simple_white_balance/sources/03.png new file mode 100644 index 000000000..eeecf07c0 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/03.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/04.png b/modules/xphoto/testdata/simple_white_balance/sources/04.png new file mode 100644 index 000000000..096a79d6d Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/04.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/05.png b/modules/xphoto/testdata/simple_white_balance/sources/05.png new file mode 100644 index 000000000..a23c632c1 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/05.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/06.png b/modules/xphoto/testdata/simple_white_balance/sources/06.png new file mode 100644 index 000000000..3bf87ef31 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/06.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/07.png b/modules/xphoto/testdata/simple_white_balance/sources/07.png new file mode 100644 index 000000000..448a86660 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/07.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/08.png b/modules/xphoto/testdata/simple_white_balance/sources/08.png new file mode 100644 index 000000000..5b4cd8008 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/08.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/09.png b/modules/xphoto/testdata/simple_white_balance/sources/09.png new file mode 100644 index 000000000..5b4cd8008 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/09.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/10.png b/modules/xphoto/testdata/simple_white_balance/sources/10.png new file mode 100644 index 000000000..e9e10baba Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/10.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/11.png b/modules/xphoto/testdata/simple_white_balance/sources/11.png new file mode 100644 index 000000000..481059c55 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/11.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/12.png b/modules/xphoto/testdata/simple_white_balance/sources/12.png new file mode 100644 index 000000000..595cdef14 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/12.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/13.png b/modules/xphoto/testdata/simple_white_balance/sources/13.png new file mode 100644 index 000000000..226c7a796 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/13.png differ diff --git a/modules/xphoto/testdata/simple_white_balance/sources/14.png b/modules/xphoto/testdata/simple_white_balance/sources/14.png new file mode 100644 index 000000000..6374d4232 Binary files /dev/null and b/modules/xphoto/testdata/simple_white_balance/sources/14.png differ diff --git a/modules/xphoto/testdata/x.png b/modules/xphoto/testdata/x.png new file mode 100644 index 000000000..e69de29bb