#include "precomp.hpp" namespace temp_viz { template Vec<_Tp, 3>* vtkpoints_data(vtkSmartPointer& points); } /////////////////////////////////////////////////////////////////////////////////////////////// /// line widget implementation temp_viz::LineWidget::LineWidget(const Point3f &pt1, const Point3f &pt2, const Color &color) { vtkSmartPointer line = vtkSmartPointer::New(); line->SetPoint1 (pt1.x, pt1.y, pt1.z); line->SetPoint2 (pt2.x, pt2.y, pt2.z); line->Update (); vtkSmartPointer mapper = vtkSmartPointer::New (); mapper->SetInput(line->GetOutput ()); vtkLODActor *actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); actor->SetMapper(mapper); setColor(color); } void temp_viz::LineWidget::setLineWidth(float line_width) { vtkLODActor *actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); actor->GetProperty()->SetLineWidth(line_width); } float temp_viz::LineWidget::getLineWidth() { vtkLODActor *actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); return actor->GetProperty()->GetLineWidth(); } /////////////////////////////////////////////////////////////////////////////////////////////// /// plane widget implementation temp_viz::PlaneWidget::PlaneWidget(const Vec4f& coefs, double size, const Color &color) { vtkSmartPointer plane = vtkSmartPointer::New (); plane->SetNormal (coefs[0], coefs[1], coefs[2]); double norm = cv::norm(cv::Vec3f(coefs.val)); plane->Push (-coefs[3] / norm); vtkSmartPointer mapper = vtkSmartPointer::New (); mapper->SetInput(plane->GetOutput ()); vtkLODActor *actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); actor->SetMapper(mapper); actor->SetScale(size); setColor(color); } temp_viz::PlaneWidget::PlaneWidget(const Vec4f& coefs, const Point3f& pt, double size, const Color &color) { vtkSmartPointer plane = vtkSmartPointer::New (); cv::Point3f coefs3(coefs[0], coefs[1], coefs[2]); double norm_sqr = 1.0 / coefs3.dot (coefs3); plane->SetNormal(coefs[0], coefs[1], coefs[2]); double t = coefs3.dot(pt) + coefs[3]; cv::Vec3f p_center = pt - coefs3 * t * norm_sqr; plane->SetCenter (p_center[0], p_center[1], p_center[2]); vtkSmartPointer mapper = vtkSmartPointer::New (); mapper->SetInput(plane->GetOutput ()); vtkLODActor *actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); actor->SetMapper(mapper); actor->SetScale(size); setColor(color); } /////////////////////////////////////////////////////////////////////////////////////////////// /// sphere widget implementation temp_viz::SphereWidget::SphereWidget(const cv::Point3f ¢er, float radius, int sphere_resolution, const Color &color) { vtkSmartPointer sphere = vtkSmartPointer::New (); sphere->SetRadius (radius); sphere->SetCenter (center.x, center.y, center.z); sphere->SetPhiResolution (sphere_resolution); sphere->SetThetaResolution (sphere_resolution); sphere->LatLongTessellationOff (); sphere->Update (); vtkSmartPointer mapper = vtkSmartPointer::New (); mapper->SetInput(sphere->GetOutput ()); vtkLODActor *actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); actor->SetMapper(mapper); setColor(color); } /////////////////////////////////////////////////////////////////////////////////////////////// /// arrow widget implementation temp_viz::ArrowWidget::ArrowWidget(const Point3f& pt1, const Point3f& pt2, const Color &color) { vtkSmartPointer arrowSource = vtkSmartPointer::New (); float startPoint[3], endPoint[3]; startPoint[0] = pt1.x; startPoint[1] = pt1.y; startPoint[2] = pt1.z; endPoint[0] = pt2.x; endPoint[1] = pt2.y; endPoint[2] = pt2.z; float normalizedX[3], normalizedY[3], normalizedZ[3]; // The X axis is a vector from start to end vtkMath::Subtract(endPoint, startPoint, normalizedX); float length = vtkMath::Norm(normalizedX); vtkMath::Normalize(normalizedX); // The Z axis is an arbitrary vecotr cross X float arbitrary[3]; arbitrary[0] = vtkMath::Random(-10,10); arbitrary[1] = vtkMath::Random(-10,10); arbitrary[2] = vtkMath::Random(-10,10); vtkMath::Cross(normalizedX, arbitrary, normalizedZ); vtkMath::Normalize(normalizedZ); // The Y axis is Z cross X vtkMath::Cross(normalizedZ, normalizedX, normalizedY); vtkSmartPointer matrix = vtkSmartPointer::New(); // Create the direction cosine matrix matrix->Identity(); for (unsigned int i = 0; i < 3; i++) { matrix->SetElement(i, 0, normalizedX[i]); matrix->SetElement(i, 1, normalizedY[i]); matrix->SetElement(i, 2, normalizedZ[i]); } // Apply the transforms vtkSmartPointer transform = vtkSmartPointer::New(); transform->Translate(startPoint); transform->Concatenate(matrix); transform->Scale(length, length, length); // Transform the polydata vtkSmartPointer transformPD = vtkSmartPointer::New(); transformPD->SetTransform(transform); transformPD->SetInputConnection(arrowSource->GetOutputPort()); vtkSmartPointer mapper = vtkSmartPointer::New (); mapper->SetInput(transformPD->GetOutput ()); vtkLODActor *actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); actor->SetMapper(mapper); setColor(color); } /////////////////////////////////////////////////////////////////////////////////////////////// /// circle widget implementation temp_viz::CircleWidget::CircleWidget(const temp_viz::Point3f& pt, double radius, double thickness, const temp_viz::Color& color) { vtkSmartPointer disk = vtkSmartPointer::New (); // Maybe the resolution should be lower e.g. 50 or 25 disk->SetCircumferentialResolution (50); disk->SetInnerRadius (radius - thickness); disk->SetOuterRadius (radius + thickness); // Set the circle origin vtkSmartPointer t = vtkSmartPointer::New (); t->Identity (); t->Translate (pt.x, pt.y, pt.z); vtkSmartPointer tf = vtkSmartPointer::New (); tf->SetTransform (t); tf->SetInputConnection (disk->GetOutputPort ()); vtkSmartPointer mapper = vtkSmartPointer::New (); mapper->SetInput(tf->GetOutput ()); vtkLODActor *actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); actor->SetMapper(mapper); setColor(color); } /////////////////////////////////////////////////////////////////////////////////////////////// /// cylinder widget implementation temp_viz::CylinderWidget::CylinderWidget(const Point3f& pt_on_axis, const Point3f& axis_direction, double radius, int numsides, const Color &color) { const cv::Point3f pt2 = pt_on_axis + axis_direction; vtkSmartPointer line = vtkSmartPointer::New (); line->SetPoint1 (pt_on_axis.x, pt_on_axis.y, pt_on_axis.z); line->SetPoint2 (pt2.x, pt2.y, pt2.z); vtkSmartPointer tuber = vtkSmartPointer::New (); tuber->SetInputConnection (line->GetOutputPort ()); tuber->SetRadius (radius); tuber->SetNumberOfSides (numsides); vtkSmartPointer mapper = vtkSmartPointer::New (); mapper->SetInput(tuber->GetOutput ()); vtkLODActor *actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); actor->SetMapper(mapper); setColor(color); } /////////////////////////////////////////////////////////////////////////////////////////////// /// cylinder widget implementation temp_viz::CubeWidget::CubeWidget(const Point3f& pt_min, const Point3f& pt_max, bool wire_frame, const Color &color) { vtkSmartPointer cube = vtkSmartPointer::New (); cube->SetBounds (pt_min.x, pt_max.x, pt_min.y, pt_max.y, pt_min.z, pt_max.z); vtkSmartPointer mapper = vtkSmartPointer::New (); mapper->SetInput(cube->GetOutput ()); vtkLODActor *actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); actor->SetMapper(mapper); if (wire_frame) actor->GetProperty ()->SetRepresentationToWireframe (); setColor(color); } /////////////////////////////////////////////////////////////////////////////////////////////// /// coordinate system widget implementation temp_viz::CoordinateSystemWidget::CoordinateSystemWidget(double scale, const Affine3f& affine) { vtkSmartPointer axes = vtkSmartPointer::New (); axes->SetOrigin (0, 0, 0); axes->SetScaleFactor (scale); vtkSmartPointer axes_colors = vtkSmartPointer::New (); axes_colors->Allocate (6); axes_colors->InsertNextValue (0.0); axes_colors->InsertNextValue (0.0); axes_colors->InsertNextValue (0.5); axes_colors->InsertNextValue (0.5); axes_colors->InsertNextValue (1.0); axes_colors->InsertNextValue (1.0); vtkSmartPointer axes_data = axes->GetOutput (); axes_data->Update (); axes_data->GetPointData ()->SetScalars (axes_colors); vtkSmartPointer axes_tubes = vtkSmartPointer::New (); axes_tubes->SetInput (axes_data); axes_tubes->SetRadius (axes->GetScaleFactor () / 50.0); axes_tubes->SetNumberOfSides (6); vtkSmartPointer mapper = vtkSmartPointer::New (); mapper->SetScalarModeToUsePointData (); mapper->SetInput(axes_tubes->GetOutput ()); vtkLODActor *actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); actor->SetMapper(mapper); cv::Vec3d t = affine.translation(); actor->SetPosition (t[0], t[1], t[2]); cv::Matx33f m = affine.rotation(); cv::Vec3f rvec; cv::Rodrigues(m, rvec); float r_angle = cv::norm(rvec); rvec *= 1.f/r_angle; actor->SetOrientation(0,0,0); actor->RotateWXYZ(r_angle*180/CV_PI,rvec[0], rvec[1], rvec[2]); } /////////////////////////////////////////////////////////////////////////////////////////////// /// text widget implementation temp_viz::TextWidget::TextWidget(const String &text, const Point2i &pos, int font_size, const Color &color) : Widget(true) { vtkTextActor *actor = vtkTextActor::SafeDownCast(WidgetAccessor::getActor(*this)); actor->SetPosition (pos.x, pos.y); actor->SetInput (text.c_str ()); vtkSmartPointer tprop = actor->GetTextProperty (); tprop->SetFontSize (font_size); tprop->SetFontFamilyToArial (); tprop->SetJustificationToLeft (); tprop->BoldOn (); Color c = vtkcolor(color); tprop->SetColor (c.val); } /////////////////////////////////////////////////////////////////////////////////////////////// /// point cloud widget implementation temp_viz::CloudWidget::CloudWidget(InputArray _cloud, InputArray _colors) { Mat cloud = _cloud.getMat(); Mat colors = _colors.getMat(); CV_Assert(cloud.type() == CV_32FC3 || cloud.type() == CV_64FC3 || cloud.type() == CV_32FC4 || cloud.type() == CV_64FC4); CV_Assert(colors.type() == CV_8UC3 && cloud.size() == colors.size()); vtkLODActor * actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); vtkSmartPointer polydata = vtkSmartPointer::New (); vtkSmartPointer vertices = vtkSmartPointer::New (); polydata->SetVerts (vertices); vtkSmartPointer points = polydata->GetPoints(); vtkSmartPointer initcells; vtkIdType nr_points = cloud.total(); points = polydata->GetPoints (); if (!points) { points = vtkSmartPointer::New (); if (cloud.depth() == CV_32F) points->SetDataTypeToFloat(); else if (cloud.depth() == CV_64F) points->SetDataTypeToDouble(); polydata->SetPoints (points); } points->SetNumberOfPoints (nr_points); if (cloud.depth() == CV_32F) { // Get a pointer to the beginning of the data array Vec3f *data_beg = vtkpoints_data(points); Vec3f *data_end = NanFilter::copy(cloud, data_beg, cloud); nr_points = data_end - data_beg; } else if (cloud.depth() == CV_64F) { // Get a pointer to the beginning of the data array Vec3d *data_beg = vtkpoints_data(points); Vec3d *data_end = NanFilter::copy(cloud, data_beg, cloud); nr_points = data_end - data_beg; } points->SetNumberOfPoints (nr_points); // Update cells vtkSmartPointer cells = vertices->GetData (); // If no init cells and cells has not been initialized... if (!cells) cells = vtkSmartPointer::New (); // If we have less values then we need to recreate the array if (cells->GetNumberOfTuples () < nr_points) { cells = vtkSmartPointer::New (); // If init cells is given, and there's enough data in it, use it if (initcells && initcells->GetNumberOfTuples () >= nr_points) { cells->DeepCopy (initcells); cells->SetNumberOfComponents (2); cells->SetNumberOfTuples (nr_points); } else { // If the number of tuples is still too small, we need to recreate the array cells->SetNumberOfComponents (2); cells->SetNumberOfTuples (nr_points); vtkIdType *cell = cells->GetPointer (0); // Fill it with 1s std::fill_n (cell, nr_points * 2, 1); cell++; for (vtkIdType i = 0; i < nr_points; ++i, cell += 2) *cell = i; // Save the results in initcells initcells = vtkSmartPointer::New (); initcells->DeepCopy (cells); } } else { // The assumption here is that the current set of cells has more data than needed cells->SetNumberOfComponents (2); cells->SetNumberOfTuples (nr_points); } // Set the cells and the vertices vertices->SetCells (nr_points, cells); Vec3b* colors_data = new Vec3b[nr_points]; NanFilter::copy(colors, colors_data, cloud); vtkSmartPointer scalars = vtkSmartPointer::New (); scalars->SetNumberOfComponents (3); scalars->SetNumberOfTuples (nr_points); scalars->SetArray (colors_data->val, 3 * nr_points, 0); // Assign the colors polydata->GetPointData ()->SetScalars (scalars); vtkSmartPointer mapper = vtkSmartPointer::New (); mapper->SetInput (polydata); cv::Vec3d minmax(scalars->GetRange()); mapper->SetScalarRange(minmax.val); mapper->SetScalarModeToUsePointData (); bool interpolation = (polydata && polydata->GetNumberOfCells () != polydata->GetNumberOfVerts ()); mapper->SetInterpolateScalarsBeforeMapping (interpolation); mapper->ScalarVisibilityOn (); mapper->ImmediateModeRenderingOff (); actor->SetNumberOfCloudPoints (int (std::max (1, polydata->GetNumberOfPoints () / 10))); actor->GetProperty ()->SetInterpolationToFlat (); actor->GetProperty ()->BackfaceCullingOn (); actor->SetMapper (mapper); } temp_viz::CloudWidget::CloudWidget(InputArray _cloud, const Color &color) { Mat cloud = _cloud.getMat(); CV_Assert(cloud.type() == CV_32FC3 || cloud.type() == CV_64FC3 || cloud.type() == CV_32FC4 || cloud.type() == CV_64FC4); vtkLODActor * actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); vtkSmartPointer polydata = vtkSmartPointer::New (); vtkSmartPointer vertices = vtkSmartPointer::New (); polydata->SetVerts (vertices); vtkSmartPointer points = polydata->GetPoints(); vtkSmartPointer initcells; vtkIdType nr_points = cloud.total(); points = polydata->GetPoints (); if (!points) { points = vtkSmartPointer::New (); if (cloud.depth() == CV_32F) points->SetDataTypeToFloat(); else if (cloud.depth() == CV_64F) points->SetDataTypeToDouble(); polydata->SetPoints (points); } points->SetNumberOfPoints (nr_points); if (cloud.depth() == CV_32F) { // Get a pointer to the beginning of the data array Vec3f *data_beg = vtkpoints_data(points); Vec3f *data_end = NanFilter::copy(cloud, data_beg, cloud); nr_points = data_end - data_beg; } else if (cloud.depth() == CV_64F) { // Get a pointer to the beginning of the data array Vec3d *data_beg = vtkpoints_data(points); Vec3d *data_end = NanFilter::copy(cloud, data_beg, cloud); nr_points = data_end - data_beg; } points->SetNumberOfPoints (nr_points); // Update cells vtkSmartPointer cells = vertices->GetData (); // If no init cells and cells has not been initialized... if (!cells) cells = vtkSmartPointer::New (); // If we have less values then we need to recreate the array if (cells->GetNumberOfTuples () < nr_points) { cells = vtkSmartPointer::New (); // If init cells is given, and there's enough data in it, use it if (initcells && initcells->GetNumberOfTuples () >= nr_points) { cells->DeepCopy (initcells); cells->SetNumberOfComponents (2); cells->SetNumberOfTuples (nr_points); } else { // If the number of tuples is still too small, we need to recreate the array cells->SetNumberOfComponents (2); cells->SetNumberOfTuples (nr_points); vtkIdType *cell = cells->GetPointer (0); // Fill it with 1s std::fill_n (cell, nr_points * 2, 1); cell++; for (vtkIdType i = 0; i < nr_points; ++i, cell += 2) *cell = i; // Save the results in initcells initcells = vtkSmartPointer::New (); initcells->DeepCopy (cells); } } else { // The assumption here is that the current set of cells has more data than needed cells->SetNumberOfComponents (2); cells->SetNumberOfTuples (nr_points); } // Set the cells and the vertices vertices->SetCells (nr_points, cells); Color c = vtkcolor(color); polydata->GetPointData ()->SetScalars (0); vtkSmartPointer mapper = vtkSmartPointer::New (); mapper->SetInput (polydata); bool interpolation = (polydata && polydata->GetNumberOfCells () != polydata->GetNumberOfVerts ()); mapper->SetInterpolateScalarsBeforeMapping (interpolation); mapper->ScalarVisibilityOff (); mapper->ImmediateModeRenderingOff (); actor->SetNumberOfCloudPoints (int (std::max (1, polydata->GetNumberOfPoints () / 10))); actor->GetProperty ()->SetInterpolationToFlat (); actor->GetProperty ()->BackfaceCullingOn (); actor->GetProperty ()->SetColor(c.val); actor->SetMapper (mapper); } /////////////////////////////////////////////////////////////////////////////////////////////// /// cloud normals widget implementation struct temp_viz::CloudNormalsWidget::ApplyCloudNormals { template struct Impl { static vtkSmartPointer applyOrganized(const cv::Mat &cloud, const cv::Mat& normals, int level, float scale, _Tp *&pts, vtkIdType &nr_normals) { vtkIdType point_step = static_cast (sqrt (double (level))); nr_normals = (static_cast ((cloud.cols - 1)/ point_step) + 1) * (static_cast ((cloud.rows - 1) / point_step) + 1); vtkSmartPointer lines = vtkSmartPointer::New(); pts = new _Tp[2 * nr_normals * 3]; int cch = cloud.channels(); vtkIdType cell_count = 0; for (vtkIdType y = 0; y < cloud.rows; y += point_step) { const _Tp *prow = cloud.ptr<_Tp>(y); const _Tp *nrow = normals.ptr<_Tp>(y); for (vtkIdType x = 0; x < cloud.cols; x += point_step + cch) { pts[2 * cell_count * 3 + 0] = prow[x]; pts[2 * cell_count * 3 + 1] = prow[x+1]; pts[2 * cell_count * 3 + 2] = prow[x+2]; pts[2 * cell_count * 3 + 3] = prow[x] + nrow[x] * scale; pts[2 * cell_count * 3 + 4] = prow[x+1] + nrow[x+1] * scale; pts[2 * cell_count * 3 + 5] = prow[x+2] + nrow[x+2] * scale; lines->InsertNextCell (2); lines->InsertCellPoint (2 * cell_count); lines->InsertCellPoint (2 * cell_count + 1); cell_count++; } } return lines; } static vtkSmartPointer applyUnorganized(const cv::Mat &cloud, const cv::Mat& normals, int level, float scale, _Tp *&pts, vtkIdType &nr_normals) { vtkSmartPointer lines = vtkSmartPointer::New(); nr_normals = (cloud.size().area() - 1) / level + 1 ; pts = new _Tp[2 * nr_normals * 3]; int cch = cloud.channels(); const _Tp *p = cloud.ptr<_Tp>(); const _Tp *n = normals.ptr<_Tp>(); for (vtkIdType i = 0, j = 0; j < nr_normals; j++, i = j * level * cch) { pts[2 * j * 3 + 0] = p[i]; pts[2 * j * 3 + 1] = p[i+1]; pts[2 * j * 3 + 2] = p[i+2]; pts[2 * j * 3 + 3] = p[i] + n[i] * scale; pts[2 * j * 3 + 4] = p[i+1] + n[i+1] * scale; pts[2 * j * 3 + 5] = p[i+2] + n[i+2] * scale; lines->InsertNextCell (2); lines->InsertCellPoint (2 * j); lines->InsertCellPoint (2 * j + 1); } return lines; } }; template static inline vtkSmartPointer apply(const cv::Mat &cloud, const cv::Mat& normals, int level, float scale, _Tp *&pts, vtkIdType &nr_normals) { if (cloud.cols > 1 && cloud.rows > 1) return ApplyCloudNormals::Impl<_Tp>::applyOrganized(cloud, normals, level, scale, pts, nr_normals); else return ApplyCloudNormals::Impl<_Tp>::applyUnorganized(cloud, normals, level, scale, pts, nr_normals); } }; temp_viz::CloudNormalsWidget::CloudNormalsWidget(InputArray _cloud, InputArray _normals, int level, float scale) { Mat cloud = _cloud.getMat(); Mat normals = _normals.getMat(); CV_Assert(cloud.type() == CV_32FC3 || cloud.type() == CV_64FC3 || cloud.type() == CV_32FC4 || cloud.type() == CV_64FC4); CV_Assert(cloud.size() == normals.size() && cloud.type() == normals.type()); vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer lines = vtkSmartPointer::New(); vtkIdType nr_normals = 0; if (cloud.depth() == CV_32F) { points->SetDataTypeToFloat(); vtkSmartPointer data = vtkSmartPointer::New (); data->SetNumberOfComponents (3); float* pts = 0; lines = ApplyCloudNormals::apply(cloud, normals, level, scale, pts, nr_normals); data->SetArray (&pts[0], 2 * nr_normals * 3, 0); points->SetData (data); } else { points->SetDataTypeToDouble(); vtkSmartPointer data = vtkSmartPointer::New (); data->SetNumberOfComponents (3); double* pts = 0; lines = ApplyCloudNormals::apply(cloud, normals, level, scale, pts, nr_normals); data->SetArray (&pts[0], 2 * nr_normals * 3, 0); points->SetData (data); } vtkSmartPointer polyData = vtkSmartPointer::New(); polyData->SetPoints (points); polyData->SetLines (lines); vtkSmartPointer mapper = vtkSmartPointer::New (); mapper->SetInput (polyData); mapper->SetColorModeToMapScalars(); mapper->SetScalarModeToUsePointData(); vtkLODActor * actor = vtkLODActor::SafeDownCast(WidgetAccessor::getActor(*this)); actor->SetMapper(mapper); }