解决ZXing.net二维码识别率低的问题
c#.net调用zxing识别二维码,有时候成功率比微信扫码低很多,尝试解决了一些,效果还行。
网上说用商业花钱买的那个,我没试,不知道效果怎么样。
using AForge.Imaging.Filters; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; using System.Windows.Forms; using ZXing; using ZXing.Common; // //using AForge.Imaging; //using AForge.Imaging.Filters; namespace iPublic.识别 { /// <summary> /// 二维码识别 /// 依赖ZXing /// 修改记录: /// 2025-01-19, 海宏软件 /// </summary> public static class QRCodeReader { /// <summary> /// 识别发票(BMP 图像文件)二维码 /// 修改记录: /// 2025-02-06, 海宏软件, 首先识别全图,如果没有识别成功,然后识别上下左右切分成多块,2分、3分.然后是灰度、黑白二值化 /// </summary> /// <param name="imagePath"></param> /// <returns></returns> public static string ReadQRCode(string imagePath) { return ReadQRCode(new Bitmap(imagePath)); } public static string ReadQRCode(Bitmap ABitmap) { ZXing.Result result = null; DateTime dBegin = DateTime.Now; Bitmap bitmap = ABitmap; // 创建一个BarcodeReader实例,设置解码选项 var options = new DecodingOptions { // 核心参数配置 CharacterSet = "UTF-8", // 解决中文乱码:ml-citation{ref="3,5" data="citationList"} TryHarder = true, // 启用深度扫描模式:ml-citation{ref="5" data="citationList"} //PureBarcode = false, // 允许非纯条形码图像false // 专用二维码参数 PossibleFormats = new List<BarcodeFormat> { BarcodeFormat.QR_CODE }, // 图像增强参数 UseCode39ExtendedMode = true, UseCode39RelaxedExtendedMode = true }; var barcodeReader = new BarcodeReader { Options = options, AutoRotate = true, // 自动旋转识别:ml-citation{ref="4" data="citationList"} TryInverted = true // 尝试反转色识别 }; // 尝试识别原图,如果识别到二维码,返回识别的条码内容 result = 尝试识别(barcodeReader, bitmap, "0"); //, true, false, false if (result != null) return result.Text; ////第二种识别方式ZBar:原文链接:https://blog.csdn.net/fuzhenglai/article/details/103732113 //using (ZBar.ImageScanner scanner = new ZBar.ImageScanner()) //{ // var symbols = scanner.Scan(bitmap); // if (symbols.Count > 0 && symbols[0].Data.Length > 8) // { // result.DataView = symbols[0].Data; // result.Code = Enum.ResultCode.DealSuccess; // result.Message = "识别成功"; // return ReturnResp(result); // } //} // 如果没有识别到二维码,按照上下左右四个区域分别进行识别 int width = ABitmap.Width; int height = ABitmap.Height; int halfWidth = width / 2; int halfHeight = height / 2; Rectangle[] regions = new Rectangle[] { //2分法:上中下 new Rectangle(halfWidth, 0, halfWidth, halfHeight), // 上右1 new Rectangle(0, 0, halfWidth, halfHeight), // 上左0 new Rectangle(0, halfHeight, halfWidth, halfHeight), // 下左2 new Rectangle(halfWidth, halfHeight, halfWidth, halfHeight), // 下右3 //3分法:上 new Rectangle(2*width/3,0,width/3,height/3), // 右上角2 new Rectangle(0,0,width/3,height/3), // 左上角0 new Rectangle(width/3,0,width/3,height/3), // 上中1 //3分法:中 new Rectangle(0,2*height/3,width/3,height/3), // 左下角 new Rectangle(width/3,2*height/3,width/3,height/3), // 下中 new Rectangle(2*width/3,2*height/3,width/3,height/3), // 右下角 //3分法:下 new Rectangle(0,height/3,width/3,height/3), // 左中 new Rectangle(width/3,height/3,width/3,height/3), // 中中 new Rectangle(2*width/3,height/3,width/3,height/3), // 右中 }; for (int i = 0; i < regions.Length; i++) { var region = regions[i]; using (var bitmapRegion = ABitmap.Clone(region, ABitmap.PixelFormat)) { result = 尝试识别(barcodeReader, bitmapRegion, (i + 1).ToString() + "x" + region.Left + "y" + region.Top + "w" + region.Width + "h" + region.Height); if (result != null) return result.Text; } } //最后再来尝试原图的512和黑白二值化 //result = 尝试识别(barcodeReader, bitmap, "99", false, true, true); // 如果所有区域都没有识别到二维码,返回空字符串 return string.Empty; } private static Result 尝试识别(BarcodeReader barcodeReader, Bitmap ABitmap, string 备注 = "", bool 尝试原图 = true, bool 尝试512像素 = true, bool 尝试黑白二值化 = true) { string s = "", sDir = "", sFile = "", sClass = "QRCodeReader.尝试识别", sDebug = ""; Bitmap bitmap = new Bitmap(ABitmap); //ABitmap ZXing.Result result = null; try { if (尝试原图) { result = barcodeReader.Decode(bitmap); if (result != null && !string.IsNullOrEmpty(result.Text)) return result; ZXing.Result[] results = barcodeReader.DecodeMultiple(bitmap); if (results != null && results.Length > 0 && !string.IsNullOrEmpty(results[0].Text)) return results[0]; saveTempImage(bitmap, 备注 + "原图"); } ////如果识别不了,尝试转成512 //if (尝试512像素 && bitmap.Width > 512) //{ // bitmap = 优化图片尺寸(ABitmap); // result = barcodeReader.Decode(bitmap); // if (result != null && !string.IsNullOrEmpty(result.Text)) return result; // saveTempImage(bitmap, 备注 + "调整512像素"); // //再尝试灰度优化512的 // if (尝试黑白二值化) // { // bitmap = 灰度优化图片_点阵(bitmap); // bitmap = AForge二值化图片_Auto(bitmap); //二值化图片_原生、AForge二值化图片、AForge二值化图片_Auto // result = barcodeReader.Decode(bitmap); // if (result != null && !string.IsNullOrEmpty(result.Text)) return result; // saveTempImage(bitmap, 备注 + "调整512后二值化"); // } //} //再尝试灰度优化原图 if (尝试黑白二值化) { bitmap = 灰度优化图片_点阵(ABitmap); //灰度优化图片 bitmap = AForge二值化图片_Auto(bitmap); //二值化图片_原生、AForge二值化图片、AForge二值化图片_Auto result = barcodeReader.Decode(bitmap); saveTempImage(bitmap, 备注 + "原图二值化"); } } catch (Exception x) { iLog.Info(sClass, x.Message + ".\r\n" + x.StackTrace); } return result; } private static Bitmap AForge二值化图片(Bitmap original, int threshold = 128) { // 灰度转换 Grayscale grayscale = new Grayscale(0.2125, 0.587, 0.114); //grayscale = new Grayscale(255, 255, 255); Bitmap gray = grayscale.Apply(original); // 二值化强化 var thresholdFilter = new Threshold(threshold); return thresholdFilter.Apply(gray); } //参考:https://blog.csdn.net/weixin_33858249/article/details/94550512 private static Bitmap AForge二值化图片_Auto(Bitmap original) { int[] histogram = new int[256]; int minGrayValue = 255, maxGrayValue = 0; int threshold = -1, newThreshold=0; Bitmap bitmap = new Bitmap(original); string s = "", sClass = "QRCodeReader.AForge二值化图片_Auto", sDebug = ""; try { //求取直方图 for (int i = 0; i < bitmap.Width; i++) { for (int j = 0; j < bitmap.Height; j++) { Color pixelColor = bitmap.GetPixel(i, j); histogram[pixelColor.R]++; if (pixelColor.R > maxGrayValue) maxGrayValue = pixelColor.R; if (pixelColor.R < minGrayValue) minGrayValue = pixelColor.R; } } //迭代计算阀值 newThreshold = (minGrayValue + maxGrayValue) / 2; for (int iterationTimes = 0; threshold != newThreshold && iterationTimes < 100; iterationTimes++) { threshold = newThreshold; int lP1 = 0; int lP2 = 0; int lS1 = 0; int lS2 = 0; //求两个区域的灰度的平均值 for (int i = minGrayValue; i < threshold; i++) { lP1 += histogram[i] * i; lS1 += histogram[i]; } int mean1GrayValue = (lP1 / lS1); for (int i = threshold + 1; i < maxGrayValue; i++) { lP2 += histogram[i] * i; lS2 += histogram[i]; } int mean2GrayValue = (lP2 / lS2); newThreshold = (mean1GrayValue + mean2GrayValue) / 2; } //计算二值化 for (int i = 0; i < bitmap.Width; i++) { for (int j = 0; j < bitmap.Height; j++) { Color pixelColor = bitmap.GetPixel(i, j); if (pixelColor.R > threshold) bitmap.SetPixel(i, j, Color.FromArgb(255, 255, 255)); else bitmap.SetPixel(i, j, Color.FromArgb(0, 0, 0)); } } ////用AForge的二值化强化 //AForge二值化图片(bitmap, threshold); } catch (Exception x) { iLog.Info(sClass, x.Message + ".\r\n" + x.StackTrace); } return bitmap; } // 灰度化+二值化(纯System.Drawing实现) private static Bitmap 二值化图片_原生(Bitmap original, int threshold = 128) { //0.2125, 0.587, 0.114 var gray = new Bitmap(original.Width, original.Height); for (int y = 0; y < original.Height; y++) { for (int x = 0; x < original.Width; x++) { Color c = original.GetPixel(x, y); int grayValue = (int)(c.R * 0.2125 + c.G * 0.587 + c.B * 0.114); gray.SetPixel(x, y, Color.FromArgb(grayValue, grayValue, grayValue)); } } // 二值化 var binary = new Bitmap(gray.Width, gray.Height); for (int y = 0; y < gray.Height; y++) { for (int x = 0; x < gray.Width; x++) { int v = gray.GetPixel(x, y).R > threshold ? 255 : 0; binary.SetPixel(x, y, Color.FromArgb(v, v, v)); } } return binary; } //参考:https://blog.csdn.net/weixin_33858249/article/details/94550512 private static Bitmap 灰度优化图片_点阵(Bitmap original) { Bitmap bitmap = original; string s = "", sClass = "QRCodeReader.灰度优化图片_点阵", sDebug = ""; try { for (int i = 0; i < bitmap.Width; i++) { for (int j = 0; j < bitmap.Height; j++) { Color pixelColor = bitmap.GetPixel(i, j); //计算灰度值.0.2125, 0.587, 0.114 int grey = (int)(0.299 * pixelColor.R + 0.587 * pixelColor.G + 0.114 * pixelColor.B); Color newColor = Color.FromArgb(grey, grey, grey); bitmap.SetPixel(i, j, newColor); } } } catch (Exception x) { iLog.Info(sClass, x.Message + ".\r\n" + x.StackTrace + ""); } return bitmap; } // 灰度转换 public static Bitmap 灰度优化图片(Bitmap source) { Bitmap newBitmap = new Bitmap(source.Width, source.Height); using (Graphics g = Graphics.FromImage(newBitmap)) { ColorMatrix colorMatrix = new ColorMatrix(new float[][] { new float[] {.3f, .3f, .3f, 0, 0}, new float[] {.59f, .59f, .59f, 0, 0}, new float[] {.11f, .11f, .11f, 0, 0}, new float[] {0, 0, 0, 1, 0}, new float[] {0, 0, 0, 0, 1} }); using (ImageAttributes attributes = new ImageAttributes()) { attributes.SetColorMatrix(colorMatrix); g.DrawImage(source, new Rectangle(0, 0, source.Width, source.Height), 0, 0, source.Width, source.Height, GraphicsUnit.Pixel, attributes); } } return newBitmap; } public static Bitmap 优化图片尺寸(Bitmap original, int targetWidth = 512) { //将图像宽度固定为 512px(经验证的最佳尺寸),高度按比例缩放 //int width = Math.Min(original.Width, targetWidth); //int height = (int)(original.Height * (width / (double)original.Width)); //return new Bitmap(original, width, height); string s = "", sClass = "QRCodeReader.优化图片尺寸", sDebug = ""; Bitmap bitmap = original, result = null; double zoomMultiple = 1; int toWidth = targetWidth, toHeight = original.Height; try { zoomMultiple = Convert.ToDouble(targetWidth) / Convert.ToDouble(original.Width); toHeight = Convert.ToInt32(original.Height * zoomMultiple); result = new Bitmap(toWidth, toHeight); using (Graphics g = Graphics.FromImage(result)) { g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; g.Clear(Color.Transparent); g.DrawImage(bitmap, new Rectangle(0, 0, toWidth, toHeight), new Rectangle(0, 0, bitmap.Width, bitmap.Height), GraphicsUnit.Pixel); } } catch (Exception x) { iLog.Info(sClass, x.Message + ".\r\n" + x.StackTrace); } return result; } private static string saveTempImage(Bitmap bitmap, string 名称附加) { return ""; string s = "", sDir = "D:\\Temp\\" + DateTime.Now.ToString("yyyyMMdd") + "\\", sFile = DateTime.Now.ToString("yyyyMMdd_HHmmss"); if (!Directory.Exists(sDir)) Directory.CreateDirectory(sDir); sFile += DateTime.Now.ToString("fffffff") + "_" + (new Random().Next(10000).ToString().PadLeft(4, '0')) + "#" + 名称附加 + ".bmp"; bitmap.Save(s = sDir + sFile, bitmap.RawFormat); return s; } } }