BarCode.cs 16.4 KB
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using HalconDotNet;

namespace BLL
{
    public class BarCode
    {
        private const int ANGLE = 3;     //角度在3°以内算同一个
        private const int SPACE = 300;   //条码的间隔距离

        public BarCode()
        {
        }

        public string ErrInfo { set; get; }

        public Bitmap Image { set; get; }

        public bool GetRectCode(out List<CodeSplice.LabelRect> label)
        {
            label = new List<CodeSplice.LabelRect>();
            List<CodeSplice.BarCode> info = ExtractBarCode();
            if (Image == null) return false;
            if (info.Count == 0) return false;


            //条码归类
            for (int i = 0; i < info.Count; i++)
            {
                int idx = -1;
                for (int j = 0; j < label.Count; j++)
                {
                    for (int k = 0; k < label[j].CodeCount; k++)
                    {
                        if (Math.Abs(Math.Abs(label[j].Code[k].Angle) - Math.Abs(info[i].Angle)) < ANGLE)
                        {
                            if (Math.Abs(label[j].Code[k].Distance - info[i].Distance) < SPACE)
                            {
                                idx = j;
                                break;
                            }
                        }
                    }
                    if (idx > -1) break;
                }

                if (idx == -1)
                {
                    CodeSplice.LabelRect rect = new CodeSplice.LabelRect { AvgAngle = info[i].Angle, CodeCount = 1 };
                    rect.Code.Add(info[i]);
                    label.Add(rect);
                }
                else
                {
                    label[idx].AvgAngle = 0;  //角度平均
                    label[idx].CodeCount++;
                    label[idx].Code.Add(info[i]);
                    for (int j = 0; j < label[idx].Code.Count; j++)
                        label[idx].AvgAngle += label[idx].Code[j].Angle;
                    label[idx].AvgAngle /= label[idx].CodeCount;
                }
            }



            //条码排序
            for (int i = 0; i < label.Count; i++)
            {
                if (label[i].CodeCount < 2) continue;
                //直线 y = kx   k = tanA
                //角度A=0-A  C#和halcon相反
                double tan = Math.Tan(AngleToRadian(0 - label[i].AvgAngle));
                double[] d = new double[label[i].Code.Count];  //点到直线的距离
                for (int j = 0; j < label[i].Code.Count; j++)
                    d[j] = Math.Abs((tan * label[i].Code[j].Center.X - label[i].Code[j].Center.Y) / Math.Sqrt(tan * tan + 1));

                info = new List<CodeSplice.BarCode>();
                int idx;
                double min, max;
                if (label[i].AvgAngle > 0)  //从近到远排列
                {
                    for (int k = 0; k < d.Length; k++)
                    {
                        idx = -1;
                        min = double.MaxValue;
                        for (int j = 0; j < d.Length; j++)
                        {
                            if (min > d[j])
                            {
                                min = d[j];
                                idx = j;
                            }
                        }
                        info.Add(label[i].Code[idx]);
                        d[idx] = double.MaxValue;
                    }
                }
                else  //从远到近排列
                {
                    for (int k = 0; k < d.Length; k++)
                    {
                        idx = -1;
                        max = double.MinValue;
                        for (int j = 0; j < d.Length; j++)
                        {
                            if (max < d[j])
                            {
                                max = d[j];
                                idx = j;
                            }
                        }
                        info.Add(label[i].Code[idx]);
                        d[idx] = double.MinValue;
                    }
                }
                label[i].Code = info;

            }





            return true;
        }

