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<Bitmap> 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<string>());
|
}
|
|
private static readonly object lockObj = new object();
|
|
/// <summary>
|
/// 算子逻辑
|
/// </summary>
|
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<string> listCodeStrings = new List<string>();
|
|
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<string> CodeStrings = new List<string>();
|
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
|
}
|
}
|
}
|
|
/// <summary>
|
/// 创建条码类型
|
/// </summary>
|
public BarcodeReader<Bitmap> CreateCodeMode()
|
{
|
return new BarcodeReader<Bitmap>(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,
|
//};
|
}
|
}
|
}
|