You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

260 lines
5.2 KiB

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
}