diff --git a/modules/viz/include/opencv2/viz.hpp b/modules/viz/include/opencv2/viz.hpp index 32e32a7a3b..89810d5472 100644 --- a/modules/viz/include/opencv2/viz.hpp +++ b/modules/viz/include/opencv2/viz.hpp @@ -98,6 +98,11 @@ namespace cv CV_EXPORTS void writeCloud(const String& file, InputArray cloud, InputArray colors = noArray(), InputArray normals = noArray(), bool binary = false); CV_EXPORTS Mat readCloud (const String& file, OutputArray colors = noArray(), OutputArray normals = noArray()); + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Read mesh. Only ply format is supported now + + CV_EXPORTS Mesh readMesh (const String& file); + /////////////////////////////////////////////////////////////////////////////////////////////// /// Read/write poses and trajectories diff --git a/modules/viz/include/opencv2/viz/types.hpp b/modules/viz/include/opencv2/viz/types.hpp index 01ddd0289e..cdd89d3a0f 100644 --- a/modules/viz/include/opencv2/viz/types.hpp +++ b/modules/viz/include/opencv2/viz/types.hpp @@ -113,6 +113,8 @@ namespace cv //! where n is the number of points in the poligon, and id is a zero-offset index into an associated cloud. Mat polygons; + Mat texture, tcoords; + //! Loads mesh from a given ply file static Mesh load(const String& file); }; diff --git a/modules/viz/src/clouds.cpp b/modules/viz/src/clouds.cpp index d5283d430b..ff81775465 100644 --- a/modules/viz/src/clouds.cpp +++ b/modules/viz/src/clouds.cpp @@ -95,8 +95,25 @@ cv::viz::WPaintedCloud::WPaintedCloud(InputArray cloud) Vec6d bounds(cloud_source->GetOutput()->GetPoints()->GetBounds()); - WPaintedCloud cloud_widget(cloud, Vec3d(bounds[0], bounds[2], bounds[4]), Vec3d(bounds[1], bounds[3], bounds[5])); - *this = cloud_widget; + vtkSmartPointer elevation = vtkSmartPointer::New(); + elevation->SetInputConnection(cloud_source->GetOutputPort()); + elevation->SetLowPoint(bounds[0], bounds[2], bounds[4]); + elevation->SetHighPoint(bounds[1], bounds[3], bounds[5]); + elevation->SetScalarRange(0.0, 1.0); + elevation->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, vtkPolyData::SafeDownCast(elevation->GetOutput())); + mapper->ImmediateModeRenderingOff(); + mapper->ScalarVisibilityOn(); + mapper->SetColorModeToMapScalars(); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); } cv::viz::WPaintedCloud::WPaintedCloud(InputArray cloud, const Point3d& p1, const Point3d& p2) @@ -329,7 +346,7 @@ cv::viz::WMesh::WMesh(const Mesh &mesh) CV_Assert(mesh.cloud.rows == 1 && mesh.polygons.type() == CV_32SC1); vtkSmartPointer source = vtkSmartPointer::New(); - source->SetColorCloud(mesh.cloud, mesh.colors); + source->SetColorCloudNormalsTCoords(mesh.cloud, mesh.colors, mesh.normals, mesh.tcoords); source->Update(); Mat lookup_buffer(1, mesh.cloud.total(), CV_32SC1); @@ -394,6 +411,16 @@ cv::viz::WMesh::WMesh(const Mesh &mesh) actor->GetProperty()->ShadingOff(); actor->SetMapper(mapper); + if (!mesh.texture.empty()) + { + vtkSmartPointer image_source = vtkSmartPointer::New(); + image_source->SetImage(mesh.texture); + + vtkSmartPointer texture = vtkSmartPointer::New(); + texture->SetInputConnection(image_source->GetOutputPort()); + actor->SetTexture(texture); + } + WidgetAccessor::setProp(*this, actor); } diff --git a/modules/viz/src/vizcore.cpp b/modules/viz/src/vizcore.cpp index a553f8d721..ee74f2a123 100644 --- a/modules/viz/src/vizcore.cpp +++ b/modules/viz/src/vizcore.cpp @@ -205,6 +205,8 @@ cv::Mat cv::viz::readCloud(const String& file, OutputArray colors, OutputArray n return cloud; } +cv::viz::Mesh cv::viz::readMesh(const String& file) { return Mesh::load(file); } + /////////////////////////////////////////////////////////////////////////////////////////////// /// Read/write poses and trajectories diff --git a/modules/viz/src/vtk/vtkCloudMatSource.cpp b/modules/viz/src/vtk/vtkCloudMatSource.cpp index 7884f1a9a2..9a341b78ca 100644 --- a/modules/viz/src/vtk/vtkCloudMatSource.cpp +++ b/modules/viz/src/vtk/vtkCloudMatSource.cpp @@ -128,7 +128,34 @@ int cv::viz::vtkCloudMatSource::SetColorCloudNormals(InputArray _cloud, InputArr else if (normals.depth() == CV_64F && cloud.depth() == CV_64F) filterNanNormalsCopy(normals, cloud, total); else - CV_Assert(!"Unsupported normals type"); + CV_Assert(!"Unsupported normals/cloud type"); + + return total; +} + +int cv::viz::vtkCloudMatSource::SetColorCloudNormalsTCoords(InputArray _cloud, InputArray _colors, InputArray _normals, InputArray _tcoords) +{ + int total = SetColorCloudNormals(_cloud, _colors, _normals); + + if (_tcoords.empty()) + return total; + + CV_Assert(_tcoords.depth() == CV_32F || _tcoords.depth() == CV_64F); + CV_Assert(_tcoords.channels() == 2 && _tcoords.size() == _cloud.size()); + + Mat cloud = _cloud.getMat(); + Mat tcoords = _tcoords.getMat(); + + if (tcoords.depth() == CV_32F && cloud.depth() == CV_32F) + filterNanTCoordsCopy(tcoords, cloud, total); + else if (tcoords.depth() == CV_32F && cloud.depth() == CV_64F) + filterNanTCoordsCopy(tcoords, cloud, total); + else if (tcoords.depth() == CV_64F && cloud.depth() == CV_32F) + filterNanTCoordsCopy(tcoords, cloud, total); + else if (tcoords.depth() == CV_64F && cloud.depth() == CV_64F) + filterNanTCoordsCopy(tcoords, cloud, total); + else + CV_Assert(!"Unsupported tcoords/cloud type"); return total; } @@ -146,6 +173,9 @@ int cv::viz::vtkCloudMatSource::RequestData(vtkInformation *vtkNotUsed(request), if (normals) output->GetPointData()->SetNormals(normals); + if (tcoords) + output->GetPointData()->SetTCoords(tcoords); + return 1; } @@ -232,3 +262,25 @@ void cv::viz::vtkCloudMatSource::filterNanNormalsCopy(const Mat& cloud_normals, normals->SetTuple(pos++, srow); } } + +template +void cv::viz::vtkCloudMatSource::filterNanTCoordsCopy(const Mat& _tcoords, const Mat& mask, int total) +{ + typedef Vec<_Tn, 2> Vec2; + tcoords = vtkSmartPointer< VtkDepthTraits<_Tn>::array_type >::New(); + tcoords->SetName("TextureCoordinates"); + tcoords->SetNumberOfComponents(2); + tcoords->SetNumberOfTuples(total); + + int pos = 0; + for (int y = 0; y < mask.rows; ++y) + { + const Vec2* srow = _tcoords.ptr(y); + const Vec2* send = srow + _tcoords.cols; + const _Msk* mrow = mask.ptr<_Msk>(y); + + for (; srow != send; ++srow, mrow += mask.channels()) + if (!isNan(mrow)) + tcoords->SetTuple(pos++, srow->val); + } +} diff --git a/modules/viz/src/vtk/vtkCloudMatSource.h b/modules/viz/src/vtk/vtkCloudMatSource.h index a7f9fff89a..56bd93e066 100644 --- a/modules/viz/src/vtk/vtkCloudMatSource.h +++ b/modules/viz/src/vtk/vtkCloudMatSource.h @@ -62,8 +62,9 @@ namespace cv vtkTypeMacro(vtkCloudMatSource,vtkPolyDataAlgorithm) virtual int SetCloud(InputArray cloud); - virtual int SetColorCloud(InputArray cloud, InputArray colors = noArray()); - virtual int SetColorCloudNormals(InputArray cloud, InputArray colors = noArray(), InputArray normals = noArray()); + virtual int SetColorCloud(InputArray cloud, InputArray colors); + virtual int SetColorCloudNormals(InputArray cloud, InputArray colors, InputArray normals); + virtual int SetColorCloudNormalsTCoords(InputArray cloud, InputArray colors, InputArray normals, InputArray tcoords); protected: vtkCloudMatSource(); @@ -75,6 +76,7 @@ namespace cv vtkSmartPointer vertices; vtkSmartPointer scalars; vtkSmartPointer normals; + vtkSmartPointer tcoords; private: vtkCloudMatSource(const vtkCloudMatSource&); // Not implemented. void operator=(const vtkCloudMatSource&); // Not implemented. @@ -84,6 +86,9 @@ namespace cv template void filterNanNormalsCopy(const Mat& cloud_normals, const Mat& mask, int total); + + template + void filterNanTCoordsCopy(const Mat& tcoords, const Mat& mask, int total); }; } }