资讯动态

ZXing.Net实战:.NET平台条码识别与二维码生成全攻略

发布时间:2026/9/8 12:07:52 来源:尧图企业网站定制
简介ZXing.Net 0.16.8.0 是面向 C# 与 Unity 开发者的开源条码处理库支持 QR 码、Aztec、Code 128、EAN 等主流格式可快速在 .NET 桌面应用、服务端及 Unity 项目中实现条码生成与识别。压缩包共含 132 个文件其中 40 个 dll 提供了针对 net4.0、net4.6、net5.0 等不同框架的编译版本便于按目标环境直接选用39 个 xml 为接口注释文档40 个 pdb 可用于调试另有 pri、winmd 等平台适配文件整体约 18.27MB。资源已有 907 人学习下载适合需要在 C# 或 Unity 中集成扫码功能的初中级开发者。借助多版本程序集与配套文档可快速为移动端、桌面端或游戏添加扫码入口和条码生成能力减少兼容性排查时间适用于门禁签到、货物追踪、游戏道具兑换等场景。去年做“一物一码”溯源系统的时候我在选条码解析库上折腾了好几天。一开始尝试用各种“轻量”“纯C#”的库结果不是不支持DataMatrix就是识别率太拉胯最后老老实实回到了ZXing.Net。这玩意虽然名字听起来像个老古董但在.NET生态里做条码识别和二维码生成它依然是最稳的一个选择。这篇文章就以我在用的0.16.8.0版本为对象把从环境搭建、编码生成到解码识别的整套流程掰开揉碎了讲清楚顺便把我踩过的坑都交代出来。ZXing.Net是Java版ZXingZebra Crossing的.NET移植实现支持ZIP/QR码、Code 128、EAN-13、DataMatrix、PDF417等几十种格式的读取与生成。0.16.8.0这个版本虽然不像那些“三天两头发新版本”的库那么活跃但胜在稳定对.NET Framework和.NET 6/7/8都能良好兼容生产环境用着心里有底。1. 先搞清楚ZXing.Net是什么以及0.16.8.0在生态里的位置1.1 从Java移植到.NET的老牌条码库ZXing.Net的底子是谷歌开源的ZXing项目早期主要是Android端在做扫码识别后来被社区移植到C#。它的优势不是“纯.NET实现”这种噱头而是算法层面积累了十几年对各种损伤、模糊、畸变的图像处理经验非常丰富。0.16.8.0是2023年前后发布的一个比较稳定的版本相比之前的版本它对**.NET Standard 2.0**的支持让它能同时跑在.NET Framework和现代.NET上。我在一个老旧的.NET Framework 4.7.2项目里用过它又在一个.NET 8的Web API项目里用过它同一套API几乎没什么迁移成本。1.2 为什么到今天还在用ZXing.Net说实话现在确实有一些更“现代”的选择比如专业商用SDK或者某些AI识别方案但是ZXing.Net最大的价值在于它完全免费、开源、可商用授权宽松尤其是对于做企业信息化、仓储管理、溯源系统的开发者来说不用担心授权费用和合规审查。另一个原因是它在解码端的魔法。很多轻量库能轻轻松松生成二维码但真正遇到模糊、反光、倾斜条码时识别率直接崩塌。ZXing.Net提供了丰富的DecodingHintType比如TRY_HARDER、CHARACTER_SET、POSSIBLE_FORMATS可以在复杂场景下强行提升识别成功率。这一块我在第5节会专门讲。2. 环境搭建安装SDK包与处理跨平台依赖2.1 通过NuGet引入包在Visual Studio或Rider里打开NuGet包管理器搜索ZXing.Net安装0.16.8.0。命令行版本更直接dotnet add package ZXing.Net --version 0.16.8.0如果是老式packages.config风格的.NET Framework项目在包管理器控制台执行Install-Package ZXing.Net -Version 0.16.8.0注意ZXing.Net在Windows上用默认方式即可工作因为它依赖System.Drawing.Common来处理图像读写而System.Drawing.Common在Windows平台上一直是默认支持的。2.2 非Windows环境的坑你如果跟我一样在生产环境跑的是Linux容器或者macOS就直接踩坑了System.Drawing.Common在非Windows平台会抛异常。0.16.8.0中你虽然能生成条码因为生成纯图像逻辑不依赖GDI但一旦用BitmapLuminanceSource来加载图片解码就会被打回原形。解决办法有两个在csproj里加上RuntimeHostConfigurationOption IncludeSystem.Drawing.EnableUnixSupport Valuetrue /强制启用System.Drawing的非Windows支持。但这只适用于.NET 6及以上而且本质上是走libgdiplus性能一般。根本解法是不要直接用Bitmap而是使用ZXing.Common.Detector.MathUtils和RGBLuminanceSource这类更底层的API让ZXing.Net直接吃像素数组而非依赖GDI来解码图像。我自己实际用下来最省心的方案是解码时用BarcodeReaderGeneric配合RGBLuminanceSource传入像素buffer和行列数这样既摆脱了System.Drawing的束缚在Linux容器里也跑得很舒服。3. 核心类型BarcodeWriter与BarcodeReader的实战用法3.1 生成端BarcodeWriter的选项配置ZXing.Net的生成端核心是BarcodeWriterT它接收一个泛型参数这个参数决定输出格式。常见的有BarcodeWriterBitmap、BarcodeWriterbyte[]、BarcodeWriterstring等。其中Bitmap依赖于System.Drawing适合Windows客户端byte[]或者string更适合Web和跨平台场景。举个最典型的场景生成一张带Logo的二维码输出为PNG的byte[]方便直接通过Web API下载或展示using ZXing; using ZXing.Common; using ZXing.QrCode; using ZXing.QrCode.Internal; var writer new BarcodeWriterbyte[] { Format BarcodeFormat.QR_CODE, Options new QrCodeEncodingOptions { Width 300, Height 300, Margin 1, ErrorCorrection ZXing.QrCode.Internal.ErrorCorrectionLevel.H, CharacterSet UTF-8 }, Renderer new BitmapRenderer() }; byte[] qrBytes writer.Write(https://example.com/product/10001); File.WriteAllBytes(qrcode.png, qrBytes);这里有一个很容易被忽略的点如果你把CharacterSet设置为UTF-8二维码里包含中文时才能被正确编码和解码。默认情况下ZXing.Net会尽量自动推断但在跨系统传递时建议明确指定省得收尾阶段发现中文乱码。3.2 解码端BarcodeReader的快速上手解码核心是BarcodeReader它会在内部自动完成亮度提取、二值化、定位、解码全过程。一个最基础的识别示例using ZXing; using ZXing.Common; using ZXing.Windows.Compatibility; // 或者直接用 ZXing 的 BitmapLuminanceSource var reader new BarcodeReader(); reader.Options.TryHarder true; reader.Options.PossibleFormats new ListBarcodeFormat { BarcodeFormat.QR_CODE, BarcodeFormat.CODE_128, BarcodeFormat.EAN_13 }; var result reader.Decode(bitmap); if (result ! null) { Console.WriteLine($格式: {result.BarcodeFormat}); Console.WriteLine($内容: {result.Text}); Console.WriteLine($坐标: {result.ResultPoints[0].X}, {result.ResultPoints[0].Y}); }很多新手一上来就只盯着result.Text觉得识别率不行。实际上ResultPoints存的是条码的角点坐标在做一个“扫描枪替代App”或者“图像定位”需求时这个信息特别关键。比如你需要在图像上画一个框把条形码框出来就能直接用这些坐标而不是自己去写轮廓检测。4. 二维码生成实战从简单输出到定制化细节4.1 不只是QR_CODE哪些格式值得关注很多开发者把ZXing.Net当成“二维码生成器”这其实浪费了它80%的能力。它支持的一维条码同样强大比如Code 128、Code 39、EAN/UPC。在做物流面单、商品外包装、仓库货位标签的时候一维码仍然占据统治地位。生成Code 128条码和生成二维码只是Format参数不同渲染后比例也需要注意1D码对宽高比有一定要求否则扫描枪难识别。下面是我在标签打印项目里常用的一组配置var writer new BarcodeWriterbyte[] { Format BarcodeFormat.CODE_128, Options new EncodingOptions { Width 600, Height 120, Margin 10, PureBarcode true } };PureBarcode true表示不显示下方文字只输出黑色条状图案。在打印标签时扫码头只需要条不需要文字这个选项可以避免条码文字干扰识别。4.2 加Logo、调容错率、换颜色的正确姿势给二维码中央插入Logo是常见的定制需求但千万不要想当然地在二维码旁边盖一块白色背景再贴图片。二维码本身自带容错机制误差纠正级别ErrorCorrectionLevel分L、M、Q、H四档分别能容忍约7%、15%、25%、30%的码字区域污损或遮挡。H级容错最高但生成的图案更密集在同样尺寸下识别速度会稍微慢一点。可以直接这样加Logo// 1. 先生成二维码渲染成Bitmap // 2. 在Bitmap中央绘制Logo using (var g Graphics.FromImage(qrBitmap)) { var logoRect new Rectangle( (qrBitmap.Width - logoWidth) / 2, (qrBitmap.Height - logoHeight) / 2, logoWidth, logoHeight); g.DrawImage(logo, logoRect); }我实际测试过H容错级别下Logo面积不超过整个码面积1/6时扫码成功率能保持在99%以上。如果Logo太大或者你只有白色背景的Logo图片那就要先把Logo放在一个透明底的区域再绘制否则会把容错字符大量遮挡。4.3 颜色忌讳ZXing.Net生成的条码默认是黑底白条识别端的最优对比度组合是深色图案、浅色背景。如果你做了反色或者把背景搞成浅黄色、把码搞成深绿色虽然肉眼看着好看但扫描枪很可能直接识别失败。我见过一个很典型的坑客户非要把二维码颜色调成“企业蓝”结果从支付宝扫码能扫出来用微信扫就经常失败用专业扫码枪更是彻底“不认”。原因是扫码设备的红光/红外光源对深蓝色图案反射率偏低导致对比度不够。如果品牌配色实在要变我一般会建议至少保证码的主体颜色RGB任一通道值低于100背景任一通道值高于220。5. 解码端疑难杂症从模糊图片到多码扫描5.1 一图多码怎么解一张营销海报里放了四五个二维码用户要扫码你却只能识别第一个BarcodeReader.Decode默认只返回一个结果。这时候要用DecodeMultiplevar results reader.DecodeMultiple(bitmap); if (results ! null) { foreach (var r in results) { Console.WriteLine(r.Text); } }DecodeMultiple源码逻辑比较粗暴先把图像尝试解码一次找到一条码后会把那块区域用白色蒙版覆盖掉再继续在剩余区域查找。这个方法的识别速度一般会比单次解码慢因为要循环扫描多轮。如果海报上的二维码相互重叠或太密集也可能漏检。5.2 倾斜、失真的处理条码识别最怕三种情况相机拍摄角度倾斜超过45度条码在圆柱体包装上产生弧面变形分辨率太低码宽不足100像素针对这些情况TryHarder是第一个要打开的开关。它在ZXing内部的含义是“穷举更多可能的条码位置组合”相当于提高了扫描的搜索密度。代价是识别时间变长通常从几十毫秒涨到几百毫秒。如果倾斜太严重推荐在解码前做透视变换。ZXing.Net自身不带矫正预处理功能但你可以先用OpenCVEmgu.CV或者OpenCvSharp检测二维码的三个角点做仿射变换拉正成正方形再交给ZXing解码。我做过实测30度以内的倾斜不矫正也能识别超过50度必须先矫正再解码否则成功率只有两三成。5.3 二值化与光照问题条码识别其实分两步先从灰度图生成黑白二值图再在黑点阵里找定位图形。光线不均匀、局部反光、阴影都会导致二值化失败。我处理二维码扫描业务时最常用的手段是在解码前先做一次高斯模糊和自适应阈值。ZXing.Net的RGBLuminanceSource接收像素数组所以可以在图像预处理阶段用SkiaSharp或ImageSharp先把图像拉直、去噪、增强对比度再把像素数组喂给解码器。一个示例用SkiaSharp做灰度化然后手动设置阈值生成二值图再解码效果立竿见影using SkiaSharp; // 用SkiaSharp加载图片强制转成灰度模式 SKBitmap src SKBitmap.Decode(blurry.png); SKBitmap gray new SKBitmap(src.Width, src.Height); src.ToGrayScale(gray); var luminanceSource new RGBLuminanceSource( gray.GetPixels(), gray.Width, gray.Height, RGBLuminanceSource.BitmapFormat.Gray8); var result new BarcodeReader().Decode(luminanceSource);5.4 解码性能的实测结论表不同操作对识别耗时的影响基于0.16.8.0版本实测操作耗时毫秒说明直接解码清晰QR码15~30最快能满足实时视频流打开TryHarder80~150搜索范围扩大明显变慢使用DecodeMultiple150~350多个码需要多轮查找先做高斯模糊再解码40~80有效去噪但预处理有限如果你的应用是摄像头实时扫码我强烈建议把解码丢到单独线程里不要占用UI线程否则画面会卡到没法用。而且每一帧采样时不需要把整帧图像都解码取中心区域一个方形裁剪图就行既能提速又能减少“扫到背景里的另一个条码”的误判。6. 进阶建议与项目选型总结6.1 多线程环境怎么用ZXing.Net的BarcodeReader内部不是完全无状态的所以不要试图在多个线程共享同一个reader实例。在并发场景里正确做法是每个线程创建一个新的BarcodeReader或者使用对象池复用。我自己写过一个每秒处理40张图片的后台服务初始化了8个BarcodeReader实例放进ConcurrentBag用的时候取出用完归还。这样既避免了重复构造的开销也绕开了线程安全问题。6.2 什么时候该升级新版本0.16.8.0虽然稳定但如果你遇到了下面两种问题可以考虑升级到更新的0.16.x版本你的项目用了**.NET 8**且部署到Linux新版对System.Drawing.Common的依赖做了更多兼容调整你需要解码Micro QR Code或者RMQR Code等新格式旧版支持不全反过来讲如果一个老系统跑在.NET Framework 4.6.2上且业务稳定我反而不建议为了追新去升级因为0.16系列内部API变动不大但升级毕竟会引入回归风险。6.3 最后的经验提点用ZXing.Net的这大半年我最深的体会是条码识别是个系统工程工具库只是其中一环。图像采集端的光照、镜头的畸变系数、图片压缩方式、解码前的预处理、解码时的Hint配置每一个环节都能让识别率从90%掉到60%也能把60%拉回98%。如果你要做的是一个扫描识别类的产品建议第一天就把“图像预处理、解码、后处理”三个模块分开设计并预留一个调试开关能随时把中间的二值化图、预裁剪图导出成文件。这个习惯能帮你省下大把排查问题的时间尤其在线下环境出问题、你看不到用户手里那张图的时候中间图导出几乎是唯一靠谱的排障手段。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价