身份证号码识别(golang)

身份证号码识别(golang)

使用golang写一个简单的身份证号码识别

基本思路

  1. 拿到一张身份证图片,先确定身份证号码的位置
  2. 将该部分取出,然后进行二值化,比如将数字变成白色,背景变为黑色
  3. 按照第二步数字的颜色(这里以数字为白色为例),遍历像素点找出左边第一个白点和右边最后一个白点的坐标,对图片更加细致的切割
  4. 将图片分割即将每一个数字切割出来
  5. 识别数字

结果展示:

在这里插入图片描述
识别结果:
在这里插入图片描述

代码实现

主函数

file, err := os.Open("xxxx") //此处自行更改自己所需要识别的图片路径
	defer file.Close()
	if err != nil {
		panic(err)
	}
	//解析图片
	img, err := jpeg.Decode(file)
	if err != nil {
		log.Fatalln(err)
	}

其他函数自行在主函数中调用

  1. 号码定位
func Number(src image.Image)image.Image{
	rect := src.Bounds() // 获取图片的大小
	//左上角坐标
	//此处图片的尺寸需要根据所需识别的图片进行确定
	left := image.Point{X: rect.Dx()*220 / 620 , Y: rect.Dy()*325/385}
	//右下角坐标
	//此处图片的尺寸需要根据所需识别的图片进行确定
	right := image.Point{X: rect.Dx()*540 / 620 , Y: rect.Dy()*345/385}
	newReact := image.Rectangle{
		Min: image.Point{X: 0, Y: 0},
		Max: image.Point{X: right.X - left.X, Y: right.Y - left.Y},
	} // 创建一个新的矩形 ,将原图切割后的图片保存在该矩形中
	newImage := image.NewRGBA(newReact) // 创建一个新的图片
	draw.Draw(newImage, newReact, src, left, draw.Over) // 将原图绘制到新图片中
	return newImage
}
  1. 将图片二值化
func Binarization(src image.Image)image.Image{
	//将图片灰化
	dst := image.NewGray16(src.Bounds()) // 创建一个新的灰度图
	draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Src) // 将原图绘制到新图片中

	//遍历像素点,实现二值化
	for x := 0; x < src.Bounds().Dx(); x++ {
		for y := 0; y < src.Bounds().Dy(); y++ {
			r,_,_,_ := src.At(x, y).RGBA() //取出每个像素的r,g,b,a
			if r < 0x5555{
				dst.Set(x, y, color.White) //将灰度值小于0x5555的像素置为0
			}else{
				dst.Set(x,y,color.Black)
			}
		}
	}
	return dst
}
  1. 寻找边缘坐标更加细致的切割图片
func CutImage(src image.Image) image.Image{
	var left , right image.Point //左上角右下角坐标
	//寻找左边边缘白点的x坐标
	for x:= 0 ; x < src.Bounds().Dx() ; x++{
		for y := 0 ; y < src.Bounds().Dy() ; y++{
			r,_,_,_ := src.At(x,y).RGBA()
			if r == 0xFFFF{
				left.X = x
				x = src.Bounds().Dx()  //使外层循环结束
				break
			}
		}
	}
	//寻找左边边缘白点的y坐标
	for y:= 0 ; y < src.Bounds().Dy() ; y++{
		for x := 0 ; x < src.Bounds().Dx() ; x++{
			r,_,_,_ := src.At(x,y).RGBA()
			if r == 0xFFFF{
				left.Y = y
				y = src.Bounds().Dy()  //使外层循环结束
				break
			}
		}
	}
	//寻找右边边缘白点的x坐标
	for x:= src.Bounds().Dx()  ; x > 0 ; x--{
		for y := src.Bounds().Dy() ; y > 0 ; y--{
			r,_,_,_ := src.At(x,y).RGBA()
			if r == 0xFFFF{
				right.X = x + 1
				x = 0  //使外层循环结束
				break
			}
		}
	}
	//寻找右边边缘白点的y坐标
	for y:= src.Bounds().Dy()-1 ; y > 0 ; y--{
		for x := src.Bounds().Dx()-1 ; x > 0 ; x--{
			r,_,_,_ := src.At(x,y).RGBA()
			if r == 0xFFFF{
				right.Y = y + 1
				y = 0  //使外层循环结束
				break
			}
		}
	}
	//按照坐标点将图像精准切割
	newReact := image.Rect(0, 0, right.X - left.X + 1 ,
		right.Y - left.Y  +2) // 创建一个新的矩形 ,将原图切割后的图片保存在该矩形中
	log.Println(left, right)
	log.Println(src.Bounds(),newReact)
	dst := image.NewRGBA(newReact) 
	draw.Draw(dst ,dst.Bounds() , src , left , draw.Over)
	return dst
}
  1. 将每一个数字切割出来
