123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- package main
- import (
- "flag"
- "fmt"
- "image"
- "image/color"
- "image/draw"
- "image/jpeg"
- _ "image/png"
- "net/http"
- "os"
- "strconv"
- "strings"
- "time"
- "github.com/nfnt/resize"
- )
- /*
- 1. 读取验证码
- 2. 将验证码图片二值化
- 3. 将二值化后的图片进行近似等分切割(可能有更好的方法,对于精度不高要求不高的话等分即可)
- 4. 将切割后的图片进行识别(调用train()函数,手动标注验证码,生成指纹数组,也可以写入到一个文件中,然后读取)
- 5. 将识别后的结果进行拼接
- */
- var (
- model = flag.String("m", "train", "运行模式【train/predict】")
- imageFile = flag.String("img", "./demo.jpg", "要识别的图像文件")
- threshold = flag.Uint64("ct", 0xcccc, "颜色过滤阈值")
- clearColorThreshold uint32
- )
- func init() {
- flag.Parse()
- clearColorThreshold = uint32(*threshold)
- if _, err := os.Stat("./view"); err != nil {
- os.Mkdir("./view", 0777)
- }
- }
- // 判断r四周是否有任意两个连通白块,如果没有,则认为是噪点
- func imgConnect(ru, rd, rl, rr uint32) bool {
- if (ru < clearColorThreshold &&
- (rl < clearColorThreshold || rr < clearColorThreshold)) ||
- (rd < clearColorThreshold && (rl < clearColorThreshold || rr < clearColorThreshold)) ||
- (rl < clearColorThreshold && (ru < clearColorThreshold || rd < clearColorThreshold)) ||
- (rr < clearColorThreshold && (ru < clearColorThreshold || rd < clearColorThreshold)) {
- return true
- }
- return false
- }
- // 二值化
- func imgBarbarization(img image.Image) image.Image {
- binImg := image.NewGray16(img.Bounds())
- draw.Draw(binImg, binImg.Bounds(), img, img.Bounds().Min, draw.Over)
- rect := binImg.Bounds()
- // 遍历点像素点
- for x := 0; x < rect.Dx(); x++ {
- for y := 0; y < rect.Dy(); y++ {
- // 获取颜色
- r, _, _, _ := binImg.At(x, y).RGBA()
- ru, _, _, _ := img.At(x, y+1).RGBA()
- rd, _, _, _ := img.At(x, y-1).RGBA()
- rl, _, _, _ := img.At(x-1, y).RGBA()
- rr, _, _, _ := img.At(x+1, y).RGBA()
- if r < clearColorThreshold && imgConnect(ru, rd, rl, rr) {
- binImg.Set(x, y, color.White)
- } else {
- binImg.Set(x, y, color.Black)
- }
- }
- }
- return binImg
- }
- // 删除图片无用外部轮廓
- func imgCutSide(img image.Image) image.Image {
- rect := img.Bounds()
- // 开始x坐标
- leftStartX := 0
- for x := 0; x < rect.Dx(); x++ {
- lxflag := false
- for y := 0; y < rect.Dy(); y++ {
- // 获取颜色
- r, _, _, _ := img.At(x, y).RGBA()
- if r == 0xFFFF {
- lxflag = true
- break
- }
- }
- if lxflag {
- leftStartX = x
- break
- }
- }
- // 开始y坐标
- leftStartY := 0
- for y := 0; y < rect.Dy(); y++ {
- lyflag := false
- for x := 0; x < rect.Dx(); x++ {
- // 获取颜色
- r, _, _, _ := img.At(x, y).RGBA()
- if r == 0xFFFF {
- lyflag = true
- break
- }
- }
- if lyflag {
- leftStartY = y
- break
- }
- }
- // 结束x坐标
- rightEndX := 0
- for x := rect.Dx(); x > 0; x-- {
- rxflag := false
- for y := rect.Dy(); y > 0; y-- {
- // 获取颜色
- r, _, _, _ := img.At(x, y).RGBA()
- if r == 0xFFFF {
- rxflag = true
- break
- }
- }
- if rxflag {
- rightEndX = x
- break
- }
- }
- // 结束y坐标
- rightEndY := 0
- for y := rect.Dy(); y > 0; y-- {
- ryflag := false
- for x := rect.Dy(); x > 0; x-- {
- // 获取颜色
- r, _, _, _ := img.At(x, y).RGBA()
- if r == 0xFFFF {
- ryflag = true
- break
- }
- }
- if ryflag {
- rightEndY = y
- break
- }
- }
- // 创建新的图片
- rectangle := image.Rectangle{
- Min: image.Point{X: 0, Y: 0},
- Max: image.Point{X: rightEndX - leftStartX, Y: rightEndY - leftStartY},
- }
- newSideImg := image.NewGray16(rectangle)
- draw.Draw(newSideImg, newSideImg.Bounds(), img, image.Point{X: leftStartX, Y: leftStartY}, draw.Over)
- return newSideImg
- }
- func cutImage(img image.Image) []image.Image {
- rect := img.Bounds()
- imgs := make([]image.Image, 0)
- // 记录当前x的位置
- nowCutStartX := 0
- for x := 0; x <= rect.Dx(); x++ {
- every := rect.Dx() / 4
- if x%every == 0 && x != 0 {
- // 创建新的图片
- rectangle := image.Rectangle{
- Min: image.Point{X: 0, Y: 0},
- Max: image.Point{X: x + 3 - nowCutStartX, Y: rect.Dy()},
- }
- newImg := image.NewGray16(rectangle)
- draw.Draw(newImg, newImg.Bounds(), img, image.Point{X: nowCutStartX, Y: 0}, draw.Over)
- newResizeImg := imgCutSide(newImg)
- newResizeImg = resize.Resize(16, 16, newResizeImg, resize.Lanczos3)
- newResizeImg = imgCutSide(newImg)
- newResizeImg = resize.Resize(16, 16, newResizeImg, resize.Lanczos3)
- imgs = append(imgs, newResizeImg)
- nowCutStartX = x + 3
- }
- }
- return imgs
- }
- // 指纹验证
- func imgSign(imgBinary string) string {
- if imgBinary == "" {
- return imgBinary
- }
- imgBinarySign := strings.Split(imgBinary, "")
- // 相似度
- maxSimilarity := 0
- num := ""
- for _, sign := range numSign {
- tmpSimilarity := 0
- signArr := strings.Split(sign[1], "")
- for k, s := range imgBinarySign {
- if s == signArr[k] {
- tmpSimilarity++
- }
- }
- if maxSimilarity < tmpSimilarity {
- maxSimilarity = tmpSimilarity
- num = sign[0]
- }
- }
- return num
- }
- // 将切割后的字符图片转换为二进制, 与指纹库进行比对
- func recognition(imgs []image.Image) (string, []string) {
- signNum := make([]string, 0)
- for _, img := range imgs {
- tmpSignNum := make([]string, 0)
- rect := img.Bounds()
- for x := 0; x < rect.Dx(); x++ {
- for y := 0; y < rect.Dy(); y++ {
- // 获取颜色
- r, _, _, _ := img.At(x, y).RGBA()
- if r > 0x7788 {
- tmpSignNum = append(tmpSignNum, "1")
- } else {
- tmpSignNum = append(tmpSignNum, "0")
- }
- }
- }
- signNum = append(signNum, strings.Join(tmpSignNum, ""))
- }
- res := make([]string, 0)
- for _, v := range signNum {
- res = append(res, imgSign(v))
- }
- return strings.Join(res, ""), signNum
- }
- func train() {
- stringgen := ""
- f, err := os.Create("train.txt")
- if err != nil {
- fmt.Println(err)
- return
- }
- client := &http.Client{
- Timeout: 2 * time.Second,
- }
- for {
- resp, err := client.Get("https://beian.china-eia.com/servlet/validateCodeServlet?" + strconv.FormatInt(time.Now().UnixNano()/1e6, 10))
- if err != nil {
- fmt.Sprintf("打开图片失败:%+v", err.Error())
- continue
- }
- fmt.Println(time.Now().UnixNano() / 1e6)
- img, err := jpeg.Decode(resp.Body)
- if err != nil {
- fmt.Sprintf("解析图片失败:%+v", err.Error())
- continue
- }
- resp.Body.Close()
- dts, err := os.Create("./view/01.原始图片.jpeg")
- jpeg.Encode(dts, img, nil)
- // 图片二值化
- binImg := imgBarbarization(img)
- dts1, err := os.Create("./view/02.二值化后的图片.jpeg")
- jpeg.Encode(dts1, binImg, nil)
- // 图片切割
- imgCutSilce := cutImage(binImg)
- // 输出切割后的四张图片
- dts3, err := os.Create("./view/03.第一个字符.jpeg")
- jpeg.Encode(dts3, imgCutSilce[0], nil)
- dts4, err := os.Create("./view/04.第二个字符.jpeg")
- jpeg.Encode(dts4, imgCutSilce[1], nil)
- dts5, err := os.Create("./view/05.第三个字符.jpeg")
- jpeg.Encode(dts5, imgCutSilce[2], nil)
- dts6, err := os.Create("./view/06.第四个字符.jpeg")
- jpeg.Encode(dts6, imgCutSilce[3], nil)
- // 验证码识别
- result, resu := recognition(imgCutSilce)
- fmt.Println("程序识别结果: ", result)
- x := ""
- fmt.Print("请输入./view/01.原始图片正确结果: ")
- fmt.Scan(&x)
- if x == "q" {
- return
- } else if x == "k" {
- continue
- } else if len(x) != 4 {
- fmt.Println("输入的格式不对")
- continue
- }
- inputs := strings.Split(x, "")
- stringgen = fmt.Sprintf("{\"%s\", \"%s\"},\n{\"%s\", \"%s\"},\n{\"%s\", \"%s\"},\n{\"%s\", \"%s\"},\n", inputs[0], resu[0], inputs[1], resu[1], inputs[2], resu[2], inputs[3], resu[3])
- _, err = f.Write([]byte(stringgen))
- if err != nil {
- fmt.Println(err)
- }
- }
- }
- // 识别预测
- func predict(imageFile string) (string, error) {
- fi, err := os.Open(imageFile)
- if err != nil {
- return "", err
- }
- defer fi.Close()
- img, err := jpeg.Decode(fi)
- if err != nil {
- return "", err
- }
- // 图片二值化
- binImg := imgBarbarization(img)
- // 图片切割
- imgCutSilce := cutImage(binImg)
- // 验证码识别
- result, resu := recognition(imgCutSilce)
- fmt.Println("程序识别结果: ", result, resu)
- return result, nil
- }
- func main() {
- switch *model {
- case "train":
- train()
- default:
- predict(*imageFile)
- }
- }
|