用 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"}
浙公网安备 33010602011771号