        public Bitmap MergeImage(Bitmap bmp1, Bitmap bmp2)
        {
            Bitmap bmp3 = new Bitmap(bmp1.Width, bmp1.Height + bmp2.Height);
            Graphics g = Graphics.FromImage(bmp3);
            g.DrawImage(bmp1, 0, 0);
            g.DrawImage(bmp2, 0, bmp1.Height);
            g.Save();
            return bmp3;


            //halcon拼接图像
            //int width = bmp1.Width;
            //int height = bmp1.Height;

            //HObject obj1 = null;
            //HObject obj2 = null;
            //HTuple row1, column1, row2, column2;
            //HTuple coRRJunctions, coRCJunctions, coCCJuctions, rowArea, columnArea, coRRArea, coRCArea, coCCArea;

            ////图像转换
            //BitmapData bmpData = bmp1.LockBits(new Rectangle(0, 0, bmp1.Width, bmp1.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            //HOperatorSet.GenImageInterleaved(out obj1, bmpData.Scan0, "bgr", bmp1.Width, bmp1.Height, 0, "byte", 0, 0, 0, 0, -1, 0);
            //bmp1.UnlockBits(bmpData);
            //bmpData = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            //HOperatorSet.GenImageInterleaved(out obj2, bmpData.Scan0, "bgr", bmp2.Width, bmp2.Height, 0, "byte", 0, 0, 0, 0, -1, 0);
            //bmp2.UnlockBits(bmpData);

            ////获取特征点
            //HOperatorSet.PointsFoerstner(obj1, 1, 2, 3, 50, 0.1, "gauss", "true", out row1, out column1, out coRRJunctions, out coRCJunctions, out coCCJuctions, out rowArea, out columnArea, out coRRArea, out coRCArea, out coCCArea);
            //HOperatorSet.PointsFoerstner(obj2, 1, 2, 3, 50, 0.1, "gauss", "true", out row2, out column2, out coRRJunctions, out coRCJunctions, out coCCJuctions, out rowArea, out columnArea, out coRRArea, out coRCArea, out coCCArea);

            ////图像拼接
            //HOperatorSet.ProjMatchPointsRansac(obj1, obj2, row1, column1, row2, column2, "ncc", 10, 0, 0, height, width, 0, 0.5, "gold_standard", 2, 42, out HTuple homMat2D, out HTuple points1, out HTuple points2);
            //HOperatorSet.ConcatObj(obj1, obj2, out HObject obj3);
            //HOperatorSet.GenProjectiveMosaic(obj3, out HObject obj4, 1, 1, 2, homMat2D, "default", "false", out HTuple mosaicMatrices2D);

            //Bitmap bmp32 = HObject2Bpp32(obj4);
            //Bitmap bmp24 = new Bitmap(bmp32.Width, bmp32.Height, PixelFormat.Format24bppRgb);
            //using (Graphics g = Graphics.FromImage(bmp24))
            //    g.DrawImage(bmp32, 0, 0);

            //return bmp24;



        }


