前言:
本文承接上文,主要讲解获取或者转换后的点云(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等等一系列操作,都会用到这个基础的控件来进行显示和查看,是人机交互中一个重要的环节,后面的文章将会逐渐介绍点云一系列具体操作,感兴趣的小伙伴可以加入一起学习,共同进步!!!