C#中使用VTK给点云按照高程值赋色(注释很详细)

前言

C#中使用VTK显示点云,默认的颜色都是单一的颜色,这样不够美观。笔者设计一个颜色渲染函数,可以根据点云的某个轴的数值对点云进行上色。

一、渲染函数代码

       
        /*按照指定的轴设定点云颜色属性
         极小值处设置为蓝色(0,0,255),并且蓝色通道的数值由255向中间位置按坐标变化递减至0,
         绿色通道的数值由0按坐标变化递增至255;中间位置设置为绿色(0,255,0),
         并且绿色通道的数值由255按坐标变化递减至0,红色通道数值由0按坐标变化递增至255;
         极大值位置设置为(255,0,0)*/
        vtkUnsignedCharArray setColorBaseAxis(char axis, PointCloudXYZ in_pc)
        {
            vtkUnsignedCharArray colors_rgb = vtkUnsignedCharArray.New();
            //点云的极值,第一第二个元素分别是x的最小最大值,yz依次类推
            double[] minmax = new double[6];
            in_pc.GetMinMaxXYZ(minmax);
            double z = minmax[5] - minmax[4];//z轴的差值
            double y = minmax[3] - minmax[2];//y轴的差值
            double x = minmax[1] - minmax[0];//x轴的差值
            double z_median = z / 2;
            double y_median = y / 2;
            double x_median = x / 2;
            colors_rgb.SetNumberOfComponents(3);//设置颜色的组分,因为是rgb,所以组分为3
            double r = 0, g = 0, b = 0;
            if (axis == 'x')
            {
                for (int i = 0; i < in_pc.Size; i++)
                {
                    //中间值为界,x值大于中间值的b组分为0,r组分逐渐变大
                    if ((in_pc.GetX(i) - minmax[0]) > x_median)
                    {
                        //x值要先归一化再乘以255,不然数值将会超出255

                        r = (255 * ((in_pc.GetX(i) - minmax[0] - x_median) / x_median)); ;
                        g = (255 * (1 - ((in_pc.GetX(i) - minmax[0] - x_median) / x_median)));
                        b = 0;
                        colors_rgb.InsertNextTuple3(r, g, b);
                    }
                    //中间值为界,x值小于中间值的r组分为0,b组分逐渐变大
                    else
                    {
                        //x值要先归一化再乘以255,不然数值将会超出255
                        r = 0;
                        g = (255 * ((in_pc.GetX(i) - minmax[0]) / x_median));
                        b = (255 * (1 - ((in_pc.GetX(i) - minmax[0]) / x_median))); ;
                        colors_rgb.InsertNextTuple3(r, g, b);
                    }
                }
            }
            else if (axis == 'y')
            {
                for (int i = 0; i < in_pc.Size; i++)
                {
                    //中间值为界,y值大于中间值的b组分为0,r组分逐渐变大
                    if ((in_pc.GetY(i) - minmax[2]) > y_median)
                    {
                        //y值要先归一化再乘以255,不然数值将会超出255
                        r = (255 * ((in_pc.GetY(i) - minmax[2] - y_median) / y_median)); ;
                        g = (255 * (1 - ((in_pc.GetY(i) - minmax[2] - y_median) / y_median)));
                        b = 0;
                        colors_rgb.InsertNextTuple3(r, g, b);
                    }
                    //中间值为界,y值小于中间值的r组分为0,b组分逐渐变大
                    else
                    {
                        r = 0;
                        g = (255 * ((in_pc.GetY(i) - minmax[2]) / y_median));
                        b = (255 * (1 - ((in_pc.GetY(i) - minmax[2]) / y_median))); ;
                        colors_rgb.InsertNextTuple3(r, g, b);
                    }
                }
            }
            else if (axis == 'z')
            {

                for (int i = 0; i < in_pc.Size; i++)
                {
                    //中间值为界,z值大于中间值的b组分为0,r组分逐渐变大
                    if ((in_pc.GetZ(i) - minmax[4]) > z_median)
                    {
                        //z值要先归一化再乘以255,不然数值将会超出255
                        r = (255 * ((in_pc.GetZ(i) - minmax[4] - z_median) / z_median)); ;
                        g = (255 * (1 - ((in_pc.GetZ(i) - minmax[4] - z_median) / z_median)));
                        b = 0;
                        colors_rgb.InsertNextTuple3(r, g, b);
                    }
                    //中间值为界,z值小于中间值的r组分为0,b组分逐渐变大
                    else
                    {
                        r = 0;
                        g = (255 * ((in_pc.GetZ(i) - minmax[4]) / z_median));
                        b = (255 * (1 - ((in_pc.GetZ(i) - minmax[4]) / z_median)));
                        colors_rgb.InsertNextTuple3(r, g, b);
                    }
                }
            }

            return colors_rgb;
        }

