点云显示控件WPF设计

前言: 

本文承接上文,主要讲解获取或者转换后的点云(ply,ob,om3)如何进行自定义显示;控件设计遵循WPF的MVVM设计模式,UI风格使用XAML语言,下面先给大家展示一组实际效果图:

图中分别显示了2D图,3D点云以及2D轮廓线

1. 2D显示使用了HSmartWindowControlWPF控件,并添加了右键菜单,可手动加载图像或轮廓;

xaml代码如下:

        <StackPanel Grid.Row="1"  Grid.Column="0" Margin="5" 
                    x:Name="panleDepth" 
                    SizeChanged="StackPanel_SizeChanged">
            <Label Content="深度图" FontWeight="Bold"  FontSize="14"
                   x:Name="lblDepth"  Foreground="Green"
                   Background="Transparent"/>
            <HalconDotNet:HSmartWindowControlWPF   BorderBrush="Green"
                                                   BorderThickness="1.5"
                   HDoubleClickToFitContent="True" 
                        x:Name="hostDepth" Height="225" >
                <HalconDotNet:HSmartWindowControlWPF.ContextMenu>
                    <ContextMenu >
                        <MenuItem Header="加载深度图"     
                 Command="{Binding MenuItemDepthClickCommand}"
 >

                        </MenuItem>
                    </ContextMenu>
                </HalconDotNet:HSmartWindowControlWPF.ContextMenu>
            </HalconDotNet:HSmartWindowControlWPF>
        </StackPanel>

ViewModel中CS代码:

 /// <summary>
 /// 加载深度图
 /// </summary>
 private void MenuItemDepthClick()
 {
     OpenFileDialog m_OpenFileDialog = new OpenFileDialog();
     m_OpenFileDialog.Multiselect = true;
     m_OpenFileDialog.Filter = "TIFF文件|*.tiff*";
     if (m_OpenFileDialog.ShowDialog().Value)
     {
         HOperatorSet.ReadImage(out  DepthImg, m_OpenFileDialog.FileName);
         HOperatorSet.ClearWindow(hostDepth.HalconWindow);
         HOperatorSet.DispObj(DepthImg, hostDepth.HalconWindow);           

     }
 }

