液体定容-相机视觉辅助
应公司要求,完成上图容器自动定容的视觉部分,要求精度0.2ml。视觉处理用的halcon导出到c#,封装为接口,根据外部触发返回速度值0/1/2,即停止/慢速/快速定容。
设计思路
1,查找刻度线模板:
ho_Image.Dispose();
HOperatorSet.GrabImageAsync(out ho_Image, hv_CameraHandle, -1);
HImage hImage = new HImage();
HObject regin1 = new HObject();
HobjectToHimage(ho_Image, ref hImage);
mView.AddIconicVar(hImage);
//只在首次检测时查找刻度线模板和创建直线查找模型
if (times == 0)
{
HOperatorSet.SetSystem(“border_shape_models”, “false”);
//break;
ho_ROI_0.Dispose();
HOperatorSet.GenRectangle1(out ho_ROI_0, 800, 185.929, 1200, 688.786);
ho_TemplateImage.Dispose();
HOperatorSet.ReduceDomain(ho_Image, ho_ROI_0, out ho_TemplateImage);
ho_ModelContours.Dispose();
HOperatorSet.GetShapeModelContours(out ho_ModelContours, hv_ModelID, 1);
//Matching 01: Find the model using (HDevDisposeHelper dh = new HDevDisposeHelper()) { hv_Row.Dispose(); hv_Column.Dispose(); hv_Angle.Dispose(); hv_Score.Dispose(); HOperatorSet.FindShapeModel(ho_TemplateImage, hv_ModelID, (new HTuple(-3)).TupleRad(), (new HTuple(6)).TupleRad(), 0.7, 1, 0, "interpolation", (new HTuple(4)).TupleConcat(5), 0.9, out hv_Row, out hv_Column, out hv_Angle, out hv_Score); } //show XLD for (hv_I = 0; (int)hv_I <= (int)((new HTuple(hv_Score.TupleLength())) - 1); hv_I = (int)hv_I + 1) { hv_HomMat2D.Dispose(); HOperatorSet.HomMat2dIdentity(out hv_HomMat2D); using (HDevDisposeHelper dh = new HDevDisposeHelper()) { HTuple ExpTmpOutVar_0; HOperatorSet.HomMat2dRotate(hv_HomMat2D, hv_Angle.TupleSelect(hv_I), 0, 0, out ExpTmpOutVar_0); hv_HomMat2D.Dispose(); hv_HomMat2D = ExpTmpOutVar_0; } using (HDevDisposeHelper dh = new HDevDisposeHelper()) { HTuple ExpTmpOutVar_0; HOperatorSet.HomMat2dTranslate(hv_HomMat2D, hv_Row.TupleSelect(hv_I), hv_Column.TupleSelect( hv_I), out ExpTmpOutVar_0); hv_HomMat2D.Dispose(); hv_HomMat2D = ExpTmpOutVar_0; } ho_TransContours.Dispose(); HOperatorSet.AffineTransContourXld(ho_ModelContours, out ho_TransContours, hv_HomMat2D); HOperatorSet.GenEmptyObj(out regin1); HOperatorSet.GenRegionContourXld(ho_TransContours, out regin1, "filled"); if (regin1 != null && regin1.IsInitialized()) { mView.ChangeGraphicSettings(Mode.COLOR, "green"); mView.AddIconicVar(regin1); mView.Repaint(); } } hv_line.Dispose(); if (hv_Score.TupleLength() >= 1 && (double)hv_Score[0] > 0.95) { using (HDevDisposeHelper dh = new HDevDisposeHelper()) { hv_line = new HTuple(); hv_line = hv_line.TupleConcat(hv_Row[0] + 420); hv_line = hv_line.TupleConcat(366.299); hv_line = hv_line.TupleConcat(hv_Row[0] + 420); hv_line = hv_line.TupleConcat(582.921); } } else { mView.AddText("首次运行未查找到定容线模板", 100, 100, 65, "red"); mView.Repaint(); continue; } hv_MetrologyHandle.Dispose(); hv_Index1.Dispose(); HOperatorSet.CreateMetrologyModel(out hv_MetrologyHandle); HOperatorSet.AddMetrologyObjectGeneric(hv_MetrologyHandle, "line", hv_line, 410, 5, 1, 50, new HTuple(), new HTuple(), out hv_Index1); HOperatorSet.SetMetrologyObjectParam(hv_MetrologyHandle, "all", "measure_transition", "uniform"); HOperatorSet.SetMetrologyObjectParam(hv_MetrologyHandle, "all", "num_measures", 20); HOperatorSet.SetMetrologyObjectParam(hv_MetrologyHandle, "all", "num_instances", 1); HOperatorSet.SetMetrologyObjectParam(hv_MetrologyHandle, "all", "measure_sigma", 5); HOperatorSet.SetMetrologyObjectParam(hv_MetrologyHandle, "all", "measure_interpolation", "bicubic"); HOperatorSet.SetMetrologyObjectParam(hv_MetrologyHandle, "all", "measure_select", "last"); HOperatorSet.SetMetrologyObjectParam(hv_MetrologyHandle, "all", "min_score", 0.5);
}
2,查找液面位置:
HOperatorSet.ApplyMetrologyModel(ho_Image, hv_MetrologyHandle);
ho_Contours.Dispose(); hv_Row1.Dispose(); hv_Column1.Dispose();
HOperatorSet.GetMetrologyObjectMeasures(out ho_Contours, hv_MetrologyHandle, 0, “all”, out hv_Row1, out hv_Column1);
HOperatorSet.GetMetrologyObjectResult(hv_MetrologyHandle, 0, “all”, “result_type”, “score”, out HTuple line_score);
mView.ChangeGraphicSettings(Mode.COLOR, “green”);
mView.AddIconicVar(regin1);
3,判断页面位置与模板位置的距离,保存图片
//判断是否找到液位线
if (line_score.Length < 1)
{
line_score.Dispose();
idd++;
if (idd > 4)
{
stop = true;
times++;
mView.AddText(“定容结束”, 100, 100, 100, “red”);
mView.Repaint();
SaveImage(“D:图片处理”, $“D:图片处理{dayNow}{timeNow}{times}”, hImage, “bmp”);
hImage = mView.DumpWindows();
//保存处理后的截图
SaveImage(“D:图片处理”, KaTeX parse error: Expected 'EOF', got '}' at position 133: … break; }? else …“定容结束{idd}次判断”, 100, 100, 100, “red”);
mView.Repaint();
Thread.Sleep(200);
}
}
else
{
idd = 0;
ho_Cross.Dispose();
HOperatorSet.GenCrossContourXld(out ho_Cross, hv_Row1, hv_Column1, 20, 0.785398);
hv_Parameter.Dispose();
HOperatorSet.GetMetrologyObjectResult(hv_MetrologyHandle, 0, 0, “result_type”, “all_param”, out hv_Parameter);
ho_Contour.Dispose();
HOperatorSet.GetMetrologyObjectResultContour(out ho_Contour, hv_MetrologyHandle, 0, 0, 1.5);
mView.AddIconicVar(ho_Contour);
mView.Repaint();
hv_diff.Dispose();
using (HDevDisposeHelper dh = new HDevDisposeHelper()) { hv_diff = (hv_Parameter.TupleSelect(0) + hv_Parameter.TupleSelect(2)) / 2 - hv_Row; } if ((hv_Parameter.TupleSelect(0) + hv_Parameter.TupleSelect(2) - 2 * hv_Row) > 500) { mView.AddText("快速定容", 100, 100, 100, "green"); } else { mView.AddText("慢速定容", 100, 100, 100, "yellow"); } mView.Repaint();
}
//是否存图判断,30次存一张约为7秒一图
if (times % 20 == 0)
{
//保存原图
SaveImage(“D:图片处理”, $“D:图片处理{dayNow}{timeNow}{times}”, hImage, “bmp”);
hImage = mView.DumpWindows();
//保存处理后的截图
SaveImage(“D:图片处理”, $“D:图片处理{dayNow}{timeNow}{times}”, hImage, “png”);
hImage.Dispose();
}
times++;
最终效果图