身份证号码识别(golang)
使用golang写一个简单的身份证号码识别
基本思路
- 拿到一张身份证图片,先确定身份证号码的位置
- 将该部分取出,然后进行二值化,比如将数字变成白色,背景变为黑色
- 按照第二步数字的颜色(这里以数字为白色为例),遍历像素点找出左边第一个白点和右边最后一个白点的坐标,对图片更加细致的切割
- 将图片分割即将每一个数字切割出来
- 识别数字
结果展示:
识别结果:
代码实现
主函数
file, err := os.Open("xxxx") //此处自行更改自己所需要识别的图片路径defer file.Close()if err != nil {panic(err)}//解析图片img, err := jpeg.Decode(file)if err != nil {log.Fatalln(err)}
其他函数自行在主函数中调用
- 号码定位
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
}
- 将图片二值化
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,aif r < 0x5555{dst.Set(x, y, color.White) //将灰度值小于0x5555的像素置为0}else{dst.Set(x,y,color.Black)}}}return dst
}
- 寻找边缘坐标更加细致的切割图片
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 = xx = 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 = yy = 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 + 1x = 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 + 1y = 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
}
- 将每一个数字切割出来
func SplitImage(src image.Image) []image.Image {var dsts []image.ImageleftX := 0for x := 0 ; x < src.Bounds().Dx() ; x++ {temp := falsefor y := 0; y < src.Bounds().Dy(); y++ {r, _, _, _ := src.At(x, y).RGBA()if r == 0xFFFF {temp = truebreak}}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 := falsefor y := 0; y < src.Bounds().Dy(); y++ {r, _, _, _ := src.At(x1, y).RGBA()if r == 0xFFFF {temp = truebreak}}if temp {leftX = x1x = x1break}}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
使用该命令导入该包
- 数字识别
- 需要先设置一个库,将每个数字的图片遍历出来的结果与该库的内容进行比较,来确定该数字
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.0for k, v := range database.Data {sum := 0for i := 0; i < 64; i++ {if v[i:i+1] == sign[i:i+1] {sum++}}//不断比较当匹配率达到最大时,就是此时所对应的数字if float64(sum)/64 > percent {number = kpercent = 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)}
}