gozxing库介绍及使用gozxing识别图片中的多个二维码

107 阅读8分钟

gozxing库介绍及使用gozxing识别图片中的多个二维码

gozxing库 是 Java ZXing(Zebra Crossing)库的 Go 语言端口,核心用于条形码与二维码的解码(识别)编码(生成),完全基于纯 Go 实现(无 CGO 依赖),可跨平台使用,是 Go 生态中处理条码/二维码的主流工具之一。

一、核心功能

gozxing 继承了 ZXing 的核心能力,覆盖条码处理的主要场景,具体包括:

1. 解码能力(识别现有条码/二维码)
  • 支持的码制:覆盖主流一维码和二维码,常见包括:
    • 二维码:QR Code、Data Matrix、Aztec Code;
    • 一维码(条形码):Code 128、Code 39、EAN-13、EAN-8、UPC-A、UPC-E、ITF 等。
  • 输入兼容性:可处理 image.Image 类型的图像(支持 PNG、JPG 等常见格式),无需额外转换。
  • 参数控制:通过 Hints(解码参数)优化识别效果,如指定目标码制、开启“强力解码”、设置字符集等(解决模糊、变形条码识别问题)。
2. 编码能力(生成新的条码/二维码)
  • 支持的码制:主要覆盖 QR Code、Code 128、Code 39 等常用类型(部分冷门码制仅支持解码)。
  • 输出灵活性:生成的条码/二维码以 image.Image 类型返回,可直接保存为 PNG/JPG 文件,或用于网络传输。
  • 生成参数:可配置二维码容错级别(L/M/Q/H)、条码宽度/高度、边距等,满足不同场景需求(如高容错用于印刷场景)。

二、关键特性

  1. 纯 Go 实现,无依赖
    不依赖 CGO 或第三方原生库,仅依赖 Go 标准库(如 image 包),编译后可直接在 Linux、Windows、macOS 等平台运行,跨平台部署便捷。

  2. API 设计贴近 ZXing,降低学习成本
    接口命名、参数逻辑(如 DecodeHintTypeBarcodeFormat)与 Java ZXing 保持一致,熟悉 ZXing 的开发者可快速上手。

  3. 轻量高效,适合嵌入式/后台场景
    库体积小,解码/编码性能满足中小规模需求(如后台批量处理条码、嵌入式设备扫码),若需超高性能(如实时视频流扫码),可搭配图像预处理(如缩放、降噪)优化。

  4. 完善的错误处理
    解码/编码失败时会返回具体错误(如“未找到条码”“格式不支持”“参数无效”),便于问题定位。

三、典型使用场景

  • 客户端工具:桌面扫码工具、命令行条码识别/生成工具;
  • 后台服务:批量处理用户上传的条码图片(如物流单号识别、商品条码录入);
  • 嵌入式设备:树莓派等设备结合摄像头实现扫码功能;
  • 业务系统:生成带参数的二维码(如支付码、分享码),或识别用户上传的二维码(如登录验证、内容跳转)。

四、快速使用示例

1. 解码示例(识别图片中的 QR 码)
package main

import (
	"image"
	_ "image/jpeg"
	_ "image/png"
	"os"

	"github.com/makiuchi-d/gozxing"
	"github.com/makiuchi-d/gozxing/qrcode"
)

func main() {
	// 1. 读取图片文件
	file, err := os.Open("qrcode.png") // 替换为你的条码图片路径
	if err != nil {
		panic("打开图片失败: " + err.Error())
	}
	defer file.Close()

	// 2. 解码图片为 image.Image
	img, _, err := image.Decode(file)
	if err != nil {
		panic("解码图片失败: " + err.Error())
	}

	// 3. 转换为 gozxing 所需的 BinaryBitmap
	source := gozxing.NewLuminanceSourceFromImage(img)
	bmp := gozxing.NewBinaryBitmap(gozxing.NewHybridBinarizer(source))

	// 4. 配置解码参数(Hints)
	hints := map[gozxing.DecodeHintType]interface{}{
		gozxing.DecodeHintType_POSSIBLE_FORMATS: []gozxing.BarcodeFormat{gozxing.BarcodeFormat_QR_CODE}, // 指定目标码制
		gozxing.DecodeHintType_TRY_HARDER:       true, // 开启强力解码(解决模糊条码)
	}

	// 5. 初始化解码器并执行解码
	reader := qrcode.NewQRCodeReader() // QR 码专用解码器
	result, err := reader.Decode(bmp, hints)
	if err != nil {
		panic("解码失败: " + err.Error())
	}

	// 输出结果
	println("条码内容:", result.GetText())
	println("码制:", result.GetBarcodeFormat().String())
}
2. 使用gozxing识别图片中的多个二维码

