用 Go 和 Python 实现图像验证码识别系统

在本项目中,我们将使用 Python 训练深度学习验证码识别模型,然后用 Go 编写服务端代码,加载模型并进行在线识别。我们不会使用 Cgo 或直接在 Go 中进行深度学习,而是通过调用 Python 推理服务的方式实现完整的验证码识别系统。

一、项目结构

captcha-system/
├── model/
│ └── crnn_model.pt # 训练好的 PyTorch 模型
├── service/
│ ├── main.go # Go 实现的 HTTP 服务
│ └── go.mod
├── python_api/
│ ├── app.py # Python 推理接口(Flask 或 FastAPI)
│ └── utils.py # 图像处理与预测代码
└── samples/
└── demo.png # 待识别验证码样例
二、模型训练(Python)
这里使用 PyTorch 实现 CRNN 模型,训练部分略去,仅说明保存方式:
更多内容访问ttocr.com或联系1436423940

保存模型

torch.save(model.state_dict(), "crnn_model.pt")
你可以参考之前的实现方式或开源仓库,如 meijieru/crnn.pytorch。

三、部署 Python 推理服务
文件:python_api/app.py

from fastapi import FastAPI, UploadFile, File
from fastapi.responses import JSONResponse
import torch
from PIL import Image
import io
from utils import load_model, predict_image

app = FastAPI()
model = load_model("crnn_model.pt")

@app.post("/predict")
async def predict(image: UploadFile = File(...)):
content = await image.read()
img = Image.open(io.BytesIO(content)).convert('RGB')
text = predict_image(model, img)
return JSONResponse(content={"result": text})
文件:python_api/utils.py

import torch
from torchvision import transforms
from model import CRNN # 自定义的模型结构

CHARACTERS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'

def load_model(path):
model = CRNN()
model.load_state_dict(torch.load(path, map_location="cpu"))
model.eval()
return model

def predict_image(model, image):
transform = transforms.Compose([
transforms.Resize((60, 160)),
transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))
])
x = transform(image).unsqueeze(0) # [1, C, H, W]
with torch.no_grad():
out = model(x)
pred = out.argmax(2).squeeze(0).tolist()
return ''.join(CHARACTERS[i] for i in pred)
启动服务:

uvicorn app:app --host 0.0.0.0 --port 5000
四、用 Go 实现前端识别服务
文件:service/main.go

package main

import (
"bytes"
"fmt"
"github.com/gin-gonic/gin"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
)

func main() {
r := gin.Default()

r.POST("/recognize", func(c *gin.Context) {
	file, err := c.FormFile("image")
	if err != nil {
		c.JSON(400, gin.H{"error": "上传失败"})
		return
	}

	tmpPath := filepath.Join(os.TempDir(), file.Filename)
	if err := c.SaveUploadedFile(file, tmpPath); err != nil {
		c.JSON(500, gin.H{"error": "保存图片失败"})
		return
	}

	result, err := sendToPython(tmpPath)
	if err != nil {
		c.JSON(500, gin.H{"error": "识别出错"})
		return
	}

	c.JSON(200, gin.H{"code": result})
})

r.Run(":8080")

}

func sendToPython(path string) (string, error) {
file, err := os.Open(path)
if err != nil {
return "", err
}
defer file.Close()

body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("image", filepath.Base(path))
if err != nil {
	return "", err
}
_, err = io.Copy(part, file)
if err != nil {
	return "", err
}
writer.Close()

req, err := http.NewRequest("POST", "http://localhost:5000/predict", body)
req.Header.Set("Content-Type", writer.FormDataContentType())

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
	return "", err
}
defer resp.Body.Close()

result, err := io.ReadAll(resp.Body)
if err != nil {
	return "", err
}

return string(result), nil

}
五、测试

curl -X POST -F "image=@samples/demo.png" http://localhost:8080/recognize
响应示例:

{"code": "3Y6G"}

posted @ 2025-05-30 13:00  ttocr、com  阅读(12)  评论(0)    收藏  举报