二、完整调用代码

        //创建VTK控件renderWindowControl1
        private Kitware.VTK.RenderWindowControl renderWindowControl1;
        
        //点云文件路径
        string url;
        //输入的点云对象
        PointCloudXYZ cloud = new PointCloudXYZ();
        public IoDemoForm()
        {
            InitializeComponent();
            //将VTK控件renderWindowControl添加到父控件panel里
            //必须使用panel控件充当容器装载vtk控件,直接在界面中拖曳vtk控件会报错
            renderWindowControl1 = new Kitware.VTK.RenderWindowControl();
            //将renderWindowControl1边框填充到其父组件里
            renderWindowControl1.Dock = DockStyle.Fill;
            //将指定的renderWindowControl1控件添加到panel1控件集合中
            panel1.Controls.Add(renderWindowControl1);
        }
        //将点云对象可视化
        vtkRenderer showPointCloud(PointCloudXYZ in_pc)
        {
            //显示点云
            vtkPoints points = vtkPoints.New();
            //把点云指针中的点依次放进points
            for (int i = 0; i < cloud.Size; i++)
            {
                points.InsertNextPoint(cloud.GetX(i), cloud.GetY(i), cloud.GetZ(i));

            }
            //创建每个点的属性数据,这里代表颜色
            vtkUnsignedCharArray colors_rgb = setColorBaseAxis('z', cloud);
            vtkPolyData polydata = vtkPolyData.New();
            //将points数据传进polydata
            polydata.SetPoints(points);
            //将点数据的颜色属性传进polydata
            polydata.GetPointData().SetScalars(colors_rgb);

            vtkVertexGlyphFilter glyphFilter = vtkVertexGlyphFilter.New();
            glyphFilter.SetInputConnection(polydata.GetProducerPort());
            // 新建制图器
            vtkPolyDataMapper mapper = vtkPolyDataMapper.New();
            //设置颜色模式,这个是默认模式,不加也行
            //即把unsigned char类型的标量属性数据当作颜色值,不执行隐式。对于其他类型的标量数据,将通过查询表映射
            mapper.SetColorModeToDefault();

            mapper.SetScalarVisibility(1);
            mapper.SetInputConnection(glyphFilter.GetOutputPort());// 连接管道

            vtkActor actor = vtkActor.New(); // 新建角色
            actor.SetMapper(mapper); // 传递制图器

            //给图中添加颜色刻度表,刻度值和颜色表的还没对应
            vtkScalarBarActor scalarBar = vtkScalarBarActor.New();
            scalarBar.SetLookupTable(mapper.GetLookupTable());
            scalarBar.SetTitle("Point Cloud");
            scalarBar.SetHeight(0.7);
            scalarBar.SetWidth(0.1);
            scalarBar.SetNumberOfLabels(10);
            scalarBar.GetLabelTextProperty().SetFontSize(4);
            vtkRenderer out_render = vtkRenderer.New();

            out_render.AddActor(actor);
            //添加颜色刻度表
            out_render.AddActor(scalarBar);
            // 设置Viewport窗口
            out_render.SetViewport(0.0, 0.0, 1.0, 1.0);
            // 打开渐变色背景开关
            out_render.GradientBackgroundOn();
            out_render.SetBackground(0.2, 0.3, 0.3);
            out_render.SetBackground2(0.8, 0.8, 0.8);
            return out_render;
        }
        //加载ply文件
        private void loadPlyButton_Click(object sender, EventArgs e)
        {
            //清空cloud中的点,如果不清空的话,在多次打开新的点云文件时,点数会叠加
            cloud.Clear();
            //打开点云文件
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "请选择点云文件";
            ofd.InitialDirectory = @"\";
            ofd.Filter = "点云文件|*.ply";
            ofd.ShowDialog();
            url = ofd.FileName;
            //加载ply文件,并将点云对象存储到cloud中的PointCloudPointer指针中
            PclCSharp.Io.loadPlyFile(url, cloud.PointCloudXYZPointer);

            vtkRenderer renderer = showPointCloud(cloud);
            vtkRenderWindow renWin = renderWindowControl1.RenderWindow;
            // 将“角色Actor”添加到“渲染器Renderer”并渲染
            renWin.AddRenderer(renderer);
            //刷新panel,这样就不需要点击一下屏幕才会显示点云
            panel1.Refresh();
        }

