diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index e72c70d8c3d..595ce5c29f3 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,15 +1,8 @@ - - ##### System information (version) - OpenCV => :grey_question: @@ -27,4 +20,44 @@ This is a template helping you to create an issue which can be processed as quic // C++ code example ``` or attach as .txt or .zip file ---> \ No newline at end of file +--> + +##### Issue submission checklist + + - [ ] I report the issue, it's not a question + + - [ ] I checked the problem with documentation, FAQ, open issues, + answers.opencv.org, Stack Overflow, etc and have not found solution + + - [ ] I updated to latest OpenCV version and the issue is still there + + - [ ] There is reproducer code and related data files: videos, images, onnx, etc + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 210a253113c..d461389b198 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,9 +1,11 @@ - +### Pull Request Readiness Checklist -### This pullrequest changes +See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - +- [ ] I agree to contribute to the project under OpenCV (BSD) License. +- [ ] To the best of my knowledge, the proposed patch is not based on a code under GPL or other license that is incompatible with OpenCV +- [ ] The PR is proposed to proper branch +- [ ] There is reference to original bug report and related work +- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable + Patch to opencv_extra has the same branch name. +- [ ] The feature is well documented and sample code can be built with the project CMake diff --git a/.travis.yml b/.travis.yml index de69f1a5a39..815fcc6c4c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ compiler: - clang before_script: - cd ../ - - git clone --branch 3.4 --depth=1 https://github.com/opencv/opencv.git + - git clone --branch master --depth=1 https://github.com/opencv/opencv.git - mkdir build-opencv - cd build-opencv - cmake diff --git a/README.md b/README.md index 4e46b9af07e..5d0efe8e21f 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ use CMake's `BUILD_opencv_*` options. Like in this example: $ cmake -DOPENCV_EXTRA_MODULES_PATH=/modules -DBUILD_opencv_legacy=OFF ``` +If you also want to build the samples from the "samples" folder of each module, also include the "-DBUILD_EXAMPLES=ON" option. + If you prefer using the gui version of cmake (cmake-gui), then, you can add `opencv_contrib` modules within `opencv` core by doing the following: 1. start cmake-gui diff --git a/modules/README.md b/modules/README.md index 6d51a4db49d..b333043277b 100644 --- a/modules/README.md +++ b/modules/README.md @@ -10,6 +10,8 @@ $ cmake -D OPENCV_EXTRA_MODULES_PATH=/modules -D BUILD_opencv_/modules -D BUILD_opencv_/modules -D BUILD_opencv_ +#include "opencv2/highgui.hpp" +#include +#include +#include + +using namespace std; +using namespace cv; +using namespace cv::alphamat; + +const char* keys = + "{img || input image name}" + "{tri || input trimap image name}" + "{out || output image name}" + "{help h || print help message}" +; + +int main(int argc, char* argv[]) +{ + CommandLineParser parser(argc, argv, keys); + parser.about("This sample demonstrates Information Flow Alpha Matting"); + + if (parser.has("help")) + { + parser.printMessage(); + return 0; + } + + string img_path = parser.get("img"); + string trimap_path = parser.get("tri"); + string result_path = parser.get("out"); + + if (!parser.check() + || img_path.empty() || trimap_path.empty()) + { + parser.printMessage(); + parser.printErrors(); + return 1; + } + + Mat image, tmap; + + image = imread(img_path, IMREAD_COLOR); // Read the input image file + if (image.empty()) + { + printf("Cannot read image file: '%s'\n", img_path.c_str()); + return 1; + } + + tmap = imread(trimap_path, IMREAD_GRAYSCALE); + if (tmap.empty()) + { + printf("Cannot read trimap file: '%s'\n", trimap_path.c_str()); + return 1; + } + + Mat result; + infoFlow(image, tmap, result); + + if (result_path.empty()) + { + namedWindow("result alpha matte", WINDOW_NORMAL); + imshow("result alpha matte", result); + waitKey(0); + } + else + { + imwrite(result_path, result); + printf("Result saved: '%s'\n", result_path.c_str()); + } + + return 0; +} diff --git a/modules/alphamat/samples/input_images/plant.jpg b/modules/alphamat/samples/input_images/plant.jpg new file mode 100644 index 00000000000..c6f30953397 Binary files /dev/null and b/modules/alphamat/samples/input_images/plant.jpg differ diff --git a/modules/alphamat/samples/output_mattes/plant_result.jpg b/modules/alphamat/samples/output_mattes/plant_result.jpg new file mode 100644 index 00000000000..4ec7e29c6b0 Binary files /dev/null and b/modules/alphamat/samples/output_mattes/plant_result.jpg differ diff --git a/modules/alphamat/samples/trimaps/plant.png b/modules/alphamat/samples/trimaps/plant.png new file mode 100755 index 00000000000..6c646b9192d Binary files /dev/null and b/modules/alphamat/samples/trimaps/plant.png differ diff --git a/modules/alphamat/src/3rdparty/KDTreeVectorOfVectorsAdaptor.h b/modules/alphamat/src/3rdparty/KDTreeVectorOfVectorsAdaptor.h new file mode 100644 index 00000000000..893aae70ce6 --- /dev/null +++ b/modules/alphamat/src/3rdparty/KDTreeVectorOfVectorsAdaptor.h @@ -0,0 +1,117 @@ +/*********************************************************************** + * Software License Agreement (BSD License) + * + * Copyright 2011-16 Jose Luis Blanco (joseluisblancoc@gmail.com). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + *************************************************************************/ + +#pragma once + +#include "nanoflann.hpp" + +#include + +// ===== This example shows how to use nanoflann with these types of containers: ======= +//typedef std::vector > my_vector_of_vectors_t; +//typedef std::vector my_vector_of_vectors_t; // This requires #include +// ===================================================================================== + + +/** A simple vector-of-vectors adaptor for nanoflann, without duplicating the storage. + * The i'th vector represents a point in the state space. + * + * \tparam DIM If set to >0, it specifies a compile-time fixed dimensionality for the points in the data set, allowing more compiler optimizations. + * \tparam num_t The type of the point coordinates (typically, double or float). + * \tparam Distance The distance metric to use: nanoflann::metric_L1, nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. + * \tparam IndexType The type for indices in the KD-tree index (typically, size_t of int) + */ +template +struct KDTreeVectorOfVectorsAdaptor +{ + typedef KDTreeVectorOfVectorsAdaptor self_t; + typedef typename Distance::template traits::distance_t metric_t; + typedef nanoflann::KDTreeSingleIndexAdaptor< metric_t, self_t, DIM, IndexType> index_t; + + index_t* index; //! The kd-tree index for the user to call its methods as usual with any other FLANN index. + + /// Constructor: takes a const ref to the vector of vectors object with the data points + KDTreeVectorOfVectorsAdaptor(const size_t /* dimensionality */, const VectorOfVectorsType &mat, const int leaf_max_size = 10) : m_data(mat) + { + assert(mat.size() != 0 && mat[0].size() != 0); + const size_t dims = mat[0].size(); + if (DIM>0 && static_cast(dims) != DIM) + throw std::runtime_error("Data set dimensionality does not match the 'DIM' template argument"); + index = new index_t( static_cast(dims), *this /* adaptor */, nanoflann::KDTreeSingleIndexAdaptorParams(leaf_max_size ) ); + index->buildIndex(); + } + + ~KDTreeVectorOfVectorsAdaptor() { + delete index; + } + + const VectorOfVectorsType &m_data; + + /** Query for the \a num_closest closest points to a given point (entered as query_point[0:dim-1]). + * Note that this is a short-cut method for index->findNeighbors(). + * The user can also call index->... methods as desired. + * \note nChecks_IGNORED is ignored but kept for compatibility with the original FLANN interface. + */ + //inline void query(const num_t *query_point, const size_t num_closest, IndexType *out_indices, num_t *out_distances_sq, const int nChecks_IGNORED = 10) const + inline void query(const num_t *query_point, const size_t num_closest, IndexType *out_indices, num_t *out_distances_sq) const + { + nanoflann::KNNResultSet resultSet(num_closest); + resultSet.init(out_indices, out_distances_sq); + index->findNeighbors(resultSet, query_point, nanoflann::SearchParams()); + } + + /** @name Interface expected by KDTreeSingleIndexAdaptor + * @{ */ + + const self_t & derived() const { + return *this; + } + self_t & derived() { + return *this; + } + + // Must return the number of data points + inline size_t kdtree_get_point_count() const { + return m_data.size(); + } + + // Returns the dim'th component of the idx'th point in the class: + inline num_t kdtree_get_pt(const size_t idx, const size_t dim) const { + return m_data[idx][dim]; + } + + // Optional bounding-box computation: return false to default to a standard bbox computation loop. + // Return true if the BBOX was already computed by the class and returned in "bb" so it can be avoided to redo it again. + // Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 for point clouds) + template + bool kdtree_get_bbox(BBOX & /*bb*/) const { + return false; + } + + /** @} */ +}; // end of KDTreeVectorOfVectorsAdaptor diff --git a/modules/alphamat/src/3rdparty/nanoflann.hpp b/modules/alphamat/src/3rdparty/nanoflann.hpp new file mode 100644 index 00000000000..a8e4667dda6 --- /dev/null +++ b/modules/alphamat/src/3rdparty/nanoflann.hpp @@ -0,0 +1,2040 @@ +/*********************************************************************** + * Software License Agreement (BSD License) + * + * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. + * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. + * Copyright 2011-2016 Jose Luis Blanco (joseluisblancoc@gmail.com). + * All rights reserved. + * + * THE BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + *************************************************************************/ + +/** \mainpage nanoflann C++ API documentation + * nanoflann is a C++ header-only library for building KD-Trees, mostly + * optimized for 2D or 3D point clouds. + * + * nanoflann does not require compiling or installing, just an + * #include in your code. + * + * See: + * - C++ API organized by modules + * - Online README + * - Doxygen + * documentation + */ + +#ifndef NANOFLANN_HPP_ +#define NANOFLANN_HPP_ + +#include +#include +#include +#include // for abs() +#include // for fwrite() +#include // for abs() +#include +#include // std::reference_wrapper +#include +#include + +/** Library version: 0xMmP (M=Major,m=minor,P=patch) */ +#define NANOFLANN_VERSION 0x132 + +// Avoid conflicting declaration of min/max macros in windows headers +#if !defined(NOMINMAX) && \ + (defined(_WIN32) || defined(_WIN32_) || defined(WIN32) || defined(_WIN64)) +#define NOMINMAX +#ifdef max +#undef max +#undef min +#endif +#endif + +namespace nanoflann { +/** @addtogroup nanoflann_grp nanoflann C++ library for ANN + * @{ */ + +/** the PI constant (required to avoid MSVC missing symbols) */ +template T pi_const() { + return static_cast(3.14159265358979323846); +} + +/** + * Traits if object is resizable and assignable (typically has a resize | assign + * method) + */ +template struct has_resize : std::false_type {}; + +template +struct has_resize().resize(1), 0)> + : std::true_type {}; + +template struct has_assign : std::false_type {}; + +template +struct has_assign().assign(1, 0), 0)> + : std::true_type {}; + +/** + * Free function to resize a resizable object + */ +template +inline typename std::enable_if::value, void>::type +resize(Container &c, const size_t nElements) { + c.resize(nElements); +} + +/** + * Free function that has no effects on non resizable containers (e.g. + * std::array) It raises an exception if the expected size does not match + */ +template +inline typename std::enable_if::value, void>::type +resize(Container &c, const size_t nElements) { + if (nElements != c.size()) + throw std::logic_error("Try to change the size of a std::array."); +} + +/** + * Free function to assign to a container + */ +template +inline typename std::enable_if::value, void>::type +assign(Container &c, const size_t nElements, const T &value) { + c.assign(nElements, value); +} + +/** + * Free function to assign to a std::array + */ +template +inline typename std::enable_if::value, void>::type +assign(Container &c, const size_t nElements, const T &value) { + for (size_t i = 0; i < nElements; i++) + c[i] = value; +} + +/** @addtogroup result_sets_grp Result set classes + * @{ */ +template +class KNNResultSet { +public: + typedef _DistanceType DistanceType; + typedef _IndexType IndexType; + typedef _CountType CountType; + +private: + IndexType *indices; + DistanceType *dists; + CountType capacity; + CountType count; + +public: + inline KNNResultSet(CountType capacity_) + : indices(0), dists(0), capacity(capacity_), count(0) {} + + inline void init(IndexType *indices_, DistanceType *dists_) { + indices = indices_; + dists = dists_; + count = 0; + if (capacity) + dists[capacity - 1] = (std::numeric_limits::max)(); + } + + inline CountType size() const { return count; } + + inline bool full() const { return count == capacity; } + + /** + * Called during search to add an element matching the criteria. + * @return true if the search should be continued, false if the results are + * sufficient + */ + inline bool addPoint(DistanceType dist, IndexType index) { + CountType i; + for (i = count; i > 0; --i) { +#ifdef NANOFLANN_FIRST_MATCH // If defined and two points have the same + // distance, the one with the lowest-index will be + // returned first. + if ((dists[i - 1] > dist) || + ((dist == dists[i - 1]) && (indices[i - 1] > index))) { +#else + if (dists[i - 1] > dist) { +#endif + if (i < capacity) { + dists[i] = dists[i - 1]; + indices[i] = indices[i - 1]; + } + } else + break; + } + if (i < capacity) { + dists[i] = dist; + indices[i] = index; + } + if (count < capacity) + count++; + + // tell caller that the search shall continue + return true; + } + + inline DistanceType worstDist() const { return dists[capacity - 1]; } +}; + +/** operator "<" for std::sort() */ +struct IndexDist_Sorter { + /** PairType will be typically: std::pair */ + template + inline bool operator()(const PairType &p1, const PairType &p2) const { + return p1.second < p2.second; + } +}; + +/** + * A result-set class used when performing a radius based search. + */ +template +class RadiusResultSet { +public: + typedef _DistanceType DistanceType; + typedef _IndexType IndexType; + +public: + const DistanceType radius; + + std::vector> &m_indices_dists; + + inline RadiusResultSet( + DistanceType radius_, + std::vector> &indices_dists) + : radius(radius_), m_indices_dists(indices_dists) { + init(); + } + + inline void init() { clear(); } + inline void clear() { m_indices_dists.clear(); } + + inline size_t size() const { return m_indices_dists.size(); } + + inline bool full() const { return true; } + + /** + * Called during search to add an element matching the criteria. + * @return true if the search should be continued, false if the results are + * sufficient + */ + inline bool addPoint(DistanceType dist, IndexType index) { + if (dist < radius) + m_indices_dists.push_back(std::make_pair(index, dist)); + return true; + } + + inline DistanceType worstDist() const { return radius; } + + /** + * Find the worst result (furtherest neighbor) without copying or sorting + * Pre-conditions: size() > 0 + */ + std::pair worst_item() const { + if (m_indices_dists.empty()) + throw std::runtime_error("Cannot invoke RadiusResultSet::worst_item() on " + "an empty list of results."); + typedef + typename std::vector>::const_iterator + DistIt; + DistIt it = std::max_element(m_indices_dists.begin(), m_indices_dists.end(), + IndexDist_Sorter()); + return *it; + } +}; + +/** @} */ + +/** @addtogroup loadsave_grp Load/save auxiliary functions + * @{ */ +template +void save_value(FILE *stream, const T &value, size_t count = 1) { + fwrite(&value, sizeof(value), count, stream); +} + +template +void save_value(FILE *stream, const std::vector &value) { + size_t size = value.size(); + fwrite(&size, sizeof(size_t), 1, stream); + fwrite(&value[0], sizeof(T), size, stream); +} + +template +void load_value(FILE *stream, T &value, size_t count = 1) { + size_t read_cnt = fread(&value, sizeof(value), count, stream); + if (read_cnt != count) { + throw std::runtime_error("Cannot read from file"); + } +} + +template void load_value(FILE *stream, std::vector &value) { + size_t size; + size_t read_cnt = fread(&size, sizeof(size_t), 1, stream); + if (read_cnt != 1) { + throw std::runtime_error("Cannot read from file"); + } + value.resize(size); + read_cnt = fread(&value[0], sizeof(T), size, stream); + if (read_cnt != size) { + throw std::runtime_error("Cannot read from file"); + } +} +/** @} */ + +/** @addtogroup metric_grp Metric (distance) classes + * @{ */ + +struct Metric {}; + +/** Manhattan distance functor (generic version, optimized for + * high-dimensionality data sets). Corresponding distance traits: + * nanoflann::metric_L1 \tparam T Type of the elements (e.g. double, float, + * uint8_t) \tparam _DistanceType Type of distance variables (must be signed) + * (e.g. float, double, int64_t) + */ +template +struct L1_Adaptor { + typedef T ElementType; + typedef _DistanceType DistanceType; + + const DataSource &data_source; + + L1_Adaptor(const DataSource &_data_source) : data_source(_data_source) {} + + inline DistanceType evalMetric(const T *a, const size_t b_idx, size_t size, + DistanceType worst_dist = -1) const { + DistanceType result = DistanceType(); + const T *last = a + size; + const T *lastgroup = last - 3; + size_t d = 0; + + /* Process 4 items with each loop for efficiency. */ + while (a < lastgroup) { + const DistanceType diff0 = + std::abs(a[0] - data_source.kdtree_get_pt(b_idx, d++)); + const DistanceType diff1 = + std::abs(a[1] - data_source.kdtree_get_pt(b_idx, d++)); + const DistanceType diff2 = + std::abs(a[2] - data_source.kdtree_get_pt(b_idx, d++)); + const DistanceType diff3 = + std::abs(a[3] - data_source.kdtree_get_pt(b_idx, d++)); + result += diff0 + diff1 + diff2 + diff3; + a += 4; + if ((worst_dist > 0) && (result > worst_dist)) { + return result; + } + } + /* Process last 0-3 components. Not needed for standard vector lengths. */ + while (a < last) { + result += std::abs(*a++ - data_source.kdtree_get_pt(b_idx, d++)); + } + return result; + } + + template + inline DistanceType accum_dist(const U a, const V b, const size_t) const { + return std::abs(a - b); + } +}; + +/** Squared Euclidean distance functor (generic version, optimized for + * high-dimensionality data sets). Corresponding distance traits: + * nanoflann::metric_L2 \tparam T Type of the elements (e.g. double, float, + * uint8_t) \tparam _DistanceType Type of distance variables (must be signed) + * (e.g. float, double, int64_t) + */ +template +struct L2_Adaptor { + typedef T ElementType; + typedef _DistanceType DistanceType; + + const DataSource &data_source; + + L2_Adaptor(const DataSource &_data_source) : data_source(_data_source) {} + + inline DistanceType evalMetric(const T *a, const size_t b_idx, size_t size, + DistanceType worst_dist = -1) const { + DistanceType result = DistanceType(); + const T *last = a + size; + const T *lastgroup = last - 3; + size_t d = 0; + + /* Process 4 items with each loop for efficiency. */ + while (a < lastgroup) { + const DistanceType diff0 = a[0] - data_source.kdtree_get_pt(b_idx, d++); + const DistanceType diff1 = a[1] - data_source.kdtree_get_pt(b_idx, d++); + const DistanceType diff2 = a[2] - data_source.kdtree_get_pt(b_idx, d++); + const DistanceType diff3 = a[3] - data_source.kdtree_get_pt(b_idx, d++); + result += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; + a += 4; + if ((worst_dist > 0) && (result > worst_dist)) { + return result; + } + } + /* Process last 0-3 components. Not needed for standard vector lengths. */ + while (a < last) { + const DistanceType diff0 = *a++ - data_source.kdtree_get_pt(b_idx, d++); + result += diff0 * diff0; + } + return result; + } + + template + inline DistanceType accum_dist(const U a, const V b, const size_t) const { + return (a - b) * (a - b); + } +}; + +/** Squared Euclidean (L2) distance functor (suitable for low-dimensionality + * datasets, like 2D or 3D point clouds) Corresponding distance traits: + * nanoflann::metric_L2_Simple \tparam T Type of the elements (e.g. double, + * float, uint8_t) \tparam _DistanceType Type of distance variables (must be + * signed) (e.g. float, double, int64_t) + */ +template +struct L2_Simple_Adaptor { + typedef T ElementType; + typedef _DistanceType DistanceType; + + const DataSource &data_source; + + L2_Simple_Adaptor(const DataSource &_data_source) + : data_source(_data_source) {} + + inline DistanceType evalMetric(const T *a, const size_t b_idx, + size_t size) const { + DistanceType result = DistanceType(); + for (size_t i = 0; i < size; ++i) { + const DistanceType diff = a[i] - data_source.kdtree_get_pt(b_idx, i); + result += diff * diff; + } + return result; + } + + template + inline DistanceType accum_dist(const U a, const V b, const size_t) const { + return (a - b) * (a - b); + } +}; + +/** SO2 distance functor + * Corresponding distance traits: nanoflann::metric_SO2 + * \tparam T Type of the elements (e.g. double, float) + * \tparam _DistanceType Type of distance variables (must be signed) (e.g. + * float, double) orientation is constrained to be in [-pi, pi] + */ +template +struct SO2_Adaptor { + typedef T ElementType; + typedef _DistanceType DistanceType; + + const DataSource &data_source; + + SO2_Adaptor(const DataSource &_data_source) : data_source(_data_source) {} + + inline DistanceType evalMetric(const T *a, const size_t b_idx, + size_t size) const { + return accum_dist(a[size - 1], data_source.kdtree_get_pt(b_idx, size - 1), + size - 1); + } + + /** Note: this assumes that input angles are already in the range [-pi,pi] */ + template + inline DistanceType accum_dist(const U a, const V b, const size_t) const { + DistanceType result = DistanceType(); + DistanceType PI = pi_const(); + result = b - a; + if (result > PI) + result -= 2 * PI; + else if (result < -PI) + result += 2 * PI; + return result; + } +}; + +/** SO3 distance functor (Uses L2_Simple) + * Corresponding distance traits: nanoflann::metric_SO3 + * \tparam T Type of the elements (e.g. double, float) + * \tparam _DistanceType Type of distance variables (must be signed) (e.g. + * float, double) + */ +template +struct SO3_Adaptor { + typedef T ElementType; + typedef _DistanceType DistanceType; + + L2_Simple_Adaptor distance_L2_Simple; + + SO3_Adaptor(const DataSource &_data_source) + : distance_L2_Simple(_data_source) {} + + inline DistanceType evalMetric(const T *a, const size_t b_idx, + size_t size) const { + return distance_L2_Simple.evalMetric(a, b_idx, size); + } + + template + inline DistanceType accum_dist(const U a, const V b, const size_t idx) const { + return distance_L2_Simple.accum_dist(a, b, idx); + } +}; + +/** Metaprogramming helper traits class for the L1 (Manhattan) metric */ +struct metric_L1 : public Metric { + template struct traits { + typedef L1_Adaptor distance_t; + }; +}; +/** Metaprogramming helper traits class for the L2 (Euclidean) metric */ +struct metric_L2 : public Metric { + template struct traits { + typedef L2_Adaptor distance_t; + }; +}; +/** Metaprogramming helper traits class for the L2_simple (Euclidean) metric */ +struct metric_L2_Simple : public Metric { + template struct traits { + typedef L2_Simple_Adaptor distance_t; + }; +}; +/** Metaprogramming helper traits class for the SO3_InnerProdQuat metric */ +struct metric_SO2 : public Metric { + template struct traits { + typedef SO2_Adaptor distance_t; + }; +}; +/** Metaprogramming helper traits class for the SO3_InnerProdQuat metric */ +struct metric_SO3 : public Metric { + template struct traits { + typedef SO3_Adaptor distance_t; + }; +}; + +/** @} */ + +/** @addtogroup param_grp Parameter structs + * @{ */ + +/** Parameters (see README.md) */ +struct KDTreeSingleIndexAdaptorParams { + KDTreeSingleIndexAdaptorParams(size_t _leaf_max_size = 10) + : leaf_max_size(_leaf_max_size) {} + + size_t leaf_max_size; +}; + +/** Search options for KDTreeSingleIndexAdaptor::findNeighbors() */ +struct SearchParams { + /** Note: The first argument (checks_IGNORED_) is ignored, but kept for + * compatibility with the FLANN interface */ + SearchParams(int checks_IGNORED_ = 32, float eps_ = 0, bool sorted_ = true) + : checks(checks_IGNORED_), eps(eps_), sorted(sorted_) {} + + int checks; //!< Ignored parameter (Kept for compatibility with the FLANN + //!< interface). + float eps; //!< search for eps-approximate neighbours (default: 0) + bool sorted; //!< only for radius search, require neighbours sorted by + //!< distance (default: true) +}; +/** @} */ + +/** @addtogroup memalloc_grp Memory allocation + * @{ */ + +/** + * Allocates (using C's malloc) a generic type T. + * + * Params: + * count = number of instances to allocate. + * Returns: pointer (of type T*) to memory buffer + */ +template inline T *allocate(size_t count = 1) { + T *mem = static_cast(::malloc(sizeof(T) * count)); + return mem; +} + +/** + * Pooled storage allocator + * + * The following routines allow for the efficient allocation of storage in + * small chunks from a specified pool. Rather than allowing each structure + * to be freed individually, an entire pool of storage is freed at once. + * This method has two advantages over just using malloc() and free(). First, + * it is far more efficient for allocating small objects, as there is + * no overhead for remembering all the information needed to free each + * object or consolidating fragmented memory. Second, the decision about + * how long to keep an object is made at the time of allocation, and there + * is no need to track down all the objects to free them. + * + */ + +const size_t WORDSIZE = 16; +const size_t BLOCKSIZE = 8192; + +class PooledAllocator { + /* We maintain memory alignment to word boundaries by requiring that all + allocations be in multiples of the machine wordsize. */ + /* Size of machine word in bytes. Must be power of 2. */ + /* Minimum number of bytes requested at a time from the system. Must be + * multiple of WORDSIZE. */ + + size_t remaining; /* Number of bytes left in current block of storage. */ + void *base; /* Pointer to base of current block of storage. */ + void *loc; /* Current location in block to next allocate memory. */ + + void internal_init() { + remaining = 0; + base = NULL; + usedMemory = 0; + wastedMemory = 0; + } + +public: + size_t usedMemory; + size_t wastedMemory; + + /** + Default constructor. Initializes a new pool. + */ + PooledAllocator() { internal_init(); } + + /** + * Destructor. Frees all the memory allocated in this pool. + */ + ~PooledAllocator() { free_all(); } + + /** Frees all allocated memory chunks */ + void free_all() { + while (base != NULL) { + void *prev = + *(static_cast(base)); /* Get pointer to prev block. */ + ::free(base); + base = prev; + } + internal_init(); + } + + /** + * Returns a pointer to a piece of new memory of the given size in bytes + * allocated from the pool. + */ + void *malloc(const size_t req_size) { + /* Round size up to a multiple of wordsize. The following expression + only works for WORDSIZE that is a power of 2, by masking last bits of + incremented size to zero. + */ + const size_t size = (req_size + (WORDSIZE - 1)) & ~(WORDSIZE - 1); + + /* Check whether a new block must be allocated. Note that the first word + of a block is reserved for a pointer to the previous block. + */ + if (size > remaining) { + + wastedMemory += remaining; + + /* Allocate new storage. */ + const size_t blocksize = + (size + sizeof(void *) + (WORDSIZE - 1) > BLOCKSIZE) + ? size + sizeof(void *) + (WORDSIZE - 1) + : BLOCKSIZE; + + // use the standard C malloc to allocate memory + void *m = ::malloc(blocksize); + if (!m) { + fprintf(stderr, "Failed to allocate memory.\n"); + return NULL; + } + + /* Fill first word of new block with pointer to previous block. */ + static_cast(m)[0] = base; + base = m; + + size_t shift = 0; + // int size_t = (WORDSIZE - ( (((size_t)m) + sizeof(void*)) & + // (WORDSIZE-1))) & (WORDSIZE-1); + + remaining = blocksize - sizeof(void *) - shift; + loc = (static_cast(m) + sizeof(void *) + shift); + } + void *rloc = loc; + loc = static_cast(loc) + size; + remaining -= size; + + usedMemory += size; + + return rloc; + } + + /** + * Allocates (using this pool) a generic type T. + * + * Params: + * count = number of instances to allocate. + * Returns: pointer (of type T*) to memory buffer + */ + template T *allocate(const size_t count = 1) { + T *mem = static_cast(this->malloc(sizeof(T) * count)); + return mem; + } +}; +/** @} */ + +/** @addtogroup nanoflann_metaprog_grp Auxiliary metaprogramming stuff + * @{ */ + +/** Used to declare fixed-size arrays when DIM>0, dynamically-allocated vectors + * when DIM=-1. Fixed size version for a generic DIM: + */ +template struct array_or_vector_selector { + typedef std::array container_t; +}; +/** Dynamic size version */ +template struct array_or_vector_selector<-1, T> { + typedef std::vector container_t; +}; + +/** @} */ + +/** kd-tree base-class + * + * Contains the member functions common to the classes KDTreeSingleIndexAdaptor + * and KDTreeSingleIndexDynamicAdaptor_. + * + * \tparam Derived The name of the class which inherits this class. + * \tparam DatasetAdaptor The user-provided adaptor (see comments above). + * \tparam Distance The distance metric to use, these are all classes derived + * from nanoflann::Metric \tparam DIM Dimensionality of data points (e.g. 3 for + * 3D points) \tparam IndexType Will be typically size_t or int + */ + +template +class KDTreeBaseClass { + +public: + /** Frees the previously-built index. Automatically called within + * buildIndex(). */ + void freeIndex(Derived &obj) { + obj.pool.free_all(); + obj.root_node = NULL; + obj.m_size_at_index_build = 0; + } + + typedef typename Distance::ElementType ElementType; + typedef typename Distance::DistanceType DistanceType; + + /*--------------------- Internal Data Structures --------------------------*/ + struct Node { + /** Union used because a node can be either a LEAF node or a non-leaf node, + * so both data fields are never used simultaneously */ + union { + struct leaf { + IndexType left, right; //!< Indices of points in leaf node + } lr; + struct nonleaf { + int divfeat; //!< Dimension used for subdivision. + DistanceType divlow, divhigh; //!< The values used for subdivision. + } sub; + } node_type; + Node *child1, *child2; //!< Child nodes (both=NULL mean its a leaf node) + }; + + typedef Node *NodePtr; + + struct Interval { + ElementType low, high; + }; + + /** + * Array of indices to vectors in the dataset. + */ + std::vector vind; + + NodePtr root_node; + + size_t m_leaf_max_size; + + size_t m_size; //!< Number of current points in the dataset + size_t m_size_at_index_build; //!< Number of points in the dataset when the + //!< index was built + int dim; //!< Dimensionality of each data point + + /** Define "BoundingBox" as a fixed-size or variable-size container depending + * on "DIM" */ + typedef + typename array_or_vector_selector::container_t BoundingBox; + + /** Define "distance_vector_t" as a fixed-size or variable-size container + * depending on "DIM" */ + typedef typename array_or_vector_selector::container_t + distance_vector_t; + + /** The KD-tree used to find neighbours */ + + BoundingBox root_bbox; + + /** + * Pooled memory allocator. + * + * Using a pooled memory allocator is more efficient + * than allocating memory directly when there is a large + * number small of memory allocations. + */ + PooledAllocator pool; + + /** Returns number of points in dataset */ + size_t size(const Derived &obj) const { return obj.m_size; } + + /** Returns the length of each point in the dataset */ + size_t veclen(const Derived &obj) { + return static_cast(DIM > 0 ? DIM : obj.dim); + } + + /// Helper accessor to the dataset points: + inline ElementType dataset_get(const Derived &obj, size_t idx, + int component) const { + return obj.dataset.kdtree_get_pt(idx, component); + } + + /** + * Computes the inde memory usage + * Returns: memory used by the index + */ + size_t usedMemory(Derived &obj) { + return obj.pool.usedMemory + obj.pool.wastedMemory + + obj.dataset.kdtree_get_point_count() * + sizeof(IndexType); // pool memory and vind array memory + } + + void computeMinMax(const Derived &obj, IndexType *ind, IndexType count, + int element, ElementType &min_elem, + ElementType &max_elem) { + min_elem = dataset_get(obj, ind[0], element); + max_elem = dataset_get(obj, ind[0], element); + for (IndexType i = 1; i < count; ++i) { + ElementType val = dataset_get(obj, ind[i], element); + if (val < min_elem) + min_elem = val; + if (val > max_elem) + max_elem = val; + } + } + + /** + * Create a tree node that subdivides the list of vecs from vind[first] + * to vind[last]. The routine is called recursively on each sublist. + * + * @param left index of the first vector + * @param right index of the last vector + */ + NodePtr divideTree(Derived &obj, const IndexType left, const IndexType right, + BoundingBox &bbox) { + NodePtr node = obj.pool.template allocate(); // allocate memory + + /* If too few exemplars remain, then make this a leaf node. */ + if ((right - left) <= static_cast(obj.m_leaf_max_size)) { + node->child1 = node->child2 = NULL; /* Mark as leaf node. */ + node->node_type.lr.left = left; + node->node_type.lr.right = right; + + // compute bounding-box of leaf points + for (int i = 0; i < (DIM > 0 ? DIM : obj.dim); ++i) { + bbox[i].low = dataset_get(obj, obj.vind[left], i); + bbox[i].high = dataset_get(obj, obj.vind[left], i); + } + for (IndexType k = left + 1; k < right; ++k) { + for (int i = 0; i < (DIM > 0 ? DIM : obj.dim); ++i) { + if (bbox[i].low > dataset_get(obj, obj.vind[k], i)) + bbox[i].low = dataset_get(obj, obj.vind[k], i); + if (bbox[i].high < dataset_get(obj, obj.vind[k], i)) + bbox[i].high = dataset_get(obj, obj.vind[k], i); + } + } + } else { + IndexType idx; + int cutfeat; + DistanceType cutval; + middleSplit_(obj, &obj.vind[0] + left, right - left, idx, cutfeat, cutval, + bbox); + + node->node_type.sub.divfeat = cutfeat; + + BoundingBox left_bbox(bbox); + left_bbox[cutfeat].high = cutval; + node->child1 = divideTree(obj, left, left + idx, left_bbox); + + BoundingBox right_bbox(bbox); + right_bbox[cutfeat].low = cutval; + node->child2 = divideTree(obj, left + idx, right, right_bbox); + + node->node_type.sub.divlow = left_bbox[cutfeat].high; + node->node_type.sub.divhigh = right_bbox[cutfeat].low; + + for (int i = 0; i < (DIM > 0 ? DIM : obj.dim); ++i) { + bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low); + bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high); + } + } + + return node; + } + + void middleSplit_(Derived &obj, IndexType *ind, IndexType count, + IndexType &index, int &cutfeat, DistanceType &cutval, + const BoundingBox &bbox) { + const DistanceType EPS = static_cast(0.00001); + ElementType max_span = bbox[0].high - bbox[0].low; + for (int i = 1; i < (DIM > 0 ? DIM : obj.dim); ++i) { + ElementType span = bbox[i].high - bbox[i].low; + if (span > max_span) { + max_span = span; + } + } + ElementType max_spread = -1; + cutfeat = 0; + for (int i = 0; i < (DIM > 0 ? DIM : obj.dim); ++i) { + ElementType span = bbox[i].high - bbox[i].low; + if (span > (1 - EPS) * max_span) { + ElementType min_elem, max_elem; + computeMinMax(obj, ind, count, i, min_elem, max_elem); + ElementType spread = max_elem - min_elem; + ; + if (spread > max_spread) { + cutfeat = i; + max_spread = spread; + } + } + } + // split in the middle + DistanceType split_val = (bbox[cutfeat].low + bbox[cutfeat].high) / 2; + ElementType min_elem, max_elem; + computeMinMax(obj, ind, count, cutfeat, min_elem, max_elem); + + if (split_val < min_elem) + cutval = min_elem; + else if (split_val > max_elem) + cutval = max_elem; + else + cutval = split_val; + + IndexType lim1, lim2; + planeSplit(obj, ind, count, cutfeat, cutval, lim1, lim2); + + if (lim1 > count / 2) + index = lim1; + else if (lim2 < count / 2) + index = lim2; + else + index = count / 2; + } + + /** + * Subdivide the list of points by a plane perpendicular on axe corresponding + * to the 'cutfeat' dimension at 'cutval' position. + * + * On return: + * dataset[ind[0..lim1-1]][cutfeat]cutval + */ + void planeSplit(Derived &obj, IndexType *ind, const IndexType count, + int cutfeat, DistanceType &cutval, IndexType &lim1, + IndexType &lim2) { + /* Move vector indices for left subtree to front of list. */ + IndexType left = 0; + IndexType right = count - 1; + for (;;) { + while (left <= right && dataset_get(obj, ind[left], cutfeat) < cutval) + ++left; + while (right && left <= right && + dataset_get(obj, ind[right], cutfeat) >= cutval) + --right; + if (left > right || !right) + break; // "!right" was added to support unsigned Index types + std::swap(ind[left], ind[right]); + ++left; + --right; + } + /* If either list is empty, it means that all remaining features + * are identical. Split in the middle to maintain a balanced tree. + */ + lim1 = left; + right = count - 1; + for (;;) { + while (left <= right && dataset_get(obj, ind[left], cutfeat) <= cutval) + ++left; + while (right && left <= right && + dataset_get(obj, ind[right], cutfeat) > cutval) + --right; + if (left > right || !right) + break; // "!right" was added to support unsigned Index types + std::swap(ind[left], ind[right]); + ++left; + --right; + } + lim2 = left; + } + + DistanceType computeInitialDistances(const Derived &obj, + const ElementType *vec, + distance_vector_t &dists) const { + assert(vec); + DistanceType distsq = DistanceType(); + + for (int i = 0; i < (DIM > 0 ? DIM : obj.dim); ++i) { + if (vec[i] < obj.root_bbox[i].low) { + dists[i] = obj.distance.accum_dist(vec[i], obj.root_bbox[i].low, i); + distsq += dists[i]; + } + if (vec[i] > obj.root_bbox[i].high) { + dists[i] = obj.distance.accum_dist(vec[i], obj.root_bbox[i].high, i); + distsq += dists[i]; + } + } + return distsq; + } + + void save_tree(Derived &obj, FILE *stream, NodePtr tree) { + save_value(stream, *tree); + if (tree->child1 != NULL) { + save_tree(obj, stream, tree->child1); + } + if (tree->child2 != NULL) { + save_tree(obj, stream, tree->child2); + } + } + + void load_tree(Derived &obj, FILE *stream, NodePtr &tree) { + tree = obj.pool.template allocate(); + load_value(stream, *tree); + if (tree->child1 != NULL) { + load_tree(obj, stream, tree->child1); + } + if (tree->child2 != NULL) { + load_tree(obj, stream, tree->child2); + } + } + + /** Stores the index in a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so when + * loading the index object it must be constructed associated to the same + * source of data points used while building it. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void saveIndex_(Derived &obj, FILE *stream) { + save_value(stream, obj.m_size); + save_value(stream, obj.dim); + save_value(stream, obj.root_bbox); + save_value(stream, obj.m_leaf_max_size); + save_value(stream, obj.vind); + save_tree(obj, stream, obj.root_node); + } + + /** Loads a previous index from a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so the + * index object must be constructed associated to the same source of data + * points used while building the index. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void loadIndex_(Derived &obj, FILE *stream) { + load_value(stream, obj.m_size); + load_value(stream, obj.dim); + load_value(stream, obj.root_bbox); + load_value(stream, obj.m_leaf_max_size); + load_value(stream, obj.vind); + load_tree(obj, stream, obj.root_node); + } +}; + +/** @addtogroup kdtrees_grp KD-tree classes and adaptors + * @{ */ + +/** kd-tree static index + * + * Contains the k-d trees and other information for indexing a set of points + * for nearest-neighbor matching. + * + * The class "DatasetAdaptor" must provide the following interface (can be + * non-virtual, inlined methods): + * + * \code + * // Must return the number of data poins + * inline size_t kdtree_get_point_count() const { ... } + * + * + * // Must return the dim'th component of the idx'th point in the class: + * inline T kdtree_get_pt(const size_t idx, const size_t dim) const { ... } + * + * // Optional bounding-box computation: return false to default to a standard + * bbox computation loop. + * // Return true if the BBOX was already computed by the class and returned + * in "bb" so it can be avoided to redo it again. + * // Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 + * for point clouds) template bool kdtree_get_bbox(BBOX &bb) const + * { + * bb[0].low = ...; bb[0].high = ...; // 0th dimension limits + * bb[1].low = ...; bb[1].high = ...; // 1st dimension limits + * ... + * return true; + * } + * + * \endcode + * + * \tparam DatasetAdaptor The user-provided adaptor (see comments above). + * \tparam Distance The distance metric to use: nanoflann::metric_L1, + * nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. \tparam DIM + * Dimensionality of data points (e.g. 3 for 3D points) \tparam IndexType Will + * be typically size_t or int + */ +template +class KDTreeSingleIndexAdaptor + : public KDTreeBaseClass< + KDTreeSingleIndexAdaptor, + Distance, DatasetAdaptor, DIM, IndexType> { +public: + /** Deleted copy constructor*/ + KDTreeSingleIndexAdaptor( + const KDTreeSingleIndexAdaptor + &) = delete; + + /** + * The dataset used by this index + */ + const DatasetAdaptor &dataset; //!< The source of our data + + const KDTreeSingleIndexAdaptorParams index_params; + + Distance distance; + + typedef typename nanoflann::KDTreeBaseClass< + nanoflann::KDTreeSingleIndexAdaptor, + Distance, DatasetAdaptor, DIM, IndexType> + BaseClassRef; + + typedef typename BaseClassRef::ElementType ElementType; + typedef typename BaseClassRef::DistanceType DistanceType; + + typedef typename BaseClassRef::Node Node; + typedef Node *NodePtr; + + typedef typename BaseClassRef::Interval Interval; + /** Define "BoundingBox" as a fixed-size or variable-size container depending + * on "DIM" */ + typedef typename BaseClassRef::BoundingBox BoundingBox; + + /** Define "distance_vector_t" as a fixed-size or variable-size container + * depending on "DIM" */ + typedef typename BaseClassRef::distance_vector_t distance_vector_t; + + /** + * KDTree constructor + * + * Refer to docs in README.md or online in + * https://github.com/jlblancoc/nanoflann + * + * The KD-Tree point dimension (the length of each point in the datase, e.g. 3 + * for 3D points) is determined by means of: + * - The \a DIM template parameter if >0 (highest priority) + * - Otherwise, the \a dimensionality parameter of this constructor. + * + * @param inputData Dataset with the input features + * @param params Basically, the maximum leaf node size + */ + KDTreeSingleIndexAdaptor(const int dimensionality, + const DatasetAdaptor &inputData, + const KDTreeSingleIndexAdaptorParams ¶ms = + KDTreeSingleIndexAdaptorParams()) + : dataset(inputData), index_params(params), distance(inputData) { + BaseClassRef::root_node = NULL; + BaseClassRef::m_size = dataset.kdtree_get_point_count(); + BaseClassRef::m_size_at_index_build = BaseClassRef::m_size; + BaseClassRef::dim = dimensionality; + if (DIM > 0) + BaseClassRef::dim = DIM; + BaseClassRef::m_leaf_max_size = params.leaf_max_size; + + // Create a permutable array of indices to the input vectors. + init_vind(); + } + + /** + * Builds the index + */ + void buildIndex() { + BaseClassRef::m_size = dataset.kdtree_get_point_count(); + BaseClassRef::m_size_at_index_build = BaseClassRef::m_size; + init_vind(); + this->freeIndex(*this); + BaseClassRef::m_size_at_index_build = BaseClassRef::m_size; + if (BaseClassRef::m_size == 0) + return; + computeBoundingBox(BaseClassRef::root_bbox); + BaseClassRef::root_node = + this->divideTree(*this, 0, BaseClassRef::m_size, + BaseClassRef::root_bbox); // construct the tree + } + + /** \name Query methods + * @{ */ + + /** + * Find set of nearest neighbors to vec[0:dim-1]. Their indices are stored + * inside the result object. + * + * Params: + * result = the result object in which the indices of the + * nearest-neighbors are stored vec = the vector for which to search the + * nearest neighbors + * + * \tparam RESULTSET Should be any ResultSet + * \return True if the requested neighbors could be found. + * \sa knnSearch, radiusSearch + */ + template + bool findNeighbors(RESULTSET &result, const ElementType *vec, + const SearchParams &searchParams) const { + assert(vec); + if (this->size(*this) == 0) + return false; + if (!BaseClassRef::root_node) + throw std::runtime_error( + "[nanoflann] findNeighbors() called before building the index."); + float epsError = 1 + searchParams.eps; + + distance_vector_t + dists; // fixed or variable-sized container (depending on DIM) + auto zero = static_cast(0); + assign(dists, (DIM > 0 ? DIM : BaseClassRef::dim), + zero); // Fill it with zeros. + DistanceType distsq = this->computeInitialDistances(*this, vec, dists); + searchLevel(result, vec, BaseClassRef::root_node, distsq, dists, + epsError); // "count_leaf" parameter removed since was neither + // used nor returned to the user. + return result.full(); + } + + /** + * Find the "num_closest" nearest neighbors to the \a query_point[0:dim-1]. + * Their indices are stored inside the result object. \sa radiusSearch, + * findNeighbors \note nChecks_IGNORED is ignored but kept for compatibility + * with the original FLANN interface. \return Number `N` of valid points in + * the result set. Only the first `N` entries in `out_indices` and + * `out_distances_sq` will be valid. Return may be less than `num_closest` + * only if the number of elements in the tree is less than `num_closest`. + */ + size_t knnSearch(const ElementType *query_point, const size_t num_closest, + IndexType *out_indices, DistanceType *out_distances_sq, + const int /* nChecks_IGNORED */ = 10) const { + nanoflann::KNNResultSet resultSet(num_closest); + resultSet.init(out_indices, out_distances_sq); + this->findNeighbors(resultSet, query_point, nanoflann::SearchParams()); + return resultSet.size(); + } + + /** + * Find all the neighbors to \a query_point[0:dim-1] within a maximum radius. + * The output is given as a vector of pairs, of which the first element is a + * point index and the second the corresponding distance. Previous contents of + * \a IndicesDists are cleared. + * + * If searchParams.sorted==true, the output list is sorted by ascending + * distances. + * + * For a better performance, it is advisable to do a .reserve() on the vector + * if you have any wild guess about the number of expected matches. + * + * \sa knnSearch, findNeighbors, radiusSearchCustomCallback + * \return The number of points within the given radius (i.e. indices.size() + * or dists.size() ) + */ + size_t + radiusSearch(const ElementType *query_point, const DistanceType &radius, + std::vector> &IndicesDists, + const SearchParams &searchParams) const { + RadiusResultSet resultSet(radius, IndicesDists); + const size_t nFound = + radiusSearchCustomCallback(query_point, resultSet, searchParams); + if (searchParams.sorted) + std::sort(IndicesDists.begin(), IndicesDists.end(), IndexDist_Sorter()); + return nFound; + } + + /** + * Just like radiusSearch() but with a custom callback class for each point + * found in the radius of the query. See the source of RadiusResultSet<> as a + * start point for your own classes. \sa radiusSearch + */ + template + size_t radiusSearchCustomCallback( + const ElementType *query_point, SEARCH_CALLBACK &resultSet, + const SearchParams &searchParams = SearchParams()) const { + this->findNeighbors(resultSet, query_point, searchParams); + return resultSet.size(); + } + + /** @} */ + +public: + /** Make sure the auxiliary list \a vind has the same size than the current + * dataset, and re-generate if size has changed. */ + void init_vind() { + // Create a permutable array of indices to the input vectors. + BaseClassRef::m_size = dataset.kdtree_get_point_count(); + if (BaseClassRef::vind.size() != BaseClassRef::m_size) + BaseClassRef::vind.resize(BaseClassRef::m_size); + for (size_t i = 0; i < BaseClassRef::m_size; i++) + BaseClassRef::vind[i] = i; + } + + void computeBoundingBox(BoundingBox &bbox) { + resize(bbox, (DIM > 0 ? DIM : BaseClassRef::dim)); + if (dataset.kdtree_get_bbox(bbox)) { + // Done! It was implemented in derived class + } else { + const size_t N = dataset.kdtree_get_point_count(); + if (!N) + throw std::runtime_error("[nanoflann] computeBoundingBox() called but " + "no data points found."); + for (int i = 0; i < (DIM > 0 ? DIM : BaseClassRef::dim); ++i) { + bbox[i].low = bbox[i].high = this->dataset_get(*this, 0, i); + } + for (size_t k = 1; k < N; ++k) { + for (int i = 0; i < (DIM > 0 ? DIM : BaseClassRef::dim); ++i) { + if (this->dataset_get(*this, k, i) < bbox[i].low) + bbox[i].low = this->dataset_get(*this, k, i); + if (this->dataset_get(*this, k, i) > bbox[i].high) + bbox[i].high = this->dataset_get(*this, k, i); + } + } + } + } + + /** + * Performs an exact search in the tree starting from a node. + * \tparam RESULTSET Should be any ResultSet + * \return true if the search should be continued, false if the results are + * sufficient + */ + template + bool searchLevel(RESULTSET &result_set, const ElementType *vec, + const NodePtr node, DistanceType mindistsq, + distance_vector_t &dists, const float epsError) const { + /* If this is a leaf node, then do check and return. */ + if ((node->child1 == NULL) && (node->child2 == NULL)) { + // count_leaf += (node->lr.right-node->lr.left); // Removed since was + // neither used nor returned to the user. + DistanceType worst_dist = result_set.worstDist(); + for (IndexType i = node->node_type.lr.left; i < node->node_type.lr.right; + ++i) { + const IndexType index = BaseClassRef::vind[i]; // reorder... : i; + DistanceType dist = distance.evalMetric( + vec, index, (DIM > 0 ? DIM : BaseClassRef::dim)); + if (dist < worst_dist) { + if (!result_set.addPoint(dist, BaseClassRef::vind[i])) { + // the resultset doesn't want to receive any more points, we're done + // searching! + return false; + } + } + } + return true; + } + + /* Which child branch should be taken first? */ + int idx = node->node_type.sub.divfeat; + ElementType val = vec[idx]; + DistanceType diff1 = val - node->node_type.sub.divlow; + DistanceType diff2 = val - node->node_type.sub.divhigh; + + NodePtr bestChild; + NodePtr otherChild; + DistanceType cut_dist; + if ((diff1 + diff2) < 0) { + bestChild = node->child1; + otherChild = node->child2; + cut_dist = distance.accum_dist(val, node->node_type.sub.divhigh, idx); + } else { + bestChild = node->child2; + otherChild = node->child1; + cut_dist = distance.accum_dist(val, node->node_type.sub.divlow, idx); + } + + /* Call recursively to search next level down. */ + if (!searchLevel(result_set, vec, bestChild, mindistsq, dists, epsError)) { + // the resultset doesn't want to receive any more points, we're done + // searching! + return false; + } + + DistanceType dst = dists[idx]; + mindistsq = mindistsq + cut_dist - dst; + dists[idx] = cut_dist; + if (mindistsq * epsError <= result_set.worstDist()) { + if (!searchLevel(result_set, vec, otherChild, mindistsq, dists, + epsError)) { + // the resultset doesn't want to receive any more points, we're done + // searching! + return false; + } + } + dists[idx] = dst; + return true; + } + +public: + /** Stores the index in a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so when + * loading the index object it must be constructed associated to the same + * source of data points used while building it. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void saveIndex(FILE *stream) { this->saveIndex_(*this, stream); } + + /** Loads a previous index from a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so the + * index object must be constructed associated to the same source of data + * points used while building the index. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void loadIndex(FILE *stream) { this->loadIndex_(*this, stream); } + +}; // class KDTree + +/** kd-tree dynamic index + * + * Contains the k-d trees and other information for indexing a set of points + * for nearest-neighbor matching. + * + * The class "DatasetAdaptor" must provide the following interface (can be + * non-virtual, inlined methods): + * + * \code + * // Must return the number of data poins + * inline size_t kdtree_get_point_count() const { ... } + * + * // Must return the dim'th component of the idx'th point in the class: + * inline T kdtree_get_pt(const size_t idx, const size_t dim) const { ... } + * + * // Optional bounding-box computation: return false to default to a standard + * bbox computation loop. + * // Return true if the BBOX was already computed by the class and returned + * in "bb" so it can be avoided to redo it again. + * // Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 + * for point clouds) template bool kdtree_get_bbox(BBOX &bb) const + * { + * bb[0].low = ...; bb[0].high = ...; // 0th dimension limits + * bb[1].low = ...; bb[1].high = ...; // 1st dimension limits + * ... + * return true; + * } + * + * \endcode + * + * \tparam DatasetAdaptor The user-provided adaptor (see comments above). + * \tparam Distance The distance metric to use: nanoflann::metric_L1, + * nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. \tparam DIM + * Dimensionality of data points (e.g. 3 for 3D points) \tparam IndexType Will + * be typically size_t or int + */ +template +class KDTreeSingleIndexDynamicAdaptor_ + : public KDTreeBaseClass, + Distance, DatasetAdaptor, DIM, IndexType> { +public: + /** + * The dataset used by this index + */ + const DatasetAdaptor &dataset; //!< The source of our data + + KDTreeSingleIndexAdaptorParams index_params; + + std::vector &treeIndex; + + Distance distance; + + typedef typename nanoflann::KDTreeBaseClass< + nanoflann::KDTreeSingleIndexDynamicAdaptor_, + Distance, DatasetAdaptor, DIM, IndexType> + BaseClassRef; + + typedef typename BaseClassRef::ElementType ElementType; + typedef typename BaseClassRef::DistanceType DistanceType; + + typedef typename BaseClassRef::Node Node; + typedef Node *NodePtr; + + typedef typename BaseClassRef::Interval Interval; + /** Define "BoundingBox" as a fixed-size or variable-size container depending + * on "DIM" */ + typedef typename BaseClassRef::BoundingBox BoundingBox; + + /** Define "distance_vector_t" as a fixed-size or variable-size container + * depending on "DIM" */ + typedef typename BaseClassRef::distance_vector_t distance_vector_t; + + /** + * KDTree constructor + * + * Refer to docs in README.md or online in + * https://github.com/jlblancoc/nanoflann + * + * The KD-Tree point dimension (the length of each point in the datase, e.g. 3 + * for 3D points) is determined by means of: + * - The \a DIM template parameter if >0 (highest priority) + * - Otherwise, the \a dimensionality parameter of this constructor. + * + * @param inputData Dataset with the input features + * @param params Basically, the maximum leaf node size + */ + KDTreeSingleIndexDynamicAdaptor_( + const int dimensionality, const DatasetAdaptor &inputData, + std::vector &treeIndex_, + const KDTreeSingleIndexAdaptorParams ¶ms = + KDTreeSingleIndexAdaptorParams()) + : dataset(inputData), index_params(params), treeIndex(treeIndex_), + distance(inputData) { + BaseClassRef::root_node = NULL; + BaseClassRef::m_size = 0; + BaseClassRef::m_size_at_index_build = 0; + BaseClassRef::dim = dimensionality; + if (DIM > 0) + BaseClassRef::dim = DIM; + BaseClassRef::m_leaf_max_size = params.leaf_max_size; + } + + /** Assignment operator definiton */ + KDTreeSingleIndexDynamicAdaptor_ + operator=(const KDTreeSingleIndexDynamicAdaptor_ &rhs) { + KDTreeSingleIndexDynamicAdaptor_ tmp(rhs); + std::swap(BaseClassRef::vind, tmp.BaseClassRef::vind); + std::swap(BaseClassRef::m_leaf_max_size, tmp.BaseClassRef::m_leaf_max_size); + std::swap(index_params, tmp.index_params); + std::swap(treeIndex, tmp.treeIndex); + std::swap(BaseClassRef::m_size, tmp.BaseClassRef::m_size); + std::swap(BaseClassRef::m_size_at_index_build, + tmp.BaseClassRef::m_size_at_index_build); + std::swap(BaseClassRef::root_node, tmp.BaseClassRef::root_node); + std::swap(BaseClassRef::root_bbox, tmp.BaseClassRef::root_bbox); + std::swap(BaseClassRef::pool, tmp.BaseClassRef::pool); + return *this; + } + + /** + * Builds the index + */ + void buildIndex() { + BaseClassRef::m_size = BaseClassRef::vind.size(); + this->freeIndex(*this); + BaseClassRef::m_size_at_index_build = BaseClassRef::m_size; + if (BaseClassRef::m_size == 0) + return; + computeBoundingBox(BaseClassRef::root_bbox); + BaseClassRef::root_node = + this->divideTree(*this, 0, BaseClassRef::m_size, + BaseClassRef::root_bbox); // construct the tree + } + + /** \name Query methods + * @{ */ + + /** + * Find set of nearest neighbors to vec[0:dim-1]. Their indices are stored + * inside the result object. + * + * Params: + * result = the result object in which the indices of the + * nearest-neighbors are stored vec = the vector for which to search the + * nearest neighbors + * + * \tparam RESULTSET Should be any ResultSet + * \return True if the requested neighbors could be found. + * \sa knnSearch, radiusSearch + */ + template + bool findNeighbors(RESULTSET &result, const ElementType *vec, + const SearchParams &searchParams) const { + assert(vec); + if (this->size(*this) == 0) + return false; + if (!BaseClassRef::root_node) + return false; + float epsError = 1 + searchParams.eps; + + // fixed or variable-sized container (depending on DIM) + distance_vector_t dists; + // Fill it with zeros. + assign(dists, (DIM > 0 ? DIM : BaseClassRef::dim), + static_cast(0)); + DistanceType distsq = this->computeInitialDistances(*this, vec, dists); + searchLevel(result, vec, BaseClassRef::root_node, distsq, dists, + epsError); // "count_leaf" parameter removed since was neither + // used nor returned to the user. + return result.full(); + } + + /** + * Find the "num_closest" nearest neighbors to the \a query_point[0:dim-1]. + * Their indices are stored inside the result object. \sa radiusSearch, + * findNeighbors \note nChecks_IGNORED is ignored but kept for compatibility + * with the original FLANN interface. \return Number `N` of valid points in + * the result set. Only the first `N` entries in `out_indices` and + * `out_distances_sq` will be valid. Return may be less than `num_closest` + * only if the number of elements in the tree is less than `num_closest`. + */ + size_t knnSearch(const ElementType *query_point, const size_t num_closest, + IndexType *out_indices, DistanceType *out_distances_sq, + const int /* nChecks_IGNORED */ = 10) const { + nanoflann::KNNResultSet resultSet(num_closest); + resultSet.init(out_indices, out_distances_sq); + this->findNeighbors(resultSet, query_point, nanoflann::SearchParams()); + return resultSet.size(); + } + + /** + * Find all the neighbors to \a query_point[0:dim-1] within a maximum radius. + * The output is given as a vector of pairs, of which the first element is a + * point index and the second the corresponding distance. Previous contents of + * \a IndicesDists are cleared. + * + * If searchParams.sorted==true, the output list is sorted by ascending + * distances. + * + * For a better performance, it is advisable to do a .reserve() on the vector + * if you have any wild guess about the number of expected matches. + * + * \sa knnSearch, findNeighbors, radiusSearchCustomCallback + * \return The number of points within the given radius (i.e. indices.size() + * or dists.size() ) + */ + size_t + radiusSearch(const ElementType *query_point, const DistanceType &radius, + std::vector> &IndicesDists, + const SearchParams &searchParams) const { + RadiusResultSet resultSet(radius, IndicesDists); + const size_t nFound = + radiusSearchCustomCallback(query_point, resultSet, searchParams); + if (searchParams.sorted) + std::sort(IndicesDists.begin(), IndicesDists.end(), IndexDist_Sorter()); + return nFound; + } + + /** + * Just like radiusSearch() but with a custom callback class for each point + * found in the radius of the query. See the source of RadiusResultSet<> as a + * start point for your own classes. \sa radiusSearch + */ + template + size_t radiusSearchCustomCallback( + const ElementType *query_point, SEARCH_CALLBACK &resultSet, + const SearchParams &searchParams = SearchParams()) const { + this->findNeighbors(resultSet, query_point, searchParams); + return resultSet.size(); + } + + /** @} */ + +public: + void computeBoundingBox(BoundingBox &bbox) { + resize(bbox, (DIM > 0 ? DIM : BaseClassRef::dim)); + + if (dataset.kdtree_get_bbox(bbox)) { + // Done! It was implemented in derived class + } else { + const size_t N = BaseClassRef::m_size; + if (!N) + throw std::runtime_error("[nanoflann] computeBoundingBox() called but " + "no data points found."); + for (int i = 0; i < (DIM > 0 ? DIM : BaseClassRef::dim); ++i) { + bbox[i].low = bbox[i].high = + this->dataset_get(*this, BaseClassRef::vind[0], i); + } + for (size_t k = 1; k < N; ++k) { + for (int i = 0; i < (DIM > 0 ? DIM : BaseClassRef::dim); ++i) { + if (this->dataset_get(*this, BaseClassRef::vind[k], i) < bbox[i].low) + bbox[i].low = this->dataset_get(*this, BaseClassRef::vind[k], i); + if (this->dataset_get(*this, BaseClassRef::vind[k], i) > bbox[i].high) + bbox[i].high = this->dataset_get(*this, BaseClassRef::vind[k], i); + } + } + } + } + + /** + * Performs an exact search in the tree starting from a node. + * \tparam RESULTSET Should be any ResultSet + */ + template + void searchLevel(RESULTSET &result_set, const ElementType *vec, + const NodePtr node, DistanceType mindistsq, + distance_vector_t &dists, const float epsError) const { + /* If this is a leaf node, then do check and return. */ + if ((node->child1 == NULL) && (node->child2 == NULL)) { + // count_leaf += (node->lr.right-node->lr.left); // Removed since was + // neither used nor returned to the user. + DistanceType worst_dist = result_set.worstDist(); + for (IndexType i = node->node_type.lr.left; i < node->node_type.lr.right; + ++i) { + const IndexType index = BaseClassRef::vind[i]; // reorder... : i; + if (treeIndex[index] == -1) + continue; + DistanceType dist = distance.evalMetric( + vec, index, (DIM > 0 ? DIM : BaseClassRef::dim)); + if (dist < worst_dist) { + if (!result_set.addPoint( + static_cast(dist), + static_cast( + BaseClassRef::vind[i]))) { + // the resultset doesn't want to receive any more points, we're done + // searching! + return; // false; + } + } + } + return; + } + + /* Which child branch should be taken first? */ + int idx = node->node_type.sub.divfeat; + ElementType val = vec[idx]; + DistanceType diff1 = val - node->node_type.sub.divlow; + DistanceType diff2 = val - node->node_type.sub.divhigh; + + NodePtr bestChild; + NodePtr otherChild; + DistanceType cut_dist; + if ((diff1 + diff2) < 0) { + bestChild = node->child1; + otherChild = node->child2; + cut_dist = distance.accum_dist(val, node->node_type.sub.divhigh, idx); + } else { + bestChild = node->child2; + otherChild = node->child1; + cut_dist = distance.accum_dist(val, node->node_type.sub.divlow, idx); + } + + /* Call recursively to search next level down. */ + searchLevel(result_set, vec, bestChild, mindistsq, dists, epsError); + + DistanceType dst = dists[idx]; + mindistsq = mindistsq + cut_dist - dst; + dists[idx] = cut_dist; + if (mindistsq * epsError <= result_set.worstDist()) { + searchLevel(result_set, vec, otherChild, mindistsq, dists, epsError); + } + dists[idx] = dst; + } + +public: + /** Stores the index in a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so when + * loading the index object it must be constructed associated to the same + * source of data points used while building it. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void saveIndex(FILE *stream) { this->saveIndex_(*this, stream); } + + /** Loads a previous index from a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so the + * index object must be constructed associated to the same source of data + * points used while building the index. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void loadIndex(FILE *stream) { this->loadIndex_(*this, stream); } +}; + +/** kd-tree dynaimic index + * + * class to create multiple static index and merge their results to behave as + * single dynamic index as proposed in Logarithmic Approach. + * + * Example of usage: + * examples/dynamic_pointcloud_example.cpp + * + * \tparam DatasetAdaptor The user-provided adaptor (see comments above). + * \tparam Distance The distance metric to use: nanoflann::metric_L1, + * nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. \tparam DIM + * Dimensionality of data points (e.g. 3 for 3D points) \tparam IndexType Will + * be typically size_t or int + */ +template +class KDTreeSingleIndexDynamicAdaptor { +public: + typedef typename Distance::ElementType ElementType; + typedef typename Distance::DistanceType DistanceType; + +protected: + size_t m_leaf_max_size; + size_t treeCount; + size_t pointCount; + + /** + * The dataset used by this index + */ + const DatasetAdaptor &dataset; //!< The source of our data + + std::vector treeIndex; //!< treeIndex[idx] is the index of tree in which + //!< point at idx is stored. treeIndex[idx]=-1 + //!< means that point has been removed. + + KDTreeSingleIndexAdaptorParams index_params; + + int dim; //!< Dimensionality of each data point + + typedef KDTreeSingleIndexDynamicAdaptor_ + index_container_t; + std::vector index; + +public: + /** Get a const ref to the internal list of indices; the number of indices is + * adapted dynamically as the dataset grows in size. */ + const std::vector &getAllIndices() const { return index; } + +private: + /** finds position of least significant unset bit */ + int First0Bit(IndexType num) { + int pos = 0; + while (num & 1) { + num = num >> 1; + pos++; + } + return pos; + } + + /** Creates multiple empty trees to handle dynamic support */ + void init() { + typedef KDTreeSingleIndexDynamicAdaptor_ + my_kd_tree_t; + std::vector index_( + treeCount, my_kd_tree_t(dim /*dim*/, dataset, treeIndex, index_params)); + index = index_; + } + +public: + Distance distance; + + /** + * KDTree constructor + * + * Refer to docs in README.md or online in + * https://github.com/jlblancoc/nanoflann + * + * The KD-Tree point dimension (the length of each point in the datase, e.g. 3 + * for 3D points) is determined by means of: + * - The \a DIM template parameter if >0 (highest priority) + * - Otherwise, the \a dimensionality parameter of this constructor. + * + * @param inputData Dataset with the input features + * @param params Basically, the maximum leaf node size + */ + KDTreeSingleIndexDynamicAdaptor(const int dimensionality, + const DatasetAdaptor &inputData, + const KDTreeSingleIndexAdaptorParams ¶ms = + KDTreeSingleIndexAdaptorParams(), + const size_t maximumPointCount = 1000000000U) + : dataset(inputData), index_params(params), distance(inputData) { + treeCount = static_cast(std::log2(maximumPointCount)); + pointCount = 0U; + dim = dimensionality; + treeIndex.clear(); + if (DIM > 0) + dim = DIM; + m_leaf_max_size = params.leaf_max_size; + init(); + const size_t num_initial_points = dataset.kdtree_get_point_count(); + if (num_initial_points > 0) { + addPoints(0, num_initial_points - 1); + } + } + + /** Deleted copy constructor*/ + KDTreeSingleIndexDynamicAdaptor( + const KDTreeSingleIndexDynamicAdaptor &) = delete; + + /** Add points to the set, Inserts all points from [start, end] */ + void addPoints(IndexType start, IndexType end) { + size_t count = end - start + 1; + treeIndex.resize(treeIndex.size() + count); + for (IndexType idx = start; idx <= end; idx++) { + int pos = First0Bit(pointCount); + index[pos].vind.clear(); + treeIndex[pointCount] = pos; + for (int i = 0; i < pos; i++) { + for (int j = 0; j < static_cast(index[i].vind.size()); j++) { + index[pos].vind.push_back(index[i].vind[j]); + if (treeIndex[index[i].vind[j]] != -1) + treeIndex[index[i].vind[j]] = pos; + } + index[i].vind.clear(); + index[i].freeIndex(index[i]); + } + index[pos].vind.push_back(idx); + index[pos].buildIndex(); + pointCount++; + } + } + + /** Remove a point from the set (Lazy Deletion) */ + void removePoint(size_t idx) { + if (idx >= pointCount) + return; + treeIndex[idx] = -1; + } + + /** + * Find set of nearest neighbors to vec[0:dim-1]. Their indices are stored + * inside the result object. + * + * Params: + * result = the result object in which the indices of the + * nearest-neighbors are stored vec = the vector for which to search the + * nearest neighbors + * + * \tparam RESULTSET Should be any ResultSet + * \return True if the requested neighbors could be found. + * \sa knnSearch, radiusSearch + */ + template + bool findNeighbors(RESULTSET &result, const ElementType *vec, + const SearchParams &searchParams) const { + for (size_t i = 0; i < treeCount; i++) { + index[i].findNeighbors(result, &vec[0], searchParams); + } + return result.full(); + } +}; + +/** An L2-metric KD-tree adaptor for working with data directly stored in an + * Eigen Matrix, without duplicating the data storage. Each row in the matrix + * represents a point in the state space. + * + * Example of usage: + * \code + * Eigen::Matrix mat; + * // Fill out "mat"... + * + * typedef KDTreeEigenMatrixAdaptor< Eigen::Matrix > + * my_kd_tree_t; const int max_leaf = 10; my_kd_tree_t mat_index(mat, max_leaf + * ); mat_index.index->buildIndex(); mat_index.index->... \endcode + * + * \tparam DIM If set to >0, it specifies a compile-time fixed dimensionality + * for the points in the data set, allowing more compiler optimizations. \tparam + * Distance The distance metric to use: nanoflann::metric_L1, + * nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. + */ +template +struct KDTreeEigenMatrixAdaptor { + typedef KDTreeEigenMatrixAdaptor self_t; + typedef typename MatrixType::Scalar num_t; + typedef typename MatrixType::Index IndexType; + typedef + typename Distance::template traits::distance_t metric_t; + typedef KDTreeSingleIndexAdaptor + index_t; + + index_t *index; //! The kd-tree index for the user to call its methods as + //! usual with any other FLANN index. + + /// Constructor: takes a const ref to the matrix object with the data points + KDTreeEigenMatrixAdaptor(const size_t dimensionality, + const std::reference_wrapper &mat, + const int leaf_max_size = 10) + : m_data_matrix(mat) { + const auto dims = mat.get().cols(); + if (size_t(dims) != dimensionality) + throw std::runtime_error( + "Error: 'dimensionality' must match column count in data matrix"); + if (DIM > 0 && int(dims) != DIM) + throw std::runtime_error( + "Data set dimensionality does not match the 'DIM' template argument"); + index = + new index_t(static_cast(dims), *this /* adaptor */, + nanoflann::KDTreeSingleIndexAdaptorParams(leaf_max_size)); + index->buildIndex(); + } + +public: + /** Deleted copy constructor */ + KDTreeEigenMatrixAdaptor(const self_t &) = delete; + + ~KDTreeEigenMatrixAdaptor() { delete index; } + + const std::reference_wrapper m_data_matrix; + + /** Query for the \a num_closest closest points to a given point (entered as + * query_point[0:dim-1]). Note that this is a short-cut method for + * index->findNeighbors(). The user can also call index->... methods as + * desired. \note nChecks_IGNORED is ignored but kept for compatibility with + * the original FLANN interface. + */ + inline void query(const num_t *query_point, const size_t num_closest, + IndexType *out_indices, num_t *out_distances_sq, + const int /* nChecks_IGNORED */ = 10) const { + nanoflann::KNNResultSet resultSet(num_closest); + resultSet.init(out_indices, out_distances_sq); + index->findNeighbors(resultSet, query_point, nanoflann::SearchParams()); + } + + /** @name Interface expected by KDTreeSingleIndexAdaptor + * @{ */ + + const self_t &derived() const { return *this; } + self_t &derived() { return *this; } + + // Must return the number of data points + inline size_t kdtree_get_point_count() const { + return m_data_matrix.get().rows(); + } + + // Returns the dim'th component of the idx'th point in the class: + inline num_t kdtree_get_pt(const IndexType idx, size_t dim) const { + return m_data_matrix.get().coeff(idx, IndexType(dim)); + } + + // Optional bounding-box computation: return false to default to a standard + // bbox computation loop. + // Return true if the BBOX was already computed by the class and returned in + // "bb" so it can be avoided to redo it again. Look at bb.size() to find out + // the expected dimensionality (e.g. 2 or 3 for point clouds) + template bool kdtree_get_bbox(BBOX & /*bb*/) const { + return false; + } + + /** @} */ + +}; // end of KDTreeEigenMatrixAdaptor + /** @} */ + +/** @} */ // end of grouping +} // namespace nanoflann + +#endif /* NANOFLANN_HPP_ */ diff --git a/modules/alphamat/src/cm.cpp b/modules/alphamat/src/cm.cpp new file mode 100644 index 00000000000..19dcb961e4a --- /dev/null +++ b/modules/alphamat/src/cm.cpp @@ -0,0 +1,171 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "precomp.hpp" +#include "intraU.hpp" +#include "cm.hpp" + +namespace cv { namespace alphamat { + +static +void generateFVectorCM(my_vector_of_vectors_t& samples, Mat& img) +{ + int nRows = img.rows; + int nCols = img.cols; + + samples.resize(nRows * nCols); + + int i, j; + + for (i = 0; i < nRows; ++i) + { + for (j = 0; j < nCols; ++j) + { + samples[i * nCols + j].resize(ALPHAMAT_DIM); + samples[i * nCols + j][0] = img.at(i, j)[0] / 255.0; + samples[i * nCols + j][1] = img.at(i, j)[1] / 255.0; + samples[i * nCols + j][2] = img.at(i, j)[2] / 255.0; + samples[i * nCols + j][3] = double(i) / nRows; + samples[i * nCols + j][4] = double(j) / nCols; + } + } +} + +static +void kdtree_CM(Mat& img, my_vector_of_vectors_t& indm, my_vector_of_vectors_t& samples, std::unordered_set& unk) +{ + // Generate feature vectors for intra U: + generateFVectorCM(samples, img); + + // Query point: same as samples from which KD tree is generated + + // construct a kd-tree index: + // Dimensionality set at run-time (default: L2) + // ------------------------------------------------------------ + typedef KDTreeVectorOfVectorsAdaptor my_kd_tree_t; + my_kd_tree_t mat_index(ALPHAMAT_DIM /*dim*/, samples, 10 /* max leaf */); + mat_index.index->buildIndex(); + + // do a knn search with cm = 20 + const size_t num_results = 20 + 1; + + int N = unk.size(); + + std::vector ret_indexes(num_results); + std::vector out_dists_sqr(num_results); + nanoflann::KNNResultSet resultSet(num_results); + + indm.resize(N); + int i = 0; + for (std::unordered_set::iterator it = unk.begin(); it != unk.end(); it++) + { + resultSet.init(&ret_indexes[0], &out_dists_sqr[0]); + mat_index.index->findNeighbors(resultSet, &samples[*it][0], nanoflann::SearchParams(10)); + + indm[i].resize(num_results - 1); + for (std::size_t j = 1; j < num_results; j++) + { + indm[i][j - 1] = ret_indexes[j]; + } + i++; + } +} + +static +void lle(my_vector_of_vectors_t& indm, my_vector_of_vectors_t& samples, float eps, std::unordered_set& unk, + SparseMatrix& Wcm, SparseMatrix& Dcm, Mat& img) +{ + CV_LOG_INFO(NULL, "ALPHAMAT: In cm's lle function"); + int k = indm[0].size(); //number of neighbours that we are considering + int n = indm.size(); //number of unknown pixels + + typedef Triplet T; + std::vector triplets, td; + + my_vector_of_vectors_t wcm; + wcm.resize(n); + + Mat C(20, 20, DataType::type), rhs(20, 1, DataType::type), Z(3, 20, DataType::type), weights(20, 1, DataType::type), pt(3, 1, DataType::type); + Mat ptDotN(20, 1, DataType::type), imd(20, 1, DataType::type); + Mat Cones(20, 1, DataType::type), Cinv(20, 1, DataType::type); + float alpha, beta, lagrangeMult; + Cones += 1; + + C = 0; + rhs = 1; + + int i, ind = 0; + for (std::unordered_set::iterator it = unk.begin(); it != unk.end(); it++) + { + // filling values in Z + i = *it; + + int index_nbr; + for (int j = 0; j < k; j++) + { + index_nbr = indm[ind][j]; + for (int p = 0; p < ALPHAMAT_DIM - 2; p++) + { + Z.at(p, j) = samples[index_nbr][p]; + } + } + pt.at(0, 0) = samples[i][0]; + pt.at(1, 0) = samples[i][1]; + pt.at(2, 0) = samples[i][2]; + + C = Z.t() * Z; + for (int p = 0; p < k; p++) + { + C.at(p, p) += eps; + } + + ptDotN = Z.t() * pt; + solve(C, ptDotN, imd); + alpha = 1 - cv::sum(imd)[0]; + solve(C, Cones, Cinv); + beta = cv::sum(Cinv)[0]; //% sum of elements of inv(corr) + lagrangeMult = alpha / beta; + solve(C, ptDotN + lagrangeMult * Cones, weights); + + float sum = cv::sum(weights)[0]; + weights = weights / sum; + + int cMaj_i = findColMajorInd(i, img.rows, img.cols); + + for (int j = 0; j < k; j++) + { + int cMaj_ind_j = findColMajorInd(indm[ind][j], img.rows, img.cols); + triplets.push_back(T(cMaj_i, cMaj_ind_j, weights.at(j, 0))); + td.push_back(T(cMaj_i, cMaj_i, weights.at(j, 0))); + } + ind++; + } + + Wcm.setFromTriplets(triplets.begin(), triplets.end()); + Dcm.setFromTriplets(td.begin(), td.end()); +} + +void cm(Mat& image, Mat& tmap, SparseMatrix& Wcm, SparseMatrix& Dcm) +{ + my_vector_of_vectors_t samples, indm, Euu; + + int i, j; + std::unordered_set unk; + for (i = 0; i < tmap.rows; i++) + { + for (j = 0; j < tmap.cols; j++) + { + uchar pix = tmap.at(i, j); + if (pix == 128) + unk.insert(i * tmap.cols + j); + } + } + + kdtree_CM(image, indm, samples, unk); + float eps = 0.00001; + lle(indm, samples, eps, unk, Wcm, Dcm, image); + CV_LOG_INFO(NULL, "ALPHAMAT: cm DONE"); +} + +}} // namespace cv::alphamat diff --git a/modules/alphamat/src/cm.hpp b/modules/alphamat/src/cm.hpp new file mode 100644 index 00000000000..2e6baf431e3 --- /dev/null +++ b/modules/alphamat/src/cm.hpp @@ -0,0 +1,17 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef __OPENCV_ALPHAMAT_CM_H__ +#define __OPENCV_ALPHAMAT_CM_H__ + +namespace cv { namespace alphamat { + +using namespace Eigen; +using namespace nanoflann; + +void cm(Mat& image, Mat& tmap, SparseMatrix& Wcm, SparseMatrix& Dcm); + +}} + +#endif diff --git a/modules/alphamat/src/infoflow.cpp b/modules/alphamat/src/infoflow.cpp new file mode 100644 index 00000000000..e85ed8db5d1 --- /dev/null +++ b/modules/alphamat/src/infoflow.cpp @@ -0,0 +1,130 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "precomp.hpp" + +#include + +using namespace Eigen; + +namespace cv { namespace alphamat { + +static +void solve(SparseMatrix Wcm, SparseMatrix Wuu, SparseMatrix Wl, SparseMatrix Dcm, + SparseMatrix Duu, SparseMatrix Dl, SparseMatrix T, + Mat& wf, Mat& alpha) +{ + float suu = 0.01, sl = 0.1, lamd = 100; + + SparseMatrix Lifm = ((Dcm - Wcm).transpose()) * (Dcm - Wcm) + sl * (Dl - Wl) + suu * (Duu - Wuu); + + SparseMatrix A; + int n = wf.rows; + VectorXd b(n), x(n); + + Eigen::VectorXd wf_; + cv2eigen(wf, wf_); + + A = Lifm + lamd * T; + b = (lamd * T) * (wf_); + + ConjugateGradient, Lower | Upper> cg; + + cg.setMaxIterations(500); + cg.compute(A); + x = cg.solve(b); + CV_LOG_INFO(NULL, "ALPHAMAT: #iterations: " << cg.iterations()); + CV_LOG_INFO(NULL, "ALPHAMAT: estimated error: " << cg.error()); + + int nRows = alpha.rows; + int nCols = alpha.cols; + float pix_alpha; + for (int j = 0; j < nCols; ++j) + { + for (int i = 0; i < nRows; ++i) + { + pix_alpha = x(i + j * nRows); + if (pix_alpha < 0) + pix_alpha = 0; + if (pix_alpha > 1) + pix_alpha = 1; + alpha.at(i, j) = uchar(pix_alpha * 255); + } + } +} + +void infoFlow(InputArray image_ia, InputArray tmap_ia, OutputArray result) +{ + Mat image = image_ia.getMat(); + Mat tmap = tmap_ia.getMat(); + + int64 begin = cv::getTickCount(); + + int nRows = image.rows; + int nCols = image.cols; + int N = nRows * nCols; + + SparseMatrix T(N, N); + typedef Triplet Tr; + std::vector triplets; + + //Pre-process trimap + for (int i = 0; i < nRows; ++i) + { + for (int j = 0; j < nCols; ++j) + { + uchar& pix = tmap.at(i, j); + if (pix <= 0.2f * 255) + pix = 0; + else if (pix >= 0.8f * 255) + pix = 255; + else + pix = 128; + } + } + + Mat wf = Mat::zeros(nRows * nCols, 1, CV_8U); + + // Column Major Interpretation for working with SparseMatrix + for (int i = 0; i < nRows; ++i) + { + for (int j = 0; j < nCols; ++j) + { + uchar pix = tmap.at(i, j); + + // collection of known pixels samples + triplets.push_back(Tr(i + j * nRows, i + j * nRows, (pix != 128) ? 1 : 0)); + + // foreground pixel + wf.at(i + j * nRows, 0) = (pix > 200) ? 1 : 0; + } + } + + SparseMatrix Wl(N, N), Dl(N, N); + local_info(image, tmap, Wl, Dl); + + SparseMatrix Wcm(N, N), Dcm(N, N); + cm(image, tmap, Wcm, Dcm); + + Mat new_tmap = tmap.clone(); + + SparseMatrix Wuu(N, N), Duu(N, N); + Mat image_t = image.t(); + Mat tmap_t = tmap.t(); + UU(image, tmap, Wuu, Duu); + + double elapsed_secs = ((double)(getTickCount() - begin)) / getTickFrequency(); + + T.setFromTriplets(triplets.begin(), triplets.end()); + + Mat alpha = Mat::zeros(nRows, nCols, CV_8UC1); + solve(Wcm, Wuu, Wl, Dcm, Duu, Dl, T, wf, alpha); + + alpha.copyTo(result); + + elapsed_secs = ((double)(getTickCount() - begin)) / getTickFrequency(); + CV_LOG_INFO(NULL, "ALPHAMAT: total time: " << elapsed_secs); +} + +}} // namespace cv::alphamat diff --git a/modules/alphamat/src/intraU.cpp b/modules/alphamat/src/intraU.cpp new file mode 100644 index 00000000000..167458fcfc1 --- /dev/null +++ b/modules/alphamat/src/intraU.cpp @@ -0,0 +1,152 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "precomp.hpp" +#include "intraU.hpp" + +namespace cv { namespace alphamat { + +int findColMajorInd(int rowMajorInd, int nRows, int nCols) +{ + int iInd = rowMajorInd / nCols; + int jInd = rowMajorInd % nCols; + return (jInd * nRows + iInd); +} + +static +void generateFVectorIntraU(my_vector_of_vectors_t& samples, Mat& img, Mat& tmap, std::vector& orig_ind) +{ + int nRows = img.rows; + int nCols = img.cols; + int unk_count = 0; + int i, j; + for (i = 0; i < nRows; ++i) + { + for (j = 0; j < nCols; ++j) + { + uchar pix = tmap.at(i, j); + if (pix == 128) + unk_count++; + } + } + samples.resize(unk_count); + orig_ind.resize(unk_count); + + int c1 = 0; + for (i = 0; i < nRows; ++i) + { + for (j = 0; j < nCols; ++j) + { + uchar pix = tmap.at(i, j); + if (pix == 128) // collection of unknown pixels samples + { + samples[c1].resize(ALPHAMAT_DIM); + samples[c1][0] = img.at(i, j)[0] / 255.0; + samples[c1][1] = img.at(i, j)[1] / 255.0; + samples[c1][2] = img.at(i, j)[2] / 255.0; + samples[c1][3] = (double(i + 1) / nRows) / 20; + samples[c1][4] = (double(j + 1) / nCols) / 20; + orig_ind[c1] = i * nCols + j; + c1++; + } + } + } + + CV_LOG_INFO(NULL, "ALPHAMAT: Total number of unknown pixels : " << c1); +} + +static +void kdtree_intraU(Mat& img, Mat& tmap, my_vector_of_vectors_t& indm, my_vector_of_vectors_t& samples, std::vector& orig_ind) +{ + // Generate feature vectors for intra U: + generateFVectorIntraU(samples, img, tmap, orig_ind); + + typedef KDTreeVectorOfVectorsAdaptor my_kd_tree_t; + my_kd_tree_t mat_index(ALPHAMAT_DIM /*dim*/, samples, 10 /* max leaf */); + mat_index.index->buildIndex(); + // do a knn search with ku = 5 + const size_t num_results = 5 + 1; + + int N = samples.size(); // no. of unknown samples + + std::vector ret_indexes(num_results); + std::vector out_dists_sqr(num_results); + nanoflann::KNNResultSet resultSet(num_results); + + indm.resize(N); + for (int i = 0; i < N; i++) + { + resultSet.init(&ret_indexes[0], &out_dists_sqr[0]); + mat_index.index->findNeighbors(resultSet, &samples[i][0], nanoflann::SearchParams(10)); + + indm[i].resize(num_results - 1); + for (std::size_t j = 1; j < num_results; j++) + { + indm[i][j - 1] = ret_indexes[j]; + } + } +} + +static +double l1norm(std::vector& x, std::vector& y) +{ + double sum = 0; + for (int i = 0; i < ALPHAMAT_DIM; i++) + sum += abs(x[i] - y[i]); + return sum / ALPHAMAT_DIM; +} + +static +void intraU(Mat& img, my_vector_of_vectors_t& indm, my_vector_of_vectors_t& samples, + std::vector& orig_ind, SparseMatrix& Wuu, SparseMatrix& Duu) +{ + // input: indm, samples + int n = indm.size(); // num of unknown samples + CV_LOG_INFO(NULL, "ALPHAMAT: num of unknown samples, n : " << n); + + int i, j, nbr_ind; + for (i = 0; i < n; i++) + { + samples[i][3] *= 1 / 100; + samples[i][4] *= 1 / 100; + } + + my_vector_of_vectors_t weights; + typedef Triplet T; + std::vector triplets, td; + + double weight; + for (i = 0; i < n; i++) + { + int num_nbr = indm[i].size(); + int cMaj_i = findColMajorInd(orig_ind[i], img.rows, img.cols); + for (j = 0; j < num_nbr; j++) + { + nbr_ind = indm[i][j]; + int cMaj_nbr_j = findColMajorInd(orig_ind[nbr_ind], img.rows, img.cols); + weight = max(1 - l1norm(samples[i], samples[j]), 0.0); + + triplets.push_back(T(cMaj_i, cMaj_nbr_j, weight / 2)); + td.push_back(T(cMaj_i, cMaj_i, weight / 2)); + + triplets.push_back(T(cMaj_nbr_j, cMaj_i, weight / 2)); + td.push_back(T(cMaj_nbr_j, cMaj_nbr_j, weight / 2)); + } + } + + Wuu.setFromTriplets(triplets.begin(), triplets.end()); + Duu.setFromTriplets(td.begin(), td.end()); +} + +void UU(Mat& image, Mat& tmap, SparseMatrix& Wuu, SparseMatrix& Duu) +{ + my_vector_of_vectors_t samples, indm; + std::vector orig_ind; + + kdtree_intraU(image, tmap, indm, samples, orig_ind); + intraU(image, indm, samples, orig_ind, Wuu, Duu); + CV_LOG_INFO(NULL, "ALPHAMAT: Intra U Done"); +} + +}} // namespace cv::alphamat diff --git a/modules/alphamat/src/intraU.hpp b/modules/alphamat/src/intraU.hpp new file mode 100644 index 00000000000..fdd7dcf639f --- /dev/null +++ b/modules/alphamat/src/intraU.hpp @@ -0,0 +1,23 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef __OPENCV_ALPHAMAT_INTRAU_H__ +#define __OPENCV_ALPHAMAT_INTRAU_H__ + +namespace cv { namespace alphamat { + +const int ALPHAMAT_DIM = 5; // dimension of feature vectors + +using namespace Eigen; +using namespace nanoflann; + +typedef std::vector> my_vector_of_vectors_t; + +int findColMajorInd(int rowMajorInd, int nRows, int nCols); + +void UU(Mat& image, Mat& tmap, SparseMatrix& Wuu, SparseMatrix& Duu); + +}} // namespace + +#endif diff --git a/modules/alphamat/src/local_info.cpp b/modules/alphamat/src/local_info.cpp new file mode 100644 index 00000000000..5460e864351 --- /dev/null +++ b/modules/alphamat/src/local_info.cpp @@ -0,0 +1,153 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// #ifndef local_info +// #define local_info + +#include "precomp.hpp" +#include "local_info.hpp" + +namespace cv { namespace alphamat { + +void local_info(Mat& img, Mat& tmap, SparseMatrix& Wl, SparseMatrix& Dl) +{ + float eps = 0.000001; + int win_size = 1; + + int nRows = img.rows; + int nCols = img.cols; + int N = img.rows * img.cols; + Mat unk_img = Mat::zeros(cv::Size(nCols, nRows), CV_32FC1); + + for (int i = 0; i < nRows; ++i) + { + for (int j = 0; j < nCols; ++j) + { + uchar pix = tmap.at(i, j); + if (pix == 128) // collection of unknown pixels samples + { + unk_img.at(i, j) = 255; + } + } + } + + Mat element = getStructuringElement(MORPH_RECT, Size(2 * win_size + 1, 2 * win_size + 1)); + /// Apply the dilation operation + Mat dilation_dst = unk_img.clone(); + //dilate(unk_img, dilation_dst, element); + + int num_win = (win_size * 2 + 1) * (win_size * 2 + 1); // number of pixels in window + typedef Triplet T; + std::vector triplets, td, tl; + int neighInd[9]; + int i, j; + for (j = win_size; j < nCols - win_size; j++) + { + for (i = win_size; i < nRows - win_size; i++) + { + uchar pix = tmap.at(i, j); + //std::cout << i+j*nRows << " --> " << pix << std::endl; + if (pix != 128) + continue; + // extract the window out of image + Mat win = img.rowRange(i - win_size, i + win_size + 1); + win = win.colRange(j - win_size, j + win_size + 1); + Mat win_ravel = Mat::zeros(9, 3, CV_64F); // doubt ?? + double sum1 = 0; + double sum2 = 0; + double sum3 = 0; + + int c = 0; + for (int q = -1; q <= 1; q++) + { + for (int p = -1; p <= 1; p++) + { + neighInd[c] = (j + q) * nRows + (i + p); // column major + c++; + } + } + + c = 0; + //parsing column major way in the window + for (int q = 0; q < win_size * 2 + 1; q++) + { + for (int p = 0; p < win_size * 2 + 1; p++) + { + win_ravel.at(c, 0) = win.at(p, q)[0] / 255.0; + win_ravel.at(c, 1) = win.at(p, q)[1] / 255.0; + win_ravel.at(c, 2) = win.at(p, q)[2] / 255.0; + sum1 += win.at(p, q)[0] / 255.0; + sum2 += win.at(p, q)[1] / 255.0; + sum3 += win.at(p, q)[2] / 255.0; + c++; + } + } + win = win_ravel; + Mat win_mean = Mat::zeros(1, 3, CV_64F); + win_mean.at(0, 0) = sum1 / num_win; + win_mean.at(0, 1) = sum2 / num_win; + win_mean.at(0, 2) = sum3 / num_win; + + // calculate the covariance matrix + Mat covariance = (win.t() * win / num_win) - (win_mean.t() * win_mean); + + Mat I = Mat::eye(img.channels(), img.channels(), CV_64F); + Mat I1 = (covariance + (eps / num_win) * I); + Mat I1_inv = I1.inv(); + + Mat X = win - repeat(win_mean, num_win, 1); + Mat vals = (1 + X * I1_inv * X.t()) / num_win; + + for (int q = 0; q < num_win; q++) + { + for (int p = 0; p < num_win; p++) + { + triplets.push_back(T(neighInd[p], neighInd[q], vals.at(p, q))); + } + } + } + } + + std::vector tsp; + SparseMatrix W(N, N), Wsp(N, N); + W.setFromTriplets(triplets.begin(), triplets.end()); + + SparseMatrix Wt = W.transpose(); + SparseMatrix Ws = Wt + W; + W = Ws; + + for (int k = 0; k < W.outerSize(); ++k) + { + double sumCol = 0; + for (SparseMatrix::InnerIterator it(W, k); it; ++it) + { + sumCol += it.value(); + } + if (sumCol < 0.05) + sumCol = 1; + tsp.push_back(T(k, k, 1 / sumCol)); + } + Wsp.setFromTriplets(tsp.begin(), tsp.end()); + + Wl = Wsp * W; // For normalization + //Wl = W; // No normalization + + SparseMatrix Wlt = Wl.transpose(); + + for (int k = 0; k < Wlt.outerSize(); ++k) + { + double sumarr = 0; + for (SparseMatrix::InnerIterator it(Wlt, k); it; ++it) + sumarr += it.value(); + td.push_back(T(k, k, sumarr)); + } + + Dl.setFromTriplets(td.begin(), td.end()); + + CV_LOG_INFO(NULL, "ALPHAMAT: local_info DONE"); +} + +}} // namespace cv::alphamat + +// #endif diff --git a/modules/alphamat/src/local_info.hpp b/modules/alphamat/src/local_info.hpp new file mode 100644 index 00000000000..178add2e579 --- /dev/null +++ b/modules/alphamat/src/local_info.hpp @@ -0,0 +1,17 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef __OPENCV_ALPHAMAT_LOCAL_INFO_H__ +#define __OPENCV_ALPHAMAT_LOCAL_INFO_H__ + + +namespace cv { namespace alphamat { + +using namespace Eigen; + +void local_info(Mat& img, Mat& tmap, SparseMatrix& Wl, SparseMatrix& Dl); + +}} // namespace + +#endif diff --git a/modules/alphamat/src/precomp.hpp b/modules/alphamat/src/precomp.hpp new file mode 100644 index 00000000000..e043d813ded --- /dev/null +++ b/modules/alphamat/src/precomp.hpp @@ -0,0 +1,31 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef __OPENCV_PRECOMP_H__ +#define __OPENCV_PRECOMP_H__ + +#include +#include +#include + +#include +#include +#include + +#include + +#include "3rdparty/nanoflann.hpp" +#include "3rdparty/KDTreeVectorOfVectorsAdaptor.h" + +#ifdef HAVE_EIGEN +#include +#include +#include +#endif + +#include "intraU.hpp" +#include "cm.hpp" +#include "local_info.hpp" + +#endif diff --git a/modules/alphamat/tutorials/alphamat_tutorial.markdown b/modules/alphamat/tutorials/alphamat_tutorial.markdown new file mode 100644 index 00000000000..03cd329f5f2 --- /dev/null +++ b/modules/alphamat/tutorials/alphamat_tutorial.markdown @@ -0,0 +1,53 @@ +Information Flow Alpha Matting {#tutorial_alphamat} +============================ + +This project was part of Google Summer of Code 2019. + +*Student:* Muskaan Kularia + +*Mentor:* Sunita Nayak + +Alphamatting is the problem of extracting the foreground from an image. The extracted foreground can be used for further operations like changing the background in an image. + +Given an input image and its corresponding trimap, we try to extract the foreground from the background. Following is an example: + +Input Image: ![](samples/input_images/plant.jpg) +Input Trimap: ![](samples/trimaps/plant.png) +Output alpha Matte: ![](samples/output_mattes/plant_result.jpg) + +This project is implementation of @cite aksoy2017designing . It required implementation of parts of other papers [2,3,4]. + +# Building + +This module uses the Eigen package. + +Build the sample code of the alphamat module using the following two cmake commands run inside the build folder: +``` +cmake -DOPENCV_EXTRA_MODULES_PATH= -DBUILD_EXAMPLES=ON .. + +cmake --build . --config Release --target example_alphamat_information_flow_matting +``` +Please refer to OpenCV building tutorials for further details, if needed. + +# Testing + +The built target can be tested as follows: +``` +example_alphamat_information_flow_matting -img= -tri= -out= +``` +# Source Code of the sample + +@includelineno alphamat/samples/information_flow_matting.cpp + + +# References + +[1] Yagiz Aksoy, Tunc Ozan Aydin, Marc Pollefeys, "[Designing Effective Inter-Pixel Information Flow for Natural Image Matting](http://people.inf.ethz.ch/aksoyy/ifm/)", CVPR, 2017. + +[2] Roweis, Sam T., and Lawrence K. Saul. "[Nonlinear dimensionality reduction by locally linear embedding](https://science.sciencemag.org/content/290/5500/2323)" Science 290.5500 (2000): 2323-2326. + +[3] Anat Levin, Dani Lischinski, Yair Weiss, "[A Closed Form Solution to Natural Image Matting](https://www.researchgate.net/publication/5764820_A_Closed-Form_Solution_to_Natural_Image_Matting)", IEEE TPAMI, 2008. + +[4] Qifeng Chen, Dingzeyu Li, Chi-Keung Tang, "[KNN Matting](http://dingzeyu.li/files/knn-matting-tpami.pdf)", IEEE TPAMI, 2013. + +[5] Yagiz Aksoy, "[Affinity Based Matting Toolbox](https://github.com/yaksoy/AffinityBasedMattingToolbox)". diff --git a/modules/aruco/include/opencv2/aruco.hpp b/modules/aruco/include/opencv2/aruco.hpp index f0d34ed98ac..3cf62d2accd 100644 --- a/modules/aruco/include/opencv2/aruco.hpp +++ b/modules/aruco/include/opencv2/aruco.hpp @@ -392,8 +392,8 @@ class CV_EXPORTS_W GridBoard : public Board { * Note that returning a 0 means the pose has not been estimated. */ CV_EXPORTS_W int estimatePoseBoard(InputArrayOfArrays corners, InputArray ids, const Ptr &board, - InputArray cameraMatrix, InputArray distCoeffs, OutputArray rvec, - OutputArray tvec, bool useExtrinsicGuess = false); + InputArray cameraMatrix, InputArray distCoeffs, InputOutputArray rvec, + InputOutputArray tvec, bool useExtrinsicGuess = false); diff --git a/modules/aruco/include/opencv2/aruco/charuco.hpp b/modules/aruco/include/opencv2/aruco/charuco.hpp index 14aa5713e87..2e6ae62865a 100644 --- a/modules/aruco/include/opencv2/aruco/charuco.hpp +++ b/modules/aruco/include/opencv2/aruco/charuco.hpp @@ -184,8 +184,8 @@ CV_EXPORTS_W int interpolateCornersCharuco(InputArrayOfArrays markerCorners, Inp */ CV_EXPORTS_W bool estimatePoseCharucoBoard(InputArray charucoCorners, InputArray charucoIds, const Ptr &board, InputArray cameraMatrix, - InputArray distCoeffs, OutputArray rvec, OutputArray tvec, - bool useExtrinsicGuess = false); + InputArray distCoeffs, InputOutputArray rvec, + InputOutputArray tvec, bool useExtrinsicGuess = false); diff --git a/modules/aruco/misc/pattern_generator/MarkerPrinter.py b/modules/aruco/misc/pattern_generator/MarkerPrinter.py new file mode 100644 index 00000000000..301f4f918fe --- /dev/null +++ b/modules/aruco/misc/pattern_generator/MarkerPrinter.py @@ -0,0 +1,1296 @@ +#!/usr/bin/env python3 + +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright (c) 2019, Josh Chien. All rights reserved. + +from argparse import ArgumentParser +import numpy as np +from PIL import Image +import io +import warnings +import os +import cairo +from cairosvg import svg2png +import math +import tempfile + +def SaveArucoDictBytesList(filePath = "arucoDictBytesList.npz"): + import numpy as np + + # cv2 is optional dependency + try: + import cv2 + from cv2 import aruco + + # Name, Flag + dictInfo = \ + [ + ("DICT_4X4_1000", aruco.DICT_4X4_1000), + ("DICT_5X5_1000", aruco.DICT_5X5_1000), + ("DICT_6X6_1000", aruco.DICT_6X6_1000), + ("DICT_7X7_1000", aruco.DICT_7X7_1000), + ("DICT_ARUCO_ORIGINAL", aruco.DICT_ARUCO_ORIGINAL), + ("DICT_APRILTAG_16h5", aruco.DICT_APRILTAG_16h5), + ("DICT_APRILTAG_25h9", aruco.DICT_APRILTAG_25h9), + ("DICT_APRILTAG_36h10", aruco.DICT_APRILTAG_36h10), + ("DICT_APRILTAG_36h11", aruco.DICT_APRILTAG_36h11), + ] + + arucoDictBytesList = {} + for name, flag in dictInfo: + arucoDict = aruco.Dictionary_get(flag) + arucoDictBytesList[name] = arucoDict.bytesList + + np.savez_compressed(filePath, **arucoDictBytesList) + return arucoDictBytesList + + except Exception as e: + warnings.warn(str(e)) + return None + + return None + +class MarkerPrinter: + + debugMode = None # "LINE" "BLOCK" + + # Static Vars + # SVG https://oreillymedia.github.io/Using_SVG/guide/units.html + # for PDF and SVG, 1 pixel = 1/72 inch, 1 cm = 1/2.54 inch, 1pixl = 2.54/72 cm, 1cm = 72/2.54 pixels + ptPerMeter = 72 / 2.54 * 100 + + surface = { + ".SVG": cairo.SVGSurface, + ".PDF": cairo.PDFSurface, + ".PS": cairo.PSSurface } + + if (os.path.isfile("arucoDictBytesList.npz")): + arucoDictBytesList = np.load("arucoDictBytesList.npz") + else: + warnings.warn("Missing build-in arucoDictBytesList.npz, generate it again") + arucoDictBytesList = SaveArucoDictBytesList(filePath = "arucoDictBytesList.npz") + + arucoDictMarkerSize = \ + { + "DICT_4X4_1000": 4, + "DICT_5X5_1000": 5, + "DICT_6X6_1000": 6, + "DICT_7X7_1000": 7, + "DICT_ARUCO_ORIGINAL": 5, + "DICT_APRILTAG_16h5": 4, + "DICT_APRILTAG_25h9": 5, + "DICT_APRILTAG_36h10": 6, + "DICT_APRILTAG_36h11": 6, + } + + def ArucoBits(dictionary, markerID): + bytesList = MarkerPrinter.arucoDictBytesList[dictionary][markerID].ravel() + markerSize = MarkerPrinter.arucoDictMarkerSize[dictionary] + + arucoBits = np.zeros(shape = (markerSize, markerSize), dtype = bool) + base2List = np.array( [128, 64, 32, 16, 8, 4, 2, 1], dtype = np.uint8) + currentByteIdx = 0 + currentByte = bytesList[currentByteIdx] + currentBit = 0 + for row in range(markerSize): + for col in range(markerSize): + if(currentByte >= base2List[currentBit]): + arucoBits[row, col] = True + currentByte -= base2List[currentBit] + currentBit = currentBit + 1 + if(currentBit == 8): + currentByteIdx = currentByteIdx + 1 + currentByte = bytesList[currentByteIdx] + if(8 * (currentByteIdx + 1) > arucoBits.size): + currentBit = 8 * (currentByteIdx + 1) - arucoBits.size + else: + currentBit = 0; + return arucoBits + + def __DrawBlock(context, + dictionary = None, markerLength = None, borderBits = 1, + chessboardSize = (1, 1), squareLength = None, firstMarkerID = 0, + blockX = 0, blockY = 0, originX = 0, originY = 0, pageBorderX = 0, pageBorderY = 0, + mode = "CHESS" ): + + if(squareLength is None): + squareLength = markerLength + + if(markerLength is None): + markerLength = squareLength + + if((squareLength is None) or (markerLength is None)): + raise ValueError("lenght is None") + + dawMarkerBlock = False + if ((mode == "ARUCO") or (mode == "ARUCOGRID")): + dawMarkerBlock = True + elif(chessboardSize[1] % 2 == 0): + dawMarkerBlock = (( blockX % 2 == 0 ) == ( blockY % 2 == 0 )) + else: + dawMarkerBlock = (( blockX % 2 == 0 ) != ( blockY % 2 == 0 )) + + if(dawMarkerBlock): + if (mode != "CHESS"): + if(dictionary is None): + raise ValueError("dictionary is None") + + if (mode == "CHARUCO"): + originX = (blockX - originX) * squareLength + (squareLength - markerLength)*0.5 + pageBorderX + originY = (blockY - originY) * squareLength + (squareLength - markerLength)*0.5 + pageBorderY + else: + originX = (blockX - originX) * squareLength + pageBorderX + originY = (blockY - originY) * squareLength + pageBorderY + + context.set_source_rgba(0.0, 0.0, 0.0, 1.0) + context.rectangle(originX, originY, markerLength, markerLength) + context.fill() + + # Generate marker + if (mode == "CHARUCO"): + markerID = firstMarkerID + (blockY * chessboardSize[0] + blockX) // 2 + elif (mode == "ARUCO"): + markerID = firstMarkerID + elif (mode == "ARUCOGRID"): + markerID = firstMarkerID + (blockY * chessboardSize[0] + blockX) + + marker = MarkerPrinter.ArucoBits(dictionary, markerID) + markerSize = marker.shape[0] + unitLength = markerLength / (float)(markerSize + borderBits * 2) + + markerBitMap = np.zeros(shape = (markerSize+borderBits*2, markerSize+borderBits*2), dtype = bool) + markerBitMap[borderBits:-borderBits,borderBits:-borderBits] = marker + markerBitMap = np.swapaxes(markerBitMap, 0, 1) + + # Compute edges + hEdges = np.zeros(shape = (markerSize+1,markerSize+1), dtype = bool) + vEdges = np.zeros(shape = (markerSize+1,markerSize+1), dtype = bool) + + for mx in range(markerSize): + for my in range(markerSize+1): + if ( markerBitMap[mx + borderBits, my + borderBits - 1] ^ markerBitMap[mx + borderBits, my + borderBits]): + hEdges[mx, my] = True + + for mx in range(markerSize+1): + for my in range(markerSize): + if ( markerBitMap[mx + borderBits - 1, my + borderBits] ^ markerBitMap[mx + borderBits, my + borderBits]): + vEdges[mx, my] = True + + # Use for debug, check edge or position is correct or not + if(MarkerPrinter.debugMode is not None): + if(MarkerPrinter.debugMode.upper() == "LINE"): + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + context.set_line_width(unitLength * 0.1) + for mx in range(markerSize+1): + for my in range(markerSize+1): + if(hEdges[mx, my]): + context.move_to(originX + unitLength * (mx + borderBits ), originY + unitLength * (my + borderBits )) + context.line_to(originX + unitLength * (mx + borderBits + 1), originY + unitLength * (my + borderBits )) + context.stroke() + if(vEdges[mx, my]): + context.move_to(originX + unitLength * (mx + borderBits ), originY + unitLength * (my + borderBits )) + context.line_to(originX + unitLength * (mx + borderBits ), originY + unitLength * (my + borderBits + 1)) + context.stroke() + + elif(MarkerPrinter.debugMode.upper() == "BLOCK"): + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + for mx in range(markerSize): + for my in range(markerSize): + if(markerBitMap[mx + borderBits, my + borderBits]): + context.rectangle( + originX + unitLength * (mx + borderBits), + originY + unitLength * (my + borderBits), + unitLength, unitLength) + context.fill() + + else: + while(True): + found = False + + # Find start position + sx = 0 + sy = 0 + for my in range(markerSize): + for mx in range(markerSize): + if(hEdges[mx, my]): + found = True + sx = mx + sy = my + if(markerBitMap[sx + borderBits, sy + borderBits - 1]): + context.set_source_rgba(0.0, 0.0, 0.0, 1.0) + else: + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + break + if(found): + break + + context.move_to (originX + unitLength * (sx + borderBits), originY + unitLength * (sy + borderBits)) + + # Use wall follower maze solving algorithm to draw white part + cx = sx + cy = sy + cd = 3 # 0 right, 1 down, 2 left, 3 up + while(True): + nd = (cd + 1)%4 + moved = False + if(nd == 0): + if(hEdges[cx, cy]): + hEdges[cx, cy] = False + cx = cx + 1 + moved = True + elif(nd == 1): + if(vEdges[cx, cy]): + vEdges[cx, cy] = False + cy = cy + 1 + moved = True + elif(nd == 2): + if(hEdges[cx - 1, cy]): + hEdges[cx - 1, cy] = False + cx = cx - 1 + moved = True + elif(nd == 3): + if(vEdges[cx, cy - 1]): + vEdges[cx, cy - 1] = False + cy = cy - 1 + moved = True + + if((cx == sx) and (cy == sy)): + context.close_path () + break + else: + if(moved): + context.line_to(originX + unitLength * (cx + borderBits), originY + unitLength * (cy + borderBits)) + cd = nd + + if (found): + context.fill() + else: + break + + else: + originX = (blockX - originX) * squareLength + pageBorderX + originY = (blockY - originY) * squareLength + pageBorderY + context.set_source_rgba(0.0, 0.0, 0.0, 1.0) + context.rectangle(originX, originY, squareLength, squareLength) + context.fill() + + def __CheckChessMarkerImage(chessboardSize, squareLength, subSize=None, pageBorder=(0,0)): + if(len(chessboardSize) != 2): + raise ValueError("len(chessboardSize) != 2") + else: + sizeX, sizeY = chessboardSize + + if(len(pageBorder) != 2): + raise ValueError("len(pageBorder) != 2") + else: + pageBorderX, pageBorderY = pageBorder + + if(sizeX <= 1): + raise ValueError("sizeX <= 1") + + if(sizeY <= 1): + raise ValueError("sizeY <= 1") + + if(squareLength <= 0): + raise ValueError("squareLength <= 0") + + if(pageBorderX < 0): + raise ValueError("pageBorderX < 0") + + if(pageBorderY < 0): + raise ValueError("pageBorderY < 0") + + if(subSize is not None): + subSizeX, subSizeY = subSize + + if(subSizeX < 0): + raise ValueError("subSizeX < 0") + + if(subSizeY < 0): + raise ValueError("subSizeY < 0") + + def PreviewChessMarkerImage(chessboardSize, squareLength, pageBorder=(0, 0), dpi=96): + MarkerPrinter.__CheckChessMarkerImage(chessboardSize, squareLength, pageBorder=pageBorder) + + squareLength = squareLength * MarkerPrinter.ptPerMeter + pageBorder = (pageBorder[0] * MarkerPrinter.ptPerMeter, pageBorder[1] * MarkerPrinter.ptPerMeter) + + prevImage = None + with tempfile.TemporaryDirectory() as tmpdirname: + with MarkerPrinter.surface[".SVG"] ( + os.path.join(tmpdirname, "tempSVG.svg"), + chessboardSize[0] * squareLength + pageBorder[0] * 2, + chessboardSize[1] * squareLength + pageBorder[1] * 2) as surface: + context = cairo.Context(surface) + + context.set_source_rgba(0.5, 0.5, 0.5, 1.0) + context.rectangle(0, 0, + chessboardSize[0] * squareLength + pageBorder[0] * 2, + chessboardSize[1] * squareLength + pageBorder[1] * 2) + context.fill() + + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + context.rectangle(pageBorder[0], pageBorder[1], + chessboardSize[0] * squareLength, + chessboardSize[1] * squareLength) + context.fill() + + for bx in range(chessboardSize[0]): + for by in range(chessboardSize[1]): + MarkerPrinter.__DrawBlock( + context = context, + chessboardSize = chessboardSize, + squareLength = squareLength, + blockX = bx, + blockY = by, + pageBorderX = pageBorder[0], + pageBorderY = pageBorder[1], + mode = "CHESS") + + with open(os.path.join(tmpdirname, "tempSVG.svg")) as file: + prevImage = Image.open(io.BytesIO(svg2png(bytestring=file.read(), dpi=dpi))) + + return prevImage + + def GenChessMarkerImage(filePath, chessboardSize, squareLength, subSize=None, pageBorder=(0, 0)): + MarkerPrinter.__CheckChessMarkerImage(chessboardSize, squareLength, subSize=subSize, pageBorder=pageBorder) + + squareLength = squareLength * MarkerPrinter.ptPerMeter + pageBorder = (pageBorder[0] * MarkerPrinter.ptPerMeter, pageBorder[1] * MarkerPrinter.ptPerMeter) + + # Check + path, nameExt = os.path.split(filePath) + name, ext = os.path.splitext(nameExt) + + if(len(path) > 0): + if not(os.path.isdir(path)): + os.makedirs(path) + + if((ext.upper() != ".SVG") and (ext.upper() != ".PS") and (ext.upper() != ".PDF")): + raise ValueError("file extention is not supported, should be: svg, ps, pdf") + + # Draw + with MarkerPrinter.surface[ext.upper()] ( + filePath, + chessboardSize[0] * squareLength + pageBorder[0] * 2, + chessboardSize[1] * squareLength + pageBorder[1] * 2) as surface: + context = cairo.Context(surface) + + context.set_source_rgba(0.5, 0.5, 0.5, 1.0) + context.rectangle(0, 0, + chessboardSize[0] * squareLength + pageBorder[0] * 2, + chessboardSize[1] * squareLength + pageBorder[1] * 2) + context.fill() + + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + context.rectangle(pageBorder[0], pageBorder[1], + chessboardSize[0] * squareLength, + chessboardSize[1] * squareLength) + context.fill() + + for bx in range(chessboardSize[0]): + for by in range(chessboardSize[1]): + MarkerPrinter.__DrawBlock( + context = context, + chessboardSize = chessboardSize, + squareLength = squareLength, + blockX = bx, + blockY = by, + pageBorderX = pageBorder[0], + pageBorderY = pageBorder[1], + mode = "CHESS" ) + + if(subSize is not None): + subDivide = (\ + chessboardSize[0] // subSize[0] + int(chessboardSize[0] % subSize[0] > 0), + chessboardSize[1] // subSize[1] + int(chessboardSize[1] % subSize[1] > 0)) + + subChessboardBlockX = np.clip ( np.arange(0, subSize[0] * subDivide[0] + 1, subSize[0]), 0, chessboardSize[0]) + subChessboardBlockY = np.clip ( np.arange(0, subSize[1] * subDivide[1] + 1, subSize[1]), 0, chessboardSize[1]) + + subChessboardSliceX = subChessboardBlockX.astype(np.float) * squareLength + subChessboardSliceY = subChessboardBlockY.astype(np.float) * squareLength + + for subXID in range(subDivide[0]): + for subYID in range(subDivide[1]): + subName = name + \ + "_X" + str(subChessboardBlockX[subXID]) + "_" + str(subChessboardBlockX[subXID+1]) + \ + "_Y" + str(subChessboardBlockY[subYID]) + "_" + str(subChessboardBlockY[subYID+1]) + + with MarkerPrinter.surface[ext.upper()]( + os.path.join(path, subName + ext), + subChessboardSliceX[subXID+1] - subChessboardSliceX[subXID] + pageBorder[0] * 2, + subChessboardSliceY[subYID+1] - subChessboardSliceY[subYID] + pageBorder[1] * 2) as surface: + context = cairo.Context(surface) + + context.set_source_rgba(0.5, 0.5, 0.5, 1.0) + context.rectangle(0, 0, + subChessboardSliceX[subXID+1] - subChessboardSliceX[subXID] + pageBorder[0] * 2, + subChessboardSliceY[subYID+1] - subChessboardSliceY[subYID] + pageBorder[1] * 2) + context.fill() + + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + context.rectangle(pageBorder[0], pageBorder[1], + subChessboardSliceX[subXID+1] - subChessboardSliceX[subXID], + subChessboardSliceY[subYID+1] - subChessboardSliceY[subYID]) + context.fill() + + for bx in range(subChessboardBlockX[subXID+1] - subChessboardBlockX[subXID]): + for by in range(subChessboardBlockY[subYID+1] - subChessboardBlockY[subYID]): + MarkerPrinter.__DrawBlock( + context = context, + chessboardSize = chessboardSize, + squareLength = squareLength, + blockX = subChessboardBlockX[subXID] + bx, + blockY = subChessboardBlockY[subYID] + by, + originX = subChessboardBlockX[subXID], + originY = subChessboardBlockY[subYID], + pageBorderX = pageBorder[0], + pageBorderY = pageBorder[1], + mode = "CHESS" ) + + + def __CheckArucoMarkerImage(dictionary, markerID, markerLength, borderBits=1, pageBorder=(0, 0)): + if(len(pageBorder) != 2): + raise ValueError("len(pageBorder) != 2") + else: + pageBorderX, pageBorderY = pageBorder + + if not (dictionary in MarkerPrinter.arucoDictBytesList): + raise ValueError("dictionary is not support") + + if(MarkerPrinter.arucoDictBytesList[dictionary].shape[0] <= markerID ): + raise ValueError("markerID is not in aruce dictionary") + + if(markerID < 0): + raise ValueError("markerID < 0") + + if(markerLength <= 0): + raise ValueError("markerLength <= 0") + + if(borderBits <= 0): + raise ValueError("borderBits <= 0") + + if(pageBorderX < 0): + raise ValueError("pageBorderX < 0") + + if(pageBorderY < 0): + raise ValueError("pageBorderY < 0") + + def PreviewArucoMarkerImage(dictionary, markerID, markerLength, borderBits=1, pageBorder=(0, 0), dpi=96): + MarkerPrinter.__CheckArucoMarkerImage(dictionary, markerID, markerLength, borderBits=borderBits, pageBorder=pageBorder) + + markerLength = markerLength * MarkerPrinter.ptPerMeter + pageBorder = (pageBorder[0] * MarkerPrinter.ptPerMeter, pageBorder[1] * MarkerPrinter.ptPerMeter) + + prevImage = None + with tempfile.TemporaryDirectory() as tmpdirname: + with MarkerPrinter.surface[".SVG"] ( + os.path.join(tmpdirname, "tempSVG.svg"), + markerLength + pageBorder[0] * 2, + markerLength + pageBorder[1] * 2) as surface: + context = cairo.Context(surface) + + context.set_source_rgba(0.5, 0.5, 0.5, 1.0) + context.rectangle(0, 0, + markerLength + pageBorder[0] * 2, + markerLength + pageBorder[1] * 2) + context.fill() + + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + context.rectangle(pageBorder[0], pageBorder[1], + markerLength, + markerLength) + context.fill() + + MarkerPrinter.__DrawBlock( + context = context, + dictionary = dictionary, + markerLength = markerLength, + borderBits = borderBits, + firstMarkerID = markerID, + pageBorderX = pageBorder[0], + pageBorderY = pageBorder[1], + mode = "ARUCO") + + with open(os.path.join(tmpdirname, "tempSVG.svg")) as file: + prevImage = Image.open(io.BytesIO(svg2png(bytestring=file.read(), dpi=dpi))) + + return prevImage + + def GenArucoMarkerImage(filePath, dictionary, markerID, markerLength, borderBits=1, pageBorder=(0, 0)): + MarkerPrinter.__CheckArucoMarkerImage(dictionary, markerID, markerLength, borderBits=borderBits, pageBorder=pageBorder) + + markerLength = markerLength * MarkerPrinter.ptPerMeter + pageBorder = (pageBorder[0] * MarkerPrinter.ptPerMeter, pageBorder[1] * MarkerPrinter.ptPerMeter) + + # Check + path, nameExt = os.path.split(filePath) + name, ext = os.path.splitext(nameExt) + + if(len(path) > 0): + if not(os.path.isdir(path)): + os.makedirs(path) + + if((ext.upper() != ".SVG") and (ext.upper() != ".PS") and (ext.upper() != ".PDF")): + raise ValueError("file extention is not supported, should be: svg, ps, pdf") + + # Draw + with MarkerPrinter.surface[ext.upper()] ( + filePath, + markerLength + pageBorder[0] * 2, + markerLength + pageBorder[1] * 2) as surface: + context = cairo.Context(surface) + + context.set_source_rgba(0.5, 0.5, 0.5, 1.0) + context.rectangle(0, 0, + markerLength + pageBorder[0] * 2, + markerLength + pageBorder[1] * 2) + context.fill() + + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + context.rectangle(pageBorder[0], pageBorder[1], + markerLength, + markerLength) + context.fill() + + MarkerPrinter.__DrawBlock( + context = context, + dictionary = dictionary, + markerLength = markerLength, + borderBits = borderBits, + firstMarkerID = markerID, + pageBorderX = pageBorder[0], + pageBorderY = pageBorder[1], + mode = "ARUCO") + + def __CheckCharucoMarkerImage(dictionary, chessboardSize, squareLength, markerLength, borderBits=1, subSize=None, pageBorder=(0, 0)): + if(len(chessboardSize) != 2): + raise ValueError("len(chessboardSize) != 2") + else: + sizeX, sizeY = chessboardSize + + if(len(pageBorder) != 2): + raise ValueError("len(pageBorder) != 2") + else: + pageBorderX, pageBorderY = pageBorder + + if not (dictionary in MarkerPrinter.arucoDictBytesList): + raise ValueError("dictionary is not support") + + if(MarkerPrinter.arucoDictBytesList[dictionary].shape[0] < (( sizeX * sizeY ) // 2)): + raise ValueError("aruce dictionary is not enough for your board size") + + if(sizeX <= 1): + raise ValueError("sizeX <= 1") + + if(sizeY <= 1): + raise ValueError("sizeY <= 1") + + if(squareLength <= 0): + raise ValueError("squareLength <= 0") + + if(markerLength <= 0): + raise ValueError("markerLength <= 0") + + if(squareLength < markerLength): + raise ValueError("squareLength < markerLength") + + if(borderBits <= 0): + raise ValueError("borderBits <= 0") + + if(pageBorderX < 0): + raise ValueError("pageBorderX < 0") + + if(pageBorderY < 0): + raise ValueError("pageBorderY < 0") + + if(subSize is not None): + subSizeX, subSizeY = subSize + + if(subSizeX < 0): + raise ValueError("subSizeX < 0") + + if(subSizeY < 0): + raise ValueError("subSizeY < 0") + + def PreviewCharucoMarkerImage(dictionary, chessboardSize, squareLength, markerLength, borderBits=1, pageBorder=(0, 0), dpi=96): + MarkerPrinter.__CheckCharucoMarkerImage(dictionary, chessboardSize, squareLength, markerLength, borderBits=borderBits, pageBorder=pageBorder) + + squareLength = squareLength * MarkerPrinter.ptPerMeter + markerLength = markerLength * MarkerPrinter.ptPerMeter + pageBorder = (pageBorder[0] * MarkerPrinter.ptPerMeter, pageBorder[1] * MarkerPrinter.ptPerMeter) + + prevImage = None + with tempfile.TemporaryDirectory() as tmpdirname: + with MarkerPrinter.surface[".SVG"] ( + os.path.join(tmpdirname, "tempSVG.svg"), + chessboardSize[0] * squareLength + pageBorder[0] * 2, + chessboardSize[1] * squareLength + pageBorder[1] * 2) as surface: + context = cairo.Context(surface) + + context.set_source_rgba(0.5, 0.5, 0.5, 1.0) + context.rectangle(0, 0, + chessboardSize[0] * squareLength + pageBorder[0] * 2, + chessboardSize[1] * squareLength + pageBorder[1] * 2) + context.fill() + + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + context.rectangle(pageBorder[0], pageBorder[1], + chessboardSize[0] * squareLength, + chessboardSize[1] * squareLength) + context.fill() + + for bx in range(chessboardSize[0]): + for by in range(chessboardSize[1]): + MarkerPrinter.__DrawBlock( + context = context, + dictionary = dictionary, + markerLength = markerLength, + borderBits = borderBits, + chessboardSize = chessboardSize, + squareLength = squareLength, + blockX = bx, + blockY = by, + pageBorderX = pageBorder[0], + pageBorderY = pageBorder[1], + mode = "CHARUCO") + + with open(os.path.join(tmpdirname, "tempSVG.svg")) as file: + prevImage = Image.open(io.BytesIO(svg2png(bytestring=file.read(), dpi=dpi))) + + return prevImage + + def GenCharucoMarkerImage(filePath, dictionary, chessboardSize, squareLength, markerLength, borderBits=1, subSize=None, pageBorder=(0, 0)): + MarkerPrinter.__CheckCharucoMarkerImage(dictionary, chessboardSize, squareLength, markerLength, borderBits=borderBits, subSize=subSize, pageBorder=pageBorder) + + squareLength = squareLength * MarkerPrinter.ptPerMeter + markerLength = markerLength * MarkerPrinter.ptPerMeter + pageBorder = (pageBorder[0] * MarkerPrinter.ptPerMeter, pageBorder[1] * MarkerPrinter.ptPerMeter) + + # Check + path, nameExt = os.path.split(filePath) + name, ext = os.path.splitext(nameExt) + + if(len(path) > 0): + if not(os.path.isdir(path)): + os.makedirs(path) + + if((ext.upper() != ".SVG") and (ext.upper() != ".PS") and (ext.upper() != ".PDF")): + raise ValueError("file extention is not supported, should be: svg, ps, pdf") + + # Draw + with MarkerPrinter.surface[ext.upper()] ( + filePath, + chessboardSize[0] * squareLength + pageBorder[0] * 2, + chessboardSize[1] * squareLength + pageBorder[1] * 2) as surface: + context = cairo.Context(surface) + + context.set_source_rgba(0.5, 0.5, 0.5, 1.0) + context.rectangle(0, 0, + chessboardSize[0] * squareLength + pageBorder[0] * 2, + chessboardSize[1] * squareLength + pageBorder[1] * 2) + context.fill() + + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + context.rectangle(pageBorder[0], pageBorder[1], + chessboardSize[0] * squareLength, + chessboardSize[1] * squareLength) + context.fill() + + for bx in range(chessboardSize[0]): + for by in range(chessboardSize[1]): + MarkerPrinter.__DrawBlock( + context = context, + dictionary = dictionary, + markerLength = markerLength, + borderBits = borderBits, + chessboardSize = chessboardSize, + squareLength = squareLength, + blockX = bx, + blockY = by, + pageBorderX = pageBorder[0], + pageBorderY = pageBorder[1], + mode = "CHARUCO") + + if(subSize is not None): + subDivide = (\ + chessboardSize[0] // subSize[0] + int(chessboardSize[0] % subSize[0] > 0), + chessboardSize[1] // subSize[1] + int(chessboardSize[1] % subSize[1] > 0)) + + subChessboardBlockX = np.clip ( np.arange(0, subSize[0] * subDivide[0] + 1, subSize[0]), 0, chessboardSize[0]) + subChessboardBlockY = np.clip ( np.arange(0, subSize[1] * subDivide[1] + 1, subSize[1]), 0, chessboardSize[1]) + + subChessboardSliceX = subChessboardBlockX.astype(np.float) * squareLength + subChessboardSliceY = subChessboardBlockY.astype(np.float) * squareLength + + for subXID in range(subDivide[0]): + for subYID in range(subDivide[1]): + subName = name + \ + "_X" + str(subChessboardBlockX[subXID]) + "_" + str(subChessboardBlockX[subXID+1]) + \ + "_Y" + str(subChessboardBlockY[subYID]) + "_" + str(subChessboardBlockY[subYID+1]) + + with MarkerPrinter.surface[ext.upper()]( + os.path.join(path, subName + ext), + subChessboardSliceX[subXID+1] - subChessboardSliceX[subXID] + pageBorder[0] * 2, + subChessboardSliceY[subYID+1] - subChessboardSliceY[subYID] + pageBorder[1] * 2) as surface: + context = cairo.Context(surface) + + context.set_source_rgba(0.5, 0.5, 0.5, 1.0) + context.rectangle(0, 0, + subChessboardSliceX[subXID+1] - subChessboardSliceX[subXID] + pageBorder[0] * 2, + subChessboardSliceY[subYID+1] - subChessboardSliceY[subYID] + pageBorder[1] * 2) + context.fill() + + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + context.rectangle(pageBorder[0], pageBorder[1], + subChessboardSliceX[subXID+1] - subChessboardSliceX[subXID], + subChessboardSliceY[subYID+1] - subChessboardSliceY[subYID]) + context.fill() + + for bx in range(subChessboardBlockX[subXID+1] - subChessboardBlockX[subXID]): + for by in range(subChessboardBlockY[subYID+1] - subChessboardBlockY[subYID]): + MarkerPrinter.__DrawBlock( + context = context, + dictionary = dictionary, + markerLength = markerLength, + borderBits = borderBits, + chessboardSize = chessboardSize, + squareLength = squareLength, + blockX = subChessboardBlockX[subXID] + bx, + blockY = subChessboardBlockY[subYID] + by, + originX = subChessboardBlockX[subXID], + originY = subChessboardBlockY[subYID], + pageBorderX = pageBorder[0], + pageBorderY = pageBorder[1], + mode = "CHARUCO") + + def __CheckArucoGridMarkerImage(dictionary, chessboardSize, markerLength, markerSeparation, firstMarker, borderBits=1, subSize=None, pageBorder=(0, 0)): + if(len(chessboardSize) != 2): + raise ValueError("len(chessboardSize) != 2") + else: + sizeX, sizeY = chessboardSize + + if(len(pageBorder) != 2): + raise ValueError("len(pageBorder) != 2") + else: + pageBorderX, pageBorderY = pageBorder + + if not (dictionary in MarkerPrinter.arucoDictBytesList): + raise ValueError("dictionary is not support") + + if(MarkerPrinter.arucoDictBytesList[dictionary].shape[0] < (( sizeX * sizeY ) + firstMarker)): + raise ValueError("aruce dictionary is not enough for your board size and firstMarker") + + if(sizeX <= 1): + raise ValueError("sizeX <= 1") + + if(sizeY <= 1): + raise ValueError("sizeY <= 1") + + if(markerLength <= 0): + raise ValueError("markerLength <= 0") + + if(markerSeparation <= 0): + raise ValueError("markerSeparation <= 0") + + if(borderBits <= 0): + raise ValueError("borderBits <= 0") + + if(pageBorderX < 0): + raise ValueError("pageBorderX < 0") + + if(pageBorderY < 0): + raise ValueError("pageBorderY < 0") + + if(subSize is not None): + subSizeX, subSizeY = subSize + + if(subSizeX < 0): + raise ValueError("subSizeX < 0") + + if(subSizeY < 0): + raise ValueError("subSizeY < 0") + + def PreviewArucoGridMarkerImage(dictionary, chessboardSize, markerLength, markerSeparation, firstMarker, borderBits=1, pageBorder=(0, 0), dpi=96): + MarkerPrinter.__CheckArucoGridMarkerImage(dictionary, chessboardSize, markerLength, markerSeparation, firstMarker, borderBits=borderBits, pageBorder=pageBorder) + + markerLength = markerLength * MarkerPrinter.ptPerMeter + markerSeparation = markerSeparation * MarkerPrinter.ptPerMeter + pageBorder = (pageBorder[0] * MarkerPrinter.ptPerMeter, pageBorder[1] * MarkerPrinter.ptPerMeter) + + prevImage = None + with tempfile.TemporaryDirectory() as tmpdirname: + with MarkerPrinter.surface[".SVG"] ( + os.path.join(tmpdirname, "tempSVG.svg"), + chessboardSize[0] * markerLength + (chessboardSize[0] - 1) * markerSeparation + pageBorder[0] * 2, + chessboardSize[1] * markerLength + (chessboardSize[1] - 1) * markerSeparation + pageBorder[1] * 2) as surface: + context = cairo.Context(surface) + + context.set_source_rgba(0.5, 0.5, 0.5, 1.0) + context.rectangle(0, 0, + chessboardSize[0] * markerLength + (chessboardSize[0] - 1) * markerSeparation + pageBorder[0] * 2, + chessboardSize[1] * markerLength + (chessboardSize[1] - 1) * markerSeparation + pageBorder[1] * 2) + context.fill() + + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + context.rectangle(pageBorder[0], pageBorder[1], + chessboardSize[0] * markerLength + (chessboardSize[0] - 1) * markerSeparation, + chessboardSize[1] * markerLength + (chessboardSize[1] - 1) * markerSeparation) + context.fill() + + for bx in range(chessboardSize[0]): + for by in range(chessboardSize[1]): + MarkerPrinter.__DrawBlock( + context = context, + dictionary = dictionary, + markerLength = markerLength, + borderBits = borderBits, + chessboardSize = chessboardSize, + squareLength = markerLength + markerSeparation, + firstMarkerID = firstMarker, + blockX = bx, + blockY = by, + pageBorderX = pageBorder[0], + pageBorderY = pageBorder[1], + mode = "ARUCOGRID") + + with open(os.path.join(tmpdirname, "tempSVG.svg")) as file: + prevImage = Image.open(io.BytesIO(svg2png(bytestring=file.read(), dpi=dpi))) + + return prevImage + + def GenArucoGridMarkerImage(filePath, dictionary, chessboardSize, markerLength, markerSeparation, firstMarker, borderBits=1, subSize=None, pageBorder=(0, 0)): + MarkerPrinter.__CheckArucoGridMarkerImage(dictionary, chessboardSize, markerLength, markerSeparation, firstMarker, borderBits=borderBits, subSize=subSize, pageBorder=pageBorder) + + markerLength = markerLength * MarkerPrinter.ptPerMeter + markerSeparation = markerSeparation * MarkerPrinter.ptPerMeter + pageBorder = (pageBorder[0] * MarkerPrinter.ptPerMeter, pageBorder[1] * MarkerPrinter.ptPerMeter) + + # Check + path, nameExt = os.path.split(filePath) + name, ext = os.path.splitext(nameExt) + + if(len(path) > 0): + if not(os.path.isdir(path)): + os.makedirs(path) + + if((ext.upper() != ".SVG") and (ext.upper() != ".PS") and (ext.upper() != ".PDF")): + raise ValueError("file extention is not supported, should be: svg, ps, pdf") + + # Draw + with MarkerPrinter.surface[ext.upper()] ( + filePath, + chessboardSize[0] * markerLength + (chessboardSize[0] - 1) * markerSeparation + pageBorder[0] * 2, + chessboardSize[1] * markerLength + (chessboardSize[1] - 1) * markerSeparation + pageBorder[1] * 2) as surface: + context = cairo.Context(surface) + + context.set_source_rgba(0.5, 0.5, 0.5, 1.0) + context.rectangle(0, 0, + chessboardSize[0] * markerLength + (chessboardSize[0] - 1) * markerSeparation + pageBorder[0] * 2, + chessboardSize[1] * markerLength + (chessboardSize[1] - 1) * markerSeparation + pageBorder[1] * 2) + context.fill() + + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + context.rectangle(pageBorder[0], pageBorder[1], + chessboardSize[0] * markerLength + (chessboardSize[0] - 1) * markerSeparation, + chessboardSize[1] * markerLength + (chessboardSize[1] - 1) * markerSeparation) + context.fill() + + for bx in range(chessboardSize[0]): + for by in range(chessboardSize[1]): + MarkerPrinter.__DrawBlock( + context = context, + dictionary = dictionary, + markerLength = markerLength, + borderBits = borderBits, + chessboardSize = chessboardSize, + squareLength = markerLength + markerSeparation, + firstMarkerID = firstMarker, + blockX = bx, + blockY = by, + pageBorderX = pageBorder[0], + pageBorderY = pageBorder[1], + mode = "ARUCOGRID") + + if(subSize is not None): + subDivide = (\ + chessboardSize[0] // subSize[0] + int(chessboardSize[0] % subSize[0] > 0), + chessboardSize[1] // subSize[1] + int(chessboardSize[1] % subSize[1] > 0)) + + subChessboardBlockX = np.clip ( np.arange(0, subSize[0] * subDivide[0] + 1, subSize[0]), 0, chessboardSize[0]) + subChessboardBlockY = np.clip ( np.arange(0, subSize[1] * subDivide[1] + 1, subSize[1]), 0, chessboardSize[1]) + + subChessboardSliceX = subChessboardBlockX.astype(np.float) * (markerLength + markerSeparation) + subChessboardSliceY = subChessboardBlockY.astype(np.float) * (markerLength + markerSeparation) + + subChessboardSliceX[-1] -= markerSeparation + subChessboardSliceY[-1] -= markerSeparation + + for subXID in range(subDivide[0]): + for subYID in range(subDivide[1]): + subName = name + \ + "_X" + str(subChessboardBlockX[subXID]) + "_" + str(subChessboardBlockX[subXID+1]) + \ + "_Y" + str(subChessboardBlockY[subYID]) + "_" + str(subChessboardBlockY[subYID+1]) + + with MarkerPrinter.surface[ext.upper()]( + os.path.join(path, subName + ext), + subChessboardSliceX[subXID+1] - subChessboardSliceX[subXID] + pageBorder[0] * 2, + subChessboardSliceY[subYID+1] - subChessboardSliceY[subYID] + pageBorder[1] * 2) as surface: + context = cairo.Context(surface) + + context.set_source_rgba(0.5, 0.5, 0.5, 1.0) + context.rectangle(0, 0, + subChessboardSliceX[subXID+1] - subChessboardSliceX[subXID] + pageBorder[0] * 2, + subChessboardSliceY[subYID+1] - subChessboardSliceY[subYID] + pageBorder[1] * 2) + context.fill() + + context.set_source_rgba(1.0, 1.0, 1.0, 1.0) + context.rectangle(pageBorder[0], pageBorder[1], + subChessboardSliceX[subXID+1] - subChessboardSliceX[subXID], + subChessboardSliceY[subYID+1] - subChessboardSliceY[subYID]) + context.fill() + + for bx in range(subChessboardBlockX[subXID+1] - subChessboardBlockX[subXID]): + for by in range(subChessboardBlockY[subYID+1] - subChessboardBlockY[subYID]): + MarkerPrinter.__DrawBlock( + context = context, + dictionary = dictionary, + markerLength = markerLength, + borderBits = borderBits, + chessboardSize = chessboardSize, + squareLength = markerLength + markerSeparation, + firstMarkerID = firstMarker, + blockX = subChessboardBlockX[subXID] + bx, + blockY = subChessboardBlockY[subYID] + by, + originX = subChessboardBlockX[subXID], + originY = subChessboardBlockY[subYID], + pageBorderX = pageBorder[0], + pageBorderY = pageBorder[1], + mode = "ARUCOGRID") + +if __name__ == '__main__': + parser = ArgumentParser() + + # Save marker image parameters + chessGroup = parser.add_argument_group('chess', 'Chessboard') + arucoGroup = parser.add_argument_group('aruco', 'ArUco') + arucoGridGroup = parser.add_argument_group('aruco_grid', 'ArUco grid') + charucoGroup = parser.add_argument_group('charuco', 'ChArUco') + exclusiveGroup = parser.add_mutually_exclusive_group() + + exclusiveGroup.add_argument( + "--chess", action='store_true', default=False, + help="Choose to save chessboard marker") + + exclusiveGroup.add_argument( + "--aruco", action='store_true', default=False, + help="Choose to save ArUco marker") + + exclusiveGroup.add_argument( + "--aruco_grid", action='store_true', default=False, + help="Choose to save ArUco grid marker") + + exclusiveGroup.add_argument( + "--charuco", action='store_true', default=False, + help="Choose to save ChArUco marker") + + # Utility functions parameters + exclusiveGroup.add_argument( + "--generate", dest="arucoDataFileName", + help="Generate aruco data to FILE", metavar="FILE") + + exclusiveGroup.add_argument( + "--list_dictionary", action='store_true', default=False, + help="List predefined aruco dictionary") + + # Parameters + # fileName + parser.add_argument( + "--file", dest="fileName", default="./image.pdf", + help="Save marker image to FILE", metavar="FILE") + for group in [chessGroup, arucoGroup, arucoGridGroup, charucoGroup]: + group.add_argument( + "--" + group.title + "_file", dest="fileName", + help="Save marker image to FILE", metavar="FILE") + + # dictionary + parser.add_argument( + "--dictionary", dest="dictionary", default="DICT_ARUCO_ORIGINAL", + help="Generate marker via predefined DICTIONARY aruco dictionary", metavar="DICTIONARY") + for group in [arucoGroup, arucoGridGroup, charucoGroup]: + group.add_argument( + "--" + group.title + "_dictionary", dest="dictionary", + help="Generate marker via predefined DICTIONARY aruco dictionary", metavar="DICTIONARY") + + # size + parser.add_argument( + "--size_x", dest="sizeX", default="16", + help="Save marker image with N board width", metavar="N") + parser.add_argument( + "--size_y", dest="sizeY", default="9", + help="Save marker image with N board height", metavar="N") + + for group in [chessGroup, arucoGridGroup, charucoGroup]: + group.add_argument( + "--" + group.title + "_size_x", dest="sizeX", + help="Save marker image with N board width", metavar="N") + group.add_argument( + "--" + group.title + "_size_y", dest="sizeY", + help="Save marker image with N board height", metavar="N") + + # length + parser.add_argument( + "--square_length", dest="squareLength", default="0.09", + help="Save marker image with L square length (Unit: meter)", metavar="L") + parser.add_argument( + "--marker_length", dest="markerLength", default="0.07", + help="Save marker image with L marker length (Unit: meter)", metavar="L") + parser.add_argument( + "--marker_separation", dest="markerSeparation", default="0.02", + help="Save marker image with L separation length (Unit: meter)", metavar="L") + + for group in [chessGroup, charucoGroup]: + group.add_argument( + "--" + group.title + "_square_length", dest="squareLength", + help="Save marker image with L blocks length (Unit: meter)", metavar="L") + + for group in [arucoGroup, arucoGridGroup, charucoGroup]: + group.add_argument( + "--" + group.title + "_marker_length", dest="markerLength", + help="Save marker image with L marker length (Unit: meter)", metavar="L") + + for group in [arucoGridGroup]: + group.add_argument( + "--" + group.title + "_marker_separation", dest="markerSeparation", + help="Save marker image with L gap length (Unit: meter)", metavar="L") + + # else + parser.add_argument( + "--marker_id", dest="markerID", default="0", + help="Save marker image with ID marker", metavar="ID") + parser.add_argument( + "--first_marker", dest="firstMarker", default="0", + help="Save marker image that start with ID marker", metavar="ID") + parser.add_argument( + "--border_bits", dest="borderBits", default="1", + help="Save marker image with N border size", metavar="N") + + for group in [arucoGroup]: + group.add_argument( + "--" + group.title + "_marker_id", dest="markerID", + help="Save marker image with ID marker", metavar="ID") + + for group in [arucoGridGroup]: + group.add_argument( + "--" + group.title + "_first_marker", dest="firstMarker", + help="Save marker image that start with ID marker", metavar="ID") + + for group in [arucoGroup, arucoGridGroup, charucoGroup]: + group.add_argument( + "--" + group.title + "_border_bits", dest="borderBits", + help="Save marker image with N border size", metavar="N") + + # sub size + parser.add_argument( + "--sub_size_x", dest="subSizeX", default="0", + help="Save marker image with N chuck width", metavar="N") + parser.add_argument( + "--sub_size_y", dest="subSizeY", default="0", + help="Save marker image with N chuck height", metavar="N") + + for group in [chessGroup, arucoGridGroup, charucoGroup]: + group.add_argument( + "--" + group.title + "_sub_size_x", dest="subSizeX", + help="Save marker image with N chuck width", metavar="N") + group.add_argument( + "--" + group.title + "_sub_size_y", dest="subSizeY", + help="Save marker image with N chuck height", metavar="N") + + # page border + parser.add_argument( + "--page_border_x", dest="pageBorderX", default="0", + help="Save with page border width L length (Unit: meter)", metavar="L") + parser.add_argument( + "--page_border_y", dest="pageBorderY", default="0", + help="Save with page border height L length (Unit: meter)", metavar="L") + + for group in [chessGroup, arucoGroup, arucoGridGroup, charucoGroup]: + group.add_argument( + "--" + group.title + "_page_border_x", dest="pageBorderX", default="0", + help="Save with page border width L length (Unit: meter)", metavar="L") + group.add_argument( + "--" + group.title + "_page_border_y", dest="pageBorderY", default="0", + help="Save with page border height L length (Unit: meter)", metavar="L") + + # Run + args = parser.parse_args() + + if(args.arucoDataFileName is not None): + print("Generate aruco data to: " + args.arucoDataFileName) + SaveArucoDictBytesList(args.arucoDataFileName) + + elif(args.list_dictionary): + print("List predefined aruco dictionary") + for i in MarkerPrinter.arucoDictBytesList.keys(): + print(i) + + elif(args.chess): + try: + sizeX = int(args.sizeX) + sizeY = int(args.sizeY) + squareLength = float(args.squareLength) + subSizeX = int(args.subSizeX) + subSizeY = int(args.subSizeY) + pageBorderX = float(args.pageBorderX) + pageBorderY = float(args.pageBorderY) + except ValueError as e: + warnings.warn(str(e)) + else: + print("Save chessboard marker with parms: " + \ + str({ \ + "fileName": args.fileName, \ + "sizeX": sizeX, \ + "sizeY": sizeY, \ + "squareLength": squareLength, \ + "subSizeX": subSizeX, \ + "subSizeY": subSizeY, \ + "pageBorderX": pageBorderX, \ + "pageBorderY": pageBorderY, \ + })) + + subSize = None + + if(subSizeX > 0): + if(subSizeY > 0): + subSize = (subSizeX, subSizeY) + else: + subSize = (subSizeX, sizeY) + else: + if(subSizeY > 0): + subSize = (sizeX, subSizeY) + else: + subSize = None + + # Gen + MarkerPrinter.GenChessMarkerImage(args.fileName, (sizeX, sizeY), squareLength, subSize = subSize, pageBorder = (pageBorderX, pageBorderY)) + + elif(args.aruco): + try: + markerLength = float(args.markerLength) + markerID = int(args.markerID) + borderBits = int(args.borderBits) + pageBorderX = float(args.pageBorderX) + pageBorderY = float(args.pageBorderY) + except ValueError as e: + warnings.warn(str(e)) + else: + print("Save ArUco marker with parms: " + \ + str({ \ + "fileName": args.fileName, \ + "dictionary": args.dictionary, \ + "markerLength": markerLength, \ + "markerID": markerID, \ + "borderBits": borderBits, \ + "pageBorderX": pageBorderX, \ + "pageBorderY": pageBorderY, \ + })) + + # Gen + MarkerPrinter.GenArucoMarkerImage(args.fileName, args.dictionary, markerID, markerLength, borderBits=borderBits, pageBorder = (pageBorderX, pageBorderY)) + + elif(args.aruco_grid): + try: + sizeX = int(args.sizeX) + sizeY = int(args.sizeY) + markerLength = float(args.markerLength) + markerSeparation = float(args.markerSeparation) + firstMarker = int(args.firstMarker) + borderBits = int(args.borderBits) + subSizeX = int(args.subSizeX) + subSizeY = int(args.subSizeY) + pageBorderX = float(args.pageBorderX) + pageBorderY = float(args.pageBorderY) + except ValueError as e: + warnings.warn(str(e)) + else: + print("Save ArUco grid marker with parms: " + \ + str({ \ + "fileName": args.fileName, \ + "dictionary": args.dictionary, \ + "sizeX": sizeX, \ + "sizeY": sizeY, \ + "markerLength": markerLength, \ + "markerSeparation": markerSeparation, \ + "firstMarker": firstMarker, \ + "borderBits": borderBits, \ + "subSizeX": subSizeX, \ + "subSizeY": subSizeY, \ + "pageBorderX": pageBorderX, \ + "pageBorderY": pageBorderY, \ + })) + + subSize = None + + if(subSizeX > 0): + if(subSizeY > 0): + subSize = (subSizeX, subSizeY) + else: + subSize = (subSizeX, sizeY) + else: + if(subSizeY > 0): + subSize = (sizeX, subSizeY) + else: + subSize = None + + # Gen + MarkerPrinter.GenArucoGridMarkerImage(args.fileName, args.dictionary, (sizeX, sizeY), markerLength, markerSeparation, firstMarker, borderBits=borderBits, subSize=subSize, pageBorder = (pageBorderX, pageBorderY)) + + elif(args.charuco): + try: + sizeX = int(args.sizeX) + sizeY = int(args.sizeY) + squareLength = float(args.squareLength) + markerLength = float(args.markerLength) + borderBits = int(args.borderBits) + subSizeX = int(args.subSizeX) + subSizeY = int(args.subSizeY) + pageBorderX = float(args.pageBorderX) + pageBorderY = float(args.pageBorderY) + except ValueError as e: + warnings.warn(str(e)) + else: + print("Save ChArUco marker with parms: " + \ + str({ \ + "fileName": args.fileName, \ + "dictionary": args.dictionary, \ + "sizeX": sizeX, \ + "sizeY": sizeY, \ + "squareLength": squareLength, \ + "markerLength": markerLength, \ + "borderBits": borderBits, \ + "subSizeX": subSizeX, \ + "subSizeY": subSizeY, \ + "pageBorderX": pageBorderX, \ + "pageBorderY": pageBorderY, \ + })) + + subSize = None + + if(subSizeX > 0): + if(subSizeY > 0): + subSize = (subSizeX, subSizeY) + else: + subSize = (subSizeX, sizeY) + else: + if(subSizeY > 0): + subSize = (sizeX, subSizeY) + else: + subSize = None + + # Gen + MarkerPrinter.GenCharucoMarkerImage(args.fileName, args.dictionary, (sizeX, sizeY), squareLength, markerLength, borderBits=borderBits, subSize=subSize, pageBorder = (pageBorderX, pageBorderY)) + + else: + parser.print_help() diff --git a/modules/aruco/misc/pattern_generator/MarkerPrinterGUI.py b/modules/aruco/misc/pattern_generator/MarkerPrinterGUI.py new file mode 100644 index 00000000000..bdd6d3cb0f0 --- /dev/null +++ b/modules/aruco/misc/pattern_generator/MarkerPrinterGUI.py @@ -0,0 +1,565 @@ +#!/usr/bin/env python3 + +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright (c) 2019, Josh Chien. All rights reserved. + +from MarkerPrinter import * + +import tkinter as tk +from tkinter import ttk, filedialog, messagebox + +import time + +import PIL.Image +import PIL.ImageTk + +class MarkerPrinterGUI: + + def VisDPI(self, shape): + scale0 = float(self.displayShape[0]) / float(shape[0]) + scale1 = float(self.displayShape[1]) / float(shape[1]) + if(scale0 > scale1): + return scale1 * 96.0 + else: + return scale0 * 96.0 + + def OnShowingHelpGithub(self): + messagebox.showinfo("Github", + "https://github.com/dogod621/OpenCVMarkerPrinter") + + def OnCloseWindow(self): + if(self.window is not None): + if messagebox.askokcancel("Quit", "Do you want to quit?"): + self.window.destroy() + self.window = None + + def OnSelectCharucoMarkerDictionary(self, pDictName): + self.charucoMarkerDictionaryStr.set(pDictName) + + def __SaveMarker(GenMarkerImageCallback, *args, **kwargs): + + if(kwargs.get("subSize",None) is not None): + subSizeX, subSizeY = kwargs["subSize"] + + kwargs["subSize"] = None + + if(subSizeX > 0): + if(subSizeY > 0): + kwargs["subSize"] = (subSizeX, subSizeY) + else: + kwargs["subSize"] = (subSizeX, sizeY) + else: + if(subSizeY > 0): + kwargs["subSize"] = (sizeX, subSizeY) + else: + kwargs["subSize"] = None + + try: + askFileName = filedialog.asksaveasfilename(initialdir = os.path.abspath("./"), title = "Output", filetypes = (\ + ("scalable vector graphics files","*.svg"), \ + ("portable document format files","*.pdf"), \ + ("post script files","*.ps")), + defaultextension="*.*") + + if (askFileName): + GenMarkerImageCallback(askFileName, *args, **kwargs) + + except Exception as e: + warnings.warn(str(e)) + messagebox.showinfo("Error", "Save marker failed") + return + + def OnPreviewOrSaveCharucoMarker(self, askSave = False): + try: + sizeX = int(self.charucoMarkerChessboardSizeXStr.get()) + sizeY = int(self.charucoMarkerChessboardSizeYStr.get()) + squareLength = float(self.charucoMarkerSquareLengthStr.get()) + markerLength = float(self.charucoMarkerMarkerLengthStr.get()) + borderBits = int(self.charucoMarkerBorderBitsStr.get()) + dictionary = self.charucoMarkerDictionaryStr.get() + subSizeX = int(self.charucoMarkerSaveSubSizeXStr.get()) + subSizeY = int(self.charucoMarkerSaveSubSizeYStr.get()) + pageBorderX = float(self.charucoMarkerSavePageBorderXStr.get()) + pageBorderY = float(self.charucoMarkerSavePageBorderYStr.get()) + except ValueError as e: + warnings.warn(str(e)) + messagebox.showinfo("Error", "Enter invalid parameters") + return + except Exception as e: + warnings.warn(str(e)) + messagebox.showinfo("Error", "Fail to get parameters") + return + + # Preview + try: + dpi = self.VisDPI(((sizeY * squareLength + pageBorderY * 2) * MarkerPrinter.ptPerMeter, (sizeX * squareLength + pageBorderX * 2) * MarkerPrinter.ptPerMeter)) + tkImage = PIL.ImageTk.PhotoImage(image = MarkerPrinter.PreviewCharucoMarkerImage(dictionary, (sizeX, sizeY), squareLength, markerLength, borderBits=borderBits, pageBorder = (pageBorderX, pageBorderY), dpi=dpi)) + self.charucoMarkerImageLabel.imgtk = tkImage + self.charucoMarkerImageLabel.config(image=tkImage) + except Exception as e: + warnings.warn(str(e)) + messagebox.showinfo("Error", "create marker failed") + return + + # Save + if(askSave): + MarkerPrinterGUI.__SaveMarker(MarkerPrinter.GenCharucoMarkerImage, \ + dictionary, (sizeX, sizeY), squareLength, markerLength, borderBits=borderBits, subSize = (subSizeX, subSizeY), pageBorder = (pageBorderX, pageBorderY)) + + def OnPreviewCharucoMarker(self): + self.OnPreviewOrSaveCharucoMarker(askSave = False) + + def OnSaveCharucoMarker(self): + self.OnPreviewOrSaveCharucoMarker(askSave = True) + + def InitCharucoMarkerTab(self): + self.charucoMarkerUIFrame = ttk.Frame(self.charucoMarkerTab) + self.charucoMarkerImageTab = ttk.Frame(self.charucoMarkerTab) + self.charucoMarkerUIFrame2 = ttk.Frame(self.charucoMarkerTab) + + self.charucoMarkerUIFrame.grid(row=0, column=0, sticky = tk.NSEW) + self.charucoMarkerImageTab.grid(row=1, column=0, sticky = tk.NSEW) + self.charucoMarkerUIFrame2.grid(row=2, column=0, sticky = tk.NSEW) + + self.charucoMarkerImageLabel = tk.Label(self.charucoMarkerImageTab) + self.charucoMarkerImageLabel.grid(row=0, column=0, sticky = tk.NSEW) + + tk.Label(self.charucoMarkerUIFrame, text="dictionary").grid(row=0, column=0, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame, text="chessboardSizeX").grid(row=0, column=1, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame, text="chessboardSizeY").grid(row=0, column=2, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame, text="squareLength (Unit: Meter)").grid(row=0, column=3, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame, text="markerLength (Unit: Meter)").grid(row=0, column=4, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame, text="borderBits").grid(row=0, column=5, sticky = tk.NSEW) + + self.charucoMarkerDictionaryStr = tk.StringVar() + self.charucoMarkerChessboardSizeXStr = tk.StringVar() + self.charucoMarkerChessboardSizeXStr.set("16") + self.charucoMarkerChessboardSizeYStr = tk.StringVar() + self.charucoMarkerChessboardSizeYStr.set("9") + self.charucoMarkerSquareLengthStr = tk.StringVar() + self.charucoMarkerSquareLengthStr.set("0.09") + self.charucoMarkerMarkerLengthStr = tk.StringVar() + self.charucoMarkerMarkerLengthStr.set("0.07") + self.charucoMarkerBorderBitsStr = tk.StringVar() + self.charucoMarkerBorderBitsStr.set("1") + + self.charucoMarkerDictionaryMenue = tk.OptionMenu(self.charucoMarkerUIFrame, self.charucoMarkerDictionaryStr, "DICT_ARUCO_ORIGINAL", command = self.OnSelectCharucoMarkerDictionary) + self.charucoMarkerDictionaryMenue.grid(row=1, column=0, sticky = tk.NSEW) + tk.Entry(self.charucoMarkerUIFrame, textvariable=self.charucoMarkerChessboardSizeXStr).grid(row=1, column=1, sticky = tk.NSEW) + tk.Entry(self.charucoMarkerUIFrame, textvariable=self.charucoMarkerChessboardSizeYStr).grid(row=1, column=2, sticky = tk.NSEW) + tk.Entry(self.charucoMarkerUIFrame, textvariable=self.charucoMarkerSquareLengthStr).grid(row=1, column=3, sticky = tk.NSEW) + tk.Entry(self.charucoMarkerUIFrame, textvariable=self.charucoMarkerMarkerLengthStr).grid(row=1, column=4, sticky = tk.NSEW) + tk.Entry(self.charucoMarkerUIFrame, textvariable=self.charucoMarkerBorderBitsStr).grid(row=1, column=5, sticky = tk.NSEW) + + tk.Button(self.charucoMarkerUIFrame2, text = "Preview", command = self.OnPreviewCharucoMarker).grid(row=1, column=0, sticky = tk.NSEW) + tk.Button(self.charucoMarkerUIFrame2, text = "Save", command = self.OnSaveCharucoMarker).grid(row=1, column=1, sticky = tk.NSEW) + + tk.Label(self.charucoMarkerUIFrame2, text="Save opetions:").grid(row=0, column=2, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame2, text="(set 0 as disable)").grid(row=1, column=2, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame2, text="subSizeX").grid(row=0, column=3, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame2, text="subSizeY").grid(row=0, column=4, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame2, text="Divide to chunks, chunk sizeX").grid(row=2, column=3, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame2, text="Divide to chunks, chunk sizeY").grid(row=2, column=4, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame2, text="pageBorderX (Unit: Meter)").grid(row=0, column=5, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame2, text="pageBorderY (Unit: Meter)").grid(row=0, column=6, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame2, text="Border or page").grid(row=2, column=5, sticky = tk.NSEW) + tk.Label(self.charucoMarkerUIFrame2, text="Border or page").grid(row=2, column=6, sticky = tk.NSEW) + + self.charucoMarkerSaveSubSizeXStr = tk.StringVar() + self.charucoMarkerSaveSubSizeXStr.set("0") + self.charucoMarkerSaveSubSizeYStr = tk.StringVar() + self.charucoMarkerSaveSubSizeYStr.set("0") + self.charucoMarkerSavePageBorderXStr = tk.StringVar() + self.charucoMarkerSavePageBorderXStr.set("0.02") + self.charucoMarkerSavePageBorderYStr = tk.StringVar() + self.charucoMarkerSavePageBorderYStr.set("0.02") + + tk.Entry(self.charucoMarkerUIFrame2, textvariable=self.charucoMarkerSaveSubSizeXStr).grid(row=1, column=3, sticky = tk.NSEW) + tk.Entry(self.charucoMarkerUIFrame2, textvariable=self.charucoMarkerSaveSubSizeYStr).grid(row=1, column=4, sticky = tk.NSEW) + tk.Entry(self.charucoMarkerUIFrame2, textvariable=self.charucoMarkerSavePageBorderXStr).grid(row=1, column=5, sticky = tk.NSEW) + tk.Entry(self.charucoMarkerUIFrame2, textvariable=self.charucoMarkerSavePageBorderYStr).grid(row=1, column=6, sticky = tk.NSEW) + + self.charucoMarkerDictionaryMenue['menu'].delete(0, 'end') + for dictName in self.dictList: + self.charucoMarkerDictionaryMenue['menu'].add_command(label=dictName, command=tk._setit(self.charucoMarkerDictionaryStr, dictName, self.OnSelectCharucoMarkerDictionary)) + + self.OnSelectCharucoMarkerDictionary("DICT_ARUCO_ORIGINAL") + + def OnSelectArucoGridMarkerDictionary(self, pDictName): + self.arucoGridMarkerDictionaryStr.set(pDictName) + + def OnPreviewOrSaveArucoGridMarker(self, askSave = False): + try: + markersX = int(self.arucoGridMarkerMarkersXStr.get()) + markersY = int(self.arucoGridMarkerMarkersYStr.get()) + markerLength = float(self.arucoGridMarkerMarkerLengthStr.get()) + markerSeparation = float(self.arucoGridMarkerMarkerSeparationStr.get()) + borderBits = int(self.arucoGridMarkerBorderBitsStr.get()) + firstMarker = int(self.arucoGridMarkerFirstMarkerStr.get()) + dictionary = self.arucoGridMarkerDictionaryStr.get() + subSizeX = int(self.arucoGridMarkerSaveSubSizeXStr.get()) + subSizeY = int(self.arucoGridMarkerSaveSubSizeYStr.get()) + pageBorderX = float(self.arucoGridMarkerSavePageBorderXStr.get()) + pageBorderY = float(self.arucoGridMarkerSavePageBorderYStr.get()) + except ValueError as e: + warnings.warn(str(e)) + messagebox.showinfo("Error", "Enter invalid parameters") + return + except Exception as e: + warnings.warn(str(e)) + messagebox.showinfo("Error", "Fail to get parameters") + return + + # Preview + try: + dpi=self.VisDPI(((markersY * markerLength + (markersY - 1) * markerSeparation + pageBorderY * 2) * MarkerPrinter.ptPerMeter, (markersX * markerLength + (markersX - 1) * markerSeparation + pageBorderX * 2) * MarkerPrinter.ptPerMeter)) + tkImage = PIL.ImageTk.PhotoImage(image = MarkerPrinter.PreviewArucoGridMarkerImage(dictionary, (markersX, markersY), markerLength, markerSeparation, firstMarker, borderBits=borderBits, pageBorder = (pageBorderX, pageBorderY), dpi=dpi)) + self.arucoGridMarkerImageLabel.imgtk = tkImage + self.arucoGridMarkerImageLabel.config(image=tkImage) + except Exception as e: + warnings.warn(str(e)) + messagebox.showinfo("Error", "create marker failed") + return + + # Save + if(askSave): + MarkerPrinterGUI.__SaveMarker(MarkerPrinter.GenArucoGridMarkerImage, \ + dictionary, (markersX, markersY), markerLength, markerSeparation, firstMarker, borderBits=borderBits, subSize = (subSizeX, subSizeY), pageBorder = (pageBorderX, pageBorderY)) + + def OnPreviewArucoGridMarker(self): + self.OnPreviewOrSaveArucoGridMarker(askSave = False) + + def OnSaveArucoGridMarker(self): + self.OnPreviewOrSaveArucoGridMarker(askSave = True) + + def InitArucoGridMarkerTab(self): + self.arucoGridMarkerUIFrame = ttk.Frame(self.arucoGridMarkerTab) + self.arucoGridMarkerImageTab = ttk.Frame(self.arucoGridMarkerTab) + self.arucoGridMarkerUIFrame2 = ttk.Frame(self.arucoGridMarkerTab) + + self.arucoGridMarkerUIFrame.grid(row=0, column=0, sticky = tk.NSEW) + self.arucoGridMarkerImageTab.grid(row=1, column=0, sticky = tk.NSEW) + self.arucoGridMarkerUIFrame2.grid(row=2, column=0, sticky = tk.NSEW) + + self.arucoGridMarkerImageLabel = tk.Label(self.arucoGridMarkerImageTab) + self.arucoGridMarkerImageLabel.grid(row=0, column=0, sticky = tk.NSEW) + + tk.Label(self.arucoGridMarkerUIFrame, text="dictionary").grid(row=0, column=0, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame, text="markersX").grid(row=0, column=1, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame, text="markersY").grid(row=0, column=2, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame, text="markerLength (Unit: Meter)").grid(row=0, column=3, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame, text="markerSeparation (Unit: Meter)").grid(row=0, column=4, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame, text="firstMarker").grid(row=0, column=5, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame, text="borderBits").grid(row=0, column=6, sticky = tk.NSEW) + + self.arucoGridMarkerDictionaryStr = tk.StringVar() + self.arucoGridMarkerMarkersXStr = tk.StringVar() + self.arucoGridMarkerMarkersXStr.set("16") + self.arucoGridMarkerMarkersYStr = tk.StringVar() + self.arucoGridMarkerMarkersYStr.set("9") + self.arucoGridMarkerMarkerLengthStr = tk.StringVar() + self.arucoGridMarkerMarkerLengthStr.set("0.07") + self.arucoGridMarkerMarkerSeparationStr = tk.StringVar() + self.arucoGridMarkerMarkerSeparationStr.set("0.02") + self.arucoGridMarkerFirstMarkerStr = tk.StringVar() + self.arucoGridMarkerFirstMarkerStr.set("0") + self.arucoGridMarkerBorderBitsStr = tk.StringVar() + self.arucoGridMarkerBorderBitsStr.set("1") + + self.arucoGridMarkerDictionaryMenue = tk.OptionMenu(self.arucoGridMarkerUIFrame, self.arucoGridMarkerDictionaryStr, "DICT_ARUCO_ORIGINAL", command = self.OnSelectArucoGridMarkerDictionary) + self.arucoGridMarkerDictionaryMenue.grid(row=1, column=0, sticky = tk.NSEW) + tk.Entry(self.arucoGridMarkerUIFrame, textvariable=self.arucoGridMarkerMarkersXStr).grid(row=1, column=1, sticky = tk.NSEW) + tk.Entry(self.arucoGridMarkerUIFrame, textvariable=self.arucoGridMarkerMarkersYStr).grid(row=1, column=2, sticky = tk.NSEW) + tk.Entry(self.arucoGridMarkerUIFrame, textvariable=self.arucoGridMarkerMarkerLengthStr).grid(row=1, column=3, sticky = tk.NSEW) + tk.Entry(self.arucoGridMarkerUIFrame, textvariable=self.arucoGridMarkerMarkerSeparationStr).grid(row=1, column=4, sticky = tk.NSEW) + tk.Entry(self.arucoGridMarkerUIFrame, textvariable=self.arucoGridMarkerFirstMarkerStr).grid(row=1, column=5, sticky = tk.NSEW) + tk.Entry(self.arucoGridMarkerUIFrame, textvariable=self.arucoGridMarkerBorderBitsStr).grid(row=1, column=6, sticky = tk.NSEW) + + tk.Button(self.arucoGridMarkerUIFrame2, text = "Preview", command = self.OnPreviewArucoGridMarker).grid(row=1, column=0, sticky = tk.NSEW) + tk.Button(self.arucoGridMarkerUIFrame2, text = "Save", command = self.OnSaveArucoGridMarker).grid(row=1, column=1, sticky = tk.NSEW) + + tk.Label(self.arucoGridMarkerUIFrame2, text="Save opetions:").grid(row=0, column=2, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame2, text="(set 0 as disable)").grid(row=1, column=2, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame2, text="subSizeX").grid(row=0, column=3, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame2, text="subSizeY").grid(row=0, column=4, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame2, text="Divide to chunks, chunk sizeX").grid(row=2, column=3, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame2, text="Divide to chunks, chunk sizeY").grid(row=2, column=4, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame2, text="pageBorderX (Unit: Meter)").grid(row=0, column=5, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame2, text="pageBorderY (Unit: Meter)").grid(row=0, column=6, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame2, text="Border or page").grid(row=2, column=5, sticky = tk.NSEW) + tk.Label(self.arucoGridMarkerUIFrame2, text="Border or page").grid(row=2, column=6, sticky = tk.NSEW) + + self.arucoGridMarkerSaveSubSizeXStr = tk.StringVar() + self.arucoGridMarkerSaveSubSizeXStr.set("0") + self.arucoGridMarkerSaveSubSizeYStr = tk.StringVar() + self.arucoGridMarkerSaveSubSizeYStr.set("0") + self.arucoGridMarkerSavePageBorderXStr = tk.StringVar() + self.arucoGridMarkerSavePageBorderXStr.set("0.02") + self.arucoGridMarkerSavePageBorderYStr = tk.StringVar() + self.arucoGridMarkerSavePageBorderYStr.set("0.02") + + tk.Entry(self.arucoGridMarkerUIFrame2, textvariable=self.arucoGridMarkerSaveSubSizeXStr).grid(row=1, column=3, sticky = tk.NSEW) + tk.Entry(self.arucoGridMarkerUIFrame2, textvariable=self.arucoGridMarkerSaveSubSizeYStr).grid(row=1, column=4, sticky = tk.NSEW) + tk.Entry(self.arucoGridMarkerUIFrame2, textvariable=self.arucoGridMarkerSavePageBorderXStr).grid(row=1, column=5, sticky = tk.NSEW) + tk.Entry(self.arucoGridMarkerUIFrame2, textvariable=self.arucoGridMarkerSavePageBorderYStr).grid(row=1, column=6, sticky = tk.NSEW) + + self.arucoGridMarkerDictionaryMenue['menu'].delete(0, 'end') + for dictName in self.dictList: + self.arucoGridMarkerDictionaryMenue['menu'].add_command(label=dictName, command=tk._setit(self.arucoGridMarkerDictionaryStr, dictName, self.OnSelectArucoGridMarkerDictionary)) + + self.OnSelectArucoGridMarkerDictionary("DICT_ARUCO_ORIGINAL") + + def OnSelectArucoMarkerDictionary(self, pDictName): + self.arucoMarkerDictionaryStr.set(pDictName) + + def OnPreviewOrSaveArucoMarker(self, askSave = False): + try: + markerID = int(self.arucoMarkerMarkerIDStr.get()) + markerLength = float(self.arucoMarkerMarkerLengthStr.get()) + borderBits = int(self.arucoMarkerBorderBitsStr.get()) + dictionary = self.arucoMarkerDictionaryStr.get() + pageBorderX = float(self.arucoMarkerSavePageBorderXStr.get()) + pageBorderY = float(self.arucoMarkerSavePageBorderYStr.get()) + except ValueError as e: + warnings.warn(str(e)) + messagebox.showinfo("Error", "Enter invalid parameters") + return + except Exception as e: + warnings.warn(str(e)) + messagebox.showinfo("Error", "Fail to get parameters") + return + + # Preview + try: + dpi=self.VisDPI(((markerLength + pageBorderY * 2) * MarkerPrinter.ptPerMeter, (markerLength + pageBorderX * 2) * MarkerPrinter.ptPerMeter)) + tkImage = PIL.ImageTk.PhotoImage(image = MarkerPrinter.PreviewArucoMarkerImage(dictionary, markerID, markerLength, borderBits=borderBits, pageBorder = (pageBorderX, pageBorderY), dpi=dpi)) + self.arucoMarkerImageLabel.imgtk = tkImage + self.arucoMarkerImageLabel.config(image=tkImage) + except Exception as e: + warnings.warn(str(e)) + messagebox.showinfo("Error", "create marker failed") + return + + # Save + if(askSave): + MarkerPrinterGUI.__SaveMarker(MarkerPrinter.GenArucoMarkerImage, \ + dictionary, markerID, markerLength, borderBits=borderBits, pageBorder = (pageBorderX, pageBorderY)) + + def OnPreviewArucoMarker(self): + self.OnPreviewOrSaveArucoMarker(askSave = False) + + def OnSaveArucoMarker(self): + self.OnPreviewOrSaveArucoMarker(askSave = True) + + def InitArucoMarkerTab(self): + self.arucoMarkerUIFrame = ttk.Frame(self.arucoMarkerTab) + self.arucoMarkerImageTab = ttk.Frame(self.arucoMarkerTab) + self.arucoMarkerUIFrame2 = ttk.Frame(self.arucoMarkerTab) + + self.arucoMarkerUIFrame.grid(row=0, column=0, sticky = tk.NSEW) + self.arucoMarkerImageTab.grid(row=1, column=0, sticky = tk.NSEW) + self.arucoMarkerUIFrame2.grid(row=2, column=0, sticky = tk.NSEW) + + self.arucoMarkerImageLabel = tk.Label(self.arucoMarkerImageTab) + self.arucoMarkerImageLabel.grid(row=0, column=0, sticky = tk.NSEW) + + tk.Label(self.arucoMarkerUIFrame, text="dictionary").grid(row=0, column=0, sticky = tk.NSEW) + tk.Label(self.arucoMarkerUIFrame, text="markerID").grid(row=0, column=1, sticky = tk.NSEW) + tk.Label(self.arucoMarkerUIFrame, text="markerLength (Unit: Meter)").grid(row=0, column=2, sticky = tk.NSEW) + tk.Label(self.arucoMarkerUIFrame, text="borderBits").grid(row=0, column=3, sticky = tk.NSEW) + + self.arucoMarkerDictionaryStr = tk.StringVar() + self.arucoMarkerMarkerIDStr = tk.StringVar() + self.arucoMarkerMarkerIDStr.set("0") + self.arucoMarkerMarkerLengthStr = tk.StringVar() + self.arucoMarkerMarkerLengthStr.set("0.07") + self.arucoMarkerBorderBitsStr = tk.StringVar() + self.arucoMarkerBorderBitsStr.set("1") + + self.arucoMarkerDictionaryMenue = tk.OptionMenu(self.arucoMarkerUIFrame, self.arucoMarkerDictionaryStr, "DICT_ARUCO_ORIGINAL", command = self.OnSelectArucoMarkerDictionary) + self.arucoMarkerDictionaryMenue.grid(row=1, column=0, sticky = tk.NSEW) + tk.Entry(self.arucoMarkerUIFrame, textvariable=self.arucoMarkerMarkerIDStr).grid(row=1, column=1, sticky = tk.NSEW) + tk.Entry(self.arucoMarkerUIFrame, textvariable=self.arucoMarkerMarkerLengthStr).grid(row=1, column=2, sticky = tk.NSEW) + tk.Entry(self.arucoMarkerUIFrame, textvariable=self.arucoMarkerBorderBitsStr).grid(row=1, column=3, sticky = tk.NSEW) + + tk.Button(self.arucoMarkerUIFrame2, text = "Preview", command = self.OnPreviewArucoMarker).grid(row=0, column=0, sticky = tk.NSEW) + tk.Button(self.arucoMarkerUIFrame2, text = "Save", command = self.OnSaveArucoMarker).grid(row=0, column=1, sticky = tk.NSEW) + + tk.Label(self.arucoMarkerUIFrame2, text="Save opetions:").grid(row=0, column=2, sticky = tk.NSEW) + tk.Label(self.arucoMarkerUIFrame2, text="(set 0 as disable)").grid(row=1, column=2, sticky = tk.NSEW) + tk.Label(self.arucoMarkerUIFrame2, text="pageBorderX (Unit: Meter)").grid(row=0, column=3, sticky = tk.NSEW) + tk.Label(self.arucoMarkerUIFrame2, text="pageBorderY (Unit: Meter)").grid(row=0, column=4, sticky = tk.NSEW) + tk.Label(self.arucoMarkerUIFrame2, text="Border or page").grid(row=2, column=3, sticky = tk.NSEW) + tk.Label(self.arucoMarkerUIFrame2, text="Border or page").grid(row=2, column=4, sticky = tk.NSEW) + + self.arucoMarkerSavePageBorderXStr = tk.StringVar() + self.arucoMarkerSavePageBorderXStr.set("0.02") + self.arucoMarkerSavePageBorderYStr = tk.StringVar() + self.arucoMarkerSavePageBorderYStr.set("0.02") + + tk.Entry(self.arucoMarkerUIFrame2, textvariable=self.arucoMarkerSavePageBorderXStr).grid(row=1, column=3, sticky = tk.NSEW) + tk.Entry(self.arucoMarkerUIFrame2, textvariable=self.arucoMarkerSavePageBorderYStr).grid(row=1, column=4, sticky = tk.NSEW) + + self.arucoMarkerDictionaryMenue['menu'].delete(0, 'end') + for dictName in self.dictList: + self.arucoMarkerDictionaryMenue['menu'].add_command(label=dictName, command=tk._setit(self.arucoMarkerDictionaryStr, dictName, self.OnSelectArucoMarkerDictionary)) + + self.OnSelectArucoMarkerDictionary("DICT_ARUCO_ORIGINAL") + + def OnPreviewOrSaveChessMarker(self, askSave = False): + try: + sizeX = int(self.chessMarkerChessboardSizeXStr.get()) + sizeY = int(self.chessMarkerChessboardSizeYStr.get()) + squareLength = float(self.chessMarkerSquareLengthStr.get()) + subSizeX = int(self.chessMarkerSaveSubSizeXStr.get()) + subSizeY = int(self.chessMarkerSaveSubSizeYStr.get()) + pageBorderX = float(self.chessMarkerSavePageBorderXStr.get()) + pageBorderY = float(self.chessMarkerSavePageBorderYStr.get()) + except ValueError as e: + warnings.warn(str(e)) + messagebox.showinfo("Error", "Enter invalid parameters") + return + except Exception as e: + warnings.warn(str(e)) + messagebox.showinfo("Error", "Fail to get parameters") + return + + # Preview + try: + dpi=self.VisDPI(((sizeY * squareLength + pageBorderY * 2) * MarkerPrinter.ptPerMeter, (sizeX * squareLength + pageBorderX * 2) * MarkerPrinter.ptPerMeter)) + tkImage = PIL.ImageTk.PhotoImage(image = MarkerPrinter.PreviewChessMarkerImage((sizeX, sizeY), squareLength, pageBorder = (pageBorderX, pageBorderY), dpi=dpi)) + self.chessMarkerImageLabel.imgtk = tkImage + self.chessMarkerImageLabel.config(image=tkImage) + except Exception as e: + warnings.warn(str(e)) + messagebox.showinfo("Error", "create marker failed") + return + + # Save + if(askSave): + MarkerPrinterGUI.__SaveMarker(MarkerPrinter.GenChessMarkerImage, \ + (sizeX, sizeY), squareLength, subSize = (subSizeX, subSizeY), pageBorder = (pageBorderX, pageBorderY)) + + def OnPreviewChessMarker(self): + self.OnPreviewOrSaveChessMarker(askSave = False) + + def OnSaveChessMarker(self): + self.OnPreviewOrSaveChessMarker(askSave = True) + + def InitChessMarkerTab(self): + self.chessMarkerUIFrame = ttk.Frame(self.chessMarkerTab) + self.chessMarkerImageTab = ttk.Frame(self.chessMarkerTab) + self.chessMarkerUIFrame2 = ttk.Frame(self.chessMarkerTab) + + self.chessMarkerUIFrame.grid(row=0, column=0, sticky = tk.NSEW) + self.chessMarkerImageTab.grid(row=1, column=0, sticky = tk.NSEW) + self.chessMarkerUIFrame2.grid(row=2, column=0, sticky = tk.NSEW) + + self.chessMarkerImageLabel = tk.Label(self.chessMarkerImageTab) + self.chessMarkerImageLabel.grid(row=0, column=0, sticky = tk.NSEW) + + tk.Label(self.chessMarkerUIFrame, text="chessboardSizeX").grid(row=0, column=0, sticky = tk.NSEW) + tk.Label(self.chessMarkerUIFrame, text="chessboardSizeY").grid(row=0, column=1, sticky = tk.NSEW) + tk.Label(self.chessMarkerUIFrame, text="squareLength (Unit: Meter)").grid(row=0, column=2, sticky = tk.NSEW) + + self.chessMarkerChessboardSizeXStr = tk.StringVar() + self.chessMarkerChessboardSizeXStr.set("16") + self.chessMarkerChessboardSizeYStr = tk.StringVar() + self.chessMarkerChessboardSizeYStr.set("9") + self.chessMarkerSquareLengthStr = tk.StringVar() + self.chessMarkerSquareLengthStr.set("0.09") + + tk.Entry(self.chessMarkerUIFrame, textvariable=self.chessMarkerChessboardSizeXStr).grid(row=1, column=0, sticky = tk.NSEW) + tk.Entry(self.chessMarkerUIFrame, textvariable=self.chessMarkerChessboardSizeYStr).grid(row=1, column=1, sticky = tk.NSEW) + tk.Entry(self.chessMarkerUIFrame, textvariable=self.chessMarkerSquareLengthStr).grid(row=1, column=2, sticky = tk.NSEW) + + tk.Button(self.chessMarkerUIFrame2, text = "Preview", command = self.OnPreviewChessMarker).grid(row=1, column=0, sticky = tk.NSEW) + tk.Button(self.chessMarkerUIFrame2, text = "Save", command = self.OnSaveChessMarker).grid(row=1, column=1, sticky = tk.NSEW) + + tk.Label(self.chessMarkerUIFrame2, text="Save opetions:").grid(row=0, column=2, sticky = tk.NSEW) + tk.Label(self.chessMarkerUIFrame2, text="(set 0 as disable)").grid(row=1, column=2, sticky = tk.NSEW) + tk.Label(self.chessMarkerUIFrame2, text="subSizeX").grid(row=0, column=3, sticky = tk.NSEW) + tk.Label(self.chessMarkerUIFrame2, text="subSizeY").grid(row=0, column=4, sticky = tk.NSEW) + tk.Label(self.chessMarkerUIFrame2, text="Divide to chunks, chunk sizeX").grid(row=2, column=3, sticky = tk.NSEW) + tk.Label(self.chessMarkerUIFrame2, text="Divide to chunks, chunk sizeY").grid(row=2, column=4, sticky = tk.NSEW) + tk.Label(self.chessMarkerUIFrame2, text="pageBorderX (Unit: Meter)").grid(row=0, column=5, sticky = tk.NSEW) + tk.Label(self.chessMarkerUIFrame2, text="pageBorderY (Unit: Meter)").grid(row=0, column=6, sticky = tk.NSEW) + tk.Label(self.chessMarkerUIFrame2, text="Border or page").grid(row=2, column=5, sticky = tk.NSEW) + tk.Label(self.chessMarkerUIFrame2, text="Border or page").grid(row=2, column=6, sticky = tk.NSEW) + + self.chessMarkerSaveSubSizeXStr = tk.StringVar() + self.chessMarkerSaveSubSizeXStr.set("0") + self.chessMarkerSaveSubSizeYStr = tk.StringVar() + self.chessMarkerSaveSubSizeYStr.set("0") + self.chessMarkerSavePageBorderXStr = tk.StringVar() + self.chessMarkerSavePageBorderXStr.set("0.02") + self.chessMarkerSavePageBorderYStr = tk.StringVar() + self.chessMarkerSavePageBorderYStr.set("0.02") + + tk.Entry(self.chessMarkerUIFrame2, textvariable=self.chessMarkerSaveSubSizeXStr).grid(row=1, column=3, sticky = tk.NSEW) + tk.Entry(self.chessMarkerUIFrame2, textvariable=self.chessMarkerSaveSubSizeYStr).grid(row=1, column=4, sticky = tk.NSEW) + tk.Entry(self.chessMarkerUIFrame2, textvariable=self.chessMarkerSavePageBorderXStr).grid(row=1, column=5, sticky = tk.NSEW) + tk.Entry(self.chessMarkerUIFrame2, textvariable=self.chessMarkerSavePageBorderYStr).grid(row=1, column=6, sticky = tk.NSEW) + + def Update(self): + time.sleep(0) + self.window.after(self.delay, self.Update) + + def __init__(self, pDelay=15, pDisplayShape=(int(400), int(1200))): + self.delay = pDelay + self.displayShape = pDisplayShape + + self.dictList = MarkerPrinter.arucoDictBytesList.keys() + + # GUI + self.window = tk.Tk() + self.notebook = ttk.Notebook(self.window) + self.notebook.grid(row=0, column=0, sticky = tk.NSEW) + + self.window.title("MarkerPrinterGUI") + self.window.config(cursor="arrow") + self.window.protocol("WM_DELETE_WINDOW", self.OnCloseWindow) + + # Menues + self.menu = tk.Menu(self.window) + self.helpMenu = tk.Menu(self.menu, tearoff=0) + self.menu.add_cascade(label="Help", menu=self.helpMenu) + self.helpMenu.add_command(label="Github", command=self.OnShowingHelpGithub) + self.helpMenu.add_command(label="DEBUG_LINE_MODE", command=self.On_DEBUG_LINE_MODE) + self.helpMenu.add_command(label="DEBUG_BLOCK_MODE", command=self.On_DEBUG_BLOCK_MODE) + self.helpMenu.add_command(label="CLOSE_DEBUG_MODE", command=self.On_CLOSE_DEBUG_MODE) + self.window.config(menu=self.menu) + + self.charucoMarkerTab = ttk.Frame(self.notebook) + self.arucoMarkerTab = ttk.Frame(self.notebook) + self.arucoGridMarkerTab = ttk.Frame(self.notebook) + self.chessMarkerTab = ttk.Frame(self.notebook) + + self.notebook.add(self.charucoMarkerTab, text='ChArUco Marker') + self.notebook.add(self.arucoMarkerTab, text='ArUco Marker') + self.notebook.add(self.arucoGridMarkerTab, text='ArUcoGrid Marker') + self.notebook.add(self.chessMarkerTab, text='Chessboard Marker') + + self.InitCharucoMarkerTab() + self.InitArucoMarkerTab() + self.InitArucoGridMarkerTab() + self.InitChessMarkerTab() + + self.Update() + self.window.mainloop() + + def On_DEBUG_LINE_MODE(self): + messagebox.showinfo("Note", "You enabled the debug mode: \"LINE\"") + MarkerPrinter.debugMode = "LINE" + + def On_DEBUG_BLOCK_MODE(self): + messagebox.showinfo("Note", "You enabled the debug mode: \"BLOCK\"") + MarkerPrinter.debugMode = "BLOCK" + + def On_CLOSE_DEBUG_MODE(self): + messagebox.showinfo("Note", "You closed the debug mode") + MarkerPrinter.debugMode = None + +if __name__ == '__main__': + MarkerPrinterGUI() diff --git a/modules/aruco/misc/pattern_generator/README.md b/modules/aruco/misc/pattern_generator/README.md new file mode 100644 index 00000000000..994dc9311b7 --- /dev/null +++ b/modules/aruco/misc/pattern_generator/README.md @@ -0,0 +1,68 @@ +# OpenCVMarkerPrinter + +## Description +This small app can save some commonly used opencv markers such as ArUco, ArUcoGrid, Chessboard and ChArUco to vector graphics file. **Supported vector graphics file format: .svg, .pdf and .ps.** + + + +### Dependencies +#### MarkerPrinter + * numpy + * PIL(Pillow, for image processing) + * cairo(for drawing vector graphic) + * cairosvg(for svg to png) + +#### MarkerPrinterGUI + * tkinter(for GUI) + +## Tutorial +#### GUI +``` +python MarkerPrinterGUI.py +``` + +You can switch ArUco, ArUcoGrid, Chessboard and ChArUco mode at the GUI tab, then you can select dictionary from the GUI menu and modify board shape, marker size, border width... etc. at the GUI entry, finally click the preview or save button to show the marker image on the GUI window or save it to file. + +#### Command-Line +##### Print help +``` +python MarkerPrinter.py +``` + +##### Print predefined dictionary list +``` +python MarkerPrinter.py --list_dictionary +``` + +##### Save chessboard +``` +python MarkerPrinter.py --chess --file "./chess.pdf" --size_x 16 --size_y 9 --square_length 0.09 +``` + +##### Save ArUco +``` +python MarkerPrinter.py --aruco --file "./aruco.pdf" --dictionary DICT_ARUCO_ORIGINAL --marker_length 0.07 --marker_id 0 --border_bits 1 +``` + +##### Save ArUco Grid +``` +python MarkerPrinter.py --aruco_grid --file "./aruco_grid.pdf" --dictionary DICT_ARUCO_ORIGINAL --size_x 16 --size_y 9 --marker_length 0.07 --marker_separation 0.02 --first_marker 0 --border_bits 1 +``` + +##### Save ChArUco +``` +python MarkerPrinter.py --charuco --file "./charuco.pdf" --dictionary DICT_ARUCO_ORIGINAL --size_x 16 --size_y 9 --square_length 0.09 --marker_length 0.07 --border_bits 1 +``` + +## Useful Options: +### Divde output to chunks +If you are using consumer level printer, you will suffer from not able printing too large marker, so just set chunks shape at the GUI subSize entry before saving the marker to files, it will divide output marker to chunks. If you are using command-line interface, just add --sub_size_x x --sub_size_y y as parameters. + +### Page border +If you are printing the image directly, you will need add page border to protect the marker, so just set page border at the GUI pageBorder entry before saving the marker to files. If you are using command-line interface, just add --page_border_x x --page_border_y y as parameters. + +### Generate aruco data: +Although there is a built-in aruco dictionary data, but if you want to update the dictionary(If aruco update predefined dictionary list), just install opencv-python and opencv-contrib-python, and than run +``` +python MarkerPrinter.py --generate arucoDictBytesList.npz +``` diff --git a/modules/aruco/misc/pattern_generator/arucoDictBytesList.npz b/modules/aruco/misc/pattern_generator/arucoDictBytesList.npz new file mode 100644 index 00000000000..64bcbc96cb5 Binary files /dev/null and b/modules/aruco/misc/pattern_generator/arucoDictBytesList.npz differ diff --git a/modules/aruco/misc/pattern_generator/doc/images/MarkerPrinterGUI.jpg b/modules/aruco/misc/pattern_generator/doc/images/MarkerPrinterGUI.jpg new file mode 100644 index 00000000000..4aed275565a Binary files /dev/null and b/modules/aruco/misc/pattern_generator/doc/images/MarkerPrinterGUI.jpg differ diff --git a/modules/aruco/src/aruco.cpp b/modules/aruco/src/aruco.cpp index 41b137d3c5b..dee2669ebc1 100644 --- a/modules/aruco/src/aruco.cpp +++ b/modules/aruco/src/aruco.cpp @@ -345,49 +345,6 @@ static void _filterTooCloseCandidates(const vector< vector< Point2f > > &candida contoursSetOut.push_back(smallerContours); } - -/** - * ParallelLoopBody class for the parallelization of the basic candidate detections using - * different threhold window sizes. Called from function _detectInitialCandidates() - */ -class DetectInitialCandidatesParallel : public ParallelLoopBody { - public: - DetectInitialCandidatesParallel(const Mat *_grey, - vector< vector< vector< Point2f > > > *_candidatesArrays, - vector< vector< vector< Point > > > *_contoursArrays, - const Ptr &_params) - : grey(_grey), candidatesArrays(_candidatesArrays), contoursArrays(_contoursArrays), - params(_params) {} - - void operator()(const Range &range) const CV_OVERRIDE { - const int begin = range.start; - const int end = range.end; - - for(int i = begin; i < end; i++) { - int currScale = - params->adaptiveThreshWinSizeMin + i * params->adaptiveThreshWinSizeStep; - // threshold - Mat thresh; - _threshold(*grey, thresh, currScale, params->adaptiveThreshConstant); - - // detect rectangles - _findMarkerContours(thresh, (*candidatesArrays)[i], (*contoursArrays)[i], - params->minMarkerPerimeterRate, params->maxMarkerPerimeterRate, - params->polygonalApproxAccuracyRate, params->minCornerDistanceRate, - params->minDistanceToBorder); - } - } - - private: - DetectInitialCandidatesParallel &operator=(const DetectInitialCandidatesParallel &); - - const Mat *grey; - vector< vector< vector< Point2f > > > *candidatesArrays; - vector< vector< vector< Point > > > *contoursArrays; - const Ptr ¶ms; -}; - - /** * @brief Initial steps on finding square candidates */ @@ -407,21 +364,23 @@ static void _detectInitialCandidates(const Mat &grey, vector< vector< Point2f > vector< vector< vector< Point > > > contoursArrays((size_t) nScales); ////for each value in the interval of thresholding window sizes - // for(int i = 0; i < nScales; i++) { - // int currScale = params.adaptiveThreshWinSizeMin + i*params.adaptiveThreshWinSizeStep; - // // treshold - // Mat thresh; - // _threshold(grey, thresh, currScale, params.adaptiveThreshConstant); - // // detect rectangles - // _findMarkerContours(thresh, candidatesArrays[i], contoursArrays[i], - // params.minMarkerPerimeterRate, - // params.maxMarkerPerimeterRate, params.polygonalApproxAccuracyRate, - // params.minCornerDistance, params.minDistanceToBorder); - //} - - // this is the parallel call for the previous commented loop (result is equivalent) - parallel_for_(Range(0, nScales), DetectInitialCandidatesParallel(&grey, &candidatesArrays, - &contoursArrays, params)); + parallel_for_(Range(0, nScales), [&](const Range& range) { + const int begin = range.start; + const int end = range.end; + + for (int i = begin; i < end; i++) { + int currScale = params->adaptiveThreshWinSizeMin + i * params->adaptiveThreshWinSizeStep; + // threshold + Mat thresh; + _threshold(grey, thresh, currScale, params->adaptiveThreshConstant); + + // detect rectangles + _findMarkerContours(thresh, candidatesArrays[i], contoursArrays[i], + params->minMarkerPerimeterRate, params->maxMarkerPerimeterRate, + params->polygonalApproxAccuracyRate, params->minCornerDistanceRate, + params->minDistanceToBorder); + } + }); // join candidates for(int i = 0; i < nScales; i++) { @@ -612,50 +571,6 @@ static uint8_t _identifyOneCandidate(const Ptr& dictionary, InputArr return typ; } - -/** - * ParallelLoopBody class for the parallelization of the marker identification step - * Called from function _identifyCandidates() - */ -class IdentifyCandidatesParallel : public ParallelLoopBody { - public: - IdentifyCandidatesParallel(const Mat& _grey, vector< vector< Point2f > >& _candidates, - const Ptr &_dictionary, - vector< int >& _idsTmp, vector< uint8_t >& _validCandidates, - const Ptr &_params, - vector< int > &_rotated) - : grey(_grey), candidates(_candidates), dictionary(_dictionary), - idsTmp(_idsTmp), validCandidates(_validCandidates), params(_params), rotated(_rotated) {} - - void operator()(const Range &range) const CV_OVERRIDE - { - const int begin = range.start; - const int end = range.end; - - for(int i = begin; i < end; i++) { - int currId; - validCandidates[i] = _identifyOneCandidate(dictionary, grey, candidates[i], currId, params, rotated[i]); - - if(validCandidates[i] > 0) - idsTmp[i] = currId; - } - - } - - private: - IdentifyCandidatesParallel &operator=(const IdentifyCandidatesParallel &); // to quiet MSVC - - const Mat &grey; - vector< vector< Point2f > >& candidates; - const Ptr &dictionary; - vector< int > &idsTmp; - vector< uint8_t > &validCandidates; - const Ptr ¶ms; - vector< int > &rotated; -}; - - - /** * @brief Copy the contents of a corners vector to an OutputArray, settings its size. */ @@ -721,11 +636,20 @@ static void _identifyCandidates(InputArray _image, vector< vector< vector< Point vector< uint8_t > validCandidates(ncandidates, 0); //// Analyze each of the candidates - parallel_for_(Range(0, ncandidates), - IdentifyCandidatesParallel(grey, - params->detectInvertedMarker ? _candidatesSet[1] : _candidatesSet[0], - _dictionary, idsTmp, - validCandidates, params, rotated)); + parallel_for_(Range(0, ncandidates), [&](const Range &range) { + const int begin = range.start; + const int end = range.end; + + vector< vector< Point2f > >& candidates = params->detectInvertedMarker ? _candidatesSet[1] : _candidatesSet[0]; + + for(int i = begin; i < end; i++) { + int currId; + validCandidates[i] = _identifyOneCandidate(_dictionary, grey, candidates[i], currId, params, rotated[i]); + + if(validCandidates[i] > 0) + idsTmp[i] = currId; + } + }); for(int i = 0; i < ncandidates; i++) { if(validCandidates[i] > 0) { @@ -776,40 +700,6 @@ static void _getSingleMarkerObjectPoints(float markerLength, OutputArray _objPoi objPoints.ptr< Vec3f >(0)[3] = Vec3f(-markerLength / 2.f, -markerLength / 2.f, 0); } - - - -/** - * ParallelLoopBody class for the parallelization of the marker corner subpixel refinement - * Called from function detectMarkers() - */ -class MarkerSubpixelParallel : public ParallelLoopBody { - public: - MarkerSubpixelParallel(const Mat *_grey, OutputArrayOfArrays _corners, - const Ptr &_params) - : grey(_grey), corners(_corners), params(_params) {} - - void operator()(const Range &range) const CV_OVERRIDE { - const int begin = range.start; - const int end = range.end; - - for(int i = begin; i < end; i++) { - cornerSubPix(*grey, corners.getMat(i), - Size(params->cornerRefinementWinSize, params->cornerRefinementWinSize), - Size(-1, -1), TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, - params->cornerRefinementMaxIterations, - params->cornerRefinementMinAccuracy)); - } - } - - private: - MarkerSubpixelParallel &operator=(const MarkerSubpixelParallel &); // to quiet MSVC - - const Mat *grey; - OutputArrayOfArrays corners; - const Ptr ¶ms; -}; - /** * Line fitting A * B = C :: Called from function refineCandidateLines * @param nContours, contour-container @@ -952,34 +842,6 @@ static void _refineCandidateLines(std::vector& nContours, std::vector >& _contours, vector< vector< Point2f > >& _candidates, const Mat& _camMatrix, const Mat& _distCoeff) - : contours(_contours), candidates(_candidates), camMatrix(_camMatrix), distCoeff(_distCoeff){} - - void operator()(const Range &range) const CV_OVERRIDE { - - for(int i = range.start; i < range.end; i++) { - _refineCandidateLines(contours[i], candidates[i], camMatrix, distCoeff); - } - } - - private: - MarkerContourParallel &operator=(const MarkerContourParallel &){ - return *this; - } - - vector< vector< Point > >& contours; - vector< vector< Point2f > >& candidates; - const Mat& camMatrix; - const Mat& distCoeff; -}; - #ifdef APRIL_DEBUG static void _darken(const Mat &im){ for (int y = 0; y < im.rows; y++) { @@ -1159,17 +1021,19 @@ void detectMarkers(InputArray _image, const Ptr &_dictionary, Output _params->cornerRefinementMinAccuracy > 0); //// do corner refinement for each of the detected markers - // for (unsigned int i = 0; i < _corners.cols(); i++) { - // cornerSubPix(grey, _corners.getMat(i), - // Size(params.cornerRefinementWinSize, params.cornerRefinementWinSize), - // Size(-1, -1), TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, - // params.cornerRefinementMaxIterations, - // params.cornerRefinementMinAccuracy)); - //} - - // this is the parallel call for the previous commented loop (result is equivalent) - parallel_for_(Range(0, _corners.cols()), - MarkerSubpixelParallel(&grey, _corners, _params)); + parallel_for_(Range(0, _corners.cols()), [&](const Range& range) { + const int begin = range.start; + const int end = range.end; + + for (int i = begin; i < end; i++) { + cornerSubPix(grey, _corners.getMat(i), + Size(_params->cornerRefinementWinSize, _params->cornerRefinementWinSize), + Size(-1, -1), + TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, + _params->cornerRefinementMaxIterations, + _params->cornerRefinementMinAccuracy)); + } + }); } /// STEP 3, Optional : Corner refinement :: use contour container @@ -1178,7 +1042,12 @@ void detectMarkers(InputArray _image, const Ptr &_dictionary, Output if(! _ids.empty()){ // do corner refinement using the contours for each detected markers - parallel_for_(Range(0, _corners.cols()), MarkerContourParallel(contours, candidates, camMatrix.getMat(), distCoeff.getMat())); + parallel_for_(Range(0, _corners.cols()), [&](const Range& range) { + for (int i = range.start; i < range.end; i++) { + _refineCandidateLines(contours[i], candidates[i], camMatrix.getMat(), + distCoeff.getMat()); + } + }); // copy the corners to the output array _copyVector2Output(candidates, _corners); @@ -1186,42 +1055,6 @@ void detectMarkers(InputArray _image, const Ptr &_dictionary, Output } } - - -/** - * ParallelLoopBody class for the parallelization of the single markers pose estimation - * Called from function estimatePoseSingleMarkers() - */ -class SinglePoseEstimationParallel : public ParallelLoopBody { - public: - SinglePoseEstimationParallel(Mat& _markerObjPoints, InputArrayOfArrays _corners, - InputArray _cameraMatrix, InputArray _distCoeffs, - Mat& _rvecs, Mat& _tvecs) - : markerObjPoints(_markerObjPoints), corners(_corners), cameraMatrix(_cameraMatrix), - distCoeffs(_distCoeffs), rvecs(_rvecs), tvecs(_tvecs) {} - - void operator()(const Range &range) const CV_OVERRIDE { - const int begin = range.start; - const int end = range.end; - - for(int i = begin; i < end; i++) { - solvePnP(markerObjPoints, corners.getMat(i), cameraMatrix, distCoeffs, - rvecs.at(i), tvecs.at(i)); - } - } - - private: - SinglePoseEstimationParallel &operator=(const SinglePoseEstimationParallel &); // to quiet MSVC - - Mat& markerObjPoints; - InputArrayOfArrays corners; - InputArray cameraMatrix, distCoeffs; - Mat& rvecs, tvecs; -}; - - - - /** */ void estimatePoseSingleMarkers(InputArrayOfArrays _corners, float markerLength, @@ -1239,15 +1072,16 @@ void estimatePoseSingleMarkers(InputArrayOfArrays _corners, float markerLength, Mat rvecs = _rvecs.getMat(), tvecs = _tvecs.getMat(); //// for each marker, calculate its pose - // for (int i = 0; i < nMarkers; i++) { - // solvePnP(markerObjPoints, _corners.getMat(i), _cameraMatrix, _distCoeffs, - // _rvecs.getMat(i), _tvecs.getMat(i)); - //} - - // this is the parallel call for the previous commented loop (result is equivalent) - parallel_for_(Range(0, nMarkers), - SinglePoseEstimationParallel(markerObjPoints, _corners, _cameraMatrix, - _distCoeffs, rvecs, tvecs)); + parallel_for_(Range(0, nMarkers), [&](const Range& range) { + const int begin = range.start; + const int end = range.end; + + for (int i = begin; i < end; i++) { + solvePnP(markerObjPoints, _corners.getMat(i), _cameraMatrix, _distCoeffs, rvecs.at(i), + tvecs.at(i)); + } + }); + if(_objPoints.needed()){ markerObjPoints.convertTo(_objPoints, -1); } @@ -1599,8 +1433,8 @@ void refineDetectedMarkers(InputArray _image, const Ptr &_board, /** */ int estimatePoseBoard(InputArrayOfArrays _corners, InputArray _ids, const Ptr &board, - InputArray _cameraMatrix, InputArray _distCoeffs, OutputArray _rvec, - OutputArray _tvec, bool useExtrinsicGuess) { + InputArray _cameraMatrix, InputArray _distCoeffs, InputOutputArray _rvec, + InputOutputArray _tvec, bool useExtrinsicGuess) { CV_Assert(_corners.total() == _ids.total()); diff --git a/modules/aruco/src/charuco.cpp b/modules/aruco/src/charuco.cpp index e1cf224371b..d39dca9960d 100644 --- a/modules/aruco/src/charuco.cpp +++ b/modules/aruco/src/charuco.cpp @@ -270,50 +270,6 @@ static int _filterCornersWithoutMinMarkers(const Ptr &_board, return (int)_filteredCharucoIds.total(); } - -/** - * ParallelLoopBody class for the parallelization of the charuco corners subpixel refinement - * Called from function _selectAndRefineChessboardCorners() - */ -class CharucoSubpixelParallel : public ParallelLoopBody { - public: - CharucoSubpixelParallel(const Mat *_grey, vector< Point2f > *_filteredChessboardImgPoints, - vector< Size > *_filteredWinSizes, const Ptr &_params) - : grey(_grey), filteredChessboardImgPoints(_filteredChessboardImgPoints), - filteredWinSizes(_filteredWinSizes), params(_params) {} - - void operator()(const Range &range) const CV_OVERRIDE { - const int begin = range.start; - const int end = range.end; - - for(int i = begin; i < end; i++) { - vector< Point2f > in; - in.push_back((*filteredChessboardImgPoints)[i]); - Size winSize = (*filteredWinSizes)[i]; - if(winSize.height == -1 || winSize.width == -1) - winSize = Size(params->cornerRefinementWinSize, params->cornerRefinementWinSize); - - cornerSubPix(*grey, in, winSize, Size(), - TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, - params->cornerRefinementMaxIterations, - params->cornerRefinementMinAccuracy)); - - (*filteredChessboardImgPoints)[i] = in[0]; - } - } - - private: - CharucoSubpixelParallel &operator=(const CharucoSubpixelParallel &); // to quiet MSVC - - const Mat *grey; - vector< Point2f > *filteredChessboardImgPoints; - vector< Size > *filteredWinSizes; - const Ptr ¶ms; -}; - - - - /** * @brief From all projected chessboard corners, select those inside the image and apply subpixel * refinement. Returns number of valid corners. @@ -353,23 +309,25 @@ static int _selectAndRefineChessboardCorners(InputArray _allCorners, InputArray const Ptr params = DetectorParameters::create(); // use default params for corner refinement //// For each of the charuco corners, apply subpixel refinement using its correspondind winSize - // for(unsigned int i=0; i in; - // in.push_back(filteredChessboardImgPoints[i]); - // Size winSize = filteredWinSizes[i]; - // if(winSize.height == -1 || winSize.width == -1) - // winSize = Size(params.cornerRefinementWinSize, params.cornerRefinementWinSize); - // cornerSubPix(grey, in, winSize, Size(), - // TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, - // params->cornerRefinementMaxIterations, - // params->cornerRefinementMinAccuracy)); - // filteredChessboardImgPoints[i] = in[0]; - //} - - // this is the parallel call for the previous commented loop (result is equivalent) - parallel_for_( - Range(0, (int)filteredChessboardImgPoints.size()), - CharucoSubpixelParallel(&grey, &filteredChessboardImgPoints, &filteredWinSizes, params)); + parallel_for_(Range(0, (int)filteredChessboardImgPoints.size()), [&](const Range& range) { + const int begin = range.start; + const int end = range.end; + + for (int i = begin; i < end; i++) { + vector in; + in.push_back(filteredChessboardImgPoints[i]); + Size winSize = filteredWinSizes[i]; + if (winSize.height == -1 || winSize.width == -1) + winSize = Size(params->cornerRefinementWinSize, params->cornerRefinementWinSize); + + cornerSubPix(grey, in, winSize, Size(), + TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, + params->cornerRefinementMaxIterations, + params->cornerRefinementMinAccuracy)); + + filteredChessboardImgPoints[i] = in[0]; + } + }); // parse output Mat(filteredChessboardImgPoints).copyTo(_selectedCorners); @@ -656,7 +614,7 @@ static bool _arePointsEnoughForPoseEstimation(const vector< Point3f > &points) { */ bool estimatePoseCharucoBoard(InputArray _charucoCorners, InputArray _charucoIds, const Ptr &_board, InputArray _cameraMatrix, InputArray _distCoeffs, - OutputArray _rvec, OutputArray _tvec, bool useExtrinsicGuess) { + InputOutputArray _rvec, InputOutputArray _tvec, bool useExtrinsicGuess) { CV_Assert((_charucoCorners.getMat().total() == _charucoIds.getMat().total())); diff --git a/modules/aruco/tutorials/table_of_content_aruco.markdown b/modules/aruco/tutorials/table_of_content_aruco.markdown index 7129bdc626c..7f7932d6b51 100644 --- a/modules/aruco/tutorials/table_of_content_aruco.markdown +++ b/modules/aruco/tutorials/table_of_content_aruco.markdown @@ -11,6 +11,11 @@ Also, the ChArUco functionalities combine ArUco markers with traditional chessbo an easy and versatile corner detection. The module also includes the functions to detect ChArUco corners and use them for pose estimation and camera calibration. +If you are going to print out the markers, an useful script/GUI tool is place at +opencv_contrib/modules/aruco/misc/pattern_generator/ that can generate vector graphics +of ArUco, ArUcoGrid and ChArUco boards. It can help you to print out the pattern with real size +and without artifacts. + - @subpage tutorial_aruco_detection *Compatibility:* \> OpenCV 3.0 diff --git a/modules/bioinspired/src/retina.cpp b/modules/bioinspired/src/retina.cpp index a6b42f0501d..fa6e582cef7 100644 --- a/modules/bioinspired/src/retina.cpp +++ b/modules/bioinspired/src/retina.cpp @@ -562,7 +562,7 @@ bool RetinaImpl::ocl_run(InputArray inputMatToConvert) void RetinaImpl::run(InputArray inputMatToConvert) { - CV_OCL_RUN((_ocl_retina != 0 && inputMatToConvert.isUMat()), ocl_run(inputMatToConvert)); + CV_OCL_RUN((_ocl_retina && inputMatToConvert.isUMat()), ocl_run(inputMatToConvert)); _wasOCLRunCalled = false; // first convert input image to the compatible format : std::valarray @@ -830,7 +830,7 @@ bool RetinaImpl::_convertCvMat2ValarrayBuffer(InputArray inputMat, std::valarray void RetinaImpl::clearBuffers() { #ifdef HAVE_OPENCL - if (_ocl_retina != 0) + if (_ocl_retina) _ocl_retina->clearBuffers(); #endif diff --git a/modules/ccalib/include/opencv2/ccalib.hpp b/modules/ccalib/include/opencv2/ccalib.hpp index c9b9391cb48..538ec0f81b6 100644 --- a/modules/ccalib/include/opencv2/ccalib.hpp +++ b/modules/ccalib/include/opencv2/ccalib.hpp @@ -96,21 +96,21 @@ class CV_EXPORTS CustomPattern : public Algorithm Calls the calirateCamera function with the same inputs. */ - bool findRt(InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix, InputArray distCoeffs, - OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess = false, int flags = SOLVEPNP_ITERATIVE); - bool findRt(InputArray image, InputArray cameraMatrix, InputArray distCoeffs, - OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess = false, int flags = SOLVEPNP_ITERATIVE); + bool findRt(InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix, InputArray distCoeffs, + InputOutputArray rvec, InputOutputArray tvec, bool useExtrinsicGuess = false, int flags = SOLVEPNP_ITERATIVE); + bool findRt(InputArray image, InputArray cameraMatrix, InputArray distCoeffs, + InputOutputArray rvec, InputOutputArray tvec, bool useExtrinsicGuess = false, int flags = SOLVEPNP_ITERATIVE); /**< Uses solvePnP to find the rotation and translation of the pattern with respect to the camera frame. */ - bool findRtRANSAC(InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix, InputArray distCoeffs, - OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess = false, int iterationsCount = 100, - float reprojectionError = 8.0, int minInliersCount = 100, OutputArray inliers = noArray(), int flags = SOLVEPNP_ITERATIVE); - bool findRtRANSAC(InputArray image, InputArray cameraMatrix, InputArray distCoeffs, - OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess = false, int iterationsCount = 100, - float reprojectionError = 8.0, int minInliersCount = 100, OutputArray inliers = noArray(), int flags = SOLVEPNP_ITERATIVE); + bool findRtRANSAC(InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix, InputArray distCoeffs, + InputOutputArray rvec, InputOutputArray tvec, bool useExtrinsicGuess = false, int iterationsCount = 100, + float reprojectionError = 8.0, int minInliersCount = 100, OutputArray inliers = noArray(), int flags = SOLVEPNP_ITERATIVE); + bool findRtRANSAC(InputArray image, InputArray cameraMatrix, InputArray distCoeffs, + InputOutputArray rvec, InputOutputArray tvec, bool useExtrinsicGuess = false, int iterationsCount = 100, + float reprojectionError = 8.0, int minInliersCount = 100, OutputArray inliers = noArray(), int flags = SOLVEPNP_ITERATIVE); /**< Uses solvePnPRansac() */ diff --git a/modules/ccalib/src/ccalib.cpp b/modules/ccalib/src/ccalib.cpp index 249d5b14a5c..03f1414d8e6 100644 --- a/modules/ccalib/src/ccalib.cpp +++ b/modules/ccalib/src/ccalib.cpp @@ -425,13 +425,13 @@ double CustomPattern::calibrate(InputArrayOfArrays objectPoints, InputArrayOfArr } bool CustomPattern::findRt(InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix, - InputArray distCoeffs, OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess, int flags) + InputArray distCoeffs, InputOutputArray rvec, InputOutputArray tvec, bool useExtrinsicGuess, int flags) { return solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess, flags); } bool CustomPattern::findRt(InputArray image, InputArray cameraMatrix, InputArray distCoeffs, - OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess, int flags) + InputOutputArray rvec, InputOutputArray tvec, bool useExtrinsicGuess, int flags) { vector imagePoints; vector objectPoints; @@ -442,17 +442,17 @@ bool CustomPattern::findRt(InputArray image, InputArray cameraMatrix, InputArray } bool CustomPattern::findRtRANSAC(InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix, InputArray distCoeffs, - OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess, int iterationsCount, - float reprojectionError, int minInliersCount, OutputArray inliers, int flags) + InputOutputArray rvec, InputOutputArray tvec, bool useExtrinsicGuess, int iterationsCount, + float reprojectionError, int minInliersCount, OutputArray inliers, int flags) { solvePnPRansac(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess, - iterationsCount, reprojectionError, minInliersCount, inliers, flags); + iterationsCount, reprojectionError, minInliersCount, inliers, flags); return true; // for consistency with the other methods } bool CustomPattern::findRtRANSAC(InputArray image, InputArray cameraMatrix, InputArray distCoeffs, - OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess, int iterationsCount, - float reprojectionError, int minInliersCount, OutputArray inliers, int flags) + InputOutputArray rvec, InputOutputArray tvec, bool useExtrinsicGuess, int iterationsCount, + float reprojectionError, int minInliersCount, OutputArray inliers, int flags) { vector imagePoints; vector objectPoints; @@ -460,7 +460,7 @@ bool CustomPattern::findRtRANSAC(InputArray image, InputArray cameraMatrix, Inpu if (!findPattern(image, imagePoints, objectPoints)) return false; solvePnPRansac(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess, - iterationsCount, reprojectionError, minInliersCount, inliers, flags); + iterationsCount, reprojectionError, minInliersCount, inliers, flags); return true; } diff --git a/modules/cudaarithm/CMakeLists.txt b/modules/cudaarithm/CMakeLists.txt new file mode 100644 index 00000000000..d552bb4ebe9 --- /dev/null +++ b/modules/cudaarithm/CMakeLists.txt @@ -0,0 +1,27 @@ +if(IOS OR WINRT OR (NOT HAVE_CUDA AND NOT BUILD_CUDA_STUBS)) + ocv_module_disable(cudaarithm) +endif() + +set(the_description "CUDA-accelerated Operations on Matrices") + +ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4127 /wd4324 /wd4512 -Wundef -Wmissing-declarations -Wshadow) + +ocv_add_module(cudaarithm opencv_core OPTIONAL opencv_cudev WRAP python) + +ocv_module_include_directories() +ocv_glob_module_sources() + +set(extra_libs "") + +if(HAVE_CUBLAS) + list(APPEND extra_libs ${CUDA_cublas_LIBRARY}) +endif() + +if(HAVE_CUFFT) + list(APPEND extra_libs ${CUDA_cufft_LIBRARY}) +endif() + +ocv_create_module(${extra_libs}) + +ocv_add_accuracy_tests(DEPENDS_ON opencv_imgproc) +ocv_add_perf_tests(DEPENDS_ON opencv_imgproc) diff --git a/modules/cudaarithm/include/opencv2/cudaarithm.hpp b/modules/cudaarithm/include/opencv2/cudaarithm.hpp new file mode 100644 index 00000000000..4aa6582dcf1 --- /dev/null +++ b/modules/cudaarithm/include/opencv2/cudaarithm.hpp @@ -0,0 +1,886 @@ +/*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_CUDAARITHM_HPP +#define OPENCV_CUDAARITHM_HPP + +#ifndef __cplusplus +# error cudaarithm.hpp header must be compiled as C++ +#endif + +#include "opencv2/core/cuda.hpp" + +/** + @addtogroup cuda + @{ + @defgroup cudaarithm Operations on Matrices + @{ + @defgroup cudaarithm_core Core Operations on Matrices + @defgroup cudaarithm_elem Per-element Operations + @defgroup cudaarithm_reduce Matrix Reductions + @defgroup cudaarithm_arithm Arithm Operations on Matrices + @} + @} + */ + +namespace cv { namespace cuda { + +//! @addtogroup cudaarithm +//! @{ + +//! @addtogroup cudaarithm_elem +//! @{ + +/** @brief Computes a matrix-matrix or matrix-scalar sum. + +@param src1 First source matrix or scalar. +@param src2 Second source matrix or scalar. Matrix should have the same size and type as src1 . +@param dst Destination matrix that has the same size and number of channels as the input array(s). +The depth is defined by dtype or src1 depth. +@param mask Optional operation mask, 8-bit single channel array, that specifies elements of the +destination array to be changed. The mask can be used only with single channel images. +@param dtype Optional depth of the output array. +@param stream Stream for the asynchronous version. + +@sa add + */ +CV_EXPORTS_W void add(InputArray src1, InputArray src2, OutputArray dst, InputArray mask = noArray(), int dtype = -1, Stream& stream = Stream::Null()); + +/** @brief Computes a matrix-matrix or matrix-scalar difference. + +@param src1 First source matrix or scalar. +@param src2 Second source matrix or scalar. Matrix should have the same size and type as src1 . +@param dst Destination matrix that has the same size and number of channels as the input array(s). +The depth is defined by dtype or src1 depth. +@param mask Optional operation mask, 8-bit single channel array, that specifies elements of the +destination array to be changed. The mask can be used only with single channel images. +@param dtype Optional depth of the output array. +@param stream Stream for the asynchronous version. + +@sa subtract + */ +CV_EXPORTS_W void subtract(InputArray src1, InputArray src2, OutputArray dst, InputArray mask = noArray(), int dtype = -1, Stream& stream = Stream::Null()); + +/** @brief Computes a matrix-matrix or matrix-scalar per-element product. + +@param src1 First source matrix or scalar. +@param src2 Second source matrix or scalar. +@param dst Destination matrix that has the same size and number of channels as the input array(s). +The depth is defined by dtype or src1 depth. +@param scale Optional scale factor. +@param dtype Optional depth of the output array. +@param stream Stream for the asynchronous version. + +@sa multiply + */ +CV_EXPORTS_W void multiply(InputArray src1, InputArray src2, OutputArray dst, double scale = 1, int dtype = -1, Stream& stream = Stream::Null()); + +/** @brief Computes a matrix-matrix or matrix-scalar division. + +@param src1 First source matrix or a scalar. +@param src2 Second source matrix or scalar. +@param dst Destination matrix that has the same size and number of channels as the input array(s). +The depth is defined by dtype or src1 depth. +@param scale Optional scale factor. +@param dtype Optional depth of the output array. +@param stream Stream for the asynchronous version. + +This function, in contrast to divide, uses a round-down rounding mode. + +@sa divide + */ +CV_EXPORTS_W void divide(InputArray src1, InputArray src2, OutputArray dst, double scale = 1, int dtype = -1, Stream& stream = Stream::Null()); + +/** @brief Computes per-element absolute difference of two matrices (or of a matrix and scalar). + +@param src1 First source matrix or scalar. +@param src2 Second source matrix or scalar. +@param dst Destination matrix that has the same size and type as the input array(s). +@param stream Stream for the asynchronous version. + +@sa absdiff + */ +CV_EXPORTS_W void absdiff(InputArray src1, InputArray src2, OutputArray dst, Stream& stream = Stream::Null()); + +/** @brief Computes an absolute value of each matrix element. + +@param src Source matrix. +@param dst Destination matrix with the same size and type as src . +@param stream Stream for the asynchronous version. + +@sa abs + */ +CV_EXPORTS_W void abs(InputArray src, OutputArray dst, Stream& stream = Stream::Null()); + +/** @brief Computes a square value of each matrix element. + +@param src Source matrix. +@param dst Destination matrix with the same size and type as src . +@param stream Stream for the asynchronous version. + */ +CV_EXPORTS_W void sqr(InputArray src, OutputArray dst, Stream& stream = Stream::Null()); + +/** @brief Computes a square root of each matrix element. + +@param src Source matrix. +@param dst Destination matrix with the same size and type as src . +@param stream Stream for the asynchronous version. + +@sa sqrt + */ +CV_EXPORTS_W void sqrt(InputArray src, OutputArray dst, Stream& stream = Stream::Null()); + +/** @brief Computes an exponent of each matrix element. + +@param src Source matrix. +@param dst Destination matrix with the same size and type as src . +@param stream Stream for the asynchronous version. + +@sa exp + */ +CV_EXPORTS_W void exp(InputArray src, OutputArray dst, Stream& stream = Stream::Null()); + +/** @brief Computes a natural logarithm of absolute value of each matrix element. + +@param src Source matrix. +@param dst Destination matrix with the same size and type as src . +@param stream Stream for the asynchronous version. + +@sa log + */ +CV_EXPORTS_W void log(InputArray src, OutputArray dst, Stream& stream = Stream::Null()); + +/** @brief Raises every matrix element to a power. + +@param src Source matrix. +@param power Exponent of power. +@param dst Destination matrix with the same size and type as src . +@param stream Stream for the asynchronous version. + +The function pow raises every element of the input matrix to power : + +\f[\texttt{dst} (I) = \fork{\texttt{src}(I)^power}{if \texttt{power} is integer}{|\texttt{src}(I)|^power}{otherwise}\f] + +@sa pow + */ +CV_EXPORTS_W void pow(InputArray src, double power, OutputArray dst, Stream& stream = Stream::Null()); + +/** @brief Compares elements of two matrices (or of a matrix and scalar). + +@param src1 First source matrix or scalar. +@param src2 Second source matrix or scalar. +@param dst Destination matrix that has the same size as the input array(s) and type CV_8U. +@param cmpop Flag specifying the relation between the elements to be checked: +- **CMP_EQ:** a(.) == b(.) +- **CMP_GT:** a(.) \> b(.) +- **CMP_GE:** a(.) \>= b(.) +- **CMP_LT:** a(.) \< b(.) +- **CMP_LE:** a(.) \<= b(.) +- **CMP_NE:** a(.) != b(.) +@param stream Stream for the asynchronous version. + +@sa compare + */ +CV_EXPORTS_W void compare(InputArray src1, InputArray src2, OutputArray dst, int cmpop, Stream& stream = Stream::Null()); + +/** @brief Performs a per-element bitwise inversion. + +@param src Source matrix. +@param dst Destination matrix with the same size and type as src . +@param mask Optional operation mask, 8-bit single channel array, that specifies elements of the +destination array to be changed. The mask can be used only with single channel images. +@param stream Stream for the asynchronous version. + */ +CV_EXPORTS_W void bitwise_not(InputArray src, OutputArray dst, InputArray mask = noArray(), Stream& stream = Stream::Null()); + +/** @brief Performs a per-element bitwise disjunction of two matrices (or of matrix and scalar). + +@param src1 First source matrix or scalar. +@param src2 Second source matrix or scalar. +@param dst Destination matrix that has the same size and type as the input array(s). +@param mask Optional operation mask, 8-bit single channel array, that specifies elements of the +destination array to be changed. The mask can be used only with single channel images. +@param stream Stream for the asynchronous version. + */ +CV_EXPORTS_W void bitwise_or(InputArray src1, InputArray src2, OutputArray dst, InputArray mask = noArray(), Stream& stream = Stream::Null()); + +/** @brief Performs a per-element bitwise conjunction of two matrices (or of matrix and scalar). + +@param src1 First source matrix or scalar. +@param src2 Second source matrix or scalar. +@param dst Destination matrix that has the same size and type as the input array(s). +@param mask Optional operation mask, 8-bit single channel array, that specifies elements of the +destination array to be changed. The mask can be used only with single channel images. +@param stream Stream for the asynchronous version. + */ +CV_EXPORTS_W void bitwise_and(InputArray src1, InputArray src2, OutputArray dst, InputArray mask = noArray(), Stream& stream = Stream::Null()); + +/** @brief Performs a per-element bitwise exclusive or operation of two matrices (or of matrix and scalar). + +@param src1 First source matrix or scalar. +@param src2 Second source matrix or scalar. +@param dst Destination matrix that has the same size and type as the input array(s). +@param mask Optional operation mask, 8-bit single channel array, that specifies elements of the +destination array to be changed. The mask can be used only with single channel images. +@param stream Stream for the asynchronous version. + */ +CV_EXPORTS_W void bitwise_xor(InputArray src1, InputArray src2, OutputArray dst, InputArray mask = noArray(), Stream& stream = Stream::Null()); + +/** @brief Performs pixel by pixel right shift of an image by a constant value. + +@param src Source matrix. Supports 1, 3 and 4 channels images with integers elements. +@param val Constant values, one per channel. +@param dst Destination matrix with the same size and type as src . +@param stream Stream for the asynchronous version. + */ +CV_EXPORTS void rshift(InputArray src, Scalar_ val, OutputArray dst, Stream& stream = Stream::Null()); + +CV_WRAP inline void rshift(InputArray src, Scalar val, OutputArray dst, Stream& stream = Stream::Null()) { + rshift(src, Scalar_(val), dst, stream); +} + +/** @brief Performs pixel by pixel right left of an image by a constant value. + +@param src Source matrix. Supports 1, 3 and 4 channels images with CV_8U , CV_16U or CV_32S +depth. +@param val Constant values, one per channel. +@param dst Destination matrix with the same size and type as src . +@param stream Stream for the asynchronous version. + */ +CV_EXPORTS void lshift(InputArray src, Scalar_ val, OutputArray dst, Stream& stream = Stream::Null()); + +CV_WRAP inline void lshift(InputArray src, Scalar val, OutputArray dst, Stream& stream = Stream::Null()) { + lshift(src, Scalar_(val), dst, stream); +} + +/** @brief Computes the per-element minimum of two matrices (or a matrix and a scalar). + +@param src1 First source matrix or scalar. +@param src2 Second source matrix or scalar. +@param dst Destination matrix that has the same size and type as the input array(s). +@param stream Stream for the asynchronous version. + +@sa min + */ +CV_EXPORTS_W void min(InputArray src1, InputArray src2, OutputArray dst, Stream& stream = Stream::Null()); + +/** @brief Computes the per-element maximum of two matrices (or a matrix and a scalar). + +@param src1 First source matrix or scalar. +@param src2 Second source matrix or scalar. +@param dst Destination matrix that has the same size and type as the input array(s). +@param stream Stream for the asynchronous version. + +@sa max + */ +CV_EXPORTS_W void max(InputArray src1, InputArray src2, OutputArray dst, Stream& stream = Stream::Null()); + +/** @brief Computes the weighted sum of two arrays. + +@param src1 First source array. +@param alpha Weight for the first array elements. +@param src2 Second source array of the same size and channel number as src1 . +@param beta Weight for the second array elements. +@param dst Destination array that has the same size and number of channels as the input arrays. +@param gamma Scalar added to each sum. +@param dtype Optional depth of the destination array. When both input arrays have the same depth, +dtype can be set to -1, which will be equivalent to src1.depth(). +@param stream Stream for the asynchronous version. + +The function addWeighted calculates the weighted sum of two arrays as follows: + +\f[\texttt{dst} (I)= \texttt{saturate} ( \texttt{src1} (I)* \texttt{alpha} + \texttt{src2} (I)* \texttt{beta} + \texttt{gamma} )\f] + +where I is a multi-dimensional index of array elements. In case of multi-channel arrays, each +channel is processed independently. + +@sa addWeighted + */ +CV_EXPORTS_W void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, + int dtype = -1, Stream& stream = Stream::Null()); + +//! adds scaled array to another one (dst = alpha*src1 + src2) +static inline void scaleAdd(InputArray src1, double alpha, InputArray src2, OutputArray dst, Stream& stream = Stream::Null()) +{ + addWeighted(src1, alpha, src2, 1.0, 0.0, dst, -1, stream); +} + +/** @brief Applies a fixed-level threshold to each array element. + +@param src Source array (single-channel). +@param dst Destination array with the same size and type as src . +@param thresh Threshold value. +@param maxval Maximum value to use with THRESH_BINARY and THRESH_BINARY_INV threshold types. +@param type Threshold type. For details, see threshold . The THRESH_OTSU and THRESH_TRIANGLE +threshold types are not supported. +@param stream Stream for the asynchronous version. + +@sa threshold + */ +CV_EXPORTS_W double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type, Stream& stream = Stream::Null()); + +/** @brief Computes magnitudes of complex matrix elements. + +@param xy Source complex matrix in the interleaved format ( CV_32FC2 ). +@param magnitude Destination matrix of float magnitudes ( CV_32FC1 ). +@param stream Stream for the asynchronous version. + +@sa magnitude + */ +CV_EXPORTS_W void magnitude(InputArray xy, OutputArray magnitude, Stream& stream = Stream::Null()); + +/** @brief Computes squared magnitudes of complex matrix elements. + +@param xy Source complex matrix in the interleaved format ( CV_32FC2 ). +@param magnitude Destination matrix of float magnitude squares ( CV_32FC1 ). +@param stream Stream for the asynchronous version. + */ +CV_EXPORTS_W void magnitudeSqr(InputArray xy, OutputArray magnitude, Stream& stream = Stream::Null()); + +/** @overload + computes magnitude of each (x(i), y(i)) vector + supports only floating-point source +@param x Source matrix containing real components ( CV_32FC1 ). +@param y Source matrix containing imaginary components ( CV_32FC1 ). +@param magnitude Destination matrix of float magnitudes ( CV_32FC1 ). +@param stream Stream for the asynchronous version. + */ +CV_EXPORTS_W void magnitude(InputArray x, InputArray y, OutputArray magnitude, Stream& stream = Stream::Null()); + +/** @overload + computes squared magnitude of each (x(i), y(i)) vector + supports only floating-point source +@param x Source matrix containing real components ( CV_32FC1 ). +@param y Source matrix containing imaginary components ( CV_32FC1 ). +@param magnitude Destination matrix of float magnitude squares ( CV_32FC1 ). +@param stream Stream for the asynchronous version. +*/ +CV_EXPORTS_W void magnitudeSqr(InputArray x, InputArray y, OutputArray magnitude, Stream& stream = Stream::Null()); + +/** @brief Computes polar angles of complex matrix elements. + +@param x Source matrix containing real components ( CV_32FC1 ). +@param y Source matrix containing imaginary components ( CV_32FC1 ). +@param angle Destination matrix of angles ( CV_32FC1 ). +@param angleInDegrees Flag for angles that must be evaluated in degrees. +@param stream Stream for the asynchronous version. + +@sa phase + */ +CV_EXPORTS_W void phase(InputArray x, InputArray y, OutputArray angle, bool angleInDegrees = false, Stream& stream = Stream::Null()); + +/** @brief Converts Cartesian coordinates into polar. + +@param x Source matrix containing real components ( CV_32FC1 ). +@param y Source matrix containing imaginary components ( CV_32FC1 ). +@param magnitude Destination matrix of float magnitudes ( CV_32FC1 ). +@param angle Destination matrix of angles ( CV_32FC1 ). +@param angleInDegrees Flag for angles that must be evaluated in degrees. +@param stream Stream for the asynchronous version. + +@sa cartToPolar + */ +CV_EXPORTS_W void cartToPolar(InputArray x, InputArray y, OutputArray magnitude, OutputArray angle, bool angleInDegrees = false, Stream& stream = Stream::Null()); + +/** @brief Converts polar coordinates into Cartesian. + +@param magnitude Source matrix containing magnitudes ( CV_32FC1 or CV_64FC1 ). +@param angle Source matrix containing angles ( same type as magnitude ). +@param x Destination matrix of real components ( same type as magnitude ). +@param y Destination matrix of imaginary components ( same type as magnitude ). +@param angleInDegrees Flag that indicates angles in degrees. +@param stream Stream for the asynchronous version. + */ +CV_EXPORTS_W void polarToCart(InputArray magnitude, InputArray angle, OutputArray x, OutputArray y, bool angleInDegrees = false, Stream& stream = Stream::Null()); + +//! @} cudaarithm_elem + +//! @addtogroup cudaarithm_core +//! @{ + +/** @brief Makes a multi-channel matrix out of several single-channel matrices. + +@param src Array/vector of source matrices. +@param n Number of source matrices. +@param dst Destination matrix. +@param stream Stream for the asynchronous version. + +@sa merge + */ +CV_EXPORTS void merge(const GpuMat* src, size_t n, OutputArray dst, Stream& stream = Stream::Null()); +/** @overload */ +CV_EXPORTS_W void merge(const std::vector& src, OutputArray dst, Stream& stream = Stream::Null()); + +/** @brief Copies each plane of a multi-channel matrix into an array. + +@param src Source matrix. +@param dst Destination array/vector of single-channel matrices. +@param stream Stream for the asynchronous version. + +@sa split + */ +CV_EXPORTS void split(InputArray src, GpuMat* dst, Stream& stream = Stream::Null()); +/** @overload */ +CV_EXPORTS_W void split(InputArray src, CV_OUT std::vector& dst, Stream& stream = Stream::Null()); + +/** @brief Transposes a matrix. + +@param src1 Source matrix. 1-, 4-, 8-byte element sizes are supported for now. +@param dst Destination matrix. +@param stream Stream for the asynchronous version. + +@sa transpose + */ +CV_EXPORTS_W void transpose(InputArray src1, OutputArray dst, Stream& stream = Stream::Null()); + +/** @brief Flips a 2D matrix around vertical, horizontal, or both axes. + +@param src Source matrix. Supports 1, 3 and 4 channels images with CV_8U, CV_16U, CV_32S or +CV_32F depth. +@param dst Destination matrix. +@param flipCode Flip mode for the source: +- 0 Flips around x-axis. +- \> 0 Flips around y-axis. +- \< 0 Flips around both axes. +@param stream Stream for the asynchronous version. + +@sa flip + */ +CV_EXPORTS_W void flip(InputArray src, OutputArray dst, int flipCode, Stream& stream = Stream::Null()); + +/** @brief Base class for transform using lookup table. + */ +class CV_EXPORTS_W LookUpTable : public Algorithm +{ +public: + /** @brief Transforms the source matrix into the destination matrix using the given look-up table: + dst(I) = lut(src(I)) . + + @param src Source matrix. CV_8UC1 and CV_8UC3 matrices are supported for now. + @param dst Destination matrix. + @param stream Stream for the asynchronous version. + */ + CV_WRAP virtual void transform(InputArray src, OutputArray dst, Stream& stream = Stream::Null()) = 0; +}; + +/** @brief Creates implementation for cuda::LookUpTable . + +@param lut Look-up table of 256 elements. It is a continuous CV_8U matrix. + */ +CV_EXPORTS_W Ptr createLookUpTable(InputArray lut); + +/** @brief Forms a border around an image. + +@param src Source image. CV_8UC1 , CV_8UC4 , CV_32SC1 , and CV_32FC1 types are supported. +@param dst Destination image with the same type as src. The size is +Size(src.cols+left+right, src.rows+top+bottom) . +@param top Number of top pixels +@param bottom Number of bottom pixels +@param left Number of left pixels +@param right Number of pixels in each direction from the source image rectangle to extrapolate. +For example: top=1, bottom=1, left=1, right=1 mean that 1 pixel-wide border needs to be built. +@param borderType Border type. See borderInterpolate for details. BORDER_REFLECT101 , +BORDER_REPLICATE , BORDER_CONSTANT , BORDER_REFLECT and BORDER_WRAP are supported for now. +@param value Border value. +@param stream Stream for the asynchronous version. + */ +CV_EXPORTS_W void copyMakeBorder(InputArray src, OutputArray dst, int top, int bottom, int left, int right, int borderType, + Scalar value = Scalar(), Stream& stream = Stream::Null()); + +//! @} cudaarithm_core + +//! @addtogroup cudaarithm_reduce +//! @{ + +/** @brief Returns the norm of a matrix (or difference of two matrices). + +@param src1 Source matrix. Any matrices except 64F are supported. +@param normType Norm type. NORM_L1 , NORM_L2 , and NORM_INF are supported for now. +@param mask optional operation mask; it must have the same size as src1 and CV_8UC1 type. + +@sa norm + */ +CV_EXPORTS_W double norm(InputArray src1, int normType, InputArray mask = noArray()); +/** @overload */ +CV_EXPORTS_W void calcNorm(InputArray src, OutputArray dst, int normType, InputArray mask = noArray(), Stream& stream = Stream::Null()); + +/** @brief Returns the difference of two matrices. + +@param src1 Source matrix. Any matrices except 64F are supported. +@param src2 Second source matrix (if any) with the same size and type as src1. +@param normType Norm type. NORM_L1 , NORM_L2 , and NORM_INF are supported for now. + +@sa norm + */ +CV_EXPORTS_W double norm(InputArray src1, InputArray src2, int normType=NORM_L2); +/** @overload */ +CV_EXPORTS_W void calcNormDiff(InputArray src1, InputArray src2, OutputArray dst, int normType=NORM_L2, Stream& stream = Stream::Null()); + +/** @brief Returns the sum of matrix elements. + +@param src Source image of any depth except for CV_64F . +@param mask optional operation mask; it must have the same size as src1 and CV_8UC1 type. + +@sa sum + */ +CV_EXPORTS_W Scalar sum(InputArray src, InputArray mask = noArray()); +/** @overload */ +CV_EXPORTS_W void calcSum(InputArray src, OutputArray dst, InputArray mask = noArray(), Stream& stream = Stream::Null()); + +/** @brief Returns the sum of absolute values for matrix elements. + +@param src Source image of any depth except for CV_64F . +@param mask optional operation mask; it must have the same size as src1 and CV_8UC1 type. + */ +CV_EXPORTS_W Scalar absSum(InputArray src, InputArray mask = noArray()); +/** @overload */ +CV_EXPORTS_W void calcAbsSum(InputArray src, OutputArray dst, InputArray mask = noArray(), Stream& stream = Stream::Null()); + +/** @brief Returns the squared sum of matrix elements. + +@param src Source image of any depth except for CV_64F . +@param mask optional operation mask; it must have the same size as src1 and CV_8UC1 type. + */ +CV_EXPORTS_W Scalar sqrSum(InputArray src, InputArray mask = noArray()); +/** @overload */ +CV_EXPORTS_W void calcSqrSum(InputArray src, OutputArray dst, InputArray mask = noArray(), Stream& stream = Stream::Null()); + +/** @brief Finds global minimum and maximum matrix elements and returns their values. + +@param src Single-channel source image. +@param minVal Pointer to the returned minimum value. Use NULL if not required. +@param maxVal Pointer to the returned maximum value. Use NULL if not required. +@param mask Optional mask to select a sub-matrix. + +The function does not work with CV_64F images on GPUs with the compute capability \< 1.3. + +@sa minMaxLoc + */ +CV_EXPORTS_W void minMax(InputArray src, double* minVal, double* maxVal, InputArray mask = noArray()); +/** @overload */ +CV_EXPORTS_W void findMinMax(InputArray src, OutputArray dst, InputArray mask = noArray(), Stream& stream = Stream::Null()); + +/** @brief Finds global minimum and maximum matrix elements and returns their values with locations. + +@param src Single-channel source image. +@param minVal Pointer to the returned minimum value. Use NULL if not required. +@param maxVal Pointer to the returned maximum value. Use NULL if not required. +@param minLoc Pointer to the returned minimum location. Use NULL if not required. +@param maxLoc Pointer to the returned maximum location. Use NULL if not required. +@param mask Optional mask to select a sub-matrix. + +The function does not work with CV_64F images on GPU with the compute capability \< 1.3. + +@sa minMaxLoc + */ +CV_EXPORTS_W void minMaxLoc(InputArray src, double* minVal, double* maxVal, Point* minLoc, Point* maxLoc, + InputArray mask = noArray()); +/** @overload */ +CV_EXPORTS_W void findMinMaxLoc(InputArray src, OutputArray minMaxVals, OutputArray loc, + InputArray mask = noArray(), Stream& stream = Stream::Null()); + +/** @brief Counts non-zero matrix elements. + +@param src Single-channel source image. + +The function does not work with CV_64F images on GPUs with the compute capability \< 1.3. + +@sa countNonZero + */ +CV_EXPORTS_W int countNonZero(InputArray src); +/** @overload */ +CV_EXPORTS_W void countNonZero(InputArray src, OutputArray dst, Stream& stream = Stream::Null()); + +/** @brief Reduces a matrix to a vector. + +@param mtx Source 2D matrix. +@param vec Destination vector. Its size and type is defined by dim and dtype parameters. +@param dim Dimension index along which the matrix is reduced. 0 means that the matrix is reduced +to a single row. 1 means that the matrix is reduced to a single column. +@param reduceOp Reduction operation that could be one of the following: +- **CV_REDUCE_SUM** The output is the sum of all rows/columns of the matrix. +- **CV_REDUCE_AVG** The output is the mean vector of all rows/columns of the matrix. +- **CV_REDUCE_MAX** The output is the maximum (column/row-wise) of all rows/columns of the +matrix. +- **CV_REDUCE_MIN** The output is the minimum (column/row-wise) of all rows/columns of the +matrix. +@param dtype When it is negative, the destination vector will have the same type as the source +matrix. Otherwise, its type will be CV_MAKE_TYPE(CV_MAT_DEPTH(dtype), mtx.channels()) . +@param stream Stream for the asynchronous version. + +The function reduce reduces the matrix to a vector by treating the matrix rows/columns as a set of +1D vectors and performing the specified operation on the vectors until a single row/column is +obtained. For example, the function can be used to compute horizontal and vertical projections of a +raster image. In case of CV_REDUCE_SUM and CV_REDUCE_AVG , the output may have a larger element +bit-depth to preserve accuracy. And multi-channel arrays are also supported in these two reduction +modes. + +@sa reduce + */ +CV_EXPORTS_W void reduce(InputArray mtx, OutputArray vec, int dim, int reduceOp, int dtype = -1, Stream& stream = Stream::Null()); + +/** @brief Computes a mean value and a standard deviation of matrix elements. + +@param mtx Source matrix. CV_8UC1 matrices are supported for now. +@param mean Mean value. +@param stddev Standard deviation value. + +@sa meanStdDev + */ +CV_EXPORTS_W void meanStdDev(InputArray mtx, Scalar& mean, Scalar& stddev); +/** @overload */ +CV_EXPORTS_W void meanStdDev(InputArray mtx, OutputArray dst, Stream& stream = Stream::Null()); + +/** @brief Computes a standard deviation of integral images. + +@param src Source image. Only the CV_32SC1 type is supported. +@param sqr Squared source image. Only the CV_32FC1 type is supported. +@param dst Destination image with the same type and size as src . +@param rect Rectangular window. +@param stream Stream for the asynchronous version. + */ +CV_EXPORTS_W void rectStdDev(InputArray src, InputArray sqr, OutputArray dst, Rect rect, Stream& stream = Stream::Null()); + +/** @brief Normalizes the norm or value range of an array. + +@param src Input array. +@param dst Output array of the same size as src . +@param alpha Norm value to normalize to or the lower range boundary in case of the range +normalization. +@param beta Upper range boundary in case of the range normalization; it is not used for the norm +normalization. +@param norm_type Normalization type ( NORM_MINMAX , NORM_L2 , NORM_L1 or NORM_INF ). +@param dtype When negative, the output array has the same type as src; otherwise, it has the same +number of channels as src and the depth =CV_MAT_DEPTH(dtype). +@param mask Optional operation mask. +@param stream Stream for the asynchronous version. + +@sa normalize + */ +CV_EXPORTS_W void normalize(InputArray src, OutputArray dst, double alpha, double beta, + int norm_type, int dtype, InputArray mask = noArray(), + Stream& stream = Stream::Null()); + +/** @brief Computes an integral image. + +@param src Source image. Only CV_8UC1 images are supported for now. +@param sum Integral image containing 32-bit unsigned integer values packed into CV_32SC1 . +@param stream Stream for the asynchronous version. + +@sa integral + */ +CV_EXPORTS_W void integral(InputArray src, OutputArray sum, Stream& stream = Stream::Null()); + +/** @brief Computes a squared integral image. + +@param src Source image. Only CV_8UC1 images are supported for now. +@param sqsum Squared integral image containing 64-bit unsigned integer values packed into +CV_64FC1 . +@param stream Stream for the asynchronous version. + */ +CV_EXPORTS_W void sqrIntegral(InputArray src, OutputArray sqsum, Stream& stream = Stream::Null()); + +//! @} cudaarithm_reduce + +//! @addtogroup cudaarithm_arithm +//! @{ + +/** @brief Performs generalized matrix multiplication. + +@param src1 First multiplied input matrix that should have CV_32FC1 , CV_64FC1 , CV_32FC2 , or +CV_64FC2 type. +@param src2 Second multiplied input matrix of the same type as src1 . +@param alpha Weight of the matrix product. +@param src3 Third optional delta matrix added to the matrix product. It should have the same type +as src1 and src2 . +@param beta Weight of src3 . +@param dst Destination matrix. It has the proper size and the same type as input matrices. +@param flags Operation flags: +- **GEMM_1_T** transpose src1 +- **GEMM_2_T** transpose src2 +- **GEMM_3_T** transpose src3 +@param stream Stream for the asynchronous version. + +The function performs generalized matrix multiplication similar to the gemm functions in BLAS level +3. For example, gemm(src1, src2, alpha, src3, beta, dst, GEMM_1_T + GEMM_3_T) corresponds to + +\f[\texttt{dst} = \texttt{alpha} \cdot \texttt{src1} ^T \cdot \texttt{src2} + \texttt{beta} \cdot \texttt{src3} ^T\f] + +@note Transposition operation doesn't support CV_64FC2 input type. + +@sa gemm + */ +CV_EXPORTS_W void gemm(InputArray src1, InputArray src2, double alpha, + InputArray src3, double beta, OutputArray dst, int flags = 0, Stream& stream = Stream::Null()); + +/** @brief Performs a per-element multiplication of two Fourier spectrums. + +@param src1 First spectrum. +@param src2 Second spectrum with the same size and type as a . +@param dst Destination spectrum. +@param flags Mock parameter used for CPU/CUDA interfaces similarity. +@param conjB Optional flag to specify if the second spectrum needs to be conjugated before the +multiplication. +@param stream Stream for the asynchronous version. + +Only full (not packed) CV_32FC2 complex spectrums in the interleaved format are supported for now. + +@sa mulSpectrums + */ +CV_EXPORTS_W void mulSpectrums(InputArray src1, InputArray src2, OutputArray dst, int flags, bool conjB=false, Stream& stream = Stream::Null()); + +/** @brief Performs a per-element multiplication of two Fourier spectrums and scales the result. + +@param src1 First spectrum. +@param src2 Second spectrum with the same size and type as a . +@param dst Destination spectrum. +@param flags Mock parameter used for CPU/CUDA interfaces similarity, simply add a `0` value. +@param scale Scale constant. +@param conjB Optional flag to specify if the second spectrum needs to be conjugated before the +multiplication. +@param stream Stream for the asynchronous version. + +Only full (not packed) CV_32FC2 complex spectrums in the interleaved format are supported for now. + +@sa mulSpectrums + */ +CV_EXPORTS_W void mulAndScaleSpectrums(InputArray src1, InputArray src2, OutputArray dst, int flags, float scale, bool conjB=false, Stream& stream = Stream::Null()); + +/** @brief Performs a forward or inverse discrete Fourier transform (1D or 2D) of the floating point matrix. + +@param src Source matrix (real or complex). +@param dst Destination matrix (real or complex). +@param dft_size Size of a discrete Fourier transform. +@param flags Optional flags: +- **DFT_ROWS** transforms each individual row of the source matrix. +- **DFT_SCALE** scales the result: divide it by the number of elements in the transform +(obtained from dft_size ). +- **DFT_INVERSE** inverts DFT. Use for complex-complex cases (real-complex and complex-real +cases are always forward and inverse, respectively). +- **DFT_COMPLEX_INPUT** Specifies that input is complex input with 2 channels. +- **DFT_REAL_OUTPUT** specifies the output as real. The source matrix is the result of +real-complex transform, so the destination matrix must be real. +@param stream Stream for the asynchronous version. + +Use to handle real matrices ( CV32FC1 ) and complex matrices in the interleaved format ( CV32FC2 ). + +The source matrix should be continuous, otherwise reallocation and data copying is performed. The +function chooses an operation mode depending on the flags, size, and channel count of the source +matrix: + +- If the source matrix is complex and the output is not specified as real, the destination +matrix is complex and has the dft_size size and CV_32FC2 type. The destination matrix +contains a full result of the DFT (forward or inverse). +- If the source matrix is complex and the output is specified as real, the function assumes that +its input is the result of the forward transform (see the next item). The destination matrix +has the dft_size size and CV_32FC1 type. It contains the result of the inverse DFT. +- If the source matrix is real (its type is CV_32FC1 ), forward DFT is performed. The result of +the DFT is packed into complex ( CV_32FC2 ) matrix. So, the width of the destination matrix +is dft_size.width / 2 + 1 . But if the source is a single column, the height is reduced +instead of the width. + +@sa dft + */ +CV_EXPORTS_W void dft(InputArray src, OutputArray dst, Size dft_size, int flags=0, Stream& stream = Stream::Null()); + +/** @brief Base class for DFT operator as a cv::Algorithm. : + */ +class CV_EXPORTS_W DFT : public Algorithm +{ +public: + /** @brief Computes an FFT of a given image. + + @param image Source image. Only CV_32FC1 images are supported for now. + @param result Result image. + @param stream Stream for the asynchronous version. + */ + CV_WRAP virtual void compute(InputArray image, OutputArray result, Stream& stream = Stream::Null()) = 0; +}; + +/** @brief Creates implementation for cuda::DFT. + +@param dft_size The image size. +@param flags Optional flags: +- **DFT_ROWS** transforms each individual row of the source matrix. +- **DFT_SCALE** scales the result: divide it by the number of elements in the transform +(obtained from dft_size ). +- **DFT_INVERSE** inverts DFT. Use for complex-complex cases (real-complex and complex-real +cases are always forward and inverse, respectively). +- **DFT_COMPLEX_INPUT** Specifies that inputs will be complex with 2 channels. +- **DFT_REAL_OUTPUT** specifies the output as real. The source matrix is the result of +real-complex transform, so the destination matrix must be real. + */ +CV_EXPORTS_W Ptr createDFT(Size dft_size, int flags); + +/** @brief Base class for convolution (or cross-correlation) operator. : + */ +class CV_EXPORTS_W Convolution : public Algorithm +{ +public: + /** @brief Computes a convolution (or cross-correlation) of two images. + + @param image Source image. Only CV_32FC1 images are supported for now. + @param templ Template image. The size is not greater than the image size. The type is the same as + image . + @param result Result image. If image is *W x H* and templ is *w x h*, then result must be *W-w+1 x + H-h+1*. + @param ccorr Flags to evaluate cross-correlation instead of convolution. + @param stream Stream for the asynchronous version. + */ + CV_WRAP virtual void convolve(InputArray image, InputArray templ, OutputArray result, bool ccorr = false, Stream& stream = Stream::Null()) = 0; +}; + +/** @brief Creates implementation for cuda::Convolution . + +@param user_block_size Block size. If you leave default value Size(0,0) then automatic +estimation of block size will be used (which is optimized for speed). By varying user_block_size +you can reduce memory requirements at the cost of speed. + */ +CV_EXPORTS_W Ptr createConvolution(Size user_block_size = Size()); + +//! @} cudaarithm_arithm + +//! @} cudaarithm + +}} // namespace cv { namespace cuda { + +#endif /* OPENCV_CUDAARITHM_HPP */ diff --git a/modules/cudaarithm/misc/python/test/test_cudaarithm.py b/modules/cudaarithm/misc/python/test/test_cudaarithm.py new file mode 100644 index 00000000000..b068fae44bf --- /dev/null +++ b/modules/cudaarithm/misc/python/test/test_cudaarithm.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python +import os +import cv2 as cv +import numpy as np + +from tests_common import NewOpenCVTests, unittest + +class cudaarithm_test(NewOpenCVTests): + def setUp(self): + super(cudaarithm_test, self).setUp() + if not cv.cuda.getCudaEnabledDeviceCount(): + self.skipTest("No CUDA-capable device is detected") + + def test_cudaarithm(self): + npMat = (np.random.random((128, 128, 3)) * 255).astype(np.uint8) + + cuMat = cv.cuda_GpuMat(npMat) + cuMatDst = cv.cuda_GpuMat(cuMat.size(),cuMat.type()) + cuMatB = cv.cuda_GpuMat(cuMat.size(),cv.CV_8UC1) + cuMatG = cv.cuda_GpuMat(cuMat.size(),cv.CV_8UC1) + cuMatR = cv.cuda_GpuMat(cuMat.size(),cv.CV_8UC1) + + self.assertTrue(np.allclose(cv.cuda.merge(cv.cuda.split(cuMat)),npMat)) + + cv.cuda.split(cuMat,[cuMatB,cuMatG,cuMatR]) + cv.cuda.merge([cuMatB,cuMatG,cuMatR],cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),npMat)) + + shift = (np.random.random((cuMat.channels(),)) * 8).astype(np.uint8).tolist() + self.assertTrue(np.allclose(cv.cuda.rshift(cuMat,shift).download(),npMat >> shift)) + cv.cuda.rshift(cuMat,shift,cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),npMat >> shift)) + + self.assertTrue(np.allclose(cv.cuda.lshift(cuMat,shift).download(),(npMat << shift).astype('uint8'))) + cv.cuda.lshift(cuMat,shift,cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),(npMat << shift).astype('uint8'))) + + def test_arithmetic(self): + npMat1 = np.random.random((128, 128, 3)) - 0.5 + npMat2 = np.random.random((128, 128, 3)) - 0.5 + + cuMat1 = cv.cuda_GpuMat() + cuMat2 = cv.cuda_GpuMat() + cuMat1.upload(npMat1) + cuMat2.upload(npMat2) + cuMatDst = cv.cuda_GpuMat(cuMat1.size(),cuMat1.type()) + + self.assertTrue(np.allclose(cv.cuda.add(cuMat1, cuMat2).download(), + cv.add(npMat1, npMat2))) + + cv.cuda.add(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.add(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.subtract(cuMat1, cuMat2).download(), + cv.subtract(npMat1, npMat2))) + + cv.cuda.subtract(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.subtract(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.multiply(cuMat1, cuMat2).download(), + cv.multiply(npMat1, npMat2))) + + cv.cuda.multiply(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.multiply(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.divide(cuMat1, cuMat2).download(), + cv.divide(npMat1, npMat2))) + + cv.cuda.divide(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.divide(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.absdiff(cuMat1, cuMat2).download(), + cv.absdiff(npMat1, npMat2))) + + cv.cuda.absdiff(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.absdiff(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.compare(cuMat1, cuMat2, cv.CMP_GE).download(), + cv.compare(npMat1, npMat2, cv.CMP_GE))) + + cuMatDst1 = cv.cuda_GpuMat(cuMat1.size(),cv.CV_8UC3) + cv.cuda.compare(cuMat1, cuMat2, cv.CMP_GE, cuMatDst1) + self.assertTrue(np.allclose(cuMatDst1.download(),cv.compare(npMat1, npMat2, cv.CMP_GE))) + + self.assertTrue(np.allclose(cv.cuda.abs(cuMat1).download(), + np.abs(npMat1))) + + cv.cuda.abs(cuMat1, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),np.abs(npMat1))) + + self.assertTrue(np.allclose(cv.cuda.sqrt(cv.cuda.sqr(cuMat1)).download(), + cv.cuda.abs(cuMat1).download())) + + cv.cuda.sqr(cuMat1, cuMatDst) + cv.cuda.sqrt(cuMatDst, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.cuda.abs(cuMat1).download())) + + self.assertTrue(np.allclose(cv.cuda.log(cv.cuda.exp(cuMat1)).download(), + npMat1)) + + cv.cuda.exp(cuMat1, cuMatDst) + cv.cuda.log(cuMatDst, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),npMat1)) + + self.assertTrue(np.allclose(cv.cuda.pow(cuMat1, 2).download(), + cv.pow(npMat1, 2))) + + cv.cuda.pow(cuMat1, 2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.pow(npMat1, 2))) + + def test_logical(self): + npMat1 = (np.random.random((128, 128)) * 255).astype(np.uint8) + npMat2 = (np.random.random((128, 128)) * 255).astype(np.uint8) + + cuMat1 = cv.cuda_GpuMat() + cuMat2 = cv.cuda_GpuMat() + cuMat1.upload(npMat1) + cuMat2.upload(npMat2) + cuMatDst = cv.cuda_GpuMat(cuMat1.size(),cuMat1.type()) + + self.assertTrue(np.allclose(cv.cuda.bitwise_or(cuMat1, cuMat2).download(), + cv.bitwise_or(npMat1, npMat2))) + + cv.cuda.bitwise_or(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.bitwise_or(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.bitwise_and(cuMat1, cuMat2).download(), + cv.bitwise_and(npMat1, npMat2))) + + cv.cuda.bitwise_and(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.bitwise_and(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.bitwise_xor(cuMat1, cuMat2).download(), + cv.bitwise_xor(npMat1, npMat2))) + + cv.cuda.bitwise_xor(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.bitwise_xor(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.bitwise_not(cuMat1).download(), + cv.bitwise_not(npMat1))) + + cv.cuda.bitwise_not(cuMat1, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.bitwise_not(npMat1))) + + self.assertTrue(np.allclose(cv.cuda.min(cuMat1, cuMat2).download(), + cv.min(npMat1, npMat2))) + + cv.cuda.min(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.min(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.max(cuMat1, cuMat2).download(), + cv.max(npMat1, npMat2))) + + cv.cuda.max(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.max(npMat1, npMat2))) + + def test_convolution(self): + npMat = (np.random.random((128, 128)) * 255).astype(np.float32) + npDims = np.array(npMat.shape) + kernel = (np.random.random((3, 3)) * 1).astype(np.float32) + kernelDims = np.array(kernel.shape) + iS = (kernelDims/2).astype(int) + iE = npDims - kernelDims + iS + + cuMat = cv.cuda_GpuMat(npMat) + cuKernel= cv.cuda_GpuMat(kernel) + cuMatDst = cv.cuda_GpuMat(tuple(npDims - kernelDims + 1), cuMat.type()) + conv = cv.cuda.createConvolution() + + self.assertTrue(np.allclose(conv.convolve(cuMat,cuKernel,ccorr=True).download(), + cv.filter2D(npMat,-1,kernel,anchor=(-1,-1))[iS[0]:iE[0]+1,iS[1]:iE[1]+1])) + + conv.convolve(cuMat,cuKernel,cuMatDst,True) + self.assertTrue(np.allclose(cuMatDst.download(), + cv.filter2D(npMat,-1,kernel,anchor=(-1,-1))[iS[0]:iE[0]+1,iS[1]:iE[1]+1])) + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() \ No newline at end of file diff --git a/modules/cudaarithm/perf/perf_arithm.cpp b/modules/cudaarithm/perf/perf_arithm.cpp new file mode 100644 index 00000000000..ca23e19dc14 --- /dev/null +++ b/modules/cudaarithm/perf/perf_arithm.cpp @@ -0,0 +1,254 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "perf_precomp.hpp" + +namespace opencv_test { namespace { + +////////////////////////////////////////////////////////////////////// +// GEMM + +#ifdef HAVE_CUBLAS + +CV_FLAGS(GemmFlags, 0, cv::GEMM_1_T, cv::GEMM_2_T, cv::GEMM_3_T) +#define ALL_GEMM_FLAGS Values(GemmFlags(0), GemmFlags(cv::GEMM_1_T), GemmFlags(cv::GEMM_2_T), GemmFlags(cv::GEMM_3_T), \ + GemmFlags(cv::GEMM_1_T | cv::GEMM_2_T), GemmFlags(cv::GEMM_1_T | cv::GEMM_3_T), GemmFlags(cv::GEMM_1_T | cv::GEMM_2_T | cv::GEMM_3_T)) + +DEF_PARAM_TEST(Sz_Type_Flags, cv::Size, MatType, GemmFlags); + +PERF_TEST_P(Sz_Type_Flags, GEMM, + Combine(Values(cv::Size(512, 512), cv::Size(1024, 1024)), + Values(CV_32FC1, CV_32FC2, CV_64FC1), + ALL_GEMM_FLAGS)) +{ + const cv::Size size = GET_PARAM(0); + const int type = GET_PARAM(1); + const int flags = GET_PARAM(2); + + cv::Mat src1(size, type); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, type); + declare.in(src2, WARMUP_RNG); + + cv::Mat src3(size, type); + declare.in(src3, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + declare.time(5.0); + + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + const cv::cuda::GpuMat d_src3(src3); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::gemm(d_src1, d_src2, 1.0, d_src3, 1.0, dst, flags); + + CUDA_SANITY_CHECK(dst, 1e-6, ERROR_RELATIVE); + } + else + { + declare.time(50.0); + + cv::Mat dst; + + TEST_CYCLE() cv::gemm(src1, src2, 1.0, src3, 1.0, dst, flags); + + CPU_SANITY_CHECK(dst); + } +} + +#endif + +////////////////////////////////////////////////////////////////////// +// MulSpectrums + +CV_FLAGS(DftFlags, 0, cv::DFT_INVERSE, cv::DFT_SCALE, cv::DFT_ROWS, cv::DFT_COMPLEX_OUTPUT, cv::DFT_REAL_OUTPUT) + +DEF_PARAM_TEST(Sz_Flags, cv::Size, DftFlags); + +PERF_TEST_P(Sz_Flags, MulSpectrums, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(0, DftFlags(cv::DFT_ROWS)))) +{ + const cv::Size size = GET_PARAM(0); + const int flag = GET_PARAM(1); + + cv::Mat a(size, CV_32FC2); + cv::Mat b(size, CV_32FC2); + declare.in(a, b, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_a(a); + const cv::cuda::GpuMat d_b(b); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::mulSpectrums(d_a, d_b, dst, flag); + + CUDA_SANITY_CHECK(dst, 1e-6, ERROR_RELATIVE); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::mulSpectrums(a, b, dst, flag); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// MulAndScaleSpectrums + +PERF_TEST_P(Sz, MulAndScaleSpectrums, + CUDA_TYPICAL_MAT_SIZES) +{ + const cv::Size size = GetParam(); + + const float scale = 1.f / size.area(); + + cv::Mat src1(size, CV_32FC2); + cv::Mat src2(size, CV_32FC2); + declare.in(src1,src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::mulAndScaleSpectrums(d_src1, d_src2, dst, cv::DFT_ROWS, scale, false); + + CUDA_SANITY_CHECK(dst, 1e-6, ERROR_RELATIVE); + } + else + { + FAIL_NO_CPU(); + } +} + +////////////////////////////////////////////////////////////////////// +// Dft + +PERF_TEST_P(Sz_Flags, Dft, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(0, DftFlags(cv::DFT_ROWS), DftFlags(cv::DFT_INVERSE)))) +{ + declare.time(10.0); + + const cv::Size size = GET_PARAM(0); + const int flag = GET_PARAM(1); + + cv::Mat src(size, CV_32FC2); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::dft(d_src, dst, size, flag); + + CUDA_SANITY_CHECK(dst, 1e-6, ERROR_RELATIVE); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::dft(src, dst, flag); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// Convolve + +DEF_PARAM_TEST(Sz_KernelSz_Ccorr, cv::Size, int, bool); + +PERF_TEST_P(Sz_KernelSz_Ccorr, Convolve, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(17, 27, 32, 64), + Bool())) +{ + declare.time(10.0); + + const cv::Size size = GET_PARAM(0); + const int templ_size = GET_PARAM(1); + const bool ccorr = GET_PARAM(2); + + const cv::Mat image(size, CV_32FC1); + const cv::Mat templ(templ_size, templ_size, CV_32FC1); + declare.in(image, templ, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + cv::cuda::GpuMat d_image = cv::cuda::createContinuous(size, CV_32FC1); + d_image.upload(image); + + cv::cuda::GpuMat d_templ = cv::cuda::createContinuous(templ_size, templ_size, CV_32FC1); + d_templ.upload(templ); + + cv::Ptr convolution = cv::cuda::createConvolution(); + + cv::cuda::GpuMat dst; + + TEST_CYCLE() convolution->convolve(d_image, d_templ, dst, ccorr); + + CUDA_SANITY_CHECK(dst, 1e-6, ERROR_RELATIVE); + } + else + { + if (ccorr) + FAIL_NO_CPU(); + + cv::Mat dst; + + TEST_CYCLE() cv::filter2D(image, dst, image.depth(), templ); + + CPU_SANITY_CHECK(dst); + } +} + +}} // namespace diff --git a/modules/cudaarithm/perf/perf_core.cpp b/modules/cudaarithm/perf/perf_core.cpp new file mode 100644 index 00000000000..bc9f0e2f715 --- /dev/null +++ b/modules/cudaarithm/perf/perf_core.cpp @@ -0,0 +1,323 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "perf_precomp.hpp" + +namespace opencv_test { namespace { + +#define ARITHM_MAT_DEPTH Values(CV_8U, CV_16U, CV_32F, CV_64F) + +////////////////////////////////////////////////////////////////////// +// Merge + +DEF_PARAM_TEST(Sz_Depth_Cn, cv::Size, MatDepth, MatCn); + +PERF_TEST_P(Sz_Depth_Cn, Merge, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH, + Values(2, 3, 4))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); + + std::vector src(channels); + for (int i = 0; i < channels; ++i) + { + src[i].create(size, depth); + declare.in(src[i], WARMUP_RNG); + } + + if (PERF_RUN_CUDA()) + { + std::vector d_src(channels); + for (int i = 0; i < channels; ++i) + d_src[i].upload(src[i]); + + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::merge(d_src, dst); + + CUDA_SANITY_CHECK(dst, 1e-10); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::merge(src, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// Split + +PERF_TEST_P(Sz_Depth_Cn, Split, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH, + Values(2, 3, 4))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); + + cv::Mat src(size, CV_MAKE_TYPE(depth, channels)); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + std::vector dst; + + TEST_CYCLE() cv::cuda::split(d_src, dst); + + const cv::cuda::GpuMat& dst0 = dst[0]; + const cv::cuda::GpuMat& dst1 = dst[1]; + + CUDA_SANITY_CHECK(dst0, 1e-10); + CUDA_SANITY_CHECK(dst1, 1e-10); + } + else + { + std::vector dst; + + TEST_CYCLE() cv::split(src, dst); + + const cv::Mat& dst0 = dst[0]; + const cv::Mat& dst1 = dst[1]; + + CPU_SANITY_CHECK(dst0); + CPU_SANITY_CHECK(dst1); + } +} + +////////////////////////////////////////////////////////////////////// +// Transpose + +PERF_TEST_P(Sz_Type, Transpose, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8UC1, CV_8UC4, CV_16UC2, CV_16SC2, CV_32SC1, CV_32SC2, CV_64FC1))) +{ + const cv::Size size = GET_PARAM(0); + const int type = GET_PARAM(1); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::transpose(d_src, dst); + + CUDA_SANITY_CHECK(dst, 1e-10); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::transpose(src, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// Flip + +enum {FLIP_BOTH = 0, FLIP_X = 1, FLIP_Y = -1}; +CV_ENUM(FlipCode, FLIP_BOTH, FLIP_X, FLIP_Y) + +DEF_PARAM_TEST(Sz_Depth_Cn_Code, cv::Size, MatDepth, MatCn, FlipCode); + +PERF_TEST_P(Sz_Depth_Cn_Code, Flip, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + CUDA_CHANNELS_1_3_4, + FlipCode::all())) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); + const int flipCode = GET_PARAM(3); + + const int type = CV_MAKE_TYPE(depth, channels); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::flip(d_src, dst, flipCode); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::flip(src, dst, flipCode); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// LutOneChannel + +PERF_TEST_P(Sz_Type, LutOneChannel, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8UC1, CV_8UC3))) +{ + const cv::Size size = GET_PARAM(0); + const int type = GET_PARAM(1); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + cv::Mat lut(1, 256, CV_8UC1); + declare.in(lut, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + cv::Ptr lutAlg = cv::cuda::createLookUpTable(lut); + + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() lutAlg->transform(d_src, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::LUT(src, lut, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// LutMultiChannel + +PERF_TEST_P(Sz_Type, LutMultiChannel, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8UC3))) +{ + const cv::Size size = GET_PARAM(0); + const int type = GET_PARAM(1); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + cv::Mat lut(1, 256, CV_MAKE_TYPE(CV_8U, src.channels())); + declare.in(lut, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + cv::Ptr lutAlg = cv::cuda::createLookUpTable(lut); + + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() lutAlg->transform(d_src, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::LUT(src, lut, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// CopyMakeBorder + +DEF_PARAM_TEST(Sz_Depth_Cn_Border, cv::Size, MatDepth, MatCn, BorderMode); + +PERF_TEST_P(Sz_Depth_Cn_Border, CopyMakeBorder, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + CUDA_CHANNELS_1_3_4, + ALL_BORDER_MODES)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); + const int borderMode = GET_PARAM(3); + + const int type = CV_MAKE_TYPE(depth, channels); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::copyMakeBorder(d_src, dst, 5, 5, 5, 5, borderMode); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::copyMakeBorder(src, dst, 5, 5, 5, 5, borderMode); + + CPU_SANITY_CHECK(dst); + } +} + +}} // namespace diff --git a/modules/cudaarithm/perf/perf_element_operations.cpp b/modules/cudaarithm/perf/perf_element_operations.cpp new file mode 100644 index 00000000000..9aa2d4e4e0f --- /dev/null +++ b/modules/cudaarithm/perf/perf_element_operations.cpp @@ -0,0 +1,1504 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "perf_precomp.hpp" + +namespace opencv_test { namespace { + +#define ARITHM_MAT_DEPTH Values(CV_8U, CV_16U, CV_32F, CV_64F) + +////////////////////////////////////////////////////////////////////// +// AddMat + +DEF_PARAM_TEST(Sz_Depth, cv::Size, MatDepth); + +PERF_TEST_P(Sz_Depth, AddMat, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src1(size, depth); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, depth); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::add(d_src1, d_src2, dst); + + CUDA_SANITY_CHECK(dst, 1e-10); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::add(src1, src2, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// AddScalar + +PERF_TEST_P(Sz_Depth, AddScalar, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + cv::Scalar s; + declare.in(s, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::add(d_src, s, dst); + + CUDA_SANITY_CHECK(dst, 1e-10); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::add(src, s, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// SubtractMat + +PERF_TEST_P(Sz_Depth, SubtractMat, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src1(size, depth); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, depth); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::subtract(d_src1, d_src2, dst); + + CUDA_SANITY_CHECK(dst, 1e-10); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::subtract(src1, src2, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// SubtractScalar + +PERF_TEST_P(Sz_Depth, SubtractScalar, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + cv::Scalar s; + declare.in(s, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::subtract(d_src, s, dst); + + CUDA_SANITY_CHECK(dst, 1e-10); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::subtract(src, s, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// MultiplyMat + +PERF_TEST_P(Sz_Depth, MultiplyMat, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src1(size, depth); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, depth); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::multiply(d_src1, d_src2, dst); + + CUDA_SANITY_CHECK(dst, 1e-6); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::multiply(src1, src2, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// MultiplyScalar + +PERF_TEST_P(Sz_Depth, MultiplyScalar, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + cv::Scalar s; + declare.in(s, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::multiply(d_src, s, dst); + + CUDA_SANITY_CHECK(dst, 1e-6); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::multiply(src, s, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// DivideMat + +PERF_TEST_P(Sz_Depth, DivideMat, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src1(size, depth); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, depth); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::divide(d_src1, d_src2, dst); + + CUDA_SANITY_CHECK(dst, 1e-6); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::divide(src1, src2, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// DivideScalar + +PERF_TEST_P(Sz_Depth, DivideScalar, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + cv::Scalar s; + declare.in(s, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::divide(d_src, s, dst); + + CUDA_SANITY_CHECK(dst, 1e-6); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::divide(src, s, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// DivideScalarInv + +PERF_TEST_P(Sz_Depth, DivideScalarInv, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + cv::Scalar s; + declare.in(s, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::divide(s[0], d_src, dst); + + CUDA_SANITY_CHECK(dst, 1e-6); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::divide(s, src, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// AbsDiffMat + +PERF_TEST_P(Sz_Depth, AbsDiffMat, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src1(size, depth); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, depth); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::absdiff(d_src1, d_src2, dst); + + CUDA_SANITY_CHECK(dst, 1e-10); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::absdiff(src1, src2, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// AbsDiffScalar + +PERF_TEST_P(Sz_Depth, AbsDiffScalar, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + cv::Scalar s; + declare.in(s, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::absdiff(d_src, s, dst); + + CUDA_SANITY_CHECK(dst, 1e-10); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::absdiff(src, s, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// Abs + +PERF_TEST_P(Sz_Depth, Abs, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_16S, CV_32F))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::abs(d_src, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + FAIL_NO_CPU(); + } +} + +////////////////////////////////////////////////////////////////////// +// Sqr + +PERF_TEST_P(Sz_Depth, Sqr, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16S, CV_32F))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::sqr(d_src, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + FAIL_NO_CPU(); + } +} + +////////////////////////////////////////////////////////////////////// +// Sqrt + +PERF_TEST_P(Sz_Depth, Sqrt, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16S, CV_32F))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + cv::randu(src, 0, 100000); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::sqrt(d_src, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::sqrt(src, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// Log + +PERF_TEST_P(Sz_Depth, Log, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16S, CV_32F))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + cv::randu(src, 0, 100000); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::log(d_src, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::log(src, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// Exp + +PERF_TEST_P(Sz_Depth, Exp, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16S, CV_32F))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + cv::randu(src, 0, 10); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::exp(d_src, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::exp(src, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// Pow + +DEF_PARAM_TEST(Sz_Depth_Power, cv::Size, MatDepth, double); + +PERF_TEST_P(Sz_Depth_Power, Pow, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16S, CV_32F), + Values(0.3, 2.0, 2.4))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const double power = GET_PARAM(2); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::pow(d_src, power, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::pow(src, power, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// CompareMat + +CV_ENUM(CmpCode, cv::CMP_EQ, cv::CMP_GT, cv::CMP_GE, cv::CMP_LT, cv::CMP_LE, cv::CMP_NE) + +DEF_PARAM_TEST(Sz_Depth_Code, cv::Size, MatDepth, CmpCode); + +PERF_TEST_P(Sz_Depth_Code, CompareMat, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH, + CmpCode::all())) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int cmp_code = GET_PARAM(2); + + cv::Mat src1(size, depth); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, depth); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::compare(d_src1, d_src2, dst, cmp_code); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::compare(src1, src2, dst, cmp_code); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// CompareScalar + +PERF_TEST_P(Sz_Depth_Code, CompareScalar, + Combine(CUDA_TYPICAL_MAT_SIZES, + ARITHM_MAT_DEPTH, + CmpCode::all())) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int cmp_code = GET_PARAM(2); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + cv::Scalar s; + declare.in(s, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::compare(d_src, s, dst, cmp_code); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::compare(src, s, dst, cmp_code); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// BitwiseNot + +PERF_TEST_P(Sz_Depth, BitwiseNot, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32S))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::bitwise_not(d_src, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::bitwise_not(src, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// BitwiseAndMat + +PERF_TEST_P(Sz_Depth, BitwiseAndMat, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32S))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src1(size, depth); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, depth); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::bitwise_and(d_src1, d_src2, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::bitwise_and(src1, src2, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// BitwiseAndScalar + +DEF_PARAM_TEST(Sz_Depth_Cn, cv::Size, MatDepth, MatCn); + +PERF_TEST_P(Sz_Depth_Cn, BitwiseAndScalar, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32S), + CUDA_CHANNELS_1_3_4)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); + + const int type = CV_MAKE_TYPE(depth, channels); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + cv::Scalar s; + declare.in(s, WARMUP_RNG); + cv::Scalar_ is = s; + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::bitwise_and(d_src, is, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::bitwise_and(src, is, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// BitwiseOrMat + +PERF_TEST_P(Sz_Depth, BitwiseOrMat, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32S))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src1(size, depth); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, depth); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::bitwise_or(d_src1, d_src2, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::bitwise_or(src1, src2, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// BitwiseOrScalar + +PERF_TEST_P(Sz_Depth_Cn, BitwiseOrScalar, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32S), + CUDA_CHANNELS_1_3_4)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); + + const int type = CV_MAKE_TYPE(depth, channels); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + cv::Scalar s; + declare.in(s, WARMUP_RNG); + cv::Scalar_ is = s; + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::bitwise_or(d_src, is, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::bitwise_or(src, is, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// BitwiseXorMat + +PERF_TEST_P(Sz_Depth, BitwiseXorMat, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32S))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src1(size, depth); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, depth); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::bitwise_xor(d_src1, d_src2, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::bitwise_xor(src1, src2, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// BitwiseXorScalar + +PERF_TEST_P(Sz_Depth_Cn, BitwiseXorScalar, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32S), + CUDA_CHANNELS_1_3_4)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); + + const int type = CV_MAKE_TYPE(depth, channels); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + cv::Scalar s; + declare.in(s, WARMUP_RNG); + cv::Scalar_ is = s; + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::bitwise_xor(d_src, is, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::bitwise_xor(src, is, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// RShift + +PERF_TEST_P(Sz_Depth_Cn, RShift, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32S), + CUDA_CHANNELS_1_3_4)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); + + const int type = CV_MAKE_TYPE(depth, channels); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + const cv::Scalar_ val = cv::Scalar_::all(4); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::rshift(d_src, val, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + FAIL_NO_CPU(); + } +} + +////////////////////////////////////////////////////////////////////// +// LShift + +PERF_TEST_P(Sz_Depth_Cn, LShift, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32S), + CUDA_CHANNELS_1_3_4)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); + + const int type = CV_MAKE_TYPE(depth, channels); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + const cv::Scalar_ val = cv::Scalar_::all(4); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::lshift(d_src, val, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + FAIL_NO_CPU(); + } +} + +////////////////////////////////////////////////////////////////////// +// MinMat + +PERF_TEST_P(Sz_Depth, MinMat, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src1(size, depth); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, depth); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::min(d_src1, d_src2, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::min(src1, src2, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// MinScalar + +PERF_TEST_P(Sz_Depth, MinScalar, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + cv::Scalar val; + declare.in(val, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::min(d_src, val[0], dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::min(src, val[0], dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// MaxMat + +PERF_TEST_P(Sz_Depth, MaxMat, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src1(size, depth); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, depth); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::max(d_src1, d_src2, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::max(src1, src2, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// MaxScalar + +PERF_TEST_P(Sz_Depth, MaxScalar, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + cv::Scalar val; + declare.in(val, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::max(d_src, val[0], dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::max(src, val[0], dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// AddWeighted + +DEF_PARAM_TEST(Sz_3Depth, cv::Size, MatDepth, MatDepth, MatDepth); + +PERF_TEST_P(Sz_3Depth, AddWeighted, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F, CV_64F), + Values(CV_8U, CV_16U, CV_32F, CV_64F), + Values(CV_8U, CV_16U, CV_32F, CV_64F))) +{ + const cv::Size size = GET_PARAM(0); + const int depth1 = GET_PARAM(1); + const int depth2 = GET_PARAM(2); + const int dst_depth = GET_PARAM(3); + + cv::Mat src1(size, depth1); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, depth2); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::addWeighted(d_src1, 0.5, d_src2, 0.5, 10.0, dst, dst_depth); + + CUDA_SANITY_CHECK(dst, 1e-10); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::addWeighted(src1, 0.5, src2, 0.5, 10.0, dst, dst_depth); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// MagnitudeComplex + +PERF_TEST_P(Sz, MagnitudeComplex, + CUDA_TYPICAL_MAT_SIZES) +{ + const cv::Size size = GetParam(); + + cv::Mat src(size, CV_32FC2); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::magnitude(d_src, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat xy[2]; + cv::split(src, xy); + + cv::Mat dst; + + TEST_CYCLE() cv::magnitude(xy[0], xy[1], dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// MagnitudeSqrComplex + +PERF_TEST_P(Sz, MagnitudeSqrComplex, + CUDA_TYPICAL_MAT_SIZES) +{ + const cv::Size size = GetParam(); + + cv::Mat src(size, CV_32FC2); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::magnitudeSqr(d_src, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + FAIL_NO_CPU(); + } +} + +////////////////////////////////////////////////////////////////////// +// Magnitude + +PERF_TEST_P(Sz, Magnitude, + CUDA_TYPICAL_MAT_SIZES) +{ + const cv::Size size = GetParam(); + + cv::Mat src1(size, CV_32FC1); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, CV_32FC1); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::magnitude(d_src1, d_src2, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::magnitude(src1, src2, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// MagnitudeSqr + +PERF_TEST_P(Sz, MagnitudeSqr, + CUDA_TYPICAL_MAT_SIZES) +{ + const cv::Size size = GetParam(); + + cv::Mat src1(size, CV_32FC1); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, CV_32FC1); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::magnitudeSqr(d_src1, d_src2, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + FAIL_NO_CPU(); + } +} + +////////////////////////////////////////////////////////////////////// +// Phase + +DEF_PARAM_TEST(Sz_AngleInDegrees, cv::Size, bool); +DEF_PARAM_TEST(Sz_Type_AngleInDegrees, cv::Size, MatType, bool); + +PERF_TEST_P(Sz_AngleInDegrees, Phase, + Combine(CUDA_TYPICAL_MAT_SIZES, + Bool())) +{ + const cv::Size size = GET_PARAM(0); + const bool angleInDegrees = GET_PARAM(1); + + cv::Mat src1(size, CV_32FC1); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, CV_32FC1); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::phase(d_src1, d_src2, dst, angleInDegrees); + + CUDA_SANITY_CHECK(dst, 1e-6, ERROR_RELATIVE); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::phase(src1, src2, dst, angleInDegrees); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// CartToPolar + +PERF_TEST_P(Sz_AngleInDegrees, CartToPolar, + Combine(CUDA_TYPICAL_MAT_SIZES, + Bool())) +{ + const cv::Size size = GET_PARAM(0); + const bool angleInDegrees = GET_PARAM(1); + + cv::Mat src1(size, CV_32FC1); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, CV_32FC1); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + cv::cuda::GpuMat magnitude; + cv::cuda::GpuMat angle; + + TEST_CYCLE() cv::cuda::cartToPolar(d_src1, d_src2, magnitude, angle, angleInDegrees); + + CUDA_SANITY_CHECK(magnitude); + CUDA_SANITY_CHECK(angle, 1e-6, ERROR_RELATIVE); + } + else + { + cv::Mat magnitude; + cv::Mat angle; + + TEST_CYCLE() cv::cartToPolar(src1, src2, magnitude, angle, angleInDegrees); + + CPU_SANITY_CHECK(magnitude); + CPU_SANITY_CHECK(angle); + } +} + +////////////////////////////////////////////////////////////////////// +// PolarToCart + +PERF_TEST_P(Sz_Type_AngleInDegrees, PolarToCart, + Combine(CUDA_TYPICAL_MAT_SIZES, + testing::Values(CV_32FC1, CV_64FC1), + Bool())) +{ + const cv::Size size = GET_PARAM(0); + const int type = GET_PARAM(1); + const bool angleInDegrees = GET_PARAM(2); + + cv::Mat magnitude(size, type); + declare.in(magnitude, WARMUP_RNG); + + cv::Mat angle(size, type); + declare.in(angle, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_magnitude(magnitude); + const cv::cuda::GpuMat d_angle(angle); + cv::cuda::GpuMat x; + cv::cuda::GpuMat y; + + TEST_CYCLE() cv::cuda::polarToCart(d_magnitude, d_angle, x, y, angleInDegrees); + + CUDA_SANITY_CHECK(x); + CUDA_SANITY_CHECK(y); + } + else + { + cv::Mat x; + cv::Mat y; + + TEST_CYCLE() cv::polarToCart(magnitude, angle, x, y, angleInDegrees); + + CPU_SANITY_CHECK(x); + CPU_SANITY_CHECK(y); + } +} + +////////////////////////////////////////////////////////////////////// +// Threshold + +CV_ENUM(ThreshOp, cv::THRESH_BINARY, cv::THRESH_BINARY_INV, cv::THRESH_TRUNC, cv::THRESH_TOZERO, cv::THRESH_TOZERO_INV) + +DEF_PARAM_TEST(Sz_Depth_Op, cv::Size, MatDepth, ThreshOp); + +PERF_TEST_P(Sz_Depth_Op, Threshold, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F, CV_64F), + ThreshOp::all())) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int threshOp = GET_PARAM(2); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::threshold(d_src, dst, 100.0, 255.0, threshOp); + + CUDA_SANITY_CHECK(dst, 1e-10); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::threshold(src, dst, 100.0, 255.0, threshOp); + + CPU_SANITY_CHECK(dst); + } +} + +}} // namespace diff --git a/modules/cudaarithm/perf/perf_main.cpp b/modules/cudaarithm/perf/perf_main.cpp new file mode 100644 index 00000000000..118d7596ac2 --- /dev/null +++ b/modules/cudaarithm/perf/perf_main.cpp @@ -0,0 +1,47 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "perf_precomp.hpp" + +using namespace perf; + +CV_PERF_TEST_CUDA_MAIN(cudaarithm) diff --git a/modules/cudaarithm/perf/perf_precomp.hpp b/modules/cudaarithm/perf/perf_precomp.hpp new file mode 100644 index 00000000000..071ac946537 --- /dev/null +++ b/modules/cudaarithm/perf/perf_precomp.hpp @@ -0,0 +1,55 @@ +/*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_PERF_PRECOMP_HPP__ +#define __OPENCV_PERF_PRECOMP_HPP__ + +#include "opencv2/ts.hpp" +#include "opencv2/ts/cuda_perf.hpp" + +#include "opencv2/cudaarithm.hpp" + +namespace opencv_test { +using namespace perf; +using namespace testing; +} + +#endif diff --git a/modules/cudaarithm/perf/perf_reductions.cpp b/modules/cudaarithm/perf/perf_reductions.cpp new file mode 100644 index 00000000000..71bb5524a63 --- /dev/null +++ b/modules/cudaarithm/perf/perf_reductions.cpp @@ -0,0 +1,520 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "perf_precomp.hpp" + +namespace opencv_test { namespace { + +////////////////////////////////////////////////////////////////////// +// Norm + +DEF_PARAM_TEST(Sz_Depth_Norm, cv::Size, MatDepth, NormType); + +PERF_TEST_P(Sz_Depth_Norm, Norm, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32S, CV_32F), + Values(NormType(cv::NORM_INF), NormType(cv::NORM_L1), NormType(cv::NORM_L2)))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int normType = GET_PARAM(2); + + cv::Mat src(size, depth); + if (depth == CV_8U) + cv::randu(src, 0, 254); + else + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat d_buf; + double gpu_dst; + + TEST_CYCLE() gpu_dst = cv::cuda::norm(d_src, normType, d_buf); + + SANITY_CHECK(gpu_dst, 1e-6, ERROR_RELATIVE); + } + else + { + double cpu_dst; + + TEST_CYCLE() cpu_dst = cv::norm(src, normType); + + SANITY_CHECK(cpu_dst, 1e-6, ERROR_RELATIVE); + } +} + +////////////////////////////////////////////////////////////////////// +// NormDiff + +DEF_PARAM_TEST(Sz_Norm, cv::Size, NormType); + +PERF_TEST_P(Sz_Norm, NormDiff, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(NormType(cv::NORM_INF), NormType(cv::NORM_L1), NormType(cv::NORM_L2)))) +{ + const cv::Size size = GET_PARAM(0); + const int normType = GET_PARAM(1); + + cv::Mat src1(size, CV_8UC1); + declare.in(src1, WARMUP_RNG); + + cv::Mat src2(size, CV_8UC1); + declare.in(src2, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src1(src1); + const cv::cuda::GpuMat d_src2(src2); + double gpu_dst; + + TEST_CYCLE() gpu_dst = cv::cuda::norm(d_src1, d_src2, normType); + + SANITY_CHECK(gpu_dst); + + } + else + { + double cpu_dst; + + TEST_CYCLE() cpu_dst = cv::norm(src1, src2, normType); + + SANITY_CHECK(cpu_dst); + } +} + +////////////////////////////////////////////////////////////////////// +// Sum + +DEF_PARAM_TEST(Sz_Depth_Cn, cv::Size, MatDepth, MatCn); + +PERF_TEST_P(Sz_Depth_Cn, Sum, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + CUDA_CHANNELS_1_3_4)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); + + const int type = CV_MAKE_TYPE(depth, channels); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::Scalar gpu_dst; + + TEST_CYCLE() gpu_dst = cv::cuda::sum(d_src); + + SANITY_CHECK(gpu_dst, 1e-5, ERROR_RELATIVE); + } + else + { + cv::Scalar cpu_dst; + + TEST_CYCLE() cpu_dst = cv::sum(src); + + SANITY_CHECK(cpu_dst, 1e-6, ERROR_RELATIVE); + } +} + +////////////////////////////////////////////////////////////////////// +// SumAbs + +PERF_TEST_P(Sz_Depth_Cn, SumAbs, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + CUDA_CHANNELS_1_3_4)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); + + const int type = CV_MAKE_TYPE(depth, channels); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::Scalar gpu_dst; + + TEST_CYCLE() gpu_dst = cv::cuda::absSum(d_src); + + SANITY_CHECK(gpu_dst, 1e-6, ERROR_RELATIVE); + } + else + { + FAIL_NO_CPU(); + } +} + +////////////////////////////////////////////////////////////////////// +// SumSqr + +PERF_TEST_P(Sz_Depth_Cn, SumSqr, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + CUDA_CHANNELS_1_3_4)) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); + + const int type = CV_MAKE_TYPE(depth, channels); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::Scalar gpu_dst; + + TEST_CYCLE() gpu_dst = cv::cuda::sqrSum(d_src); + + SANITY_CHECK(gpu_dst, 1e-6, ERROR_RELATIVE); + } + else + { + FAIL_NO_CPU(); + } +} + +////////////////////////////////////////////////////////////////////// +// MinMax + +DEF_PARAM_TEST(Sz_Depth, cv::Size, MatDepth); + +PERF_TEST_P(Sz_Depth, MinMax, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F, CV_64F))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + if (depth == CV_8U) + cv::randu(src, 0, 254); + else + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + double gpu_minVal, gpu_maxVal; + + TEST_CYCLE() cv::cuda::minMax(d_src, &gpu_minVal, &gpu_maxVal, cv::cuda::GpuMat()); + + SANITY_CHECK(gpu_minVal, 1e-10); + SANITY_CHECK(gpu_maxVal, 1e-10); + } + else + { + double cpu_minVal, cpu_maxVal; + + TEST_CYCLE() cv::minMaxLoc(src, &cpu_minVal, &cpu_maxVal); + + SANITY_CHECK(cpu_minVal); + SANITY_CHECK(cpu_maxVal); + } +} + +////////////////////////////////////////////////////////////////////// +// MinMaxLoc + +PERF_TEST_P(Sz_Depth, MinMaxLoc, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F, CV_64F))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + if (depth == CV_8U) + cv::randu(src, 0, 254); + else + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + double gpu_minVal, gpu_maxVal; + cv::Point gpu_minLoc, gpu_maxLoc; + + TEST_CYCLE() cv::cuda::minMaxLoc(d_src, &gpu_minVal, &gpu_maxVal, &gpu_minLoc, &gpu_maxLoc); + + SANITY_CHECK(gpu_minVal, 1e-10); + SANITY_CHECK(gpu_maxVal, 1e-10); + } + else + { + double cpu_minVal, cpu_maxVal; + cv::Point cpu_minLoc, cpu_maxLoc; + + TEST_CYCLE() cv::minMaxLoc(src, &cpu_minVal, &cpu_maxVal, &cpu_minLoc, &cpu_maxLoc); + + SANITY_CHECK(cpu_minVal); + SANITY_CHECK(cpu_maxVal); + } +} + +////////////////////////////////////////////////////////////////////// +// CountNonZero + +PERF_TEST_P(Sz_Depth, CountNonZero, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F, CV_64F))) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + + cv::Mat src(size, depth); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + int gpu_dst = 0; + + TEST_CYCLE() gpu_dst = cv::cuda::countNonZero(d_src); + + SANITY_CHECK(gpu_dst); + } + else + { + int cpu_dst = 0; + + TEST_CYCLE() cpu_dst = cv::countNonZero(src); + + SANITY_CHECK(cpu_dst); + } +} + +////////////////////////////////////////////////////////////////////// +// Reduce + +CV_ENUM(ReduceCode, REDUCE_SUM, REDUCE_AVG, REDUCE_MAX, REDUCE_MIN) + +enum {Rows = 0, Cols = 1}; +CV_ENUM(ReduceDim, Rows, Cols) + +DEF_PARAM_TEST(Sz_Depth_Cn_Code_Dim, cv::Size, MatDepth, MatCn, ReduceCode, ReduceDim); + +PERF_TEST_P(Sz_Depth_Cn_Code_Dim, Reduce, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_16S, CV_32F), + Values(1, 2, 3, 4), + ReduceCode::all(), + ReduceDim::all())) +{ + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); + const int reduceOp = GET_PARAM(3); + const int dim = GET_PARAM(4); + + const int type = CV_MAKE_TYPE(depth, channels); + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::reduce(d_src, dst, dim, reduceOp, CV_32F); + + dst = dst.reshape(dst.channels(), 1); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::reduce(src, dst, dim, reduceOp, CV_32F); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// Normalize + +DEF_PARAM_TEST(Sz_Depth_NormType, cv::Size, MatDepth, NormType); + +PERF_TEST_P(Sz_Depth_NormType, Normalize, + Combine(CUDA_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F, CV_64F), + Values(NormType(cv::NORM_INF), + NormType(cv::NORM_L1), + NormType(cv::NORM_L2), + NormType(cv::NORM_MINMAX)))) +{ + const cv::Size size = GET_PARAM(0); + const int type = GET_PARAM(1); + const int norm_type = GET_PARAM(2); + + const double alpha = 1; + const double beta = 0; + + cv::Mat src(size, type); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::normalize(d_src, dst, alpha, beta, norm_type, type, cv::cuda::GpuMat()); + + CUDA_SANITY_CHECK(dst, 1e-6); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::normalize(src, dst, alpha, beta, norm_type, type); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// MeanStdDev + +PERF_TEST_P(Sz, MeanStdDev, + CUDA_TYPICAL_MAT_SIZES) +{ + const cv::Size size = GetParam(); + + cv::Mat src(size, CV_8UC1); + declare.in(src, WARMUP_RNG); + + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::Scalar gpu_mean; + cv::Scalar gpu_stddev; + + TEST_CYCLE() cv::cuda::meanStdDev(d_src, gpu_mean, gpu_stddev); + + SANITY_CHECK(gpu_mean); + SANITY_CHECK(gpu_stddev); + } + else + { + cv::Scalar cpu_mean; + cv::Scalar cpu_stddev; + + TEST_CYCLE() cv::meanStdDev(src, cpu_mean, cpu_stddev); + + SANITY_CHECK(cpu_mean); + SANITY_CHECK(cpu_stddev); + } +} + +////////////////////////////////////////////////////////////////////// +// Integral + +PERF_TEST_P(Sz, Integral, + CUDA_TYPICAL_MAT_SIZES) +{ + const cv::Size size = GetParam(); + + cv::Mat src(size, CV_8UC1); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::integral(d_src, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + cv::Mat dst; + + TEST_CYCLE() cv::integral(src, dst); + + CPU_SANITY_CHECK(dst); + } +} + +////////////////////////////////////////////////////////////////////// +// IntegralSqr + +PERF_TEST_P(Sz, IntegralSqr, + CUDA_TYPICAL_MAT_SIZES) +{ + const cv::Size size = GetParam(); + + cv::Mat src(size, CV_8UC1); + declare.in(src, WARMUP_RNG); + + if (PERF_RUN_CUDA()) + { + const cv::cuda::GpuMat d_src(src); + cv::cuda::GpuMat dst; + + TEST_CYCLE() cv::cuda::sqrIntegral(d_src, dst); + + CUDA_SANITY_CHECK(dst); + } + else + { + FAIL_NO_CPU(); + } +} + +}} // namespace diff --git a/modules/cudaarithm/src/arithm.cpp b/modules/cudaarithm/src/arithm.cpp new file mode 100644 index 00000000000..381580cff43 --- /dev/null +++ b/modules/cudaarithm/src/arithm.cpp @@ -0,0 +1,582 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" + +using namespace cv; +using namespace cv::cuda; + +#if !defined (HAVE_CUDA) || defined (CUDA_DISABLER) + +void cv::cuda::gemm(InputArray, InputArray, double, InputArray, double, OutputArray, int, Stream&) { throw_no_cuda(); } + +void cv::cuda::mulSpectrums(InputArray, InputArray, OutputArray, int, bool, Stream&) { throw_no_cuda(); } +void cv::cuda::mulAndScaleSpectrums(InputArray, InputArray, OutputArray, int, float, bool, Stream&) { throw_no_cuda(); } + +void cv::cuda::dft(InputArray, OutputArray, Size, int, Stream&) { throw_no_cuda(); } + +Ptr cv::cuda::createConvolution(Size) { throw_no_cuda(); return Ptr(); } + +#else /* !defined (HAVE_CUDA) */ + +namespace +{ + #define error_entry(entry) { entry, #entry } + + struct ErrorEntry + { + int code; + const char* str; + }; + + struct ErrorEntryComparer + { + int code; + ErrorEntryComparer(int code_) : code(code_) {} + bool operator()(const ErrorEntry& e) const { return e.code == code; } + }; + + String getErrorString(int code, const ErrorEntry* errors, size_t n) + { + size_t idx = std::find_if(errors, errors + n, ErrorEntryComparer(code)) - errors; + + const char* msg = (idx != n) ? errors[idx].str : "Unknown error code"; + String str = cv::format("%s [Code = %d]", msg, code); + + return str; + } +} + +#ifdef HAVE_CUBLAS + namespace + { + const ErrorEntry cublas_errors[] = + { + error_entry( CUBLAS_STATUS_SUCCESS ), + error_entry( CUBLAS_STATUS_NOT_INITIALIZED ), + error_entry( CUBLAS_STATUS_ALLOC_FAILED ), + error_entry( CUBLAS_STATUS_INVALID_VALUE ), + error_entry( CUBLAS_STATUS_ARCH_MISMATCH ), + error_entry( CUBLAS_STATUS_MAPPING_ERROR ), + error_entry( CUBLAS_STATUS_EXECUTION_FAILED ), + error_entry( CUBLAS_STATUS_INTERNAL_ERROR ) + }; + + const size_t cublas_error_num = sizeof(cublas_errors) / sizeof(cublas_errors[0]); + + static inline void ___cublasSafeCall(cublasStatus_t err, const char* file, const int line, const char* func) + { + if (CUBLAS_STATUS_SUCCESS != err) + { + String msg = getErrorString(err, cublas_errors, cublas_error_num); + cv::error(cv::Error::GpuApiCallError, msg, func, file, line); + } + } + } + + #define cublasSafeCall(expr) ___cublasSafeCall(expr, __FILE__, __LINE__, CV_Func) +#endif // HAVE_CUBLAS + +#ifdef HAVE_CUFFT + namespace + { + ////////////////////////////////////////////////////////////////////////// + // CUFFT errors + + const ErrorEntry cufft_errors[] = + { + error_entry( CUFFT_INVALID_PLAN ), + error_entry( CUFFT_ALLOC_FAILED ), + error_entry( CUFFT_INVALID_TYPE ), + error_entry( CUFFT_INVALID_VALUE ), + error_entry( CUFFT_INTERNAL_ERROR ), + error_entry( CUFFT_EXEC_FAILED ), + error_entry( CUFFT_SETUP_FAILED ), + error_entry( CUFFT_INVALID_SIZE ), + error_entry( CUFFT_UNALIGNED_DATA ) + }; + + const int cufft_error_num = sizeof(cufft_errors) / sizeof(cufft_errors[0]); + + void ___cufftSafeCall(int err, const char* file, const int line, const char* func) + { + if (CUFFT_SUCCESS != err) + { + String msg = getErrorString(err, cufft_errors, cufft_error_num); + cv::error(cv::Error::GpuApiCallError, msg, func, file, line); + } + } + } + + #define cufftSafeCall(expr) ___cufftSafeCall(expr, __FILE__, __LINE__, CV_Func) + +#endif + +//////////////////////////////////////////////////////////////////////// +// gemm + +void cv::cuda::gemm(InputArray _src1, InputArray _src2, double alpha, InputArray _src3, double beta, OutputArray _dst, int flags, Stream& stream) +{ +#ifndef HAVE_CUBLAS + CV_UNUSED(_src1); + CV_UNUSED(_src2); + CV_UNUSED(alpha); + CV_UNUSED(_src3); + CV_UNUSED(beta); + CV_UNUSED(_dst); + CV_UNUSED(flags); + CV_UNUSED(stream); + CV_Error(Error::StsNotImplemented, "The library was build without CUBLAS"); +#else + // CUBLAS works with column-major matrices + + GpuMat src1 = getInputMat(_src1, stream); + GpuMat src2 = getInputMat(_src2, stream); + GpuMat src3 = getInputMat(_src3, stream); + + CV_Assert( src1.type() == CV_32FC1 || src1.type() == CV_32FC2 || src1.type() == CV_64FC1 || src1.type() == CV_64FC2 ); + CV_Assert( src2.type() == src1.type() && (src3.empty() || src3.type() == src1.type()) ); + + if (src1.depth() == CV_64F) + { + if (!deviceSupports(NATIVE_DOUBLE)) + CV_Error(cv::Error::StsUnsupportedFormat, "The device doesn't support double"); + } + + bool tr1 = (flags & GEMM_1_T) != 0; + bool tr2 = (flags & GEMM_2_T) != 0; + bool tr3 = (flags & GEMM_3_T) != 0; + + if (src1.type() == CV_64FC2) + { + if (tr1 || tr2 || tr3) + CV_Error(cv::Error::StsNotImplemented, "transpose operation doesn't implemented for CV_64FC2 type"); + } + + Size src1Size = tr1 ? Size(src1.rows, src1.cols) : src1.size(); + Size src2Size = tr2 ? Size(src2.rows, src2.cols) : src2.size(); + Size src3Size = tr3 ? Size(src3.rows, src3.cols) : src3.size(); + Size dstSize(src2Size.width, src1Size.height); + + CV_Assert( src1Size.width == src2Size.height ); + CV_Assert( src3.empty() || src3Size == dstSize ); + + GpuMat dst = getOutputMat(_dst, dstSize, src1.type(), stream); + + if (beta != 0) + { + if (src3.empty()) + { + dst.setTo(Scalar::all(0), stream); + } + else + { + if (tr3) + { + cuda::transpose(src3, dst, stream); + } + else + { + src3.copyTo(dst, stream); + } + } + } + + cublasHandle_t handle; + cublasSafeCall( cublasCreate_v2(&handle) ); + + cublasSafeCall( cublasSetStream_v2(handle, StreamAccessor::getStream(stream)) ); + + cublasSafeCall( cublasSetPointerMode_v2(handle, CUBLAS_POINTER_MODE_HOST) ); + + const float alphaf = static_cast(alpha); + const float betaf = static_cast(beta); + + const cuComplex alphacf = make_cuComplex(alphaf, 0); + const cuComplex betacf = make_cuComplex(betaf, 0); + + const cuDoubleComplex alphac = make_cuDoubleComplex(alpha, 0); + const cuDoubleComplex betac = make_cuDoubleComplex(beta, 0); + + cublasOperation_t transa = tr2 ? CUBLAS_OP_T : CUBLAS_OP_N; + cublasOperation_t transb = tr1 ? CUBLAS_OP_T : CUBLAS_OP_N; + + switch (src1.type()) + { + case CV_32FC1: + cublasSafeCall( cublasSgemm_v2(handle, transa, transb, tr2 ? src2.rows : src2.cols, tr1 ? src1.cols : src1.rows, tr2 ? src2.cols : src2.rows, + &alphaf, + src2.ptr(), static_cast(src2.step / sizeof(float)), + src1.ptr(), static_cast(src1.step / sizeof(float)), + &betaf, + dst.ptr(), static_cast(dst.step / sizeof(float))) ); + break; + + case CV_64FC1: + cublasSafeCall( cublasDgemm_v2(handle, transa, transb, tr2 ? src2.rows : src2.cols, tr1 ? src1.cols : src1.rows, tr2 ? src2.cols : src2.rows, + &alpha, + src2.ptr(), static_cast(src2.step / sizeof(double)), + src1.ptr(), static_cast(src1.step / sizeof(double)), + &beta, + dst.ptr(), static_cast(dst.step / sizeof(double))) ); + break; + + case CV_32FC2: + cublasSafeCall( cublasCgemm_v2(handle, transa, transb, tr2 ? src2.rows : src2.cols, tr1 ? src1.cols : src1.rows, tr2 ? src2.cols : src2.rows, + &alphacf, + src2.ptr(), static_cast(src2.step / sizeof(cuComplex)), + src1.ptr(), static_cast(src1.step / sizeof(cuComplex)), + &betacf, + dst.ptr(), static_cast(dst.step / sizeof(cuComplex))) ); + break; + + case CV_64FC2: + cublasSafeCall( cublasZgemm_v2(handle, transa, transb, tr2 ? src2.rows : src2.cols, tr1 ? src1.cols : src1.rows, tr2 ? src2.cols : src2.rows, + &alphac, + src2.ptr(), static_cast(src2.step / sizeof(cuDoubleComplex)), + src1.ptr(), static_cast(src1.step / sizeof(cuDoubleComplex)), + &betac, + dst.ptr(), static_cast(dst.step / sizeof(cuDoubleComplex))) ); + break; + } + + cublasSafeCall( cublasDestroy_v2(handle) ); + + syncOutput(dst, _dst, stream); +#endif +} + +////////////////////////////////////////////////////////////////////////////// +// DFT function + +void cv::cuda::dft(InputArray _src, OutputArray _dst, Size dft_size, int flags, Stream& stream) +{ + if (getInputMat(_src, stream).channels() == 2) + flags |= DFT_COMPLEX_INPUT; + + Ptr dft = createDFT(dft_size, flags); + dft->compute(_src, _dst, stream); +} + +////////////////////////////////////////////////////////////////////////////// +// DFT algorithm + +#ifdef HAVE_CUFFT + +namespace +{ + + class DFTImpl : public DFT + { + Size dft_size, dft_size_opt; + bool is_1d_input, is_row_dft, is_scaled_dft, is_inverse, is_complex_input, is_complex_output; + + cufftType dft_type; + cufftHandle plan; + + public: + DFTImpl(Size dft_size, int flags) + : dft_size(dft_size), + dft_size_opt(dft_size), + is_1d_input((dft_size.height == 1) || (dft_size.width == 1)), + is_row_dft((flags & DFT_ROWS) != 0), + is_scaled_dft((flags & DFT_SCALE) != 0), + is_inverse((flags & DFT_INVERSE) != 0), + is_complex_input((flags & DFT_COMPLEX_INPUT) != 0), + is_complex_output(!(flags & DFT_REAL_OUTPUT)), + dft_type(!is_complex_input ? CUFFT_R2C : (is_complex_output ? CUFFT_C2C : CUFFT_C2R)) + { + // We don't support unpacked output (in the case of real input) + CV_Assert( !(flags & DFT_COMPLEX_OUTPUT) ); + + // We don't support real-to-real transform + CV_Assert( is_complex_input || is_complex_output ); + + if (is_1d_input && !is_row_dft) + { + // If the source matrix is single column handle it as single row + dft_size_opt.width = std::max(dft_size.width, dft_size.height); + dft_size_opt.height = std::min(dft_size.width, dft_size.height); + } + + CV_Assert( dft_size_opt.width > 1 ); + + if (is_1d_input || is_row_dft) + cufftSafeCall( cufftPlan1d(&plan, dft_size_opt.width, dft_type, dft_size_opt.height) ); + else + cufftSafeCall( cufftPlan2d(&plan, dft_size_opt.height, dft_size_opt.width, dft_type) ); + } + + ~DFTImpl() + { + cufftSafeCall( cufftDestroy(plan) ); + } + + void compute(InputArray _src, OutputArray _dst, Stream& stream) + { + GpuMat src = getInputMat(_src, stream); + + CV_Assert( src.type() == CV_32FC1 || src.type() == CV_32FC2 ); + CV_Assert( is_complex_input == (src.channels() == 2) ); + + // Make sure here we work with the continuous input, + // as CUFFT can't handle gaps + GpuMat src_cont; + if (src.isContinuous()) + { + src_cont = src; + } + else + { + BufferPool pool(stream); + src_cont.allocator = pool.getAllocator(); + createContinuous(src.rows, src.cols, src.type(), src_cont); + src.copyTo(src_cont, stream); + } + + cufftSafeCall( cufftSetStream(plan, StreamAccessor::getStream(stream)) ); + + if (is_complex_input) + { + if (is_complex_output) + { + createContinuous(dft_size, CV_32FC2, _dst); + GpuMat dst = _dst.getGpuMat(); + + cufftSafeCall(cufftExecC2C( + plan, src_cont.ptr(), dst.ptr(), + is_inverse ? CUFFT_INVERSE : CUFFT_FORWARD)); + } + else + { + createContinuous(dft_size, CV_32F, _dst); + GpuMat dst = _dst.getGpuMat(); + + cufftSafeCall(cufftExecC2R( + plan, src_cont.ptr(), dst.ptr())); + } + } + else + { + // We could swap dft_size for efficiency. Here we must reflect it + if (dft_size == dft_size_opt) + createContinuous(Size(dft_size.width / 2 + 1, dft_size.height), CV_32FC2, _dst); + else + createContinuous(Size(dft_size.width, dft_size.height / 2 + 1), CV_32FC2, _dst); + + GpuMat dst = _dst.getGpuMat(); + + cufftSafeCall(cufftExecR2C( + plan, src_cont.ptr(), dst.ptr())); + } + + if (is_scaled_dft) + cuda::multiply(_dst, Scalar::all(1. / dft_size.area()), _dst, 1, -1, stream); + } + }; +} + +#endif + +Ptr cv::cuda::createDFT(Size dft_size, int flags) +{ +#ifndef HAVE_CUFFT + CV_UNUSED(dft_size); + CV_UNUSED(flags); + CV_Error(Error::StsNotImplemented, "The library was build without CUFFT"); + return Ptr(); +#else + return makePtr(dft_size, flags); +#endif +} + +////////////////////////////////////////////////////////////////////////////// +// Convolution + +#ifdef HAVE_CUFFT + +namespace +{ + class ConvolutionImpl : public Convolution + { + public: + explicit ConvolutionImpl(Size user_block_size_) : user_block_size(user_block_size_) {} + + void convolve(InputArray image, InputArray templ, OutputArray result, bool ccorr = false, Stream& stream = Stream::Null()); + + private: + void create(Size image_size, Size templ_size); + static Size estimateBlockSize(Size result_size); + + Size result_size; + Size block_size; + Size user_block_size; + Size dft_size; + + GpuMat image_spect, templ_spect, result_spect; + GpuMat image_block, templ_block, result_data; + }; + + void ConvolutionImpl::create(Size image_size, Size templ_size) + { + result_size = Size(image_size.width - templ_size.width + 1, + image_size.height - templ_size.height + 1); + + block_size = user_block_size; + if (user_block_size.width == 0 || user_block_size.height == 0) + block_size = estimateBlockSize(result_size); + + dft_size.width = 1 << int(ceil(std::log(block_size.width + templ_size.width - 1.) / std::log(2.))); + dft_size.height = 1 << int(ceil(std::log(block_size.height + templ_size.height - 1.) / std::log(2.))); + + // CUFFT has hard-coded kernels for power-of-2 sizes (up to 8192), + // see CUDA Toolkit 4.1 CUFFT Library Programming Guide + if (dft_size.width > 8192) + dft_size.width = getOptimalDFTSize(block_size.width + templ_size.width - 1); + if (dft_size.height > 8192) + dft_size.height = getOptimalDFTSize(block_size.height + templ_size.height - 1); + + // To avoid wasting time doing small DFTs + dft_size.width = std::max(dft_size.width, 512); + dft_size.height = std::max(dft_size.height, 512); + + createContinuous(dft_size, CV_32F, image_block); + createContinuous(dft_size, CV_32F, templ_block); + createContinuous(dft_size, CV_32F, result_data); + + int spect_len = dft_size.height * (dft_size.width / 2 + 1); + createContinuous(1, spect_len, CV_32FC2, image_spect); + createContinuous(1, spect_len, CV_32FC2, templ_spect); + createContinuous(1, spect_len, CV_32FC2, result_spect); + + // Use maximum result matrix block size for the estimated DFT block size + block_size.width = std::min(dft_size.width - templ_size.width + 1, result_size.width); + block_size.height = std::min(dft_size.height - templ_size.height + 1, result_size.height); + } + + Size ConvolutionImpl::estimateBlockSize(Size result_size) + { + int width = (result_size.width + 2) / 3; + int height = (result_size.height + 2) / 3; + width = std::min(width, result_size.width); + height = std::min(height, result_size.height); + return Size(width, height); + } + + void ConvolutionImpl::convolve(InputArray _image, InputArray _templ, OutputArray _result, bool ccorr, Stream& _stream) + { + GpuMat image = getInputMat(_image, _stream); + GpuMat templ = getInputMat(_templ, _stream); + + CV_Assert( image.type() == CV_32FC1 ); + CV_Assert( templ.type() == CV_32FC1 ); + + create(image.size(), templ.size()); + + GpuMat result = getOutputMat(_result, result_size, CV_32FC1, _stream); + + cudaStream_t stream = StreamAccessor::getStream(_stream); + + cufftHandle planR2C, planC2R; + cufftSafeCall( cufftPlan2d(&planC2R, dft_size.height, dft_size.width, CUFFT_C2R) ); + cufftSafeCall( cufftPlan2d(&planR2C, dft_size.height, dft_size.width, CUFFT_R2C) ); + + cufftSafeCall( cufftSetStream(planR2C, stream) ); + cufftSafeCall( cufftSetStream(planC2R, stream) ); + + GpuMat templ_roi(templ.size(), CV_32FC1, templ.data, templ.step); + cuda::copyMakeBorder(templ_roi, templ_block, 0, templ_block.rows - templ_roi.rows, 0, + templ_block.cols - templ_roi.cols, 0, Scalar(), _stream); + + cufftSafeCall( cufftExecR2C(planR2C, templ_block.ptr(), templ_spect.ptr()) ); + + // Process all blocks of the result matrix + for (int y = 0; y < result.rows; y += block_size.height) + { + for (int x = 0; x < result.cols; x += block_size.width) + { + Size image_roi_size(std::min(x + dft_size.width, image.cols) - x, + std::min(y + dft_size.height, image.rows) - y); + GpuMat image_roi(image_roi_size, CV_32F, (void*)(image.ptr(y) + x), + image.step); + cuda::copyMakeBorder(image_roi, image_block, 0, image_block.rows - image_roi.rows, + 0, image_block.cols - image_roi.cols, 0, Scalar(), _stream); + + cufftSafeCall(cufftExecR2C(planR2C, image_block.ptr(), + image_spect.ptr())); + cuda::mulAndScaleSpectrums(image_spect, templ_spect, result_spect, 0, + 1.f / dft_size.area(), ccorr, _stream); + cufftSafeCall(cufftExecC2R(planC2R, result_spect.ptr(), + result_data.ptr())); + + Size result_roi_size(std::min(x + block_size.width, result.cols) - x, + std::min(y + block_size.height, result.rows) - y); + GpuMat result_roi(result_roi_size, result.type(), + (void*)(result.ptr(y) + x), result.step); + GpuMat result_block(result_roi_size, result_data.type(), + result_data.ptr(), result_data.step); + + result_block.copyTo(result_roi, _stream); + } + } + + cufftSafeCall( cufftDestroy(planR2C) ); + cufftSafeCall( cufftDestroy(planC2R) ); + + syncOutput(result, _result, _stream); + } +} + +#endif + +Ptr cv::cuda::createConvolution(Size user_block_size) +{ +#ifndef HAVE_CUFFT + CV_UNUSED(user_block_size); + CV_Error(Error::StsNotImplemented, "The library was build without CUFFT"); + return Ptr(); +#else + return makePtr(user_block_size); +#endif +} + +#endif /* !defined (HAVE_CUDA) */ diff --git a/modules/cudaarithm/src/core.cpp b/modules/cudaarithm/src/core.cpp new file mode 100644 index 00000000000..6d97e15dbbd --- /dev/null +++ b/modules/cudaarithm/src/core.cpp @@ -0,0 +1,133 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" + +using namespace cv; +using namespace cv::cuda; + +#if !defined (HAVE_CUDA) || defined (CUDA_DISABLER) + +void cv::cuda::merge(const GpuMat*, size_t, OutputArray, Stream&) { throw_no_cuda(); } +void cv::cuda::merge(const std::vector&, OutputArray, Stream&) { throw_no_cuda(); } + +void cv::cuda::split(InputArray, GpuMat*, Stream&) { throw_no_cuda(); } +void cv::cuda::split(InputArray, std::vector&, Stream&) { throw_no_cuda(); } + +void cv::cuda::transpose(InputArray, OutputArray, Stream&) { throw_no_cuda(); } + +void cv::cuda::flip(InputArray, OutputArray, int, Stream&) { throw_no_cuda(); } + +void cv::cuda::copyMakeBorder(InputArray, OutputArray, int, int, int, int, int, Scalar, Stream&) { throw_no_cuda(); } + +#else /* !defined (HAVE_CUDA) */ + +//////////////////////////////////////////////////////////////////////// +// flip + +namespace +{ + template struct NppTypeTraits; + template<> struct NppTypeTraits { typedef Npp8u npp_t; }; + template<> struct NppTypeTraits { typedef Npp8s npp_t; }; + template<> struct NppTypeTraits { typedef Npp16u npp_t; }; + template<> struct NppTypeTraits { typedef Npp16s npp_t; }; + template<> struct NppTypeTraits { typedef Npp32s npp_t; }; + template<> struct NppTypeTraits { typedef Npp32f npp_t; }; + template<> struct NppTypeTraits { typedef Npp64f npp_t; }; + + template struct NppMirrorFunc + { + typedef typename NppTypeTraits::npp_t npp_t; + + typedef NppStatus (*func_t)(const npp_t* pSrc, int nSrcStep, npp_t* pDst, int nDstStep, NppiSize oROI, NppiAxis flip); + }; + + template ::func_t func> struct NppMirror + { + typedef typename NppMirrorFunc::npp_t npp_t; + + static void call(const GpuMat& src, GpuMat& dst, int flipCode, cudaStream_t stream) + { + NppStreamHandler h(stream); + + NppiSize sz; + sz.width = src.cols; + sz.height = src.rows; + + nppSafeCall( func(src.ptr(), static_cast(src.step), + dst.ptr(), static_cast(dst.step), sz, + (flipCode == 0 ? NPP_HORIZONTAL_AXIS : (flipCode > 0 ? NPP_VERTICAL_AXIS : NPP_BOTH_AXIS))) ); + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } + }; +} + +void cv::cuda::flip(InputArray _src, OutputArray _dst, int flipCode, Stream& stream) +{ + typedef void (*func_t)(const GpuMat& src, GpuMat& dst, int flipCode, cudaStream_t stream); + static const func_t funcs[6][4] = + { + {NppMirror::call, 0, NppMirror::call, NppMirror::call}, + {0,0,0,0}, + {NppMirror::call, 0, NppMirror::call, NppMirror::call}, + {0,0,0,0}, + {NppMirror::call, 0, NppMirror::call, NppMirror::call}, + {NppMirror::call, 0, NppMirror::call, NppMirror::call} + }; + + GpuMat src = getInputMat(_src, stream); + + CV_Assert(src.depth() == CV_8U || src.depth() == CV_16U || src.depth() == CV_32S || src.depth() == CV_32F); + CV_Assert(src.channels() == 1 || src.channels() == 3 || src.channels() == 4); + + _dst.create(src.size(), src.type()); + GpuMat dst = getOutputMat(_dst, src.size(), src.type(), stream); + + funcs[src.depth()][src.channels() - 1](src, dst, flipCode, StreamAccessor::getStream(stream)); + + syncOutput(dst, _dst, stream); +} + +#endif /* !defined (HAVE_CUDA) */ diff --git a/modules/cudaarithm/src/cuda/absdiff_mat.cu b/modules/cudaarithm/src/cuda/absdiff_mat.cu new file mode 100644 index 00000000000..ec04f122845 --- /dev/null +++ b/modules/cudaarithm/src/cuda/absdiff_mat.cu @@ -0,0 +1,188 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "opencv2/opencv_modules.hpp" + +#ifndef HAVE_OPENCV_CUDEV + +#error "opencv_cudev is required" + +#else + +#include "opencv2/cudev.hpp" + +using namespace cv::cudev; + +void absDiffMat(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, const GpuMat&, double, Stream& stream, int); + +namespace +{ + __device__ __forceinline__ int _abs(int a) + { + return ::abs(a); + } + __device__ __forceinline__ float _abs(float a) + { + return ::fabsf(a); + } + __device__ __forceinline__ double _abs(double a) + { + return ::fabs(a); + } + + template struct AbsDiffOp1 : binary_function + { + __device__ __forceinline__ T operator ()(T a, T b) const + { + return saturate_cast(_abs(a - b)); + } + }; + + template struct TransformPolicy : DefaultTransformPolicy + { + }; + template <> struct TransformPolicy : DefaultTransformPolicy + { + enum { + shift = 1 + }; + }; + + template + void absDiffMat_v1(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, Stream& stream) + { + gridTransformBinary_< TransformPolicy >(globPtr(src1), globPtr(src2), globPtr(dst), AbsDiffOp1(), stream); + } + + struct AbsDiffOp2 : binary_function + { + __device__ __forceinline__ uint operator ()(uint a, uint b) const + { + return vabsdiff2(a, b); + } + }; + + void absDiffMat_v2(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, Stream& stream) + { + const int vcols = src1.cols >> 1; + + GlobPtrSz src1_ = globPtr((uint*) src1.data, src1.step, src1.rows, vcols); + GlobPtrSz src2_ = globPtr((uint*) src2.data, src2.step, src1.rows, vcols); + GlobPtrSz dst_ = globPtr((uint*) dst.data, dst.step, src1.rows, vcols); + + gridTransformBinary(src1_, src2_, dst_, AbsDiffOp2(), stream); + } + + struct AbsDiffOp4 : binary_function + { + __device__ __forceinline__ uint operator ()(uint a, uint b) const + { + return vabsdiff4(a, b); + } + }; + + void absDiffMat_v4(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, Stream& stream) + { + const int vcols = src1.cols >> 2; + + GlobPtrSz src1_ = globPtr((uint*) src1.data, src1.step, src1.rows, vcols); + GlobPtrSz src2_ = globPtr((uint*) src2.data, src2.step, src1.rows, vcols); + GlobPtrSz dst_ = globPtr((uint*) dst.data, dst.step, src1.rows, vcols); + + gridTransformBinary(src1_, src2_, dst_, AbsDiffOp4(), stream); + } +} + +void absDiffMat(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, const GpuMat&, double, Stream& stream, int) +{ + typedef void (*func_t)(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, Stream& stream); + static const func_t funcs[] = + { + absDiffMat_v1, + absDiffMat_v1, + absDiffMat_v1, + absDiffMat_v1, + absDiffMat_v1, + absDiffMat_v1, + absDiffMat_v1 + }; + + const int depth = src1.depth(); + + CV_DbgAssert( depth <= CV_64F ); + + GpuMat src1_ = src1.reshape(1); + GpuMat src2_ = src2.reshape(1); + GpuMat dst_ = dst.reshape(1); + + if (depth == CV_8U || depth == CV_16U) + { + const intptr_t src1ptr = reinterpret_cast(src1_.data); + const intptr_t src2ptr = reinterpret_cast(src2_.data); + const intptr_t dstptr = reinterpret_cast(dst_.data); + + const bool isAllAligned = (src1ptr & 31) == 0 && (src2ptr & 31) == 0 && (dstptr & 31) == 0; + + if (isAllAligned) + { + if (depth == CV_8U && (src1_.cols & 3) == 0) + { + absDiffMat_v4(src1_, src2_, dst_, stream); + return; + } + else if (depth == CV_16U && (src1_.cols & 1) == 0) + { + absDiffMat_v2(src1_, src2_, dst_, stream); + return; + } + } + } + + const func_t func = funcs[depth]; + + if (!func) + CV_Error(cv::Error::StsUnsupportedFormat, "Unsupported combination of source and destination types"); + + func(src1_, src2_, dst_, stream); +} + +#endif diff --git a/modules/cudaarithm/src/cuda/absdiff_scalar.cu b/modules/cudaarithm/src/cuda/absdiff_scalar.cu new file mode 100644 index 00000000000..0955e40c8b1 --- /dev/null +++ b/modules/cudaarithm/src/cuda/absdiff_scalar.cu @@ -0,0 +1,133 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "opencv2/opencv_modules.hpp" + +#ifndef HAVE_OPENCV_CUDEV + +#error "opencv_cudev is required" + +#else + +#include "opencv2/cudev.hpp" + +using namespace cv::cudev; + +void absDiffScalar(const GpuMat& src, cv::Scalar val, bool, GpuMat& dst, const GpuMat&, double, Stream& stream, int); + +namespace +{ + template struct AbsDiffScalarOp : unary_function + { + ScalarType val; + + __device__ __forceinline__ DstType operator ()(SrcType a) const + { + abs_func f; + return saturate_cast(f(saturate_cast(a) - val)); + } + }; + + template struct TransformPolicy : DefaultTransformPolicy + { + }; + template <> struct TransformPolicy : DefaultTransformPolicy + { + enum { + shift = 1 + }; + }; + + template + void absDiffScalarImpl(const GpuMat& src, cv::Scalar value, GpuMat& dst, Stream& stream) + { + typedef typename MakeVec::cn>::type ScalarType; + + cv::Scalar_ value_ = value; + + AbsDiffScalarOp op; + op.val = VecTraits::make(value_.val); + gridTransformUnary_< TransformPolicy >(globPtr(src), globPtr(dst), op, stream); + } +} + +void absDiffScalar(const GpuMat& src, cv::Scalar val, bool, GpuMat& dst, const GpuMat&, double, Stream& stream, int) +{ + typedef void (*func_t)(const GpuMat& src, cv::Scalar val, GpuMat& dst, Stream& stream); + static const func_t funcs[7][4] = + { + { + absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl + }, + { + absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl + }, + { + absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl + }, + { + absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl + }, + { + absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl + }, + { + absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl + }, + { + absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl, absDiffScalarImpl + } + }; + + const int sdepth = src.depth(); + const int cn = src.channels(); + + CV_DbgAssert( sdepth <= CV_64F && cn <= 4 && src.type() == dst.type()); + + const func_t func = funcs[sdepth][cn - 1]; + if (!func) + CV_Error(cv::Error::StsUnsupportedFormat, "Unsupported combination of source and destination types"); + + func(src, val, dst, stream); +} + +#endif diff --git a/modules/cudaarithm/src/cuda/add_mat.cu b/modules/cudaarithm/src/cuda/add_mat.cu new file mode 100644 index 00000000000..4166cc104e0 --- /dev/null +++ b/modules/cudaarithm/src/cuda/add_mat.cu @@ -0,0 +1,225 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "opencv2/opencv_modules.hpp" + +#ifndef HAVE_OPENCV_CUDEV + +#error "opencv_cudev is required" + +#else + +#include "opencv2/cudev.hpp" + +using namespace cv::cudev; + +void addMat(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, const GpuMat& mask, double, Stream& _stream, int); + +namespace +{ + template struct AddOp1 : binary_function + { + __device__ __forceinline__ D operator ()(T a, T b) const + { + return saturate_cast(a + b); + } + }; + + template + void addMat_v1(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, const GpuMat& mask, Stream& stream) + { + if (mask.data) + gridTransformBinary(globPtr(src1), globPtr(src2), globPtr(dst), AddOp1(), globPtr(mask), stream); + else + gridTransformBinary(globPtr(src1), globPtr(src2), globPtr(dst), AddOp1(), stream); + } + + struct AddOp2 : binary_function + { + __device__ __forceinline__ uint operator ()(uint a, uint b) const + { + return vadd2(a, b); + } + }; + + void addMat_v2(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, Stream& stream) + { + const int vcols = src1.cols >> 1; + + GlobPtrSz src1_ = globPtr((uint*) src1.data, src1.step, src1.rows, vcols); + GlobPtrSz src2_ = globPtr((uint*) src2.data, src2.step, src1.rows, vcols); + GlobPtrSz dst_ = globPtr((uint*) dst.data, dst.step, src1.rows, vcols); + + gridTransformBinary(src1_, src2_, dst_, AddOp2(), stream); + } + + struct AddOp4 : binary_function + { + __device__ __forceinline__ uint operator ()(uint a, uint b) const + { + return vadd4(a, b); + } + }; + + void addMat_v4(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, Stream& stream) + { + const int vcols = src1.cols >> 2; + + GlobPtrSz src1_ = globPtr((uint*) src1.data, src1.step, src1.rows, vcols); + GlobPtrSz src2_ = globPtr((uint*) src2.data, src2.step, src1.rows, vcols); + GlobPtrSz dst_ = globPtr((uint*) dst.data, dst.step, src1.rows, vcols); + + gridTransformBinary(src1_, src2_, dst_, AddOp4(), stream); + } +} + +void addMat(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, const GpuMat& mask, double, Stream& stream, int) +{ + typedef void (*func_t)(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, const GpuMat& mask, Stream& stream); + static const func_t funcs[7][7] = + { + { + addMat_v1, + addMat_v1, + addMat_v1, + addMat_v1, + addMat_v1, + addMat_v1, + addMat_v1 + }, + { + addMat_v1, + addMat_v1, + addMat_v1, + addMat_v1, + addMat_v1, + addMat_v1, + addMat_v1 + }, + { + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + addMat_v1, + addMat_v1, + addMat_v1, + addMat_v1, + addMat_v1 + }, + { + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + addMat_v1, + addMat_v1, + addMat_v1, + addMat_v1, + addMat_v1 + }, + { + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + addMat_v1, + addMat_v1, + addMat_v1 + }, + { + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + addMat_v1, + addMat_v1 + }, + { + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + 0 /*addMat_v1*/, + addMat_v1 + } + }; + + const int sdepth = src1.depth(); + const int ddepth = dst.depth(); + + CV_DbgAssert( sdepth <= CV_64F && ddepth <= CV_64F ); + + GpuMat src1_ = src1.reshape(1); + GpuMat src2_ = src2.reshape(1); + GpuMat dst_ = dst.reshape(1); + + if (mask.empty() && (sdepth == CV_8U || sdepth == CV_16U) && ddepth == sdepth) + { + const intptr_t src1ptr = reinterpret_cast(src1_.data); + const intptr_t src2ptr = reinterpret_cast(src2_.data); + const intptr_t dstptr = reinterpret_cast(dst_.data); + + const bool isAllAligned = (src1ptr & 31) == 0 && (src2ptr & 31) == 0 && (dstptr & 31) == 0; + + if (isAllAligned) + { + if (sdepth == CV_8U && (src1_.cols & 3) == 0) + { + addMat_v4(src1_, src2_, dst_, stream); + return; + } + else if (sdepth == CV_16U && (src1_.cols & 1) == 0) + { + addMat_v2(src1_, src2_, dst_, stream); + return; + } + } + } + + const func_t func = funcs[sdepth][ddepth]; + + if (!func) + CV_Error(cv::Error::StsUnsupportedFormat, "Unsupported combination of source and destination types"); + + func(src1_, src2_, dst_, mask, stream); +} + +#endif diff --git a/modules/cudaarithm/src/cuda/add_scalar.cu b/modules/cudaarithm/src/cuda/add_scalar.cu new file mode 100644 index 00000000000..92838a2a57d --- /dev/null +++ b/modules/cudaarithm/src/cuda/add_scalar.cu @@ -0,0 +1,180 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "opencv2/opencv_modules.hpp" + +#ifndef HAVE_OPENCV_CUDEV + +#error "opencv_cudev is required" + +#else + +#include "opencv2/cudev.hpp" + +using namespace cv::cudev; + +void addScalar(const GpuMat& src, cv::Scalar val, bool, GpuMat& dst, const GpuMat& mask, double, Stream& stream, int); + +namespace +{ + template struct AddScalarOp : unary_function + { + ScalarType val; + + __device__ __forceinline__ DstType operator ()(SrcType a) const + { + return saturate_cast(saturate_cast(a) + val); + } + }; + + template struct TransformPolicy : DefaultTransformPolicy + { + }; + template <> struct TransformPolicy : DefaultTransformPolicy + { + enum { + shift = 1 + }; + }; + + template + void addScalarImpl(const GpuMat& src, cv::Scalar value, GpuMat& dst, const GpuMat& mask, Stream& stream) + { + typedef typename MakeVec::cn>::type ScalarType; + + cv::Scalar_ value_ = value; + + AddScalarOp op; + op.val = VecTraits::make(value_.val); + + if (mask.data) + gridTransformUnary_< TransformPolicy >(globPtr(src), globPtr(dst), op, globPtr(mask), stream); + else + gridTransformUnary_< TransformPolicy >(globPtr(src), globPtr(dst), op, stream); + } +} + +void addScalar(const GpuMat& src, cv::Scalar val, bool, GpuMat& dst, const GpuMat& mask, double, Stream& stream, int) +{ + typedef void (*func_t)(const GpuMat& src, cv::Scalar val, GpuMat& dst, const GpuMat& mask, Stream& stream); + static const func_t funcs[7][7][4] = + { + { + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl} + }, + { + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl} + }, + { + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl} + }, + { + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl} + }, + { + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl} + }, + { + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl} + }, + { + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/, 0 /*addScalarImpl*/}, + {addScalarImpl, addScalarImpl, addScalarImpl, addScalarImpl} + } + }; + + const int sdepth = src.depth(); + const int ddepth = dst.depth(); + const int cn = src.channels(); + + CV_DbgAssert( sdepth <= CV_64F && ddepth <= CV_64F && cn <= 4 ); + + const func_t func = funcs[sdepth][ddepth][cn - 1]; + + if (!func) + CV_Error(cv::Error::StsUnsupportedFormat, "Unsupported combination of source and destination types"); + + func(src, val, dst, mask, stream); +} + +#endif diff --git a/modules/cudaarithm/src/cuda/add_weighted.cu b/modules/cudaarithm/src/cuda/add_weighted.cu new file mode 100644 index 00000000000..929301076d3 --- /dev/null +++ b/modules/cudaarithm/src/cuda/add_weighted.cu @@ -0,0 +1,596 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "opencv2/opencv_modules.hpp" + +#ifndef HAVE_OPENCV_CUDEV + +#error "opencv_cudev is required" + +#else + +#include "opencv2/cudaarithm.hpp" +#include "opencv2/cudev.hpp" +#include "opencv2/core/private.cuda.hpp" + +using namespace cv; +using namespace cv::cuda; +using namespace cv::cudev; + +namespace +{ + template struct AddWeightedOp : binary_function + { + S alpha; + S beta; + S gamma; + + __device__ __forceinline__ D operator ()(T1 a, T2 b) const + { + return cudev::saturate_cast(a * alpha + b * beta + gamma); + } + }; + + template struct TransformPolicy : DefaultTransformPolicy + { + }; + template <> struct TransformPolicy : DefaultTransformPolicy + { + enum { + shift = 1 + }; + }; + + template + void addWeightedImpl(const GpuMat& src1, double alpha, const GpuMat& src2, double beta, double gamma, GpuMat& dst, Stream& stream) + { + typedef typename LargerType::type larger_type1; + typedef typename LargerType::type larger_type2; + typedef typename LargerType::type scalar_type; + + AddWeightedOp op; + op.alpha = static_cast(alpha); + op.beta = static_cast(beta); + op.gamma = static_cast(gamma); + + gridTransformBinary_< TransformPolicy >(globPtr(src1), globPtr(src2), globPtr(dst), op, stream); + } +} + +void cv::cuda::addWeighted(InputArray _src1, double alpha, InputArray _src2, double beta, double gamma, OutputArray _dst, int ddepth, Stream& stream) +{ + typedef void (*func_t)(const GpuMat& src1, double alpha, const GpuMat& src2, double beta, double gamma, GpuMat& dst, Stream& stream); + static const func_t funcs[7][7][7] = + { + { + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + } + }, + { + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + } + }, + { + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + } + }, + { + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + } + }, + { + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + } + }, + { + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + } + }, + { + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/, + 0/*addWeightedImpl*/ + }, + { + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl, + addWeightedImpl + } + } + }; + + GpuMat src1 = getInputMat(_src1, stream); + GpuMat src2 = getInputMat(_src2, stream); + + int sdepth1 = src1.depth(); + int sdepth2 = src2.depth(); + + ddepth = ddepth >= 0 ? CV_MAT_DEPTH(ddepth) : std::max(sdepth1, sdepth2); + const int cn = src1.channels(); + + CV_Assert( src2.size() == src1.size() && src2.channels() == cn ); + CV_Assert( sdepth1 <= CV_64F && sdepth2 <= CV_64F && ddepth <= CV_64F ); + + GpuMat dst = getOutputMat(_dst, src1.size(), CV_MAKE_TYPE(ddepth, cn), stream); + + GpuMat src1_single = src1.reshape(1); + GpuMat src2_single = src2.reshape(1); + GpuMat dst_single = dst.reshape(1); + + if (sdepth1 > sdepth2) + { + src1_single.swap(src2_single); + std::swap(alpha, beta); + std::swap(sdepth1, sdepth2); + } + + const func_t func = funcs[sdepth1][sdepth2][ddepth]; + + if (!func) + CV_Error(cv::Error::StsUnsupportedFormat, "Unsupported combination of source and destination types"); + + func(src1_single, alpha, src2_single, beta, gamma, dst_single, stream); + + syncOutput(dst, _dst, stream); +} + +#endif diff --git a/modules/cudaarithm/src/cuda/bitwise_mat.cu b/modules/cudaarithm/src/cuda/bitwise_mat.cu new file mode 100644 index 00000000000..f151c1a4862 --- /dev/null +++ b/modules/cudaarithm/src/cuda/bitwise_mat.cu @@ -0,0 +1,230 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "opencv2/opencv_modules.hpp" + +#ifndef HAVE_OPENCV_CUDEV + +#error "opencv_cudev is required" + +#else + +#include "opencv2/cudaarithm.hpp" +#include "opencv2/cudev.hpp" +#include "opencv2/core/private.cuda.hpp" + +using namespace cv; +using namespace cv::cuda; +using namespace cv::cudev; + +void bitMat(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, const GpuMat& mask, double, Stream& stream, int op); + +////////////////////////////////////////////////////////////////////////////// +/// bitwise_not + +void cv::cuda::bitwise_not(InputArray _src, OutputArray _dst, InputArray _mask, Stream& stream) +{ + GpuMat src = getInputMat(_src, stream); + GpuMat mask = getInputMat(_mask, stream); + + const int depth = src.depth(); + + CV_DbgAssert( depth <= CV_32F ); + CV_DbgAssert( mask.empty() || (mask.type() == CV_8UC1 && mask.size() == src.size()) ); + + GpuMat dst = getOutputMat(_dst, src.size(), src.type(), stream); + + if (mask.empty()) + { + const int bcols = (int) (src.cols * src.elemSize()); + + if ((bcols & 3) == 0) + { + const int vcols = bcols >> 2; + + GlobPtrSz vsrc = globPtr((uint*) src.data, src.step, src.rows, vcols); + GlobPtrSz vdst = globPtr((uint*) dst.data, dst.step, src.rows, vcols); + + gridTransformUnary(vsrc, vdst, bit_not(), stream); + } + else if ((bcols & 1) == 0) + { + const int vcols = bcols >> 1; + + GlobPtrSz vsrc = globPtr((ushort*) src.data, src.step, src.rows, vcols); + GlobPtrSz vdst = globPtr((ushort*) dst.data, dst.step, src.rows, vcols); + + gridTransformUnary(vsrc, vdst, bit_not(), stream); + } + else + { + GlobPtrSz vsrc = globPtr((uchar*) src.data, src.step, src.rows, bcols); + GlobPtrSz vdst = globPtr((uchar*) dst.data, dst.step, src.rows, bcols); + + gridTransformUnary(vsrc, vdst, bit_not(), stream); + } + } + else + { + if (depth == CV_32F || depth == CV_32S) + { + GlobPtrSz vsrc = globPtr((uint*) src.data, src.step, src.rows, src.cols * src.channels()); + GlobPtrSz vdst = globPtr((uint*) dst.data, dst.step, src.rows, src.cols * src.channels()); + + gridTransformUnary(vsrc, vdst, bit_not(), singleMaskChannels(globPtr(mask), src.channels()), stream); + } + else if (depth == CV_16S || depth == CV_16U) + { + GlobPtrSz vsrc = globPtr((ushort*) src.data, src.step, src.rows, src.cols * src.channels()); + GlobPtrSz vdst = globPtr((ushort*) dst.data, dst.step, src.rows, src.cols * src.channels()); + + gridTransformUnary(vsrc, vdst, bit_not(), singleMaskChannels(globPtr(mask), src.channels()), stream); + } + else + { + GlobPtrSz vsrc = globPtr((uchar*) src.data, src.step, src.rows, src.cols * src.channels()); + GlobPtrSz vdst = globPtr((uchar*) dst.data, dst.step, src.rows, src.cols * src.channels()); + + gridTransformUnary(vsrc, vdst, bit_not(), singleMaskChannels(globPtr(mask), src.channels()), stream); + } + } + + syncOutput(dst, _dst, stream); +} + +////////////////////////////////////////////////////////////////////////////// +/// Binary bitwise logical operations + +namespace +{ + template