由于gozxing只能识别图片中第一个码,要识别图片中多个二维码,需要循环识别和标记遮盖区域,直到没有新的二维码被识别出来。

package main

import (
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/jpeg"
	"log"
	"math"
	"os"

	"github.com/makiuchi-d/gozxing"
	"github.com/makiuchi-d/gozxing/qrcode"
)

// ReadMultipleQRCodes 从图片中识别多个二维码
// 参数:
//   - inputImagePath: 输入图片路径
//   - outputImagePath: 输出标记了识别区域的图片路径(可选)
// 返回值:
//   - []string: 识别到的二维码内容列表
//   - error: 错误信息
func ReadMultipleQRCodes(inputImagePath, outputImagePath string) ([]string, error) {
	// 打开输入图片文件
	inputFile, err := os.Open(inputImagePath)
	if err != nil {
		return nil, fmt.Errorf("无法打开输入图片: %v", err)
	}
	// 函数结束时关闭文件
	defer inputFile.Close()

	// 解码图片
	img, _, err := image.Decode(inputFile)
	if err != nil {
		return nil, fmt.Errorf("无法解码图片: %v", err)
	}

	// 存储识别到的二维码内容
	var results []string
	
	// 循环识别,直到没有新的二维码被识别出来
	for {
		// 创建BinaryBitmap用于解码
		bmp, err := gozxing.NewBinaryBitmapFromImage(img)
		if err != nil {
			return nil, fmt.Errorf("无法创建BinaryBitmap: %v", err)
		}

		// 创建二维码读取器
		qrReader := qrcode.NewQRCodeReader()

		// 设置解码参数
		hints := make(map[gozxing.DecodeHintType]interface{})
		// 指定预期的二维码格式
		hints[gozxing.DecodeHintType_POSSIBLE_FORMATS] = []gozxing.BarcodeFormat{
			gozxing.BarcodeFormat_QR_CODE,
		}
		// 开启TRY_HARDER模式提升识别率
		hints[gozxing.DecodeHintType_TRY_HARDER] = true
		// 设置字符集
		hints[gozxing.DecodeHintType_CHARACTER_SET] = "UTF-8"

		// 尝试解码
		result, err := qrReader.Decode(bmp, hints)
		if err != nil {
			// 没有更多可识别的二维码
			if _, ok := err.(gozxing.NotFoundException); ok {
				fmt.Println("没有更多二维码可识别")
				break
			}
			log.Printf("解码出错: %v", err)
			return results, err
		}

		// 获取二维码内容
		text := result.GetText()
		fmt.Printf("识别到二维码内容: %s\n", text)
		results = append(results, text)

		// 获取二维码位置信息
		points := result.GetResultPoints()
		if len(points) == 0 {
			log.Println("警告:未获取到二维码位置信息")
			continue
		}

		// 计算需要遮盖的矩形区域
		minX, minY := math.MaxFloat64, math.MaxFloat64
		maxX, maxY := -math.MaxFloat64, -math.MaxFloat64
		for _, p := range points {
			if p.GetX() < minX {
				minX = p.GetX()
			}
			if p.GetY() < minY {
				minY = p.GetY()
			}
			if p.GetX() > maxX {
				maxX = p.GetX()
			}
			if p.GetY() > maxY {
				maxY = p.GetY()
			}
		}

		// 构造一个稍微大一点的矩形,确保完全覆盖二维码
		rect := image.Rect(
			int(minX)-2, // 向左扩展2个像素
			int(minY)-2, // 向上扩展2个像素
			int(maxX)+2, // 向右扩展2个像素
			int(maxY)+2, // 向下扩展2个像素
		)

		// 确保矩形在图像边界内
		imgBounds := img.Bounds()
		rect = rect.Intersect(imgBounds)

		// 在原图上进行"遮盖"操作
		rgba := image.NewRGBA(imgBounds)
		draw.Draw(rgba, imgBounds, img, imgBounds.Min, draw.Src)

		// 使用白色遮盖已识别的二维码
		maskColor := color.RGBA{255, 255, 255, 255}
		mask := image.NewUniform(maskColor)
		draw.Draw(rgba, rect, mask, image.Point{}, draw.Src)

		// 更新图像,为下一次循环做准备
		img = rgba
	}

	// 如果指定了输出路径,则保存标记了识别区域的图片
	if outputImagePath != "" {
		outFile, err := os.Create(outputImagePath)
		if err != nil {
			log.Printf("无法创建输出文件: %v", err)
		} else {
			err = jpeg.Encode(outFile, img, &jpeg.Options{Quality: 90})
			if err != nil {
				log.Printf("无法保存标记后的图像: %v", err)
			}
			outFile.Close()
			fmt.Printf("标记后的图像已保存为%s\n", outputImagePath)
		}
	}

	return results, nil
}

