SVM 字符识别方案
这是传统 OCR(车牌、验证码、工业字符)的标准做法之一。
一、整体方案
OpenCV 4.x + SVM(C-SVC)
特征:HOG / LBP / 像素投影(简单高效)
训练 → 保存模型 → 识别
支持数字 / 字母 / 自定义字符集
样本图像
↓
预处理(灰度、二值、归一化)
↓
特征提取(HOG / LBP)
↓
SVM 训练
↓
模型文件(.xml)
↓
实时识别
二、开发环境
| 项目 | 说明 |
|---|---|
| IDE | Visual Studio 2017–2022 |
| 语言 | C++ |
| 库 | OpenCV 4.x |
| 架构 | x64 |
OpenCV 3.x 之后 SVM 接口已重构,下面代码适用于 OpenCV 4.x
三、字符样本准备
1、样本格式
dataset/
├── train/
│ ├── 0/
│ │ ├── 0_001.jpg
│ │ ├── 0_002.jpg
│ ├── 1/
│ ├── ...
│ └── 9/
├── test/
- 单通道灰度图
- 尺寸统一(推荐 20×20 或 28×28)
四、特征提取
推荐:HOG(最稳)
#include <opencv2/opencv.hpp>
#include <opencv2/ml.hpp>
using namespace cv;
using namespace cv::ml;
Mat extractHOG(const Mat& img)
{
HOGDescriptor hog(
Size(20, 20),
Size(10, 10),
Size(5, 5),
Size(5, 5),
9
);
std::vector<float> descriptors;
hog.compute(img, descriptors);
return Mat(descriptors).clone();
}
每个字符 → 一维特征向量
五、SVM 训练程序
1、读取样本
void loadSamples(
const std::string& folder,
std::vector<Mat>& features,
std::vector<int>& labels)
{
for (int label = 0; label <= 9; ++label)
{
std::string subDir = folder + "\\" + std::to_string(label);
std::vector<String> files;
glob(subDir + "\\*.jpg", files);
for (auto& file : files)
{
Mat img = imread(file, IMREAD_GRAYSCALE);
resize(img, img, Size(20, 20));
equalizeHist(img, img);
Mat feat = extractHOG(img);
features.push_back(feat);
labels.push_back(label);
}
}
}
2、训练 SVM
int main()
{
std::vector<Mat> features;
std::vector<int> labels;
loadSamples("dataset/train", features, labels);
Mat trainData, trainLabels;
trainData = features.getMat(0);
trainLabels = Mat(labels).reshape(1, (int)labels.size());
Ptr<SVM> svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::RBF);
svm->setC(10.0);
svm->setGamma(0.01);
svm->setTermCriteria(TermCriteria(
TermCriteria::MAX_ITER, 1000, 1e-6));
std::cout << "Training..." << std::endl;
svm->train(trainData, ROW_SAMPLE, trainLabels);
svm->save("svm_char_model.xml");
std::cout << "Done." << std::endl;
return 0;
}
六、字符识别
int predictChar(const Mat& img, Ptr<SVM>& svm)
{
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
resize(gray, gray, Size(20, 20));
equalizeHist(gray, gray);
Mat feat = extractHOG(gray);
return (int)svm->predict(feat);
}
Ptr<SVM> svm = SVM::load<SVM>("svm_char_model.xml");
int result = predictChar(testImg, svm);
std::cout << "Predict: " << result << std::endl;
七、测试准确率
float evaluate(Ptr<SVM>& svm, const std::string& folder)
{
std::vector<Mat> features;
std::vector<int> labels;
loadSamples(folder, features, labels);
int correct = 0;
for (size_t i = 0; i < features.size(); ++i)
{
int pred = (int)svm->predict(features[i]);
if (pred == labels[i]) correct++;
}
return (float)correct / features.size();
}
参考代码 运用SVM进行训练和识别的的字符识别程序 www.youwenfan.com/contentcsv/101701.html
八、常见字符识别坑位
| 问题 | 解决 |
|---|---|
| 准确率 < 85% | 样本不足 / 噪声多 |
| 字符歪斜 | 先做 透视矫正 / 倾斜校正 |
| 字体差异大 | 增加样本多样性 |
| 特征区分度低 | HOG + LBP 融合 |
| 速度慢 | 改用 线性核 SVM |
九、工程级优化建议
工业字符
- 特征:HOG + 水平/垂直投影
- SVM 核:RBF(高精度)
实时识别
- 特征降维(PCA)
- 线性 SVM(速度快 10 倍)
多字符识别
- 先分割 → 再逐个预测