func SplitImage(src image.Image) []image.Image  {
	var dsts []image.Image
	leftX := 0
	for x := 0 ; x < src.Bounds().Dx() ; x++ {
		temp := false
		for y := 0; y < src.Bounds().Dy(); y++ {
			r, _, _, _ := src.At(x, y).RGBA()
			if r == 0xFFFF {
				temp = true
				break
			}
		}
		if temp {
			continue
		}
		dst := image.NewGray16(image.Rect(0, 0, x-leftX, src.Bounds().Dy()))
		draw.Draw(dst, dst.Bounds(), src, image.Point{X: leftX, Y: 0}, draw.Src)
		//下一个起点
		for x1 := x + 1 ; x1 < src.Bounds().Dx(); x1++ {
			temp := false
			for y := 0; y < src.Bounds().Dy(); y++ {
				r, _, _, _ := src.At(x1, y).RGBA()
				if r == 0xFFFF {
					temp = true
					break
				}
			}
			if temp {
				leftX = x1
				x = x1
				break
			}
		}
		img := resize.Resize(8 , 8 , dst  ,resize.Lanczos3)
		dsts = append(dsts , img)
	}
	//fmt.Println(len(dsts))
	return dsts
}

这部分使用了一个外部库,用来更改图片尺寸大小,将每个数字所在的图片的大小进行统一可以更好的进行数字识别
库名 :“github.com/nfnt/resize”

go get github.com/nfnt/resize

使用该命令导入该包

  1. 数字识别
  • 需要先设置一个库,将每个数字的图片遍历出来的结果与该库的内容进行比较,来确定该数字
var Data = map[string]string{
 	"0": "0111110011111110000000001000000010000000100000100111111000011000",
	"1": "0100000001000000010000001100000011000000111111101111111011111110",
	"2": "0000001010000110000010001000100010011000000100001110000001100000",
	"3": "0000000000000000000000000001000000110000011100101101001011001110",
	"4": "0000110000011100001001000100010000000100000111100000110000000100",
	"5": "0000000011100000001000000000000000000010000100000001011000011100",
	"6": "0000110000111110001100100110000010000000100100100001111000001100",
	"7": "0000000000000000000011100001111000010000001000001100000011000000",
	"8": "0100111011111010100100100001000000010000101100100110111000000100",
	"9": "0010000001110000100110000000101000001110100011000111100001100000",
}

  • 对每个数字图片进行遍历,得到的结果与库进行比较到处结果
func NumberDistinguish(srcs []image.Image) string {
	id := ""
	for i := 0; i < len(srcs); i++ {
		// 获取图片的指纹
		sign := ""
		for x := 0; x < srcs[i].Bounds().Dx(); x++ {
			for y := 0; y < srcs[i].Bounds().Dy(); y++ {
				r, _, _, _ := srcs[i].At(x, y).RGBA()
				if r > 0x7777 {
					sign += "1"
				} else {
					sign += "0"
				}
			}
		}

		// 对比指纹
		number := ""
		//对比相似率
		percent := 0.0
		for k, v := range database.Data {
			sum := 0
			for i := 0; i < 64; i++ {
				if v[i:i+1] == sign[i:i+1] {
					sum++
				}
			}
			//不断比较当匹配率达到最大时,就是此时所对应的数字
			if float64(sum)/64 > percent {
				number = k
				percent = float64(sum) / 64  
			}
		}

		log.Println(sign, number, percent)
		id += number
	}
	return id
}

在代码调试过程中需要将图片输出查看

  • 查看单张图片
func ShowImg(src image.Image) {
	dst, err := os.Create("./preview/output.jpg") //可以自己更改为想要将图片存放的位置
	if err != nil {
		log.Fatalln(err)
	}
	jpeg.Encode(dst, src, nil)
}
  • 查看多张图片
func ShowImgs(srcs []image.Image) {
	for i := 0; i < len(srcs); i++ {
		dst, err := os.Create(fmt.Sprintf("./preview/output-%d.jpg", i))
		if err != nil {
			log.Fatalln(err)
		}
		jpeg.Encode(dst, srcs[i], nil)
	}
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aurora_Galaxy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值