在这里轮廓的单独显示和图像显示不一样,为了确保显示比例大小适中,需要做一些额外的转换:

 /// <summary>
 /// 单独显示轮廓
 /// </summary>
 /// <param name="contour"></param>
 /// <param name="hv_WindowHandle"></param>
 private static void ShowContour(HTuple winHandle,
     HObject contour)

 {
     if (!ObjectValided(contour))
         return;
     // Local iconic variables 

     HObject ho_Contours1, ho_ContoursAffineTrans;

     // Local control variables 

     HTuple hv_DxfStatus1 = new HTuple();
     HTuple hv_Row1 = new HTuple(), hv_Column1 = new HTuple();
     HTuple hv_Width = new HTuple(), hv_Height = new HTuple();
     HTuple hv_cr = new HTuple(), hv_cc = new HTuple(), hv_Area = new HTuple();
     HTuple hv_Row = new HTuple(), hv_Column = new HTuple();
     HTuple hv_PointOrder = new HTuple(), hv_Row11 = new HTuple();
     HTuple hv_Column11 = new HTuple(), hv_Row2 = new HTuple();
     HTuple hv_Column2 = new HTuple(), hv_width1 = new HTuple();
     HTuple hv_height1 = new HTuple(), hv_k1 = new HTuple();
     HTuple hv_k2 = new HTuple(), hv_k = new HTuple(), hv_HomMat2DIdentity = new HTuple();
     HTuple hv_HomMat2DScale1 = new HTuple(), hv_RowTrans = new HTuple();
     HTuple hv_ColTrans = new HTuple(), hv_HomMat2DTranslate = new HTuple();
     // Initialize local and output iconic variables 
     HOperatorSet.GenEmptyObj(out ho_Contours1);
     HOperatorSet.GenEmptyObj(out ho_ContoursAffineTrans);
     //read_contour_xld_dxf (Contours, 'C:/Users/Administrator/Desktop/transply.dxf', [], [], DxfStatus)
     /选择最长轮廓中心点
     HOperatorSet.CountObj(contour, out HTuple number);
     int num = number.I;
     double maxLength = 0;
     int indexer = 0;
     for (int i = 1; i <= num; i++)
     {
         HOperatorSet.SelectObj(contour, out HObject objectSelected, i);

         HOperatorSet.LengthXld(objectSelected, out HTuple xldLength);
         if (xldLength.D > maxLength)
         {
             maxLength = xldLength.D;
             indexer = i;
         }
     }
     ///选择最长的一个
     ho_Contours1.Dispose();
     HOperatorSet.SelectObj(contour, out ho_Contours1, indexer);

     hv_Row1.Dispose(); hv_Column1.Dispose(); hv_Width.Dispose(); hv_Height.Dispose();
     HOperatorSet.GetWindowExtents(winHandle, out hv_Row1, out hv_Column1, out hv_Width,
         out hv_Height);
     hv_cr.Dispose();
     using (HDevDisposeHelper dh = new HDevDisposeHelper())
     {
         hv_cr = hv_Row1 + (hv_Height / 2);
     }
     hv_cc.Dispose();
     using (HDevDisposeHelper dh = new HDevDisposeHelper())
     {
         hv_cc = hv_Column1 + (hv_Width / 2);
     }

     hv_Area.Dispose(); hv_Row.Dispose(); hv_Column.Dispose(); hv_PointOrder.Dispose();
     HOperatorSet.AreaCenterXld(ho_Contours1, out hv_Area, out hv_Row, out hv_Column,
         out hv_PointOrder);

     hv_Row11.Dispose(); hv_Column11.Dispose(); hv_Row2.Dispose(); hv_Column2.Dispose();
     HOperatorSet.SmallestRectangle1Xld(ho_Contours1, out hv_Row11, out hv_Column11,
         out hv_Row2, out hv_Column2);
     hv_width1.Dispose();
     using (HDevDisposeHelper dh = new HDevDisposeHelper())
     {
         hv_width1 = hv_Column2 - hv_Column11;
     }
     hv_height1.Dispose();
     using (HDevDisposeHelper dh = new HDevDisposeHelper())
     {
         hv_height1 = hv_Row2 - hv_Row11;
     }
     hv_k1.Dispose();
     using (HDevDisposeHelper dh = new HDevDisposeHelper())
     {
         hv_k1 = hv_Width / hv_width1;
     }
     hv_k2.Dispose();
     using (HDevDisposeHelper dh = new HDevDisposeHelper())
     {
         hv_k2 = hv_Height / hv_height1;
     }
     if ((int)(new HTuple(hv_k1.TupleGreaterEqual(hv_k2))) != 0)
     {
         hv_k.Dispose();
         hv_k = new HTuple(hv_k2);
     }
     else
     {
         hv_k.Dispose();
         hv_k = new HTuple(hv_k1);
     }

     hv_HomMat2DIdentity.Dispose();
     HOperatorSet.HomMat2dIdentity(out hv_HomMat2DIdentity);
     hv_HomMat2DScale1.Dispose();
     HOperatorSet.HomMat2dScale(hv_HomMat2DIdentity, hv_k, hv_k, 0, 0, out hv_HomMat2DScale1);
     //hom_mat2d_scale_local (HomMat2DIdentity, k, k, HomMat2DScale)
     hv_RowTrans.Dispose(); hv_ColTrans.Dispose();
     HOperatorSet.AffineTransPixel(hv_HomMat2DScale1, hv_Row, hv_Column, out hv_RowTrans,
         out hv_ColTrans);
     using (HDevDisposeHelper dh = new HDevDisposeHelper())
     {
         hv_HomMat2DTranslate.Dispose();
         HOperatorSet.HomMat2dTranslate(hv_HomMat2DScale1, hv_cr - hv_RowTrans, hv_cc - hv_ColTrans,
             out hv_HomMat2DTranslate);
     }
     ho_ContoursAffineTrans.Dispose();
     HOperatorSet.AffineTransContourXld(contour, out ho_ContoursAffineTrans,
         hv_HomMat2DTranslate);


     HOperatorSet.ClearWindow(winHandle);
     HOperatorSet.SetColored(winHandle, 12);
     HOperatorSet.DispObj(ho_ContoursAffineTrans, winHandle);

     ho_Contours1.Dispose();
     hv_DxfStatus1.Dispose();
     hv_Row1.Dispose();
     hv_Column1.Dispose();
     hv_Width.Dispose();
     hv_Height.Dispose();
     hv_cr.Dispose();
     hv_cc.Dispose();
     hv_Area.Dispose();
     hv_Row.Dispose();
     hv_Column.Dispose();
     hv_PointOrder.Dispose();
     hv_Row11.Dispose();
     hv_Column11.Dispose();
     hv_Row2.Dispose();
     hv_Column2.Dispose();
     hv_width1.Dispose();
     hv_height1.Dispose();
     hv_k1.Dispose();
     hv_k2.Dispose();
     hv_k.Dispose();
     hv_HomMat2DIdentity.Dispose();
     hv_HomMat2DScale1.Dispose();
     hv_RowTrans.Dispose();
     hv_ColTrans.Dispose();
     hv_HomMat2DTranslate.Dispose();

 }