        /// <summary>
        /// 提取一维条码信息
        /// </summary>
        /// <returns></returns>
        private List<CodeSplice.BarCode> ExtractBarCode()
        {
            HObject hObj = null;
            List<CodeSplice.BarCode> codeInfo = new List<CodeSplice.BarCode>();

            //图像转成halcon的类型
            BitmapData bmpData = Image.LockBits(new Rectangle(0, 0, Image.Width, Image.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            try
            {
                HOperatorSet.GenImageInterleaved(out hObj, bmpData.Scan0, "bgr", Image.Width, Image.Height, 0, "byte", 0, 0, 0, 0, -1, 0);
                Image.UnlockBits(bmpData);
            }
            catch (Exception ex)
            {
                hObj = null;
                Image.UnlockBits(bmpData);
                ErrInfo = ex.Message;
                CodeSplice.Common.Log.Out(ErrInfo);
                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.AreaCenter(symbolRegions, out HTuple hv_Area, out HTuple hv_Row, out HTuple hv_Column);  //获取面积中心点
                //HOperatorSet.RegionFeatures(symbolRegions, "width", out HTuple hv_width);  //条形码宽度,映射到X轴的长度
                //HOperatorSet.RegionFeatures(symbolRegions, "height", out HTuple hv_height);  //条形码高度,映射到Y轴的长度

                //row,中点的行,y
                //column,中点的列,x
                //phi,弧度,没有方向,-pi~pi
                //length1,长边的半径
                //length2,短边的半径
                HOperatorSet.SmallestRectangle2(symbolRegions, out HTuple row, out HTuple column, out HTuple phi, out HTuple length1, out HTuple length2);

                HOperatorSet.ClearBarCodeModel(hv_BarCode);  //清除条码模型



                //支持‘Data Matrix ECC 200’、‘QR Code’和‘PDF417’共3种类型
                //‘standard_recognition’、‘enhanced_recognition’、‘maximum_recognition’
                //HOperatorSet.CreateDataCode2dModel("QR Code", "default_parameters", "maximum_recognition", out HTuple dataCodeHandle);
                //HOperatorSet.SetDataCode2dParam(dataCodeHandle, "timeout", 200);  //一个二维码的解码时间
                ////HOperatorSet.SetDataCode2dParam(dataCodeHandle, "symbol_size_min", 16);  //码粒最小个数
                ////HOperatorSet.SetDataCode2dParam(dataCodeHandle, "symbol_size_max", 30);  //码粒最大个数
                //HOperatorSet.SetDataCode2dParam(dataCodeHandle, "module_size_min", 10);  //码粒最小像素
                //HOperatorSet.SetDataCode2dParam(dataCodeHandle, "module_size_max", 30);  //码粒最大像素
                //HOperatorSet.FindDataCode2d(grayImage, out HObject symbolXLDs, dataCodeHandle, new HTuple(), new HTuple(), 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.ClearDataCode2dModel(dataCodeHandle);


                if (hv_String.Length > 0)
                {
                    int n = hv_String.SArr.Length;
                    for (int i = 0; i < n; i++)
                    {
                        CodeSplice.BarCode info = new CodeSplice.BarCode();
                        info.ID = i;
                        info.Text = hv_String.SArr[i].Trim();
                        info.CodeType = hv_Type.SArr[i];
                        info.Angle = Convert.ToSingle(hv_Orientation.DArr[i]);
                        info.Center = new PointF(Convert.ToSingle(column.DArr[i]), Convert.ToSingle(row.DArr[i]));
                        info.Width = Convert.ToSingle(length1.DArr[i] * 2);
                        info.Height = Convert.ToSingle(length2.DArr[i] * 2);
                        double tan = Math.Tan(AngleToRadian(0 - info.Angle));
                        info.Distance = Math.Abs((tan * info.Center.X - info.Center.Y) / Math.Sqrt(tan * tan + 1));
                        info.Mode = "";
                        info.Start = 0;
                        info.Length = info.Text.Length;
                        codeInfo.Add(info);
                    }
                }

                //if (decodedDataStrings.Length > 0)
                //{
                //    int n = decodedDataStrings.SArr.Length;
                //    for (int i = 0; i < n; i++)
                //    {
                //        CodeSplice.BarCode info = new CodeSplice.BarCode();
                //        info.Text = decodedDataStrings.SArr[i];
                //        info.CodeType = "QR Code";
                //        info.Angle = 0;
                //        info.Center = new PointF();
                //        info.Width = Convert.ToSingle(_cols.LArr[i] * _width.DArr[i]);
                //        info.Height = Convert.ToSingle(_rows.LArr[i] * _height.DArr[i]);
                //        info.Distance = 0;
                //        info.Mode = "";
                //        info.Start = 0;
                //        info.Length = 0;
                //        codeInfo.Add(info);
                //    }
                //}

                return codeInfo;
            }
            catch (Exception ex)
            {
                ErrInfo = ex.Message;
                CodeSplice.Common.Log.Out(ErrInfo);
                return codeInfo;
            }
        }

        /// <summary>
        /// 角度转弧度
        /// </summary>
        /// <param name="angle"></param>
        /// <returns></returns>
        private double AngleToRadian(float angle)
        {
            return angle * Math.PI / 180;
        }

        /// <summary>
        /// 弧度转角度
        /// </summary>
        /// <param name="radian"></param>
        /// <returns></returns>
        private double RadianToAngle(float radian)
        {
            return radian * 180 / Math.PI;
        }

        public byte[] LoadImage(Bitmap bmp)
        {
            if (bmp.PixelFormat != PixelFormat.Format24bppRgb) return null;
            int width = bmp.Width;
            int height = bmp.Height;

            //扫描行宽度
            int stride = width * 3;
            int m = stride % 4;
            if (m != 0) m = 4 - m;
            stride += m;

            byte[] rtn = new byte[stride * height];
            Rectangle rect = new Rectangle(0, 0, width, height);
            BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
            System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, rtn, 0, bmpData.Stride * height);
            bmp.UnlockBits(bmpData);

            int idx1 = 0, idx2 = 0;
            int len = width * 3;
            byte[] src = new byte[width * height * 3];
            for (int i = 0; i < height; i++)
            {
                Array.Copy(rtn, idx1, src, idx2, len);
                idx1 += len + m;
                idx2 += len;
            }
            return src;
        }





        private Bitmap HObject2Bpp32(HObject image)
        {
            HTuple hred, hgreen, hblue, type, width, height;
            HOperatorSet.GetImagePointer3(image, out hred, out hgreen, out hblue, out type, out width, out height);
            Rectangle rect = new Rectangle(0, 0, width, height);

            Bitmap res = new Bitmap(width, height, PixelFormat.Format32bppRgb);
            BitmapData bitmapData = res.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
            unsafe
            {
                byte* bptr = (byte*)bitmapData.Scan0;
                byte* r = ((byte*)hred.L);
                byte* g = ((byte*)hgreen.L);
                byte* b = ((byte*)hblue.L);
                for (int i = 0; i < width * height; i++)
                {
                    bptr[i * 4] = (b)[i];
                    bptr[i * 4 + 1] = (g)[i];
                    bptr[i * 4 + 2] = (r)[i];
                    bptr[i * 4 + 3] = 255;
                }
            }

            res.UnlockBits(bitmapData);
            return res;
        }



    }
}