Xamarin Forms中如何从图库图片扫描识别QR Code二维码
Xamarin 基于ZXing实现本地图片QR码扫描的修复方案
你的代码存在几个容易导致ZXing识别失败的问题,按以下步骤调整即可解决返回null的问题:
1. 首先修复基础语法错误
你现有代码中DependencyService调用缺少闭合尖括号,修正为:
var binaryBitmap = Xamarin.Forms.DependencyService.Get<IImageHelper>().GetBinaryBitmap(file.GetStream());
2. 配置ZXing解码提示参数
未配置解码优化参数是识别失败的高频原因,添加DecodeHints开启识别优化、指定QR码格式:
var file = await Plugin.Media.CrossMedia.Current.PickPhotoAsync(); if (file == null) return; var reader = new QRCodeReader(); // 新增解码提示配置 var hints = new Dictionary<DecodeHintType, object> { { DecodeHintType.TRY_HARDER, true }, // 开启高精度识别模式 { DecodeHintType.POSSIBLE_FORMATS, new List<BarcodeFormat> { BarcodeFormat.QR_CODE } }, // 仅识别QR码减少性能消耗 { DecodeHintType.CHARACTER_SET, "UTF-8" } // 指定字符编码避免中文乱码 }; var binaryBitmap = Xamarin.Forms.DependencyService.Get<IImageHelper>().GetBinaryBitmap(file.GetStream()); var result = reader.decode(binaryBitmap, hints); // 解码时传入hints
3. 优化像素读取逻辑,避免逐像素取值性能问题
你当前的GetRgbBytes方法逐像素调用GetPixel性能极低,大尺寸图片会触发识别超时,替换为批量读取像素的实现:
private byte[] GetRgbBytes(Bitmap image) { int width = image.Width; int height = image.Height; int[] pixels = new int[width * height]; // 批量读取所有像素到数组 image.GetPixels(pixels, 0, width, 0, 0, width, height); byte[] rgbBytes = new byte[width * height * 3]; for (int i = 0; i < pixels.Length; i++) { int pixel = pixels[i]; rgbBytes[i * 3] = (byte)((pixel >> 16) & 0xff); // 红通道 rgbBytes[i * 3 + 1] = (byte)((pixel >> 8) & 0xff); // 绿通道 rgbBytes[i * 3 + 2] = (byte)(pixel & 0xff); // 蓝通道 } return rgbBytes; }
4. 增加Exif旋转修正
手机拍摄的照片通常自带Exif旋转标记,未修正旋转的歪斜二维码会导致ZXing识别失败,在GetBinaryBitmap中新增旋转处理逻辑:
public BinaryBitmap GetBinaryBitmap(Stream image) { // 先将流转为字节数组,同时用于解码和Exif读取 byte[] imageBytes; using (var ms = new MemoryStream()) { image.CopyTo(ms); imageBytes = ms.ToArray(); } // 解码Bitmap,可根据图片尺寸调整InSampleSize做降采样,提升识别效率 BitmapFactory.Options options = new BitmapFactory.Options { InSampleSize = 2 // 宽高缩小为原尺寸的1/2,可根据实际场景调整 }; Bitmap bitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length, options); // 读取Exif旋转角度 var exif = new ExifInterface(new MemoryStream(imageBytes)); var orientation = exif.GetAttributeInt(ExifInterface.TagOrientation, (int)Orientation.Normal); float rotation = orientation switch { (int)Orientation.Rotate90 => 90, (int)Orientation.Rotate180 => 180, (int)Orientation.Rotate270 => 270, _ => 0 }; // 修正Bitmap旋转 if (rotation != 0) { Matrix matrix = new Matrix(); matrix.PostRotate(rotation); bitmap = Bitmap.CreateBitmap(bitmap, 0, 0, bitmap.Width, bitmap.Height, matrix, true); } // 生成BinaryBitmap返回 byte[] rgbBytes = GetRgbBytes(bitmap); var luminanceSource = new RGBLuminanceSource(rgbBytes, bitmap.Width, bitmap.Height); var binarizer = new HybridBinarizer(luminanceSource); return new BinaryBitmap(binarizer); }
更简单的替代方案
无需手动封装BinaryBitmap,直接使用ZXing.Net.Mobile自带的BarcodeReader类的重载方法,直接传入处理后的Bitmap即可,ZXing内部会自动完成像素转换、二值化等逻辑:
// IImageHelper接口新增方法实现 public string ScanQrFromImage(Stream imageStream) { byte[] imageBytes; using (var ms = new MemoryStream()) { imageStream.CopyTo(ms); imageBytes = ms.ToArray(); } // 解码并修正旋转(逻辑同上) var bitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length); // 修正旋转逻辑省略,和上面一致 var reader = new BarcodeReader { AutoRotate = true, Options = new DecodingOptions { TryHarder = true, PossibleFormats = new List<BarcodeFormat> { BarcodeFormat.QR_CODE } } }; var result = reader.Decode(bitmap); return result?.Text; }
内容的提问来源于stack exchange,提问作者Tahir Mahmood Hashmi
相关产品推荐
相关产品推荐

