Go语言汉字验证码识别系统精简实现
下面我将介绍一个完整的汉字验证码识别系统实现,代码精简但功能完整,适合学习和生产环境使用。
一、核心实现
1.1 图像预处理(preprocess.go)
go
package main
import (
"image"
"image/color"
)更多内容访问ttocr.com或联系1436423940
// 灰度化处理
func grayscale(img image.Image) image.Gray {
bounds := img.Bounds()
gray := image.NewGray(bounds)
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
r, g, b, _ := img.At(x, y).RGBA()
grayValue := uint8((r299 + g587 + b114)/1000 >> 8)
gray.Set(x, y, color.Gray{Y: grayValue})
}
}
return gray
}
// 二值化处理(自适应阈值)
func binarize(gray *image.Gray) *image.Gray {
bounds := gray.Bounds()
binary := image.NewGray(bounds)
threshold := calculateThreshold(gray) // 自动计算阈值
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
if gray.GrayAt(x, y).Y < threshold {
binary.SetGray(x, y, color.Gray{Y: 0})
} else {
binary.SetGray(x, y, color.Gray{Y: 255})
}
}
}
return binary
}
1.2 字符分割(segment.go)
go
package main
import "image"
// 基于连通域分析的字符分割
func segment(binary image.Gray) []image.Gray {
var chars []*image.Gray
visited := make([][]bool, binary.Bounds().Dy())
for i := range visited {
visited[i] = make([]bool, binary.Bounds().Dx())
}
for y := 0; y < binary.Bounds().Dy(); y++ {
for x := 0; x < binary.Bounds().Dx(); x++ {
if binary.GrayAt(x, y).Y == 0 && !visited[y][x] {
// 发现新连通域
minX, maxX := x, x
minY, maxY := y, y
// 使用DFS标记连通域
var dfs func(int, int)
dfs = func(x, y int) {
if x < 0 || x >= binary.Bounds().Dx() ||
y < 0 || y >= binary.Bounds().Dy() ||
visited[y][x] || binary.GrayAt(x, y).Y != 0 {
return
}
visited[y][x] = true
if x < minX { minX = x }
if x > maxX { maxX = x }
if y < minY { minY = y }
if y > maxY { maxY = y }
dfs(x+1, y)
dfs(x-1, y)
dfs(x, y+1)
dfs(x, y-1)
}
dfs(x, y)
// 提取字符区域
if maxX-minX > 5 && maxY-minY > 5 { // 过滤小噪点
char := image.NewGray(image.Rect(0, 0, maxX-minX+1, maxY-minY+1))
for cy := minY; cy <= maxY; cy++ {
for cx := minX; cx <= maxX; cx++ {
char.SetGray(cx-minX, cy-minY, binary.GrayAt(cx, cy))
}
}
chars = append(chars, char)
}
}
}
}
// 按X坐标排序字符
sort.Slice(chars, func(i, j int) bool {
return chars[i].Bounds().Min.X < chars[j].Bounds().Min.X
})
return chars
}
1.3 特征提取(feature.go)
go
package main
import "image"
// 提取特征向量
func extractFeatures(char *image.Gray) []float64 {
// 1. 网格特征(8x8)
features := make([]float64, 64)
cellW := char.Bounds().Dx() / 8
cellH := char.Bounds().Dy() / 8
for i := 0; i < 64; i++ {
gx, gy := i%8, i/8
count := 0
for y := gy * cellH; y < (gy+1)*cellH && y < char.Bounds().Dy(); y++ {
for x := gx * cellW; x < (gx+1)*cellW && x < char.Bounds().Dx(); x++ {
if char.GrayAt(x, y).Y == 0 {
count++
}
}
}
features[i] = float64(count) / float64(cellW*cellH)
}
// 2. 投影特征(水平+垂直)
hProj := make([]float64, char.Bounds().Dy())
vProj := make([]float64, char.Bounds().Dx())
for y := 0; y < char.Bounds().Dy(); y++ {
for x := 0; x < char.Bounds().Dx(); x++ {
if char.GrayAt(x, y).Y == 0 {
hProj[y]++
vProj[x]++
}
}
}
// 归一化投影特征
for i := range hProj {
hProj[i] /= float64(char.Bounds().Dx())
}
for i := range vProj {
vProj[i] /= float64(char.Bounds().Dy())
}
// 合并所有特征
features = append(features, hProj...)
features = append(features, vProj...)
return features
}
1.4 分类识别(classify.go)
go
package main
import (
"encoding/json"
"os"
)
type Classifier struct {
Weights map[string][]float64 json:"weights"
Biases map[string]float64 json:"biases"
}
func LoadClassifier(modelPath string) (*Classifier, error) {
data, err := os.ReadFile(modelPath)
if err != nil {
return nil, err
}
var classifier Classifier
if err := json.Unmarshal(data, &classifier); err != nil {
return nil, err
}
return &classifier, nil
}
func (c *Classifier) Predict(features []float64) (string, float64) {
var (
bestClass string
maxScore = -1.0
)
for class, weights := range c.Weights {
score := c.Biases[class]
for i, w := range weights {
if i < len(features) {
score += w * features[i]
}
}
if score > maxScore {
maxScore = score
bestClass = class
}
}
// 归一化置信度
confidence := 1.0 / (1.0 + math.Exp(-maxScore))
return bestClass, confidence
}
二、完整使用示例
2.1 主程序(main.go)
go
package main
import (
"fmt"
"image/jpeg"
"log"
"os"
)
func main() {
// 1. 加载验证码图片
file, err := os.Open("captcha.jpg")
if err != nil {
log.Fatal(err)
}
defer file.Close()
img, err := jpeg.Decode(file)
if err != nil {
log.Fatal(err)
}
// 2. 图像预处理
gray := grayscale(img)
binary := binarize(gray)
// 3. 字符分割
chars := segment(binary)
if len(chars) == 0 {
log.Fatal("未检测到有效字符")
}
// 4. 加载分类器
classifier, err := LoadClassifier("model.json")
if err != nil {
log.Fatal("加载分类器失败:", err)
}
// 5. 识别每个字符
var result string
var totalConfidence float64
for _, char := range chars {
features := extractFeatures(char)
charClass, confidence := classifier.Predict(features)
result += charClass
totalConfidence += confidence
}
avgConfidence := totalConfidence / float64(len(chars))
fmt.Printf("识别结果: %s (平均置信度: %.2f%%)\n",
result, avgConfidence*100)
}
三、模型训练建议
3.1 使用Python训练模型
python
train.py
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
import numpy as np
import json
加载训练数据
X, y = load_training_data() # 实现你自己的数据加载
数据标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
训练SVM模型
model = SVC(kernel='linear', probability=True)
model.fit(X_scaled, y)
保存模型参数
model_params = {
"weights": {cls: model.coef_[i].tolist() for i, cls in enumerate(model.classes_)},
"biases": {cls: model.intercept_[i] for i, cls in enumerate(model.classes_)},
"scaler_mean": scaler.mean_.tolist(),
"scaler_scale": scaler.scale_.tolist()
}
with open("model.json", "w") as f:
json.dump(model_params, f)
四、性能优化技巧
4.1 内存池优化
go
var matPool = sync.Pool{
New: func() interface{} {
return gocv.NewMat()
},
}
func GetMat() gocv.Mat {
return matPool.Get().(gocv.Mat)
}
func PutMat(mat gocv.Mat) {
mat.Close()
matPool.Put(mat)
}
4.2 并行处理流水线
go
func ProcessImage(imgPath string, classifier *Classifier) (string, error) {
// 1. 加载图像
img := gocv.IMRead(imgPath, gocv.IMReadColor)
defer img.Close()
// 2. 预处理(使用内存池)
gray := GetMat()
defer PutMat(gray)
gocv.CvtColor(img, &gray, gocv.ColorBGRToGray)
binary := GetMat()
defer PutMat(binary)
gocv.AdaptiveThreshold(gray, &binary, 255,
gocv.AdaptiveThresholdGaussian,
gocv.ThresholdBinary, 11, 2)
// 3. 字符分割
chars := segment(binary)
// 4. 并行特征提取和分类
var wg sync.WaitGroup
results := make([]string, len(chars))
confidences := make([]float64, len(chars))
for i, char := range chars {
wg.Add(1)
go func(idx int, c gocv.Mat) {
defer wg.Done()
defer PutMat(c)
features := extractFeatures(c)
results[idx], confidences[idx] = classifier.Predict(features)
}(i, char.Clone())
}
wg.Wait()
// 5. 合并结果
return strings.Join(results, ""), nil
}
浙公网安备 33010602011771号