基于halcon的直线查找之卡尺

我这人比较懒得罗里吧嗦,基本都是直接说两点:why?how?

基于halcon的直线查找之卡尺

直线的检测在halcon中主要有两种方式:直线拟合和卡尺直线。这里分享卡尺方法,拟合请移步:直线拟合

直线卡尺

首先把整体思路列出来:
CreateMetrologyModel创建一个测量句柄;
AddMetrologyObjectLineMeasure添加一个线“Line”的模型;
SetMetrologyObjectParam设置相关测量参数。比如:卡尺垂直于边的长度,卡尺的宽度,卡尺的个数,保留的卡尺个数,边缘最低对比度等;
ApplyMetrologyModel开始找边;
GetMetrologyObjectMeasuresGetMetrologyObjectResultContour将找到的边界结果显示,同时获得每个卡尺找到的点坐标。

是不是很简单?没错,就这么简单无脑。作为调包侠,是还是觉得用的算子有点多啊…感觉给别人用这么多函数有点low?那就自己再封一层壳吧,瞬间高大上了。

CTRL + CV时刻

直接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;
}

注意

我还有更简洁的版本

### 使用 Halcon 实现卡尺工具进行缘检测或尺寸测量 #### 创建并配置测量模型 为了使用 Halcon卡尺功能,首先需要创建一个测量模型。此操作可以通过 `create_metrology_model` 函数完成。 ```cpp // 定义测量区域参数 double Row, Column; Row = 200; // 测量区中心行坐标 Column = 300; // 测量区中心列坐标 double Phi = 0; // 测量区旋转角度 (弧度制) double Width = 100; // 测量区宽度 double Height = 80; // 测量区高度 // 设置卡尺参数 int NumCalipers = 5; // 卡尺数量 double Distance = 20; // 各个卡尺间的间距(像素) // 创建测量模型 Hlong ModelID; create_metrology_model(Row, Column, Phi, Width, Height, 'caliper', NumCalipers, Distance, &ModelID); ``` 上述代码片段定义了一个位于图像特定位置的测量窗口,并设置了该区域内使用的卡尺数目及其间隔距离[^1]。 #### 添加特征到测量模型 接着向已建立好的测量模型中加入具体的测量任务,比如直线缘检测: ```cpp add_metrology_caliper(ModelID, "edge", 90, 7, 1, 1); // 参数解释:"edge"表示执行缘检测;90为方向角; // 7是用于寻界的扫描线数;最后两个1分别指定了最小和最大灰度变化阈值。 ``` 这段代码说明了如何指定要查找的对象界的方向以及敏感程度等细节设置[^3]。 #### 执行测量过程 当一切准备就绪之后,则可以调用相应函数来实际运行这些设定,在输入图象上实施测量动作: ```cpp execute_metrology_object(Image, ModelID); get_metrology_contour(metrologyHandle, Contours); dev_display(Contours); // 显示轮廓结果以便验证准确性 ``` 这里展示了怎样启动整个流程并对所得数据做初步可视化处理的方法[^2]。 #### 结果获取与分析 最终可以从生成的结果集中提取所需的信息,例如计算两平行线间距离或是求取某些形状的角度特性等等。 ```cpp get_metrology_result_caliper(ModelID, ResultIDs, Positions, Angles, Distances); for(int i=0;i<Distances.Length();i++) { cout << "Distance between edges at position ["<<Positions[i]<<"] is "<<Distances[i]<<" pixels."<<endl; } ``` 以上部分介绍了读取由卡尺测得的具体数值的方式,包括但不限于各处的距离信息等。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值