Barcode.cs 14.3 KB
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using HalconDotNet;
using CameraVisionLib.Model;

namespace Asa.Barcode
{
    /// <summary>
    /// 条码识别,1DBarcode、2DBarcode
    /// </summary>
    internal class Identify
    {
        private ScanParam scanParam;

        /// <summary>
        /// 条码识别,1DBarcode、2DBarcode
        /// </summary>
        /// <param name="param"></param>
        public Identify(ScanParam param)
        {
            scanParam = param;
        }

        /// <summary>
        /// 获取条码
        /// </summary>
        /// <param name="image"></param>
        /// <returns></returns>
        public List<BarcodeInfo> GetCode(Bitmap image)
        {
            List<BarcodeInfo> code = new();
            if (image == null) return code;

            switch (scanParam.CodeOrder)
            {
                case ScanCodeOrder.HalconOnly:
                    code.AddRange(HalconGetCode(image));
                    break;
                case ScanCodeOrder.EyemLibOnly:
                    code.AddRange(EyemGetCode(image));
                    break;
                case ScanCodeOrder.HalconEyemLib:
                    code.AddRange(HalconGetCode(image));
                    if (code.Count == 0) code.AddRange(EyemGetCode(image));
                    break;
                case ScanCodeOrder.EyemLibHalcon:
                    code.AddRange(EyemGetCode(image));
                    if (code.Count == 0) code.AddRange(HalconGetCode(image));
                    break;
            }

            return code;
        }







        private List<BarcodeInfo> HalconGetCode(Bitmap image)
        {
            List<BarcodeInfo> code = new();
            Bitmap bmp = new(image);  //防止原图被释放

            switch (scanParam.HalconType)
            {
                case CodeType.All:
                    code.AddRange(HalconExtract1DCode(bmp, scanParam.Zoom1D));
                    code.AddRange(HalconExtract2DCode(bmp, scanParam.Zoom2D));
                    break;
                case CodeType.Barcode1D:
                    code.AddRange(HalconExtract1DCode(bmp, scanParam.Zoom1D));
                    break;
                case CodeType.Barcode2D:
                    code.AddRange(HalconExtract2DCode(bmp, scanParam.Zoom2D));
                    break;
            }

            return code;
        }

        private List<BarcodeInfo> EyemGetCode(Bitmap image)
        {
            List<BarcodeInfo> code = new();

            string tempPath = AppDomain.CurrentDomain.BaseDirectory + string.Format("Img{0:yyyy_MM_dd HH_mm_ss}.png", DateTime.Now);
            image.Save(tempPath, ImageFormat.Png);

            switch (scanParam.EyemType)
            {
                case CodeType.All:
                    code.AddRange(EyemExtractCode(tempPath, "CODE_128|CODE_39|CODE_93", scanParam.EyemBlockSize, scanParam.EyemRangeC, scanParam.EyemSymbolMin, scanParam.EyemSymbolMax));
                    code.AddRange(EyemExtractCode(tempPath, "QR_CODE|DATA_MATRIX", scanParam.EyemBlockSize, scanParam.EyemRangeC, scanParam.EyemSymbolMin, scanParam.EyemSymbolMax));
                    break;
                case CodeType.Barcode1D:
                    code.AddRange(EyemExtractCode(tempPath, "CODE_128|CODE_39|CODE_93", scanParam.EyemBlockSize, scanParam.EyemRangeC, scanParam.EyemSymbolMin, scanParam.EyemSymbolMax));
                    break;
                case CodeType.Barcode2D:
                    code.AddRange(EyemExtractCode(tempPath, "QR_CODE|DATA_MATRIX", scanParam.EyemBlockSize, scanParam.EyemRangeC, scanParam.EyemSymbolMin, scanParam.EyemSymbolMax));
                    break;
            }

            System.IO.File.Delete(tempPath);
            return code;
        }