显示的效果如下图: 

可进行多轮廓同时显示,适应控件大小,自动调节比例,居中显示;

2. 3D点云显示使用了HWindowControlWPF控件,对比发现该控件在点云显示比上面的更流畅,操作简洁,不需要设置鼠标移动图像等属性为false;但是在给它添加右键菜单会出现一个小问题小插曲,直接添加后会发现右键无效,这个其实跟他们内部路由事件触发先后有关,感兴趣的小伙伴也可以自己去尝试下,不过博主在这里直接贴出可解决的办法,如下代码
 

 #region--------右键菜单使能-------------
 private void hostPointCloud_HMouseDown(object sender, HMouseEventArgsWPF e)
 {
     if(e.Button== MouseButton.Right)
     {
         menuPointCloud.PlacementTarget = hostPointCloud;
         menuPointCloud.Placement = PlacementMode.MousePoint;
         menuPointCloud.IsOpen = true;
     }
    
 }
  #endregion

代码直接设置ContextMenu的父级元素为HWindowControlWPF控件,  当右键菜单的菜单项被点击时,我们首先获取菜单项的父级ContextMenu,然后使用PlacementTarget属性获取父级元素,这样就不会找错或查找失败,导致无法弹出右键菜单了。

3. 3D点云可查看显示,单独开启了查看线程,方便平移旋转放大缩小等操作查看,关键代码也在下面贴出:

 /// <summary>
 /// 显示3D模型
 /// </summary>
 async private void View3D(_3DViewer viewer,
     HTuple hWindowsHandle,
     HTuple hv_ObjectModel3D)
 {
     Eum3DDisplayStatus displayStatus = viewer.Get3DDisplayStatus;
     if (displayStatus == Eum3DDisplayStatus.view)
         return;   //不许重复进入
     viewer.ExitLoop = false;
     HTuple hv_Name = new HTuple();
     //hv_Name.Dispose();
     hv_Name = new HTuple();
     hv_Name[0] = "lut";
     hv_Name[1] = "color_attrib";
     hv_Name[2] = "disp_pose";
     HTuple hv_value = new HTuple();
     //hv_value.Dispose();
     hv_value = new HTuple();
     hv_value[0] = "color1";
     hv_value[1] = "coord_z";
     hv_value[2] = "true";

     HOperatorSet.ClearWindow(hWindowsHandle);
     await Task<HObject>.Run(() =>
     {
         viewer.ViewObjectModel3d(hWindowsHandle,
             hv_ObjectModel3D, new HTuple(),
                new HTuple(), hv_Name, hv_value, "3D visualization", new HTuple(),
                new HTuple(), out HTuple hv_PoseOut);
     });
 }

4. 附件演示视频:

20241213_190310

20241213_135032

到这里我们就基本完成了图像,轮廓,点云的显示及查看了,而在实际的项目中,可能这些操作我们都需要用到,比如从3D轮廓传感器获取深度图,到点云图的生成,以及OBB,配准,滤波,坐标筛选,距离筛选,点云分割,平面拟合,单行点云提取,点云到轮廓投影,轮廓取点,坐标映射到3D等等一系列操作,都会用到这个基础的控件来进行显示和查看,是人机交互中一个重要的环节,后面的文章将会逐渐介绍点云一系列具体操作,感兴趣的小伙伴可以加入一起学习,共同进步!!!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

%Onmyway

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值