Halcon和C#联合编程实现硬币识别(个人入门练习)

本人为机器视觉入门小白,自己突发奇想,想将自己阶段性的学习总结一下,所以基于自己的条件(即家里没有工业相机、镜头和目标对象)做了一个硬币识别的小Demo。一共分为以下几个部分:
1.图片数据来源和分析
2.HALCON实现
3.C#搭建界面框架
4.C#中嵌入HALCON代码,即联合编程
5.导出.exe文件,运行以及Debug
目标是这样的,在图片中识别出每种类型的硬币数量,然后计算出总钱数。先上结果。C#界面和结果:
Halcon和C#联合编程实现硬币识别(个人入门练习)_第1张图片
1.图片数据来源和分析
图片大小及格式均统一,拍摄的时候已经尽可能的去除了背景噪点,所以图像处理的要求并不很高(重点在于思路)。硬币区分原理本人代码采用直径范围来判断(也可以半径或区域面积,HALCON算子可选项很多)

2.HALCON实现
首先在HALCON自带的HDevelop软件里实现图像处理部分。HALCON代码如下。HD里面采用文件夹读图以方便测试,C#里会修改成选择文件夹图片

list_files ('F:/09 Halcon练习项目/硬币识别/硬币识别APP2/picture', ['files','follow_links'], ImageFiles)
tuple_regexp_select (ImageFiles, ['\\.(tif|tiff|gif|bmp|jpg|jpeg|jp2|png|pcx|pgm|ppm|pbm|xwd|ima|hobj)$','ignore_case'], ImageFiles)
for Index := 0 to |ImageFiles| - 1 by 1
    read_image (Image, ImageFiles[Index])
    * Image Acquisition 01: Do something
    *计算图像尺寸,得到图像宽度和高度
get_image_size (Image, Width, Height)
*灰度化 
rgb1_to_gray (Image, GrayImage1)
*平滑处理
median_image (GrayImage1, ImageMedian, 'square', 1, 'mirrored')
*二进制阈值分割图像
threshold (ImageMedian, Region, 120, 255)
*填充图像中各个区域的小孔
fill_up (Region, RegionFillUp)
*区域连通
connection (RegionFillUp, ConnectedRegions)
*面积筛选
select_shape(ConnectedRegions, SelectedRegions, 'area', 'and', 13000, 36296)
*圆形度筛选
select_shape(SelectedRegions, SelectedRegions1, 'circularity', 'and', 0.75, 1)
*计算区域数量
count_obj (SelectedRegions1, Number)
*得到各个区域数据
gen_contour_region_xld (SelectedRegions1, Contours1, 'center')
fit_circle_contour_xld (Contours1, 'algebraic', -1, 0, 0, 3, 2, Row, Column, Radius, StartPhi, EndPhi, PointOrder)
i:=0
dev_display (Image) 
*设置显示颜色 
dev_set_color ('red')
*设置显示线宽
dev_set_line_width (3)
*循环,分别将所有区域画出
for J:= 0 to Number by 1
if(J

3.C#搭建界面框架
图片显示窗口采用HALCON的HWindowControl。Halcon和C#联合编程实现硬币识别(个人入门练习)_第2张图片
4.C#中嵌入HALCON代码,即联合编程
第2步中的HALCON代码导出成C#代码,即.cs文件。然后将图像算法部分作为一个函数放置在C#代码中。
对于我这种入门菜鸟来说,这里有两点需要学习和练习。一个是窗口适应图片大小,二个是C#选择文件夹里的文件
(1)窗口适应图片,论坛里借鉴别的大神的代码,加载到HALCON显示图片的那行代码的前面。

HOperatorSet.GetImageSize(ho_Image, out hv_Width, out hv_Height);
HOperatorSet.SetPart(hv_ExpDefaultWinHandle, 0, 0, hv_Height - 1, hv_Width - 1);

这里我的HWindowControl窗口长宽比和图片像素长宽比是一样的(数值可以不一样,比例要一样)这样图片不会因为适应窗口出现变形,只会同比例缩小放大。
(2)C#选择文件,即点击界面上的“加载图片”按钮,出现文件夹目录。同样借鉴的大神代码

private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog1 = new OpenFileDialog();
            openFileDialog1.Filter = "PNG文件|*.png*|JPEG文件|*.jpg*|BMP文件|*.bmp*";
            openFileDialog1.RestoreDirectory = true;
            openFileDialog1.FilterIndex = 1;
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {  ImagePath = openFileDialog1.FileName;
             HD.ReadPicture(hWindowControl1.HalconWindow, ImagePath);
            }
        }