结果见下图
在这里插入图片描述

总结

注意!!!完整代码中使用了一个额外的库PclCSharp,这是笔者自己封装的Pcl的C#版本库,出现位置在:

        //输入的点云对象
        PointCloudXYZ cloud = new PointCloudXYZ();

        //加载ply文件,并将点云对象存储到cloud中的PointCloudPointer指针中
        PclCSharp.Io.loadPlyFile(url, cloud.PointCloudXYZPointer);

如果想不加修改地直接使用该代码,请移步我的github地址下载对应的包PclCSharp库,要是网络不稳定,可以去我的gitee地址PclCSharp库,如果对你有帮助的话,可以给我一个star。
如果你觉得这样很麻烦,也可以直接将上面的渲染函数加入到你自己的代码中,渲染函数只引用了vtk包。

  • 9
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
VTK是一种强大的可视化工具库,它可以用于处理和显示各种类型的数据,包括点数据。点数据是由一系列离散的点组成的,每个点包含位置信息和颜信息。 在VTK使用RGB点时,需要使用vtkPolyData数据结构来存储点数据,并使用vtkPoints和vtkUnsignedCharArray来分别存储点的位置和颜信息。 首先,我们需要创建一个vtkPolyData对象,并为其添加属性。使用vtkPoints来存储点的位置信息,每个点的位置由三个浮点数表示,分别表示X、Y和Z坐标。使用vtkUnsignedCharArray来存储点的颜信息,每个点的颜由三个无符号整数表示,分别表示红、绿和蓝通道的。可以使用vtkSmartPointer来管理对象的内存,以防止内存泄漏。 接下来,我们需要向vtkPolyData添加点数据。可以使用vtkPolyData的SetPoints方法将vtkPoints对象与vtkPolyData关联起来,以存储点的位置信息。使用vtkPolyData的GetPointData方法可以获取点数据,然后使用SetScalars方法将vtkUnsignedCharArray对象与vtkPolyData关联起来,以存储点的颜信息。 最后,我们可以使用vtkPolyDataMapper和vtkActor将点数据渲染到屏幕上。vtkPolyDataMapper将点数据转换为渲染器可以理解的形式,并将其与vtkActor关联起来。vtkActor定义了点数据的显示属性,如颜、大小和透明度等。 使用vtkRenderer和vtkRenderWindow可以创建一个窗口,并将vtkActor添加到渲染器。最后,调用vtkRenderWindow的Render方法可以将渲染结果显示在窗口上。 总之,通过vtkPolyData、vtkPoints、vtkUnsignedCharArray等类,我们可以在VTK使用RGB点数据,并通过渲染器将其显示在屏幕上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值