        private List<BarcodeInfo> HalconExtract1DCode(Bitmap bmp, float zoom)
        {
            List<BarcodeInfo> codeInfo = new();

            //图像转成halcon的类型
            BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            HObject hObj;
            try
            {
                HOperatorSet.GenImageInterleaved(out hObj, bmpData.Scan0, "bgr", bmp.Width, bmp.Height, 0, "byte", 0, 0, 0, 0, -1, 0);
                if (zoom != 1)
                    HOperatorSet.ZoomImageFactor(hObj, out hObj, zoom, zoom, "bilinear");
                bmp.UnlockBits(bmpData);
            }
            catch (Exception ex)
            {
                bmp.UnlockBits(bmpData);
                Common.log.Error("Extract1DCode", ex);
                return codeInfo;
            }

            try
            {
                HOperatorSet.Rgb1ToGray(hObj, out HObject grayImage);
                HOperatorSet.CreateBarCodeModel(new HTuple(), new HTuple(), out HTuple hv_BarCode);  //创建条码模型
                HOperatorSet.SetBarCodeParam(hv_BarCode, "num_scanlines", 5);  //扫描线的最大数量
                HOperatorSet.SetBarCodeParam(hv_BarCode, "min_identical_scanlines", 3);  //成功解码最少扫描线数量
                HOperatorSet.SetBarCodeParam(hv_BarCode, "start_stop_tolerance", "high");  //扫描线的起点和终点的容许误差,high误差大,low误差小
                HOperatorSet.SetBarCodeParam(hv_BarCode, "max_diff_orient", 5);  //条码相邻两条竖条边缘扭曲的最大角度容差
                HOperatorSet.FindBarCode(grayImage, out HObject symbolRegions, hv_BarCode, "auto", out HTuple hv_String);  //寻找条码
                HOperatorSet.GetBarCodeResult(hv_BarCode, "all", "decoded_types", out HTuple hv_Type);  //获取条码类型
                HOperatorSet.GetBarCodeResult(hv_BarCode, "all", "orientation", out HTuple hv_Orientation);  //获取条码方向,x轴逆时针[0,180],顺时针[0,-180]
                HOperatorSet.SmallestRectangle2(symbolRegions, out HTuple row, out HTuple column, out HTuple phi, out HTuple length1, out HTuple length2);
                HOperatorSet.ClearBarCodeModel(hv_BarCode);  //清除条码模型
                Common.log.Info("Halcon Extract1DCode Count=" + hv_String.Length);

                if (hv_String.Length > 0)
                {
                    int n = hv_String.SArr.Length;
                    for (int i = 0; i < n; i++)
                    {
                        BarcodeInfo info = new()
                        {
                            Text = hv_String.SArr[i].Trim(),
                            CodeType = hv_Type.SArr[i],
                            Angle = Convert.ToSingle(hv_Orientation.DArr[i]),
                            Center = new PointF(Convert.ToSingle(column.DArr[i] / zoom), Convert.ToSingle(row.DArr[i] / zoom)),
                            Size = new SizeF(Convert.ToSingle(length1.DArr[i] * 2), Convert.ToSingle(length2.DArr[i] * 2))
                        };
                        double tan = Math.Tan(AngleToRadian(0 - info.Angle));
                        info.Distance = Convert.ToSingle(Math.Abs((tan * info.Center.X - info.Center.Y) / Math.Sqrt(tan * tan + 1)));
                        codeInfo.Add(info);
                    }
                }
            }
            catch (Exception ex)
            {
                Common.log.Error("Extract1DCode", ex);
            }

            return codeInfo;
        }