button1_Click即是“打开图片”按钮。
关于Halcon算子部分的代码,我是单独建的一个函数,然后在Form1里面调用该函数,以实现图像处理和显示。
代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HalconDotNet;

public partial class HDevelopExport
{
    public void InitHalcon()
    {
        // Default settings used in HDevelop 
        HOperatorSet.SetSystem("do_low_error", "false");
    }

    public HTuple hv_ExpDefaultWinHandle;

    HObject ho_Image = null, ho_GrayImage1 = null;
    HObject ho_ImageMedian = null, ho_Region = null, ho_RegionFillUp = null;
    HObject ho_ConnectedRegions = null, ho_SelectedRegions = null;
    HObject ho_SelectedRegions1 = null, ho_Contours = null, ho_Contours2 = null;
    HTuple hv_ImageFiles = null, hv_Index = null;
    HTuple hv_Width = new HTuple(), hv_Height = new HTuple();

    HTuple hv_Number = new HTuple(), hv_Row = new HTuple();
    HTuple hv_Column = new HTuple(), hv_Radius = new HTuple();
    HTuple hv_StartPhi = new HTuple(), hv_EndPhi = new HTuple();
    HTuple hv_PointOrder = new HTuple(), hv_i = new HTuple();
    HTuple hv_J = new HTuple();

    public void ReadPicture(HTuple Window, string ImagePath)
    {
        //读图并显示
        hv_ExpDefaultWinHandle = Window;
        HOperatorSet.GenEmptyObj(out ho_Image);
        ho_Image.Dispose();

        HOperatorSet.ReadImage(out ho_Image, ImagePath);
        HOperatorSet.GetImageSize(ho_Image, out hv_Width, out hv_Height);
        HOperatorSet.SetPart(hv_ExpDefaultWinHandle, 0, 0, hv_Height - 1, hv_Width - 1);
        HOperatorSet.DispObj(ho_Image, hv_ExpDefaultWinHandle);
    }