func main() {
	// 示例使用
	inputImage := "./output/page_001_150dpi.png"  // 输入图片路径
	outputImage := "./output/page_001_marked.jpg" // 输出标记图片路径

	// 识别二维码
	results, err := ReadMultipleQRCodes(inputImage, outputImage)
	if err != nil {
		log.Fatal("识别失败:", err)
	}

	// 打印识别结果
	fmt.Println("\n=== 所有识别到的二维码内容 ===")
	for i, res := range results {
		fmt.Printf("%d: %s\n", i+1, res)
	}
}

这段代码展示了如何使用gozxing库识别图片中的多个二维码。关键点包括:

  1. 使用[gozxing.NewBinaryBitmapFromImage]
  2. 使用[qrcode.NewQRCodeReader()]创建二维码读取器
  3. 设置解码参数提升识别率
  4. 循环识别并遮盖已识别的二维码,避免重复识别
  5. 获取二维码位置信息并可视化标记
3. 编码示例(生成 QR 码)
package main

import (
	"image/png"
	"os"

	"github.com/makiuchi-d/gozxing"
	"github.com/makiuchi-d/gozxing/qrcode"
)

func main() {
	// 1. 配置编码参数
	contents := "https://example.com" // 二维码内容
	width := 300                     // 二维码宽度
	height := 300                    // 二维码高度
	hints := map[gozxing.EncodeHintType]interface{}{
		gozxing.EncodeHintType_CHARACTER_SET: "UTF-8",       // 字符集
		gozxing.EncodeHintType_ERROR_CORRECTION: qrcode.ErrorCorrectionLevel_H, // 高容错(H级,可恢复30%损坏内容)
		gozxing.EncodeHintType_MARGIN: 1, // 边距(单位:像素)
	}

	// 2. 生成二维码图像
 writer := qrcode.NewQRCodeWriter()
	img, err := writer.Write(contents, gozxing.BarcodeFormat_QR_CODE, width, height, hints)
	if err != nil {
		panic("生成二维码失败: " + err.Error())
	}

	// 3. 保存为 PNG 文件
	file, err := os.Create("generated_qrcode.png")
	if err != nil {
		panic("创建文件失败: " + err.Error())
	}
	defer file.Close()

	if err := png.Encode(file, img); err != nil {
		panic("保存图片失败: " + err.Error())
	}

	println("二维码生成成功,已保存为 generated_qrcode.png")
}

五、依赖与安装

  1. Go 版本要求:建议 Go 1.15+(兼容主流新版本)。
  2. 安装方式:通过 go get 直接下载:
    go get -u github.com/makiuchi-d/gozxing
    

六、常见问题与解决方案

  1. 解码失败(提示“not found”)

    • 检查图片中条码是否清晰(避免模糊、变形、反光);
    • 开启 DecodeHintType_TRY_HARDER 参数,增强解码容错;
    • 确认 POSSIBLE_FORMATS 指定的码制与实际条码一致(如误将条形码指定为 QR_CODE)。
  2. “BarcodeFormat_QR_CODE undefined” 错误

    • 该常量定义在 gozxing 根包下,需用 gozxing.BarcodeFormat_QR_CODE 引用(而非子包如 qrcode.BarcodeFormat_QR_CODE)。
  3. 编码时提示“unsupported format”

    • 部分码制仅支持解码(如 Aztec Code),确认目标码制在 writer.Write 中支持(如 QR Code、Code 128 均支持编码)。

七、使用场景

gozxing 是一个用于二维码/条码识别与生成库,提供了丰富的功能和 API,适用于各种场景。以下是一些常见的使用场景:

  • 扫码应用:在移动端或桌面应用中集成扫码功能,识别用户扫描的二维码/条码内容。
  • 数据传输:在移动设备上进行二维码数据传输,如二维码登录、二维码支付等。
  • 数据共享:在移动设备之间进行数据传输,如二维码分享、二维码登录等。
  • 商品管理:在库存管理、商品追踪等场景中使用条码/二维码进行商品识别和管理。
  • 数据验证:在数据传输中,对数据进行验证,如二维码数据是否正确、数据是否被篡改等。
  • 数据安全:在数据传输中,对数据进行加密,如二维码数据加密传输、二维码数据签名等。
  • 数据统计:在数据传输中,对数据进行统计,如二维码数据使用情况、数据传输量等。
  • 图像处理:结合图像处理库,对二维码/条码图像进行预处理,如图像增强、图像裁剪等,提高识别率。
  • 二维码/条码识别与生成:在移动设备上进行二维码/条码识别和生成,如二维码登录、二维码支付等。
  • 批量处理:在后台服务中批量处理二维码/条码图片,如批量识别、批量生成等。