using HalconDotNet; using LB_SmartVisionCommon; using LB_VisionControls; using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using ZXing; using ZXing.Common; using ZXing.Multi.QrCode; namespace LB_VisionProcesses.Alogrithms { public class FindCode2dTool : TAlgorithm { public enum Code2dType { None, QRCode, BarCode }; BarcodeReader zx_BarCodeHandle = null; QRCodeMultiReader zx_QRCodeHandle = null; public FindCode2dTool() { strProcessClass = "LB_VisionProcesses.Alogrithms.FindCode2dTool"; strProcessName = "Z读码工具"; //创建一个条码模型 zx_BarCodeHandle = CreateCodeMode(); zx_QRCodeHandle = CreateQRCodeMode(); Params.Inputs.Add("Code2dType", "BarCode"); Params.Inputs.Add("CodeHead", ""); Params.Inputs.Add("CodeLength", 0); Params.Inputs.Add("MinThreshold", 80); Params.Inputs.Add("MaxThreshold", 180); Params.Inputs.Add("MinCount", 0); Params.Inputs.Add("MaxCount", 9999); Params.Outputs.Add("CodeStrings", new List()); } private static readonly object lockObj = new object(); /// /// 算子逻辑 /// public override void TAlgorithmMain() { lock (lockObj) { #region 初始化变量 ZXing.Result[] results = null; #endregion try { if (InputImage == null) { Msg = "输入图片为空"; Result = false; AsyncLogHelper.Error(Msg); return; } if (!(InputImage is Mat)) { Msg = "输入图片格式不为Mat"; Result = false; AsyncLogHelper.Error(Msg); return; } object DomainImage = null; #region 裁剪区域 if (!ReduceDomainImage(InputImage, ref DomainImage)) { Msg = "裁剪区域失败"; Result = false; AsyncLogHelper.Error(Msg); return; } this.Record = new MsgRecord(); #endregion #region 算子逻辑 Bitmap hoDomainImage = null; Mat mGray = new Mat(); //ZXing针对识别灰度图像效果更好,所以这里转换为灰度图像 if (((Mat)DomainImage).Channels() == 3) { Cv2.CvtColor(((Mat)DomainImage), mGray, ColorConversionCodes.BGR2GRAY); } else { mGray = ((Mat)DomainImage).Clone(); } //高斯模糊的 Size(3,3) 和二值化的 blockSize=11 需根据图像分辨率调整。 if (true) { /*降噪(高斯模糊或中值滤波)*/ //高斯模糊:适合轻度噪声 //Cv2.GaussianBlur(mGray, mGray, new OpenCvSharp.Size(3, 3), 0); //中值滤波:适合椒盐噪声 //Cv2.MedianBlur(mGray, mGray, 3); /*锐化*/ //Mat sharpened = new Mat(); //float[] kernelData = { 0, -1, 0, -1, 5, -1, 0, -1, 0 }; //Mat kernel = Mat.FromArray(kernelData).Reshape(1, 3); // 转换为 3x3 矩阵 //Cv2.Filter2D(mGray, mGray, -1, kernel); /*二值化*/ //自适应阈值(推荐光照不均场景) //Cv2.AdaptiveThreshold(mGray, mGray, 255, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 11, 2); //大津算法(OTSU) //Cv2.Threshold(mGray, mGray, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu); /*透视校正(倾斜条码)*/ //// 检测条码轮廓并找到四个角点 //Point2f[] corners = FindBarcodeCorners(binary); //if (corners != null) //{ // Mat warpMat = Cv2.GetPerspectiveTransform(corners, new Point2f[] { ... }); // Cv2.WarpPerspective(binary, binary, warpMat, binary.Size()); //} } //将Mat转换为Bitmap,供ZXing使用 TAlgorithm.Mat2Bitmap(mGray, out hoDomainImage); string CodeHead = (ProcessParams.ConvertToString(Params.Inputs["CodeHead"])).TrimEnd(); int CodeLength = ProcessParams.ConvertToInt32(Params.Inputs["CodeLength"]); // 尝试转换字符串为枚举类型 if (!Enum.TryParse(ProcessParams.ConvertToString(Params.Inputs["Code2dType"]), out Code2dType eCode2dType)) { eCode2dType = Code2dType.None; } if (string.IsNullOrEmpty(CodeHead) || CodeHead.Trim() == "") { switch (eCode2dType) { case Code2dType.None: { break; } case Code2dType.QRCode: { TAlgorithm.Bitmap2BinaryBitmap(hoDomainImage, out BinaryBitmap binaryBitmap); results = zx_QRCodeHandle.decodeMultiple(binaryBitmap); TAlgorithm.BinaryBitmap2Bitmap(binaryBitmap, out hoDomainImage); break; } case Code2dType.BarCode: { results = zx_BarCodeHandle.DecodeMultiple(hoDomainImage); break; } } } else { //色阶映射,增强图像对比度 //scale_gray_map(hoDomainImage, out HObject ho_Image2, i, j); switch (eCode2dType) { case Code2dType.None: { break; } case Code2dType.QRCode: { TAlgorithm.Bitmap2BinaryBitmap(hoDomainImage, out BinaryBitmap binaryBitmap); results = zx_QRCodeHandle.decodeMultiple(binaryBitmap); TAlgorithm.BinaryBitmap2Bitmap(binaryBitmap, out hoDomainImage); break; } case Code2dType.BarCode: { results = zx_BarCodeHandle.DecodeMultiple(hoDomainImage); break; } } if (results != null) { List listCodeStrings = new List(); foreach (var result in results) { string strBarCode = result.Text; if (strBarCode.StartsWith(CodeHead) && strBarCode.Length == CodeLength) { var points = result.ResultPoints; ((MsgRecord)Record).AddRecord(strBarCode, points[0].Y, points[0].X); OutputImage = hoDomainImage; listCodeStrings.Add(strBarCode); Params.Outputs["CodeStrings"] = listCodeStrings; Params.Outputs["Count"] = 1; Msg = "运行成功"; Result = true; return; } } } OutputImage = null; Params.Outputs["CodeStrings"] = null; Params.Outputs["Count"] = 0; if (CodeLength > 0) { Msg = string.Format("没有找到指定头为\"{0}\"且长度为\"{1}\"的码", CodeHead, CodeLength); } else { Msg = string.Format("没有找到指定头为\"{0}\"码", CodeHead); } Result = false; AsyncLogHelper.Info(Msg); return; } #endregion #region 结果处理 List CodeStrings = new List(); if (results != null) { foreach (var result in results) { CodeStrings.Add(result.Text); var points = result.ResultPoints; ((MsgRecord)Record).AddRecord(result.Text, points[0].Y, points[0].X); } } Params.Outputs["CodeStrings"] = CodeStrings; Params.Outputs["Count"] = CodeStrings.Count; #endregion #region 生成OutputImage给后续处理 try { OutputImage = hoDomainImage; } catch (Exception ex) { Msg = "生成OutputImage失败,原因是:" + ex.ToString(); Result = false; AsyncLogHelper.Error(Msg); return; } #endregion if (Msg == "运行超时") { Result = false; return; } int MinCount = ProcessParams.ConvertToInt32(Params.Inputs["MinCount"]); int MaxCount = ProcessParams.ConvertToInt32(Params.Inputs["MaxCount"]); if (CodeStrings.Count < MinCount || CodeStrings.Count > MaxCount) { Msg = string.Format("结果个数超出范围({0},{1})", MinCount, MaxCount); Result = false; AsyncLogHelper.Error(Msg); return; } Msg = "运行成功"; Result = true; return; } catch (Exception ex) { Msg = "运行失败,原因是:" + ex.ToString().TrimEnd(); OutputImage = null; Result = false; AsyncLogHelper.Error(Msg); return; } finally { if (!Result) { Params.Outputs["CodeStrings"] = ""; Params.Outputs["Count"] = 0; } bCompleted = true; #region 内存释放 #endregion } } } /// /// 创建条码类型 /// public BarcodeReader CreateCodeMode() { return new BarcodeReader(null) { Options = new DecodingOptions { TryHarder = true, PossibleFormats = new[] { //一维码 BarcodeFormat.All_1D //, BarcodeFormat.EAN_13 //, BarcodeFormat.EAN_8 //, BarcodeFormat.CODE_39 //, BarcodeFormat.CODE_93 //, BarcodeFormat.CODE_128 //, BarcodeFormat.CODABAR } }, AutoRotate = true, }; } public QRCodeMultiReader CreateQRCodeMode() { return new QRCodeMultiReader(); //return new BarcodeReader //{ // Options = new DecodingOptions // { // TryHarder = true, // PossibleFormats = new[] // { // //这个是二维码 // BarcodeFormat.QR_CODE // //, BarcodeFormat.DATA_MATRIX // //, BarcodeFormat.MAXICODE // //, BarcodeFormat.AZTEC // } // }, // AutoRotate = true, //}; } } }