SVM 字符识别方案

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/

四、特征提取

推荐: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

九、工程级优化建议

工业字符

实时识别

多字符识别

 

专注于matlab/simulink,电子电路,编程