解决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;
        }


    }
}

  

 

posted @ 2025-06-19 11:58  海宏软件  阅读(119)  评论(0)    收藏  举报