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 }