commit d1818b16b2387576c16023ecb5c61d316059b796 Author: dustoair <107600816+dustoair@users.noreply.github.com> Date: Wed Aug 10 12:25:23 2022 +0800 身份证号码简易识别 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..59877e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +go.sum +*.jpg \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..940e9db --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ + + +https://www.bilibili.com/video/BV19L4y1T7sE +https://www.bilibili.com/video/BV19L4y1T7sE?p=6 +https://gist.github.com/ohko/5fd4f294c89d4f11ede58255c5f625aa +【golang】身份证号码识别 + +最关键的定位和识别 没有深度,图片可能位置不一样有失真,变形。角度不可能和照片垂直,所以图像不可能这样简单被定位出来 +大致的思路就是:1.二值化 2.腐蚀(让黑的地方变粗、变粗、变粗) ,就会形成一些黑块 3.找到长宽比例是身份证长宽比例的黑块 4.把这个黑块切割出来。 + +```go +var Signs = map[string]string{ +"0": "0000000001000010000000000000000000000000000000000100001000011000", +"1": "0000000000000000010000000100000001000000010010100100001001100000", +"2": "0000000000000100000000011000100100000000000100000100000000100000", +"3": "0000000000000000000000000001000000010000010000000100101000000100", +"5": "0000000001100000000000000000000000000000000100100000110000000100", +"9": "0010000001000000000010000000000000000000000011000100100000000000", +"6": "0000000000001010000100000000000001000000000000000000101000000000", +} +``` + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f52e912 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module IdSense + +go 1.18 + +require github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 diff --git a/main.go b/main.go new file mode 100644 index 0000000..e976c5c --- /dev/null +++ b/main.go @@ -0,0 +1,259 @@ +package main + +import ( + "fmt" + "github.com/nfnt/resize" + "image" + "image/color" + "image/draw" + "image/jpeg" + "log" + "os" + "strconv" +) + +var Signs = map[string]string{ + "0": "0011111001111111011100110110000101100001011000010011111100000110", + "1": "0010000001100000011000000110000011111110111111100111111000000000", + "2": "1000011010001110100011101001101010011010111100101111001001100010", + "3": "0100001101000011010100110111000101111011011111110100111000000110", + "4": "0000110000111100011111001110110011001110000111100001110000001000", + "5": "0001000001110001011100010111000101111011010111110100111000001100", + "7": "0100000001000000010001110100111101011100011110000111000001100000", + "9": "0111000001111000110110111001111011011110111111001111100001110000", +} + +func main() { + // 打开文件 + // 配合bilibili视频 https://www.bilibili.com/video/BV19L4y1T7sE + f, _ := os.Open("id.jpg") + defer f.Close() + + // 解析图片 + img, err := jpeg.Decode(f) + if err != nil { + log.Fatalln(err) + } + + img = catImg(img) + + img = bin01(img) + + img = minImg(img) + imgs := splitImg(img) + showImgs(imgs) + id := analyse(imgs) + log.Println(id) + +} + +// 数字识别 +func analyse(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" + } + } + } + + // 对比指纹 + mb := "" + percent := 0.0 + for k, v := range Signs { + sum := 0 + for i := 0; i < 64; i++ { + if v[i:i+1] == sign[i:i+1] { + sum++ + } + } + + if float64(sum)/64 > percent { + mb = k + percent = float64(sum) / 64 + } + } + + //log.Println("output-"+strconv.Itoa(i)+".jpg", sign, mb, percent) + fmt.Println("output-"+strconv.Itoa(i)+".jpg", sign, mb, percent) + id += mb + } + + return id +} + +// 切割图片 +func splitImg(src image.Image) []image.Image { + var dsts []image.Image + + posx := 0 + for x := 0; x < src.Bounds().Dx(); x++ { + + found := false + for y := 0; y < src.Bounds().Dy(); y++ { + r, _, _, _ := src.At(x, y).RGBA() + if r == 0xFFFF { + found = true + break + } + } + + if found { + continue + } + + dst := image.NewGray16(image.Rect(0, 0, x-posx, src.Bounds().Dy())) + draw.Draw(dst, dst.Bounds(), src, image.Point{X: posx, Y: 0}, draw.Src) + + // 下一个起点 + for xx := x + 1; xx < src.Bounds().Dx(); xx++ { + found := false + for y := 0; y < src.Bounds().Dy(); y++ { + r, _, _, _ := src.At(xx, y).RGBA() + if r == 0xFFFF { + found = true + break + } + } + if found { + posx = xx + x = xx + break + } + } + + //使用第三方库将图片重置为8** + img := resize.Resize(8, 8, dst, resize.Lanczos3) + dsts = append(dsts, img) + } + + return dsts +} + +// 去边 +func minImg(src image.Image) image.Image { + // 第一个白点 和 最后一个白点的坐标 + rect := src.Bounds() + + var ( + lt, rb image.Point + ) + + // 左上角x + for x := 0; x < rect.Dx(); x++ { + for y := 0; y < rect.Dy(); y++ { + r, _, _, _ := src.At(x, y).RGBA() + if r == 0xFFFF { + lt.X = x + x = rect.Dx() + break + } + } + } + // 左上角y + for y := 0; y < rect.Dy(); y++ { + for x := 0; x < rect.Dx(); x++ { + r, _, _, _ := src.At(x, y).RGBA() + if r == 0xFFFF { + lt.Y = y + y = rect.Dy() + break + } + } + } + // 右下角x + for x := rect.Dx() - 1; x > 0; x-- { + for y := rect.Dy() - 1; y > 0; y-- { + r, _, _, _ := src.At(x, y).RGBA() + if r == 0xFFFF { + rb.X = x + 1 + x = 0 + break + } + } + } + // 右下角y + for y := rect.Dy() - 1; y > 0; y-- { + for x := rect.Dx() - 1; x > 0; x-- { + r, _, _, _ := src.At(x, y).RGBA() + if r == 0xFFFF { + rb.Y = y + y = 0 + break + } + } + } + + newRect := image.Rect(0, 0, rb.X-lt.X+1, rb.Y-lt.Y) + log.Println(lt, rb) + log.Println(src.Bounds(), newRect) + dst := image.NewRGBA(newRect) + draw.Draw(dst, dst.Bounds(), src, lt, draw.Over) + return dst +} + +// 二值化 +func bin01(src image.Image) image.Image { + dst := image.NewGray16(src.Bounds()) + draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Over) + + rect := src.Bounds() + for x := 0; x < rect.Dx(); x++ { + for y := 0; y < rect.Dy(); y++ { + r, _, _, _ := dst.At(x, y).RGBA() + if r < 0x5555 { + dst.Set(x, y, color.White) + } else { + dst.Set(x, y, color.Black) + } + } + } + + return dst +} + +// 预览图片 +func showImg(src image.Image) { + dst, err := os.Create("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("output-%d.jpg", i)) + if err != nil { + log.Fatalln(err) + } + jpeg.Encode(dst, srcs[i], nil) + } +} + +// 号码定位 +func catImg(src image.Image) image.Image { + rect := src.Bounds() + // 左上角 w*100/290, h*150/180 + lt := image.Point{X: rect.Dx() * 100 / 290, Y: rect.Dy() * 150 / 180} + // 右下角 w*260/290, h*170/180 + rb := image.Point{X: rect.Dx() * 260 / 290, Y: rect.Dy() * 165 / 180} + newRect := image.Rectangle{ + Min: image.Point{X: 0, Y: 0}, + Max: image.Point{X: rb.X - lt.X, Y: rb.Y - lt.Y}, + } + dst := image.NewRGBA(newRect) + draw.Draw(dst, dst.Bounds(), src, lt, draw.Over) + return dst +}