        private List<BarcodeInfo> HalconExtract2DCode(Bitmap bmp, float zoom)
        {
            List<BarcodeInfo> codeInfo = new();

            //图像转成halcon的类型
            BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            HObject hObj;
            try
            {
                HOperatorSet.GenImageInterleaved(out hObj, bmpData.Scan0, "bgr", bmp.Width, bmp.Height, 0, "byte", 0, 0, 0, 0, -1, 0);
                if (zoom != 1)
                    HOperatorSet.ZoomImageFactor(hObj, out hObj, zoom, zoom, "bilinear");
                bmp.UnlockBits(bmpData);
            }
            catch (Exception ex)
            {
                bmp.UnlockBits(bmpData);
                Common.log.Error("Extract2DCode", ex);
                return codeInfo;
            }

            string[] type = new string[] { "Data Matrix ECC 200", "QR Code", "PDF417" };
            try
            {
                for (int i = 0; i < type.Length; i++)
                {
                    HOperatorSet.Rgb1ToGray(hObj, out HObject grayImage);
                    //支持‘Data Matrix ECC 200’、‘QR Code’和‘PDF417’共3种类型
                    //‘standard_recognition’、‘enhanced_recognition’、‘maximum_recognition’
                    HOperatorSet.CreateDataCode2dModel(type[i], "default_parameters", "maximum_recognition", out HTuple dataCodeHandle);
                    HOperatorSet.SetDataCode2dParam(dataCodeHandle, "timeout", 1000);  //一个二维码的解码时间
                    //HOperatorSet.SetDataCode2dParam(dataCodeHandle, "symbol_size_min", 16);  //码粒最小个数
                    //HOperatorSet.SetDataCode2dParam(dataCodeHandle, "symbol_size_max", 30);  //码粒最大个数
                    if (i != 2)
                    {
                        HOperatorSet.SetDataCode2dParam(dataCodeHandle, "module_size_min", 3);   //码粒最小像素
                        HOperatorSet.SetDataCode2dParam(dataCodeHandle, "module_size_max", 20);  //码粒最大像素
                    }
                    HOperatorSet.FindDataCode2d(grayImage, out HObject symbolXLDs, dataCodeHandle, "stop_after_result_num", 5, out HTuple resultHandles, out HTuple decodedDataStrings);

                    //码粒的个数
                    HOperatorSet.GetDataCode2dResults(dataCodeHandle, "all_results", "symbol_rows", out HTuple _rows);
                    HOperatorSet.GetDataCode2dResults(dataCodeHandle, "all_results", "symbol_cols", out HTuple _cols);

                    //每个码粒的宽高
                    HOperatorSet.GetDataCode2dResults(dataCodeHandle, "all_results", "module_height", out HTuple _height);
                    HOperatorSet.GetDataCode2dResults(dataCodeHandle, "all_results", "module_width", out HTuple _width);

                    HOperatorSet.AreaCenterXld(symbolXLDs, out HTuple hv_Area, out HTuple hv_Row, out HTuple hv_Column, out HTuple hv_PointOrder);
                    //释放
                    HOperatorSet.ClearDataCode2dModel(dataCodeHandle);
                    Common.log.Info("Halcon Extract2DCode " + type[i] + " Count=" + decodedDataStrings.Length);

                    if (decodedDataStrings.Length > 0)
                    {
                        int n = decodedDataStrings.SArr.Length;
                        for (int j = 0; j < n; j++)
                        {
                            BarcodeInfo info = new()
                            {
                                Text = decodedDataStrings.SArr[j].Trim(),
                                CodeType = type[j],
                                Angle = 0,
                                Center = new PointF(Convert.ToSingle(hv_Column.DArr[j] / zoom), Convert.ToSingle(hv_Row.DArr[j] / zoom)),
                                Size = new SizeF(Convert.ToSingle(_cols.LArr[j] * _width.DArr[j]), Convert.ToSingle(_rows.LArr[j] * _height.DArr[j])),
                                Distance = 0
                            };
                            codeInfo.Add(info);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Common.log.Error("Extract2DCode", ex);
            }

            return codeInfo;
        }

        private unsafe List<BarcodeInfo> EyemExtractCode(string file, string codeType, int iBlockSize, int iRangeC, int iSymbolMin, int iSymbolMax)
        {
            List<BarcodeInfo> codeInfo = new();
            BarcodeAPI.DataCodeHandle hObject = null;
            BarcodeAPI.EyemImage tpImage;
            tpImage.ucpImage = IntPtr.Zero;

            try
            {
                int rtn = BarcodeAPI.eyemImageRead(file, 3, out tpImage);
                BarcodeAPI.EyemRect tpRoi = new()
                {
                    iXs = 0,
                    iYs = 0,
                    iWidth = tpImage.iWidth,
                    iHeight = tpImage.iHeight
                };

                string ext = System.IO.Path.GetExtension(file);
                file = file.Substring(0, file.Length - ext.Length);
                int result = BarcodeAPI.eyemDetectAndDecode(tpImage, tpRoi, file, codeType,
                    out hObject, out BarcodeAPI.EyemBarCode* tpResults, out int ipNum, false, iBlockSize, iRangeC, iSymbolMin, iSymbolMax, 1d);
                if (result != 0 || ipNum == 0)
                {
                    Common.log.Info("EyemExtractCode " + codeType + " result=" + result + " ipNum=" + ipNum);
                    return codeInfo;
                }
                Common.log.Info("EyemExtractCode " + codeType + " Count=" + ipNum);

                for (int i = 0; i < ipNum; i++)
                {
                    BarcodeInfo info = new()
                    {
                        Text = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(tpResults[i].hText).Trim(),
                        CodeType = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(tpResults[i].hType).Trim(),
                        Angle = Convert.ToSingle(tpResults[i].dAngle) + 180,
                        Center = new PointF(tpResults[i].iCenterX, tpResults[i].iCenterY),
                        Size = new SizeF(0, 0)
                    };
                    double tan = Math.Tan(AngleToRadian(0 - info.Angle));
                    info.Distance = Convert.ToSingle(Math.Abs((tan * info.Center.X - info.Center.Y) / Math.Sqrt(tan * tan + 1)));
                    codeInfo.Add(info);
                }
            }
            catch (Exception ex)
            {
                Common.log.Error("EyemExtractCode", ex);
            }
            finally
            {
                if (hObject != null)
                    hObject.Dispose();
                if (file != null)
                    BarcodeAPI.eyemImageFree(tpImage.ucpImage);
            }
            return codeInfo;
        }

        private double AngleToRadian(float angle)
        {
            //角度转弧度
            return angle * Math.PI / 180;
        }


    }
}