我这人比较懒得罗里吧嗦,基本都是直接说两点:why?how?
直线的检测在halcon中主要有两种方式:直线拟合和卡尺直线。这里分享卡尺方法,拟合请移步:直线拟合
首先把整体思路列出来:
①CreateMetrologyModel创建一个测量句柄;
②AddMetrologyObjectLineMeasure添加一个线“Line”的模型;
③SetMetrologyObjectParam设置相关测量参数。比如:卡尺垂直于边的长度,卡尺的宽度,卡尺的个数,保留的卡尺个数,边缘最低对比度等;
④ApplyMetrologyModel开始找边;
⑤GetMetrologyObjectMeasures、GetMetrologyObjectResultContour将找到的边界结果显示,同时获得每个卡尺找到的点坐标。
是不是很简单?没错,就这么简单无脑。作为调包侠,是还是觉得用的算子有点多啊…感觉给别人用这么多函数有点low?那就自己再封一层壳吧,瞬间高大上了。
直接copy拿去用吧
// 代码量有点冗杂,很多参数判断。起始作为底层供调用的方法,无需写这些判断,推荐判断安全性的问题应该交给上层调用者来保证,底层保证效率就行,避免上层判断,底层又判断,造成重复耗时。可以删掉,从CreateMetrologyModel开始。
void FindLineCaliper(HObject ho_Image, HObject* ho_ResultContour, HObject* ho_MeasureContour,
HTuple hv_Row1, HTuple hv_Column1, HTuple hv_Row2, HTuple hv_Column2, HTuple hv_MeasureLength,
HTuple hv_MeasureWidth, HTuple hv_MeasureSigma, HTuple hv_MeasureThreshold, HTuple hv_NumInstances,
HTuple hv_MeasureSelect, HTuple hv_MeasureTransition, HTuple hv_NumMeasures,
HTuple* hv_LineFind, HTuple* hv_RowStart, HTuple* hv_ColumnStart, HTuple* hv_RowEnd,
HTuple* hv_ColumnEnd, HTuple* hv_RowFrame, HTuple* hv_ColumnFrame)
{
HObject ho_MeasuredLines;
HTuple hv_num_instances, hv_measure_select, hv_measure_transition;
HTuple hv_num_measures, hv_ValueName, hv_Value, hv_Channels;
HTuple hv_Width, hv_Height, hv_IsNumber, hv_Index, hv_Index1;
HTuple hv_IsString, hv_NumMatches1, hv_NumMatches2, hv_IsString1;
HTuple hv_NumMatches3, hv_NumMatches4, hv_MetrologyHandle;
HTuple hv_IndexLine, hv_RowMeasure, hv_ColumnMeasure, hv_IndexSearch;
HTuple hv_Frame_Parameter, hv_Index2, hv_NumMatches5, hv_NumMatches6;;
//*********找直线函数**********
//****输入参数:Image:输入图像, 需要单通道图像*****
//****输入参数:Row1,Column1,Row2,Column2:找直线的参考基准线, 其位置与需要找的直线位置相同或近似平行*****
//****输入参数:MeasureLength,MeasureWidth,MeasureSigma,MeasureThreshold:卡尺的长宽, Sigma值和阈值,支持数组输入*****
//****输入参数:ParameterName,ParameterValue:检测控制参数名称和值, 具体请见下表*****
//****输入参数: NumInstances: 检测的直线数量,必须为正整数***
//****输入参数: NumMeasures: 检测的卡尺数量*****
//****输入参数:MeasureTransition:找线的方式, 由暗到明, 由明到暗,只能是'positive'或'negative'*****
//****输入参数:MeasureSelect: 每个卡尺内找到的点是最近点还是最远点, 只能是'first'或'last'*****
//****输出参数:LineFind: 0:无法找到直线;1:可以找到直线*****
//****输出参数:RowStart,ColumnStart,RowEnd,ColumnEnd:找到的直线起点与终点的坐标*****
//****输出参数:ResultContour:生成的直线图形*****
//****输出参数:MeasureContour:生成的卡尺图形*****
//****输出参数:RowFrame,ColumnFrame:每个卡尺找到的点的坐标*****
//****函数作者:*****
//****更新时间:*****
//******************
//****默认参数值*****
//******************
(*hv_LineFind) = 0;
(*hv_RowStart) = 0;
(*hv_ColumnStart) = 0;
(*hv_RowEnd) = 0;
(*hv_ColumnEnd) = 0;
(*hv_RowFrame) = HTuple();
(*hv_ColumnFrame) = HTuple();
hv_num_instances = 1;
hv_measure_select = "last";
hv_measure_transition = "positive";
hv_num_measures = 40;
hv_ValueName.Clear();
hv_ValueName[0] = "num_instances";
hv_ValueName[1] = "num_measures";
hv_ValueName[2] = "measure_transition";
hv_ValueName[3] = "measure_select";
//****参数类型和范围确认*****
//****int/real 型:['int',min,max], min=<输入值<=max,上下限若是无穷大,则写作'' *****
//****string 型:['string','a','b',...,'z'],输入值需是'a','a','b',...,'z'中的任意一个值*****
hv_Value.Clear();
hv_Value[0] = "int";
hv_Value[1] = 1;
hv_Value[2] = "";
hv_Value[3] = "int";
hv_Value[4] = 1;
hv_Value[5] = "";
hv_Value[6] = "string";
hv_Value[7] = "positive";
hv_Value[8] = "negative";
hv_Value[9] = "string";
hv_Value[10] = "first";
hv_Value[11] = "last";
//*************************************
//****1.判断输入参数是否满足计算要求*****
//*************************************
//****a.判断输入图像是否为单通道图像*****
CountChannels(ho_Image, &hv_Channels);
if (0 != (hv_Channels != 1))
{
Rgb1ToGray(ho_Image, &ho_Image);
}
GetImageSize(ho_Image, &hv_Width, &hv_Height);
//****b.判断输入的直线参数是否为number,若出现非数字格式的输入情况,则直接返回*****
TupleIsNumber((((((((hv_Row1.TupleConcat(hv_Column1)).TupleConcat(hv_Row2)).TupleConcat(hv_Column2)).TupleConcat(hv_MeasureLength)).TupleConcat(hv_MeasureWidth)).TupleConcat(hv_MeasureThreshold)).TupleConcat(hv_NumInstances)).TupleConcat(hv_NumMeasures),
&hv_IsNumber);
{
HTuple end_val50 = (hv_IsNumber.TupleLength()) - 1;
HTuple step_val50 = 1;
for (hv_Index = 0; hv_Index.Continue(end_val50, step_val50); hv_Index += step_val50)
{
if (0 != (HTuple(hv_IsNumber[hv_Index]) == 0))
{
return;
}
}
}
//****判断输入参数名称和数值的类型是否正确*****
//****MeasureLeght≥1*****
{
HTuple end_val58 = (hv_MeasureLength.TupleLength()) - 1;
HTuple step_val58 = 1;
for (hv_Index1 = 0; hv_Index1.Continue(end_val58, step_val58); hv_Index1 += step_val58)
{
if (0 != (HTuple(hv_MeasureLength[hv_Index1]) < 1))
{
hv_MeasureLength[hv_Index1] = 1;
}
}
}
//****MeasureWidth≥1*****
{
HTuple end_val65 = (hv_MeasureWidth.TupleLength()) - 1;
HTuple step_val65 = 1;
for (hv_Index1 = 0; hv_Index1.Continue(end_val65, step_val65); hv_Index1 += step_val65)
{
if (0 != (HTuple(hv_MeasureWidth[hv_Index1]) < 1))
{
hv_MeasureWidth[hv_Index1] = 1;
}
}
}
//****MeasureSigma≥0.4*****
{
HTuple end_val72 = (hv_MeasureSigma.TupleLength()) - 1;
HTuple step_val72 = 1;
for (hv_Index1 = 0; hv_Index1.Continue(end_val72, step_val72); hv_Index1 += step_val72)
{
if (0 != (HTuple(hv_MeasureSigma[hv_Index1]) < 0.4))
{
hv_MeasureSigma[hv_Index1] = 0.4;
}
}
}
//****MeasureThreshold≥1, MeasureThreshold≤255*****
{
HTuple end_val79 = (hv_MeasureThreshold.TupleLength()) - 1;
HTuple step_val79 = 1;
for (hv_Index1 = 0; hv_Index1.Continue(end_val79, step_val79); hv_Index1 += step_val79)
{
if (0 != (HTuple(hv_MeasureThreshold[hv_Index1]) < 1))
{
hv_MeasureThreshold[hv_Index1] = 1;
}
if (0 != (HTuple(hv_MeasureThreshold[hv_Index1]) > 255))
{
hv_MeasureThreshold[hv_Index1] = 255;
}
}
}
//****NumInstances ≥1*****
TupleInt(hv_NumInstances, &hv_num_instances);
if (0 != (hv_num_instances < 1))
{
hv_num_instances = 1;
}
//****NumMeasures ≥1******
TupleInt(hv_NumMeasures, &hv_num_measures);
if (0 != (hv_num_measures < 1))
{
hv_num_measures = 1;
}
//****MeasureTransion: 'positive' or 'negative'*****
TupleIsString(hv_MeasureTransition, &hv_IsString);
if (0 != hv_IsString)
{
TupleRegexpTest(hv_MeasureTransition, (HTuple("positive").Append("ignore_case")), &hv_NumMatches1);
if (0 != (hv_NumMatches1 == 1))
{
hv_measure_transition = "positive";
}
TupleRegexpTest(hv_MeasureTransition, (HTuple("negative").Append("ignore_case")), &hv_NumMatches2);
if (0 != (hv_NumMatches2 == 1))
{
hv_measure_transition = "negative";
}
TupleRegexpTest(hv_MeasureTransition, (HTuple("all").Append("ignore_case")), &hv_NumMatches3);
if (0 != (hv_NumMatches3 == 1))
{
hv_measure_transition = "all";
}
}
//****MeasureSelect: 'first' or 'last'*****
TupleIsString(hv_MeasureSelect, &hv_IsString1);
if (0 != hv_IsString1)
{
TupleRegexpTest(hv_MeasureSelect, (HTuple("first").Append("ignore_case")), &hv_NumMatches4);
if (0 != (hv_NumMatches4 == 1))
{
hv_measure_select = "first";
}
TupleRegexpTest(hv_MeasureSelect, (HTuple("last").Append("ignore_case")), &hv_NumMatches5);
if (0 != (hv_NumMatches5 == 1))
{
hv_measure_select = "last";
}
TupleRegexpTest(hv_MeasureSelect, (HTuple("all").Append("ignore_case")), &hv_NumMatches6);
if (0 != (hv_NumMatches6 == 1))
{
hv_measure_select = "all";
}
}
//********************
//****2.找直线函数*****
//********************
CreateMetrologyModel(&hv_MetrologyHandle);
//****添加多个直线寻找---3/7*****
//for Index3 := 0 to |MeasureLength|-1 by 1
//for Index4 := 0 to |MeasureWidth|-1 by 1
//for Index5 := 0 to |MeasureSigma|-1 by 1
//for Index6 := 0 to |MeasureThreshold|-1 by 1
//set_metrology_model_image_size (MetrologyHandle, Width, Height)
//add_metrology_object_line_measure (MetrologyHandle, Row1, Column1, Row2, Column2, MeasureLength[Index3], MeasureWidth[Index4], MeasureSigma[Index5], MeasureThreshold[Index6], [], [], IndexLine)
//endfor
//endfor
//endfor
//endfor
//add_metrology_object_line_measure (MetrologyHandle, Row1, Column1, Row2, Column2, MeasureLength, MeasureWidth, MeasureSigma, MeasureThreshold, [], [], Index)
SetMetrologyModelImageSize(hv_MetrologyHandle, hv_Width, hv_Height);
AddMetrologyObjectLineMeasure(hv_MetrologyHandle, hv_Row1, hv_Column1, hv_Row2,
hv_Column2, hv_MeasureLength, hv_MeasureWidth, hv_MeasureSigma, hv_MeasureThreshold,
HTuple(), HTuple(), &hv_IndexLine);
SetMetrologyObjectParam(hv_MetrologyHandle, "all", "num_instances", hv_num_instances);
SetMetrologyObjectParam(hv_MetrologyHandle, "all", "measure_select", hv_measure_select);
SetMetrologyObjectParam(hv_MetrologyHandle, "all", "measure_transition", hv_measure_transition);
SetMetrologyObjectParam(hv_MetrologyHandle, "all", "num_measures", hv_num_measures);
ApplyMetrologyModel(ho_Image, hv_MetrologyHandle);
if (0 != (hv_IndexLine > 0))
{
GetMetrologyObjectMeasures(&(*ho_MeasureContour), hv_MetrologyHandle, "all", "all", &hv_RowMeasure, &hv_ColumnMeasure);
GetMetrologyObjectResultContour(&ho_MeasuredLines, hv_MetrologyHandle, "all", "all", 1.5);
(*hv_LineFind) = 1;
}
else
{
(*hv_LineFind) = 0;
}
//**********************
//****3.获取直线参数*****
//**********************
{
HTuple end_val163 = hv_IndexLine;
HTuple step_val163 = 1;
for (hv_IndexSearch = 0; hv_IndexSearch.Continue(end_val163, step_val163); hv_IndexSearch += step_val163)
{
GetMetrologyObjectResult(hv_MetrologyHandle, hv_IndexSearch, "all", "result_type", "all_param", &hv_Frame_Parameter);
if (0 != ((hv_Frame_Parameter.TupleLength()) == (hv_num_instances * 4)))
{
(*hv_LineFind) = 1;
{
HTuple end_val167 = ((hv_Frame_Parameter.TupleLength()) / 4) - 1;
HTuple step_val167 = 1;
for (hv_Index2 = 0; hv_Index2.Continue(end_val167, step_val167); hv_Index2 += step_val167)
{
(*hv_RowStart)[hv_Index2] = HTuple(hv_Frame_Parameter[hv_Index2 * hv_num_instances]);
(*hv_ColumnStart)[hv_Index2] = HTuple(hv_Frame_Parameter[(hv_Index2 * hv_num_instances) + 1]);
(*hv_RowEnd)[hv_Index2] = HTuple(hv_Frame_Parameter[(hv_Index2 * hv_num_instances) + 2]);
(*hv_ColumnEnd)[hv_Index2] = HTuple(hv_Frame_Parameter[(hv_Index2 * hv_num_instances) + 3]);
}
}
}
if (0 != (HTuple((*hv_LineFind) == 1).TupleOr(hv_IndexSearch == hv_IndexLine)))
{
GetMetrologyObjectResultContour(&(*ho_ResultContour), hv_MetrologyHandle, hv_IndexSearch, "all", 1.5);
GetMetrologyObjectMeasures(&(*ho_MeasureContour), hv_MetrologyHandle, hv_IndexSearch, "all", &(*hv_RowFrame), &(*hv_ColumnFrame));
ClearMetrologyModel(hv_MetrologyHandle);
break;
}
}
}
return;
}
我还有更简洁的版本