diff --git a/CMakeLists.txt b/CMakeLists.txt index 696695995d..b4dc92e8dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -269,39 +269,93 @@ endif() # --- Python Support --- if(NOT IOS) - include(cmake/VISPDetectPython.cmake) + # Make sure to refresh the python interpreter every time we rerun cmake + # If we don't do this, we may use an old or invalid python when installing the bindings + # that was cached by a previous attempt at building + if(CMAKE_VERSION VERSION_LESS "3.15.0") + set(PYTHON3_CACHE_LIST + PYTHON3INTERP_FOUND PYTHONINTERP_FOUND PYTHONLIBS_FOUND PYTHON_FOUND + PYTHON3_EXECUTABLE PYTHON_EXECUTABLE + ) + foreach (_variableName ${PYTHON3_CACHE_LIST}) + unset(${_variableName} CACHE) + endforeach() + include(cmake/VISPDetectPython.cmake) + else() + set(PYTHON3_CACHE_LIST + Python3_FOUND Python3_EXECUTABLE Python3_Interpreter_FOUND Python3_LIBRARIES + _Python3_EXECUTABLE _Python3_INCLUDE_DIR _Python3_INTERPRETER_PROPERTIES _Python3_LIBRARY_RELEASE + ) + foreach (_variableName ${PYTHON3_CACHE_LIST}) + unset(${_variableName} CACHE) + endforeach() + # Find strategy + set(Python3_FIND_REGISTRY LAST) + set(Python3_FIND_VIRTUALENV FIRST) + set(Python3_FIND_STRATEGY LOCATION) + find_package (Python3 COMPONENTS Interpreter Development) + + # Alias variables to be consistent with previous detection method + set(PYTHON3_FOUND ${Python3_FOUND}) + set(PYTHON3_EXECUTABLE ${Python3_EXECUTABLE}) + set(PYTHON_DEFAULT_EXECUTABLE ${PYTHON3_EXECUTABLE}) + set(PYTHON3INTERP_FOUND ${Python3_Interpreter_FOUND}) + set(PYTHON3_VERSION_STRING ${Python3_VERSION}) + endif() endif() -if(CMAKE_VERSION VERSION_LESS "3.19.0") +# --- Python Bindings requirements --- +VP_OPTION(USE_PYBIND11 pybind11 QUIET "Include pybind11 to create Python bindings" "" ON) + +# Minimum tool versions +set(CMAKE_MINIMUM_VERSION_PYTHON_BINDINGS "3.19.0") +set(PYTHON3_MINIMUM_VERSION_PYTHON_BINDINGS "3.7.0") +if(CMAKE_VERSION VERSION_LESS ${CMAKE_MINIMUM_VERSION_PYTHON_BINDINGS}) set(CMAKE_NOT_OK_FOR_BINDINGS TRUE) + message(STATUS "Required CMake version for Python bindings is ${CMAKE_MINIMUM_VERSION_PYTHON_BINDINGS}, + but you have ${CMAKE_VERSION}. + Python bindings generation will be deactivated. + ") else() set(CMAKE_NOT_OK_FOR_BINDINGS FALSE) endif() -if(CMAKE_NOT_OK_FOR_BINDINGS) - status("${CMAKE_NOT_OK_FOR_BINDINGS}") - status("CMake version required for Python bindings is 3.19.0, but you have ${CMAKE_VERSION}. Python bindings generation will be deactivated") +if(PYTHON3_VERSION_STRING VERSION_LESS ${PYTHON3_MINIMUM_VERSION_PYTHON_BINDINGS}) + set(PYTHON3_NOT_OK_FOR_BINDINGS TRUE) + message(STATUS "Required Python version for Python bindings is ${PYTHON3_MINIMUM_VERSION_PYTHON_BINDINGS}, + but you have ${PYTHON3_VERSION_STRING}. + Python bindings generation will be deactivated. + ") +else() + set(PYTHON3_NOT_OK_FOR_BINDINGS FALSE) +endif() +if(VISP_CXX_STANDARD LESS VISP_CXX_STANDARD_17) + set(CXX_STANDARD_NOT_OK_FOR_BINDINGS TRUE) + message(STATUS "Required C++ standard is C++17, but you have ${VISP_CXX_STANDARD}") +else() + set(CXX_STANDARD_NOT_OK_FOR_BINDINGS FALSE) endif() -# --- Python Bindings requirements --- -# this avoids non-active conda from getting picked anyway on Windows -#set(Python_FIND_REGISTRY LAST) -# Use environment variable PATH to decide preference for Python -#set(Python_FIND_VIRTUALENV FIRST) -#set(Python_FIND_STRATEGY LOCATION) - -#find_package(Python 3.7 COMPONENTS Interpreter Development) # TODO: use visp function to find python? -#if(Python_FOUND) -# set(VISP_PYTHON_BINDINGS_EXECUTABLE "${Python_EXECUTABLE}") -#endif() -#find_package(pybind11) -VP_OPTION(USE_PYBIND11 pybind11 QUIET "Include pybind11 to create Python bindings" "" ON) -#if(pybind11_FOUND) -# set(VISP_PYBIND11_DIR "${pybind11_DIR}") -#endif() -#message("${pybind11_FOUND}") +# Forbid system Python +if(DEFINED ENV{VIRTUAL_ENV} OR DEFINED ENV{CONDA_PREFIX}) + set(_pip_args) + set(VISP_PYTHON_IS_SYSTEM_WIDE FALSE) +else() + # First solution: raise an error when cmake will call pip install + # set(_pip_args "--require-virtualenv") # If this is a system python, throw an error + if(PYTHON3_FOUND) + message(STATUS "The python version that you are using (${PYTHON3_EXECUTABLE}) is the system interpreter. + pip packages should not be installed system-wide! + Python bindings targets will be deactivated! + To reenable them, install conda or virtualenv, + delete the CMakeCache file then rerun cmake when inside the virtual environment. + ") + set(VISP_PYTHON_IS_SYSTEM_WIDE TRUE) + endif() +endif() + # --- @@ -447,7 +501,7 @@ VP_OPTION(BUILD_ANDROID_EXAMPLES "" "" "Build examples for Android platform" VP_OPTION(INSTALL_ANDROID_EXAMPLES "" "" "Install Android examples" "" OFF IF ANDROID ) # Build python bindings as an option -VP_OPTION(BUILD_PYTHON_BINDINGS "" "" "Build Python bindings" "" ON IF (PYTHON3INTERP_FOUND AND USE_PYBIND11 AND NOT CMAKE_NOT_OK_FOR_BINDINGS) ) +VP_OPTION(BUILD_PYTHON_BINDINGS "" "" "Build Python bindings" "" ON IF (PYTHON3INTERP_FOUND AND USE_PYBIND11 AND NOT CMAKE_NOT_OK_FOR_BINDINGS AND NOT VISP_PYTHON_IS_SYSTEM_WIDE AND NOT PYTHON3_NOT_OK_FOR_BINDINGS AND NOT CXX_STANDARD_NOT_OK_FOR_BINDINGS) ) VP_OPTION(BUILD_PYTHON_BINDINGS_DOC "" "" "Build the documentation for the Python bindings" "" ON IF BUILD_PYTHON_BINDINGS ) @@ -1623,8 +1677,17 @@ if(BUILD_PYTHON_BINDINGS) status(" Package version:" "${VISP_PYTHON_PACKAGE_VERSION}") status(" Wrapped modules:" "${VISP_PYTHON_BOUND_MODULES}") status(" Generated input config:" "${VISP_PYTHON_GENERATED_CONFIG_FILE}") +else() + status(" Requirements: ") + status(" Python version > ${PYTHON3_MINIMUM_VERSION_PYTHON_BINDINGS}:" PYTHON3_FOUND AND NOT PYTHON3_NOT_OK_FOR_BINDINGS THEN "ok (ver ${PYTHON3_VERSION_STRING})" ELSE "python not found or too old (${PYTHON3_VERSION_STRING})") + status(" Python in Virtual environment or conda:" VISP_PYTHON_IS_SYSTEM_WIDE THEN "failed" ELSE "ok") + status(" Pybind11 found:" USE_PYBIND11 THEN "ok" ELSE "failed") + status(" CMake > ${CMAKE_MINIMUM_VERSION_PYTHON_BINDINGS}:" CMAKE_NOT_OK_FOR_BINDINGS THEN "failed (${CMAKE_VERSION})" ELSE "ok (${CMAKE_VERSION})") + status(" C++ standard > ${VISP_CXX_STANDARD_17}:" CXX_STANDARD_NOT_OK_FOR_BINDINGS THEN "failed (${VISP_CXX_STANDARD})" ELSE "ok (${VISP_CXX_STANDARD})") + endif() + # ============================ Options =========================== status("") status(" Build options: ") diff --git a/modules/core/include/visp3/core/vpBSpline.h b/modules/core/include/visp3/core/vpBSpline.h index 29debfaf87..7181f7f491 100644 --- a/modules/core/include/visp3/core/vpBSpline.h +++ b/modules/core/include/visp3/core/vpBSpline.h @@ -57,7 +57,8 @@ - k indicates which kth derivative is computed. - value is the numerical value of \f$ N_{i,p}^k(u) \f$. */ -typedef struct vpBasisFunction { +typedef struct vpBasisFunction +{ unsigned int i; unsigned int p; double u; @@ -104,8 +105,8 @@ typedef struct vpBasisFunction { class VISP_EXPORT vpBSpline { - public /*protected*/: - //! Vector wich contains the control points +public /*protected*/: +//! Vector wich contains the control points std::vector controlPoints; //! Vector which contain the knots \f$ {u0, ..., um} \f$ std::vector knots; @@ -214,24 +215,24 @@ class VISP_EXPORT vpBSpline } } - static unsigned int findSpan(double l_u, unsigned int l_p, std::vector &l_knots); - unsigned int findSpan(double u); + static unsigned int findSpan(double l_u, unsigned int l_p, const std::vector &l_knots); + unsigned int findSpan(double u) const; static vpBasisFunction *computeBasisFuns(double l_u, unsigned int l_i, unsigned int l_p, - std::vector &l_knots); - vpBasisFunction *computeBasisFuns(double u); + const std::vector &l_knots); + vpBasisFunction *computeBasisFuns(double u) const; static vpBasisFunction **computeDersBasisFuns(double l_u, unsigned int l_i, unsigned int l_p, unsigned int l_der, - std::vector &l_knots); - vpBasisFunction **computeDersBasisFuns(double u, unsigned int der); + const std::vector &l_knots); + vpBasisFunction **computeDersBasisFuns(double u, unsigned int der) const; - static vpImagePoint computeCurvePoint(double l_u, unsigned int l_i, unsigned int l_p, std::vector &l_knots, - std::vector &l_controlPoints); - vpImagePoint computeCurvePoint(double u); + static vpImagePoint computeCurvePoint(double l_u, unsigned int l_i, unsigned int l_p, const std::vector &l_knots, + const std::vector &l_controlPoints); + vpImagePoint computeCurvePoint(double u) const; static vpImagePoint *computeCurveDers(double l_u, unsigned int l_i, unsigned int l_p, unsigned int l_der, - std::vector &l_knots, std::vector &l_controlPoints); - vpImagePoint *computeCurveDers(double u, unsigned int der); + const std::vector &l_knots, const std::vector &l_controlPoints); + vpImagePoint *computeCurveDers(double u, unsigned int der) const; }; #endif diff --git a/modules/core/include/visp3/core/vpCannyEdgeDetection.h b/modules/core/include/visp3/core/vpCannyEdgeDetection.h index cae0d4e109..b2548e5303 100644 --- a/modules/core/include/visp3/core/vpCannyEdgeDetection.h +++ b/modules/core/include/visp3/core/vpCannyEdgeDetection.h @@ -43,7 +43,6 @@ // 3rd parties include #ifdef VISP_HAVE_NLOHMANN_JSON #include -using json = nlohmann::json; //! json namespace shortcut #endif class VISP_EXPORT vpCannyEdgeDetection @@ -213,7 +212,7 @@ class VISP_EXPORT vpCannyEdgeDetection * \param[in] j : The JSON object, resulting from the parsing of a JSON file. * \param[out] detector : The detector that will be initialized from the JSON data. */ - friend inline void from_json(const json &j, vpCannyEdgeDetection &detector) + friend inline void from_json(const nlohmann::json &j, vpCannyEdgeDetection &detector) { std::string filteringAndGradientName = vpImageFilter::vpCannyFilteringAndGradientTypeToString(detector.m_filteringAndGradientType); filteringAndGradientName = j.value("filteringAndGradientType", filteringAndGradientName); @@ -233,10 +232,10 @@ class VISP_EXPORT vpCannyEdgeDetection * \param[out] j : A JSON parser object. * \param[in] detector : The vpCannyEdgeDetection object that must be parsed into JSON format. */ - friend inline void to_json(json &j, const vpCannyEdgeDetection &detector) + friend inline void to_json(nlohmann::json &j, const vpCannyEdgeDetection &detector) { std::string filteringAndGradientName = vpImageFilter::vpCannyFilteringAndGradientTypeToString(detector.m_filteringAndGradientType); - j = json { + j = nlohmann::json { {"filteringAndGradientType", filteringAndGradientName}, {"gaussianSize", detector.m_gaussianKernelSize}, {"gaussianStdev", detector.m_gaussianStdev}, diff --git a/modules/core/include/visp3/core/vpFrameGrabber.h b/modules/core/include/visp3/core/vpFrameGrabber.h index c92e2a6e33..b13af19f45 100644 --- a/modules/core/include/visp3/core/vpFrameGrabber.h +++ b/modules/core/include/visp3/core/vpFrameGrabber.h @@ -103,9 +103,9 @@ class VISP_EXPORT vpFrameGrabber /** @name Inherited functionalities from vpFramegrabber */ //@{ //! Return the number of rows in the image. - inline unsigned int getHeight() const { return height; } + unsigned int getHeight() const; //! Return the number of columns in the image. - inline unsigned int getWidth() const { return width; } + unsigned int getWidth() const; //@} public: diff --git a/modules/core/include/visp3/core/vpImageTools.h b/modules/core/include/visp3/core/vpImageTools.h index 58a910d885..d5afac7536 100644 --- a/modules/core/include/visp3/core/vpImageTools.h +++ b/modules/core/include/visp3/core/vpImageTools.h @@ -1133,7 +1133,7 @@ void vpImageTools::resize(const vpImage &I, vpImage &Ires, const vpI } } } - } +} #if defined(VISP_HAVE_SIMDLIB) template <> @@ -1511,24 +1511,24 @@ void vpImageTools::warpLinear(const vpImage &src, const vpMatrix &T, vpIma const float s = xi_ - x_; if (y_ < static_cast(src.getHeight()) - 1 && x_ < static_cast(src.getWidth()) - 1) { - const Type val00 = src[y_][x_]; - const Type val01 = src[y_][x_ + 1]; - const Type val10 = src[y_ + 1][x_]; - const Type val11 = src[y_ + 1][x_ + 1]; + const float val00 = static_cast(src[y_][x_]); + const float val01 = static_cast(src[y_][x_ + 1]); + const float val10 = static_cast(src[y_ + 1][x_]); + const float val11 = static_cast(src[y_ + 1][x_ + 1]); const float col0 = lerp(val00, val01, s); const float col1 = lerp(val10, val11, s); const float interp = lerp(col0, col1, t); dst[i][j] = vpMath::saturate(interp); } else if (y_ < static_cast(src.getHeight()) - 1) { - const Type val00 = src[y_][x_]; - const Type val10 = src[y_ + 1][x_]; + const float val00 = static_cast(src[y_][x_]); + const float val10 = static_cast(src[y_ + 1][x_]); const float interp = lerp(val00, val10, t); dst[i][j] = vpMath::saturate(interp); } else if (x_ < static_cast(src.getWidth()) - 1) { - const Type val00 = src[y_][x_]; - const Type val01 = src[y_][x_ + 1]; + const float val00 = static_cast(src[y_][x_]); + const float val01 = static_cast(src[y_][x_ + 1]); const float interp = lerp(val00, val01, s); dst[i][j] = vpMath::saturate(interp); } @@ -1583,24 +1583,24 @@ void vpImageTools::warpLinear(const vpImage &src, const vpMatrix &T, vpIma double t = y - y_lower; if (y_lower < static_cast(src.getHeight()) - 1 && x_lower < static_cast(src.getWidth()) - 1) { - const Type val00 = src[y_lower][x_lower]; - const Type val01 = src[y_lower][x_lower + 1]; - const Type val10 = src[y_lower + 1][x_lower]; - const Type val11 = src[y_lower + 1][x_lower + 1]; + const double val00 = static_cast(src[y_lower][x_lower]); + const double val01 = static_cast(src[y_lower][x_lower + 1]); + const double val10 = static_cast(src[y_lower + 1][x_lower]); + const double val11 = static_cast(src[y_lower + 1][x_lower + 1]); const double col0 = lerp(val00, val01, s); const double col1 = lerp(val10, val11, s); const double interp = lerp(col0, col1, t); dst[i][j] = vpMath::saturate(interp); } else if (y_lower < static_cast(src.getHeight()) - 1) { - const Type val00 = src[y_lower][x_lower]; - const Type val10 = src[y_lower + 1][x_lower]; + const double val00 = static_cast(src[y_lower][x_lower]); + const double val10 = static_cast(src[y_lower + 1][x_lower]); const double interp = lerp(val00, val10, t); dst[i][j] = vpMath::saturate(interp); } else if (x_lower < static_cast(src.getWidth()) - 1) { - const Type val00 = src[y_lower][x_lower]; - const Type val01 = src[y_lower][x_lower + 1]; + const double val00 = static_cast(src[y_lower][x_lower]); + const double val01 = static_cast(src[y_lower][x_lower + 1]); const double interp = lerp(val00, val01, s); dst[i][j] = vpMath::saturate(interp); } diff --git a/modules/core/include/visp3/core/vpMatrix.h b/modules/core/include/visp3/core/vpMatrix.h index 771c162f01..2f353d7712 100644 --- a/modules/core/include/visp3/core/vpMatrix.h +++ b/modules/core/include/visp3/core/vpMatrix.h @@ -1060,7 +1060,7 @@ class VISP_EXPORT vpMatrix : public vpArray2D /*! \deprecated You should rather use inverseByCholeskyLapack() or inverseByCholesky(). */ - vpMatrix inverseByCholeskyGsl() const + vp_deprecated vpMatrix inverseByCholeskyGsl() const { #if defined(VISP_HAVE_LAPACK) return inverseByCholeskyLapack(); @@ -1072,7 +1072,7 @@ class VISP_EXPORT vpMatrix : public vpArray2D /*! \deprecated You should rather use inverseByQRLapack() or inverseByQR(). */ - vpMatrix inverseByQRGsl() const + vp_deprecated vpMatrix inverseByQRGsl() const { #if defined(VISP_HAVE_LAPACK) return inverseByQRLapack(); @@ -1084,7 +1084,7 @@ class VISP_EXPORT vpMatrix : public vpArray2D /*! \deprecated You should rather use pseudoInverseLapack() or pseudoInverse(). */ - vpMatrix pseudoInverseGsl(double svThreshold = 1e-6) const + vp_deprecated vpMatrix pseudoInverseGsl(double svThreshold = 1e-6) const { #if defined(VISP_HAVE_LAPACK) return pseudoInverseLapack(svThreshold); @@ -1097,7 +1097,7 @@ class VISP_EXPORT vpMatrix : public vpArray2D /*! \deprecated You should rather use pseudoInverseLapack() or pseudoInverse(). */ - unsigned int pseudoInverseGsl(vpMatrix &Ap, double svThreshold = 1e-6) const + vp_deprecated unsigned int pseudoInverseGsl(vpMatrix &Ap, double svThreshold = 1e-6) const { #if defined(VISP_HAVE_LAPACK) return pseudoInverseLapack(Ap, svThreshold); @@ -1111,7 +1111,7 @@ class VISP_EXPORT vpMatrix : public vpArray2D /*! \deprecated You should rather use pseudoInverseLapack() or pseudoInverse(). */ - unsigned int pseudoInverseGsl(vpMatrix &Ap, vpColVector &sv, double svThreshold = 1e-6) const + vp_deprecated unsigned int pseudoInverseGsl(vpMatrix &Ap, vpColVector &sv, double svThreshold = 1e-6) const { #if defined(VISP_HAVE_LAPACK) return pseudoInverseLapack(Ap, sv, svThreshold); @@ -1126,7 +1126,7 @@ class VISP_EXPORT vpMatrix : public vpArray2D /*! \deprecated You should rather use pseudoInverseLapack() or pseudoInverse(). */ - unsigned int pseudoInverseGsl(vpMatrix &Ap, vpColVector &sv, double svThreshold, vpMatrix &imA, vpMatrix &imAt, + vp_deprecated unsigned int pseudoInverseGsl(vpMatrix &Ap, vpColVector &sv, double svThreshold, vpMatrix &imA, vpMatrix &imAt, vpMatrix &kerAt) const { #if defined(VISP_HAVE_LAPACK) @@ -1145,7 +1145,7 @@ class VISP_EXPORT vpMatrix : public vpArray2D /*! \deprecated You should rather use svdLapack() or svd(). */ - void svdGsl(vpColVector &w, vpMatrix &V) + vp_deprecated void svdGsl(vpColVector &w, vpMatrix &V) { #if defined(VISP_HAVE_LAPACK) svdLapack(w, V); diff --git a/modules/core/include/visp3/core/vpRequest.h b/modules/core/include/visp3/core/vpRequest.h index 41aa8c521a..20939485e7 100644 --- a/modules/core/include/visp3/core/vpRequest.h +++ b/modules/core/include/visp3/core/vpRequest.h @@ -133,9 +133,9 @@ class VISP_EXPORT vpRequest vpRequest(); virtual ~vpRequest(); - void addParameter(char *params); - void addParameter(std::string ¶ms); - void addParameter(std::vector &listOfparams); + void addParameter(const char *params); + void addParameter(const std::string ¶ms); + void addParameter(const std::vector &listOfparams); template void addParameterObject(T *params, const int &sizeOfObject = sizeof(T)); /*! @@ -178,7 +178,7 @@ class VISP_EXPORT vpRequest \return ID of the request. */ - std::string getId() { return request_id; } + std::string getId() const { return request_id; } /*! Change the ID of the request. @@ -194,7 +194,7 @@ class VISP_EXPORT vpRequest \return Number of parameters. */ - unsigned int size() { return (unsigned int)listOfParams.size(); } + unsigned int size() const { return (unsigned int)listOfParams.size(); } }; //######## Definition of Template Functions ######## diff --git a/modules/core/include/visp3/core/vpServer.h b/modules/core/include/visp3/core/vpServer.h index b2c06cd1c5..2ab89b9ded 100644 --- a/modules/core/include/visp3/core/vpServer.h +++ b/modules/core/include/visp3/core/vpServer.h @@ -185,7 +185,7 @@ class VISP_EXPORT vpServer : public vpNetwork \return True if the server is started, false otherwise. */ - bool isStarted() { return started; } + bool isStarted() const { return started; } /*! Get the maximum number of clients that can be connected to the server. @@ -194,14 +194,14 @@ class VISP_EXPORT vpServer : public vpNetwork \return Maximum number of clients. */ - unsigned int getMaxNumberOfClients() { return max_clients; } + unsigned int getMaxNumberOfClients() const { return max_clients; } /*! Get the number of clients connected to the server. \return Number of clients connected. */ - unsigned int getNumberOfClients() { return (unsigned int)receptor_list.size(); } + unsigned int getNumberOfClients() const { return (unsigned int)receptor_list.size(); } void print(); @@ -214,7 +214,7 @@ class VISP_EXPORT vpServer : public vpNetwork \param l : Maximum number of clients. */ - void setMaxNumberOfClients(unsigned int &l) { max_clients = l; } + void setMaxNumberOfClients(const unsigned int &l) { max_clients = l; } }; #endif diff --git a/modules/core/src/framegrabber/vpFrameGrabber.cpp b/modules/core/src/framegrabber/vpFrameGrabber.cpp new file mode 100644 index 0000000000..18caf7780c --- /dev/null +++ b/modules/core/src/framegrabber/vpFrameGrabber.cpp @@ -0,0 +1,45 @@ +/* + * ViSP, open source Visual Servoing Platform software. + * Copyright (C) 2005 - 2023 by Inria. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact Inria about acquiring a ViSP Professional + * Edition License. + * + * See https://visp.inria.fr for more information. + * + * This software was developed at: + * Inria Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * + * If you have questions regarding the use of this file, please contact + * Inria at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Frame grabbing. + */ + + +#include + +unsigned int vpFrameGrabber::getHeight() const +{ + return height; +} + +unsigned int vpFrameGrabber::getWidth() const +{ + return width; +} \ No newline at end of file diff --git a/modules/core/src/image/vpCannyEdgeDetection.cpp b/modules/core/src/image/vpCannyEdgeDetection.cpp index 3241678ad2..22758f9439 100644 --- a/modules/core/src/image/vpCannyEdgeDetection.cpp +++ b/modules/core/src/image/vpCannyEdgeDetection.cpp @@ -91,6 +91,9 @@ vpCannyEdgeDetection::vpCannyEdgeDetection(const int &gaussianKernelSize, const } #ifdef VISP_HAVE_NLOHMANN_JSON + +using json = nlohmann::json; + vpCannyEdgeDetection::vpCannyEdgeDetection(const std::string &jsonPath) { initFromJSON(jsonPath); diff --git a/modules/core/src/math/misc/vpStatisticalTestShewhart.cpp b/modules/core/src/math/misc/vpStatisticalTestShewhart.cpp index f514b7be8c..811d6a9d8e 100644 --- a/modules/core/src/math/misc/vpStatisticalTestShewhart.cpp +++ b/modules/core/src/math/misc/vpStatisticalTestShewhart.cpp @@ -43,6 +43,7 @@ #include +const int vpStatisticalTestShewhart::NB_DATA_SIGNAL; const bool vpStatisticalTestShewhart::CONST_ALL_WECO_ACTIVATED[vpStatisticalTestShewhart::COUNT_WECO - 1] = { true, true, true, true }; std::string vpStatisticalTestShewhart::vpWecoRulesAlarmToString(const vpStatisticalTestShewhart::vpWecoRulesAlarm &alarm) diff --git a/modules/core/src/math/spline/vpBSpline.cpp b/modules/core/src/math/spline/vpBSpline.cpp index 408757f339..4f4b16cfb1 100644 --- a/modules/core/src/math/spline/vpBSpline.cpp +++ b/modules/core/src/math/spline/vpBSpline.cpp @@ -76,7 +76,7 @@ vpBSpline::~vpBSpline() { } \return the number of the knot interval in which \f$ l_u \f$ lies. */ -unsigned int vpBSpline::findSpan(double l_u, unsigned int l_p, std::vector &l_knots) +unsigned int vpBSpline::findSpan(double l_u, unsigned int l_p, const std::vector &l_knots) { unsigned int m = (unsigned int)l_knots.size() - 1; @@ -120,7 +120,7 @@ unsigned int vpBSpline::findSpan(double l_u, unsigned int l_p, std::vector &l_knots) + const std::vector &l_knots) { vpBasisFunction *N = new vpBasisFunction[l_p + 1]; @@ -190,7 +190,7 @@ vpBasisFunction *vpBSpline::computeBasisFuns(double l_u, unsigned int l_i, unsig \return An array containing the nonvanishing basis functions at \f$ u \f$. The size of the array is \f$ p +1 \f$. */ -vpBasisFunction *vpBSpline::computeBasisFuns(double u) +vpBasisFunction *vpBSpline::computeBasisFuns(double u) const { unsigned int i = findSpan(u); return computeBasisFuns(u, i, p, knots); @@ -226,7 +226,7 @@ vpBasisFunction *vpBSpline::computeBasisFuns(double u) functions. return[k] is the list of the kth derivatives. */ vpBasisFunction **vpBSpline::computeDersBasisFuns(double l_u, unsigned int l_i, unsigned int l_p, unsigned int l_der, - std::vector &l_knots) + const std::vector &l_knots) { vpBasisFunction **N; N = new vpBasisFunction *[l_der + 1]; @@ -355,7 +355,7 @@ vpBasisFunction **vpBSpline::computeDersBasisFuns(double l_u, unsigned int l_i, Example : return[0] is the list of the 0th derivatives ie the basis functions. return[k] is the list of the kth derivatives. */ -vpBasisFunction **vpBSpline::computeDersBasisFuns(double u, unsigned int der) +vpBasisFunction **vpBSpline::computeDersBasisFuns(double u, unsigned int der) const { unsigned int i = findSpan(u); return computeDersBasisFuns(u, i, p, der, knots); @@ -372,8 +372,8 @@ vpBasisFunction **vpBSpline::computeDersBasisFuns(double u, unsigned int der) return the coordinates of a point corresponding to the knot \f$ u \f$. */ -vpImagePoint vpBSpline::computeCurvePoint(double l_u, unsigned int l_i, unsigned int l_p, std::vector &l_knots, - std::vector &l_controlPoints) +vpImagePoint vpBSpline::computeCurvePoint(double l_u, unsigned int l_i, unsigned int l_p, const std::vector &l_knots, + const std::vector &l_controlPoints) { vpBasisFunction *N = computeBasisFuns(l_u, l_i, l_p, l_knots); vpImagePoint pt; @@ -401,7 +401,7 @@ vpImagePoint vpBSpline::computeCurvePoint(double l_u, unsigned int l_i, unsigned return the coordinates of a point corresponding to the knot \f$ u \f$. */ -vpImagePoint vpBSpline::computeCurvePoint(double u) +vpImagePoint vpBSpline::computeCurvePoint(double u) const { vpBasisFunction *N = computeBasisFuns(u); vpImagePoint pt; @@ -443,7 +443,7 @@ vpImagePoint vpBSpline::computeCurvePoint(double u) the array. */ vpImagePoint *vpBSpline::computeCurveDers(double l_u, unsigned int l_i, unsigned int l_p, unsigned int l_der, - std::vector &l_knots, std::vector &l_controlPoints) + const std::vector &l_knots, const std::vector &l_controlPoints) { vpImagePoint *derivate = new vpImagePoint[l_der + 1]; vpBasisFunction **N; @@ -489,7 +489,7 @@ vpImagePoint *vpBSpline::computeCurveDers(double l_u, unsigned int l_i, unsigned for \f$ k = 0, ... , der \f$. The kth derivative is in the kth cell of the array. */ -vpImagePoint *vpBSpline::computeCurveDers(double u, unsigned int der) +vpImagePoint *vpBSpline::computeCurveDers(double u, unsigned int der) const { vpImagePoint *derivate = new vpImagePoint[der + 1]; vpBasisFunction **N; diff --git a/modules/core/src/tools/network/vpRequest.cpp b/modules/core/src/tools/network/vpRequest.cpp index 80d23fa814..bc3fd7b313 100644 --- a/modules/core/src/tools/network/vpRequest.cpp +++ b/modules/core/src/tools/network/vpRequest.cpp @@ -38,9 +38,9 @@ #include -vpRequest::vpRequest() : request_id(""), listOfParams() {} +vpRequest::vpRequest() : request_id(""), listOfParams() { } -vpRequest::~vpRequest() {} +vpRequest::~vpRequest() { } /*! Add a message as parameter of the request. @@ -49,7 +49,7 @@ vpRequest::~vpRequest() {} \param params : Array of characters representing the message to add. */ -void vpRequest::addParameter(char *params) +void vpRequest::addParameter(const char *params) { std::string val = params; listOfParams.push_back(val); @@ -62,7 +62,7 @@ void vpRequest::addParameter(char *params) \param params : std::string representing the message to add. */ -void vpRequest::addParameter(std::string ¶ms) { listOfParams.push_back(params); } +void vpRequest::addParameter(const std::string ¶ms) { listOfParams.push_back(params); } /*! Add messages as parameters of the request. @@ -72,8 +72,9 @@ void vpRequest::addParameter(std::string ¶ms) { listOfParams.push_back(param \param listOfparams : Array of std::string representing the messages to add. */ -void vpRequest::addParameter(std::vector &listOfparams) +void vpRequest::addParameter(const std::vector &listOfparams) { - for (unsigned int i = 0; i < listOfparams.size(); i++) - listOfparams.push_back(listOfparams[i]); + for (unsigned int i = 0; i < listOfparams.size(); i++) { + this->listOfParams.push_back(listOfparams[i]); + } } diff --git a/modules/detection/include/visp3/detection/vpDetectorDNNOpenCV.h b/modules/detection/include/visp3/detection/vpDetectorDNNOpenCV.h index 87606ba1cf..fa20745bef 100644 --- a/modules/detection/include/visp3/detection/vpDetectorDNNOpenCV.h +++ b/modules/detection/include/visp3/detection/vpDetectorDNNOpenCV.h @@ -54,7 +54,6 @@ #include #ifdef VISP_HAVE_NLOHMANN_JSON #include -using json = nlohmann::json; //! json namespace shortcut #endif /*! @@ -201,7 +200,7 @@ class VISP_EXPORT vpDetectorDNNOpenCV * \param j The JSON object, resulting from the parsing of a JSON file. * \param config The configuration of the network, that will be initialized from the JSON data. */ - friend inline void from_json(const json &j, NetConfig &config) + friend inline void from_json(const nlohmann::json &j, NetConfig &config) { config.m_confThreshold = j.value("confidenceThreshold", config.m_confThreshold); if (config.m_confThreshold <= 0) { @@ -241,11 +240,11 @@ class VISP_EXPORT vpDetectorDNNOpenCV * \param j A JSON parser object. * \param config The vpDetectorDNNOpenCV::NetConfig that must be parsed into JSON format. */ - friend inline void to_json(json &j, const NetConfig &config) + friend inline void to_json(nlohmann::json &j, const NetConfig &config) { std::pair resolution = { config.m_inputSize.width, config.m_inputSize.height }; std::vector v_mean = { config.m_mean[0], config.m_mean[1], config.m_mean[2] }; - j = json { + j = nlohmann::json { {"confidenceThreshold", config.m_confThreshold } , {"nmsThreshold" , config.m_nmsThreshold } , {"filterSizeRatio" , config.m_filterSizeRatio} , @@ -515,7 +514,7 @@ class VISP_EXPORT vpDetectorDNNOpenCV * \param j The JSON object, resulting from the parsing of a JSON file. * \param network The network, that will be initialized from the JSON data. */ - friend inline void from_json(const json &j, vpDetectorDNNOpenCV &network) + friend inline void from_json(const nlohmann::json &j, vpDetectorDNNOpenCV &network) { network.m_netConfig = j.value("networkSettings", network.m_netConfig); } @@ -526,9 +525,9 @@ class VISP_EXPORT vpDetectorDNNOpenCV * \param j The JSON parser. * \param network The network we want to parse the configuration. */ - friend inline void to_json(json &j, const vpDetectorDNNOpenCV &network) + friend inline void to_json(nlohmann::json &j, const vpDetectorDNNOpenCV &network) { - j = json { + j = nlohmann::json { {"networkSettings", network.m_netConfig} }; } diff --git a/modules/detection/src/dnn/vpDetectorDNNOpenCV.cpp b/modules/detection/src/dnn/vpDetectorDNNOpenCV.cpp index bb17b37a4a..f9ad489009 100644 --- a/modules/detection/src/dnn/vpDetectorDNNOpenCV.cpp +++ b/modules/detection/src/dnn/vpDetectorDNNOpenCV.cpp @@ -170,6 +170,9 @@ vpDetectorDNNOpenCV::vpDetectorDNNOpenCV(const NetConfig &config, const DNNResul } #ifdef VISP_HAVE_NLOHMANN_JSON + +using json = nlohmann::json; + /** * \brief Construct a new vpDetectorDNNOpenCV object from a JSON file and a potential parsing method. * diff --git a/modules/gui/src/display/windows/vpDisplayWin32.cpp b/modules/gui/src/display/windows/vpDisplayWin32.cpp index a8d5013761..551fe65770 100644 --- a/modules/gui/src/display/windows/vpDisplayWin32.cpp +++ b/modules/gui/src/display/windows/vpDisplayWin32.cpp @@ -57,10 +57,24 @@ void vpCreateWindow(threadParam *param) } /*! - Constructor. + Constructors. */ vpDisplayWin32::vpDisplayWin32(vpWin32Renderer *rend) : iStatus(false), window(rend) { } +vpDisplayWin32::vpDisplayWin32(vpImage &I, int winx, int winy, const std::string &title) + : iStatus(false), window(nullptr) +{ + init(I, winx, winy, title); +} + +vpDisplayWin32::vpDisplayWin32(vpImage &I, int winx, int winy, const std::string &title) + : iStatus(false), window(nullptr) +{ + init(I, winx, winy, title); +} + + + /*! Destructor. */ diff --git a/modules/imgproc/include/visp3/imgproc/vpCircleHoughTransform.h b/modules/imgproc/include/visp3/imgproc/vpCircleHoughTransform.h index 8eefb325cf..d2c489b60a 100644 --- a/modules/imgproc/include/visp3/imgproc/vpCircleHoughTransform.h +++ b/modules/imgproc/include/visp3/imgproc/vpCircleHoughTransform.h @@ -45,7 +45,6 @@ // 3rd parties inclue #ifdef VISP_HAVE_NLOHMANN_JSON #include -using json = nlohmann::json; #endif #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_17) @@ -496,6 +495,8 @@ class VISP_EXPORT vpCircleHoughTransform */ inline static vpCircleHoughTransformParameters createFromJSON(const std::string &jsonFile) { + using json = nlohmann::json; + std::ifstream file(jsonFile); if (!file.good()) { std::stringstream ss; @@ -528,6 +529,7 @@ class VISP_EXPORT vpCircleHoughTransform */ inline void saveConfigurationInJSON(const std::string &jsonPath) const { + using json = nlohmann::json; std::ofstream file(jsonPath); const json j = *this; file << j.dump(4); @@ -541,7 +543,7 @@ class VISP_EXPORT vpCircleHoughTransform * \param[in] j : The JSON object, resulting from the parsing of a JSON file. * \param[out] params : The circle Hough transform parameters that will be initialized from the JSON data. */ - inline friend void from_json(const json &j, vpCircleHoughTransformParameters ¶ms) + friend inline void from_json(const nlohmann::json &j, vpCircleHoughTransformParameters ¶ms) { std::string filteringAndGradientName = vpImageFilter::vpCannyFilteringAndGradientTypeToString(params.m_filteringAndGradientType); filteringAndGradientName = j.value("filteringAndGradientType", filteringAndGradientName); @@ -619,11 +621,11 @@ class VISP_EXPORT vpCircleHoughTransform * \param[out] j : A JSON parser object. * \param[in] params : The circle Hough transform parameters that will be serialized in the json object. */ - inline friend void to_json(json &j, const vpCircleHoughTransformParameters ¶ms) + friend inline void to_json(nlohmann::json &j, const vpCircleHoughTransformParameters ¶ms) { std::pair radiusLimits = { params.m_minRadius, params.m_maxRadius }; - j = json { + j = nlohmann::json { {"filteringAndGradientType", vpImageFilter::vpCannyFilteringAndGradientTypeToString(params.m_filteringAndGradientType)}, {"gaussianKernelSize", params.m_gaussianKernelSize}, {"gaussianStdev", params.m_gaussianStdev}, @@ -760,7 +762,7 @@ class VISP_EXPORT vpCircleHoughTransform * \param[in] j The JSON object, resulting from the parsing of a JSON file. * \param[out] detector The detector, that will be initialized from the JSON data. */ - inline friend void from_json(const json &j, vpCircleHoughTransform &detector) + friend inline void from_json(const nlohmann::json &j, vpCircleHoughTransform &detector) { detector.m_algoParams = j; } @@ -771,7 +773,7 @@ class VISP_EXPORT vpCircleHoughTransform * \param[out] j A JSON parser object. * \param[in] detector The vpCircleHoughTransform that must be parsed into JSON format. */ - inline friend void to_json(json &j, const vpCircleHoughTransform &detector) + friend inline void to_json(nlohmann::json &j, const vpCircleHoughTransform &detector) { j = detector.m_algoParams; } @@ -1160,8 +1162,8 @@ class VISP_EXPORT vpCircleHoughTransform */ friend VISP_EXPORT std::ostream &operator<<(std::ostream &os, const vpCircleHoughTransform &detector); - static const unsigned char edgeMapOn = 255; - static const unsigned char edgeMapOff = 0; + static const unsigned char edgeMapOn; + static const unsigned char edgeMapOff; protected: /** diff --git a/modules/imgproc/src/vpCircleHoughTransform.cpp b/modules/imgproc/src/vpCircleHoughTransform.cpp index 70aeb1933b..f111de36a3 100644 --- a/modules/imgproc/src/vpCircleHoughTransform.cpp +++ b/modules/imgproc/src/vpCircleHoughTransform.cpp @@ -33,6 +33,10 @@ #include +// Static variables +const unsigned char vpCircleHoughTransform::edgeMapOn = 255; +const unsigned char vpCircleHoughTransform::edgeMapOff = 0; + #if (VISP_CXX_STANDARD == VISP_CXX_STANDARD_98) namespace { @@ -119,6 +123,8 @@ vpCircleHoughTransform::~vpCircleHoughTransform() { } #ifdef VISP_HAVE_NLOHMANN_JSON +using json = nlohmann::json; + vpCircleHoughTransform::vpCircleHoughTransform(const std::string &jsonPath) { initFromJSON(jsonPath); @@ -249,7 +255,7 @@ vpCircleHoughTransform::detect(const vpImage &I, const int &nbCir auto hasBetterProba = [](std::pair a, std::pair b) { return (a.second > b.second); - }; + }; #endif std::sort(v_id_proba.begin(), v_id_proba.end(), hasBetterProba); @@ -691,19 +697,19 @@ vpCircleHoughTransform::computeCenterCandidates() const int &offsetX, const int &offsetY, const int &nbCols, const int &nbRows, vpImage &accum, bool &hasToStop) { - if (((x - offsetX) < 0) || - ((x - offsetX) >= nbCols) || - ((y - offsetY) < 0) || - ((y - offsetY) >= nbRows) - ) { - hasToStop = true; - } - else { - float dx = (x_orig - static_cast(x)); - float dy = (y_orig - static_cast(y)); - accum[y - offsetY][x - offsetX] += std::abs(dx) + std::abs(dy); - } - }; + if (((x - offsetX) < 0) || + ((x - offsetX) >= nbCols) || + ((y - offsetY) < 0) || + ((y - offsetY) >= nbRows) + ) { + hasToStop = true; + } + else { + float dx = (x_orig - static_cast(x)); + float dy = (y_orig - static_cast(y)); + accum[y - offsetY][x - offsetX] += std::abs(dx) + std::abs(dy); + } + }; #endif updateAccumulator(x1, y1, x_low, y_low, @@ -848,8 +854,8 @@ vpCircleHoughTransform::computeCenterCandidates() #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11) auto sortingCenters = [](const std::pair, float> &position_vote_a, const std::pair, float> &position_vote_b) { - return position_vote_a.second > position_vote_b.second; - }; + return position_vote_a.second > position_vote_b.second; + }; #endif std::sort(merged_peaks_position_votes.begin(), merged_peaks_position_votes.end(), sortingCenters); @@ -985,7 +991,7 @@ vpCircleHoughTransform::computeCircleCandidates() r_effective = weigthedSumRadius / votes; } return r_effective; - }; + }; #endif // Merging similar candidates diff --git a/modules/python/CMakeLists.txt b/modules/python/CMakeLists.txt index 5762537873..ef6ea0766c 100644 --- a/modules/python/CMakeLists.txt +++ b/modules/python/CMakeLists.txt @@ -44,13 +44,6 @@ find_package(VISP REQUIRED) # TODO: check for pip -# Set pip args -if(DEFINED ENV{VIRTUAL_ENV} OR DEFINED ENV{CONDA_PREFIX}) - set(_pip_args) -else() - set(_pip_args "--user") -endif() - # Step 1: Generate configuration file # Define modules for which to generate python bindings set(python_ignored_modules "visp_python" "visp_java_bindings_generator" "visp_java" ) @@ -126,6 +119,9 @@ if(BUILD_PYTHON_BINDINGS_DOC) add_subdirectory(doc) endif() +# Step 6: Test bindings +add_subdirectory(test) + # Export Variables to parent cmake set(VISP_PYTHON_BOUND_MODULES "") diff --git a/modules/python/GenerateConfig.cmake b/modules/python/GenerateConfig.cmake index 9281405e0b..b70add884b 100644 --- a/modules/python/GenerateConfig.cmake +++ b/modules/python/GenerateConfig.cmake @@ -115,6 +115,9 @@ endif() # OS if(WIN32) string(JSON json_defines SET ${json_defines} "_WIN32" "null") + string(JSON json_defines SET ${json_defines} "DWORD" "\"uint64_t\"") + string(JSON json_defines SET ${json_defines} "WINAPI" "\"__stdcall\"") + string(JSON json_defines SET ${json_defines} "LPVOID" "\"void*\"") endif() if(UNIX) string(JSON json_defines SET ${json_defines} "__linux__" "null") diff --git a/modules/python/bindings/CMakeLists.txt b/modules/python/bindings/CMakeLists.txt index 644d148970..9f62524a58 100644 --- a/modules/python/bindings/CMakeLists.txt +++ b/modules/python/bindings/CMakeLists.txt @@ -38,18 +38,34 @@ set_source_files_properties(${python_bindings_cpp_src} PROPERTIES GENERATED TRUE pybind11_add_module(_visp ${python_bindings_cpp_src}) -# Place library in binary/visp dir so that it doesn't pollute lib dir +# Place library in build/modules/python/bindings dir so that it doesn't pollute lib dir # This .so file is not treated the same as the others and we shouldn't link against it when compiling in C++ -# when installing the python module, pip will look into the "visp" subfolder for .so files to copy into the site-packages - +# when installing the python module, pip will look into this subfolder for .so files to copy into the site-packages file(MAKE_DIRECTORY "${bindings_gen_location}/src") + set_target_properties(_visp PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + ) +# With MSVC, the compiled pyd file is placed in a Release/Debug folder +set(build_configs "NONE" "RELEASE" "DEBUG" "RELEASEWITHDEBINFO" "RELWITHDEBINFO") +foreach(imp_config ${build_configs}) +set_target_properties(_visp PROPERTIES + LIBRARY_OUTPUT_DIRECTORY_${imp_config} "${CMAKE_CURRENT_BINARY_DIR}/visp" +) +endforeach() + +foreach(visp_lib ${VISP_LIBRARIES}) + get_target_property(dir ${visp_lib} LIBRARY_OUTPUT_DIRECTORY) + get_target_property(n ${visp_lib} OUTPUT_NAME) +endforeach() + +set_target_properties(_visp PROPERTIES EXCLUDE_FROM_ALL TRUE) -target_include_directories(_visp PRIVATE include) # Include directory containing custom bindings -target_include_directories(_visp PRIVATE ${VISP_INCLUDE_DIRS}) -target_link_libraries(_visp PRIVATE ${VISP_LIBRARIES}) +target_include_directories(_visp PUBLIC include) # Include directory containing custom bindings +target_include_directories(_visp PUBLIC ${VISP_INCLUDE_DIRS}) +target_link_libraries(_visp PUBLIC ${VISP_LIBRARIES}) add_dependencies(_visp visp_python_bindings_generator_run) # Setup pip install diff --git a/modules/python/bindings/include/blob.hpp b/modules/python/bindings/include/blob.hpp index 5dd634a473..e64420faf4 100644 --- a/modules/python/bindings/include/blob.hpp +++ b/modules/python/bindings/include/blob.hpp @@ -37,7 +37,7 @@ #include #include #include - +#include #include #include diff --git a/modules/python/bindings/include/core/arrays.hpp b/modules/python/bindings/include/core/arrays.hpp index 7c862728c2..6e365e9da4 100644 --- a/modules/python/bindings/include/core/arrays.hpp +++ b/modules/python/bindings/include/core/arrays.hpp @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -92,6 +93,50 @@ py::buffer_info get_buffer_info(vpHomogeneousMatrix &array) return make_array_buffer(array.data, { array.getRows(), array.getCols() }, true); } +/* +* Print helpers +*/ + +const char *matlab_str_help = R"doc( + Returns the Matlab representation of this data array (see matlabPrint in the C++ documentation) +)doc"; +const char *csv_str_help = R"doc( + Returns the CSV representation of this data array (see csvPrint in the C++ documentation) +)doc"; +const char *maple_str_help = R"doc( + Returns the CSV representation of this data array (see maplePrint in the C++ documentation) +)doc"; + +const char *cpp_str_help = R"doc( + Returns a C++ code representation of this data array (see cppPrint in the C++ documentation) + + :param name: variable name of the matrix. + :param byte_per_byte: Whether to print byte per byte defaults to false. +)doc"; + +template +void add_print_helper(PybindClass &pyCls, std::ostream &(T:: *fn)(std::ostream &) const, const S pythonName, const char *help) +{ + pyCls.def(pythonName, [fn](const T &self) -> std::string { + std::stringstream ss; + (self.*fn)(ss); + return ss.str(); + }, help); +} + +template +void add_cpp_print_helper(PybindClass &pyCls, std::ostream &(T:: *fn)(std::ostream &, const std::string &, bool) const) +{ + pyCls.def("strCppCode", [fn](const T &self, const std::string &name = "A", bool byte_per_byte = false) -> std::string { + std::stringstream ss; + (self.*fn)(ss, name, byte_per_byte); + return ss.str(); + }, cpp_str_help, py::arg("name"), py::arg("byte_per_byte") = false); +} + + + + /* * Array 2D indexing */ @@ -183,7 +228,7 @@ void bindings_vpArray2D(py::class_> &pyArray2D) pyArray2D.def(py::init([](np_array_cf &np_array) { verify_array_shape_and_dims(np_array, 2, "ViSP 2D array"); - const std::vector shape = np_array.request().shape; + const std::vector shape = np_array.request().shape; vpArray2D result(shape[0], shape[1]); copy_data_from_np(np_array, result.data); return result; @@ -207,7 +252,7 @@ void bindings_vpMatrix(py::class_> &pyMatrix) pyMatrix.def(py::init([](np_array_cf np_array) { verify_array_shape_and_dims(np_array, 2, "ViSP Matrix"); - const std::vector shape = np_array.request().shape; + const std::vector shape = np_array.request().shape; vpMatrix result(shape[0], shape[1]); copy_data_from_np(np_array, result.data); return result; @@ -218,6 +263,11 @@ Construct a matrix by **copying** a 2D numpy array. )doc", py::arg("np_array")); + add_print_helper(pyMatrix, &vpMatrix::csvPrint, "strCsv", csv_str_help); + add_print_helper(pyMatrix, &vpMatrix::maplePrint, "strMaple", maple_str_help); + add_print_helper(pyMatrix, &vpMatrix::matlabPrint, "strMatlab", matlab_str_help); + add_cpp_print_helper(pyMatrix, &vpMatrix::cppPrint); + define_get_item_2d_array>, vpMatrix, double>(pyMatrix); } @@ -231,7 +281,7 @@ void bindings_vpRotationMatrix(py::class_> & }, numpy_fn_doc_nonwritable, py::keep_alive<0, 1>()); pyRotationMatrix.def(py::init([](np_array_cf np_array) { verify_array_shape_and_dims(np_array, { 3, 3 }, "ViSP rotation matrix"); - const std::vector shape = np_array.request().shape; + const std::vector shape = np_array.request().shape; vpRotationMatrix result; copy_data_from_np(np_array, result.data); if (!result.isARotationMatrix()) { @@ -258,7 +308,7 @@ void bindings_vpHomogeneousMatrix(py::class_ np_array) { verify_array_shape_and_dims(np_array, { 4, 4 }, "ViSP homogeneous matrix"); - const std::vector shape = np_array.request().shape; + const std::vector shape = np_array.request().shape; vpHomogeneousMatrix result; copy_data_from_np(np_array, result.data); if (!result.isAnHomogeneousMatrix()) { @@ -287,9 +337,9 @@ void bindings_vpTranslationVector(py::class_()); pyTranslationVector.def(py::init([](np_array_cf np_array) { - const std::vector required_shape = { 3 }; + const std::vector required_shape = { 3 }; verify_array_shape_and_dims(np_array, required_shape, "ViSP translation vector"); - const std::vector shape = np_array.request().shape; + const std::vector shape = np_array.request().shape; vpTranslationVector result; copy_data_from_np(np_array, result.data); return result; @@ -313,7 +363,7 @@ void bindings_vpColVector(py::class_> &pyColVecto pyColVector.def(py::init([](np_array_cf np_array) { verify_array_shape_and_dims(np_array, 1, "ViSP column vector"); - const std::vector shape = np_array.request().shape; + const std::vector shape = np_array.request().shape; vpColVector result(shape[0]); copy_data_from_np(np_array, result.data); return result; @@ -325,6 +375,11 @@ Construct a column vector by **copying** a 1D numpy array. )doc", py::arg("np_array")); define_get_item_1d_array>, vpColVector, double>(pyColVector); + add_print_helper(pyColVector, &vpColVector::csvPrint, "strCsv", csv_str_help); + add_print_helper(pyColVector, &vpColVector::maplePrint, "strMaple", maple_str_help); + add_print_helper(pyColVector, &vpColVector::matlabPrint, "strMatlab", matlab_str_help); + add_cpp_print_helper(pyColVector, &vpColVector::cppPrint); + } void bindings_vpRowVector(py::class_> &pyRowVector) @@ -335,7 +390,7 @@ void bindings_vpRowVector(py::class_> &pyRowVecto }, numpy_fn_doc_writable, py::keep_alive<0, 1>()); pyRowVector.def(py::init([](np_array_cf np_array) { verify_array_shape_and_dims(np_array, 1, "ViSP row vector"); - const std::vector shape = np_array.request().shape; + const std::vector shape = np_array.request().shape; vpRowVector result(shape[0]); copy_data_from_np(np_array, result.data); return result; @@ -346,6 +401,10 @@ Construct a row vector by **copying** a 1D numpy array. )doc", py::arg("np_array")); define_get_item_1d_array>, vpRowVector, double>(pyRowVector); + add_print_helper(pyRowVector, &vpRowVector::csvPrint, "strCsv", csv_str_help); + add_print_helper(pyRowVector, &vpRowVector::maplePrint, "strMaple", maple_str_help); + add_print_helper(pyRowVector, &vpRowVector::matlabPrint, "strMatlab", matlab_str_help); + add_cpp_print_helper(pyRowVector, &vpRowVector::cppPrint); } diff --git a/modules/python/bindings/include/core/image_conversions.hpp b/modules/python/bindings/include/core/image_conversions.hpp index 68099d9d15..5a8d3b9deb 100644 --- a/modules/python/bindings/include/core/image_conversions.hpp +++ b/modules/python/bindings/include/core/image_conversions.hpp @@ -37,24 +37,36 @@ #include #include #include - +#include #include + namespace { using ConversionFunction1D = void(*)(unsigned char *, unsigned char *, unsigned int); using ConversionFunction2D = void(*)(unsigned char *, unsigned char *, unsigned int, unsigned int); +using ConversionFunction2DWithFlip = void(*)(unsigned char *, unsigned char *, unsigned int, unsigned int, bool); +using ConversionFunction2DWithFlipAndNThreads = void(*)(unsigned char *, unsigned char *, unsigned int, unsigned int, bool, unsigned int); + + using ComputeBytesFunction = unsigned(*)(unsigned int, unsigned int); void call_conversion_fn(ConversionFunction2D fn, unsigned char *src, unsigned char *dest, unsigned int h, unsigned int w) { fn(src, dest, h, w); } +void call_conversion_fn(ConversionFunction2DWithFlip fn, unsigned char *src, unsigned char *dest, unsigned int h, unsigned int w, bool flip) +{ + fn(src, dest, h, w, flip); +} void call_conversion_fn(ConversionFunction1D fn, unsigned char *src, unsigned char *dest, unsigned int h, unsigned int w) { fn(src, dest, h * w); } + + + template struct SimpleConversionStruct { @@ -96,14 +108,59 @@ struct SimpleConversionStruct else if (destBytesPerPixel == 1 && bufdest.ndim == 3 && bufdest.shape[2] > 1) { throw std::runtime_error("Destination should be a either a 2D array of shape H x W or a 3D array of shape (H, W, 1)"); } - - unsigned char *src_ptr = static_cast(bufsrc.ptr); unsigned char *dest_ptr = static_cast(bufdest.ptr); call_conversion_fn(fn, src_ptr, dest_ptr, bufsrc.shape[0], bufsrc.shape[1]); - }, py::arg("src"), py::arg("dest")); + }, "See C++ documentation of the function for more info", py::arg("src"), py::arg("dest")); } +}; +template <> +struct SimpleConversionStruct +{ + SimpleConversionStruct(const std::string &name, ConversionFunction2DWithFlip fn, unsigned int srcBytesPerPixel, unsigned int destBytesPerPixel) : + name(name), fn(fn), srcBytesPerPixel(srcBytesPerPixel), destBytesPerPixel(destBytesPerPixel) + { } + std::string name; + ConversionFunction2DWithFlip fn; + unsigned int srcBytesPerPixel; + unsigned int destBytesPerPixel; + + void add_conversion_binding(py::class_ &pyImageConvert) + { + pyImageConvert.def_static(name.c_str(), [this](py::array_t &src, + py::array_t &dest, bool flip) { + py::buffer_info bufsrc = src.request(), bufdest = dest.request(); + if (bufsrc.ndim < 2 || bufdest.ndim < 2) { + throw std::runtime_error("Expected to have src and dest arrays with at least two dimensions."); + } + if (bufsrc.shape[0] != bufdest.shape[0] || bufsrc.shape[1] != bufdest.shape[1]) { + std::stringstream ss; + ss << "src and dest must have the same number of pixels, but got src = " << shape_to_string(bufsrc.shape); + ss << "and dest = " << shape_to_string(bufdest.shape); + throw std::runtime_error(ss.str()); + } + if (srcBytesPerPixel > 1 && (bufsrc.ndim != 3 || bufsrc.shape[2] != srcBytesPerPixel)) { + std::stringstream ss; + ss << "Source array should be a 3D array of shape (H, W, " << srcBytesPerPixel << ")"; + throw std::runtime_error(ss.str()); + } + else if (srcBytesPerPixel == 1 && bufsrc.ndim == 3 && bufsrc.shape[2] > 1) { + throw std::runtime_error("Source array should be a either a 2D array of shape H x W or a 3D array of shape (H, W, 1)"); + } + if (destBytesPerPixel > 1 && (bufdest.ndim != 3 || bufdest.shape[2] != destBytesPerPixel)) { + std::stringstream ss; + ss << "Destination array should be a 3D array of shape (H, W, " << destBytesPerPixel << ")"; + throw std::runtime_error(ss.str()); + } + else if (destBytesPerPixel == 1 && bufdest.ndim == 3 && bufdest.shape[2] > 1) { + throw std::runtime_error("Destination should be a either a 2D array of shape H x W or a 3D array of shape (H, W, 1)"); + } + unsigned char *src_ptr = static_cast(bufsrc.ptr); + unsigned char *dest_ptr = static_cast(bufdest.ptr); + call_conversion_fn(fn, src_ptr, dest_ptr, bufsrc.shape[0], bufsrc.shape[1], flip); + }, "See C++ documentation of the function for more info", py::arg("src"), py::arg("dest"), py::arg("flip") = false); + } }; template @@ -157,7 +214,6 @@ struct ConversionFromYUVLike call_conversion_fn(fn, src_ptr, dest_ptr, bufdest.shape[0], bufdest.shape[1]); }, py::arg("src"), py::arg("dest")); } - }; unsigned size422(unsigned h, unsigned w) @@ -173,9 +229,113 @@ unsigned size411(unsigned h, unsigned w) return h * w + ((h / 4) * (w / 4)) * 2; } +template +void add_hsv_to_rgb_or_rgba_binding(py::class_ &pyImageConvert, + void (*fn)(const T *, const T *, const T *, unsigned char *, unsigned int), const char *name, const unsigned destBytes) +{ + pyImageConvert.def_static(name, [fn, destBytes](py::array_t &src, + py::array_t &dest) { + py::buffer_info bufsrc = src.request(), bufdest = dest.request(); + if (bufsrc.ndim != 3 || bufdest.ndim != 3) { + throw std::runtime_error("Expected to have src and dest arrays with at least two dimensions."); + } + if (bufsrc.shape[0] != 3) { + throw std::runtime_error("Source array should be a 3D array of shape (3, H, W) "); + } + if (bufdest.shape[2] != destBytes) { + std::stringstream ss; + ss << "Target array should be a 3D array of shape (H, W, " << destBytes << ")"; + throw std::runtime_error(ss.str()); + } + const unsigned height = bufsrc.shape[1]; + const unsigned width = bufsrc.shape[2]; + if (bufdest.shape[0] != height || bufdest.shape[1] != width) { + std::stringstream ss; + ss << "src and dest must have the same number of pixels, but got HSV planes with dimensions (" << height << ", " << width << ")"; + ss << "and RGB array with dimensions (" << bufdest.shape[0] << ", " << bufdest.shape[1] << ")"; + throw std::runtime_error(ss.str()); + } + + const T *h = static_cast(bufsrc.ptr); + const T *s = h + (height * width); + const T *v = s + (height * width); + unsigned char *dest_ptr = static_cast(bufdest.ptr); + fn(h, s, v, dest_ptr, height * width); + + }, "Convert from HSV Planes (as a 3 x H x W array) to a an RGB/RGBA array (as an H x W x 3 or H x W x 4 array)", py::arg("hsv"), py::arg("rgb")); } +template +void add_rgb_or_rgba_to_hsv_binding(py::class_ &pyImageConvert, + void (*fn)(const unsigned char *, T *, T *, T *, unsigned int), const char *name, const unsigned destBytes) +{ + pyImageConvert.def_static(name, [fn, destBytes](py::array_t &src, + py::array_t &dest) { + py::buffer_info bufsrc = src.request(), bufdest = dest.request(); + if (bufsrc.ndim != 3 || bufdest.ndim != 3) { + throw std::runtime_error("Expected to have src and dest arrays with at least two dimensions."); + } + if (bufdest.shape[0] != 3) { + throw std::runtime_error("Source array should be a 3D array of shape (3, H, W) "); + } + if (bufsrc.shape[2] != destBytes) { + std::stringstream ss; + ss << "Target array should be a 3D array of shape (H, W, " << destBytes << ")"; + throw std::runtime_error(ss.str()); + } + const unsigned height = bufdest.shape[1]; + const unsigned width = bufdest.shape[2]; + if (bufsrc.shape[0] != height || bufsrc.shape[1] != width) { + std::stringstream ss; + ss << "src and dest must have the same number of pixels, but got HSV planes with dimensions (" << height << ", " << width << ")"; + ss << "and RGB array with dimensions (" << bufdest.shape[0] << ", " << bufdest.shape[1] << ")"; + throw std::runtime_error(ss.str()); + } + + T *h = static_cast(bufdest.ptr); + T *s = h + (height * width); + T *v = s + (height * width); + const unsigned char *rgb = static_cast(bufsrc.ptr); + fn(rgb, h, s, v, height * width); + + }, "Convert from HSV Planes (as a 3 x H x W array) to a an RGB/RGBA array (as an H x W x 3 or H x W x 4 array)", py::arg("rgb"), py::arg("hsv")); +} + +/* Demosaicing implem */ +template +void add_demosaic_to_rgba_fn(py::class_ &pyImageConvert, void (*fn)(const DataType *, DataType *, unsigned int, unsigned int, unsigned int), const char *name) +{ + pyImageConvert.def_static(name, [fn](py::array_t &src, + py::array_t &dest, + unsigned int num_threads) { + py::buffer_info bufsrc = src.request(), bufdest = dest.request(); + const unsigned destBytes = 4; + + if (bufsrc.ndim != 2 || bufdest.ndim != 3) { + throw std::runtime_error("Expected to have source array with two dimensions and destination RGBA array with 3."); + } + if (bufdest.shape[2] != destBytes) { + std::stringstream ss; + ss << "Target array should be a 3D array of shape (H, W, " << destBytes << ")"; + throw std::runtime_error(ss.str()); + } + const unsigned height = bufdest.shape[0]; + const unsigned width = bufdest.shape[1]; + if (bufsrc.shape[0] != height || bufsrc.shape[1] != width) { + std::stringstream ss; + ss << "src and dest must have the same number of pixels, but got source with dimensions (" << height << ", " << width << ")"; + ss << "and RGB array with dimensions (" << bufdest.shape[0] << ", " << bufdest.shape[1] << ")"; + throw std::runtime_error(ss.str()); + } + + const DataType *bayer = static_cast(bufsrc.ptr); + DataType *rgba = static_cast(bufdest.ptr); + fn(bayer, rgba, height, width, num_threads); + + }, "Demosaic function implementation, see C++ documentation.", py::arg("bayer_data"), py::arg("rgba"), py::arg("num_threads") = 0); +} +} void bindings_vpImageConvert(py::class_ &pyImageConvert) { @@ -187,27 +347,40 @@ void bindings_vpImageConvert(py::class_ &pyImageConvert) SimpleConversionStruct("YUV444ToRGBa", &vpImageConvert::YUV444ToRGBa, 3, 4), SimpleConversionStruct("RGBToRGBa", static_cast(&vpImageConvert::RGBToRGBa), 3, 4), SimpleConversionStruct("RGBaToRGB", &vpImageConvert::RGBaToRGB, 4, 3), + SimpleConversionStruct("RGBaToGrey", static_cast(&vpImageConvert::RGBaToGrey), 4, 1), SimpleConversionStruct("GreyToRGB", &vpImageConvert::GreyToRGB, 1, 3), SimpleConversionStruct("GreyToRGBa", static_cast(&vpImageConvert::GreyToRGBa), 1, 4), SimpleConversionStruct("RGBToGrey", static_cast(&vpImageConvert::RGBToGrey), 3, 1), + SimpleConversionStruct("MONO16ToGrey", static_cast(&vpImageConvert::MONO16ToGrey), 2, 1), + SimpleConversionStruct("MONO16ToRGBa", static_cast(&vpImageConvert::MONO16ToRGBa), 2, 4) + }; for (auto &conversion: conversions) { conversion.add_conversion_binding(pyImageConvert); } } - //YUV conversions + // Simple conversions with flip + { + std::vector> conversions = { + SimpleConversionStruct("BGRToRGBa", static_cast(&vpImageConvert::BGRToRGBa), 3, 4), + SimpleConversionStruct("BGRaToRGBa", static_cast(&vpImageConvert::BGRaToRGBa), 4, 4) + }; + for (auto &conversion: conversions) { + conversion.add_conversion_binding(pyImageConvert); + } + } + + // YUV conversions { using Conv = ConversionFromYUVLike; std::vector conversions = { Conv("YUYVToRGBa", &vpImageConvert::YUYVToRGBa, &size422, 4), Conv("YUYVToRGB", &vpImageConvert::YUYVToRGB, &size422, 3), - Conv("YV12ToRGBa", &vpImageConvert::YV12ToRGBa, &size420, 4), Conv("YV12ToRGB", &vpImageConvert::YV12ToRGB, &size420, 3), Conv("YUV420ToRGBa", &vpImageConvert::YUV420ToRGBa, &size420, 4), Conv("YUV420ToRGB", &vpImageConvert::YUV420ToRGB, &size420, 3), - Conv("YVU9ToRGBa", &vpImageConvert::YVU9ToRGBa, &size411, 4), Conv("YVU9ToRGB", &vpImageConvert::YVU9ToRGB, &size411, 3), }; @@ -237,6 +410,54 @@ void bindings_vpImageConvert(py::class_ &pyImageConvert) conversion.add_conversion_binding(pyImageConvert); } } + + // HSV <-> RGB/a + add_hsv_to_rgb_or_rgba_binding(pyImageConvert, vpImageConvert::HSVToRGB, "HSVToRGB", 3); + add_hsv_to_rgb_or_rgba_binding(pyImageConvert, vpImageConvert::HSVToRGB, "HSVToRGB", 3); + add_hsv_to_rgb_or_rgba_binding(pyImageConvert, vpImageConvert::HSVToRGBa, "HSVToRGBa", 4); + add_hsv_to_rgb_or_rgba_binding(pyImageConvert, vpImageConvert::HSVToRGBa, "HSVToRGBa", 4); + + add_rgb_or_rgba_to_hsv_binding(pyImageConvert, vpImageConvert::RGBToHSV, "RGBToHSV", 3); + add_rgb_or_rgba_to_hsv_binding(pyImageConvert, vpImageConvert::RGBToHSV, "RGBToHSV", 3); + add_rgb_or_rgba_to_hsv_binding(pyImageConvert, vpImageConvert::RGBaToHSV, "RGBaToHSV", 4); + add_rgb_or_rgba_to_hsv_binding(pyImageConvert, vpImageConvert::RGBaToHSV, "RGBaToHSV", 4); + + + // uint8_t implems + { + using DemosaicFn = void (*)(const uint8_t *, uint8_t *, unsigned int, unsigned int, unsigned int); + std::vector> functions = { + {static_cast(&vpImageConvert::demosaicRGGBToRGBaMalvar), "demosaicRGGBToRGBaMalvar"}, + {static_cast(&vpImageConvert::demosaicGRBGToRGBaMalvar), "demosaicGRBGToRGBaMalvar"}, + {static_cast(&vpImageConvert::demosaicGBRGToRGBaMalvar), "demosaicGBRGToRGBaMalvar"}, + {static_cast(&vpImageConvert::demosaicBGGRToRGBaMalvar), "demosaicBGGRToRGBaMalvar"}, + {static_cast(&vpImageConvert::demosaicRGGBToRGBaBilinear), "demosaicRGGBToRGBaBilinear"}, + {static_cast(&vpImageConvert::demosaicGRBGToRGBaBilinear), "demosaicGRBGToRGBaBilinear"}, + {static_cast(&vpImageConvert::demosaicGBRGToRGBaBilinear), "demosaicGBRGToRGBaBilinear"}, + {static_cast(&vpImageConvert::demosaicBGGRToRGBaBilinear), "demosaicBGGRToRGBaBilinear"} + }; + for (const auto &pair: functions) { + add_demosaic_to_rgba_fn(pyImageConvert, pair.first, pair.second); + } + } + //UInt16_t implems + { + using DemosaicFn = void (*)(const uint16_t *, uint16_t *, unsigned int, unsigned int, unsigned int); + std::vector> functions = { + {static_cast(&vpImageConvert::demosaicRGGBToRGBaMalvar), "demosaicRGGBToRGBaMalvar"}, + {static_cast(&vpImageConvert::demosaicGRBGToRGBaMalvar), "demosaicGRBGToRGBaMalvar"}, + {static_cast(&vpImageConvert::demosaicGBRGToRGBaMalvar), "demosaicGBRGToRGBaMalvar"}, + {static_cast(&vpImageConvert::demosaicBGGRToRGBaMalvar), "demosaicBGGRToRGBaMalvar"}, + {static_cast(&vpImageConvert::demosaicRGGBToRGBaBilinear), "demosaicRGGBToRGBaBilinear"}, + {static_cast(&vpImageConvert::demosaicGRBGToRGBaBilinear), "demosaicGRBGToRGBaBilinear"}, + {static_cast(&vpImageConvert::demosaicGBRGToRGBaBilinear), "demosaicGBRGToRGBaBilinear"}, + {static_cast(&vpImageConvert::demosaicBGGRToRGBaBilinear), "demosaicBGGRToRGBaBilinear"} + }; + for (const auto &pair: functions) { + add_demosaic_to_rgba_fn(pyImageConvert, pair.first, pair.second); + } + } + } #endif diff --git a/modules/python/bindings/include/core/images.hpp b/modules/python/bindings/include/core/images.hpp index 53e0a08e19..3303b341b0 100644 --- a/modules/python/bindings/include/core/images.hpp +++ b/modules/python/bindings/include/core/images.hpp @@ -33,7 +33,7 @@ #ifndef VISP_PYTHON_CORE_IMAGES_HPP #define VISP_PYTHON_CORE_IMAGES_HPP - +#include #include #include #include @@ -109,7 +109,7 @@ bindings_vpImage(py::class_> &pyImage) pyImage.def(py::init([](np_array_cf &np_array) { verify_array_shape_and_dims(np_array, 2, "ViSP Image"); - const std::vector shape = np_array.request().shape; + const std::vector shape = np_array.request().shape; vpImage result(shape[0], shape[1]); copy_data_from_np(np_array, result.bitmap); return result; @@ -151,7 +151,7 @@ bindings_vpImage(py::class_> &pyImage) pyImage.def(py::init([](np_array_cf &np_array) { verify_array_shape_and_dims(np_array, 3, "ViSP RGBa image"); - const std::vector shape = np_array.request().shape; + const std::vector shape = np_array.request().shape; if (shape[2] != 4) { throw std::runtime_error("Tried to copy a 3D numpy array that does not have 4 elements per pixel into a ViSP RGBA image"); } @@ -196,7 +196,7 @@ bindings_vpImage(py::class_> &pyImage) pyImage.def(py::init([](np_array_cf &np_array) { verify_array_shape_and_dims(np_array, 3, "ViSP RGBa image"); - const std::vector shape = np_array.request().shape; + const std::vector shape = np_array.request().shape; if (shape[2] != 3) { throw std::runtime_error("Tried to copy a 3D numpy array that does not have 3 elements per pixel into a ViSP RGBf image"); } diff --git a/modules/python/bindings/include/core/pixel_meter.hpp b/modules/python/bindings/include/core/pixel_meter.hpp index c55b272e2d..3e4a08f6b1 100644 --- a/modules/python/bindings/include/core/pixel_meter.hpp +++ b/modules/python/bindings/include/core/pixel_meter.hpp @@ -40,6 +40,7 @@ #include #include +#include #include "core/utils.hpp" @@ -61,7 +62,7 @@ void bindings_vpPixelMeterConversion(py::class_ &pyPM) double *x_ptr = static_cast(xs.request().ptr); double *y_ptr = static_cast(ys.request().ptr); - for (ssize_t i = 0; i < bufu.size; ++i) { + for (py::ssize_t i = 0; i < bufu.size; ++i) { vpPixelMeterConversion::convertPoint(cam, u_ptr[i], v_ptr[i], x_ptr[i], y_ptr[i]); } @@ -124,7 +125,7 @@ void bindings_vpMeterPixelConversion(py::class_ &pyMP) double *u_ptr = static_cast(us.request().ptr); double *v_ptr = static_cast(vs.request().ptr); - for (ssize_t i = 0; i < bufx.size; ++i) { + for (py::ssize_t i = 0; i < bufx.size; ++i) { vpMeterPixelConversion::convertPoint(cam, x_ptr[i], y_ptr[i], u_ptr[i], v_ptr[i]); } diff --git a/modules/python/bindings/include/core/utils.hpp b/modules/python/bindings/include/core/utils.hpp index 3bb413bdf0..404dbc53fd 100644 --- a/modules/python/bindings/include/core/utils.hpp +++ b/modules/python/bindings/include/core/utils.hpp @@ -52,7 +52,7 @@ using np_array_cf = py::array_t template py::buffer_info make_array_buffer(T *data, std::array dims, bool readonly) { - std::array strides; + std::array strides; for (unsigned i = 0; i < N; i++) { unsigned s = sizeof(T); for (unsigned j = i + 1; j < N; ++j) { @@ -71,7 +71,7 @@ py::buffer_info make_array_buffer(T *data, std::array dims, bool re ); } -std::string shape_to_string(const std::vector &shape) +std::string shape_to_string(const std::vector &shape) { std::stringstream ss; ss << "("; @@ -89,7 +89,7 @@ template void verify_array_shape_and_dims(np_array_cf np_array, unsigned dims, const char *class_name) { py::buffer_info buffer = np_array.request(); - std::vector shape = buffer.shape; + std::vector shape = buffer.shape; if (shape.size() != dims) { std::stringstream ss; ss << "Tried to instanciate " << class_name @@ -100,11 +100,11 @@ void verify_array_shape_and_dims(np_array_cf np_array, unsigned dims, cons } } template -void verify_array_shape_and_dims(np_array_cf np_array, std::vector expected_dims, const char *class_name) +void verify_array_shape_and_dims(np_array_cf np_array, std::vector expected_dims, const char *class_name) { verify_array_shape_and_dims(np_array, expected_dims.size(), class_name); py::buffer_info buffer = np_array.request(); - std::vector shape = buffer.shape; + std::vector shape = buffer.shape; bool invalid_shape = false; for (unsigned int i = 0; i < expected_dims.size(); ++i) { if (shape[i] != expected_dims[i]) { @@ -125,9 +125,9 @@ template void copy_data_from_np(np_array_cf src, Item *dest) { py::buffer_info buffer = src.request(); - std::vector shape = buffer.shape; + std::vector shape = buffer.shape; unsigned int elements = 1; - for (ssize_t dim : shape) { + for (py::ssize_t dim : shape) { elements *= dim; } const Item *data = (Item *)buffer.ptr; diff --git a/modules/python/bindings/include/mbt.hpp b/modules/python/bindings/include/mbt.hpp index 2dc974c728..4ef94b65d4 100644 --- a/modules/python/bindings/include/mbt.hpp +++ b/modules/python/bindings/include/mbt.hpp @@ -37,6 +37,7 @@ #include #include #include +#include #include namespace py = pybind11; @@ -50,7 +51,7 @@ void bindings_vpMbGenericTracker(py::class_ &py for (const auto &point_cloud_pair: mapOfPointClouds) { py::buffer_info buffer = point_cloud_pair.second.request(); - if (buffer.ndim != 3 and buffer.shape[2] != 3) { + if (buffer.ndim != 3 && buffer.shape[2] != 3) { std::stringstream ss; ss << "Pointcloud error: pointcloud at key: " << point_cloud_pair.first << " should be a 3D numpy array of dimensions H X W x 3"; diff --git a/modules/python/bindings/visp/__init__.py b/modules/python/bindings/visp/__init__.py index 4807a8bea3..6bbe0c7b2b 100644 --- a/modules/python/bindings/visp/__init__.py +++ b/modules/python/bindings/visp/__init__.py @@ -34,16 +34,30 @@ ############################################################################# import sys +import os # import os # sys.path.append(os.path.dirname(__file__)) # print(sys.path) -from .bindings import * -import _visp +try: + from ._visp import * + from ._visp import __dict__ as cpp_extension_dict +except ImportError: + import platform + if platform.system() == "Windows": # On windows import can fail because DLLs are not found in the default search paths + from .windows_dll_manager import get_dll_paths, build_directory_manager + # Use the context to clean up the PATH/dll directories after the import (no namespace pollution) + with build_directory_manager() as dll_dir_manager: + for p in get_dll_paths(): + dll_dir_manager.add_dll_directory(p) + from ._visp import * + from ._visp import __dict__ as cpp_extension_dict + else: + raise ImportError('Could not import ViSP python bindings') # Fake module names -for k in _visp.__dict__: +for k in cpp_extension_dict: from types import ModuleType if isinstance(_visp.__dict__[k], ModuleType): sys.modules[f'{__name__}.{k}'] = _visp.__dict__[k] diff --git a/modules/python/bindings/visp/windows_dll_manager.py b/modules/python/bindings/visp/windows_dll_manager.py new file mode 100644 index 0000000000..4653195782 --- /dev/null +++ b/modules/python/bindings/visp/windows_dll_manager.py @@ -0,0 +1,78 @@ +''' +This code is directly adapted from proxsuite_nlp, see: + +- https://github.com/Simple-Robotics/proxsuite-nlp/blob/main/bindings/python/proxsuite_nlp/windows_dll_manager.py +- https://github.com/Simple-Robotics/proxsuite-nlp/blob/main/bindings/python/proxsuite_nlp/__init__.py + +On windows, since Python 3.8, dll directories must be explicetly specified (cannot go through path), see +- https://docs.python.org/3/library/os.html#os.add_dll_directory + +''' + + +import os +import sys +import contextlib + + +def get_dll_paths(): + # Assumes that we are in a conda environment, and that ViSP DLLs and the dependencies are installed in this environment + # For the choice of defaults: see https://peps.python.org/pep-0250/#implementation + DEFAULT_DLL_PATHS = [ + '..\\..\\..\\..\\bin', # when current folder is lib/python-version/site-packages/package + '..\\..\\..\\..\\Library\\bin', + '..\\..\\..\\bin', # when current folder is lib/site-packages/package + '..\\..\\..\\Library\\bin', + ] + # If we have a different setup, the user should specify their own paths + visp_user_defined_dll_paths = os.getenv("VISP_WINDOWS_DLL_PATH") + dll_paths = [ + os.path.join(os.path.dirname(__file__), dll_path) for dll_path in DEFAULT_DLL_PATHS + ] + + if visp_user_defined_dll_paths is not None: + dll_paths.extend(visp_user_defined_dll_paths.split(os.pathsep)) + + return dll_paths + +class PathManager(contextlib.AbstractContextManager): + """Restore PATH state after importing Python module""" + + def add_dll_directory(self, dll_dir: str): + os.environ["PATH"] += os.pathsep + dll_dir + + def __enter__(self): + self.old_path = os.environ["PATH"] + return self + + def __exit__(self, *exc_details): + os.environ["PATH"] = self.old_path + + +class DllDirectoryManager(contextlib.AbstractContextManager): + """Restore DllDirectory state after importing Python module""" + + def add_dll_directory(self, dll_dir: str): + # add_dll_directory can fail on relative path and non + # existing path. + # Since we don't know all the fail criterion we just ignore + # thrown exception + try: + self.dll_dirs.append(os.add_dll_directory(dll_dir)) + except OSError: + pass + + def __enter__(self): + self.dll_dirs = [] + return self + + def __exit__(self, *exc_details): + for d in self.dll_dirs: + d.close() + + +def build_directory_manager(): + if sys.version_info >= (3, 8): + return DllDirectoryManager() + else: # Below 3.8, the path variable is used to search for DLLs + return PathManager() diff --git a/modules/python/config/ar.json b/modules/python/config/ar.json index 9b6b904d82..7d1985301a 100644 --- a/modules/python/config/ar.json +++ b/modules/python/config/ar.json @@ -1,7 +1,7 @@ { "ignored_headers": [], - "ignored_classes": [], + "ignored_classes": [ "vpAROgre" ], "user_defined_headers": [], "classes": {}, "enums": {} -} \ No newline at end of file +} diff --git a/modules/python/config/core.json b/modules/python/config/core.json index 1701b70c7f..49797242f6 100644 --- a/modules/python/config/core.json +++ b/modules/python/config/core.json @@ -4,6 +4,7 @@ "vpFrameGrabberException", "vpIoException", "vpDisplayException", "vpMatrixException"], "user_defined_headers": ["core.hpp"], + "config_includes": ["core_image.json", "core_math.json"], "enums": { "vpMunkres::STEP_T": { "ignore": true @@ -12,243 +13,182 @@ "ignore": true } }, - "classes": { - "vpIoTools": { - "ignored_attributes": ["separator"] + "functions": [ + { + "static": false, + "signature": "void visp2eigen(const vpThetaUVector&, Eigen::AngleAxis&)", + "ignore": true }, - "vpArray2D": { - "additional_bindings": "bindings_vpArray2D", - "use_buffer_protocol": true, - "specializations": [ - { - "python_name": "ArrayDouble2D", - "arguments": ["double"] - } - ], - "methods": - [ - { - "static": true, - "signature": "void insert(const vpArray2D &, const vpArray2D &, vpArray2D &, unsigned int, unsigned int)", - "custom_name": "insertStatic" - } - ] + { + "static": false, + "signature": "void visp2eigen(const vpQuaternionVector&, Eigen::Quaternion&)", + "ignore": true }, - "vpMath" :{ - "methods": [ - { - "static": true, - "signature": "double lineFitting(const std::vector&, double&, double&, double&)", - "use_default_param_policy": false, - "param_is_input": [ - true, - false, - false, - false - ], - "param_is_output": [ - false, - true, - true, - true - ] - } - ] + { + "static": false, + "signature": "void visp2eigen(const vpHomogeneousMatrix&, Eigen::MatrixBase&)", + "ignore": true }, - "vpImage": { - "ignore_repr": true, - "additional_bindings": "bindings_vpImage", - "use_buffer_protocol": true, - "specializations": [ - { - "python_name": "ImageGray", - "arguments": ["unsigned char"] - }, - { - "python_name": "ImageFloat", - "arguments": ["float"] - }, - { - "python_name": "ImageDouble", - "arguments": ["double"] - }, - { - "python_name": "ImageUInt16", - "arguments": ["uint16_t"] - }, - { - "python_name": "ImageRGBa", - "arguments": ["vpRGBa"] - }, - { - "python_name": "ImageRGBf", - "arguments": ["vpRGBf"] - } - ], - "methods": - [ - { - "static": true, - "signature": "void insert(const vpArray2D &, const vpArray2D &, vpArray2D &, unsigned int, unsigned int)", - "custom_name": "insertStatic" - } - ] + { + "static": false, + "signature": "void visp2eigen(const vpMatrix&, Eigen::MatrixBase&)", + "ignore": true }, - "vpTranslationVector": { - "additional_bindings": "bindings_vpTranslationVector", - "methods": - [ - { - "static": true, - "signature": "vpMatrix skew(const vpTranslationVector &)", - "custom_name": "skewOf" - }, - { - "static": true, - "signature": "void skew(const vpTranslationVector &, vpMatrix&)", - "custom_name": "skewOf" - } - ] + { + "static": false, + "signature": "void eigen2visp(const Eigen::AngleAxis&, vpThetaUVector&)", + "ignore": true + }, + { + "static": false, + "signature": "void eigen2visp(const Eigen::Quaternion&, vpQuaternionVector&)", + "ignore": true + }, + { + "static": true, + "signature": "unsigned long vp_mz_crc32(unsigned long, const unsigned char*, size_t)", + "ignore": true + }, + { + "static": false, + "signature": "std::vector create_npy_header(const std::vector&)", + "ignore": true + }, + { + "static": false, + "signature": "void parse_npy_header(FILE*, size_t&, std::vector&, bool&)", + "ignore": true + }, + { + "static": false, + "signature": "void parse_npy_header(unsigned char*, size_t&, std::vector&, bool&)", + "ignore": true + }, + { + "static": false, + "signature": "void parse_zip_footer(FILE*, uint16_t&, size_t&, size_t&)", + "ignore": true + }, + { + "static": false, + "signature": "void npy_save(std::string, const T*, const std::vector, std::string)", + "ignore": true + }, + { + "static": false, + "signature": "void npz_save(std::string, std::string, const T*, const std::vector&, std::string)", + "ignore": true }, - "vpColVector": { - "additional_bindings": "bindings_vpColVector", - "use_buffer_protocol": true, + { + "static": false, + "signature": "void npy_save(std::string, const std::vector, std::string)", + "ignore": true + }, + { + "static": false, + "signature": "void npz_save(std::string, std::string, const std::vector, std::string)", + "ignore": true + } + ], + "classes": { + "vpIoTools": { + "ignored_attributes": ["separator"], "methods": [ { + "signature": "void readBinaryValueLE(std::ifstream&, int16_t&)", "static": true, - "signature": "vpColVector stack(const vpColVector &, const vpColVector &)", - "custom_name": "stackVectors" + "ignore": true }, { + "signature": "void readBinaryValueLE(std::ifstream&, uint16_t&)", "static": true, - "signature": "void stack(const vpColVector &, const vpColVector &, vpColVector &)", - "custom_name": "stackVectors" - } - ] - }, - "vpRowVector": { - "additional_bindings": "bindings_vpRowVector", - "use_buffer_protocol": true, - "methods": [ - { - "static": true, - "signature": "vpRowVector stack(const vpRowVector &, const vpRowVector &)", - "custom_name": "stackVectors" + "ignore": true }, { + "signature": "void readBinaryValueLE(std::ifstream&, int32_t&)", "static": true, - "signature": "void stack(const vpRowVector &, const vpRowVector &, vpRowVector &)", - "custom_name": "stackVectors" - } - ] - }, - "vpMatrix": { - "ignore_repr": true, - "additional_bindings": "bindings_vpMatrix", - "use_buffer_protocol": true, - "methods": - [ + "ignore": true + }, { - + "signature": "void readBinaryValueLE(std::ifstream&, uint32_t&)", "static": true, - "signature": "vpMatrix insert(const vpMatrix &, const vpMatrix &, unsigned int , unsigned int)", - "custom_name": "insertMatrixInMatrix" + "ignore": true }, { - + "signature": "void readBinaryValueLE(std::ifstream&, float&)", "static": true, - "signature": "void insert(const vpMatrix &, const vpMatrix &, vpMatrix &, unsigned int , unsigned int)", - "custom_name": "insertMatrixInMatrix" + "ignore": true }, { - + "signature": "void readBinaryValueLE(std::ifstream&, double&)", "static": true, - "signature": "void kron(const vpMatrix &, const vpMatrix &, vpMatrix &)", - "custom_name": "kronStatic" + "ignore": true }, { - "static": true, - "signature": "vpMatrix kron(const vpMatrix &, const vpMatrix &)", - "custom_name": "kronStatic" + "signature": "void getUserName(std::string&)", + "use_default_param_policy": false, + "param_is_input": [false], + "param_is_output": [true] }, { - - "signature": "vpMatrix stack(const vpMatrix &, const vpMatrix &)", "static": true, - "custom_name": "stackMatrices" + "signature": "void getVersion(const std::string&, unsigned int&, unsigned int&, unsigned int&)", + "use_default_param_policy": false, + "param_is_input": [true, false, false, false], + "param_is_output": [false, true, true, true] }, { "static": true, - "signature": "vpMatrix stack(const vpMatrix &, const vpRowVector &)", - "custom_name": "stackRow" + "signature": "bool readConfigVar(const std::string&, float&)", + "custom_name": "readConfigVarFloat", + "use_default_param_policy": false, + "param_is_input": [true, false], + "param_is_output": [false, true] }, { - - "signature": "vpMatrix stack(const vpMatrix &, const vpColVector &)", "static": true, - "custom_name": "stackColumn" + "signature": "bool readConfigVar(const std::string&, double&)", + "custom_name": "readConfigVarDouble", + "use_default_param_policy": false, + "param_is_input": [true, false], + "param_is_output": [false, true] }, { - "signature": "void stack(const vpMatrix &, const vpMatrix &, vpMatrix &)", "static": true, - "custom_name": "stackMatrices" + "signature": "bool readConfigVar(const std::string&, unsigned int&)", + "custom_name": "readConfigVarUnsigned", + "use_default_param_policy": false, + "param_is_input": [true, false], + "param_is_output": [false, true] }, { - "signature": "void stack(const vpMatrix &, const vpRowVector &, vpMatrix &)", "static": true, - "custom_name": "stackRow" + "signature": "bool readConfigVar(const std::string&, int&)", + "custom_name": "readConfigVarInt", + "use_default_param_policy": false, + "param_is_input": [true, false], + "param_is_output": [false, true] }, { - "signature": "void stack(const vpMatrix &, const vpColVector &, vpMatrix &)", "static": true, - "custom_name": "stackColumn" - } - ] - }, - "vpRotationMatrix": { - "additional_bindings": "bindings_vpRotationMatrix", - "use_buffer_protocol": true - }, - "vpHomogeneousMatrix": { - "additional_bindings": "bindings_vpHomogeneousMatrix", - "use_buffer_protocol": true, - "methods": [ - { - "static": false, - "signature": "void convert(std::vector&)", + "signature": "bool readConfigVar(const std::string&, bool&)", + "custom_name": "readConfigVarBoolean", "use_default_param_policy": false, - "param_is_input": [ - false - ], - "param_is_output": [ - true - ] + "param_is_input": [true, false], + "param_is_output": [false, true] }, { - "static": false, - "signature": "void convert(std::vector&)", - "ignore": true - } - ] - }, - "vpThetaUVector": { - "methods": [ - { - "static": false, - "signature": "void extract(double&, vpColVector&)", + "static": true, + "signature": "bool readConfigVar(const std::string&, std::string&)", + "custom_name": "readConfigVarString", "use_default_param_policy": false, - "param_is_input": [ - false, - false - ], - "param_is_output": [ - true, - true - ] + "param_is_input": [true, false], + "param_is_output": [false, true] } ] }, + "vpPolygon": { "methods": [ @@ -310,222 +250,103 @@ "use_default_param_policy": false, "param_is_input": [true, false], "param_is_output": [false, true] - } - ] - }, - "vpPoint": { - "methods": - [ - { - "static": false, - "ignore": true, - "signature": "void getWorldCoordinates(std::vector&)" - }, - { - "static": false, - "ignore": true, - "signature": "void getWorldCoordinates(double&, double&, double&)" - } - - ] - }, - "vpBSpline": { - "methods": - [ - { - "static": true, - "signature": "unsigned int findSpan(double, unsigned int, std::vector &)", - "custom_name": "findSpanFromSpline" }, { "static": true, - "signature": "vpImagePoint computeCurvePoint(double, unsigned int, unsigned int, std::vector &, std::vector&)", - "custom_name": "computeCurvePointFromSpline" - } - ] - }, - "vpQuadProg": { - "methods": - [ - { - "static": true, - "signature": "bool solveQPe(const vpMatrix &, const vpColVector &, vpMatrix, vpColVector, vpColVector &, const double &)", - "custom_name": "solveQPeStatic" + "signature": "void getMinMaxRoi(const std::vector&, int&, int&, int&, int&)", + "use_default_param_policy": false, + "param_is_input": [ + true, false, false, false, false + ], + "param_is_output": [ + false, true, true, true, true + ] } ] }, - "vpImageTools": { - "methods": - [ + "vpImagePoint": { + "methods": [ { - "static": true, - "signature": "void warpImage(const vpImage&, const vpMatrix&, vpImage&, const vpImageTools::vpImageInterpolationType&, bool, bool)", - "specializations": - [ - ["unsigned char"], - ["vpRGBa"] - ] + "static": false, + "signature": "vpImagePoint& operator=(const vpImagePoint&&)", + "ignore": true } ] }, - "vpImageConvert": { - "additional_bindings": "bindings_vpImageConvert", + "vpPoint": { "methods": [ { - "static": true, - "signature": "void RGBaToRGB(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void RGBToRGBa(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void RGBToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int, bool)", - "ignore": true - }, - { - "static": true, - "signature": "void YUV444ToRGBa(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUV444ToRGB(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUV444ToGrey(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void GreyToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void GreyToRGBa(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void GreyToRGB(unsigned char*, unsigned char*, unsigned int, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void GreyToRGB(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUYVToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUYVToRGB(unsigned char*, unsigned char*, unsigned int, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUYVToGrey(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUV411ToRGBa(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUV411ToRGB(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUV411ToGrey(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUV422ToRGBa(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUV422ToRGB(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUV422ToGrey(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUV420ToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUV420ToRGB(unsigned char*, unsigned char*, unsigned int, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YUV420ToGrey(unsigned char*, unsigned char*, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YV12ToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int)", - "ignore": true - }, - { - "static": true, - "signature": "void YV12ToRGB(unsigned char*, unsigned char*, unsigned int, unsigned int)", - "ignore": true + "static": false, + "ignore": true, + "signature": "void getWorldCoordinates(std::vector&)" }, { - "static": true, - "signature": "void YVU9ToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int)", - "ignore": true - }, + "static": false, + "ignore": true, + "signature": "void getWorldCoordinates(double&, double&, double&)" + } + + ] + }, + "vpRect": { + "methods": [ { - "static": true, - "signature": "void YVU9ToRGB(unsigned char*, unsigned char*, unsigned int, unsigned int)", - "ignore": true - }, + "static": false, + "signature": "void getCenter(double&, double&)", + "use_default_param_policy": false, + "param_is_input": [false,false], + "param_is_output": [true, true] + } + ] + }, + "vpBSpline": { + "methods": + [ { "static": true, - "signature": "void YCbCrToRGB(unsigned char*, unsigned char*, unsigned int)", - "ignore": true + "signature": "unsigned int findSpan(double, unsigned int, const std::vector &)", + "custom_name": "findSpanFromSpline" }, { "static": true, - "signature": "void YCbCrToRGBa(unsigned char*, unsigned char*, unsigned int)", - "ignore": true + "signature": "vpImagePoint computeCurvePoint(double, unsigned int, unsigned int, const std::vector &, const std::vector&)", + "custom_name": "computeCurvePointFromSpline" }, { - "static": true, - "signature": "void YCbCrToGrey(unsigned char*, unsigned char*, unsigned int)", - "ignore": true + "static": false, + "signature": "void get_crossingPoints(std::list&)", + "use_default_param_policy": false, + "param_is_input": [ + false + ], + "param_is_output": [ + true + ] }, { - "static": true, - "signature": "void YCrCbToRGB(unsigned char*, unsigned char*, unsigned int)", - "ignore": true + "static": false, + "signature": "void get_knots(std::list&)", + "use_default_param_policy": false, + "param_is_input": [false], + "param_is_output": [true] }, + { + "static": false, + "signature": "void get_controlPoints(std::list&)", + "use_default_param_policy": false, + "param_is_input": [false], + "param_is_output": [true] + } + ] + }, + "vpQuadProg": { + "methods": + [ { "static": true, - "signature": "void YCrCbToRGBa(unsigned char*, unsigned char*, unsigned int)", - "ignore": true + "signature": "bool solveQPe(const vpMatrix &, const vpColVector &, vpMatrix, vpColVector, vpColVector &, const double &)", + "custom_name": "solveQPeStatic" } ] }, @@ -630,6 +451,36 @@ "static": true, "signature": "void displayCircle(const vpImage &, int, int, unsigned int, const vpColor &, bool, unsigned int)", "custom_name": "displayCircleStatic" + }, + { + "static": true, + "signature": "bool getKeyboardEvent(const vpImage&, std::string&, bool)", + "use_default_param_policy": false, + "param_is_input": [ + true, + false, + true + ], + "param_is_output": [ + false, + true, + false + ] + }, + { + "static": true, + "signature": "bool getKeyboardEvent(const vpImage&, std::string&, bool)", + "use_default_param_policy": false, + "param_is_input": [ + true, + false, + true + ], + "param_is_output": [ + false, + true, + false + ] } ] }, @@ -650,6 +501,22 @@ } ] }, + "vpMomentObject": { + "methods": [ + { + "static": false, + "signature": "void fromVector(std::vector&)", + "use_default_param_policy": false, + "param_is_input": [ + true + ], + "param_is_output": [ + true + ], + "comment": "The point list is modified, cannot be consted. So it is taken as input and returned." + } + ] + }, "vpPixelMeterConversion": { "additional_bindings": "bindings_vpPixelMeterConversion", "methods": [ @@ -714,6 +581,17 @@ "param_is_input": [true, true, true, true, true, false, false, false, false, false], "param_is_output": [false, false, false, false, false, true, true, true, true, true] }, + { + "static": true, + "signature": "void convertEllipse(const vpCameraParameters&, const vpSphere&, vpImagePoint&, double&, double&, double&)", + "use_default_param_policy": false, + "param_is_input": [ + true, true, true, false, false, false + ], + "param_is_output": [ + false, false, false, true, true, true + ] + }, { "static": true, "signature": "void convertLine(const vpCameraParameters&, const double&, const double&, double&, double&)", @@ -735,11 +613,48 @@ "param_is_input": [true,true,false,false], "param_is_output": [false,false,true,true] }, + { + "static": true, + "signature": "void convertEllipse(const vpCameraParameters&, const vpCircle&, vpImagePoint&, double&, double&, double&)", + "use_default_param_policy": false, + "param_is_input": [ + true, true, true, true, true, true + ], + "param_is_output": [ + false, false, false, true, true, true + ] + }, + { + "static": true, + "signature": "void convertEllipse(const vpCameraParameters&, double, double, double, double, double, vpImagePoint&, double&, double&, double&)", + "use_default_param_policy": false, + "param_is_input": [ + true, true, true, true, true, true, true, false, false, false + ], + "param_is_output": [ + false, false, false, false, false, false, false, true, true, true + ] + }, { "static": true, "signature": "void convertEllipse(const cv::Mat&, const cv::Mat&, const vpImagePoint&, double, double, double, double&, double&, double&, double&, double&)", "ignore": true }, + { + "static": true, + "signature": "void convertEllipse(const cv::Mat&, double, double, double, double, double, vpImagePoint&, double&, double&, double&)", + "ignore": true + }, + { + "static": true, + "signature": "void convertEllipse(const cv::Mat&, const vpCircle&, vpImagePoint&, double&, double&, double&)", + "ignore": true + }, + { + "static": true, + "signature": "void convertEllipse(const cv::Mat&, const vpSphere&, vpImagePoint&, double&, double&, double&)", + "ignore": true + }, { "static": true, "signature": "void convertLine(const cv::Mat&, const double&, const double&, double&, double&)", @@ -767,8 +682,170 @@ "param_is_output": [false, false, false, false, true, true] } ] + }, + "vpImageFilter": { + "methods": [ + { + "static": true, + "signature": "double derivativeFilterX(const vpImage&, unsigned int, unsigned int)", + "specializations": [ + ["TypeFilterable"] + ] + }, + { + "static": true, + "signature": "double derivativeFilterY(const vpImage&, unsigned int, unsigned int)", + "specializations": [ + ["TypeFilterable"] + ] + } + ] + }, + "vpImageMorphology": { + "methods": [ + { + "static": true, + "signature": "void dilatation(vpImage&, const int&)", + "specializations": [["TypeErodableDilatable"]] + }, + { + "static": true, + "signature": "void erosion(vpImage&, Type, Type, vpImageMorphology::vpConnexityType)", + "specializations": [["TypeErodableDilatable"]] + }, + { + "static": true, + "signature": "void erosion(vpImage&, const vpImageMorphology::vpConnexityType&)", + "specializations": [["TypeErodableDilatable"]] + }, + { + "static": true, + "signature": "void dilatation(vpImage&, Type, Type, vpImageMorphology::vpConnexityType)", + "specializations": [["TypeErodableDilatable"]] + }, + { + "static": true, + "signature": "void dilatation(vpImage&, const vpImageMorphology::vpConnexityType&)", + "specializations": [["TypeErodableDilatable"]] + }, + { + "static": true, + "signature": "void erosion(vpImage&, const int&)", + "specializations": [["TypeErodableDilatable"]] + } + ] + }, + "vpNetwork": { + "methods": [ + { + "static": false, + "signature": "int sendTo(T*, const unsigned int&, const unsigned int&)", + "ignore": true + }, + { + "static": false, + "signature": "int send(T*, const int unsigned&)", + "ignore": true + }, + { + "static": false, + "signature": "int receiveFrom(T*, const unsigned int&, const unsigned int&)", + "ignore": true + }, + { + "static": false, + "signature": "int receive(T*, const unsigned int&)", + "ignore": true + }, + { + "static": false, + "signature": "void addDecodingRequest(vpRequest*)", + "ignore": true + } + ] + }, + "vpUDPClient": { + "methods": [ + { + "static": false, + "signature": "int send(const void*, size_t)", + "ignore": true + }, + { + "static": false, + "signature": "int receive(void*, size_t, int)", + "ignore": true + }, + { + "static": false, + "signature": "int receive(std::string&, int)", + "use_default_param_policy": false, + "param_is_input": [ + false, + true + ], + "param_is_output": [ + true, + false + ] + } + ] + }, + "vpUDPServer": { + "methods": [ + { + "static": false, + "signature": "int receive(std::string&, std::string&, int)", + "use_default_param_policy": false, + "param_is_input": [false, false, true], + "param_is_output": [true, true, false] + }, + { + "static": false, + "signature": "int receive(std::string&, int)", + "use_default_param_policy": false, + "param_is_input": [false, true], + "param_is_output": [true, false] + } + ] + }, + "vpUniRand": { + "methods": [ + { + "static": true, + "signature": "std::vector shuffleVector(const std::vector&)", + "specializations": [["TypePythonScalar"]] + } + ] + }, + "vpHistogram": { + "methods": [ + { + "static": false, + "signature": "unsigned getPeaks(std::list&)", + "use_default_param_policy": false, + "param_is_input": [false], + "param_is_output": [true] + }, + { + "static": false, + "signature": "unsigned getValey(std::list&)", + "use_default_param_policy": false, + "param_is_input": [false], + "param_is_output": [true] + }, + { + "static": false, + "signature": "unsigned sort(std::list&)", + "use_default_param_policy": false, + "param_is_input": [ + true + ], + "param_is_output": [ + true + ] + } + ] } - } - } diff --git a/modules/python/config/core_image.json b/modules/python/config/core_image.json new file mode 100644 index 0000000000..11369876c3 --- /dev/null +++ b/modules/python/config/core_image.json @@ -0,0 +1,535 @@ +{ + "classes": { + "vpImage": { + "ignore_repr": true, + "additional_bindings": "bindings_vpImage", + "use_buffer_protocol": true, + "specializations": [ + { + "python_name": "ImageGray", + "arguments": ["unsigned char"] + }, + { + "python_name": "ImageFloat", + "arguments": ["float"] + }, + { + "python_name": "ImageDouble", + "arguments": ["double"] + }, + { + "python_name": "ImageUInt16", + "arguments": ["uint16_t"] + }, + { + "python_name": "ImageRGBa", + "arguments": ["vpRGBa"] + }, + { + "python_name": "ImageRGBf", + "arguments": ["vpRGBf"] + } + ], + "methods": + [ + { + "static": true, + "signature": "void insert(const vpArray2D &, const vpArray2D &, vpArray2D &, unsigned int, unsigned int)", + "custom_name": "insertStatic" + }, + { + "static": false, + "signature": "vpImage(vpImage&&)", + "ignore": true + }, + { + "static": false, + "signature": "vpImage(Type*, unsigned int, unsigned int, bool)", + "ignore": true + }, + { + "static": false, + "signature": "void init(Type*, unsigned int, unsigned int, bool)", + "ignore": true + }, + { + "static": false, + "signature": "Type* operator[](unsigned int)", + "ignore": true + }, + { + "static": false, + "signature": "Type* operator[](int)", + "ignore": true + }, + { + "static": false, + "signature": "const Type* operator[](unsigned int)", + "ignore": true + }, + { + "static": false, + "signature": "const Type* operator[](int)", + "ignore": true + } + ] + }, + "vpRGBf": { + "methods": [ + { + "static": false, + "signature": "vpRGBf& operator=(const vpRGBf&&)", + "ignore": true + } + ] + }, + "vpRGBa": { + "methods": [ + { + "static": false, + "signature": "vpRGBa& operator=(const vpRGBa&&)", + "ignore": true + } + ] + }, + "vpImageTools": { + "methods": + [ + { + "static": true, + "signature": "void warpImage(const vpImage&, const vpMatrix&, vpImage&, const vpImageTools::vpImageInterpolationType&, bool, bool)", + "specializations": + [ + ["unsigned char"], + ["float"], + ["double"], + ["uint16_t"], + ["vpRGBa"] + ] + }, + { + "static": true, + "signature": "void crop(const vpImage&, double, double, unsigned int, unsigned int, vpImage&, unsigned int, unsigned int)", + "specializations": + [ + ["TypeImage"] + ] + }, + { + "static": true, + "signature": "void crop(const vpImage&, const vpImagePoint&, unsigned int, unsigned int, vpImage&, unsigned int, unsigned int)", + "specializations": + [ + ["TypeImage"] + ] + }, + { + "static": true, + "signature": "void crop(const vpImage&, const vpRect&, vpImage&, unsigned int, unsigned int)", + "specializations": + [ + ["TypeImage"] + ] + }, + { + "static": true, + "signature": "void crop(const unsigned char*, unsigned int, unsigned int, const vpRect&, vpImage&, unsigned int, unsigned int)", + "ignore": true + } + ] + }, + "vpImageConvert": { + "additional_bindings": "bindings_vpImageConvert", + "methods": + [ + { + "static": true, + "signature": "void RGBaToRGB(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void RGBaToGrey(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void RGBToRGBa(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void RGBToGrey(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void RGBToGrey(unsigned char*, unsigned char*, unsigned int, unsigned int, bool)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void RGBToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int, bool)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUV444ToRGBa(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUV444ToRGB(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUV444ToGrey(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void GreyToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void GreyToRGBa(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void GreyToRGB(unsigned char*, unsigned char*, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void GreyToRGB(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUYVToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUYVToRGB(unsigned char*, unsigned char*, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUYVToGrey(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUV411ToRGBa(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUV411ToRGB(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUV411ToGrey(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUV422ToRGBa(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUV422ToRGB(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUV422ToGrey(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUV420ToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUV420ToRGB(unsigned char*, unsigned char*, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YUV420ToGrey(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YV12ToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YV12ToRGB(unsigned char*, unsigned char*, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YVU9ToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YVU9ToRGB(unsigned char*, unsigned char*, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YCbCrToRGB(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YCbCrToRGBa(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YCbCrToGrey(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YCrCbToRGB(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void YCrCbToRGBa(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void RGBaToHSV(const unsigned char*, unsigned char*, unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void HSVToRGB(const double*, const double*, const double*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void HSVToRGB(const unsigned char*, const unsigned char*, const unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void HSVToRGBa(const unsigned char*, const unsigned char*, const unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void HSVToRGBa(const double*, const double*, const double*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void RGBToHSV(const unsigned char*, double*, double*, double*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void RGBToHSV(const unsigned char*, unsigned char*, unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void RGBaToHSV(const unsigned char*, unsigned char*, unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void RGBaToHSV(const unsigned char*, double*, double*, double*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void MONO16ToGrey(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void MONO16ToRGBa(unsigned char*, unsigned char*, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void BGRaToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int, bool)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void BGRToGrey(unsigned char*, unsigned char*, unsigned int, unsigned int, bool, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void BGRToRGBa(unsigned char*, unsigned char*, unsigned int, unsigned int, bool)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void demosaicBGGRToRGBaBilinear(const uint8_t*, uint8_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void demosaicBGGRToRGBaBilinear(const uint16_t*, uint16_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void demosaicGBRGToRGBaBilinear(const uint8_t*, uint8_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void demosaicGBRGToRGBaBilinear(const uint16_t*, uint16_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void demosaicGRBGToRGBaBilinear(const uint8_t*, uint8_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + + }, + { + "static": true, + "signature": "void demosaicGRBGToRGBaBilinear(const uint16_t*, uint16_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void demosaicRGGBToRGBaBilinear(const uint8_t*, uint8_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void demosaicRGGBToRGBaBilinear(const uint16_t*, uint16_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void demosaicBGGRToRGBaMalvar(const uint8_t*, uint8_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void demosaicBGGRToRGBaMalvar(const uint16_t*, uint16_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void demosaicGBRGToRGBaMalvar(const uint8_t*, uint8_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + + }, + { + "static": true, + "signature": "void demosaicGBRGToRGBaMalvar(const uint16_t*, uint16_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + + }, + { + "static": true, + "signature": "void demosaicGRBGToRGBaMalvar(const uint8_t*, uint8_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void demosaicGRBGToRGBaMalvar(const uint16_t*, uint16_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void demosaicRGGBToRGBaMalvar(const uint8_t*, uint8_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + }, + { + "static": true, + "signature": "void demosaicRGGBToRGBaMalvar(const uint16_t*, uint16_t*, unsigned int, unsigned int, unsigned int)", + "ignore": true, + "custom_implem": true + } + ] + } + } +} diff --git a/modules/python/config/core_math.json b/modules/python/config/core_math.json new file mode 100644 index 0000000000..36a6d0bff0 --- /dev/null +++ b/modules/python/config/core_math.json @@ -0,0 +1,372 @@ +{ + "classes": { + "vpArray2D": { + "additional_bindings": "bindings_vpArray2D", + "use_buffer_protocol": true, + "specializations": [ + { + "python_name": "ArrayDouble2D", + "arguments": ["double"] + } + ], + "methods": + [ + { + "static": true, + "signature": "void insert(const vpArray2D &, const vpArray2D &, vpArray2D &, unsigned int, unsigned int)", + "custom_name": "insertStatic" + }, + { + "static": false, + "signature": " vpArray2D(vpArray2D&&)", + "ignore": true + }, + { + "static": false, + "signature": "vpArray2D& operator=(vpArray2D&&)", + "ignore": true + } + ] + }, + "vpMath" :{ + "methods": [ + { + "static": true, + "signature": "double lineFitting(const std::vector&, double&, double&, double&)", + "use_default_param_policy": false, + "param_is_input": [ + true, + false, + false, + false + ], + "param_is_output": [ + false, + true, + true, + true + ] + }, + { + "static": true, + "signature": "void swap(Type&, Type&)", + "ignore": true + }, + { + "static": true, + "signature": "Tp saturate(unsigned char)", + "ignore": true + }, + { + "static": true, + "signature": "Tp saturate(char)", + "ignore": true + }, + { + "static": true, + "signature": "Tp saturate(unsigned short)", + "ignore": true + }, + { + "static": true, + "signature": "Tp saturate(short)", + "ignore": true + }, + { + "static": true, + "signature": "Tp saturate(unsigned)", + "ignore": true + }, + { + "static": true, + "signature": "Tp saturate(int)", + "ignore": true + }, + { + "static": true, + "signature": "Tp saturate(float)", + "ignore": true + }, + { + "static": true, + "signature": "Tp saturate(double)", + "ignore": true + }, + { + "static": true, + "signature": "std::vector linspace(T, T, unsigned int)", + "specializations": [["TypePythonScalar"]] + }, + { + "static": true, + "signature": "Type abs(const Type&)", + "specializations": [["TypePythonScalar"]] + }, + { + "static": true, + "signature": "void swap(Type&, Type&)", + "specializations": [["TypePythonScalar"]] + }, + { + "static": true, + "signature": "Type minimum(const Type&, const Type&)", + "specializations": [["TypePythonScalar"]] + }, + { + "static": true, + "signature": "Type maximum(const Type&, const Type&)", + "specializations": [["TypePythonScalar"]] + }, + { + "static": true, + "signature": "T clamp(const T&, const T&, const T&)", + "specializations": [["TypePythonScalar"]] + } + ] + }, + "vpTranslationVector": { + "additional_bindings": "bindings_vpTranslationVector", + "methods": + [ + { + "static": true, + "signature": "vpMatrix skew(const vpTranslationVector &)", + "custom_name": "skewOf" + }, + { + "static": true, + "signature": "void skew(const vpTranslationVector &, vpMatrix&)", + "custom_name": "skewOf" + } + ] + }, + "vpColVector": { + "additional_bindings": "bindings_vpColVector", + "use_buffer_protocol": true, + "methods": [ + { + "static": true, + "signature": "vpColVector stack(const vpColVector &, const vpColVector &)", + "custom_name": "stackVectors" + }, + { + "static": true, + "signature": "void stack(const vpColVector &, const vpColVector &, vpColVector &)", + "custom_name": "stackVectors" + }, + { + "static": false, + "signature": "std::ostream& maplePrint(std::ostream&)", + "ignore": true + }, + { + "static": false, + "signature": "std::ostream& matlabPrint(std::ostream&)", + "ignore": true + }, + { + "static": false, + "signature": " vpColVector(vpColVector&&)", + "ignore": true + }, + { + "static": false, + "signature": "vpColVector& operator=(vpColVector&&)", + "ignore": true + } + ] + }, + "vpRowVector": { + "additional_bindings": "bindings_vpRowVector", + "use_buffer_protocol": true, + "methods": [ + { + "static": true, + "signature": "vpRowVector stack(const vpRowVector &, const vpRowVector &)", + "custom_name": "stackVectors" + }, + { + "static": true, + "signature": "void stack(const vpRowVector &, const vpRowVector &, vpRowVector &)", + "custom_name": "stackVectors" + }, + { + "static": false, + "signature": "std::ostream& maplePrint(std::ostream&)", + "ignore": true + }, + { + "static": false, + "signature": "std::ostream& matlabPrint(std::ostream&)", + "ignore": true + }, + { + "static": false, + "signature": "std::ostream& csvPrint(std::ostream&)", + "ignore": true + }, + { + "static": false, + "signature": "std::ostream& cppPrint(std::ostream&, const std::string&, bool)", + "ignore": true + }, + { + "static": false, + "signature": " vpRowVector(vpRowVector&&)", + "ignore": true + }, + { + "static": false, + "signature": "vpRowVector& operator=(vpRowVector&&)", + "ignore": true + } + ] + }, + "vpMatrix": { + "ignore_repr": true, + "additional_bindings": "bindings_vpMatrix", + "use_buffer_protocol": true, + "methods": + [ + { + + "static": true, + "signature": "vpMatrix insert(const vpMatrix &, const vpMatrix &, unsigned int , unsigned int)", + "custom_name": "insertMatrixInMatrix" + }, + { + + "static": true, + "signature": "void insert(const vpMatrix &, const vpMatrix &, vpMatrix &, unsigned int , unsigned int)", + "custom_name": "insertMatrixInMatrix" + }, + { + + "static": true, + "signature": "void kron(const vpMatrix &, const vpMatrix &, vpMatrix &)", + "custom_name": "kronStatic" + }, + { + + "static": true, + "signature": "vpMatrix kron(const vpMatrix &, const vpMatrix &)", + "custom_name": "kronStatic" + }, + { + + "signature": "vpMatrix stack(const vpMatrix &, const vpMatrix &)", + "static": true, + "custom_name": "stackMatrices" + }, + { + "static": true, + "signature": "vpMatrix stack(const vpMatrix &, const vpRowVector &)", + "custom_name": "stackRow" + }, + { + + "signature": "vpMatrix stack(const vpMatrix &, const vpColVector &)", + "static": true, + "custom_name": "stackColumn" + }, + { + "signature": "void stack(const vpMatrix &, const vpMatrix &, vpMatrix &)", + "static": true, + "custom_name": "stackMatrices" + }, + { + "signature": "void stack(const vpMatrix &, const vpRowVector &, vpMatrix &)", + "static": true, + "custom_name": "stackRow" + }, + { + "signature": "void stack(const vpMatrix &, const vpColVector &, vpMatrix &)", + "static": true, + "custom_name": "stackColumn" + }, + { + "static": false, + "signature": "std::ostream& cppPrint(std::ostream&, const std::string&, bool)", + "ignore": true + }, + { + "static": false, + "signature": "std::ostream& csvPrint(std::ostream&)", + "ignore": true + }, + { + "static": false, + "signature": "std::ostream& maplePrint(std::ostream&)", + "ignore": true + }, + { + "static": false, + "signature": "std::ostream& matlabPrint(std::ostream&)", + "ignore": true + }, + { + "static": false, + "signature": " vpMatrix(vpMatrix&&)", + "ignore": true + }, + { + "static": false, + "signature": "vpMatrix& operator=(vpMatrix&&)", + "ignore": true + } + ] + }, + "vpRotationMatrix": { + "additional_bindings": "bindings_vpRotationMatrix", + "use_buffer_protocol": true + }, + "vpHomogeneousMatrix": { + "additional_bindings": "bindings_vpHomogeneousMatrix", + "use_buffer_protocol": true, + "methods": [ + { + "static": false, + "signature": "void convert(std::vector&)", + "use_default_param_policy": false, + "param_is_input": [ + false + ], + "param_is_output": [ + true + ] + }, + { + "static": false, + "signature": "void convert(std::vector&)", + "ignore": true + } + ] + }, + "vpThetaUVector": { + "methods": [ + { + "static": false, + "signature": "void extract(double&, vpColVector&)", + "use_default_param_policy": false, + "param_is_input": [ + false, + false + ], + "param_is_output": [ + true, + true + ] + } + ] + }, + "vpRobust": { + "methods": [ + { + "static": false, + "signature": "vpRobust& operator=(const vpRobust&&)", + "ignore": true + } + ] + } + } +} diff --git a/modules/python/config/gui.json b/modules/python/config/gui.json index 294b0d30ac..d35b0635bc 100644 --- a/modules/python/config/gui.json +++ b/modules/python/config/gui.json @@ -1,5 +1,5 @@ { - "ignored_headers": [], + "ignored_headers": ["vpWin32Renderer.h", "vpWin32Window.h", "vpWin32API.h", "vpGDIRenderer.h"], "ignored_classes": [], "user_defined_headers": [], "classes": {}, diff --git a/modules/python/doc/_templates/custom-class-template.rst b/modules/python/doc/_templates/custom-class-template.rst index 96f82162f4..8f72e62d15 100644 --- a/modules/python/doc/_templates/custom-class-template.rst +++ b/modules/python/doc/_templates/custom-class-template.rst @@ -6,7 +6,7 @@ :members: :show-inheritance: :member-order: groupwise - :inherited-members: pybind11_builtins.pybind11_object + :inherited-members: pybind11_builtins.pybind11_object, pybind11_object :special-members: {% block methods %} diff --git a/modules/python/doc/conf.py.in b/modules/python/doc/conf.py.in index cd46bf0496..5f0df5e14b 100644 --- a/modules/python/doc/conf.py.in +++ b/modules/python/doc/conf.py.in @@ -389,6 +389,7 @@ object_description_options = [ python_type_aliases = { "_visp.": "visp.", + "visp._visp.": "visp." } diff --git a/modules/python/doc/rst/dev/dev.rst b/modules/python/doc/rst/dev/dev.rst index a3478c4bb0..e41f275ae0 100644 --- a/modules/python/doc/rst/dev/dev.rst +++ b/modules/python/doc/rst/dev/dev.rst @@ -64,8 +64,8 @@ Python side -Errors when generating bindings -------------------------------------- +Errors and issues when generating bindings +========================================== When modifying the bindings, you may encounter errors. @@ -79,6 +79,8 @@ Static and member methods have the same name If, when importing visp in python, you encounter this message: +:: + ImportError: overloading a method with both static and instance methods is not supported; error while attempting to bind instance method visp.xxx() -> None Then it means that a class has both a static method and a member method with the same name. You should :ref:`rename either one through the config files `. @@ -88,6 +90,8 @@ Abstract class not detected If you have this error: +:: + error: invalid new-expression of abstract class type ‘vpTemplateTrackerMI’ return new Class{std::forward(args)...}; In file included from /home/visp_ws/visp_build/modules/python/bindings/src/tt_mi.cpp:13:0: @@ -96,3 +100,39 @@ If you have this error: You should define the class (here vpTemplaterMI) as pure virtual in the config file (via the flag is_virtual). This error occurs because some methods are defined as pure virtual in a parent class and are not defined in the class this class: Pure virtual class detection does not look in the class hierarchy but only at the present class. + + +Template errors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you have an issue that looks like: + +:: + + Consolidate compiler generated dependencies of target _visp + [ 97%] Building CXX object modules/python/bindings/CMakeFiles/_visp.dir/src/core.cpp.o + [ 97%] Building CXX object modules/python/bindings/CMakeFiles/_visp.dir/src/robot.cpp.o + In file included from /usr/include/c++/11/bits/move.h:57, + from /usr/include/c++/11/bits/stl_pair.h:59, + from /usr/include/c++/11/bits/stl_algobase.h:64, + from /usr/include/c++/11/bits/specfun.h:45, + from /usr/include/c++/11/cmath:1935, + from /usr/include/c++/11/math.h:36, + from /home/sfelton/miniconda3/envs/wrapper3.9/include/python3.9/pyport.h:205, + from /home/sfelton/miniconda3/envs/wrapper3.9/include/python3.9/Python.h:50, + from /home/sfelton/.local/include/pybind11/detail/common.h:266, + from /home/sfelton/.local/include/pybind11/attr.h:13, + from /home/sfelton/.local/include/pybind11/detail/class.h:12, + from /home/sfelton/.local/include/pybind11/pybind11.h:13, + from /home/sfelton/software/visp_build/modules/python/bindings/src/robot.cpp:3: + /usr/include/c++/11/type_traits: **In instantiation of ‘struct std::is_move_constructible >’:** + /usr/include/c++/11/type_traits:152:12: required from ‘struct std::__and_ >, std::is_move_assignable > >’ + /usr/include/c++/11/type_traits:157:12: required from ‘struct std::__and_ > >, std::is_move_constructible >, std::is_move_assignable > >’ + /usr/include/c++/11/type_traits:2209:11: required by substitution of ‘template using _Require = std::__enable_if_t >::value> [with _Cond = {std::__not_ > >, std::is_move_constructible >, std::is_move_assignable >}]’ + /usr/include/c++/11/bits/move.h:196:5: required by substitution of ‘template std::_Require >, std::is_move_constructible<_Tp>, std::is_move_assignable<_Tp> > std::swap(_Tp&, _Tp&) [with _Tp = vpImage]’ + /home/sfelton/software/visp-sfelton/modules/core/include/visp3/core/vpImage.h:341:15: required from ‘class vpImage’ + /home/sfelton/software/visp-sfelton/modules/core/include/visp3/core/vpImage.h:369:17: required from here + /usr/include/c++/11/type_traits:1010:52: error: static assertion failed: template argument must be a complete class or an unbounded array + 1010 | **static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),** + +You should delete the files in `modules/python/` of the build directory. diff --git a/modules/python/generator/visp_python_bindgen/doc_parser.py b/modules/python/generator/visp_python_bindgen/doc_parser.py index 4dd47d8325..2e4fc4eafa 100644 --- a/modules/python/generator/visp_python_bindgen/doc_parser.py +++ b/modules/python/generator/visp_python_bindgen/doc_parser.py @@ -84,9 +84,18 @@ def to_cstring(s: str) -> str: s = re.sub('\n\n\n+', '\n\n', s) s = re.sub('\\\\ +', '\\\\', s) - return f'''R"doc( -{s} -)doc"''' + # On Windows, strings have a maximum length. + per_string_limit = 8192 + current_char = 0 + result = '' + while current_char < len(s): + result += f'''R"doc( +{s[current_char: min((current_char + per_string_limit), len(s))]})doc" + +''' + current_char += per_string_limit + return result + @dataclass class MethodDocSignature: diff --git a/modules/python/generator/visp_python_bindgen/generator.py b/modules/python/generator/visp_python_bindgen/generator.py index 51216b27eb..5b80634664 100644 --- a/modules/python/generator/visp_python_bindgen/generator.py +++ b/modules/python/generator/visp_python_bindgen/generator.py @@ -75,6 +75,7 @@ def main_str(submodule_fn_declarations, submodule_fn_calls): ''' return f''' //#define PYBIND11_DETAILED_ERROR_MESSAGES +#include #include namespace py = pybind11; {submodule_fn_declarations} @@ -101,6 +102,7 @@ def generate_module(generate_path: Path, config_path: Path) -> None: # This parallel implementation is disabled, # since the behaviour on Mac is different and leads to preprocessing not finding vpConfig.h and others # Reverting to a single process version fixes the issue + # with Pool() as pool: # new_all_headers = [] # for result in list(tqdm(pool.imap(header_preprocess, all_headers), total=len(all_headers), file=sys.stderr)): diff --git a/modules/python/generator/visp_python_bindgen/generator_config.py b/modules/python/generator/visp_python_bindgen/generator_config.py index 67b15576dc..4735813a71 100644 --- a/modules/python/generator/visp_python_bindgen/generator_config.py +++ b/modules/python/generator/visp_python_bindgen/generator_config.py @@ -80,7 +80,7 @@ def to_pcpp_args_list(self) -> List[str]: This only encompasses raw types ''' IMMUTABLE_TYPES_REGEXS = [ - '^(float|double|u?int\d+_t|unsigned|unsigned int|size_t|ssize_t|char|long|long\wlong|bool)$', + '^(float|double|u?int\d+_t|int|unsigned|unsigned int|size_t|ssize_t|char|long|long\wlong|bool)$', '^std::string$' ] @@ -156,9 +156,10 @@ def is_forbidden_function_name(name: str) -> bool: @staticmethod def update_from_main_config_file(path: Path) -> None: - assert path.exists() + assert path.exists(), f'Main config file {path} was not found' with open(path, 'r') as main_config_file: main_config = json.load(main_config_file) + print(json.dumps(main_config, indent=2)) logging.info('Updating the generator config from dict: ', main_config) GeneratorConfig.pcpp_config.include_directories = main_config['include_dirs'] @@ -178,7 +179,11 @@ def update_from_main_config_file(path: Path) -> None: deps = modules_dict[module_name].get('dependencies') # Include only headers that are in the VISP source directory - headers = list(filter(lambda h: source_dir in h.parents, headers)) + # Fix: Check specifically in the modules directory, + # since the build directory (containing vpConfig.h, that we want to ignore) + # Can be in the src folder + headers = list(filter(lambda h: (source_dir / 'modules') in h.parents, headers)) + headers_log_str = '\n\t'.join([str(header) for header in headers]) logging.info(f'Module {module_name} headers: \n\t{headers_log_str}') GeneratorConfig.module_data.append(ModuleInputData(module_name, headers, deps)) diff --git a/modules/python/generator/visp_python_bindgen/header.py b/modules/python/generator/visp_python_bindgen/header.py index faa19986aa..c32bc6db6a 100644 --- a/modules/python/generator/visp_python_bindgen/header.py +++ b/modules/python/generator/visp_python_bindgen/header.py @@ -49,6 +49,8 @@ from visp_python_bindgen.doc_parser import * from visp_python_bindgen.header_utils import * from visp_python_bindgen.generator_config import GeneratorConfig +from visp_python_bindgen.template_expansion import expand_templates + from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -162,7 +164,9 @@ def run_preprocessor(self): if not line.startswith('#define'): preprocessed_header_lines.append(line) preprocessed_header_content = ''.join(preprocessed_header_lines) + # Further refine header content: fix some simple parsing bugs preprocessed_header_content = preprocessed_header_content.replace('#include<', '#include <') # Bug in cpp header parser + preprocessed_header_content = preprocessed_header_content.replace('inline friend', 'friend inline') # Bug in cpp header parser return preprocessed_header_content @@ -239,7 +243,7 @@ def generate_class(self, bindings_container: BindingsContainer, cls: ClassScope, If it is templated, the mapping (template argument types => Python class name) must be provided in the JSON config file ''' - def generate_class_with_potiental_specialization(name_python: str, owner_specs: OrderedDict[str, str], cls_config: Dict) -> str: + def generate_class_with_potiental_specialization(name_python: str, owner_specs: 'OrderedDict[str, str]', cls_config: Dict) -> str: ''' Generate the bindings of a single class, handling a potential template specialization. The handled information is: @@ -353,9 +357,6 @@ def add_method_doc_to_pyargs(method: types.Method, py_arg_strs: List[str]) -> Li is_operator=False, is_constructor=True)) # Operator definitions - binary_return_ops = supported_const_return_binary_op_map() - binary_in_place_ops = supported_in_place_binary_op_map() - unary_return_ops = supported_const_return_unary_op_map() for method, method_config in operators: method_name = get_name(method.name) @@ -365,46 +366,50 @@ def add_method_doc_to_pyargs(method: types.Method, py_arg_strs: List[str]) -> Li py_args = get_py_args(method.parameters, owner_specs, header_env.mapping) py_args = py_args + ['py::is_operator()'] param_names = [param.name or 'arg' + str(i) for i, param in enumerate(method.parameters)] - py_args = add_method_doc_to_pyargs(method, py_args) - if len(params_strs) > 1: - logging.info(f'Found operator {name_cpp}{method_name} with more than one parameter, skipping') - rejection = RejectedMethod(name_cpp, method, method_config, get_method_signature(method_name, return_type_str, params_strs), NotGeneratedReason.NotHandled) - self.submodule.report.add_non_generated_method(rejection) - continue - elif len(params_strs) < 1: - for cpp_op, python_op_name in unary_return_ops.items(): + # if len(params_strs) > 1: + # logging.info(f'Found operator {name_cpp}{method_name} with more than one parameter, skipping') + # rejection_param_strs = [get_type(param.type, {}, header_env.mapping) for param in method.parameters] + # rejection_return_type_str = get_type(method.return_type, {}, header_env.mapping) + # rejection = RejectedMethod(name_cpp, method, method_config, get_method_signature(method_name, rejection_return_type_str, rejection_param_strs), NotGeneratedReason.NotHandled) + # self.submodule.report.add_non_generated_method(rejection) + # continue + if len(params_strs) < 1: # Unary ops + for cpp_op, python_op_name in supported_const_return_unary_op_map().items(): if method_name == f'operator{cpp_op}': - operator_str = f''' -{python_ident}.def("__{python_op_name}__", []({"const" if method_is_const else ""} {name_cpp}& self) -> {return_type_str} {{ - return {cpp_op}self; -}}, {", ".join(py_args)});''' + operator_str = lambda_const_return_unary_op(python_ident, python_op_name, cpp_op, + method_is_const, name_cpp, + return_type_str, py_args) add_to_method_dict(f'__{python_op_name}__', MethodBinding(operator_str, is_static=False, is_lambda=True, is_operator=True, is_constructor=False)) break - - logging.info(f'Found unary operator {name_cpp}::{method_name}, skipping') - continue - for cpp_op, python_op_name in binary_return_ops.items(): - if method_name == f'operator{cpp_op}': - operator_str = f''' -{python_ident}.def("__{python_op_name}__", []({"const" if method_is_const else ""} {name_cpp}& self, {params_strs[0]} o) -> {return_type_str} {{ - return (self {cpp_op} o); -}}, {", ".join(py_args)});''' - add_to_method_dict(f'__{python_op_name}__', MethodBinding(operator_str, is_static=False, is_lambda=True, - is_operator=True, is_constructor=False)) - break - for cpp_op, python_op_name in binary_in_place_ops.items(): - if method_name == f'operator{cpp_op}': - operator_str = f''' -{python_ident}.def("__{python_op_name}__", []({"const" if method_is_const else ""} {name_cpp}& self, {params_strs[0]} o) -> {return_type_str} {{ - self {cpp_op} o; - return self; -}}, {", ".join(py_args)});''' - add_to_method_dict(f'__{python_op_name}__', MethodBinding(operator_str, is_static=False, is_lambda=True, - is_operator=True, is_constructor=False)) - break + elif len(params_strs) == 1: # e.g., self + other + for cpp_op, python_op_name in supported_const_return_binary_op_map().items(): + if method_name == f'operator{cpp_op}': + operator_str = lambda_const_return_binary_op(python_ident, python_op_name, cpp_op, + method_is_const, name_cpp, params_strs[0], + return_type_str, py_args) + add_to_method_dict(f'__{python_op_name}__', MethodBinding(operator_str, is_static=False, is_lambda=True, + is_operator=True, is_constructor=False)) + break + for cpp_op, python_op_name in supported_in_place_binary_op_map().items(): + if method_name == f'operator{cpp_op}': + operator_str = lambda_in_place_binary_op(python_ident, python_op_name, cpp_op, + method_is_const, name_cpp, params_strs[0], + return_type_str, py_args) + add_to_method_dict(f'__{python_op_name}__', MethodBinding(operator_str, is_static=False, is_lambda=True, + is_operator=True, is_constructor=False)) + break + else: # N-ary operators + for cpp_op, python_op_name in supported_nary_op_map().items(): + if method_name == f'operator{cpp_op}': + operator_str = lambda_nary_op(python_ident, python_op_name, cpp_op, + method_is_const, name_cpp, params_strs, + return_type_str, py_args) + add_to_method_dict(f'__{python_op_name}__', MethodBinding(operator_str, is_static=False, is_lambda=True, + is_operator=True, is_constructor=False)) + break # Define classical methods class_def_names = BoundObjectNames(python_ident, name_python, name_cpp_no_template, name_cpp) @@ -412,6 +417,7 @@ def add_method_doc_to_pyargs(method: types.Method, py_arg_strs: List[str]) -> Li if method.template is not None and method_config.get('specializations') is not None: method_template_names = [t.name for t in method.template.params] specializations = method_config.get('specializations') + specializations = expand_templates(specializations) for method_spec in specializations: new_specs = owner_specs.copy() assert len(method_template_names) == len(method_spec) @@ -482,7 +488,7 @@ def add_method_doc_to_pyargs(method: types.Method, py_arg_strs: List[str]) -> Li if len(error_generating_overloads) > 0: logging.error(f'Overloads defined for instance and class, this will generate a pybind error') logging.error(error_generating_overloads) - raise RuntimeError + raise RuntimeError('Error generating overloads:\n' + '\n'.join(error_generating_overloads)) field_dict = {} for field in cls.fields: diff --git a/modules/python/generator/visp_python_bindgen/header_utils.py b/modules/python/generator/visp_python_bindgen/header_utils.py index 51ce4778d5..d81c7d5051 100644 --- a/modules/python/generator/visp_python_bindgen/header_utils.py +++ b/modules/python/generator/visp_python_bindgen/header_utils.py @@ -92,7 +92,6 @@ def add_level(result: List['HeaderFile'], remainder: List['HeaderFile'], depende class HeaderEnvironment(): def __init__(self, data: ParsedData): self.mapping: Dict[str, str] = self.build_naive_mapping(data.namespace, {}) - # Step 2: resolve enumeration names that are possibly hidden behind typedefs from visp_python_bindgen.enum_binding import resolve_enums_and_typedefs enum_reprs, _ = resolve_enums_and_typedefs(data.namespace, self.mapping) @@ -101,43 +100,33 @@ def __init__(self, data: ParsedData): self.mapping[value.name] = enum_repr.name + '::' + value.name def build_naive_mapping(self, data: Union[NamespaceScope, ClassScope], mapping, scope: str = ''): - if isinstance(data, NamespaceScope): - for alias in data.using_alias: - mapping[alias.alias] = get_type(alias.type, {}, mapping) - - for typedef in data.typedefs: - mapping[typedef.name] = scope + typedef.name - - for enum in data.enums: - if not name_is_anonymous(enum.typename): - enum_name = '::'.join([seg.name for seg in enum.typename.segments]) - mapping[enum_name] = scope + enum_name - - for cls in data.classes: - cls_name = '::'.join([seg.name for seg in cls.class_decl.typename.segments]) - mapping[cls_name] = scope + cls_name - mapping.update(self.build_naive_mapping(cls, mapping=mapping, scope=f'{scope}{cls_name}::')) + current_mapping = mapping.copy() + previous_mapping = None - for namespace in data.namespaces: - mapping.update(self.build_naive_mapping(data.namespaces[namespace], mapping=mapping, scope=f'{scope}{namespace}::')) + while current_mapping != previous_mapping: + previous_mapping = current_mapping.copy() - elif isinstance(data, ClassScope): for alias in data.using_alias: - mapping[alias.alias] = get_type(alias.type, {}, mapping) + current_mapping[alias.alias] = get_type(alias.type, {}, current_mapping) for typedef in data.typedefs: - mapping[typedef.name] = scope + typedef.name + current_mapping[typedef.name] = scope + typedef.name for enum in data.enums: if not name_is_anonymous(enum.typename): enum_name = '::'.join([seg.name for seg in enum.typename.segments]) - mapping[enum_name] = scope + enum_name + current_mapping[enum_name] = scope + enum_name for cls in data.classes: cls_name = '::'.join([seg.name for seg in cls.class_decl.typename.segments if not isinstance(seg, types.AnonymousName)]) - mapping[cls_name] = scope + cls_name - mapping.update(self.build_naive_mapping(cls, mapping=mapping, scope=f'{scope}{cls_name}::')) - return mapping + current_mapping[cls_name] = scope + cls_name + current_mapping.update(self.build_naive_mapping(cls, mapping=current_mapping, scope=f'{scope}{cls_name}::')) + + if isinstance(data, NamespaceScope): + for namespace in data.namespaces: + current_mapping.update(self.build_naive_mapping(data.namespaces[namespace], mapping=current_mapping, scope=f'{scope}{namespace}::')) + + return current_mapping def update_with_dependencies(self, other_envs: List['HeaderEnvironment']) -> None: for env in other_envs: diff --git a/modules/python/generator/visp_python_bindgen/methods.py b/modules/python/generator/visp_python_bindgen/methods.py index a688e8766e..12748c093d 100644 --- a/modules/python/generator/visp_python_bindgen/methods.py +++ b/modules/python/generator/visp_python_bindgen/methods.py @@ -93,6 +93,13 @@ def supported_const_return_binary_op_map(): '^': 'xor', } +def lambda_const_return_binary_op(python_ident: str, python_op_name: str, cpp_op: str, method_is_const: bool, + cpp_type: str, param_type: str, return_type: str, py_args: List[str]) -> str: + return f''' +{python_ident}.def("__{python_op_name}__", []({"const" if method_is_const else ""} {cpp_type}& self, {param_type} o) -> {return_type} {{ + return (self {cpp_op} o); +}}, {", ".join(py_args)});''' + def supported_in_place_binary_op_map(): return { '+=': 'iadd', @@ -101,11 +108,44 @@ def supported_in_place_binary_op_map(): '/=': 'itruediv', } +def lambda_in_place_binary_op(python_ident: str, python_op_name: str, cpp_op: str, method_is_const: bool, + cpp_type: str, param_type: str, return_type: str, py_args: List[str]) -> str: + return f''' +{python_ident}.def("__{python_op_name}__", []({"const" if method_is_const else ""} {cpp_type}& self, {param_type} o) -> {return_type} {{ + self {cpp_op} o; + return self; +}}, {", ".join(py_args)});''' + def supported_const_return_unary_op_map(): return { '-': 'neg', '~': 'invert', } + +def lambda_const_return_unary_op(python_ident: str, python_op_name: str, cpp_op: str, method_is_const: bool, + cpp_type: str, return_type: str, py_args: List[str]) -> str: + return f''' +{python_ident}.def("__{python_op_name}__", []({"const" if method_is_const else ""} {cpp_type}& self) -> {return_type} {{ + return {cpp_op}self; +}}, {", ".join(py_args)});''' + +def supported_nary_op_map(): + return { + '()': 'call', + } + +def lambda_nary_op(python_ident: str, python_op_name: str, cpp_op: str, method_is_const: bool, + cpp_type: str, param_types: List[str], return_type: str, py_args: List[str]) -> str: + param_names = [f'arg{i}' for i in range(len(param_types))] + param_types_and_names = [f'{t} {n}' for t,n in zip(param_types, param_names)] + maybe_return = '' if return_type == 'void' else 'return' + + return f''' +{python_ident}.def("__{python_op_name}__", []({"const" if method_is_const else ""} {cpp_type}& self, {",".join(param_types_and_names)}) -> {return_type} {{ + {maybe_return} self.operator{cpp_op}({",".join(param_names)}); +}}, {", ".join(py_args)});''' + + def find_and_define_repr_str(cls: ClassScope, cls_name: str, python_ident: str) -> str: for friend in cls.friends: if friend.fn is not None: @@ -311,7 +351,9 @@ def make_keep_alive_str(values) -> str: # Params that are only outputs: they should be declared in function. Assume that they are default constructible param_is_only_output = [not is_input and is_output for is_input, is_output in zip(param_is_input, param_is_output)] - param_declarations = [f'{get_type_for_declaration(method.parameters[i].type, specs, header_env.mapping)} {param_names[i]};' for i in range(len(param_is_only_output)) if param_is_only_output[i]] + param_type_decl = [get_type_for_declaration(method.parameters[i].type, specs, header_env.mapping) for i in range(len(param_is_only_output))] + param_decl_data = [(param_type_decl[i], param_names[i], get_default_assignement_str(param_type_decl[i])) for i in range(len(param_is_only_output)) if param_is_only_output[i]] + param_declarations = [f'{decl_type} {name}{assignment};' for (decl_type, name, assignment) in param_decl_data] param_declarations = '\n'.join(param_declarations) if is_class_method and not method.static: @@ -378,6 +420,7 @@ def define_lambda(capture: str, params: List[str], return_type: Optional[str], b ''' class NotGeneratedReason(Enum): UserIgnored = 'user_ignored', + Deleted = 'deleted', Access = 'access', Destructor = 'destructor', ReturnType = 'return_type' @@ -407,6 +450,7 @@ def get_bindable_methods_with_config(submodule: 'Submodule', methods: List[types # Order of predicates is important: The first predicate that matches will be the one shown in the log, and they do not all have the same importance filtering_predicates_and_motives = [ (lambda _, conf: conf['ignore'], NotGeneratedReason.UserIgnored), + (lambda m, _: m.deleted, NotGeneratedReason.Deleted), (lambda m, _: m.pure_virtual, NotGeneratedReason.PureVirtual), (lambda m, _: m.access is None or m.access != 'public', NotGeneratedReason.Access), (lambda m, _: m.destructor, NotGeneratedReason.Destructor), @@ -415,13 +459,13 @@ def get_bindable_methods_with_config(submodule: 'Submodule', methods: List[types (lambda m, _: not m.constructor and is_unsupported_return_type(m.return_type), NotGeneratedReason.ReturnType) ] for method in methods: - method_config = submodule.get_method_config(cls_name, method, specializations, mapping) + method_config = submodule.get_method_config(cls_name, method, {}, mapping) method_can_be_bound = True for predicate, motive in filtering_predicates_and_motives: if predicate(method, method_config): - return_str = '' if method.return_type is None else (get_type(method.return_type, specializations, mapping) or '') + return_str = '' if method.return_type is None else (get_type(method.return_type, {}, mapping) or '') method_name = '::'.join(seg.name for seg in method.name.segments) - param_strs = [get_type(param.type, specializations, mapping) or '' for param in method.parameters] + param_strs = [get_type(param.type, {}, mapping) or '' for param in method.parameters] rejected_methods.append(RejectedMethod(cls_name, method, method_config, get_method_signature(method_name, return_str, param_strs), motive)) method_can_be_bound = False break diff --git a/modules/python/generator/visp_python_bindgen/preprocessor.py b/modules/python/generator/visp_python_bindgen/preprocessor.py deleted file mode 100644 index 392913f187..0000000000 --- a/modules/python/generator/visp_python_bindgen/preprocessor.py +++ /dev/null @@ -1,201 +0,0 @@ - -############################################################################# -# -# ViSP, open source Visual Servoing Platform software. -# Copyright (C) 2005 - 2023 by Inria. All rights reserved. -# -# This software is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# See the file LICENSE.txt at the root directory of this source -# distribution for additional information about the GNU GPL. -# -# For using ViSP with software that can not be combined with the GNU -# GPL, please contact Inria about acquiring a ViSP Professional -# Edition License. -# -# See https://visp.inria.fr for more information. -# -# This software was developed at: -# Inria Rennes - Bretagne Atlantique -# Campus Universitaire de Beaulieu -# 35042 Rennes Cedex -# France -# -# If you have questions regarding the use of this file, please contact -# Inria at visp@inria.fr -# -# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -# -# Description: -# ViSP Python bindings generator -# -############################################################################# - -# ''' -# Preprocessor, derived from the command line preprocesor provided at https://github.com/ned14/pcpp/blob/master/pcpp/pcmd.py - -# ''' -# from __future__ import generators, print_function, absolute_import, division - -# import sys, argparse, traceback, os, copy, io, re -# from pcpp.preprocessor import Preprocessor, OutputDirective, Action -# from visp_python_bindgen.generator_config import PreprocessorConfig - -# class CmdPreprocessor(Preprocessor): -# def __init__(self, config: PreprocessorConfig, input: str): -# if len(argv) < 2: -# argv = [argv[0], '--help'] -# argp = argparse.ArgumentParser(prog='pcpp', -# description= -# '''A pure universal Python C (pre-)preprocessor implementation very useful for -# pre-preprocessing header only C++ libraries into single file includes and -# other such build or packaging stage malarky.''', -# epilog= -# '''Note that so pcpp can stand in for other preprocessor tooling, it -# ignores any arguments it does not understand.''') -# argp.add_argument('-o', dest = 'output', metavar = 'path', type = argparse.FileType('wt'), default=sys.stdout, nargs = '?', help = 'Output to a file instead of stdout') -# argp.add_argument('-D', dest = 'defines', metavar = 'macro[=val]', nargs = 1, action = 'append', help = 'Predefine name as a macro [with value]') -# argp.add_argument('-U', dest = 'undefines', metavar = 'macro', nargs = 1, action = 'append', help = 'Pre-undefine name as a macro') -# argp.add_argument('-N', dest = 'nevers', metavar = 'macro', nargs = 1, action = 'append', help = 'Never define name as a macro, even if defined during the preprocessing.') -# argp.add_argument('-I', dest = 'includes', metavar = 'path', nargs = 1, action = 'append', help = "Path to search for unfound #include's") -# #argp.add_argument('--passthru', dest = 'passthru', action = 'store_true', help = 'Pass through everything unexecuted except for #include and include guards (which need to be the first thing in an include file') -# argp.add_argument('--passthru-defines', dest = 'passthru_defines', action = 'store_true', help = 'Pass through but still execute #defines and #undefs if not always removed by preprocessor logic') -# argp.add_argument('--passthru-unfound-includes', dest = 'passthru_unfound_includes', action = 'store_true', help = 'Pass through #includes not found without execution') -# argp.add_argument('--passthru-unknown-exprs', dest = 'passthru_undefined_exprs', action = 'store_true', help = 'Unknown macros in expressions cause preprocessor logic to be passed through instead of executed by treating unknown macros as 0L') -# argp.add_argument('--passthru-comments', dest = 'passthru_comments', action = 'store_true', help = 'Pass through comments unmodified') -# argp.add_argument('--passthru-magic-macros', dest = 'passthru_magic_macros', action = 'store_true', help = 'Pass through double underscore magic macros unmodified') -# argp.add_argument('--passthru-includes', dest = 'passthru_includes', metavar = '', default = None, nargs = 1, help = "Regular expression for which #includes to not expand. #includes, if found, are always executed") -# argp.add_argument('--line-directive', dest = 'line_directive', metavar = 'form', default = '#line', nargs = '?', help = "Form of line directive to use, defaults to #line, specify nothing to disable output of line directives") -# args = argp.parse_known_args(argv[1:]) -# #print(args) -# for arg in args[1]: -# print("NOTE: Argument %s not known, ignoring!" % arg, file = sys.stderr) - -# self.args = args[0] -# super(CmdPreprocessor, self).__init__() - -# # Override Preprocessor instance variables -# self.define("__PCPP_ALWAYS_FALSE__ 0") -# self.define("__PCPP_ALWAYS_TRUE__ 1") - -# self.auto_pragma_once_enabled = True -# self.line_directive = config.line_directive -# if self.line_directive is not None and self.line_directive.lower() in ('nothing', 'none', ''): -# self.line_directive = None -# self.passthru_includes = re.compile(config.passthrough_includes_regex) -# self.compress = 0 -# # Pass through magic macros -# if False: -# self.undef('__DATE__') -# self.undef('__TIME__') -# self.expand_linemacro = False -# self.expand_filemacro = False -# self.expand_countermacro = False - -# # My own instance variables -# self.bypass_ifpassthru = False -# self.potential_include_guard = None - -# for d in config.defines: -# if '=' not in d: -# d += '=1' -# d = d.replace('=', ' ', 1) -# self.define(d) -# # for d in config.undefines: -# # self.undef(d) -# self.nevers = config.never_defined -# if self.args.nevers: -# self.args.nevers = [x[0] for x in self.args.nevers] - -# for include in config.include_directories: -# self.add_path(include) - -# try: -# if len(self.args.inputs) == 1: -# self.parse(self.args.inputs[0]) -# else: -# input = '' -# for i in self.args.inputs: -# input += '#include "' + i.name + '"\n' -# self.parse(input) -# self.write(self.args.output) -# except: -# print(traceback.print_exc(10), file = sys.stderr) -# print("\nINTERNAL PREPROCESSOR ERROR AT AROUND %s:%d, FATALLY EXITING NOW\n" -# % (self.lastdirective.source, self.lastdirective.lineno), file = sys.stderr) -# sys.exit(-99) -# finally: -# for i in self.args.inputs: -# i.close() -# if self.args.output != sys.stdout: -# self.args.output.close() - -# def on_include_not_found(self,is_malformed,is_system_include,curdir,includepath): -# if self.args.passthru_unfound_includes: -# raise OutputDirective(Action.IgnoreAndPassThrough) -# return super(CmdPreprocessor, self).on_include_not_found(is_malformed,is_system_include,curdir,includepath) - -# def on_unknown_macro_in_defined_expr(self,tok): -# if self.args.undefines: -# if tok.value in self.args.undefines: -# return False -# if self.args.passthru_undefined_exprs: -# return None # Pass through as expanded as possible -# return super(CmdPreprocessor, self).on_unknown_macro_in_defined_expr(tok) - -# def on_unknown_macro_in_expr(self,ident): -# if self.args.undefines: -# if ident in self.args.undefines: -# return super(CmdPreprocessor, self).on_unknown_macro_in_expr(ident) -# if self.args.passthru_undefined_exprs: -# return None # Pass through as expanded as possible -# return super(CmdPreprocessor, self).on_unknown_macro_in_expr(ident) - -# def on_unknown_macro_function_in_expr(self,ident): -# if self.args.undefines: -# if ident in self.args.undefines: -# return super(CmdPreprocessor, self).on_unknown_macro_function_in_expr(ident) -# if self.args.passthru_undefined_exprs: -# return None # Pass through as expanded as possible -# return super(CmdPreprocessor, self).on_unknown_macro_function_in_expr(ident) - -# def on_directive_handle(self,directive,toks,ifpassthru,precedingtoks): -# if ifpassthru: -# if directive.value == 'if' or directive.value == 'elif' or directive == 'else' or directive.value == 'endif': -# self.bypass_ifpassthru = len([tok for tok in toks if tok.value == '__PCPP_ALWAYS_FALSE__' or tok.value == '__PCPP_ALWAYS_TRUE__']) > 0 -# if not self.bypass_ifpassthru and (directive.value == 'define' or directive.value == 'undef'): -# if toks[0].value != self.potential_include_guard: -# raise OutputDirective(Action.IgnoreAndPassThrough) # Don't execute anything with effects when inside an #if expr with undefined macro -# if (directive.value == 'define' or directive.value == 'undef') and self.args.nevers: -# if toks[0].value in self.args.nevers: -# raise OutputDirective(Action.IgnoreAndPassThrough) -# if self.args.passthru_defines: -# super(CmdPreprocessor, self).on_directive_handle(directive,toks,ifpassthru,precedingtoks) -# return None # Pass through where possible -# return super(CmdPreprocessor, self).on_directive_handle(directive,toks,ifpassthru,precedingtoks) - -# def on_directive_unknown(self,directive,toks,ifpassthru,precedingtoks): -# if ifpassthru: -# return None # Pass through -# return super(CmdPreprocessor, self).on_directive_unknown(directive,toks,ifpassthru,precedingtoks) - -# def on_potential_include_guard(self,macro): -# self.potential_include_guard = macro -# return super(CmdPreprocessor, self).on_potential_include_guard(macro) - -# def on_comment(self,tok): -# if self.args.passthru_comments: -# return True # Pass through -# return super(CmdPreprocessor, self).on_comment(tok) - -# def main(argv=None): -# if argv is None: -# argv = sys.argv -# p = CmdPreprocessor(argv) -# return p.return_code - -# if __name__ == "__main__": -# sys.exit(main(sys.argv)) diff --git a/modules/python/generator/visp_python_bindgen/submodule.py b/modules/python/generator/visp_python_bindgen/submodule.py index 05bcb42dbd..7b9cefc0ce 100644 --- a/modules/python/generator/visp_python_bindgen/submodule.py +++ b/modules/python/generator/visp_python_bindgen/submodule.py @@ -36,6 +36,7 @@ from typing import List, Optional, Dict from pathlib import Path import json +import logging from visp_python_bindgen.header import HeaderFile from visp_python_bindgen.utils import * @@ -58,6 +59,7 @@ def set_dependencies_from_dict(self, dict_modules: Dict[str, 'Submodule'], dep_n if dep_name in dict_modules: deps.append(dict_modules[dep_name]) self.dependencies = deps + def _get_headers(self) -> List[HeaderFile]: headers = [] for include_file in self.include_path.iterdir(): @@ -68,6 +70,7 @@ def _get_headers(self) -> List[HeaderFile]: continue headers.append(HeaderFile(include_file, self)) return headers + def _get_config_file_or_create_default(self, path: Path) -> Dict: if not path.exists(): default_config = { @@ -75,7 +78,8 @@ def _get_config_file_or_create_default(self, path: Path) -> Dict: 'ignored_classes': [], 'user_defined_headers': [], 'classes': {}, - 'enums': {} + 'enums': {}, + 'config_includes': [] } with open(path, 'w') as config_file: json.dump(default_config, config_file) @@ -83,8 +87,44 @@ def _get_config_file_or_create_default(self, path: Path) -> Dict: else: with open(path, 'r') as config_file: config = json.load(config_file) + for included_config_filename in config.get('config_includes', []): + included_config_path: Path = path.parent / included_config_filename + if not included_config_path.exists(): + raise RuntimeError(f'Sub config file {included_config_path} does not exist') + logging.info(f'Trying to load subconfig file: {included_config_path}') + with open(included_config_path, 'r') as additional_config_file: + additional_config = json.load(additional_config_file) + self.add_subconfig_file(config, additional_config) + + + return config + def add_subconfig_file(self, base_config: Dict[str, any], add_config: Dict[str, any]) -> None: + ignored_fields = [ + 'ignored_headers', + 'ignored_classes', + 'user_defined_headers', + 'functions', + 'enums' + ] + for field_name in ignored_fields: + if field_name in add_config: + raise RuntimeError(f'All the field in {ignored_fields} cannot be added in the sub configuration file, but found {field_name}') + + if 'classes' not in base_config: + base_config['classes'] = add_config['classes'] + else: + base_cls_dict: Dict[str, Dict] = base_config['classes'] + add_cls_dict: Dict[str, Dict] = add_config.get('classes', {}) + for k, v in add_config['classes'].items(): + if k not in base_cls_dict: + base_cls_dict[k] = v + else: + raise RuntimeError(f'The configuration for a single class should be contained in a single dictionary, but found multiple definitions for {k}') + + + def set_headers_from_common_list(self, all_headers: List[HeaderFile]) -> None: ''' Set the submodule's headers from a list containing headers from multiple modules @@ -164,6 +204,7 @@ def class_should_be_ignored(self, class_name: str) -> bool: if 'ignored_classes' not in self.config: return False return class_name in self.config['ignored_classes'] + def header_should_be_ignored(self, header_name: str) -> bool: if 'ignored_headers' not in self.config: return False @@ -243,8 +284,8 @@ def get_submodules(config_path: Path, generate_path: Path) -> List[Submodule]: headers = module_data.headers if len(headers) == 0: print(f'Module {module_data.name} has no input headers, skipping!') - continue + # The headers are already filtered: they should all come from the ViSP source folder (no vpConfig, etc.) include_dir = headers[0].parent hh = "\n".join(map(lambda s: str(s), headers)) assert all(map(lambda header_path: header_path.parent == include_dir, headers)), f'Found headers in different directory, this case is not yet handled. Headers = {hh}' diff --git a/modules/python/generator/visp_python_bindgen/template_expansion.py b/modules/python/generator/visp_python_bindgen/template_expansion.py new file mode 100644 index 0000000000..69700e5e19 --- /dev/null +++ b/modules/python/generator/visp_python_bindgen/template_expansion.py @@ -0,0 +1,24 @@ +from typing import Dict, List +import itertools +TEMPLATE_EXPANSION_MAP: Dict[str, List[str]] = { + 'TypeReal': ['float', 'double'], + 'TypePythonScalar': ['int', 'double'], # Python itself doesn't make the distinction between int, uint, int16_t etc. + 'TypeFilterable': ['unsigned char', 'float', 'double'], + 'TypeErodableDilatable': ['unsigned char', 'float', 'double'], + 'TypeImage': ['unsigned char', 'uint16_t', 'float', 'double', 'bool', 'vpRGBa', 'vpRGBf'], + + 'TypeBaseImagePixel': ['unsigned char', 'vpRGBa'] +} + +def expand_templates(specializations: List[List[str]]) -> List[List[str]]: + result = [] + for spec in specializations: + expanded_params = [] + for param in spec: + if param in TEMPLATE_EXPANSION_MAP: + expanded_params.append(TEMPLATE_EXPANSION_MAP[param]) + else: + expanded_params.append([param]) + # Cartesian product: compute all possible combinations when expansions are taken into account + result.extend(list(itertools.product(*expanded_params))) + return result diff --git a/modules/python/generator/visp_python_bindgen/utils.py b/modules/python/generator/visp_python_bindgen/utils.py index a8c9a79302..b12ec4ce95 100644 --- a/modules/python/generator/visp_python_bindgen/utils.py +++ b/modules/python/generator/visp_python_bindgen/utils.py @@ -259,6 +259,19 @@ def get_type_for_declaration(param: Union[types.FunctionType, types.DecoratedTyp else: return get_type(param, owner_specs, header_env_mapping) +def get_default_assignement_str(type: str) -> str: + inits = [ + (['int', 'unsigned', 'uint8_t', 'uint16_t', 'size_t', 'ssize_t'], '0'), + (['float'], '0.f'), + (['double'], '0.0') + ] + + for ini in inits: + if type in ini[0]: + return '= ' + ini[1] + + return '' + def fetch_fully_qualified_id(scope: Union[NamespaceScope, ClassScope], segments: List[str]) -> Union[None, types.EnumDecl, NamespaceScope, ClassScope]: ''' Retrieve the declaration of an object from its fully qualified name. diff --git a/modules/python/stubs/run_stub_generator.py b/modules/python/stubs/run_stub_generator.py index 129c73f9bb..ca5f7aebfe 100644 --- a/modules/python/stubs/run_stub_generator.py +++ b/modules/python/stubs/run_stub_generator.py @@ -39,6 +39,11 @@ import sys import argparse +import pybind11_stubgen +from pybind11_stubgen import __main__ + + + if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--output-root', type=str, help='Path where to save the stubs') @@ -47,13 +52,10 @@ output_root = Path(args.output_root) assert output_root.exists() bin_folder = Path(sys.executable).parent - stubgen_entry_point = bin_folder / 'pybind11-stubgen' - assert stubgen_entry_point.exists() - - subprocess.run([str(stubgen_entry_point), '-o', str(output_root.absolute()), '--ignore-all-errors', '_visp'], check=True) + subprocess.run([sys.executable, '-m', 'pybind11_stubgen', '-o', str(output_root.absolute()), '--ignore-all-errors', 'visp._visp'], check=True) # Generate stubs for the bindings (C++ side) and mock it so that they appear in the true 'visp' package - p = Path('./_visp') + p = Path('./visp/_visp') target_path = Path('./visp-stubs') target_path.mkdir(exist_ok=True) for pyi_file in p.iterdir(): diff --git a/modules/python/bindings/visp/bindings.py b/modules/python/test/CMakeLists.txt similarity index 67% rename from modules/python/bindings/visp/bindings.py rename to modules/python/test/CMakeLists.txt index 05b61273e7..80e91829e4 100644 --- a/modules/python/bindings/visp/bindings.py +++ b/modules/python/test/CMakeLists.txt @@ -29,8 +29,20 @@ # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. # # Description: -# ViSP Python bindings module +# ViSP Python bindings stubs # ############################################################################# -from _visp import * + +add_custom_target(visp_python_bindings_test_dependencies + COMMAND ${PYTHON3_EXECUTABLE} -m pip install ${_pip_args} -r "${CMAKE_CURRENT_SOURCE_DIR}/requirements.txt" + COMMENT "Installing dependencies to test Python bindings..." + DEPENDS visp_python_bindings +) + +add_custom_target(visp_python_bindings_test + COMMAND ${PYTHON3_EXECUTABLE} -m pytest "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND ${PYTHON3_EXECUTABLE} -m pytest "--doctest-glob=*.rst" "${CMAKE_CURRENT_SOURCE_DIR}/../doc" + COMMENT "Testing Python bindings..." + DEPENDS visp_python_bindings_test_dependencies +) diff --git a/modules/python/test/requirements.txt b/modules/python/test/requirements.txt new file mode 100644 index 0000000000..fba349822b --- /dev/null +++ b/modules/python/test/requirements.txt @@ -0,0 +1,3 @@ +numpy +pytest +pytest-sphinx diff --git a/modules/python/test/test_conversions.py b/modules/python/test/test_conversions.py new file mode 100644 index 0000000000..e6a542fb18 --- /dev/null +++ b/modules/python/test/test_conversions.py @@ -0,0 +1,84 @@ +from visp.core import ImageConvert +import numpy as np + +def test_hsv_to_rgb_rgba(): + h, w = 50, 50 + cases = [ + { + 'bytes': 3, + 'input_dtype': np.uint8, + 'fn': ImageConvert.HSVToRGB + }, + { + 'bytes': 3, + 'input_dtype': np.float64, + 'fn': ImageConvert.HSVToRGB + }, + { + 'bytes': 4, + 'input_dtype': np.uint8, + 'fn': ImageConvert.HSVToRGBa + }, + { + 'bytes': 4, + 'input_dtype': np.float64, + 'fn': ImageConvert.HSVToRGBa + } + ] + for case in cases: + hsv = np.zeros((3, h, w), dtype=case['input_dtype']) + rgb = np.ones((h, w, case['bytes']), dtype=np.uint8) + rgb_old = rgb.copy() + case['fn'](hsv, rgb) + assert not np.allclose(rgb, rgb_old) + +def test_rgb_rgba_to_hsv(): + h, w = 50, 50 + cases = [ + { + 'bytes': 3, + 'input_dtype': np.uint8, + 'fn': ImageConvert.RGBToHSV + }, + { + 'bytes': 3, + 'input_dtype': np.float64, + 'fn': ImageConvert.RGBToHSV + }, + { + 'bytes': 4, + 'input_dtype': np.uint8, + 'fn': ImageConvert.RGBaToHSV + }, + { + 'bytes': 4, + 'input_dtype': np.float64, + 'fn': ImageConvert.RGBaToHSV + } + ] + for case in cases: + hsv = np.zeros((3, h, w), dtype=case['input_dtype']) + rgb = np.ones((h, w, case['bytes']), dtype=np.uint8) + hsv_old = hsv.copy() + case['fn'](rgb, hsv) + assert not np.allclose(hsv, hsv_old) + +def test_demosaic(): + h, w = 32, 32 + fns = [ + ImageConvert.demosaicRGGBToRGBaMalvar, + ImageConvert.demosaicGRBGToRGBaMalvar, + ImageConvert.demosaicGBRGToRGBaMalvar, + ImageConvert.demosaicBGGRToRGBaMalvar, + ImageConvert.demosaicRGGBToRGBaBilinear, + ImageConvert.demosaicGRBGToRGBaBilinear, + ImageConvert.demosaicGBRGToRGBaBilinear, + ImageConvert.demosaicBGGRToRGBaBilinear, + ] + for fn in fns: + for dtype in [np.uint8, np.uint16]: + bayer_data = np.ones((h, w), dtype=dtype) * 128 + rgba = np.zeros((h, w, 4), dtype=dtype) + old_rgba = rgba.copy() + fn(bayer_data, rgba) + assert not np.allclose(rgba, old_rgba), f'Error when testing {fn}, with dtype {dtype}' diff --git a/modules/robot/include/visp3/robot/vpPololu.h b/modules/robot/include/visp3/robot/vpPololu.h index a4c16e6bba..c0d62ab647 100644 --- a/modules/robot/include/visp3/robot/vpPololu.h +++ b/modules/robot/include/visp3/robot/vpPololu.h @@ -199,7 +199,7 @@ class VISP_EXPORT vpPololu * * \param[in] pos_pwm : Position in PWM to reach. * - * \param[in] speed_pwm : Speed to use for movement in units of (0.25 μs)/(10 ms). Default is 0, maximum speed. + * \param[in] speed_pwm : Speed to use for movement in units of (0.25 us)/(10 ms). Default is 0, maximum speed. * * \exception When PWM out of range. */ @@ -225,7 +225,7 @@ class VISP_EXPORT vpPololu * Set the pwm velocity of the motor movements. The motor will move to the edge of the * range at the given speed. * - * \param[in] pwm_vel : PWM velocity to use for movement in units of (0.25 μs)/(10 ms). When set to 0, will use the + * \param[in] pwm_vel : PWM velocity to use for movement in units of (0.25 us)/(10 ms). When set to 0, will use the * maximum speed. */ void setPwmVelocity(short pwm_vel); @@ -276,7 +276,7 @@ class VISP_EXPORT vpPololu * * \param speed_rad_s : Speed converted to rad/s. * - * \return Signed speed in units of (0.25 μs)/(10 ms). + * \return Signed speed in units of (0.25 us)/(10 ms). * * \sa speedToRadS() */ @@ -285,7 +285,7 @@ class VISP_EXPORT vpPololu /*! * Convert Pololu's pwm velocity to rad/s velocity. * - * \param[in] speed : Signed speed in units of (0.25 μs)/(10 ms). + * \param[in] speed : Signed speed in units of (0.25 us)/(10 ms). * * \return Speed converted to rad/s. * diff --git a/modules/robot/include/visp3/robot/vpRobotKinova.h b/modules/robot/include/visp3/robot/vpRobotKinova.h index 0e0738305c..885483b683 100644 --- a/modules/robot/include/visp3/robot/vpRobotKinova.h +++ b/modules/robot/include/visp3/robot/vpRobotKinova.h @@ -60,7 +60,8 @@ #elif _WIN32 #include #include -#include +#include +#include #include #include #endif diff --git a/modules/robot/include/visp3/robot/vpRobotPololuPtu.h b/modules/robot/include/visp3/robot/vpRobotPololuPtu.h index 30031bba61..4620c8567a 100644 --- a/modules/robot/include/visp3/robot/vpRobotPololuPtu.h +++ b/modules/robot/include/visp3/robot/vpRobotPololuPtu.h @@ -41,7 +41,6 @@ #include #include -using namespace std; /*! * \class vpRobotPololuPtu diff --git a/modules/sensor/include/visp3/sensor/vpForceTorqueAtiSensor.h b/modules/sensor/include/visp3/sensor/vpForceTorqueAtiSensor.h index c1ac32aeec..e743a158bc 100644 --- a/modules/sensor/include/visp3/sensor/vpForceTorqueAtiSensor.h +++ b/modules/sensor/include/visp3/sensor/vpForceTorqueAtiSensor.h @@ -96,7 +96,6 @@ class VISP_EXPORT vpForceTorqueAtiSensor : public vpComedi */ std::string getCalibrationFile() const { return m_calibfile; } vpColVector getForceTorque() const; - vpColVector getForceTorqueAsync() const; std::string getForceUnits() const; std::string getTorqueUnits() const; diff --git a/modules/sensor/include/visp3/sensor/vpLaserScan.h b/modules/sensor/include/visp3/sensor/vpLaserScan.h index 64a638dbbf..7c98fcd5ce 100644 --- a/modules/sensor/include/visp3/sensor/vpLaserScan.h +++ b/modules/sensor/include/visp3/sensor/vpLaserScan.h @@ -58,7 +58,7 @@ Other data as the start/stop angle, the start/end timestamp are also considered. */ -class VISP_EXPORT vpLaserScan +class /*VISP_EXPORT*/ vpLaserScan { public: /*! Default constructor that initialize all the internal variable to zero. diff --git a/modules/sensor/include/visp3/sensor/vpLaserScanner.h b/modules/sensor/include/visp3/sensor/vpLaserScanner.h index 5d947781b2..2800588bb4 100644 --- a/modules/sensor/include/visp3/sensor/vpLaserScanner.h +++ b/modules/sensor/include/visp3/sensor/vpLaserScanner.h @@ -51,16 +51,16 @@ \brief Class that defines a generic laser scanner. */ -class VISP_EXPORT vpLaserScanner +class /*VISP_EXPORT*/ vpLaserScanner { public: /*! Default constructor that initialize all the internal variable to zero. */ - vpLaserScanner() : ip("null"), port(0){}; + vpLaserScanner() : ip("null"), port(0) { }; /*! Copy constructor. */ - vpLaserScanner(const vpLaserScanner &scanner) : ip(scanner.ip), port(scanner.port){}; + vpLaserScanner(const vpLaserScanner &scanner) : ip(scanner.ip), port(scanner.port) { }; /*! Default destructor that does nothing. */ - virtual ~vpLaserScanner(){}; + virtual ~vpLaserScanner() { }; /*! Set the Ethernet address of the laser. */ void setIpAddress(std::string ip_address) { this->ip = ip_address; }; diff --git a/modules/sensor/src/framegrabber/ueye/vpUeyeGrabber.cpp b/modules/sensor/src/framegrabber/ueye/vpUeyeGrabber.cpp index e73a1b15fe..d82d1250b0 100644 --- a/modules/sensor/src/framegrabber/ueye/vpUeyeGrabber.cpp +++ b/modules/sensor/src/framegrabber/ueye/vpUeyeGrabber.cpp @@ -66,14 +66,16 @@ } /*! \brief image buffer properties structure */ -struct sBufferProps { +struct sBufferProps +{ int width; int height; int bitspp; }; /*! \brief camera feature properties structure */ -struct sCameraProps { +struct sCameraProps +{ bool bUsesImageFormats; int nImgFmtNormal; int nImgFmtDefaultNormal; @@ -84,7 +86,8 @@ struct sCameraProps { /*! * \brief uEye Image parameter structure */ -typedef struct _UEYE_IMAGE { +typedef struct _UEYE_IMAGE +{ char *pBuf; INT nImageID; INT nImageSeqNum; @@ -96,7 +99,7 @@ class vpUeyeGrabber::vpUeyeGrabberImpl public: vpUeyeGrabberImpl() : m_hCamera((HIDS)0), m_activeCameraSelected(-1), m_pLastBuffer(nullptr), m_cameraList(nullptr), m_bLive(true), - m_bLiveStarted(false), m_verbose(false), m_I_temp() + m_bLiveStarted(false), m_verbose(false), m_I_temp() { ZeroMemory(&m_SensorInfo, sizeof(SENSORINFO)); ZeroMemory(&m_CamInfo, sizeof(CAMINFO)); @@ -129,7 +132,8 @@ class vpUeyeGrabber::vpUeyeGrabberImpl if (m_hCamera) { if (!m_bLive) { ret = is_FreezeVideo(m_hCamera, IS_WAIT); - } else { + } + else { if (!m_bLiveStarted) { ret = is_CaptureVideo(m_hCamera, IS_DONT_WAIT); m_bLiveStarted = true; @@ -159,12 +163,12 @@ class vpUeyeGrabber::vpUeyeGrabberImpl if (timestamp_system != nullptr) { std::stringstream ss; ss << ImageInfo.TimestampSystem.wYear << ":" << std::setfill('0') << std::setw(2) - << ImageInfo.TimestampSystem.wMonth << ":" << std::setfill('0') << std::setw(2) - << ImageInfo.TimestampSystem.wDay << ":" << std::setfill('0') << std::setw(2) - << ImageInfo.TimestampSystem.wHour << ":" << std::setfill('0') << std::setw(2) - << ImageInfo.TimestampSystem.wMinute << ":" << std::setfill('0') << std::setw(2) - << ImageInfo.TimestampSystem.wSecond << ":" << std::setfill('0') << std::setw(3) - << ImageInfo.TimestampSystem.wMilliseconds; + << ImageInfo.TimestampSystem.wMonth << ":" << std::setfill('0') << std::setw(2) + << ImageInfo.TimestampSystem.wDay << ":" << std::setfill('0') << std::setw(2) + << ImageInfo.TimestampSystem.wHour << ":" << std::setfill('0') << std::setw(2) + << ImageInfo.TimestampSystem.wMinute << ":" << std::setfill('0') << std::setw(2) + << ImageInfo.TimestampSystem.wSecond << ":" << std::setfill('0') << std::setw(3) + << ImageInfo.TimestampSystem.wMilliseconds; *timestamp_system = ss.str(); } } @@ -184,9 +188,9 @@ class vpUeyeGrabber::vpUeyeGrabberImpl break; case IS_CM_SENSOR_RAW8: m_I_temp.resize(m_BufferProps.height, m_BufferProps.width), - vpImageConvert::demosaicRGGBToRGBaBilinear(reinterpret_cast(m_pLastBuffer), - reinterpret_cast(m_I_temp.bitmap), - m_BufferProps.width, m_BufferProps.height); + vpImageConvert::demosaicRGGBToRGBaBilinear(reinterpret_cast(m_pLastBuffer), + reinterpret_cast(m_I_temp.bitmap), + m_BufferProps.width, m_BufferProps.height); vpImageConvert::RGBaToGrey(reinterpret_cast(m_I_temp.bitmap), reinterpret_cast(I.bitmap), m_BufferProps.width, m_BufferProps.height); @@ -231,7 +235,8 @@ class vpUeyeGrabber::vpUeyeGrabberImpl if (m_hCamera) { if (!m_bLive) { ret = is_FreezeVideo(m_hCamera, IS_WAIT); - } else { + } + else { if (!m_bLiveStarted) { // ret = is_CaptureVideo(m_hCamera, IS_DONT_WAIT); ret = is_CaptureVideo(m_hCamera, IS_WAIT); @@ -262,12 +267,12 @@ class vpUeyeGrabber::vpUeyeGrabberImpl if (timestamp_system != nullptr) { std::stringstream ss; ss << ImageInfo.TimestampSystem.wYear << ":" << std::setfill('0') << std::setw(2) - << ImageInfo.TimestampSystem.wMonth << ":" << std::setfill('0') << std::setw(2) - << ImageInfo.TimestampSystem.wDay << ":" << std::setfill('0') << std::setw(2) - << ImageInfo.TimestampSystem.wHour << ":" << std::setfill('0') << std::setw(2) - << ImageInfo.TimestampSystem.wMinute << ":" << std::setfill('0') << std::setw(2) - << ImageInfo.TimestampSystem.wSecond << ":" << std::setfill('0') << std::setw(3) - << ImageInfo.TimestampSystem.wMilliseconds; + << ImageInfo.TimestampSystem.wMonth << ":" << std::setfill('0') << std::setw(2) + << ImageInfo.TimestampSystem.wDay << ":" << std::setfill('0') << std::setw(2) + << ImageInfo.TimestampSystem.wHour << ":" << std::setfill('0') << std::setw(2) + << ImageInfo.TimestampSystem.wMinute << ":" << std::setfill('0') << std::setw(2) + << ImageInfo.TimestampSystem.wSecond << ":" << std::setfill('0') << std::setw(3) + << ImageInfo.TimestampSystem.wMilliseconds; *timestamp_system = ss.str(); } } @@ -365,18 +370,21 @@ class vpUeyeGrabber::vpUeyeGrabberImpl if ((ret = is_GetCameraInfo(m_hCamera, &m_CamInfo)) != IS_SUCCESS) { throw(vpException(vpException::fatalError, "uEye error: GetCameraInfo failed")); - } else if ((ret = is_GetSensorInfo(m_hCamera, &m_SensorInfo)) != IS_SUCCESS) { + } + else if ((ret = is_GetSensorInfo(m_hCamera, &m_SensorInfo)) != IS_SUCCESS) { throw(vpException(vpException::fatalError, "uEye error: GetSensorInfo failed")); - } else if ((ret = is_Configuration(IS_CONFIG_INITIAL_PARAMETERSET_CMD_GET, &uInitialParameterSet, - sizeof(unsigned int))) != IS_SUCCESS) { + } + else if ((ret = is_Configuration(IS_CONFIG_INITIAL_PARAMETERSET_CMD_GET, &uInitialParameterSet, + sizeof(unsigned int))) != IS_SUCCESS) { throw(vpException(vpException::fatalError, "uEye error: querying 'initial parameter set' failed")); - } else { - // m_nWidth = m_SensorInfo.nMaxWidth; - // m_nHeight = m_SensorInfo.nMaxHeight; + } + else { + // m_nWidth = m_SensorInfo.nMaxWidth; + // m_nHeight = m_SensorInfo.nMaxHeight; - // restore all defaults - // do this only if there is no 'initial parameter set' installed. - // if an 'initial parameter set' is installed we must not overwrite this setup! + // restore all defaults + // do this only if there is no 'initial parameter set' installed. + // if an 'initial parameter set' is installed we must not overwrite this setup! if (uInitialParameterSet == IS_CONFIG_INITIAL_PARAMETERSET_NONE) { ret = is_ResetToDefault(m_hCamera); } @@ -384,7 +392,8 @@ class vpUeyeGrabber::vpUeyeGrabberImpl int colormode = 0; if (m_SensorInfo.nColorMode >= IS_COLORMODE_BAYER) { colormode = IS_CM_BGRA8_PACKED; - } else { + } + else { colormode = IS_CM_MONO8; } @@ -490,10 +499,11 @@ class vpUeyeGrabber::vpUeyeGrabberImpl if (WaitForSingleObject(m_hEvent, EVENTTHREAD_WAIT_TIMEOUT) == WAIT_OBJECT_0) { #endif return IS_SUCCESS; - } else { + } + else { return IS_TIMED_OUT; } - } + } void freeImages() { @@ -631,12 +641,15 @@ class vpUeyeGrabber::vpUeyeGrabberImpl if (ret == IS_INVALID_CAMERA_TYPE) { throw(vpException(vpException::fatalError, "The camera parameters file %s belong to a different camera", filename.c_str())); - } else if (ret == IS_INCOMPATIBLE_SETTING) { + } + else if (ret == IS_INCOMPATIBLE_SETTING) { throw(vpException(vpException::fatalError, "Because of incompatible settings, cannot load parameters from file %s", filename.c_str())); - } else if (ret != IS_SUCCESS) { + } + else if (ret != IS_SUCCESS) { throw(vpException(vpException::fatalError, "Cannot load parameters from file %s", filename.c_str())); - } else { + } + else { std::cout << "Parameters loaded sucessfully" << std::endl; } @@ -701,12 +714,14 @@ class vpUeyeGrabber::vpUeyeGrabberImpl break; } } - } else { + } + else { throw(vpException(vpException::fatalError, "uEye error: is_ImageFormat returned %d", ret)); } delete (pFormatList); - } else { + } + else { throw(vpException(vpException::fatalError, "uEye error: is_ImageFormat returned %d", ret)); } return format; @@ -743,20 +758,25 @@ class vpUeyeGrabber::vpUeyeGrabberImpl int cm = IS_CM_MONO8; if (color_mode_upper == "MONO8") { cm = IS_CM_MONO8; - } else if (color_mode_upper == "RGB24") { + } + else if (color_mode_upper == "RGB24") { cm = IS_CM_BGR8_PACKED; - } else if (color_mode_upper == "RGB32") { + } + else if (color_mode_upper == "RGB32") { cm = IS_CM_RGBA8_PACKED; - } else if (color_mode_upper == "BAYER8") { + } + else if (color_mode_upper == "BAYER8") { cm = IS_CM_SENSOR_RAW8; - } else { + } + else { throw(vpException(vpException::fatalError, "Unsupported color mode %s", color_mode.c_str())); } INT ret = IS_SUCCESS; if ((ret = is_SetColorMode(m_hCamera, cm)) != IS_SUCCESS) { std::cout << "Could not set color mode of " << m_CamListInfo.Model << " to " << color_mode << std::endl; - } else { + } + else { setupCapture(); } return ret; @@ -798,7 +818,8 @@ class vpUeyeGrabber::vpUeyeGrabberImpl return IS_NO_SUCCESS; } } - } else { // Manual + } + else { // Manual double minFrameTime, maxFrameTime, intervalFrameTime, newFrameRate; // Make sure that user-requested frame rate is achievable if ((ret = is_GetFrameTimeRange(m_hCamera, &minFrameTime, &maxFrameTime, &intervalFrameTime)) != IS_SUCCESS) { @@ -813,17 +834,18 @@ class vpUeyeGrabber::vpUeyeGrabberImpl if ((ret = is_SetFrameRate(m_hCamera, frame_rate_hz, &newFrameRate)) != IS_SUCCESS) { if (m_verbose) { std::cout << "Failed to set frame rate to " << frame_rate_hz << " MHz for " << m_CamListInfo.Model - << std::endl; + << std::endl; } return ret; - } else if (frame_rate_hz != newFrameRate) { + } + else if (frame_rate_hz != newFrameRate) { frame_rate_hz = newFrameRate; } } if (m_verbose) { std::cout << "Updated frame rate for " << m_CamListInfo.Model << ": " - << ((auto_frame_rate) ? "auto" : std::to_string(frame_rate_hz)) << " Hz" << std::endl; + << ((auto_frame_rate) ? "auto" : std::to_string(frame_rate_hz)) << " Hz" << std::endl; } return ret; @@ -872,7 +894,7 @@ class vpUeyeGrabber::vpUeyeGrabberImpl if (m_verbose) { std::cout << "Updated exposure: " << ((auto_exposure) ? "auto" : std::to_string(exposure_ms) + " ms") << " for " - << m_CamListInfo.Model << std::endl; + << m_CamListInfo.Model << std::endl; } return err; @@ -902,8 +924,9 @@ class vpUeyeGrabber::vpUeyeGrabberImpl return IS_NO_SUCCESS; } } - } else { - // Disable auto gain + } + else { + // Disable auto gain if ((err = is_SetAutoParameter(m_hCamera, IS_SET_ENABLE_AUTO_SENSOR_GAIN, &pval1, &pval2)) != IS_SUCCESS) { if ((err = is_SetAutoParameter(m_hCamera, IS_SET_ENABLE_AUTO_GAIN, &pval1, &pval2)) != IS_SUCCESS) { std::cout << m_CamListInfo.Model << " does not support auto gain mode" << std::endl; @@ -913,11 +936,12 @@ class vpUeyeGrabber::vpUeyeGrabberImpl // Set gain boost if (is_SetGainBoost(m_hCamera, IS_GET_SUPPORTED_GAINBOOST) != IS_SET_GAINBOOST_ON) { gain_boost = false; - } else { + } + else { if ((err = is_SetGainBoost(m_hCamera, (gain_boost) ? IS_SET_GAINBOOST_ON : IS_SET_GAINBOOST_OFF)) != IS_SUCCESS) { std::cout << "Failed to " << ((gain_boost) ? "enable" : "disable") << " gain boost for " - << m_CamListInfo.Model << std::endl; + << m_CamListInfo.Model << std::endl; } } @@ -932,7 +956,8 @@ class vpUeyeGrabber::vpUeyeGrabberImpl if (m_verbose) { if (auto_gain) { std::cout << "Updated gain for " << m_CamListInfo.Model << ": auto" << std::endl; - } else { + } + else { std::cout << "Updated gain for " << m_CamListInfo.Model << ": manual master gain " << master_gain << std::endl; } std::cout << "\n gain boost: " << (gain_boost ? "enabled" : "disabled") << std::endl; @@ -1031,7 +1056,8 @@ class vpUeyeGrabber::vpUeyeGrabberImpl dblAutoWb = 1.0; is_SetAutoParameter(m_hCamera, IS_SET_ENABLE_AUTO_WHITEBALANCE, &dblAutoWb, nullptr); - } else { + } + else { dblAutoWb = 0.0; is_SetAutoParameter(m_hCamera, IS_SET_AUTO_WB_ONCE, &dblAutoWb, nullptr); is_SetAutoParameter(m_hCamera, IS_SET_ENABLE_AUTO_WHITEBALANCE, &dblAutoWb, nullptr); @@ -1058,8 +1084,8 @@ class vpUeyeGrabber::vpUeyeGrabberImpl is_SetColorMode(m_hCamera, IS_CM_BGR565_PACKED); colormode = IS_CM_BGR565_PACKED; std::cout << "uEye color format 'IS_CM_BGR5_PACKED' actually not supported by vpUeyeGrabber, patched to " - "'IS_CM_BGR565_PACKED'" - << std::endl; + "'IS_CM_BGR565_PACKED'" + << std::endl; } // fill memorybuffer properties @@ -1101,7 +1127,7 @@ class vpUeyeGrabber::vpUeyeGrabberImpl HANDLE m_hEvent; #endif vpImage m_I_temp; // Temp image used for Bayer conversion -}; + }; #endif // #ifndef DOXYGEN_SHOULD_SKIP_THIS /* @@ -1113,7 +1139,7 @@ class vpUeyeGrabber::vpUeyeGrabberImpl * By default, the active camera is the first one that is found. * To select a specific camera use setActiveCamera(). */ -vpUeyeGrabber::vpUeyeGrabber() : m_impl(new vpUeyeGrabberImpl()) {} +vpUeyeGrabber::vpUeyeGrabber() : m_impl(new vpUeyeGrabberImpl()) { } /*! * Destructor. @@ -1124,7 +1150,7 @@ vpUeyeGrabber::~vpUeyeGrabber() { delete m_impl; } * Capture a new grayscale image. * * \param[out] I : Captured image. - * \param[out] timestamp_camera : Time of image capture in milli-seconds with a resolution of 0.1 μs, or nullptr if not + * \param[out] timestamp_camera : Time of image capture in milli-seconds with a resolution of 0.1 us, or nullptr if not * wanted. The time of image capture is defined as: * - The time when a (hardware or software) trigger event is received by the camera in trigger mode. * The delay between the receipt of the trigger signal and the start of exposure depends on the sensor. @@ -1149,7 +1175,7 @@ void vpUeyeGrabber::acquire(vpImage &I, double *timestamp_camera, /*! * Capture a new color image. * \param[out] I : Captured image. - * \param[out] timestamp_camera : Time of image capture in milli-seconds with a resolution of 0.1 μs, or nullptr if not + * \param[out] timestamp_camera : Time of image capture in milli-seconds with a resolution of 0.1 us, or nullptr if not * wanted. The time of image capture is defined as: * - The time when a (hardware or software) trigger event is received by the camera in trigger mode. * The delay between the receipt of the trigger signal and the start of exposure depends on the sensor. @@ -1402,6 +1428,6 @@ void vpUeyeGrabber::setVerbose(bool verbose) { m_impl->setVerbose(verbose); } #elif !defined(VISP_BUILD_SHARED_LIBS) // Work around to avoid warning: libvisp_sensor.a(vpUeyeGrabber.cpp.o) has no symbols -void dummy_vpUeyeGrabber(){}; +void dummy_vpUeyeGrabber() { }; #endif diff --git a/script/bindings-dll-diagnostic.py b/script/bindings-dll-diagnostic.py new file mode 100644 index 0000000000..114c37b5fd --- /dev/null +++ b/script/bindings-dll-diagnostic.py @@ -0,0 +1,13 @@ +import sys + +try: + import dlltracer +except: + print('You need to install dlltracer to use this script.') + print('Run: pip install dlltracer') + sys.exit(-1) + +# Trace dlls that are required by the Python bindings, using the dlltracer tool. +# This script should be run as administrator. +with dlltracer.Trace(out=sys.stdout): + import visp