    public void Process(HTuple Window,out int a, out int b ,out int c)
    {
        hv_ExpDefaultWinHandle = Window;

        HOperatorSet.GenEmptyObj(out ho_GrayImage1);
        HOperatorSet.GenEmptyObj(out ho_ImageMedian);
        HOperatorSet.GenEmptyObj(out ho_Region);
        HOperatorSet.GenEmptyObj(out ho_RegionFillUp);
        HOperatorSet.GenEmptyObj(out ho_ConnectedRegions);
        HOperatorSet.GenEmptyObj(out ho_SelectedRegions);
        HOperatorSet.GenEmptyObj(out ho_SelectedRegions1);
        HOperatorSet.GenEmptyObj(out ho_Contours);
        HOperatorSet.GenEmptyObj(out ho_Contours2);

        //灰度化
        ho_GrayImage1.Dispose();
        HOperatorSet.Rgb1ToGray(ho_Image, out ho_GrayImage1);
        ho_ImageMedian.Dispose();
        HOperatorSet.MedianImage(ho_GrayImage1, out ho_ImageMedian, "square", 1, "mirrored");
        //二进制阈值分割图像,最大限度的可分析,提取‘黑’或‘白’,自动阈值使用的阈值值UsedThreshold
        ho_Region.Dispose();
        HOperatorSet.Threshold(ho_ImageMedian, out ho_Region, 120, 255);
        //填充图像中各个区域的小孔,填充后区域个数不变
        ho_RegionFillUp.Dispose();
        HOperatorSet.FillUp(ho_Region, out ho_RegionFillUp);
        //区域连通
        ho_ConnectedRegions.Dispose();
        HOperatorSet.Connection(ho_RegionFillUp, out ho_ConnectedRegions);
        ho_SelectedRegions.Dispose();
        HOperatorSet.SelectShape(ho_ConnectedRegions, out ho_SelectedRegions, "area",
            "and", 13000, 36296);
        ho_SelectedRegions1.Dispose();
        HOperatorSet.SelectShape(ho_SelectedRegions, out ho_SelectedRegions1, "circularity",
            "and", 0.75, 1);
        HOperatorSet.CountObj(ho_SelectedRegions1, out hv_Number);
        ho_Contours.Dispose();
        HOperatorSet.GenContourRegionXld(ho_SelectedRegions1, out ho_Contours, "center");
        HOperatorSet.FitCircleContourXld(ho_Contours, "algebraic", -1, 0, 0, 3, 2,
            out hv_Row, out hv_Column, out hv_Radius, out hv_StartPhi, out hv_EndPhi,
            out hv_PointOrder);
        hv_i = 0;
        HOperatorSet.GetImageSize(ho_Image, out hv_Width, out hv_Height);
        HOperatorSet.SetPart(hv_ExpDefaultWinHandle, 0, 0, hv_Height - 1, hv_Width - 1);
        HOperatorSet.DispObj(ho_GrayImage1, hv_ExpDefaultWinHandle);
        HOperatorSet.SetColor(hv_ExpDefaultWinHandle, "red");
        HOperatorSet.SetLineWidth(hv_ExpDefaultWinHandle, 2);
        HTuple end_val26 = hv_Number;
        HTuple step_val26 = 1;
        for (hv_J = 0; hv_J.Continue(end_val26, step_val26); hv_J = hv_J.TupleAdd(step_val26))
        {
            if ((int)(new HTuple(hv_J.TupleLess(hv_Number))) != 0)
            {
                ho_Contours2.Dispose();
                HOperatorSet.GenCircleContourXld(out ho_Contours2, hv_Row.TupleSelect(hv_i),
                    hv_Column.TupleSelect(hv_i), hv_Radius.TupleSelect(hv_i), 0, 6.28318,
                    "positive", 1);
                hv_i = hv_i + 1;
                HOperatorSet.DispObj(ho_Contours2, hv_ExpDefaultWinHandle);
            }
        }

        HOperatorSet.DispObj(ho_Contours2, hv_ExpDefaultWinHandle);

        ho_Image.Dispose();
        ho_GrayImage1.Dispose();
        ho_ImageMedian.Dispose();
        ho_Region.Dispose();
        ho_RegionFillUp.Dispose();
        ho_ConnectedRegions.Dispose();
        ho_SelectedRegions.Dispose();
        ho_SelectedRegions1.Dispose();
        ho_Contours.Dispose();
        ho_Contours2.Dispose();

        a = 0;
        b = 0;
        c = 0;
        for (int i = 0; i < hv_Number; i++)
        {
            if (hv_Radius[i] > 91)
            {
                a++;    //一元
            }
            else if (hv_Radius[i] < 74)
            {

                c++;    //一角
            }
            else
            {

                b++;    //五角
            }
        }
    }
}

关于这个代码有以下几点:
(1)ReadPicture函数是读图并显示;
(2)Process函数即图像处理部分;
(3)一般的,一个函数只能返回一个值,这里我用的Out以返回多个值(即1元,5角和1角三种硬币);
(4)通过三种硬币的直径的范围来判断和分类。

5.导出.exe文件,运行以及Debug
我想把这个小程序做成绿色版,即不需要安装,可以U盘直接即插即用的,但是直接将VS文件夹里的bin\Debug里面的.exe文件导出会出现自己电脑可以实现,别的电脑上用不了的情况。经万能的CSDN搜索,是需要将halcon.dll等文件复制到程序文件夹中。.exe文件可以创建快捷方式和改名。
Halcon和C#联合编程实现硬币识别(个人入门练习)_第3张图片
初次发自己的经历,多多指教。

你可能感兴趣的:(项目示例)