From 5e0ff400fc587094e75c291d3cbb68121b71d0e4 Mon Sep 17 00:00:00 2001 From: LaurentBerger Date: Fri, 2 Apr 2021 12:21:37 +0200 Subject: [PATCH] Merge pull request #2882 from LaurentBerger:python_viz * python binding for viz tutorial widget pose reset test_tutorial 2 & 3 sample py 9 test sur 22 test PyWCloudNormal 16 sur 22 end? python test * oops * bug * comment but( this is not solved https://github.com/opencv/opencv_contrib/pull/2882#discussion_r591659839 * review 2 * oversight * oversight oops * viz: move out Py* classes from public C++ headers * viz: recover PyColor Co-authored-by: Alexander Alekhin --- modules/viz/include/opencv2/viz/types.hpp | 21 +- modules/viz/include/opencv2/viz/vizcore.hpp | 8 +- modules/viz/include/opencv2/viz/widgets.hpp | 13 +- modules/viz/misc/python/pyopencv_viz.impl.hpp | 114 ++ modules/viz/misc/python/python_viz.hpp | 1350 +++++++++++++++++ .../viz/misc/python/test/test_viz_simple.py | 439 ++++++ modules/viz/samples/viz_sample_01.py | 12 + modules/viz/samples/viz_sample_02.py | 33 + modules/viz/samples/viz_sample_03.py | 41 + modules/viz/samples/widget_pose.cpp | 28 +- 10 files changed, 2038 insertions(+), 21 deletions(-) create mode 100644 modules/viz/misc/python/pyopencv_viz.impl.hpp create mode 100644 modules/viz/misc/python/python_viz.hpp create mode 100644 modules/viz/misc/python/test/test_viz_simple.py create mode 100644 modules/viz/samples/viz_sample_01.py create mode 100644 modules/viz/samples/viz_sample_02.py create mode 100644 modules/viz/samples/viz_sample_03.py diff --git a/modules/viz/include/opencv2/viz/types.hpp b/modules/viz/include/opencv2/viz/types.hpp index 7a1829c1d..c969c873e 100644 --- a/modules/viz/include/opencv2/viz/types.hpp +++ b/modules/viz/include/opencv2/viz/types.hpp @@ -60,7 +60,7 @@ namespace cv /** @brief This class represents color in BGR order. */ - class Color : public Scalar + class Color : public Scalar // FIXIT design bug, missing CV_EXPORTS { public: Color(); @@ -117,7 +117,7 @@ namespace cv /** @brief This class wraps mesh attributes, and it can load a mesh from a ply file. : */ - class CV_EXPORTS Mesh + class CV_EXPORTS_W_SIMPLE Mesh { public: enum { @@ -126,16 +126,21 @@ namespace cv LOAD_OBJ = 2 }; - Mat cloud; //!< point coordinates of type CV_32FC3 or CV_64FC3 with only 1 row - Mat colors; //!< point color of type CV_8UC3 or CV_8UC4 with only 1 row - Mat normals; //!< point normals of type CV_32FC3, CV_32FC4, CV_64FC3 or CV_64FC4 with only 1 row + CV_PROP_RW Mat cloud; //!< point coordinates of type CV_32FC3 or CV_64FC3 with only 1 row + CV_PROP_RW Mat colors; //!< point color of type CV_8UC3 or CV_8UC4 with only 1 row + CV_PROP_RW Mat normals; //!< point normals of type CV_32FC3, CV_32FC4, CV_64FC3 or CV_64FC4 with only 1 row //! Raw integer list of the form: (n,id1,id2,...,idn, n,id1,id2,...,idn, ...) //! where n is the number of points in the polygon, and id is a zero-offset index into an associated cloud. - Mat polygons; //!< CV_32SC1 with only 1 row + CV_PROP_RW Mat polygons; //!< CV_32SC1 with only 1 row - Mat texture; - Mat tcoords; //!< CV_32FC2 or CV_64FC2 with only 1 row + CV_PROP_RW Mat texture; + CV_PROP_RW Mat tcoords; //!< CV_32FC2 or CV_64FC2 with only 1 row + + CV_WRAP Mesh() + { + // nothing + } /** @brief Loads a mesh from a ply or a obj file. diff --git a/modules/viz/include/opencv2/viz/vizcore.hpp b/modules/viz/include/opencv2/viz/vizcore.hpp index 7579ddc3e..29cb0c9d6 100644 --- a/modules/viz/include/opencv2/viz/vizcore.hpp +++ b/modules/viz/include/opencv2/viz/vizcore.hpp @@ -148,7 +148,7 @@ namespace cv * Supported channels: 3 and 4. * @param binary Used only for PLY format. */ - CV_EXPORTS void writeCloud(const String& file, InputArray cloud, InputArray colors = noArray(), InputArray normals = noArray(), bool binary = false); + CV_EXPORTS_W void writeCloud(const String& file, InputArray cloud, InputArray colors = noArray(), InputArray normals = noArray(), bool binary = false); /** * @param file Filename with extension. Supported formats: PLY, XYZ, OBJ and STL. @@ -157,12 +157,12 @@ namespace cv * @return A mat containing the point coordinates with depth CV_32F or CV_64F and number of * channels 3 or 4 with only 1 row. */ - CV_EXPORTS Mat readCloud (const String& file, OutputArray colors = noArray(), OutputArray normals = noArray()); + CV_EXPORTS_W Mat readCloud (const String& file, OutputArray colors = noArray(), OutputArray normals = noArray()); /////////////////////////////////////////////////////////////////////////////////////////////// /// Reads mesh. Only ply format is supported now and no texture load support - CV_EXPORTS Mesh readMesh(const String& file); + CV_EXPORTS_W Mesh readMesh(const String& file); /////////////////////////////////////////////////////////////////////////////////////////////// /// Read/write poses and trajectories @@ -211,7 +211,7 @@ namespace cv * @param mesh Input mesh. * @param normals Normals at very point in the mesh of type CV_64FC3. */ - CV_EXPORTS void computeNormals(const Mesh& mesh, OutputArray normals); + CV_EXPORTS_W void computeNormals(const Mesh& mesh, OutputArray normals); //! @} diff --git a/modules/viz/include/opencv2/viz/widgets.hpp b/modules/viz/include/opencv2/viz/widgets.hpp index 072653d03..5f3d765aa 100644 --- a/modules/viz/include/opencv2/viz/widgets.hpp +++ b/modules/viz/include/opencv2/viz/widgets.hpp @@ -337,7 +337,7 @@ namespace cv @param resolution Resolution of the cone. @param color Color of the cone. */ - WCone(double length, double radius, int resolution = 6.0, const Color &color = Color::white()); + WCone(double length, double radius, int resolution = 6, const Color &color = Color::white()); /** @brief Constructs repositioned planar cone. @@ -348,7 +348,7 @@ namespace cv @param color Color of the cone. */ - WCone(double radius, const Point3d& center, const Point3d& tip, int resolution = 6.0, const Color &color = Color::white()); + WCone(double radius, const Point3d& center, const Point3d& tip, int resolution = 6, const Color &color = Color::white()); }; /** @brief This 3D Widget defines a cylinder. : @@ -785,11 +785,14 @@ namespace cv @param colors Point colors. @param normals Point normals. */ - class CV_EXPORTS WMesh : public Widget3D + class CV_EXPORTS_W WMesh +#ifndef OPENCV_BINDING_PARSER + : public Widget3D +#endif { public: - WMesh(const Mesh &mesh); - WMesh(InputArray cloud, InputArray polygons, InputArray colors = noArray(), InputArray normals = noArray()); + CV_WRAP WMesh(const Mesh &mesh); + CV_WRAP WMesh(InputArray cloud, InputArray polygons, InputArray colors = noArray(), InputArray normals = noArray()); }; /** @brief This class allows to merge several widgets to single one. diff --git a/modules/viz/misc/python/pyopencv_viz.impl.hpp b/modules/viz/misc/python/pyopencv_viz.impl.hpp new file mode 100644 index 000000000..7ef8c81ce --- /dev/null +++ b/modules/viz/misc/python/pyopencv_viz.impl.hpp @@ -0,0 +1,114 @@ +#ifndef OPENCV_PYTHON_VIZ_IMPL_HPP +#define OPENCV_PYTHON_VIZ_IMPL_HPP + +#include "python_viz.hpp" + +namespace cv { namespace viz { + +PyAffine3d makeTransformToGlobalPy(const Vec3d& axis_x, const Vec3d& axis_y, const Vec3d& axis_z, const Vec3d& origin) +{ + return makeTransformToGlobal(axis_x, axis_y, axis_z, origin); +} + +PyAffine3d makeCameraPosePy(const Vec3d& position, const Vec3d& focal_point, const Vec3d& y_dir) +{ + return makeCameraPose(position, focal_point, y_dir); +} + +#define WRAP_SHOW_WIDGET_1 return Viz3d::showWidget(id, *widget.widget.get()) +#define WRAP_SHOW_WIDGET_2 return Viz3d::showWidget(id, *widget.widget.get(), pose) +inline void PyViz3d::showWidget(const String &id, PyWLine &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWLine &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWSphere &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWSphere &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWCameraPosition &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWCameraPosition &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWArrow &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWArrow &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWCircle &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWCircle &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWPlane &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWPlane &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWCone &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWCone &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWCube &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWCube &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWCylinder &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWCylinder &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWCoordinateSystem &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWPaintedCloud &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWPaintedCloud &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWCloudCollection &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWCloudCollection &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWGrid &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWGrid &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, const cv::Ptr &widget) { return Viz3d::showWidget(id, *widget.get()); } +inline void PyViz3d::showWidget(const String &id, const cv::Ptr &widget, PyAffine3d &pose) { return Viz3d::showWidget(id, *widget.get(), pose); } +inline void PyViz3d::showWidget(const String &id, PyWPolyLine &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWPolyLine &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWCloud &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWCloud &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWImage3D &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWImage3D &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWImageOverlay &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWImageOverlay &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWText &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWText &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWText3D &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWText3D &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWCloudNormals &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWCloudNormals &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWTrajectory &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWTrajectory &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWTrajectorySpheres &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWTrajectorySpheres &widget) { WRAP_SHOW_WIDGET_1; } +inline void PyViz3d::showWidget(const String &id, PyWTrajectoryFrustums &widget, PyAffine3d &pose) { WRAP_SHOW_WIDGET_2; } +inline void PyViz3d::showWidget(const String &id, PyWTrajectoryFrustums &widget) { WRAP_SHOW_WIDGET_1; } +#undef WRAP_SHOW_WIDGET_1 +#undef WRAP_SHOW_WIDGET_2 + + +PyWGrid::PyWGrid(InputArray cells, InputArray cells_spacing, const PyColor& color) +{ + if (cells.kind() == _InputArray::MAT && cells_spacing.kind() == _InputArray::MAT) + { + Mat k = cells.getMat(); + Mat l = cells_spacing.getMat(); + if (k.total() == 2 && l.total() == 2 ) + { + CV_Assert(k.type() == CV_64FC1 && k.type() == CV_64FC1); + Vec2i c1((int)k.at(0), (int)k.at(1)); + Vec2d c2(l.at(0), l.at(1)); + widget = cv::makePtr(c1, c2, color); + } + else + CV_Error(cv::Error::StsVecLengthErr, "unknown size"); + } + else + CV_Error(cv::Error::StsUnsupportedFormat, "unknown type"); +} + + +PyWTrajectoryFrustums::PyWTrajectoryFrustums(InputArray path, InputArray K, double scale, const PyColor& color) +{ + if (K.kind() == _InputArray::MAT) + { + Mat k = K.getMat(); + if (k.rows == 3 && k.cols == 3) + { + Matx33d x = k; + widget = cv::makePtr(path, x, scale, color); + + } + else if (k.total() == 2) + widget = cv::makePtr(path, Vec2d(k.at(0), k.at(1)), 1.0, color); + else + CV_Error(cv::Error::StsVecLengthErr, "unknown size"); + } + else + CV_Error(cv::Error::StsVecLengthErr, "unknown size"); +} + + +}} // namespace +#endif // OPENCV_PYTHON_VIZ_IMPL_HPP diff --git a/modules/viz/misc/python/python_viz.hpp b/modules/viz/misc/python/python_viz.hpp new file mode 100644 index 000000000..720e4f14f --- /dev/null +++ b/modules/viz/misc/python/python_viz.hpp @@ -0,0 +1,1350 @@ +#ifndef OPENCV_PYTHON_VIZ_HPP +#define OPENCV_PYTHON_VIZ_HPP + +#include "opencv2/viz.hpp" + +namespace cv { namespace viz { + +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(Color) PyColor +{ + CV_WRAP PyColor() {} + CV_WRAP PyColor(double gray) : c(gray) {} + CV_WRAP PyColor(double blue, double green, double red) : c(blue, green, red) {} + PyColor(const Color& v) : c(v) { } + + operator Color() const { return c; } + + CV_WRAP static PyColor black() { return PyColor(Color::black()); } + CV_WRAP static PyColor white() { return PyColor(Color::white()); } + CV_WRAP static PyColor blue() { return PyColor(Color::blue()); } + CV_WRAP static PyColor green() { return PyColor(Color::green()); } + CV_WRAP static PyColor red() { return PyColor(Color::red()); } + CV_WRAP static PyColor cyan() { return PyColor(Color::cyan()); } + CV_WRAP static PyColor yellow() { return PyColor(Color::yellow()); } + CV_WRAP static PyColor magenta() { return PyColor(Color::magenta()); } + + CV_WRAP static PyColor gray() { return PyColor(Color::gray()); } + CV_WRAP static PyColor silver() { return PyColor(Color::silver()); } + + CV_WRAP static PyColor mlab() { return PyColor(Color::mlab()); } + + CV_WRAP static PyColor navy() { return PyColor(Color::navy()); } + CV_WRAP static PyColor maroon() { return PyColor(Color::maroon()); } + CV_WRAP static PyColor teal() { return PyColor(Color::teal()); } + CV_WRAP static PyColor olive() { return PyColor(Color::olive()); } + CV_WRAP static PyColor purple() { return PyColor(Color::olive()); } + CV_WRAP static PyColor azure() { return PyColor(Color::olive()); } + CV_WRAP static PyColor chartreuse() { return PyColor(Color::olive()); } + CV_WRAP static PyColor rose() { return PyColor(Color::olive()); } + + CV_WRAP static PyColor lime() { return PyColor(Color::olive()); } + CV_WRAP static PyColor gold() { return PyColor(Color::olive()); } + CV_WRAP static PyColor orange() { return PyColor(Color::olive()); } + CV_WRAP static PyColor orange_red() { return PyColor(Color::olive()); } + CV_WRAP static PyColor indigo() { return PyColor(Color::olive()); } + + CV_WRAP static PyColor brown() { return PyColor(Color::olive()); } + CV_WRAP static PyColor apricot() { return PyColor(Color::olive()); } + CV_WRAP static PyColor pink() { return PyColor(Color::olive()); } + CV_WRAP static PyColor raspberry() { return PyColor(Color::olive()); } + CV_WRAP static PyColor cherry() { return PyColor(Color::olive()); } + CV_WRAP static PyColor violet() { return PyColor(Color::olive()); } + CV_WRAP static PyColor amethyst() { return PyColor(Color::amethyst()); } + CV_WRAP static PyColor bluberry() { return PyColor(Color::bluberry()); } + CV_WRAP static PyColor celestial_blue() { return PyColor(Color::celestial_blue()); } + CV_WRAP static PyColor turquoise() { return PyColor(Color::turquoise()); } + + static PyColor not_set() { return PyColor(Color::not_set()); } + CV_WRAP double get_blue() { return c[0]; } + CV_WRAP double get_green() { return c[1]; } + CV_WRAP double get_red() { return c[2]; } + + Color c; +}; + + +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(Affine3d) PyAffine3d +#ifndef OPENCV_BINDING_PARSER + : public Affine3d +#endif +{ + CV_WRAP PyAffine3d() + { + // nothing + } + inline PyAffine3d(const Affine3d& base) : Affine3d(base) + { + // nothing + } + CV_WRAP PyAffine3d(const Vec3d &rvec, const Vec3d &t = Vec3d::all(0)) + : Affine3d(rvec, t) + { + // nothing + } + CV_WRAP PyAffine3d(const Mat &rot, const Vec3f &t = Vec3d::all(0)) + : Affine3d(rot, t) + { + // nothing + } + CV_WRAP PyAffine3d translate(const Vec3d &t) + { + return Affine3d::translate(t); + } + CV_WRAP PyAffine3d rotate(const Vec3d &t) + { + return Affine3d::rotate(t); + } + CV_WRAP PyAffine3d product(const PyAffine3d &t) + { + return ((const Affine3d&)(*this)) * (const Affine3d&)t; + } + CV_WRAP static PyAffine3d Identity() + { + return Affine3d::Identity(); + } + CV_WRAP PyAffine3d inv() + { + return Affine3d::inv(); + } + CV_WRAP Mat mat() + { + return Mat(matrix); + } +}; + + +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WLine) PyWLine +{ + CV_WRAP PyWLine() + { + } + /** @brief Constructs a WLine. + + @param pt1 Start point of the line. + @param pt2 End point of the line. + @param color Color of the line. + */ + CV_WRAP PyWLine(const Point3d &pt1, const Point3d &pt2, const PyColor& color) + { + widget = cv::makePtr(pt1, pt2, color); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WPlane) PyWPlane +{ +public: + /** @brief Constructs a default plane with center point at origin and normal oriented along z-axis. + + @param size Size of the plane + @param color Color of the plane. + */ + CV_WRAP PyWPlane(const Point2d& size = Point2d(1.0, 1.0), const PyColor& color = Color(255, 255,255)) + { + widget = cv::makePtr(size, color); + } + + /** @brief Constructs a repositioned plane + + @param center Center of the plane + @param normal Plane normal orientation + @param new_yaxis Up-vector. New orientation of plane y-axis. + @param size + @param color Color of the plane. + */ + CV_WRAP PyWPlane(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, + const Point2d& size = Point2d(1.0, 1.0), const PyColor& color = Color(255, 255, 255)) + { + widget = cv::makePtr(center, normal, new_yaxis, size, color); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + +/** @brief This 3D Widget defines a sphere. : +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WSphere) PyWSphere +{ +public: + CV_WRAP PyWSphere() + { + + } + /** @brief Constructs a WSphere. + + @param center Center of the sphere. + @param radius Radius of the sphere. + @param sphere_resolution Resolution of the sphere. + @param color Color of the sphere. + */ + CV_WRAP PyWSphere(const cv::Point3d ¢er, double radius, int sphere_resolution = 10, const PyColor& color = Color(255, 255,255)) + { + widget = cv::makePtr(center, radius, sphere_resolution, color); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + +/** @brief This 3D Widget defines an arrow. +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WArrow) PyWArrow +{ +public: + CV_WRAP PyWArrow() + { + } + /** @brief Constructs an WArrow. + + @param pt1 Start point of the arrow. + @param pt2 End point of the arrow. + @param thickness Thickness of the arrow. Thickness of arrow head is also adjusted + accordingly. + @param color Color of the arrow. + + Arrow head is located at the end point of the arrow. + */ + CV_WRAP PyWArrow(const Point3d& pt1, const Point3d& pt2, double thickness = 0.03, const PyColor& color = Color(255, 255, 255)) + { + widget = cv::makePtr(pt1, pt2, thickness, color); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + +/** @brief This 3D Widget defines a cube. +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WCube) PyWCube +{ +public: + /** @brief Constructs a WCube. + + @param min_point Specifies minimum (or maximum) point of the bounding box. + @param max_point Specifies maximum (or minimum) point of the bounding box, opposite to the first parameter. + @param wire_frame If true, cube is represented as wireframe. + @param color Color of the cube. + + ![Cube Widget](images/cube_widget.png) + */ + CV_WRAP PyWCube(const Point3d& min_point = Vec3d::all(-0.5), const Point3d& max_point = Vec3d::all(0.5), + bool wire_frame = true, const PyColor& color = Color(255, 255, 255)) + { + widget = cv::makePtr(min_point, max_point, wire_frame, color); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + +/** @brief This 3D Widget defines a PyWCircle. +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WCircle) PyWCircle +{ +public: + PyWCircle() {} + /** @brief Constructs default planar circle centered at origin with plane normal along z-axis + + @param radius Radius of the circle. + @param thickness Thickness of the circle. + @param color Color of the circle. + */ + CV_WRAP PyWCircle(double radius, double thickness = 0.01, const PyColor& color = Color::white()) + { + widget = cv::makePtr(radius, thickness, color); + } + + /** @brief Constructs repositioned planar circle. + + @param radius Radius of the circle. + @param center Center of the circle. + @param normal Normal of the plane in which the circle lies. + @param thickness Thickness of the circle. + @param color Color of the circle. + */ + CV_WRAP PyWCircle(double radius, const Point3d& center, const Vec3d& normal, double thickness = 0.01, const PyColor& color = Color::white()) + { + widget = cv::makePtr(radius, center, normal, thickness, color); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + +/** @brief This 3D Widget defines a cone. : +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WCone) PyWCone +{ +public: + PyWCone() {} + /** @brief Constructs default cone oriented along x-axis with center of its base located at origin + + @param length Length of the cone. + @param radius Radius of the cone. + @param resolution Resolution of the cone. + @param color Color of the cone. + */ + CV_WRAP PyWCone(double length, double radius, int resolution = 6, const PyColor& color = Color::white()) + { + widget = cv::makePtr(length, radius, resolution, color); + } + + /** @brief Constructs repositioned planar cone. + + @param radius Radius of the cone. + @param center Center of the cone base. + @param tip Tip of the cone. + @param resolution Resolution of the cone. + @param color Color of the cone. + + */ + CV_WRAP PyWCone(double radius, const Point3d& center, const Point3d& tip, int resolution = 6, const PyColor& color = Color::white()) + { + widget = cv::makePtr(radius, center, tip, resolution, color); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + +/** @brief This 3D Widget defines a PyWCylinder. : +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WCylinder) PyWCylinder +{ +public: + CV_WRAP PyWCylinder() {} + /** @brief Constructs a WCylinder. + + @param axis_point1 A point1 on the axis of the cylinder. + @param axis_point2 A point2 on the axis of the cylinder. + @param radius Radius of the cylinder. + @param numsides Resolution of the cylinder. + @param color Color of the cylinder. + */ + CV_WRAP PyWCylinder(const Point3d& axis_point1, const Point3d& axis_point2, double radius, int numsides = 30, const PyColor& color = Color::white()) + { + widget = cv::makePtr(axis_point1, axis_point2, radius, numsides, color); + } + Ptr widget; +}; + +/** @brief This 3D Widget represents camera position in a scene by its axes or viewing frustum. : +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WCameraPosition) PyWCameraPosition +{ +public: + /** @brief Creates camera coordinate frame at the origin. + + ![Camera coordinate frame](images/cpw1.png) + */ + CV_WRAP PyWCameraPosition(double scale = 1.0) + { + widget = cv::makePtr(scale); + } + /** @brief Display the viewing frustum + @param K Intrinsic matrix of the camera or fov Field of view of the camera (horizontal, vertical). + @param scale Scale of the frustum. + @param color Color of the frustum. + + Creates viewing frustum of the camera based on its intrinsic matrix K. + + ![Camera viewing frustum](images/cpw2.png) + */ + CV_WRAP PyWCameraPosition(InputArray K, double scale = 1.0, const PyColor& color = Color(255, 255, 255)) + { + if (K.kind() == _InputArray::MAT) + { + Mat k = K.getMat(); + if (k.rows == 3 && k.cols == 3) + { + Matx33d x = k; + widget = cv::makePtr(x, scale, color); + + } + else if (k.total() == 2) + widget = cv::makePtr(Vec2d(k.at(0), k.at(1)), scale, color); + else + CV_Error(cv::Error::StsVecLengthErr, "unknown size"); + } + else + CV_Error(cv::Error::StsUnsupportedFormat, "unknown type"); + } + + /** @brief Display image on the far plane of the viewing frustum + + @param K Intrinsic matrix of the camera. + @param image BGR or Gray-Scale image that is going to be displayed on the far plane of the frustum. + @param scale Scale of the frustum and image. + @param color Color of the frustum. + + Creates viewing frustum of the camera based on its intrinsic matrix K, and displays image on + the far end plane. + + ![Camera viewing frustum with image](images/cpw3.png) + */ + CV_WRAP PyWCameraPosition(InputArray K, InputArray image, double scale = 1.0, const PyColor& color = Color(255, 255, 255)) + { + if (K.kind() == _InputArray::MAT) + { + Mat k = K.getMat(); + if (k.rows == 3 && k.cols == 3) + { + Matx33d x = k; + widget = cv::makePtr(x, image, scale, color); + + } + else if (k.total() == 2) + widget = cv::makePtr(Vec2d(k.at(0), k.at(1)), image, scale, color); + else + CV_Error(cv::Error::StsVecLengthErr, "unknown size"); + } + else + CV_Error(cv::Error::StsUnsupportedFormat, "unknown type"); + + } + /** @brief Display image on the far plane of the viewing frustum + + @param fov Field of view of the camera (horizontal, vertical). + @param image BGR or Gray-Scale image that is going to be displayed on the far plane of the frustum. + @param scale Scale of the frustum and image. + @param color Color of the frustum. + + Creates viewing frustum of the camera based on its intrinsic matrix K, and displays image on + the far end plane. + + ![Camera viewing frustum with image](images/cpw3.png) + */ + CV_WRAP PyWCameraPosition(const Point2d &fov, InputArray image, double scale = 1.0, const PyColor& color = Color(255, 255, 255)) + { + widget = cv::makePtr(fov, image, scale, color); + } + + Ptr widget; +}; +///////////////////////////////////////////////////////////////////////////// +/// Compound widgets + +/** @brief This 3D Widget represents a coordinate system. : +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WCoordinateSystem) PyWCoordinateSystem +{ +public: + /** @brief Constructs a WCoordinateSystem. + + @param scale Determines the size of the axes. + */ + CV_WRAP PyWCoordinateSystem(double scale = 1.0) + { + widget = cv::makePtr(scale); + + } + Ptr widget; +}; + +///////////////////////////////////////////////////////////////////////////// +/// Clouds + +/** @brief This 3D Widget defines a point cloud. : + +@note In case there are four channels in the cloud, fourth channel is ignored. +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WCloud) PyWCloud +{ +public: + CV_WRAP PyWCloud() + { + // nothing + } + + /** @brief Constructs a WCloud. + @param cloud Set of points which can be of type: CV_32FC3, CV_32FC4, CV_64FC3, CV_64FC4. + @param color A single Color for the whole cloud. + + Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + */ + CV_WRAP PyWCloud(InputArray cloud, const PyColor& color = Color::white()) + { + widget = cv::makePtr(cloud, color); + } + + CV_WRAP PyWCloud(InputArray cloud, InputArray colors) + { + widget = cv::makePtr(cloud, colors); + } + + CV_WRAP PyWCloud(InputArray cloud, InputArray colors, InputArray normals) + { + widget = cv::makePtr(cloud, colors, normals); + } + + /** @brief Constructs a WCloud. + @param cloud Set of points which can be of type: CV_32FC3, CV_32FC4, CV_64FC3, CV_64FC4. + @param color A single Color for the whole cloud. + @param normals Normals for each point in cloud. + + Size and type should match with the cloud parameter. + Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + */ + CV_WRAP PyWCloud(InputArray cloud, const PyColor& color, InputArray normals) + { + widget = cv::makePtr(cloud, color, normals); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; +/** @brief This 3D Widget defines a poly line. : +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WPolyLine) PyWPolyLine +{ +public: + CV_WRAP PyWPolyLine() + { + } + CV_WRAP PyWPolyLine(InputArray points, InputArray colors) + { + widget = cv::makePtr(points, colors); + } + /** @brief Constructs a WPolyLine. + + @param points Point set. + @param color Color of the poly line. + */ + CV_WRAP PyWPolyLine(InputArray points, const PyColor& color = Color::white()) + { + widget = cv::makePtr(points, color); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + +///////////////////////////////////////////////////////////////////////////// +/// Text and image widgets + +/** @brief This 2D Widget represents text overlay. +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WText) PyWText +{ +public: + CV_WRAP PyWText() + { + } + /** @brief Constructs a WText. + + @param text Text content of the widget. + @param pos Position of the text. + @param font_size Font size. + @param color Color of the text. + */ + CV_WRAP PyWText(const String &text, const Point &pos, int font_size = 20, const PyColor& color = Color::white()) + { + widget = cv::makePtr(text, pos, font_size, color); + } + + /** @brief Sets the text content of the widget. + + @param text Text content of the widget. + */ + CV_WRAP void setText(const String &text) + { + widget->setText(text); + } + /** @brief Returns the current text content of the widget. + */ + CV_WRAP String getText() const + { + return widget->getText(); + } + Ptr widget; +}; + +/** @brief This 3D Widget represents 3D text. The text always faces the camera. +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WText3D) PyWText3D +{ +public: + CV_WRAP PyWText3D() + {} + /** @brief Constructs a WText3D. + + @param text Text content of the widget. + @param position Position of the text. + @param text_scale Size of the text. + @param face_camera If true, text always faces the camera. + @param color Color of the text. + */ + CV_WRAP PyWText3D(const String &text, const Point3d &position, double text_scale = 1., bool face_camera = true, const PyColor& color = Color::white()) + { + widget = cv::makePtr(text, position, text_scale, face_camera, color); + } + /** @brief Sets the text content of the widget. + + @param text Text content of the widget. + + */ + CV_WRAP void setText(const String &text) + { + widget->setText(text); + } + /** @brief Returns the current text content of the widget. + */ + CV_WRAP String getText() const + { + return widget->getText(); + } + Ptr widget; +}; + +/** @brief This 2D Widget represents an image overlay. : +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WImageOverlay) PyWImageOverlay +{ +public: + CV_WRAP PyWImageOverlay() + { + } + /** @brief Constructs an WImageOverlay. + + @param image BGR or Gray-Scale image. + @param rect Image is scaled and positioned based on rect. + */ + CV_WRAP PyWImageOverlay(InputArray image, const Rect &rect) + { + widget = cv::makePtr(image, rect); + } + /** @brief Sets the image content of the widget. + + @param image BGR or Gray-Scale image. + */ + CV_WRAP void setImage(InputArray image) + { + widget->setImage(image); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + +/** @brief This 3D Widget represents an image in 3D space. : +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WImage3D) PyWImage3D +{ + CV_WRAP PyWImage3D() + { + } +public: + /** @brief Constructs an WImage3D. + + @param image BGR or Gray-Scale image. + @param size Size of the image. + */ + CV_WRAP PyWImage3D(InputArray image, const Point2d &size) + { + widget = cv::makePtr(image, size); + } + + /** @brief Constructs an WImage3D. + + @param image BGR or Gray-Scale image. + @param size Size of the image. + @param center Position of the image. + @param normal Normal of the plane that represents the image. + @param up_vector Determines orientation of the image. + */ + CV_WRAP PyWImage3D(InputArray image, const Point2d &size, const Vec3d ¢er, const Vec3d &normal, const Vec3d &up_vector) + { + widget = cv::makePtr(image, size, center, normal, up_vector); + } + + + /** @brief Sets the image content of the widget. + + @param image BGR or Gray-Scale image. + */ + CV_WRAP void setImage(InputArray image) + { + widget->setImage(image); + } + /** @brief Sets the image size of the widget. + + @param size the new size of the image. + */ + CV_WRAP void setSize(const Size& size) + { + widget->setSize(size); + } + Ptr widget; +}; + + +/** @brief This 3D Widget defines a grid. : + */ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WGrid) PyWGrid +{ +public: + PyWGrid() {} + /** @brief Constructs a WGrid. + + @param cells Number of cell columns and rows, respectively. + @param cells_spacing Size of each cell, respectively. + @param color Color of the grid. + */ + CV_WRAP PyWGrid(InputArray cells, InputArray cells_spacing, const PyColor& color = Color::white()); + + //! Creates repositioned grid + CV_WRAP PyWGrid(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, + const Vec2i &cells = Vec2i::all(10), const Vec2d &cells_spacing = Vec2d::all(1.0), const PyColor& color = Color::white()) + { + widget = cv::makePtr(center, normal, new_yaxis, cells, cells_spacing, color); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + + +///////////////////////////////////////////////////////////////////////////// +/// Trajectories + +/** @brief This 3D Widget represents a trajectory. : +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WTrajectory) PyWTrajectory +{ +public: + enum { FRAMES = 1, PATH = 2, BOTH = FRAMES + PATH }; + PyWTrajectory() {} + /** @brief Constructs a WTrajectory. + + @param path List of poses on a trajectory. Takes std::vector\\> with T == [float | double] + @param display_mode Display mode. This can be PATH, FRAMES, and BOTH. + @param scale Scale of the frames. Polyline is not affected. + @param color Color of the polyline that represents path. + + Frames are not affected. + Displays trajectory of the given path as follows: + - PATH : Displays a poly line that represents the path. + - FRAMES : Displays coordinate frames at each pose. + - PATH & FRAMES : Displays both poly line and coordinate frames. + */ + CV_WRAP PyWTrajectory(InputArray path, int display_mode = WTrajectory::PATH, double scale = 1.0, const PyColor& color = Color::white()) + { + widget = cv::makePtr(path, display_mode, scale, color); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + +/** @brief This 3D Widget represents a trajectory. : +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WTrajectoryFrustums) PyWTrajectoryFrustums +{ +public: + PyWTrajectoryFrustums() {} + /** @brief Constructs a WTrajectoryFrustums. + + @param path List of poses on a trajectory. Takes std::vector\\> with T == [float | double] + @param K Intrinsic matrix of the camera or fov Field of view of the camera (horizontal, vertical). + @param scale Scale of the frustums. + @param color Color of the frustums. + + Displays frustums at each pose of the trajectory. + */ + CV_WRAP PyWTrajectoryFrustums(InputArray path, InputArray K, double scale = 1.0, const PyColor& color = Color::white()); + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + +/** @brief This 3D Widget represents a trajectory using spheres and lines + +where spheres represent the positions of the camera, and lines represent the direction from +previous position to the current. : + */ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WTrajectorySpheres) PyWTrajectorySpheres +{ +public: + PyWTrajectorySpheres() {} + /** @brief Constructs a WTrajectorySpheres. + + @param path List of poses on a trajectory. Takes std::vector\\> with T == [float | double] + @param line_length Max length of the lines which point to previous position + @param radius Radius of the spheres. + @param from Color for first sphere. + @param to Color for last sphere. Intermediate spheres will have interpolated color. + */ + CV_WRAP PyWTrajectorySpheres(InputArray path, double line_length = 0.05, double radius = 0.007, + const PyColor& from = Color::red(), const PyColor& to = Color::white()) + { + widget = cv::makePtr(path, line_length, radius, from, to); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + + +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WPaintedCloud) PyWPaintedCloud +{ +public: + //! Paint cloud with default gradient between cloud bounds points + PyWPaintedCloud() + { + } + + CV_WRAP PyWPaintedCloud(InputArray cloud) + { + widget = cv::makePtr(cloud); + } + + //! Paint cloud with default gradient between given points + CV_WRAP PyWPaintedCloud(InputArray cloud, const Point3d& p1, const Point3d& p2) + { + widget = cv::makePtr(cloud, p1, p2); + } + + //! Paint cloud with gradient specified by given colors between given points + CV_WRAP PyWPaintedCloud(InputArray cloud, const Point3d& p1, const Point3d& p2, const PyColor& c1, const PyColor& c2) + { + widget = cv::makePtr(cloud, p1, p2, c1, c2); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + +/** @brief This 3D Widget defines a collection of clouds. : +@note In case there are four channels in the cloud, fourth channel is ignored. +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WCloudCollection) PyWCloudCollection +{ +public: + CV_WRAP PyWCloudCollection() + { + widget = cv::makePtr(); + } + + /** @brief Adds a cloud to the collection. + + @param cloud Point set which can be of type: CV_32FC3, CV_32FC4, CV_64FC3, CV_64FC4. + @param colors Set of colors. It has to be of the same size with cloud. + @param pose Pose of the cloud. Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + */ + CV_WRAP void addCloud(InputArray cloud, InputArray colors, const PyAffine3d &pose = PyAffine3d::Identity()) + { + widget->addCloud(cloud, colors, pose); + } + /** @brief Adds a cloud to the collection. + + @param cloud Point set which can be of type: CV_32FC3, CV_32FC4, CV_64FC3, CV_64FC4. + @param color A single Color for the whole cloud. + @param pose Pose of the cloud. Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + */ + CV_WRAP void addCloud(InputArray cloud, const PyColor& color = Color::white(), const PyAffine3d& pose = PyAffine3d::Identity()) + { + widget->addCloud(cloud, color, pose); + } + /** @brief Finalizes cloud data by repacking to single cloud. + + Useful for large cloud collections to reduce memory usage + */ + CV_WRAP void finalize() + { + widget->finalize(); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + +/** @brief This 3D Widget represents normals of a point cloud. : +*/ +struct CV_EXPORTS_W_SIMPLE CV_WRAP_AS(WCloudNormals) PyWCloudNormals +{ +public: + PyWCloudNormals() + { + } + /** @brief Constructs a WCloudNormals. + + @param cloud Point set which can be of type: CV_32FC3, CV_32FC4, CV_64FC3, CV_64FC4. + @param normals A set of normals that has to be of same type with cloud. + @param level Display only every level th normal. + @param scale Scale of the arrows that represent normals. + @param color Color of the arrows that represent normals. + + @note In case there are four channels in the cloud, fourth channel is ignored. + */ + CV_WRAP PyWCloudNormals(InputArray cloud, InputArray normals, int level = 64, double scale = 0.1, const PyColor& color = Color::white()) + { + widget = cv::makePtr(cloud, normals, level, scale, color); + } + + CV_WRAP void setRenderingProperty(int property, double value) + { + CV_Assert(widget); + widget->setRenderingProperty(property, value); + } + + Ptr widget; +}; + + +CV_WRAP_AS(makeTransformToGlobal) static inline +PyAffine3d makeTransformToGlobalPy(const Vec3d& axis_x, const Vec3d& axis_y, const Vec3d& axis_z, const Vec3d& origin = Vec3d::all(0)); + +CV_WRAP_AS(makeCameraPose) static inline +PyAffine3d makeCameraPosePy(const Vec3d& position, const Vec3d& focal_point, const Vec3d& y_dir); + + +/** @brief The Viz3d class represents a 3D visualizer window. This class is implicitly shared. +*/ +class CV_EXPORTS_AS(Viz3d) PyViz3d +#ifndef OPENCV_BINDING_PARSER + : public Viz3d +#endif +{ +public: + CV_WRAP static cv::Ptr create(const std::string& window_name = std::string()) + { + return makePtr(window_name); + } + + /** @brief The constructors. + + @param window_name Name of the window. + */ + CV_WRAP PyViz3d(const std::string& window_name = std::string()) : Viz3d(window_name) {} + + CV_WRAP void showWidget(const String &id, PyWLine &widget); + CV_WRAP void showWidget(const String &id, PyWLine &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWSphere &widget); + CV_WRAP void showWidget(const String &id, PyWSphere &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWCameraPosition &widget); + CV_WRAP void showWidget(const String &id, PyWCameraPosition &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWArrow &widget); + CV_WRAP void showWidget(const String &id, PyWArrow &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWCircle &widget); + CV_WRAP void showWidget(const String &id, PyWCircle &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWPlane &widget); + CV_WRAP void showWidget(const String &id, PyWPlane &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWCone &widget); + CV_WRAP void showWidget(const String &id, PyWCone &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWCube &widget); + CV_WRAP void showWidget(const String &id, PyWCube &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWCylinder &widget); + CV_WRAP void showWidget(const String &id, PyWCylinder &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWCoordinateSystem &widget); + CV_WRAP void showWidget(const String &id, PyWPaintedCloud &widget); + CV_WRAP void showWidget(const String &id, PyWPaintedCloud &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWCloudCollection &widget); + CV_WRAP void showWidget(const String &id, PyWCloudCollection &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWGrid &widget); + CV_WRAP void showWidget(const String &id, PyWGrid &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, const cv::Ptr &widget); + CV_WRAP void showWidget(const String &id, const cv::Ptr &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWPolyLine &widget); + CV_WRAP void showWidget(const String &id, PyWPolyLine &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWCloud &widget); + CV_WRAP void showWidget(const String &id, PyWCloud &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWImage3D &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWImage3D &widget); + CV_WRAP void showWidget(const String &id, PyWImageOverlay &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWImageOverlay &widget); + CV_WRAP void showWidget(const String &id, PyWText &widget); + CV_WRAP void showWidget(const String &id, PyWText &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWText3D &widget); + CV_WRAP void showWidget(const String &id, PyWText3D &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWCloudNormals &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWCloudNormals &widget); + CV_WRAP void showWidget(const String &id, PyWTrajectory &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWTrajectory &widget); + CV_WRAP void showWidget(const String &id, PyWTrajectorySpheres &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWTrajectorySpheres &widget); + CV_WRAP void showWidget(const String &id, PyWTrajectoryFrustums &widget, PyAffine3d &pose); + CV_WRAP void showWidget(const String &id, PyWTrajectoryFrustums &widget); + + + /** @brief Removes a widget from the window. + + @param id The id of the widget that will be removed. + */ + CV_WRAP + void removeWidget(const String &id) + { return Viz3d::removeWidget(id); } + + /** @brief Removes all widgets from the window. + */ + CV_WRAP + void removeAllWidgets() + { return Viz3d::removeAllWidgets(); } + + /** @brief Removed all widgets and displays image scaled to whole window area. + + @param image Image to be displayed. + @param window_size Size of Viz3d window. Default value means no change. + */ + CV_WRAP + void showImage(InputArray image, const Size& window_size = Size(-1, -1)) + { return Viz3d::showImage(image, window_size); } + + /** @brief Sets pose of a widget in the window. + + @param id The id of the widget whose pose will be set. @param pose The new pose of the widget. + */ + CV_WRAP + void setWidgetPose(const String &id, const PyAffine3d &pose) + { return Viz3d::setWidgetPose(id, pose); } + + /** @brief Updates pose of a widget in the window by pre-multiplying its current pose. + + @param id The id of the widget whose pose will be updated. @param pose The pose that the current + pose of the widget will be pre-multiplied by. + */ + CV_WRAP + void updateWidgetPose(const String &id, const PyAffine3d &pose) + { return Viz3d::updateWidgetPose(id, pose); } + + /** @brief Returns the current pose of a widget in the window. + + @param id The id of the widget whose pose will be returned. + */ + CV_WRAP + PyAffine3d getWidgetPose(const String &id) const + { return (PyAffine3d)Viz3d::getWidgetPose(id); } + +#if 0 + /** @brief Sets the intrinsic parameters of the viewer using Camera. + + @param camera Camera object wrapping intrinsic parameters. + */ + void setCamera(const Camera &camera); + + /** @brief Returns a camera object that contains intrinsic parameters of the current viewer. + */ + Camera getCamera() const; +#endif + + /** @brief Returns the current pose of the viewer. + */ + CV_WRAP PyAffine3d getViewerPose() const + { return (PyAffine3d)Viz3d::getViewerPose(); } + + /** @brief Sets pose of the viewer. + + @param pose The new pose of the viewer. + */ + CV_WRAP + void setViewerPose(const PyAffine3d &pose) + { return Viz3d::setViewerPose(pose); } + + /** @brief Resets camera viewpoint to a 3D widget in the scene. + + @param id Id of a 3D widget. + */ + CV_WRAP + void resetCameraViewpoint(const String &id) + { return Viz3d::resetCameraViewpoint(id); } + + /** @brief Resets camera. + */ + CV_WRAP + void resetCamera() + { return Viz3d::resetCamera(); } + + /** @brief Transforms a point in world coordinate system to window coordinate system. + + @param pt Point in world coordinate system. + @param window_coord Output point in window coordinate system. + */ + CV_WRAP void convertToWindowCoordinates(const Point3d &pt, CV_OUT Point3d &window_coord) + { return Viz3d::convertToWindowCoordinates(pt, window_coord); } + +#if 0 + /** @brief Transforms a point in window coordinate system to a 3D ray in world coordinate system. + + @param window_coord Point in window coordinate system. @param origin Output origin of the ray. + @param direction Output direction of the ray. + */ + void converTo3DRay(const Point3d &window_coord, Point3d &origin, Vec3d &direction); +#endif + + /** @brief Returns the current size of the window. + */ + CV_WRAP + Size getWindowSize() const + { return Viz3d::getWindowSize(); } + + /** @brief Sets the size of the window. + + @param window_size New size of the window. + */ + CV_WRAP + void setWindowSize(const Size& window_size) + { return Viz3d::setWindowSize(window_size); } + + /** @brief Returns the name of the window which has been set in the constructor. + * `Viz - ` is prepended to the name if necessary. + */ + CV_WRAP + String getWindowName() const + { return Viz3d::getWindowName(); } + + /** @brief Returns the Mat screenshot of the current scene. + */ + CV_WRAP + cv::Mat getScreenshot() const + { return Viz3d::getScreenshot(); } + + /** @brief Saves screenshot of the current scene. + + @param file Name of the file. + */ + CV_WRAP + void saveScreenshot(const String &file) + { return Viz3d::saveScreenshot(file); } + + /** @brief Sets the position of the window in the screen. + + @param window_position coordinates of the window + */ + CV_WRAP + void setWindowPosition(const Point& window_position) + { return Viz3d::setWindowPosition(window_position); } + + /** @brief Sets or unsets full-screen rendering mode. + + @param mode If true, window will use full-screen mode. + */ + CV_WRAP + void setFullScreen(bool mode = true) + { return Viz3d::setFullScreen(mode); } + + /** @brief Sets background color. + */ + CV_WRAP + void setBackgroundColor(const PyColor& color, const PyColor& color2 = Color::not_set()) + { return Viz3d::setBackgroundColor(color, color2); } + + CV_WRAP + void setBackgroundTexture(InputArray image = noArray()) + { return Viz3d::setBackgroundTexture(image); } + + CV_WRAP + void setBackgroundMeshLab() + { return Viz3d::setBackgroundMeshLab(); } + + /** @brief The window renders and starts the event loop. + */ + CV_WRAP + void spin() + { return Viz3d::spin(); } + + /** @brief Starts the event loop for a given time. + + @param time Amount of time in milliseconds for the event loop to keep running. + @param force_redraw If true, window renders. + */ + CV_WRAP + void spinOnce(int time = 1, bool force_redraw = false) + { return Viz3d::spinOnce(time, force_redraw); } + + /** @brief Create a window in memory instead of on the screen. + */ + CV_WRAP + void setOffScreenRendering() + { return Viz3d::setOffScreenRendering(); } + + /** @brief Remove all lights from the current scene. + */ + CV_WRAP + void removeAllLights() + { return Viz3d::removeAllLights(); } + +#if 0 + /** @brief Add a light in the scene. + + @param position The position of the light. + @param focalPoint The point at which the light is shining + @param color The color of the light + @param diffuseColor The diffuse color of the light + @param ambientColor The ambient color of the light + @param specularColor The specular color of the light + */ + void addLight(const Vec3d &position, const Vec3d &focalPoint = Vec3d(0, 0, 0), const Color &color = Color::white(), + const Color &diffuseColor = Color::white(), const Color &ambientColor = Color::black(), + const Color &specularColor = Color::white()); +#endif + + /** @brief Returns whether the event loop has been stopped. + */ + CV_WRAP + bool wasStopped() const + { return Viz3d::wasStopped(); } + + CV_WRAP + void close() + { return Viz3d::close(); } + + /** @brief Sets rendering property of a widget. + + @param id Id of the widget. + @param property Property that will be modified. + @param value The new value of the property. + + Rendering property can be one of the following: + - **POINT_SIZE** + - **OPACITY** + - **LINE_WIDTH** + - **FONT_SIZE** + + REPRESENTATION: Expected values are + - **REPRESENTATION_POINTS** + - **REPRESENTATION_WIREFRAME** + - **REPRESENTATION_SURFACE** + + IMMEDIATE_RENDERING: + - Turn on immediate rendering by setting the value to 1. + - Turn off immediate rendering by setting the value to 0. + + SHADING: Expected values are + - **SHADING_FLAT** + - **SHADING_GOURAUD** + - **SHADING_PHONG** + */ + CV_WRAP + void setRenderingProperty(const String &id, int property, double value) + { return Viz3d::setRenderingProperty(id, property, value); } + + /** @brief Returns rendering property of a widget. + + @param id Id of the widget. + @param property Property. + + Rendering property can be one of the following: + - **POINT_SIZE** + - **OPACITY** + - **LINE_WIDTH** + - **FONT_SIZE** + + REPRESENTATION: Expected values are + - **REPRESENTATION_POINTS** + - **REPRESENTATION_WIREFRAME** + - **REPRESENTATION_SURFACE** + + IMMEDIATE_RENDERING: + - Turn on immediate rendering by setting the value to 1. + - Turn off immediate rendering by setting the value to 0. + + SHADING: Expected values are + - **SHADING_FLAT** + - **SHADING_GOURAUD** + - **SHADING_PHONG** + */ + CV_WRAP + double getRenderingProperty(const String &id, int property) + { return Viz3d::getRenderingProperty(id, property); } + + /** @brief Sets geometry representation of the widgets to surface, wireframe or points. + + @param representation Geometry representation which can be one of the following: + - **REPRESENTATION_POINTS** + - **REPRESENTATION_WIREFRAME** + - **REPRESENTATION_SURFACE** + */ + CV_WRAP + void setRepresentation(int representation) + { return Viz3d::setRepresentation(representation); } + + CV_WRAP + void setGlobalWarnings(bool enabled = false) + { return Viz3d::setGlobalWarnings(enabled); } +}; + + +}} // namespace +#endif // OPENCV_PYTHON_VIZ_HPP diff --git a/modules/viz/misc/python/test/test_viz_simple.py b/modules/viz/misc/python/test/test_viz_simple.py new file mode 100644 index 000000000..973f3535a --- /dev/null +++ b/modules/viz/misc/python/test/test_viz_simple.py @@ -0,0 +1,439 @@ +import os +import numpy as np +import cv2 as cv + +from tests_common import NewOpenCVTests + +def generate_test_trajectory(): + result = [] + angle_i = np.arange(0, 271, 3) + angle_j = np.arange(0, 1200, 10) + for i, j in zip(angle_i, angle_j): + x = 2 * np.cos(i * 3 * np.pi/180.0) * (1.0 + 0.5 * np.cos(1.2 + i * 1.2 * np.pi/180.0)) + y = 0.25 + i/270.0 + np.sin(j * np.pi/180.0) * 0.2 * np.sin(0.6 + j * 1.5 * np.pi/180.0) + z = 2 * np.sin(i * 3 * np.pi/180.0) * (1.0 + 0.5 * np.cos(1.2 + i * np.pi/180.0)) + result.append(cv.viz.makeCameraPose((x, y, z), (0.0, 0, 0), (0.0, 1.0, 0.0))) + x = np.zeros(shape=(len(result), 1, 16 ), dtype= np.float64) + for idx, m in enumerate(result): + x[idx, 0, :] = m.mat().reshape(16) + return x, result + +def tutorial3(camera_pov, filename): + myWindow = cv.viz_Viz3d("Coordinate Frame") + myWindow.showWidget("axe",cv.viz_WCoordinateSystem()) + + cam_origin = (3.0, 3.0, 3.0) + cam_focal_point = (3.0,3.0,2.0) + cam_y_dir = (-1.0,0.0,0.0) + camera_pose = cv.viz.makeCameraPose(cam_origin, cam_focal_point, cam_y_dir) + transform = cv.viz.makeTransformToGlobal((0.0,-1.0,0.0), (-1.0,0.0,0.0), (0.0,0.0,-1.0), cam_origin) + dragon_cloud,_,_ = cv.viz.readCloud(filename) + cloud_widget = cv.viz_WCloud(dragon_cloud, cv.viz_Color().green()) + cloud_pose = cv.viz_Affine3d() + cloud_pose = cv.viz_Affine3d().rotate((0, np.pi / 2, 0)).translate((0, 0, 3)) + cloud_pose_global = transform.product(cloud_pose) + myWindow.showWidget("CPW_FRUSTUM", cv.viz_WCameraPosition((0.889484, 0.523599)), camera_pose) + if not camera_pov: + myWindow.showWidget("CPW", cv.viz_WCameraPosition(0.5), camera_pose) + myWindow.showWidget("dragon", cloud_widget, cloud_pose_global) + if camera_pov: + myWindow.setViewerPose(camera_pose) + +class viz_test(NewOpenCVTests): + def setUp(self): + super(viz_test, self).setUp() + if not bool(os.environ.get('OPENCV_PYTEST_RUN_VIZ', False)): + self.skipTest("Use OPENCV_PYTEST_RUN_VIZ=1 to enable VIZ UI tests") + + def test_viz_tutorial3_global_view(self): + tutorial3(False, self.find_file("viz/dragon.ply")) + + def test_viz_tutorial3_camera_view(self): + tutorial3(True, self.find_file("viz/dragon.ply")) + + def test_viz(self): + dragon_cloud,_,_ = cv.viz.readCloud(self.find_file("viz/dragon.ply")) + myWindow = cv.viz_Viz3d("abc") + myWindow.showWidget("coo", cv.viz_WCoordinateSystem(1)) + myWindow.showWidget("cloud", cv.viz_WPaintedCloud(dragon_cloud)) + myWindow.spinOnce(500, True) + + def test_viz_show_simple_widgets(self): + viz = cv.viz_Viz3d("show_simple_widgets") + viz.setBackgroundMeshLab() + + viz.showWidget("coos", cv.viz_WCoordinateSystem()) + viz.showWidget("cube", cv.viz_WCube()) + viz.showWidget("cub0", cv.viz_WCube((-1.0, -1, -1), (-0.5, -0.5, -0.5), False, cv.viz_Color().indigo())) + viz.showWidget("arro", cv.viz_WArrow((-0.5, -0.5, -0.5), (0.5, 0.5, 0.5), 0.009, cv.viz_Color().raspberry())) + viz.showWidget("cir1", cv.viz_WCircle(0.5, 0.01, cv.viz_Color.bluberry())) + viz.showWidget("cir2", cv.viz_WCircle(0.5, (0.5, 0.0, 0.0), (1.0, 0.0, 0.0), 0.01, cv.viz_Color().apricot())) + + viz.showWidget("cyl0", cv.viz_WCylinder((-0.5, 0.5, -0.5), (0.5, 0.5, -0.5), 0.125, 30, cv.viz_Color().brown())) + viz.showWidget("con0", cv.viz_WCone(0.25, 0.125, 6, cv.viz_Color().azure())) + viz.showWidget("con1", cv.viz_WCone(0.125, (0.5, -0.5, 0.5), (0.5, -1.0, 0.5), 6, cv.viz_Color().turquoise())) + text2d = cv.viz_WText("Different simple widgets", (20, 20), 20, cv.viz_Color().green()) + viz.showWidget("text2d", text2d) + text3d = cv.viz_WText3D("Simple 3D text", ( 0.5, 0.5, 0.5), 0.125, False, cv.viz_Color().green()) + viz.showWidget("text3d", text3d) + + viz.showWidget("plane1", cv.viz_WPlane((0.25, 0.75))) + viz.showWidget("plane2", cv.viz_WPlane((0.5, -0.5, -0.5), (0.0, 1.0, 1.0), (1.0, 1.0, 0.0), (1.0, 0.5), cv.viz_Color().gold())) + + viz.showWidget("grid1", cv.viz_WGrid((7,7), (0.75,0.75), cv.viz_Color().gray()), cv.viz_Affine3d().translate((0.0, 0.0, -1.0))) + + viz.spinOnce(500, True) + text2d.setText("Different simple widgets (updated)") + text3d.setText("Updated text 3D") + viz.spinOnce(500, True) + + def test_viz_show_overlay_image(self): + lena = cv.imread(self.find_file("viz/lena.png")) + gray = cv.cvtColor(lena, cv.COLOR_BGR2GRAY) + rows = lena.shape[0] + cols = lena.shape[1] + half_lsize = (lena.shape[1] // 2, lena.shape[0] // 2) + + viz = cv.viz_Viz3d("show_overlay_image") + viz.setBackgroundMeshLab(); + vsz = viz.getWindowSize() + + viz.showWidget("coos", cv.viz_WCoordinateSystem()) + viz.showWidget("cube", cv.viz_WCube()) + x = cv.viz_WImageOverlay(lena, (10, 10, half_lsize[1], half_lsize[0])) + viz.showWidget("img1", x) + viz.showWidget("img2", cv.viz_WImageOverlay(gray, (vsz[0] - 10 - cols // 2, 10, half_lsize[1], half_lsize[0]))) + viz.showWidget("img3", cv.viz_WImageOverlay(gray, (10, vsz[1] - 10 - rows // 2, half_lsize[1], half_lsize[0]))) + viz.showWidget("img5", cv.viz_WImageOverlay(lena, (vsz[0] - 10 - cols // 2, vsz[1] - 10 - rows // 2, half_lsize[1], half_lsize[0]))) + viz.showWidget("text2d", cv.viz_WText("Overlay images", (20, 20), 20, cv.viz_Color().green())) + + i = 0 + for num in range(50): + i = i + 1 + a = i % 360 + pose = (3 * np.sin(a * np.pi/180), 2.1, 3 * np.cos(a * np.pi/180)); + viz.setViewerPose(cv.viz.makeCameraPose(pose , (0.0, 0.5, 0.0), (0.0, 0.1, 0.0))) + img = lena * (np.sin(i * 10 * np.pi/180) * 0.5 + 0.5) + x.setImage(img.astype(np.uint8)) + viz.spinOnce(100, True) + viz.showWidget("text2d", cv.viz_WText("Overlay images (stopped)", (20, 20), 20, cv.viz_Color().green())) + viz.spinOnce(500, True) + + def test_viz_show_image_3d(self): + lena = cv.imread(self.find_file("viz/lena.png")) + lena_gray = cv.cvtColor(lena, cv.COLOR_BGR2GRAY) + + viz = cv.viz_Viz3d("show_image_3d") + viz.setBackgroundMeshLab() + viz.showWidget("coos", cv.viz_WCoordinateSystem()) + viz.showWidget("cube", cv.viz_WCube()); + viz.showWidget("arr0", cv.viz_WArrow((0.5, 0.0, 0.0), (1.5, 0.0, 0.0), 0.009, cv.viz_Color().raspberry())) + x = cv.viz_WImage3D(lena, (1.0, 1.0)) + viz.showWidget("img0", x, cv.viz_Affine3d((0.0, np.pi/2, 0.0), (.5, 0.0, 0.0))) + viz.showWidget("arr1", cv.viz_WArrow((-0.5, -0.5, 0.0), (0.2, 0.2, 0.0), 0.009, cv.viz_Color().raspberry())) + viz.showWidget("img1", cv.viz_WImage3D(lena_gray, (1.0, 1.0), (-0.5, -0.5, 0.0), (1.0, 1.0, 0.0), (0.0, 1.0, 0.0))) + + viz.showWidget("arr3", cv.viz_WArrow((-0.5, -0.5, -0.5), (0.5, 0.5, 0.5), 0.009, cv.viz_Color().raspberry())) + + viz.showWidget("text2d", cv.viz_WText("Images in 3D", (20, 20), 20, cv.viz_Color().green())) + + i = 0 + for num in range(50): + img = lena * (np.sin(i*7.5*np.pi/180) * 0.5 + 0.5) + x.setImage(img.astype(np.uint8)) + i = i + 1 + viz.spinOnce(100, True); + viz.showWidget("text2d", cv.viz_WText("Images in 3D (stopped)", (20, 20), 20, cv.viz_Color().green())) + viz.spinOnce(500, True) + + + + def test_viz_show_cloud_bluberry(self): + dragon_cloud,_,_ = cv.viz.readCloud(self.find_file("viz/dragon.ply")) + + pose = cv.viz_Affine3d() + pose = pose.rotate((0, 0.8, 0)); + viz = cv.viz_Viz3d("show_cloud_bluberry") + viz.setBackgroundColor(cv.viz_Color().black()) + viz.showWidget("coosys", cv.viz_WCoordinateSystem()) + viz.showWidget("dragon", cv.viz_WCloud(dragon_cloud, cv.viz_Color().bluberry()), pose) + + viz.showWidget("text2d", cv.viz_WText("Bluberry cloud", (20, 20), 20, cv.viz_Color().green())) + viz.spinOnce(500, True) + + def test_viz_show_cloud_random_color(self): + dragon_cloud,_,_ = cv.viz.readCloud(self.find_file("viz/dragon.ply")) + + colors = np.random.randint(0, 255, size=(dragon_cloud.shape[0],dragon_cloud.shape[1],3), dtype=np.uint8) + + pose = cv.viz_Affine3d() + pose = pose.rotate((0, 0.8, 0)); + + viz = cv.viz_Viz3d("show_cloud_random_color") + viz.setBackgroundMeshLab() + viz.showWidget("coosys", cv.viz_WCoordinateSystem()) + viz.showWidget("dragon", cv.viz_WCloud(dragon_cloud, colors), pose) + viz.showWidget("text2d", cv.viz_WText("Random color cloud", (20, 20), 20, cv.viz_Color().green())) + viz.spinOnce(500, True) + + def test_viz_show_cloud_masked(self): + dragon_cloud,_,_ = cv.viz.readCloud(self.find_file("viz/dragon.ply")) + + qnan = np.NAN + for idx in range(dragon_cloud.shape[0]): + if idx % 15 != 0: + dragon_cloud[idx,:] = qnan + + pose = cv.viz_Affine3d() + pose = pose.rotate((0, 0.8, 0)) + + + viz = cv.viz_Viz3d("show_cloud_masked"); + viz.showWidget("coosys", cv.viz_WCoordinateSystem()) + viz.showWidget("dragon", cv.viz_WCloud(dragon_cloud), pose) + viz.showWidget("text2d", cv.viz_WText("Nan masked cloud", (20, 20), 20, cv.viz_Color().green())) + viz.spinOnce(500, True) + + def test_viz_show_cloud_collection(self): + cloud,_,_ = cv.viz.readCloud(self.find_file("viz/dragon.ply")) + ccol = cv.viz_WCloudCollection() + pose = cv.viz_Affine3d() + pose1 = cv.viz_Affine3d().translate((0, 0, 0)).rotate((np.pi/2, 0, 0)) + ccol.addCloud(cloud, cv.viz_Color().white(), cv.viz_Affine3d().translate((0, 0, 0)).rotate((np.pi/2, 0, 0))) + ccol.addCloud(cloud, cv.viz_Color().blue(), cv.viz_Affine3d().translate((1, 0, 0))) + ccol.addCloud(cloud, cv.viz_Color().red(), cv.viz_Affine3d().translate((2, 0, 0))) + ccol.finalize(); + + viz = cv.viz_Viz3d("show_cloud_collection") + viz.setBackgroundColor(cv.viz_Color().mlab()) + viz.showWidget("coosys", cv.viz_WCoordinateSystem()); + viz.showWidget("ccol", ccol); + viz.showWidget("text2d", cv.viz_WText("Cloud collection", (20, 20), 20, cv.viz_Color(0, 255,0 ))) + viz.spinOnce(500, True) + + def test_viz_show_painted_clouds(self): + cloud,_,_ = cv.viz.readCloud(self.find_file("viz/dragon.ply")) + viz = cv.viz_Viz3d("show_painted_clouds") + viz.setBackgroundMeshLab() + viz.showWidget("coosys", cv.viz_WCoordinateSystem()) + pose1 = cv.viz_Affine3d((0.0, -np.pi/2, 0.0), (-1.5, 0.0, 0.0)) + pose2 = cv.viz_Affine3d((0.0, np.pi/2, 0.0), (1.5, 0.0, 0.0)) + + viz.showWidget("cloud1", cv.viz_WPaintedCloud(cloud), pose1) + viz.showWidget("cloud2", cv.viz_WPaintedCloud(cloud, (0.0, -0.75, -1.0), (0.0, 0.75, 0.0)), pose2); + viz.showWidget("cloud3", cv.viz_WPaintedCloud(cloud, (0.0, 0.0, -1.0), (0.0, 0.0, 1.0), cv.viz_Color().blue(), cv.viz_Color().red())) + viz.showWidget("arrow", cv.viz_WArrow((0.0, 1.0, -1.0), (0.0, 1.0, 1.0), 0.009, cv.viz_Color())) + viz.showWidget("text2d", cv.viz_WText("Painted clouds", (20, 20), 20, cv.viz_Color(0, 255, 0))) + viz.spinOnce(500, True) + + def test_viz_show_mesh(self): + mesh = cv.viz.readMesh(self.find_file("viz/dragon.ply")) + + viz = cv.viz_Viz3d("show_mesh") + viz.showWidget("coosys", cv.viz_WCoordinateSystem()); + viz.showWidget("mesh", cv.viz_WMesh(mesh), cv.viz_Affine3d().rotate((0, 0.8, 0))); + viz.showWidget("text2d", cv.viz_WText("Just mesh", (20, 20), 20, cv.viz_Color().green())) + viz.spinOnce(500, True) + + + def test_viz_show_mesh_random_colors(self): + mesh = cv.viz.readMesh(self.find_file("viz/dragon.ply")) + mesh.colors = np.random.randint(0, 255, size=mesh.colors.shape, dtype=np.uint8) + viz = cv.viz_Viz3d("show_mesh") + viz.showWidget("coosys", cv.viz_WCoordinateSystem()); + viz.showWidget("mesh", cv.viz_WMesh(mesh), cv.viz_Affine3d().rotate((0, 0.8, 0))) + viz.setRenderingProperty("mesh", cv.viz.SHADING, cv.viz.SHADING_PHONG) + viz.showWidget("text2d", cv.viz_WText("Random color mesh", (20, 20), 20, cv.viz_Color().green())) + viz.spinOnce(500, True) + + def test_viz_show_textured_mesh(self): + lena = cv.imread(self.find_file("viz/lena.png")) + + angle = np.arange(0,64) + points0 = np.vstack((np.zeros(shape=angle.shape, dtype=np.float32), np.cos(angle * np.pi /128), np.sin(angle* np.pi /128))) + points1 = np.vstack((1.57 * np.ones(shape=angle.shape, dtype=np.float32),np.cos(angle* np.pi /128), np.sin(angle* np.pi /128))) + tcoords0 = np.vstack((np.zeros(shape=angle.shape, dtype=np.float32), angle / 64)) + tcoords1 = np.vstack((np.ones(shape=angle.shape, dtype=np.float32), angle / 64)) + points = np.zeros(shape=(points0.shape[0], points0.shape[1] * 2 ),dtype=np.float32) + tcoords = np.zeros(shape=(tcoords0.shape[0], tcoords0.shape[1] * 2),dtype=np.float32) + tcoords[:,0::2] = tcoords0 + tcoords[:,1::2] = tcoords1 + points[:,0::2] = points0 * 0.75 + points[:,1::2] = points1 * 0.75 + polygons = np.zeros(shape=(4 * (points.shape[1]-2)+1),dtype=np.int32) + for idx in range(points.shape[1] // 2 - 1): + polygons[8 * idx: 8 * (idx + 1)] = [3, 2*idx, 2*idx+1, 2*idx+2, 3, 2*idx+1, 2*idx+2, 2*idx+3] + + mesh = cv.viz_Mesh() + mesh.cloud = points.transpose().reshape(1,points.shape[1],points.shape[0]) + mesh.tcoords = tcoords.transpose().reshape(1,tcoords.shape[1],tcoords.shape[0]) + mesh.polygons = polygons.reshape(1, 4 * (points.shape[1]-2)+1) + mesh.texture = lena + viz = cv.viz_Viz3d("show_textured_mesh") + viz.setBackgroundMeshLab(); + viz.showWidget("coosys", cv.viz_WCoordinateSystem()); + viz.showWidget("mesh", cv.viz_WMesh(mesh)) + viz.setRenderingProperty("mesh", cv.viz.SHADING, cv.viz.SHADING_PHONG) + viz.showWidget("text2d", cv.viz_WText("Textured mesh", (20, 20), 20, cv.viz_Color().green())); + viz.spinOnce(500, True) + + def test_viz_show_polyline(self): + palette = [ cv.viz_Color().red(), + cv.viz_Color().green(), + cv.viz_Color().blue(), + cv.viz_Color().gold(), + cv.viz_Color().raspberry(), + cv.viz_Color().bluberry(), + cv.viz_Color().lime()] + palette_size = len(palette) + polyline = np.zeros(shape=(1, 32, 3), dtype=np.float32) + colors = np.zeros(shape=(1, 32, 3), dtype=np.uint8) + for i in range(polyline.shape[1]): + polyline[0,i,0] = i / 16.0 + polyline[0,i,1] = np.cos(i * np.pi/6) + polyline[0,i,2] = np.sin(i * np.pi/6) + colors[0,i,0] = palette[i % palette_size].get_blue() + colors[0,i,1] = palette[i % palette_size].get_green() + colors[0,i,2] = palette[i % palette_size].get_red() + + viz = cv.viz_Viz3d("show_polyline") + viz.showWidget("polyline", cv.viz_WPolyLine(polyline, colors)) + viz.showWidget("coosys", cv.viz_WCoordinateSystem()) + viz.showWidget("text2d", cv.viz_WText("Polyline", (20, 20), 20, cv.viz_Color().green())) + viz.spinOnce(500, True) + + def test_viz_show_sampled_normals(self): + + mesh = cv.viz.readMesh(self.find_file("viz/dragon.ply")) + mesh.normals = cv.viz.computeNormals(mesh) + pose = cv.viz_Affine3d().rotate((0, 0.8, 0)) + viz = cv.viz_Viz3d("show_sampled_normals") + viz.showWidget("mesh", cv.viz_WMesh(mesh), pose) + viz.showWidget("normals", cv.viz_WCloudNormals(mesh.cloud, mesh.normals, 30, 0.1, cv.viz_Color().green()), pose) + viz.setRenderingProperty("normals", cv.viz.LINE_WIDTH, 2.0) + viz.showWidget("text2d", cv.viz_WText("Cloud or mesh normals", (20, 20), 20, cv.viz_Color().green())) + viz.spinOnce(500, True); + + + def test_viz_show_cloud_shaded_by_normals(self): + mesh = cv.viz.readMesh(self.find_file("viz/dragon.ply")) + mesh.normals = cv.viz.computeNormals(mesh) + pose = cv.viz_Affine3d().rotate((0, 0.8, 0)) + + cloud = cv.viz_WCloud(mesh.cloud, cv.viz_Color().white(), mesh.normals) + cloud.setRenderingProperty(cv.viz.SHADING, cv.viz.SHADING_GOURAUD) + viz = cv.viz_Viz3d("show_cloud_shaded_by_normals") + + viz.showWidget("cloud", cloud, pose) + viz.showWidget("text2d", cv.viz_WText("Cloud shaded by normals", (20, 20), 20, cv.viz_Color().green())) + viz.spinOnce(500, True) + + def test_viz_show_image_method(self): + lena = cv.imread(self.find_file("viz/lena.png")) + lena_gray = cv.cvtColor(lena, cv.COLOR_BGR2GRAY) + viz = cv.viz_Viz3d("show_image_method") + viz.showImage(lena) + viz.spinOnce(1500, True) + viz.showImage(lena, (lena.shape[1], lena.shape[0])) + viz.spinOnce(1500, True) + + #cv.viz.imshow("show_image_method", lena_gray).spinOnce(500, True) BUG + + def test_viz_show_follower(self): + viz = cv.viz_Viz3d("show_follower") + + viz.showWidget("coos", cv.viz_WCoordinateSystem()) + viz.showWidget("cube", cv.viz_WCube()) + text_3d = cv.viz_WText3D("Simple 3D follower", (-0.5, -0.5, 0.5), 0.125, True, cv.viz_Color().green()) + viz.showWidget("t3d_2", text_3d) + viz.showWidget("text2d", cv.viz_WText("Follower: text always facing camera", (20, 20), 20, cv.viz_Color().green())) + viz.setBackgroundMeshLab() + viz.spinOnce(500, True) + text_3d.setText("Updated follower 3D") + viz.spinOnce(500, True) + + def test_viz_show_trajectory_reposition(self): + mat, path = generate_test_trajectory() + viz = cv.viz_Viz3d("show_trajectory_reposition_to_origin") + viz.showWidget("coos", cv.viz_WCoordinateSystem()) + viz.showWidget("sub3", cv.viz_WTrajectory(mat[0: len(path) // 3,:,:], cv.viz.PyWTrajectory_BOTH, 0.2, cv.viz_Color().brown()), path[0].inv()) + viz.showWidget("text2d", cv.viz_WText("Trajectory resposition to origin", (20, 20), 20, cv.viz_Color().green())) + viz.spinOnce(500, True) + + def test_viz_show_trajectories(self): + mat, path = generate_test_trajectory() + size =len(path) + + sub0 = np.copy(mat[0: size//10+1,::]) + sub1 = np.copy(mat[size//10: size//5+1,::]) + sub2 = np.copy(mat[size//5: 11*size//12,::]) + sub3 = np.copy(mat[11 * size // 12 : size,::]) + sub4 = np.copy(mat[3 * size//4: 33*size//40,::]) + sub5 = np.copy(mat[11*size//12: size,::]) + K = np.array([[1024.0, 0.0, 320.0], [0.0, 1024.0, 240.0], [0.0, 0.0, 1.0]],dtype=np.float64) + + viz = cv.viz_Viz3d("show_trajectories") + viz.showWidget("coos", cv.viz_WCoordinateSystem()) + viz.showWidget("sub0", cv.viz_WTrajectorySpheres(sub0, 0.25, 0.07)) + viz.showWidget("sub1", cv.viz_WTrajectory(sub1, cv.viz.PyWTrajectory_PATH, 0.2, cv.viz_Color().brown())) + viz.showWidget("sub2", cv.viz_WTrajectory(sub2, cv.viz.PyWTrajectory_FRAMES, 0.2)) + viz.showWidget("sub3", cv.viz_WTrajectory(sub3, cv.viz.PyWTrajectory_BOTH, 0.2, cv.viz_Color().green())) + viz.showWidget("sub4", cv.viz_WTrajectoryFrustums(sub4, K, 0.3, cv.viz_Color().yellow())) + viz.showWidget("sub5", cv.viz_WTrajectoryFrustums(sub5, (0.78, 0.78), 0.15, cv.viz_Color().magenta())) #BUG + viz.showWidget("text2d", cv.viz_WText("Different kinds of supported trajectories", (20, 20), 20, cv.viz_Color().green())) + + i = 0 + for num in range(50): + i = i - 1 + a = i % 360 + pose = (np.sin(a * np.pi/180)* 7.5, 0.7, np.cos(a * np.pi/180)* 7.5) + viz.setViewerPose(cv.viz.makeCameraPose(pose , (0.0, 0.5, 0.0), (0.0, 0.1, 0.0))); + viz.spinOnce(100, True) + viz.resetCamera() + viz.spinOnce(500, True) + + def test_viz_show_camera_positions(self): + K = np.array([[1024.0, 0.0, 320.0], [0.0, 1024.0, 240.0], [0.0, 0.0, 1.0]],dtype=np.float64) + lena = cv.imread(self.find_file("viz/lena.png")) + lena_gray = cv.cvtColor(lena, cv.COLOR_BGR2GRAY) + + poses = [] + for i in range(2): + pose = (5 * np.sin(3.14 + 2.7 + i*60 * np.pi/180), 2 - i*1.5, 5 * np.cos(3.14 + 2.7 + i*60 * np.pi/180)) + poses.append(cv.viz.makeCameraPose(pose, (0.0, 0.0, 0.0), (0.0, -0.1, 0.0))) + viz = cv.viz_Viz3d("show_camera_positions") + viz.showWidget("sphe", cv.viz_WSphere((0,0,0), 1.0, 10, cv.viz_Color().orange_red())) + viz.showWidget("coos", cv.viz_WCoordinateSystem(1.5)) + viz.showWidget("pos1", cv.viz_WCameraPosition(0.75), poses[0]) + viz.showWidget("pos2", cv.viz_WCameraPosition((0.78, 0.78), lena, 2.2, cv.viz_Color().green()), poses[0]) + viz.showWidget("pos3", cv.viz_WCameraPosition(0.75), poses[0]) + viz.showWidget("pos4", cv.viz_WCameraPosition(K, lena_gray, 3, cv.viz_Color().indigo()), poses[1]) + viz.showWidget("text2d", cv.viz_WText("Camera positions with images", (20, 20), 20, cv.viz_Color().green())) + viz.spinOnce(500, True) +""" +TEST(Viz, show_widget_merger) +{ + WWidgetMerger merger; + merger.addWidget(WCube(Vec3d::all(0.0), Vec3d::all(1.0), true, Color::gold())); + + RNG& rng = theRNG(); + for(int i = 0; i < 77; ++i) + { + Vec3b c; + rng.fill(c, RNG::NORMAL, Scalar::all(128), Scalar::all(48), true); + merger.addWidget(WSphere(Vec3d(c)*(1.0/255.0), 7.0/255.0, 10, Color(c[2], c[1], c[0]))); + } + merger.finalize(); + + Viz3d viz("show_mesh_random_color"); + viz.showWidget("coo", WCoordinateSystem()); + viz.showWidget("merger", merger); + viz.showWidget("text2d", WText("Widget merger", Point(20, 20), 20, Color::green())); + viz.spinOnce(500, true); +} + + + +""" +if __name__ == '__main__': + NewOpenCVTests.bootstrap() diff --git a/modules/viz/samples/viz_sample_01.py b/modules/viz/samples/viz_sample_01.py new file mode 100644 index 000000000..6da32c9d3 --- /dev/null +++ b/modules/viz/samples/viz_sample_01.py @@ -0,0 +1,12 @@ +import numpy as np +import cv2 as cv + +v = cv.viz.Viz3d_create("Viz Demo") + +print("First event loop is over") +v.spin() +print("Second event loop is over") +v.spinOnce(1, True) +while not v.wasStopped(): + v.spinOnce(1, True) +print("Last event loop is over") diff --git a/modules/viz/samples/viz_sample_02.py b/modules/viz/samples/viz_sample_02.py new file mode 100644 index 000000000..e91a91156 --- /dev/null +++ b/modules/viz/samples/viz_sample_02.py @@ -0,0 +1,33 @@ +import numpy as np +import cv2 as cv + +my_window = cv.viz_Viz3d("Coordinate Frame") + +axe = cv.viz_PyWCoordinateSystem() +axis = cv.viz_PyWLine((-1.0,-1.0,-1.0), (1.0,1.0,1.0), cv.viz_PyColor().green()) +axis.setRenderingProperty(cv.viz.LINE_WIDTH, 4.0); +my_window.showWidget("axe",axis) +plan = cv.viz_PyWPlane((-1.0,-1.0,-1.0), (1.0,.0,.0), (-.0,.0,-1.0)) +#my_window.showWidget("plan", plan) +cube = cv.viz_PyWCube((0.5,0.5,0.0), (0.0,0.0,-0.5), True, cv.viz_PyColor().blue()) + +#my_window.showWidget("Cube Widget",cube) +pi = np.arccos(-1) +print("First event loop is over") +my_window.spin() +print("Second event loop is over") +my_window.spinOnce(1, True) +translation_phase = 0.0 +translation = 0.0 +rot_mat = np.zeros(shape=(3, 3), dtype=np.float32) +rot_vec = np.zeros(shape=(1,3),dtype=np.float32) +while not my_window.wasStopped(): + rot_vec[0, 0] += np.pi * 0.01 + rot_vec[0, 1] += np.pi * 0.01 + rot_vec[0, 2] += np.pi * 0.01 + translation_phase += pi * 0.01 + translation = np.sin(translation_phase) + pose = cv.viz_PyAffine3(rot_vec, (translation, translation, translation)) + my_window.setWidgetPosePy("Cube Widget", pose) + my_window.spinOnce(1, True) +print("Last event loop is over") diff --git a/modules/viz/samples/viz_sample_03.py b/modules/viz/samples/viz_sample_03.py new file mode 100644 index 000000000..49e5722c9 --- /dev/null +++ b/modules/viz/samples/viz_sample_03.py @@ -0,0 +1,41 @@ +import numpy as np +import cv2 as cv + +def load_bunny(): + with open(cv.samples.findFile("viz/bunny.ply"), 'r') as f: + s = f.read() + ligne = s.split('\n') + if len(ligne) == 5753: + pts3d = np.zeros(shape=(1,1889,3), dtype=np.float32) + pts3d_c = 255 * np.ones(shape=(1,1889,3), dtype=np.uint8) + pts3d_n = np.ones(shape=(1,1889,3), dtype=np.float32) + for idx in range(12,1889): + d = ligne[idx].split(' ') + pts3d[0,idx-12,:] = (float(d[0]), float(d[1]), float(d[2])) + pts3d = 5 * pts3d + return cv.viz_PyWCloud(pts3d) + +myWindow = cv.viz_Viz3d("Coordinate Frame") +axe = cv.viz_PyWCoordinateSystem() +myWindow.showWidget("axe",axe) + +cam_pos = (3.0, 3.0, 3.0) +cam_focal_point = (3.0,3.0,2.0) +cam_y_dir = (-1.0,0.0,0.0) +cam_pose = cv.viz.makeCameraPosePy(cam_pos, cam_focal_point, cam_y_dir) +print("OK") +transform = cv.viz.makeTransformToGlobalPy((0.0,-1.0,0.0), (-1.0,0.0,0.0), (0.0,0.0,-1.0), cam_pos) +pw_bunny = load_bunny() +cloud_pose = cv.viz_PyAffine3() +cloud_pose = cloud_pose.translate((0, 0, 3)) +cloud_pose_global = transform.product(cloud_pose) + +cpw = cv.viz_PyWCameraPosition(0.5) +cpw_frustum = cv.viz_PyWCameraPosition(0.3) +myWindow.showWidget("CPW", cpw); +myWindow.showWidget("CPW_FRUSTUM", cpw_frustum) +myWindow.setViewerPosePy(cam_pose) +myWindow.showWidget("bunny", pw_bunny, cloud_pose_global) +#myWindow.setWidgetPosePy("bunny") +myWindow.spin(); +print("Last event loop is over") diff --git a/modules/viz/samples/widget_pose.cpp b/modules/viz/samples/widget_pose.cpp index 5de82bd38..130044f19 100644 --- a/modules/viz/samples/widget_pose.cpp +++ b/modules/viz/samples/widget_pose.cpp @@ -52,6 +52,26 @@ int main() /// Rodrigues vector Mat rot_vec = Mat::zeros(1,3,CV_32F); float translation_phase = 0.0, translation = 0.0; + + rot_vec.at(0, 0) += (float)CV_PI * 0.01f; + rot_vec.at(0, 1) += (float)CV_PI * 0.01f; + rot_vec.at(0, 2) += (float)CV_PI * 0.01f; + + /// Shift on (1,1,1) + translation_phase += (float)CV_PI * 0.01f; + translation = sin(translation_phase); + + Mat rot_mat; + Rodrigues(rot_vec, rot_mat); + cout << "rot_mat = " << rot_mat << endl; + /// Construct pose + Affine3f pose(rot_mat, Vec3f(translation, translation, translation)); + Affine3f pose2(pose.matrix); + cout << "pose = " << pose.matrix << endl; + cout << "pose = " << pose2.matrix << endl; + + + while(!myWindow.wasStopped()) { /* Rotation using rodrigues */ @@ -64,13 +84,13 @@ int main() translation_phase += (float)CV_PI * 0.01f; translation = sin(translation_phase); - Mat rot_mat; - Rodrigues(rot_vec, rot_mat); + Mat rot_mat1; + Rodrigues(rot_vec, rot_mat1); /// Construct pose - Affine3f pose(rot_mat, Vec3f(translation, translation, translation)); + Affine3f pose1(rot_mat1, Vec3f(translation, translation, translation)); - myWindow.setWidgetPose("Cube Widget", pose); + myWindow.setWidgetPose("Cube Widget", pose1); myWindow.spinOnce(1, true); }